[
  {
    "path": ".github/workflows/alpha-release.yml",
    "content": "name: Go-Alpha-Release\n\n# 触发条件配置\non:\n  push:\n    branches-ignore:\n      - \"master\"\n    paths:\n      - \"internal/app/version.go\"\n# 权限配置：允许脚本写入仓库内容（用于发布 Release）\npermissions:\n  contents: write\n  packages: write\n\nenv:\n  NAME: ${{ github.event.repository.name }}\n\njobs:\n  check-version:\n    if: github.actor == 'haierkeys'\n    runs-on: ubuntu-latest\n    outputs:\n      should_release: ${{ steps.check.outputs.should_release }}\n      release_tag: ${{ steps.check.outputs.release_tag }}\n      commit_msg: ${{ steps.check.outputs.commit_msg }}\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          ref: ${{ github.ref }}\n          fetch-depth: 0\n\n      - name: Check Version\n        id: check\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: |\n          # 从 internal/app/version.go (HEAD) 读取当前版本\n          # 预期格式: var Version string = \"0.0.1\"\n          CURRENT_VERSION=$(grep -E 'Version\\s+string' internal/app/version.go | awk -F '\"' '{print $2}')\n          echo \"当前版本 (HEAD): $CURRENT_VERSION\"\n\n          # 从 internal/app/version.go (HEAD~1) 获取上一个版本\n          # 使用 git show 获取上一次提交的文件内容\n          # 注意: 我们将其包裹在一个块中以处理 HEAD~1 可能不存在的情况 (例如: 初始提交)\n          if git rev-parse --verify HEAD~1 >/dev/null 2>&1; then\n             PREV_FILE_CONTENT=$(git show HEAD~1:internal/app/version.go)\n             PREV_VERSION=$(echo \"$PREV_FILE_CONTENT\" | grep -E 'Version\\s+string' | awk -F '\"' '{print $2}')\n          else\n             PREV_VERSION=\"0.0.0\"\n          fi\n\n          # 如果 PREV_VERSION 为空 (例如: grep 失败), 默认为 0.0.0\n          if [ -z \"$PREV_VERSION\" ]; then\n             PREV_VERSION=\"0.0.0\"\n          fi\n\n          echo \"上一个版本 (HEAD~1): $PREV_VERSION\"\n\n          # 规范化版本以进行比较 (如果存在 'v' 前缀则移除)\n          V_LAST=${PREV_VERSION#v}\n          V_CURR=${CURRENT_VERSION#v}\n\n          # 比较版本\n          if [ \"$V_CURR\" != \"$V_LAST\" ]; then\n             # 使用 sort -V 确定当前版本是否严格大于旧版本\n             NEWER_VERSION=$(echo -e \"$V_LAST\\n$V_CURR\" | sort -V | tail -n 1)\n\n             if [ \"$NEWER_VERSION\" == \"$V_CURR\" ] && [ \"$NEWER_VERSION\" != \"$V_LAST\" ]; then\n                echo \"检测到新版本: $V_CURR > $V_LAST\"\n                echo \"should_release=true\" >> $GITHUB_OUTPUT\n                echo \"release_tag=$V_CURR\" >> $GITHUB_OUTPUT\n             else\n                echo \"版本 $V_CURR 不大于 $V_LAST。跳过发布...\"\n                echo \"should_release=false\" >> $GITHUB_OUTPUT\n             fi\n          else\n             echo \"版本匹配 ($V_CURR == $V_LAST)。跳过发布...\"\n             echo \"should_release=false\" >> $GITHUB_OUTPUT\n          fi\n\n  prepare-message:\n    needs: check-version\n    if: needs.check-version.outputs.should_release == 'true' && github.actor == 'haierkeys'\n    runs-on: ubuntu-latest\n    outputs:\n      commit_msg: ${{ steps.trans.outputs.commit_msg }}\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          ref: ${{ github.ref }}\n          fetch-depth: 0\n\n      - name: Prepare Commit Message\n        id: trans\n        run: |\n          # 获取提交文本信息\n          msg=$(git log -1 --pretty=%B)\n\n          # 设置 Python 进行翻译\n          pip install deep-translator > /dev/null 2>&1 || true\n\n          # 运行翻译脚本\n          export COMMIT_MSG=\"$msg\"\n\n          echo \"commit_msg<<EOF\" >> $GITHUB_OUTPUT\n          if [ -f \"scripts/translate_commit.py\" ]; then\n             python3 scripts/translate_commit.py >> $GITHUB_OUTPUT\n          else\n             echo \"未找到翻译脚本，使用原始消息\"\n             echo \"$msg\" >> $GITHUB_OUTPUT\n          fi\n          echo \"EOF\" >> $GITHUB_OUTPUT\n\n\n  create-release:\n    needs: [check-version, prepare-message]\n    if: needs.check-version.outputs.should_release == 'true' && github.actor == 'haierkeys'\n    name: Create Release\n    runs-on: ubuntu-latest\n    outputs:\n      upload_url: ${{ steps.create_release.outputs.upload_url }}\n    steps:\n      - name: Create Release\n        id: create_release\n        uses: softprops/action-gh-release@v2\n        env:\n          token: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          tag_name: ${{ needs.check-version.outputs.release_tag }}-alpha\n          name: ${{ needs.check-version.outputs.release_tag }}-alpha\n          draft: false\n          prerelease: true\n          generate_release_notes: true\n          body: ${{ needs.prepare-message.outputs.commit_msg }}\n          target_commitish: ${{ github.sha }} # 明确指定在当前分支的 commit 上创建 tag\n          overwrite_files: true\n\n  build-binaries:\n    needs: [check-version, prepare-message, create-release]\n    if: needs.check-version.outputs.should_release == 'true' && github.actor == 'haierkeys'\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          ref: ${{ github.ref }}\n          fetch-depth: 0\n\n      - uses: actions/setup-go@v6\n        with:\n          go-version-file: 'go.mod'\n\n      - name: Check Go Version\n        run: go version\n\n      - name: Go Build Prepare\n        run: go install github.com/mitchellh/gox@latest\n\n      - name: Go Build Multi-platform\n        env:\n          COMMIT_MSG: ${{ needs.prepare-message.outputs.commit_msg }}\n        run: make gox-all\n\n      - name: Create Changelog\n        env:\n          COMMIT_MSG: ${{ needs.prepare-message.outputs.commit_msg }}\n        run: |\n          echo \"${COMMIT_MSG}\" > ./build/changelog.txt\n\n      - name: Create GZip Archives for All Platforms\n        env:\n          RELEASE_BASENAME: ${{ env.NAME }}-${{ needs.check-version.outputs.release_tag }}\n        run: |\n          # 为所有平台创建 tar.gz 包\n          # Create tar.gz packages for all platforms\n          declare -a PLATFORMS=(\"darwin_amd64\" \"darwin_arm64\" \"linux_amd64\" \"linux_arm64\" \"linux_arm\" \"windows_amd64\")\n          for PLATFORM in \"${PLATFORMS[@]}\"; do\n            GOOS=$(echo \"$PLATFORM\" | cut -d'_' -f1)\n            GOARCH=$(echo \"$PLATFORM\" | cut -d'_' -f2)\n\n            # 这里的重命名逻辑：如果是 linux_arm，则将后缀改为 armv7l\n            # Rename linux_arm to linux-armv7l for easier identification\n            TARGET_ARCH=$GOARCH\n            if [ \"$PLATFORM\" == \"linux_arm\" ]; then\n                TARGET_ARCH=\"armv7l\"\n            fi\n\n            ARCHIVE_NAME=\"${RELEASE_BASENAME}-${GOOS}-${TARGET_ARCH}.tar.gz\"\n            echo \"Creating archive: ${ARCHIVE_NAME}\"\n            tar -czvf \"./build/${ARCHIVE_NAME}\" ./config -C \"./build/${PLATFORM}/\" .\n          done\n\n      - name: Upload Build Artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: build_file\n          path: ./build/\n\n      - name: Upload Config Artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: config\n          path: ./config\n\n      - name: Upload Release Archives\n        uses: actions/upload-artifact@v4\n        with:\n          name: release_archives\n          path: |\n            ./build/${{ env.NAME }}-${{ needs.check-version.outputs.release_tag }}-*.tar.gz\n            ./build/changelog.txt\n\n  push-docker:\n    needs: [check-version, build-binaries, create-release]\n    if: needs.check-version.outputs.should_release == 'true' && github.actor == 'haierkeys'\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Download Build Artifacts\n        uses: actions/download-artifact@v4\n        with:\n          name: build_file\n          path: ./build/\n\n      - name: Download Config Artifacts\n        uses: actions/download-artifact@v4\n        with:\n          name: config\n          path: ./config\n\n      - name: Set Environment Variables\n        run: |\n          # NAME is already set globally\n          # Use the tag from check-version\n          TAG_VERSION=${{ needs.check-version.outputs.release_tag }}\n          echo \"TAG_VERSION=${TAG_VERSION}\" >> ${GITHUB_ENV}\n          echo \"IMAGE_TAG=${TAG_VERSION}\" >> ${GITHUB_ENV}\n          echo \"BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')\" >> ${GITHUB_ENV}\n          echo \"GIT_COMMIT=$(git rev-parse --short HEAD)\" >> ${GITHUB_ENV}\n\n\n\n      - uses: docker/setup-qemu-action@v2\n      - uses: docker/setup-buildx-action@v2\n\n      - name: Docker Build & Publish to GitHub Container Registry\n        uses: elgohr/Publish-Docker-Github-Action@v5\n        with:\n          dockerfile: docker/Dockerfile\n          name: ${{ github.actor }}/${{ env.NAME }}\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n          platforms: linux/amd64,linux/arm64,linux/arm/v7\n          registry: ghcr.io\n          snapshot: false\n          tags: \"${{ env.IMAGE_TAG }}\"\n          buildargs: |\n            VERSION=${{ env.TAG_VERSION }}\n            BUILD_DATE=${{ env.BUILD_DATE }}\n            GIT_COMMIT=${{ env.GIT_COMMIT }}\n\n      - name: Docker Build & Publish to DockerHub\n        uses: elgohr/Publish-Docker-Github-Action@v5\n        with:\n          dockerfile: docker/Dockerfile\n          name: ${{ github.actor }}/${{ env.NAME }}\n          username: ${{ github.actor }}\n          password: ${{ secrets.DOCKERHUB_TOKEN }}\n          platforms: linux/amd64,linux/arm64,linux/arm/v7\n          snapshot: false\n          tags: \"${{ env.IMAGE_TAG }}\"\n          buildargs: |\n            VERSION=${{ env.TAG_VERSION }}\n            BUILD_DATE=${{ env.BUILD_DATE }}\n            GIT_COMMIT=${{ env.GIT_COMMIT }}\n\n      - name: Docker Build & Publish to CNB\n        uses: elgohr/Publish-Docker-Github-Action@v5\n        with:\n          dockerfile: docker/Dockerfile\n          name: haierkeys/${{ env.NAME }}\n          username: ${{ secrets.CNB_USERNAME }}\n          password: ${{ secrets.CNB_TOKEN }}\n          platforms: linux/amd64,linux/arm64,linux/arm/v7\n          registry: docker.cnb.cool\n          snapshot: false\n          tags: \"${{ env.IMAGE_TAG }}\"\n          buildargs: |\n            VERSION=${{ env.TAG_VERSION }}\n            BUILD_DATE=${{ env.BUILD_DATE }}\n            GIT_COMMIT=${{ env.GIT_COMMIT }}\n\n  push-release-files:\n    needs: [create-release, build-binaries, check-version]\n    if: needs.check-version.outputs.should_release == 'true'\n    runs-on: ubuntu-latest\n    steps:\n      - name: Download Release Archives\n        uses: actions/download-artifact@v4\n        with:\n          name: release_archives\n          path: ./archives/\n\n      - name: Upload Archives to GitHub Release\n        uses: softprops/action-gh-release@v2\n        with:\n          tag_name: ${{ needs.check-version.outputs.release_tag }}-alpha\n          files: ./archives/*\n\n  push-cnb-release:\n    needs: [check-version, prepare-message, build-binaries, create-release]\n    if: needs.check-version.outputs.should_release == 'true'\n    name: Push CNB Release\n    runs-on: ubuntu-latest\n    env:\n      CNB_REPO: haierkeys/fast-note-sync-service\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          ref: ${{ github.ref }}\n          fetch-depth: 0\n\n      - name: Push Tag to CNB\n        env:\n          CNB_TOKEN: ${{ secrets.CNB_TOKEN }}\n          RELEASE_TAG: ${{ needs.check-version.outputs.release_tag }}-alpha\n        run: |\n          # 确保拉取了最新的 tag 信息\n          git fetch --tags\n          git remote add cnb \"https://cnb:${CNB_TOKEN}@cnb.cool/${CNB_REPO}.git\"\n          # 直接推送已存在的 tag\n          git push cnb \"${RELEASE_TAG}\" --force\n\n      - name: Download Release Archives\n        uses: actions/download-artifact@v4\n        with:\n          name: release_archives\n          path: ./archives/\n\n      - name: Create CNB Release and Upload Assets\n        env:\n          CNB_TOKEN: ${{ secrets.CNB_TOKEN }}\n          RELEASE_TAG: ${{ needs.check-version.outputs.release_tag }}-alpha\n          RELEASE_BODY: ${{ needs.prepare-message.outputs.commit_msg }}\n        run: |\n          set -e\n\n          # ============================================================\n          # 第一步: 创建 CNB Release\n          # Step 1: Create CNB Release\n          # ============================================================\n          echo \"::group::Creating CNB Release: ${RELEASE_TAG}\"\n          RELEASE_RESPONSE=$(curl -s -w \"\\n%{http_code}\" -X POST \\\n            -H \"Accept: application/vnd.cnb.api+json\" \\\n            -H \"Authorization: Bearer ${CNB_TOKEN}\" \\\n            -H \"Content-Type: application/json\" \\\n            -d \"$(jq -n \\\n              --arg tag \"${RELEASE_TAG}\" \\\n              --arg name \"${RELEASE_TAG}\" \\\n              --arg body \"${RELEASE_BODY}\" \\\n              '{tag_name: $tag, name: $name, body: $body, prerelease: true, draft: false}')\" \\\n            \"https://api.cnb.cool/${CNB_REPO}/-/releases\")\n\n          HTTP_CODE=$(echo \"$RELEASE_RESPONSE\" | tail -1)\n          RESPONSE_BODY=$(echo \"$RELEASE_RESPONSE\" | sed '$d')\n\n          if [ \"$HTTP_CODE\" -ge 200 ] && [ \"$HTTP_CODE\" -lt 300 ]; then\n            RELEASE_ID=$(echo \"$RESPONSE_BODY\" | jq -r '.id')\n            echo \"CNB Release created successfully, ID: ${RELEASE_ID}\"\n          else\n            echo \"::warning::Failed to create CNB Release (HTTP ${HTTP_CODE}), attempting to fetch existing release...\"\n            # 尝试获取已存在的 Release / Try to get existing release\n            LIST_RESPONSE=$(curl -s \\\n              -H \"Accept: application/vnd.cnb.api+json\" \\\n              -H \"Authorization: Bearer ${CNB_TOKEN}\" \\\n              \"https://api.cnb.cool/${CNB_REPO}/-/releases\")\n            RELEASE_ID=$(echo \"$LIST_RESPONSE\" | jq -r \".[] | select(.tag_name==\\\"${RELEASE_TAG}\\\") | .id\")\n            if [ -z \"$RELEASE_ID\" ] || [ \"$RELEASE_ID\" = \"null\" ]; then\n              echo \"::error::Cannot create or find CNB Release for tag ${RELEASE_TAG}\"\n              exit 1\n            fi\n            echo \"Found existing CNB Release, ID: ${RELEASE_ID}\"\n          fi\n          echo \"::endgroup::\"\n\n          # ============================================================\n          # 第二步: 并行上传所有已打包的构建产物\n          # Step 2: Parallel upload all pre-built release archives\n          # ============================================================\n          for FILE_PATH in ./archives/*; do\n            (\n              FILE_NAME=$(basename \"$FILE_PATH\")\n\n              echo \"Uploading ${FILE_NAME}...\"\n              FILE_SIZE=$(stat -c%s \"$FILE_PATH\")\n\n              # 2a. 获取上传 URL / Get upload URL\n              UPLOAD_RESPONSE=$(curl -s -X POST \\\n                -H \"Accept: application/vnd.cnb.api+json\" \\\n                -H \"Authorization: Bearer ${CNB_TOKEN}\" \\\n                -H \"Content-Type: application/json\" \\\n                -d \"$(jq -n \\\n                  --arg name \"${FILE_NAME}\" \\\n                  --argjson size ${FILE_SIZE} \\\n                  '{asset_name: $name, size: $size, overwrite: true}')\" \\\n                \"https://api.cnb.cool/${CNB_REPO}/-/releases/${RELEASE_ID}/asset-upload-url\")\n\n              UPLOAD_URL=$(echo \"$UPLOAD_RESPONSE\" | jq -r '.upload_url')\n              VERIFY_URL=$(echo \"$UPLOAD_RESPONSE\" | jq -r '.verify_url')\n\n              if [ -z \"$UPLOAD_URL\" ] || [ \"$UPLOAD_URL\" = \"null\" ]; then\n                echo \"::error::Failed to get upload URL for ${FILE_NAME}\"\n                echo \"Response: ${UPLOAD_RESPONSE}\"\n                exit 1\n              fi\n\n              # 2b. 上传文件内容 / Upload file content\n              echo \"Uploading ${FILE_NAME} to: ${UPLOAD_URL}\"\n              CONTENT_TYPE=\"application/gzip\"\n              if [[ \"${FILE_NAME}\" == *.txt ]]; then\n                CONTENT_TYPE=\"text/plain\"\n              fi\n\n              UPLOAD_RESULT=$(curl -s -w \"\\n%{http_code}\" -X PUT \\\n                -H \"Content-Type: ${CONTENT_TYPE}\" \\\n                --data-binary @\"$FILE_PATH\" \\\n                \"$UPLOAD_URL\")\n              UPLOAD_HTTP_CODE=$(echo \"$UPLOAD_RESULT\" | tail -1)\n\n              if [ \"$UPLOAD_HTTP_CODE\" -ne 200 ] && [ \"$UPLOAD_HTTP_CODE\" -ne 201 ]; then\n                echo \"::error::Failed to upload ${FILE_NAME} (HTTP ${UPLOAD_HTTP_CODE})\"\n                exit 1\n              fi\n\n              # 2c. 确认上传完成 / Confirm upload\n              echo \"Confirming upload for ${FILE_NAME}...\"\n              CONFIRM_RESULT=$(curl -s -w \"\\n%{http_code}\" -X POST \\\n                -H \"Accept: application/vnd.cnb.api+json\" \\\n                -H \"Authorization: Bearer ${CNB_TOKEN}\" \\\n                \"${VERIFY_URL}\")\n              CONFIRM_HTTP_CODE=$(echo \"$CONFIRM_RESULT\" | tail -1)\n\n              if [ \"$CONFIRM_HTTP_CODE\" -ne 200 ] && [ \"$CONFIRM_HTTP_CODE\" -ne 201 ]; then\n                echo \"::error::Failed to confirm upload for ${FILE_NAME} (HTTP ${CONFIRM_HTTP_CODE})\"\n                exit 1\n              fi\n\n              echo \"✅ ${FILE_NAME} uploaded successfully\"\n            ) &\n          done\n\n          # 等待所有后台任务完成\n          wait\n\n          echo \"🎉 All assets uploaded to CNB Release!\""
  },
  {
    "path": ".github/workflows/mirror-to-cnb.yml",
    "content": "# Mirror repository to CNB.cool\n# 将当前仓库的所有分支和标签同步到 cnb.cool 远程仓库\nname: Mirror to CNB\n\non:\n  push:\n    branches:\n      - '**'       # 所有分支的推送都会触发\n    tags:\n      - '**'       # 所有标签的推送都会触发\n  delete:           # 分支或标签删除时也同步\n\njobs:\n  mirror:\n    name: Mirror to CNB.cool\n    if: github.actor == 'haierkeys'\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0  # 获取完整的 git 历史记录，包括所有分支和标签\n\n      - name: Push to CNB mirror\n        env:\n          CNB_USERNAME: ${{ secrets.CNB_USERNAME }}\n          CNB_TOKEN: ${{ secrets.CNB_TOKEN }}\n        run: |\n          # 设置 CNB 远程仓库地址（使用认证信息）\n          # Set CNB remote URL with authentication\n          git remote add cnb \"https://${CNB_USERNAME}:${CNB_TOKEN}@cnb.cool/haierkeys/fast-note-sync-service.git\"\n\n          # 推送所有分支和标签到 CNB 远程仓库\n          # Push all branches and tags to CNB mirror\n          git push cnb --all --force\n          git push cnb --tags --force\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Go-Release\n\n# 触发条件配置\non:\n  push:\n    # 针对 master 分支的普通提交也触发\n    branches:\n      - \"master\"\n    paths:\n      - \"internal/app/version.go\"\n\n# 权限配置：允许脚本写入仓库内容（用于发布 Release）\npermissions:\n  contents: write\n  packages: write\n\nenv:\n  NAME: ${{ github.event.repository.name }}\n\njobs:\n  check-version:\n    if: github.actor == 'haierkeys'\n    runs-on: ubuntu-latest\n    outputs:\n      should_release: ${{ steps.check.outputs.should_release }}\n      release_tag: ${{ steps.check.outputs.release_tag }}\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          ref: ${{ github.ref }}\n          fetch-depth: 0\n\n      - name: Check Version\n        id: check\n        run: |\n          # 从 internal/app/version.go (HEAD) 读取当前版本\n          # 预期格式: var Version string = \"0.0.1\"\n          CURRENT_VERSION=$(grep -E 'Version\\s+string' internal/app/version.go | awk -F '\"' '{print $2}')\n          echo \"当前版本 (HEAD): $CURRENT_VERSION\"\n\n          # 从 internal/app/version.go (HEAD~1) 获取上一个版本\n          # 使用 git show 获取上一次提交的文件内容\n          # 注意: 我们将其包裹在一个块中以处理 HEAD~1 可能不存在的情况 (例如: 初始提交)\n          if git rev-parse --verify HEAD~1 >/dev/null 2>&1; then\n             PREV_FILE_CONTENT=$(git show HEAD~1:internal/app/version.go)\n             PREV_VERSION=$(echo \"$PREV_FILE_CONTENT\" | grep -E 'Version\\s+string' | awk -F '\"' '{print $2}')\n          else\n             PREV_VERSION=\"0.0.0\"\n          fi\n\n          # 如果 PREV_VERSION 为空 (例如: grep 失败), 默认为 0.0.0\n          if [ -z \"$PREV_VERSION\" ]; then\n             PREV_VERSION=\"0.0.0\"\n          fi\n\n          echo \"上一个版本 (HEAD~1): $PREV_VERSION\"\n\n          # 规范化版本以进行比较 (如果存在 'v' 前缀则移除)\n          V_LAST=${PREV_VERSION#v}\n          V_CURR=${CURRENT_VERSION#v}\n\n          # 比较版本\n          if [ \"$V_CURR\" != \"$V_LAST\" ]; then\n             # 使用 sort -V 确定当前版本是否严格大于旧版本\n             NEWER_VERSION=$(echo -e \"$V_LAST\\n$V_CURR\" | sort -V | tail -n 1)\n\n             if [ \"$NEWER_VERSION\" == \"$V_CURR\" ] && [ \"$NEWER_VERSION\" != \"$V_LAST\" ]; then\n                echo \"检测到新版本: $V_CURR > $V_LAST\"\n                echo \"should_release=true\" >> $GITHUB_OUTPUT\n                echo \"release_tag=$V_CURR\" >> $GITHUB_OUTPUT\n             else\n                echo \"版本 $V_CURR 不大于 $V_LAST。跳过发布...\"\n                echo \"should_release=false\" >> $GITHUB_OUTPUT\n             fi\n          else\n             echo \"版本匹配 ($V_CURR == $V_LAST)。跳过发布...\"\n             echo \"should_release=false\" >> $GITHUB_OUTPUT\n          fi\n\n  prepare-message:\n    needs: check-version\n    if: needs.check-version.outputs.should_release == 'true' && github.actor == 'haierkeys'\n    runs-on: ubuntu-latest\n    outputs:\n      commit_msg: ${{ steps.trans.outputs.commit_msg }}\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          ref: ${{ github.ref }}\n          fetch-depth: 0\n\n      - name: Prepare Commit Message\n        id: trans\n        run: |\n          # 获取提交文本信息\n          msg=$(git log -1 --pretty=%B)\n\n          # 设置 Python 进行翻译\n          pip install deep-translator > /dev/null 2>&1 || true\n\n          # 运行翻译脚本\n          export COMMIT_MSG=\"$msg\"\n\n          echo \"commit_msg<<EOF\" >> $GITHUB_OUTPUT\n          if [ -f \"scripts/translate_commit.py\" ]; then\n             python3 scripts/translate_commit.py >> $GITHUB_OUTPUT\n          else\n             echo \"未找到翻译脚本，使用原始消息\"\n             echo \"$msg\" >> $GITHUB_OUTPUT\n          fi\n          echo \"EOF\" >> $GITHUB_OUTPUT\n\n  create-release:\n    needs: [check-version, prepare-message]\n    if: needs.check-version.outputs.should_release == 'true' && github.actor == 'haierkeys'\n    name: Create Release\n    runs-on: ubuntu-latest\n    outputs:\n      upload_url: ${{ steps.create_release.outputs.upload_url }}\n    steps:\n      - name: Create Release\n        id: create_release\n        uses: softprops/action-gh-release@v2\n        env:\n          token: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          tag_name: ${{ needs.check-version.outputs.release_tag }}\n          name: ${{ needs.check-version.outputs.release_tag }}\n          draft: false\n          prerelease: false\n          generate_release_notes: true\n          body: ${{ needs.prepare-message.outputs.commit_msg }}\n          target_commitish: ${{ github.sha }} # 明确指定在当前分支的 commit 上创建 tag\n          overwrite_files: true\n\n  build-binaries:\n    needs: [check-version, prepare-message, create-release]\n    if: needs.check-version.outputs.should_release == 'true' && github.actor == 'haierkeys'\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          ref: ${{ github.ref }}\n          fetch-depth: 0\n\n\n      - uses: actions/setup-go@v6\n        with:\n          go-version-file: 'go.mod'\n\n      - name: Check Go Version\n        run: go version\n\n      - name: Go Build Prepare\n        run: go install github.com/mitchellh/gox@latest\n\n      - name: Go Build Multi-platform\n        env:\n          COMMIT_MSG: ${{ needs.prepare-message.outputs.commit_msg }}\n        run: make gox-all\n\n      - name: Create Changelog\n        env:\n          COMMIT_MSG: ${{ needs.prepare-message.outputs.commit_msg }}\n        run: |\n          echo \"${COMMIT_MSG}\" > ./build/changelog.txt\n\n      - name: Create GZip Archives for All Platforms\n        env:\n          RELEASE_BASENAME: ${{ env.NAME }}-${{ needs.check-version.outputs.release_tag }}\n        run: |\n          # 为所有平台创建 tar.gz 包\n          # Create tar.gz packages for all platforms\n          declare -a PLATFORMS=(\"darwin_amd64\" \"darwin_arm64\" \"linux_amd64\" \"linux_arm64\" \"linux_arm\" \"windows_amd64\")\n          for PLATFORM in \"${PLATFORMS[@]}\"; do\n            GOOS=$(echo \"$PLATFORM\" | cut -d'_' -f1)\n            GOARCH=$(echo \"$PLATFORM\" | cut -d'_' -f2)\n\n            # 这里的重命名逻辑：如果是 linux_arm，则将后缀改为 armv7l\n            # Rename linux_arm to linux-armv7l for easier identification\n            TARGET_ARCH=$GOARCH\n            if [ \"$PLATFORM\" == \"linux_arm\" ]; then\n                TARGET_ARCH=\"armv7l\"\n            fi\n\n            ARCHIVE_NAME=\"${RELEASE_BASENAME}-${GOOS}-${TARGET_ARCH}.tar.gz\"\n            echo \"Creating archive: ${ARCHIVE_NAME}\"\n            tar -czvf \"./build/${ARCHIVE_NAME}\" ./config -C \"./build/${PLATFORM}/\" .\n          done\n\n      - name: Upload Build Artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: build_file\n          path: ./build/\n\n      - name: Upload Config Artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: config\n          path: ./config\n\n      - name: Upload Release Archives\n        uses: actions/upload-artifact@v4\n        with:\n          name: release_archives\n          path: |\n            ./build/${{ env.NAME }}-${{ needs.check-version.outputs.release_tag }}-*.tar.gz\n            ./build/changelog.txt\n\n  push-docker:\n    needs: [check-version, build-binaries,create-release]\n    if: needs.check-version.outputs.should_release == 'true' && github.actor == 'haierkeys'\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Download Build Artifacts\n        uses: actions/download-artifact@v4\n        with:\n          name: build_file\n          path: ./build/\n\n      - name: Download Config Artifacts\n        uses: actions/download-artifact@v4\n        with:\n          name: config\n          path: ./config\n\n      - name: Set Environment Variables\n        run: |\n          # NAME is already set globally\n          # Use the tag from check-version\n          TAG_VERSION=${{ needs.check-version.outputs.release_tag }}\n          echo \"TAG_VERSION=${TAG_VERSION}\" >> ${GITHUB_ENV}\n          echo \"IMAGE_TAG=${TAG_VERSION}\" >> ${GITHUB_ENV}\n          echo \"BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')\" >> ${GITHUB_ENV}\n          echo \"GIT_COMMIT=$(git rev-parse --short HEAD)\" >> ${GITHUB_ENV}\n\n      - name: Append 'latest' tag if on main branch\n        if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'\n        run: echo \"IMAGE_TAG=${{ env.IMAGE_TAG }},latest\" >> ${GITHUB_ENV}\n\n      - uses: docker/setup-qemu-action@v2\n      - uses: docker/setup-buildx-action@v2\n\n      - name: Docker Build & Publish to GitHub Container Registry\n        uses: elgohr/Publish-Docker-Github-Action@v5\n        with:\n          dockerfile: docker/Dockerfile\n          name: ${{ github.actor }}/${{ env.NAME }}\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n          platforms: linux/amd64,linux/arm64,linux/arm/v7\n          registry: ghcr.io\n          snapshot: false\n          tags: \"${{ env.IMAGE_TAG }}\"\n          buildargs: |\n            VERSION=${{ env.TAG_VERSION }}\n            BUILD_DATE=${{ env.BUILD_DATE }}\n            GIT_COMMIT=${{ env.GIT_COMMIT }}\n\n      - name: Docker Build & Publish to DockerHub\n        uses: elgohr/Publish-Docker-Github-Action@v5\n        with:\n          dockerfile: docker/Dockerfile\n          name: ${{ github.actor }}/${{ env.NAME }}\n          username: ${{ github.actor }}\n          password: ${{ secrets.DOCKERHUB_TOKEN }}\n          platforms: linux/amd64,linux/arm64,linux/arm/v7\n          snapshot: false\n          tags: \"${{ env.IMAGE_TAG }}\"\n          buildargs: |\n            VERSION=${{ env.TAG_VERSION }}\n            BUILD_DATE=${{ env.BUILD_DATE }}\n            GIT_COMMIT=${{ env.GIT_COMMIT }}\n\n      - name: Docker Build & Publish to CNB\n        uses: elgohr/Publish-Docker-Github-Action@v5\n        with:\n          dockerfile: docker/Dockerfile\n          name: haierkeys/${{ env.NAME }}\n          username: ${{ secrets.CNB_USERNAME }}\n          password: ${{ secrets.CNB_TOKEN }}\n          platforms: linux/amd64,linux/arm64,linux/arm/v7\n          registry: docker.cnb.cool\n          snapshot: false\n          tags: \"${{ env.IMAGE_TAG }}\"\n          buildargs: |\n            VERSION=${{ env.TAG_VERSION }}\n            BUILD_DATE=${{ env.BUILD_DATE }}\n            GIT_COMMIT=${{ env.GIT_COMMIT }}\n\n  push-release-files:\n    needs: [create-release, build-binaries, check-version]\n    if: needs.check-version.outputs.should_release == 'true'\n    runs-on: ubuntu-latest\n    steps:\n      - name: Download Release Archives\n        uses: actions/download-artifact@v4\n        with:\n          name: release_archives\n          path: ./archives/\n\n      - name: Upload Archives to GitHub Release\n        uses: softprops/action-gh-release@v2\n        with:\n          tag_name: ${{ needs.check-version.outputs.release_tag }}\n          files: ./archives/*\n\n  push-cnb-release:\n    needs: [check-version, prepare-message, build-binaries, create-release]\n    if: needs.check-version.outputs.should_release == 'true'\n    name: Push CNB Release\n    runs-on: ubuntu-latest\n    env:\n      CNB_REPO: haierkeys/fast-note-sync-service\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          ref: ${{ github.ref }}\n          fetch-depth: 0\n\n      - name: Push Tag to CNB\n        env:\n          CNB_TOKEN: ${{ secrets.CNB_TOKEN }}\n          RELEASE_TAG: ${{ needs.check-version.outputs.release_tag }}\n        run: |\n          # 确保拉取了最新的 tag 信息\n          git fetch --tags\n          git remote add cnb \"https://cnb:${CNB_TOKEN}@cnb.cool/${CNB_REPO}.git\"\n          # 直接推送已存在的 tag\n          git push cnb \"${RELEASE_TAG}\" --force\n\n      - name: Download Release Archives\n        uses: actions/download-artifact@v4\n        with:\n          name: release_archives\n          path: ./archives/\n\n      - name: Create CNB Release and Upload Assets\n        env:\n          CNB_TOKEN: ${{ secrets.CNB_TOKEN }}\n          RELEASE_TAG: ${{ needs.check-version.outputs.release_tag }}\n          RELEASE_BODY: ${{ needs.prepare-message.outputs.commit_msg }}\n        run: |\n          set -e\n\n          # ============================================================\n          # 第一步: 创建 CNB Release\n          # Step 1: Create CNB Release\n          # ============================================================\n          echo \"::group::Creating CNB Release: ${RELEASE_TAG}\"\n          RELEASE_RESPONSE=$(curl -s -w \"\\n%{http_code}\" -X POST \\\n            -H \"Accept: application/vnd.cnb.api+json\" \\\n            -H \"Authorization: Bearer ${CNB_TOKEN}\" \\\n            -H \"Content-Type: application/json\" \\\n            -d \"$(jq -n \\\n              --arg tag \"${RELEASE_TAG}\" \\\n              --arg name \"${RELEASE_TAG}\" \\\n              --arg body \"${RELEASE_BODY}\" \\\n              '{tag_name: $tag, name: $name, body: $body, prerelease: false, draft: false}')\" \\\n            \"https://api.cnb.cool/${CNB_REPO}/-/releases\")\n\n          HTTP_CODE=$(echo \"$RELEASE_RESPONSE\" | tail -1)\n          RESPONSE_BODY=$(echo \"$RELEASE_RESPONSE\" | sed '$d')\n\n          if [ \"$HTTP_CODE\" -ge 200 ] && [ \"$HTTP_CODE\" -lt 300 ]; then\n            RELEASE_ID=$(echo \"$RESPONSE_BODY\" | jq -r '.id')\n            echo \"CNB Release created successfully, ID: ${RELEASE_ID}\"\n          else\n            echo \"::warning::Failed to create CNB Release (HTTP ${HTTP_CODE}), attempting to fetch existing release...\"\n            # 尝试获取已存在的 Release / Try to get existing release\n            LIST_RESPONSE=$(curl -s \\\n              -H \"Accept: application/vnd.cnb.api+json\" \\\n              -H \"Authorization: Bearer ${CNB_TOKEN}\" \\\n              \"https://api.cnb.cool/${CNB_REPO}/-/releases\")\n            RELEASE_ID=$(echo \"$LIST_RESPONSE\" | jq -r \".[] | select(.tag_name==\\\"${RELEASE_TAG}\\\") | .id\")\n            if [ -z \"$RELEASE_ID\" ] || [ \"$RELEASE_ID\" = \"null\" ]; then\n              echo \"::error::Cannot create or find CNB Release for tag ${RELEASE_TAG}\"\n              exit 1\n            fi\n            echo \"Found existing CNB Release, ID: ${RELEASE_ID}\"\n          fi\n          echo \"::endgroup::\"\n\n          # ============================================================\n          # 第二步: 并行上传所有已打包的构建产物\n          # Step 2: Parallel upload all pre-built release archives\n          # ============================================================\n          for FILE_PATH in ./archives/*; do\n            (\n              FILE_NAME=$(basename \"$FILE_PATH\")\n\n              echo \"Uploading ${FILE_NAME}...\"\n              FILE_SIZE=$(stat -c%s \"$FILE_PATH\")\n\n              # 2a. 获取上传 URL / Get upload URL\n              UPLOAD_RESPONSE=$(curl -s -X POST \\\n                -H \"Accept: application/vnd.cnb.api+json\" \\\n                -H \"Authorization: Bearer ${CNB_TOKEN}\" \\\n                -H \"Content-Type: application/json\" \\\n                -d \"$(jq -n \\\n                  --arg name \"${FILE_NAME}\" \\\n                  --argjson size ${FILE_SIZE} \\\n                  '{asset_name: $name, size: $size, overwrite: true}')\" \\\n                \"https://api.cnb.cool/${CNB_REPO}/-/releases/${RELEASE_ID}/asset-upload-url\")\n\n              UPLOAD_URL=$(echo \"$UPLOAD_RESPONSE\" | jq -r '.upload_url')\n              VERIFY_URL=$(echo \"$UPLOAD_RESPONSE\" | jq -r '.verify_url')\n\n              if [ -z \"$UPLOAD_URL\" ] || [ \"$UPLOAD_URL\" = \"null\" ]; then\n                echo \"::error::Failed to get upload URL for ${FILE_NAME}\"\n                echo \"Response: ${UPLOAD_RESPONSE}\"\n                exit 1\n              fi\n\n              # 2b. 上传文件内容 / Upload file content\n              echo \"Uploading ${FILE_NAME} to: ${UPLOAD_URL}\"\n              CONTENT_TYPE=\"application/gzip\"\n              if [[ \"${FILE_NAME}\" == *.txt ]]; then\n                CONTENT_TYPE=\"text/plain\"\n              fi\n\n              UPLOAD_RESULT=$(curl -s -w \"\\n%{http_code}\" -X PUT \\\n                -H \"Content-Type: ${CONTENT_TYPE}\" \\\n                --data-binary @\"$FILE_PATH\" \\\n                \"$UPLOAD_URL\")\n              UPLOAD_HTTP_CODE=$(echo \"$UPLOAD_RESULT\" | tail -1)\n\n              if [ \"$UPLOAD_HTTP_CODE\" -ne 200 ] && [ \"$UPLOAD_HTTP_CODE\" -ne 201 ]; then\n                echo \"::error::Failed to upload ${FILE_NAME} (HTTP ${UPLOAD_HTTP_CODE})\"\n                exit 1\n              fi\n\n              # 2c. 确认上传完成 / Confirm upload\n              echo \"Confirming upload for ${FILE_NAME}...\"\n              CONFIRM_RESULT=$(curl -s -w \"\\n%{http_code}\" -X POST \\\n                -H \"Accept: application/vnd.cnb.api+json\" \\\n                -H \"Authorization: Bearer ${CNB_TOKEN}\" \\\n                \"${VERIFY_URL}\")\n              CONFIRM_HTTP_CODE=$(echo \"$CONFIRM_RESULT\" | tail -1)\n\n              if [ \"$CONFIRM_HTTP_CODE\" -ne 200 ] && [ \"$CONFIRM_HTTP_CODE\" -ne 201 ]; then\n                echo \"::error::Failed to confirm upload for ${FILE_NAME} (HTTP ${CONFIRM_HTTP_CODE})\"\n                exit 1\n              fi\n\n              echo \"✅ ${FILE_NAME} uploaded successfully\"\n            ) &\n          done\n\n          # 等待所有后台任务完成\n          wait\n\n          echo \"🎉 All assets uploaded to CNB Release!\""
  },
  {
    "path": ".gitignore",
    "content": "# -- Composer -----------------------------------------\n/composer.phar\n/composer.lock\n\n# -- Editores -----------------------------------------\n# vim\n.*.sw[a-z]\n*.un~\nSession.vim\n.netrwhist\n\n# vscode\n/.vscode\n.vscode\n\n# eclipse\n*.pydevproject\n.project\n.metadata\ntmp/**\ntmp/**/*\n*.tmp\n*.bak\n*.swp\n*~.nib\nlocal.properties\n.classpath\n.settings/\n.loadpath\n.externalToolBuilders/\n*.launch\n.buildpath\n\n# phpstorm\n.idea\n\n# textmate\n*.tmproj\n*.tmproject\ntmtags\n\n# sublimetext\n/*.sublime-project\n*.sublime-workspace\n\n# netbeans\nnbproject/private/\nbuild/\nnbbuild/\ndist/\nnbdist/\nnbactions.xml\nnb-configuration.xml\n\n# -- Sistemas Operativos ------------------------------\n# Windows\nThumbs.db\nehthumbs.db\nDesktop.ini\n$RECYCLE.BIN/\n\n# Linux\n!.gitignore\n!.htaccess\n!.env\n*~\n\n# Mac OS X\n.DS_Store\n.AppleDouble\n.LSOverride\nIcon\n._*\n.Spotlight-V100\n.Trashes\n\n# -- Project -----------------------------------------\n/build\n/storage/logs\n/storage/uploads\n/storage/\n/config/config-dev.yaml\n/config/lastVersion\n\ndocs/*_打赏 链接小票单记录*.csv\n# Claude Code\nCLAUDE.md\n.claude/\n\n# Python virtual environment\n.venv/\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2024-2026 HaierKeys\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "Makefile",
    "content": "# Docker 登录示例\n# docker login --username=xxxxxx registry.cn-shanghai.aliyuncs.com\n\n# 环境变量（可选）\n# include .env\n# export $(shell sed 's/=.*//' .env)\n\n# -------------------------\n# 项目 / 镜像 配置\n# -------------------------\nREPO = $(eval REPO := $$(shell go list -f '{{.ImportPath}}' .))$(value REPO)\n\nDockerHubUser   = haierkeys\nDockerHubName   = fast-note-sync-service\n\nReleaseTagPre   = release-v\nDevelopTagPre   = develop-v\n\nP_NAME          = fast-note-sync\nP_BIN           = fast-note-sync-service\n\n# -------------------------\n# Git / 构建信息\n# -------------------------\nGitTag          = $(shell git describe --tags --abbrev=0)\nGitVersion      = $(shell git log -1 --format=%h)\nGitVersionDesc  = $(shell git log -1 --format=%s)\nBuildTime       = $(shell date +%FT%T%z)\n\n# LDFLAGS: 注入版本信息到二进制\n# 获取提交信息，并处理掉换行符（用 @@@ 占位）以便安全地注入到 ldflags\nCOMMIT_MSG_CLEAN = $(shell echo \"$(COMMIT_MSG)\" | tr '\\n' '^' | sed 's/\\^/@@@/g' | sed 's/\"/\\\\\"/g')\nChangelog       ?= $(COMMIT_MSG_CLEAN)\nLDFLAGS = -ldflags '-X ${REPO}/internal/app.Version=$(GitTag) -X \"${REPO}/internal/app.GitTag=$(GitVersion)\" -X ${REPO}/internal/app.BuildTime=$(BuildTime) -X \"${REPO}/internal/app.Changelog=$(Changelog)\"'\n\n# go 命令封装\ngob = go build ${LDFLAGS}\ngor = go run ${LDFLAGS}\n\n# 编译相关\nCGO = CGO_ENABLED=0\n\nrootDir = $(shell pwd)\nbuildDir = $(rootDir)/build\n\n# -------------------------\n# PHONY 目标\n# -------------------------\n.PHONY: all  build-all run test clean \\\n        push-online push-dev \\\n        build-macos-amd64 build-macos-arm64 build-linux-amd64 \\\n        build-linux-arm64 build-linux-arm build-windows-amd64 gox-linux gox-all \\\n\t\tdocs fmt update air dev ver gen sup\n\n# 默认目标\nall: test build-all\n\n# -------------------------\n# 简单目标\n# -------------------------\nsup:\n\tnode scripts/process_support_csv.js\n\t@if [ ! -d \".venv\" ]; then python3 -m venv .venv; fi\n\t.venv/bin/pip install -q deep-translator\n\t.venv/bin/python scripts/process_support.py\n\tnode scripts/gen_support_md.js\n\nsup-md:\n\tnode scripts/gen_support_md.js\ntest:\n\tgo test $$(go list ./... | grep -v -E 'internal/service/mocks|internal/domain/mocks|internal/dto|internal/model|internal/query|internal/config|internal/app|/docs|internal/middleware|cmd')\n\ndev:\n\tair -c ./scripts/.air.toml\n\nair:\n\tair -c ./scripts/.air.toml\n\nfmt:\n\tgo fmt ./...\n\nupdate:\n\tgo get -u ./...\n\n# 更新版本脚本调用\nver:\n\t@node ./scripts/update-version.js $(filter-out $@,$(MAKECMDGOALS))\n\n# 捕获 ver 后面的参数，防止 make 将其视为目标\n%:\n\t@:\n\ngen:\n\tgo run -v ./cmd/gorm_gen/gen.go -type sqlite -dsn storage/database/db.sqlite3\n\tgo run -v ./cmd/model_gen/gen.go\n\ndocs:\n\tgo run github.com/swaggo/swag/cmd/swag@latest init -g main.go -o ./docs --parseDependency --parseInternal\n\n# 运行\nrun:\n#\t$(call checkStatic)\n\t$(call init)\n\t$(gor) -v $(rootDir)\n\nclean:\n\trm -rf $(buildDir)\n\n# -------------------------\n# 构建集合\n# -------------------------\nbuild-all:\n#\t$(call checkStatic)\n\t$(MAKE) build-macos-amd64\n\t$(MAKE) build-macos-arm64\n\t$(MAKE) build-linux-amd64\n\t$(MAKE) build-linux-arm64\n\t$(MAKE) build-linux-arm\n\t$(MAKE) build-windows-amd64\n\n# macOS\nbuild-macos-amd64:\n\t$(CGO) GOOS=darwin GOARCH=amd64 $(gob) -o $(buildDir)/darwin_amd64/${P_BIN} $(bin) -v $(rootDir)\n\nbuild-macos-arm64:\n\t$(CGO) GOOS=darwin GOARCH=arm64 $(gob) -o $(buildDir)/darwin_arm64/${P_BIN} -v $(rootDir)\n\n# Linux\nbuild-linux-amd64:\n# CGO_ENABLED=1 CC=musl-gcc  GOOS=linux GOARCH=amd64 $(gob) -o $(buildDir)/linux_amd64/${P_BIN} -v $(rootDir)\n\t$(CGO) GOOS=linux GOARCH=amd64 $(gob) -o $(buildDir)/linux_amd64/${P_BIN} -v $(rootDir)\n\nbuild-linux-arm64:\n\t$(CGO) GOOS=linux GOARCH=arm64 $(gob) -o $(buildDir)/linux_arm64/${P_BIN} -v $(rootDir)\n\nbuild-linux-arm:\n\t$(CGO) GOOS=linux GOARCH=arm GOARM=7 $(gob) -o $(buildDir)/linux_arm/${P_BIN} -v $(rootDir)\n\n# Windows\nbuild-windows-amd64:\n# CGO_ENABLED=0 CGO_ENABLED=1 GOOS=windows GOARCH=amd64 CC=\"x86_64-w64-mingw32-gcc -fno-stack-protector -D_FORTIFY_SOURCE=0 -lssp\" $(gob) -o $(bin).exe -v $(rootDir)\n\t$(CGO) GOOS=windows GOARCH=amd64 $(gob) -o $(buildDir)/windows_amd64/${P_BIN}.exe -v $(rootDir)\n\n# gox 辅助\ngox-linux:\n\t$(CGO) GOARM=7 gox ${LDFLAGS} -osarch=\"linux/amd64 linux/arm64 linux/arm\" -output=\"$(buildDir)/{{.OS}}_{{.Arch}}/${P_BIN}\"\n\ngox-all:\n\t$(CGO) GOARM=7 gox ${LDFLAGS} -osarch=\"darwin/amd64 darwin/arm64 linux/amd64 linux/arm64 linux/arm windows/amd64\" -output=\"$(buildDir)/{{.OS}}_{{.Arch}}/${P_BIN}\"\n\n\n# -------------------------\n# Docker 发布\n# -------------------------\npush-online: build-linux\n\t$(call dockerImageClean)\n\tdocker build --platform linux/amd64 -t $(DockerHubUser)/$(DockerHubName):latest -f docker/Dockerfile .\n\tdocker tag  $(DockerHubUser)/$(DockerHubName):latest $(DockerHubUser)/$(DockerHubName):$(ReleaseTagPre)$(GitTag)\n\tdocker push $(DockerHubUser)/$(DockerHubName):$(ReleaseTagPre)$(GitTag)\n\tdocker push $(DockerHubUser)/$(DockerHubName):latest\n\npush-dev: build-linux\n\t$(call dockerImageClean)\n\tdocker build --platform linux/amd64 -t $(DockerHubUser)/$(DockerHubName):dev-latest -f docker/Dockerfile .\n\tdocker tag $(DockerHubUser)/$(DockerHubName):dev-latest $(DockerHubUser)/$(DockerHubName):$(DevelopTagPre)$(GitTag)\n\tdocker push $(DockerHubUser)/$(DockerHubName):$(DevelopTagPre)$(GitTag)\n\tdocker push $(DockerHubUser)/$(DockerHubName):dev-latest\n\n# -------------------------\n# 代码片段（定义）\n# -------------------------\ndefine dockerImageClean\n\t@echo \"docker Image Clean\"\n\tbash docker_image_clean.sh\nendef\n\ndefine init\n\t@echo \"Build Init\"\nendef\n"
  },
  {
    "path": "README.md",
    "content": "[简体中文](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.zh-CN.md) / [English](https://github.com/haierkeys/fast-note-sync-service/blob/master/README.md) / [日本語](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.ja.md) / [한국어](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.ko.md) / [繁體中文](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.zh-TW.md)\n\nFor any questions, please create a new [issue](https://github.com/haierkeys/fast-note-sync-service/issues/new), or join the Telegram chat group for help: [https://t.me/obsidian_users](https://t.me/obsidian_users)\n\nFor mainland China, the Tencent `cnb.cool` mirror repository is recommended: [https://cnb.cool/haierkeys/fast-note-sync-service](https://cnb.cool/haierkeys/fast-note-sync-service)\n\n\n<h1 align=\"center\">Fast Note Sync Service</h1>\n\n<p align=\"center\">\n    <a href=\"https://github.com/haierkeys/fast-note-sync-service/releases\"><img src=\"https://img.shields.io/github/release/haierkeys/fast-note-sync-service?style=flat-square\" alt=\"release\"></a>\n    <a href=\"https://github.com/haierkeys/fast-note-sync-service/releases\"><img src=\"https://img.shields.io/github/v/tag/haierkeys/fast-note-sync-service?label=release-alpha&style=flat-square\" alt=\"alpha-release\"></a>\n    <a href=\"https://github.com/haierkeys/fast-note-sync-service/blob/master/LICENSE\"><img src=\"https://img.shields.io/github/license/haierkeys/fast-note-sync-service?style=flat-square\" alt=\"license\"></a>\n    <img src=\"https://img.shields.io/badge/Language-Go-00ADD8?style=flat-square\" alt=\"Go\">\n</p>\n\n<p align=\"center\">\n  <strong>High-performance, low-latency note syncing, online management, and remote REST API service platform</strong>\n  <br>\n  <em>Built with Golang + Websocket + React</em>\n</p>\n\n<p align=\"center\">\n  Data provision requires the use of the client plugin: <a href=\"https://github.com/haierkeys/obsidian-fast-note-sync\">Obsidian Fast Note Sync Plugin</a>\n</p>\n\n<div align=\"center\">\n  <div align=\"center\">\n    <a href=\"/docs/images/vault.png\"><img src=\"/docs/images/vault.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n    <a href=\"/docs/images/attach.png\"><img src=\"/docs/images/attach.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n    </div>\n  <div align=\"center\">\n    <a href=\"/docs/images/note.png\"><img src=\"/docs/images/note.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n    <a href=\"/docs/images/setting.png\"><img src=\"/docs/images/setting.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n  </div>\n</div>\n\n---\n\n## 🎯 Core Features\n\n* **🧰 Native MCP (Model Context Protocol) Support**:\n  * `FNS` can act as an MCP server connecting to `Cherry Studio`, `Cursor`, and other compatible AI clients. This grants AI the ability to read and write your private notes and attachments, with all changes syncing to all clients in real time.\n* **🚀 REST API Support**:\n  * Provides standard REST API interfaces, supporting automated program access (e.g., automation scripts, AI assistant integration) for CRUD operations on Obsidian notes.\n  * For more details, please refer to the [RESTful API Documentation](/docs/REST_API.md) or [OpenAPI Documentation](/docs/swagger.yaml).\n* **💻 Web Management Panel**:\n  * Built-in modern management interface to easily create users, generate plugin configurations, and manage vaults and note contents.\n* **🔄 Multi-device Note Syncing**:\n  * Supports automatic **Vault** creation.\n  * Supports note management (Add, Delete, Modify, Read) with millisecond-level real-time distribution of changes to all online devices.\n* **🖼️ Attachment Syncing Support**:\n  * Perfect support for syncing non-note files like images.\n  * Supports chunked upload and download of large attachments, with configurable chunk sizes, improving syncing efficiency.\n* **⚙️ Configuration Syncing**:\n  * Supports syncing `.obsidian` configuration files.\n  * Supports syncing `PDF` progress states.\n* **📝 Note History**:\n  * Ability to view historical modification versions of each note via the Web page and plugin client.\n  * (Requires Server v1.2+)\n* **🗑️ Recycle Bin**:\n  * Supports automatic transfer of notes to the recycle bin upon deletion.\n  * Supports recovering notes from the recycle bin. (Attachment recovery features will be added progressively).\n\n* **🚫 Offline Sync Strategy**:\n  * Supports automatic merging of offline note edits. (Requires setup on the plugin client).\n  * Offline deletion automatically synchronizes with server padding or deletions after reconnection. (Requires setup on the plugin client).\n\n* **🔗 Share Feature**:\n  * Ability to Create/Cancel note sharing.\n  * Automatically parses attachments such as images, audio, and video referenced in shared notes.\n  * Provides sharing access statistics.\n  * Ability to set a password for shared notes.\n  * Ability to generate short links for shared notes.\n* **📂 Directory Syncing**:\n  * Supports Create/Rename/Move/Delete syncing for folders.\n\n* **🌳 Git Automation**:\n  * Automatically updates and pushes to the remote Git repository when attachments or notes undergo changes.\n  * Automatically releases system memory after the task strictly finishes.\n\n* **☁️ Multi-Storage Backup & One-way Mirror Syncing**:\n  * Adapts to S3/OSS/R2/WebDAV/Local and other storage protocols.\n  * Supports full/incremental ZIP scheduled archive backups.\n  * Supports one-way mirror syncing of Vault resources to remote storage.\n  * Automatically cleans up expired backups, with support for custom retention days.\n\n* **🗄️ Multi-Database Support**:\n  * Natively supports mainstream databases such as SQLite, MySQL, PostgreSQL, meeting deployment needs ranging from individuals to teams.\n\n## ☕ Sponsorship & Support\n\n- If you find this plugin useful and want its development to continue, please support me via the following channels:\n\n  | Ko-fi *Non-China Region*                                                                               |    | WeChat QR Donation *China Region*                        |\n  |--------------------------------------------------------------------------------------------------|----|------------------------------------------------|\n  | [<img src=\"/docs/images/kofi.png\" alt=\"BuyMeACoffee\" height=\"150\">](https://ko-fi.com/haierkeys) | OR | <img src=\"/docs/images/wxds.png\" height=\"150\"> |\n\n  - Supported List:\n    - <a href=\"https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/Support.en.md\">Support.en.md</a>\n    - <a href=\"https://cnb.cool/haierkeys/fast-note-sync-service/-/blob/master/docs/Support.en.md\">Support.en.md (cnb.cool Mirror)</a>\n\n## ⏱️ Changelog\n\n- ♨️ [View Changelog](/docs/CHANGELOG.en.md)\n\n## 🗺️ Roadmap\n\n- [ ] Add **Mock** testing covering all levels.\n- [ ] Add WebSocket `Protobuf` transmission format support, enhancing synchronization efficiency.\n- [ ] The backend to include queries for various operational logs such as sync logs and operation logs.\n- [ ] Isolate and optimize the current authorization mechanism to elevate overall security.\n- [ ] Add WebGui note real-time update capability.\n- [ ] Add client Peer-to-Peer message transmission (non-note & attachments, similar to localsend; not saved closely on the client, saves to the server).\n- [ ] Enhance various help documents.\n- [ ] Support more Intranet Penetration (Relay gateway).\n- [ ] Quick deployment plan:\n  * Deploy FNS Server securely with just the server's public IP address and account credentials.\n- [ ] Optimize the current offline note merging scheme and introduce conflict-handling mechanisms.\n\nWe are continually improving. Here are our future development plans:\n\n> **If you have improvement suggestions or new ideas, please submit an issue to share them with us. We will sincerely evaluate and adopt appropriate suggestions.**\n\n## 🚀 Quick Deployment\n\nWe offer multiple installation methods. We recommend utilizing the **One-click Script** or **Docker**.\n\n### Method 1: One-click Script (Recommended)\n\nAutomatically detects the system environment, completes the installation, and registers the service.\n\n```bash\nbash <(curl -fsSL https://raw.githubusercontent.com/haierkeys/fast-note-sync-service/master/scripts/quest_install.sh)\n```\n\nUsers in China can utilize the Tencent `cnb.cool` mirror source:\n```bash\nbash <(curl -fsSL https://cnb.cool/haierkeys/fast-note-sync-service/-/git/raw/master/scripts/quest_install.sh) --cnb\n```\n\n**Main Script Actions:**\n\n  * Automatically downloads the optimal Release binary file for your system.\n  * Default installation path is `/opt/fast-note`, creating a global quick command abstractly named `fns` in `/usr/local/bin/fns`.\n  * Configures and launches Systemd (Linux) or Launchd (macOS) services to realize auto-start on boot.\n  * **Management Commands**: `fns [install|uninstall|start|stop|status|update|menu]`\n  * **Interactive Menu**: Run `fns` directly to enter an interactive menu enabling installation/upgrade, service control, auto-start configuration, and switching between GitHub / CNB mirrors.\n\n-----\n\n### Method 2: Docker Deployment\n\n#### Docker Run\n\n```bash\n# 1. Pull the image\ndocker pull haierkeys/fast-note-sync-service:latest\n\n# 2. Start the container\ndocker run -tid --name fast-note-sync-service \\\n    -p 9000:9000 \\\n    -v /data/fast-note-sync/storage/:/fast-note-sync/storage/ \\\n    -v /data/fast-note-sync/config/:/fast-note-sync/config/ \\\n    haierkeys/fast-note-sync-service:latest\n```\n\n#### Docker Compose\n\nCreate a `docker-compose.yaml` file:\n\n```yaml\nversion: '3'\nservices:\n  fast-note-sync-service:\n    image: haierkeys/fast-note-sync-service:latest\n    container_name: fast-note-sync-service\n    restart: always\n    ports:\n      - \"9000:9000\"  # RESTful API & WebSocket ports where /api/user/sync is the WebSocket interface address\n    volumes:\n      - ./storage:/fast-note-sync/storage  # Data storage\n      - ./config:/fast-note-sync/config    # Configuration files\n```\n\nStart the service:\n\n```bash\ndocker compose up -d\n```\n\n-----\n\n### Method 3: Manual Binary Installation\n\nDownload the latest version corresponding to your system from [Releases](https://github.com/haierkeys/fast-note-sync-service/releases), unzip, and run:\n\n```bash\n./fast-note-sync-service run -c config/config.yaml\n```\n\n## 📖 Usage Guide\n\n1.  **Access the Management Panel**:\n    Open `http://{Server IP}:9000` via your browser.\n2.  **Initial Setup**:\n    Register an account on your first visit. *(To disable registration, configure `user.register-is-enable: false` in the settings configuration file)*\n3.  **Configure Client**:\n    Log into the Management Panel, and click **\"Copy API Configuration\"**.\n4.  **Connect to Obsidian**:\n    Navigate to the Obsidian plugin configuration page, and paste the previously copied configuration details.\n\n\n## ⚙️ Configuration Instructions\n\nThe default configuration file is `config.yaml`. The application will search for it automatically in the **Root Directory** or the **config/** directory.\n\nView a complete configuration example: [config/config.yaml](https://github.com/haierkeys/fast-note-sync-service/blob/master/config/config.yaml)\n\n## 🌐 Nginx Reverse Proxy Configuration Example\n\nView a complete configuration example: [https-nginx-example.conf](https://github.com/haierkeys/fast-note-sync-service/blob/master/scripts/https-nginx-example.conf)\n\n## 🧰 MCP (Model Context Protocol) Support\n\nFNS natively supports **MCP (Model Context Protocol)**.\n\nYou can directly incorporate FNS as an MCP server with Cherry Studio, Cursor, and similar compatible AI clients. Once configured, AI attains the capacity to interpret and write within your own private notes and attachments. Furthermore, WebSocket synchronizes all MCP-directed alterations in real time across the entirety of your devices.\n\n### Access Configuration (SSE Mode)\n\nFNS furnishes an MCP interface primarily through the **SSE protocol**. General parameter requirements are as follows:\n- **Interface Address**: `http://<Your Server IP or Domain>:<Port>/api/mcp/sse`\n- **Authentication Header**: `Authorization: Bearer <Your API Token>` (Obtained from “Copy API Configuration” in the WebGUI).\n- **Optional Header**: `X-Default-Vault-Name: <Vault Name>` (Identifies the default vault for MCP operations; utilized if the `vault` parameter is unspecified during a tool call)\n- **Optional Header**: `X-Client: <Client Type>` (Relates to the client type connecting the MCP, e.g., Cherry Studio / OpenClaw)\n- **Optional Header**: `X-Client-Version: <Client Version>` (Pertains to the client's actual version interfacing with the MCP, e.g., 1.1)\n- **Optional Header**: `X-Client-Name: <Client Name>` (Designates the client's name linking with the MCP, e.g., Mac)\n\n\n\n#### Example: Cherry Studio / Cursor / Cline, etc.\n\nKindly incorporate the below JSON inside your MCP client configuration parameters:\n*(Note: Please swap `<ServerIP>`, `<Port>`, `<Token>`, and `<VaultName>` with your proper contextual details)*\n\n```json\n{\n  \"mcpServers\": {\n    \"fns\": {\n      \"url\": \"http://<ServerIP>:<Port>/api/mcp/sse\",\n      \"type\": \"sse\",\n      \"headers\": {\n        \"Content-Type\": \"application/json\",\n        \"Authorization\": \"Bearer <Token>\",\n        \"X-Default-Vault-Name\": \"<VaultName>\",\n        \"X-Client\": \"<Client>\",\n        \"X-Client-Version\": \"<ClientVersion>\",\n        \"X-Client-Name\": \"<ClientName>\"\n      }\n    }\n  }\n}\n```\n\n## 🔗 Client & Client Plugins\n\n* Obsidian Fast Note Sync Plugin\n  * [Obsidian Fast Note Sync Plugin](https://github.com/haierkeys/obsidian-fast-note-sync) / [cnb.cool Mirror](https://cnb.cool/haierkeys/obsidian-fast-note-sync)\n* Third-Party Clients\n  * [FastNodeSync-CLI ](https://github.com/Go1c/FastNodeSync-CLI) Python and FNS WS API grounded bidirectional real-time synchronization CLI client customized for headless Linux server infrastructures (like OpenClaw), providing analogous synchronization faculties equivalent to Obsidian's desktop/mobile clients."
  },
  {
    "path": "cmd/bootstrap.go",
    "content": "package cmd\n\nimport (\n\t\"os\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\n// bootstrapLogger bootstrap stage logger\n// bootstrapLogger 启动阶段日志器\n// Used to record logs during the startup process before the main logger is initialized\n// 用于在主日志器初始化之前记录启动过程中的日志\nvar bootstrapLogger *zap.Logger\n\nfunc init() {\n\t// Create encoder configuration for console output\n\t// 创建控制台输出的 encoder 配置\n\tencoderConfig := zap.NewDevelopmentEncoderConfig()\n\tencoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder\n\tencoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder\n\n\t// Create console output\n\t// 创建控制台输出\n\tconsoleEncoder := zapcore.NewConsoleEncoder(encoderConfig)\n\tconsoleWriter := zapcore.Lock(os.Stderr)\n\n\t// Set log level based on DEBUG environment variable\n\t// 根据 DEBUG 环境变量设置日志级别\n\tlevel := zapcore.InfoLevel\n\tif os.Getenv(\"DEBUG\") != \"\" {\n\t\tlevel = zapcore.DebugLevel\n\t}\n\n\tcore := zapcore.NewCore(consoleEncoder, consoleWriter, level)\n\tbootstrapLogger = zap.New(core, zap.AddCaller())\n}\n\n// BootstrapLogger gets the bootstrap stage logger\n// BootstrapLogger 获取启动阶段日志器\nfunc BootstrapLogger() *zap.Logger {\n\treturn bootstrapLogger\n}\n"
  },
  {
    "path": "cmd/gorm_gen/gen.go",
    "content": "package main\n\n// gorm gen configure\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/fileurl\"\n\t\"gorm.io/driver/mysql\"\n\t\"gorm.io/driver/sqlite\"\n\t\"gorm.io/gen\"\n\t\"gorm.io/gen/field\"\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/logger\"\n\t\"gorm.io/gorm/schema\"\n)\n\nvar (\n\tdbType string\n\tdbDsn  string\n\tstep   int\n)\n\nfunc init() {\n\n\tdType := flag.String(\"type\", \"\", \"输入类型\")\n\tdsn := flag.String(\"dsn\", \"\", \"输入DB dsn地址\")\n\tdStep := flag.Int(\"step\", 0, \"输入执行步骤\")\n\n\tflag.Parse()\n\tdbType = *dType\n\tdbDsn = *dsn\n\tstep = *dStep\n}\n\n// SQLColumnToHumpStyle sql转换成驼峰模式\nfunc SQLColumnToHumpStyle(in string) (ret string) {\n\tfor i := 0; i < len(in); i++ {\n\t\tif i > 0 && in[i-1] == '_' && in[i] != '_' {\n\t\t\ts := strings.ToUpper(string(in[i]))\n\t\t\tret += s\n\t\t} else if in[i] == '_' {\n\t\t\tcontinue\n\t\t} else {\n\t\t\tret += string(in[i])\n\t\t}\n\t}\n\treturn\n}\n\nfunc Db(dsn string, dbType string) *gorm.DB {\n\n\tdb, err := gorm.Open(useDia(dsn, dbType), &gorm.Config{\n\t\tLogger: logger.Default.LogMode(logger.Silent),\n\t\tNamingStrategy: schema.NamingStrategy{\n\t\t\tSingularTable: true, // 使用单数表名,启用该选项,此时,`User` 的表名应该是 `t_user`\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"connect db fail: %w\", err))\n\t}\n\treturn db\n}\n\nfunc useDia(dsn string, dbType string) gorm.Dialector {\n\tif dbType == \"mysql\" {\n\t\treturn mysql.Open(dsn)\n\t} else if dbType == \"sqlite\" {\n\n\t\tif !fileurl.IsExist(dsn) {\n\t\t\tfileurl.CreatePath(dsn, os.ModePerm)\n\t\t}\n\t\treturn sqlite.Open(dsn)\n\t}\n\treturn nil\n}\n\n// getTableDefaultValueTags 获取指定表的 GORM tag 配置（自动注入默认值以解决 SQLite 迁移限制）\nfunc getTableDefaultValueTags(db *gorm.DB, table string) []gen.ModelOpt {\n\tvar opts []gen.ModelOpt\n\n\tif table == \"sqlite_sequence\" || table == \"schema_version\" || strings.HasPrefix(table, \"sqlite_\") {\n\t\treturn opts\n\t}\n\n\t// 获取表的所有列信息\n\tcolumnTypes, err := db.Migrator().ColumnTypes(table)\n\tif err != nil {\n\t\treturn opts\n\t}\n\n\tfor _, col := range columnTypes {\n\t\t// 跳过主键字段\n\t\tif isPrimaryKey(col) {\n\t\t\tcontinue\n\t\t}\n\n\t\tfieldName := col.Name()\n\t\tdbType := strings.ToLower(col.DatabaseTypeName())\n\t\tdefaultValue, ok := col.DefaultValue()\n\n\t\t// 获取 GORM tag 配置，并在这里注入额外逻辑\n\t\topts = append(opts, gen.FieldGORMTag(fieldName, func(tag field.GormTag) field.GormTag {\n\t\t\t// 1. 处理默认值逻辑 (保留原逻辑)\n\t\t\tif ok && defaultValue != \"\" {\n\t\t\t\ttag.Set(\"default\", defaultValue)\n\t\t\t} else {\n\t\t\t\tif dbType == \"integer\" || dbType == \"int\" || dbType == \"bigint\" {\n\t\t\t\t\ttag.Set(\"default\", \"0\")\n\t\t\t\t} else if dbType == \"text\" || strings.Contains(dbType, \"char\") {\n\t\t\t\t\ttag.Set(\"default\", \"''\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 2. 处理时间类型兼容性 (移除 type 让 GORM 自动决定)\n\t\t\tif strings.Contains(dbType, \"datetime\") || strings.Contains(dbType, \"timestamp\") {\n\t\t\t\ttag.Remove(\"type\")\n\t\t\t}\n\n\t\t\t// 3. 处理整数类型兼容性 (移除 type 让 GORM 根据 Go 类型自动决定)\n\t\t\t// 特别是处理 SQLite 中大写 INTEGER 导致的冗余 tag，防止 MySQL/PG 识别为 32位 INT\n\t\t\tif dbType == \"integer\" || dbType == \"int\" || dbType == \"bigint\" {\n\t\t\t\ttag.Remove(\"type\")\n\t\t\t}\n\n\t\t\t// 3. 处理 MySQL 索引长度限制 (Error 1071)\n\t\t\t// 注意：GormTag 可能为 map[string][]string 或 map[string]string。\n\t\t\t// 这里通过 key 检查判断索引。\n\t\t\tisIndexed := false\n\t\t\tindexKeys := []string{\"index\", \"uniqueIndex\", \"unique_index\"}\n\t\t\tfor _, k := range indexKeys {\n\t\t\t\tif v, exists := tag[k]; exists {\n\t\t\t\t\tif len(v) > 0 {\n\t\t\t\t\t\tisIndexed = true\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif isIndexed && (strings.ToUpper(dbType) == \"TEXT\" || strings.ToUpper(dbType) == \"LONGTEXT\" || dbType == \"text\") {\n\t\t\t\ttag.Set(\"type\", \"varchar(255)\")\n\t\t\t}\n\n\t\t\treturn tag\n\t\t}))\n\t}\n\n\treturn opts\n}\n\n// isPrimaryKey 检查列是否是主键\nfunc isPrimaryKey(col gorm.ColumnType) bool {\n\tif pk, ok := col.PrimaryKey(); ok && pk {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc main() {\n\n\tg := gen.NewGenerator(gen.Config{\n\t\t// 默认会在 OutPath 目录生成CRUD代码,并且同目录下生成 model 包\n\t\t// 所以OutPath最终package不能设置为model,在有数据库表同步的情况下会产生冲突\n\t\t// 若一定要使用可以通过ModelPkgPath单独指定model package的名称\n\t\tOutPath: \"./internal/query\",\n\t\t/* ModelPkgPath: \"dal/model\"*/\n\n\t\t// gen.WithoutContext:禁用WithContext模式\n\t\t// gen.WithDefaultQuery:生成一个全局Query对象Q\n\t\t// gen.WithQueryInterface:生成Query接口\n\t\tMode:              gen.WithQueryInterface,\n\t\tWithUnitTest:      false,\n\t\tFieldWithTypeTag:  false,\n\t\tFieldWithIndexTag: true,\n\t})\n\n\tdb := Db(dbDsn, dbType)\n\tg.UseDB(db)\n\n\tvar dataMap = map[string]func(gorm.ColumnType) (dataType string){\n\t\t// int mapping\n\t\t\"integer\": func(columnType gorm.ColumnType) (dataType string) {\n\t\t\treturn \"int64\"\n\t\t},\n\t\t\"INTEGER\": func(columnType gorm.ColumnType) (dataType string) {\n\t\t\treturn \"int64\"\n\t\t},\n\t\t\"int\": func(columnType gorm.ColumnType) (dataType string) {\n\t\t\treturn \"int64\"\n\t\t},\n\t\t\"INT\": func(columnType gorm.ColumnType) (dataType string) {\n\t\t\treturn \"int64\"\n\t\t},\n\t}\n\tg.WithDataTypeMap(dataMap)\n\n\t// 获取表列表\n\ttableList, _ := db.Migrator().GetTables()\n\n\t// 基础配置\n\topts := []gen.ModelOpt{\n\t\tgen.FieldRename(\"fid\", \"FID\"),\n\t\t//gen.FieldType(\"uid\", \"int64\"),\n\t\tgen.FieldType(\"created_at\", \"timex.Time\"),\n\t\tgen.FieldType(\"updated_at\", \"timex.Time\"),\n\t\tgen.FieldType(\"deleted_at\", \"timex.Time\"),\n\t\t//gen.FieldType(\"mtime\", \"timex.Time\"),\n\t\tgen.FieldGORMTag(\"created_at\", func(tag field.GormTag) field.GormTag {\n\t\t\ttag.Set(\"autoCreateTime\", \"false\")\n\t\t\ttag.Set(\"default\", \"NULL\")\n\t\t\treturn tag\n\t\t}),\n\t\tgen.FieldGORMTag(\"updated_at\", func(tag field.GormTag) field.GormTag {\n\t\t\ttag.Set(\"autoUpdateTime\", \"false\")\n\t\t\ttag.Set(\"default\", \"NULL\")\n\t\t\treturn tag\n\t\t}),\n\t\tgen.FieldGORMTag(\"deleted_at\", func(tag field.GormTag) field.GormTag {\n\t\t\ttag.Set(\"default\", \"NULL\")\n\t\t\treturn tag\n\t\t}),\n\t\tgen.FieldGORMTag(\"mtime\", func(tag field.GormTag) field.GormTag {\n\t\t\t//tag.Set(\"type\", \"datetime\")\n\t\t\ttag.Set(\"default\", \"0\")\n\t\t\treturn tag\n\t\t}),\n\t\tgen.FieldJSONTagWithNS(func(columnName string) string {\n\t\t\treturn SQLColumnToHumpStyle(columnName)\n\t\t}),\n\n\t\tgen.FieldNewTagWithNS(\"form\", func(columnName string) string {\n\t\t\treturn SQLColumnToHumpStyle(columnName)\n\t\t}),\n\t}\n\n\tfor _, table := range tableList {\n\t\tif table == \"sqlite_sequence\" || table == \"schema_version\" || strings.HasPrefix(table, \"sqlite_\") {\n\t\t\tcontinue\n\t\t}\n\n\t\t// 组合基础选项和表特有选项\n\t\ttableOpts := append([]gen.ModelOpt{}, opts...)\n\t\ttableOpts = append(tableOpts, getTableDefaultValueTags(db, table)...)\n\n\t\tg.ApplyBasic(g.GenerateModel(table, tableOpts...))\n\t}\n\tg.Execute()\n\n}\n"
  },
  {
    "path": "cmd/mfmt/main.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"crypto/sha256\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/format\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n\t\"go.uber.org/zap\"\n\t\"golang.org/x/tools/go/packages\"\n)\n\nvar stdlib = make(map[string]bool)\n\nfunc init() {\n\tpkgs, err := packages.Load(nil, \"std\")\n\tif err != nil {\n\t\tlog.Fatal(\"get go stdlib err\", zap.Error(err))\n\t}\n\n\tfor _, pkg := range pkgs {\n\t\tif !strings.HasPrefix(pkg.ID, \"vendor\") {\n\t\t\tstdlib[pkg.ID] = true\n\t\t}\n\t}\n}\n\nvar module string\n\nfunc init() {\n\tfile, err := os.Open(\"./go.mod\")\n\tif err != nil {\n\t\tlog.Fatal(\"no go.mod file found\", zap.Error(err))\n\t}\n\tdefer file.Close()\n\n\tscanner := bufio.NewScanner(file)\n\tfor scanner.Scan() {\n\t\tline := strings.TrimSpace(scanner.Text())\n\t\tif strings.HasPrefix(line, \"module \") {\n\t\t\tmodule = strings.TrimSpace(line[7:])\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif module == \"\" {\n\t\tlog.Fatal(\"go.mod illegal\")\n\t}\n}\n\nfunc main() {\n\n\terr := filepath.Walk(\"./\", func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif info.IsDir() || strings.HasPrefix(path, \".\") || strings.HasPrefix(path, \"vendor\") || strings.HasSuffix(path, \".pb.go\") || filepath.Ext(path) != \".go\" {\n\t\t\treturn nil\n\t\t}\n\n\t\traw, err := ioutil.ReadFile(path)\n\t\tif err != nil {\n\t\t\treturn errors.Wrapf(err, \"read file %s err\", path)\n\t\t}\n\n\t\tdigest0 := sha256.Sum256(raw)\n\t\tif raw, err = format.Source(raw); err != nil {\n\t\t\treturn errors.Wrapf(err, \"format file %s err\", path)\n\t\t}\n\n\t\tfile, err := parser.ParseFile(token.NewFileSet(), \"\", raw, 0)\n\t\tif err != nil {\n\t\t\treturn errors.Wrapf(err, \"parse file %s err\", path)\n\t\t}\n\n\t\tvar first, last int\n\t\tvar imports []*ast.ImportSpec\n\t\tcomments := make(map[string]string)\n\n\t\tast.Inspect(file, func(n ast.Node) bool {\n\t\t\tswitch spec := n.(type) {\n\t\t\tcase *ast.ImportSpec:\n\t\t\t\tif first == 0 {\n\t\t\t\t\tfirst = int(spec.Pos())\n\t\t\t\t}\n\t\t\t\tlast = int(spec.End())\n\n\t\t\t\timports = append(imports, spec)\n\n\t\t\t\tk := last - 1\n\t\t\t\tfor ; k < len(raw); k++ {\n\t\t\t\t\tif raw[k] == '\\r' || raw[k] == '\\n' {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcomment := string(raw[last-1 : k])\n\t\t\t\tif index := strings.Index(comment, \"//\"); index != -1 {\n\t\t\t\t\tcomments[spec.Path.Value] = strings.TrimSpace(comment[index+2:])\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\n\t\tif imports != nil {\n\t\t\tbuf := bytes.NewBuffer(nil)\n\t\t\tbuf.Write(raw[:first-2])\n\t\t\tbuf.WriteString(sort(imports, comments))\n\t\t\tbuf.Write(raw[last-1:])\n\n\t\t\tif raw, err = format.Source(buf.Bytes()); err != nil {\n\t\t\t\treturn errors.Wrapf(err, \"double format file %s err\", path)\n\t\t\t}\n\t\t}\n\n\t\tdigest1 := sha256.Sum256(raw)\n\t\tif !bytes.Equal(digest0[:], digest1[:]) {\n\t\t\tfmt.Println(path)\n\t\t}\n\n\t\tif err = ioutil.WriteFile(path, raw, info.Mode()); err != nil {\n\t\t\treturn errors.Wrapf(err, \"write file %s err\", path)\n\t\t}\n\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\tlog.Fatal(\"scan project err\", zap.Error(err))\n\t}\n}\n\nfunc sort(imports []*ast.ImportSpec, comments map[string]string) string {\n\tsystem := bytes.NewBuffer(nil)\n\tgroup := bytes.NewBuffer(nil)\n\tothers := bytes.NewBuffer(nil)\n\n\tfor _, pkg := range imports {\n\t\tvalue := strings.Trim(pkg.Path.Value, `\"`)\n\t\tswitch {\n\t\tcase stdlib[value]:\n\t\t\tif pkg.Name != nil {\n\t\t\t\tsystem.WriteString(pkg.Name.String())\n\t\t\t\tsystem.WriteString(\" \")\n\t\t\t}\n\n\t\t\tsystem.WriteString(pkg.Path.Value)\n\t\t\tif comment, ok := comments[pkg.Path.Value]; ok {\n\t\t\t\tsystem.WriteString(\" \")\n\t\t\t\tsystem.WriteString(\"// \")\n\t\t\t\tsystem.WriteString(comment)\n\t\t\t}\n\t\t\tsystem.WriteString(\"\\n\")\n\n\t\tcase strings.HasPrefix(value, module):\n\t\t\tif pkg.Name != nil {\n\t\t\t\tgroup.WriteString(pkg.Name.String())\n\t\t\t\tgroup.WriteString(\" \")\n\t\t\t}\n\n\t\t\tgroup.WriteString(pkg.Path.Value)\n\t\t\tif comment, ok := comments[pkg.Path.Value]; ok {\n\t\t\t\tgroup.WriteString(\" \")\n\t\t\t\tgroup.WriteString(\"// \")\n\t\t\t\tgroup.WriteString(comment)\n\t\t\t}\n\t\t\tgroup.WriteString(\"\\n\")\n\n\t\tdefault:\n\t\t\tif pkg.Name != nil {\n\t\t\t\tothers.WriteString(pkg.Name.String())\n\t\t\t\tothers.WriteString(\" \")\n\t\t\t}\n\n\t\t\tothers.WriteString(pkg.Path.Value)\n\t\t\tif comment, ok := comments[pkg.Path.Value]; ok {\n\t\t\t\tothers.WriteString(\" \")\n\t\t\t\tothers.WriteString(\"// \")\n\t\t\t\tothers.WriteString(comment)\n\t\t\t}\n\t\t\tothers.WriteString(\"\\n\")\n\t\t}\n\t}\n\n\treturn fmt.Sprintf(\"%s\\n%s\\n%s\", system.String(), group.String(), others.String())\n}\n"
  },
  {
    "path": "cmd/model_gen/gen.go",
    "content": "package main\n\n// gorm gen configure\nimport (\n\t\"os\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/query\"\n\t\"gorm.io/gen\"\n)\n\nfunc main() {\n\tg := gen.NewGenerator(gen.Config{\n\t\t// 默认会在 OutPath 目录生成CRUD代码，并且同目录下生成 model 包\n\t\t// 所以OutPath最终package不能设置为model，在有数据库表同步的情况下会产生冲突\n\t\t// 若一定要使用可以通过ModelPkgPath单独指定model package的名称\n\t\tOutPath: \"./internal/query\",\n\t\t/* ModelPkgPath: \"dal/model\"*/\n\t\t// gen.WithoutContext：禁用WithContext模式\n\t\t// gen.WithDefaultQuery：生成一个全局Query对象Q\n\t\t// gen.WithQueryInterface：生成Query接口\n\t\tMode:             gen.WithQueryInterface,\n\t\tWithUnitTest:     false,\n\t\tFieldWithTypeTag: false,\n\t})\n\tv := reflect.ValueOf(query.Query{})\n\tgoContent := `\npackage model\n\nimport (\n\t\"gorm.io/gorm\"\n)\n\n\n\nfunc AutoMigrate(db *gorm.DB, key string) error {\n\tif db == nil {\n\t\treturn nil\n\t}\n\tswitch key {\n`\n\tgoContentFunc := `\n\tcase \"{NAME}\":\n\t\treturn db.AutoMigrate({NAME}{})\n`\n\n\tif v.Kind() == reflect.Struct {\n\t\tt := v.Type()\n\t\tfields := []string{}\n\t\tfor i := 0; i < v.NumField(); i++ {\n\t\t\tfield := t.Field(i)\n\t\t\tif field.Name == \"db\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfields = append(fields, field.Name+\"{}\")\n\t\t\tgoContent += strings.ReplaceAll(goContentFunc, \"{NAME}\", field.Name)\n\t\t\t//goContentHeader += fmt.Sprintf(\"type %s = %s\\n\", field.Name, field.Type.Name())\n\t\t}\n\t\t//goContent += \"\\tcase \\\"\\\":\\n\\t\\treturn db.AutoMigrate(\" + strings.Join(fields, \", \") + \")\"\n\t\tgoContent += \"\\t}\\n\\treturn nil\\n}\"\n\n\t\t_ = os.WriteFile(g.OutPath[0:len(g.OutPath)-6]+\"/model/model.go\", []byte(goContent), os.ModePerm)\n\t}\n}\n"
  },
  {
    "path": "cmd/reset_password.go",
    "content": "package cmd\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\n\tinternalApp \"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dao\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/fileurl\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/logger\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\n\t\"github.com/spf13/cobra\"\n\t\"go.uber.org/zap\"\n\t\"gorm.io/gorm\"\n)\n\nfunc init() {\n\tvar configPath string\n\tvar username string\n\tvar password string\n\n\tvar resetPasswordCmd = &cobra.Command{\n\t\tUse:   \"reset-password -u <username> -p <password> [-c config_file]\",\n\t\tShort: \"Reset a user's password by username\",\n\t\t// 通过用户名重置用户密码，无需旧密码\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tif username == \"\" {\n\t\t\t\tbootstrapLogger.Error(\"username is required, use -u flag\")\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\tif password == \"\" {\n\t\t\t\tbootstrapLogger.Error(\"password is required, use -p flag\")\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\t// Load configuration\n\t\t\t// 加载配置\n\t\t\tif configPath == \"\" {\n\t\t\t\t// We can rely on default logic in further layers or set a default here\n\t\t\t\t// Use the same logic as run.go for consistency\n\t\t\t\tif fileurl.IsExist(\"config/config-dev.yaml\") {\n\t\t\t\t\tconfigPath = \"config/config-dev.yaml\"\n\t\t\t\t} else if fileurl.IsExist(\"config.yaml\") {\n\t\t\t\t\tconfigPath = \"config.yaml\"\n\t\t\t\t} else {\n\t\t\t\t\tconfigPath = \"config/config.yaml\"\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tappConfig, configRealpath, err := internalApp.LoadConfig(configPath)\n\t\t\tif err != nil {\n\t\t\t\tbootstrapLogger.Error(\"failed to load config\", zap.Error(err))\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t\tbootstrapLogger.Info(\"loading config\", zap.String(\"path\", configRealpath))\n\n\t\t\t// Initialize logger\n\t\t\t// 初始化日志\n\t\t\tlg, err := logger.NewLogger(logger.Config{\n\t\t\t\tLevel:      appConfig.Log.Level,\n\t\t\t\tFile:       appConfig.Log.File,\n\t\t\t\tProduction: appConfig.Log.Production,\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\tbootstrapLogger.Error(\"failed to init logger\", zap.Error(err))\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\t// Initialize database\n\t\t\t// 初始化数据库\n\t\t\tdbConfig := appConfig.Database\n\t\t\tdbConfig.RunMode = appConfig.Server.RunMode\n\n\t\t\tdb, err := dao.NewEngine(dbConfig, lg)\n\t\t\tif err != nil {\n\t\t\t\tbootstrapLogger.Error(\"failed to init database\", zap.Error(err))\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\t// Initialize Dao and UserRepository\n\t\t\t// 初始化 Dao 和 UserRepository\n\t\t\tctx := context.Background()\n\t\t\tdaoObj := dao.New(db, ctx, dao.WithConfig(&dbConfig), dao.WithLogger(lg))\n\t\t\tuserRepo := dao.NewUserRepository(daoObj)\n\n\t\t\t// Look up target user by username\n\t\t\t// 根据用户名查找目标用户\n\t\t\tuser, err := userRepo.GetByUsername(ctx, username)\n\t\t\tif err != nil {\n\t\t\t\tif err == gorm.ErrRecordNotFound {\n\t\t\t\t\tfmt.Fprintf(os.Stderr, \"Error: user '%s' not found\\n\", username)\n\t\t\t\t} else {\n\t\t\t\t\tfmt.Fprintf(os.Stderr, \"Error: failed to query user: %v\\n\", err)\n\t\t\t\t}\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\t// Generate password hash\n\t\t\t// 生成密码哈希\n\t\t\thashedPassword, err := util.GeneratePasswordHash(password)\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintf(os.Stderr, \"Error: failed to generate password hash: %v\\n\", err)\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\t// Update password\n\t\t\t// 更新密码\n\t\t\tif err := userRepo.UpdatePassword(ctx, hashedPassword, user.UID); err != nil {\n\t\t\t\tfmt.Fprintf(os.Stderr, \"Error: failed to update password: %v\\n\", err)\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\tfmt.Printf(\"Password for user '%s' (uid=%d) has been reset successfully.\\n\", username, user.UID)\n\t\t},\n\t}\n\n\trootCmd.AddCommand(resetPasswordCmd)\n\tfs := resetPasswordCmd.Flags()\n\tfs.StringVarP(&configPath, \"config\", \"c\", \"\", \"config file path (default: config/config.yaml)\")\n\tfs.StringVarP(&username, \"username\", \"u\", \"\", \"target username (required)\")\n\tfs.StringVarP(&password, \"password\", \"p\", \"\", \"new password (required)\")\n}\n"
  },
  {
    "path": "cmd/root.go",
    "content": "package cmd\n\nimport (\n\t\"embed\"\n\t\"os\"\n\n\t\"github.com/spf13/cobra\"\n\t\"go.uber.org/zap\"\n)\n\nvar frontendFiles embed.FS\nvar configDefault string\nvar rootCmd = &cobra.Command{\n\tUse:   \"fast-note-sync-service\",\n\tShort: \"Fast Note Sync Service\",\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\tcmd.HelpTemplate()\n\t\tcmd.Help()\n\t},\n}\n\n// Execute executes the root command\n// Execute 执行根命令\nfunc Execute(efs embed.FS, c string) {\n\tfrontendFiles = efs\n\tconfigDefault = c\n\tif err := rootCmd.Execute(); err != nil {\n\t\tBootstrapLogger().Error(\"command execution failed\", zap.Error(err))\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "cmd/run.go",
    "content": "package cmd\n\nimport (\n\t\"os\"\n\t\"os/signal\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"syscall\"\n\t\"time\"\n\n\tinternalApp \"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/fileurl\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\n\t\"github.com/radovskyb/watcher\"\n\t\"github.com/spf13/cobra\"\n\t\"go.uber.org/zap\"\n)\n\ntype runFlags struct {\n\tdir     string // Project root directory // 项目根目录\n\tport    string // Startup port // 启动端口\n\trunMode string // Startup mode // 启动模式\n\tconfig  string // Specified configuration file path // 指定要使用的配置文件路径\n}\n\nfunc init() {\n\trunEnv := new(runFlags)\n\n\tvar runCommand = &cobra.Command{\n\t\tUse:   \"run [-c config_file] [-d working_dir] [-p port]\",\n\t\tShort: \"Run service\",\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tif len(runEnv.dir) > 0 {\n\t\t\t\terr := os.Chdir(runEnv.dir)\n\t\t\t\tif err != nil {\n\t\t\t\t\tbootstrapLogger.Error(\"failed to change the current working directory\", zap.Error(err))\n\t\t\t\t}\n\t\t\t\tbootstrapLogger.Info(\"working directory changed\", zap.String(\"dir\", runEnv.dir))\n\t\t\t}\n\n\t\t\tif len(runEnv.config) <= 0 {\n\t\t\t\tif fileurl.IsExist(\"config/config-dev.yaml\") {\n\t\t\t\t\trunEnv.config = \"config/config-dev.yaml\"\n\t\t\t\t} else if fileurl.IsExist(\"config.yaml\") {\n\t\t\t\t\trunEnv.config = \"config.yaml\"\n\t\t\t\t} else if fileurl.IsExist(\"config/config.yaml\") {\n\t\t\t\t\trunEnv.config = \"config/config.yaml\"\n\t\t\t\t} else {\n\n\t\t\t\t\tbootstrapLogger.Warn(\"config file not found, creating default config\")\n\t\t\t\t\trunEnv.config = \"config/config.yaml\"\n\n\t\t\t\t\tconfigDefault = strings.Replace(configDefault, \"fast-note-sync-Auth-Token\", util.GetRandomString(32), 1)\n\n\t\t\t\t\tif err := fileurl.CreatePath(runEnv.config, os.ModePerm); err != nil {\n\t\t\t\t\t\tbootstrapLogger.Error(\"config file auto create error\", zap.Error(err))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tfile, err := os.OpenFile(runEnv.config, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tbootstrapLogger.Error(\"config file auto create error\", zap.Error(err))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tdefer file.Close()\n\t\t\t\t\t_, err = file.WriteString(configDefault)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tbootstrapLogger.Error(\"config file auto create writing error\", zap.Error(err))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tbootstrapLogger.Info(\"config file auto create successfully\", zap.String(\"path\", runEnv.config))\n\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ts, err := NewServer(runEnv)\n\t\t\tif err != nil {\n\t\t\t\tbootstrapLogger.Error(\"api service start err\", zap.Error(err))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconfigChanged := make(chan struct{}, 1)\n\t\t\tgo func() {\n\n\t\t\t\tw := watcher.New()\n\n\t\t\t\t// Set MaxEvents to 1 to receive at most 1 event in each listening cycle\n\t\t\t\t// 将 SetMaxEvents 设置为 1，以便在每个监听周期中至多接收 1 个事件\n\t\t\t\t// If SetMaxEvents is not set, all events are sent by default.\n\t\t\t\t// 如果没有设置 SetMaxEvents，默认情况下会发送所有事件。\n\t\t\t\tw.SetMaxEvents(1)\n\n\t\t\t\t// Only notify write events.\n\t\t\t\t// 只通知写入事件。\n\t\t\t\tw.FilterOps(watcher.Write)\n\n\t\t\t\tgo func() {\n\t\t\t\t\tfor {\n\t\t\t\t\t\tselect {\n\t\t\t\t\t\tcase event := <-w.Event:\n\t\t\t\t\t\t\ts.logger.Info(\"config watcher change detected\", zap.String(\"event\", event.Op.String()), zap.String(\"file\", event.Path))\n\t\t\t\t\t\t\tselect {\n\t\t\t\t\t\t\tcase configChanged <- struct{}{}:\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\tcase err := <-w.Error:\n\t\t\t\t\t\t\ts.logger.Error(\"config watcher error\", zap.Error(err))\n\t\t\t\t\t\tcase <-w.Closed:\n\t\t\t\t\t\t\tbootstrapLogger.Info(\"config watcher closed\")\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}()\n\n\t\t\t\t// Watch config.yaml file\n\t\t\t\t// 监听 config.yaml 文件\n\t\t\t\tif err := w.Add(runEnv.config); err != nil {\n\t\t\t\t\ts.logger.Error(\"config watcher file error\", zap.Error(err))\n\t\t\t\t}\n\n\t\t\t\t// Start watching\n\t\t\t\t// 启动监听\n\t\t\t\tif err := w.Start(time.Second * 5); err != nil {\n\t\t\t\t\ts.logger.Error(\"config watcher start error\", zap.Error(err))\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\tquit1 := make(chan os.Signal, 1)\n\t\t\tsignal.Notify(quit1, syscall.SIGINT, syscall.SIGTERM)\n\n\t\t\tfor {\n\t\t\t\tselect {\n\t\t\t\tcase <-quit1:\n\t\t\t\t\ts.logger.Info(\"Received shutdown signal, initiating graceful shutdown...\")\n\t\t\t\t\ts.sc.SendCloseSignal(nil)\n\t\t\t\t\t// 等待并在处理后退出循环\n\t\t\t\t\tgoto wait_and_exit\n\t\t\t\tcase <-configChanged:\n\t\t\t\t\ts.logger.Info(\"Reloading server due to config change...\")\n\t\t\t\t\ts.sc.SendCloseSignal(nil)\n\t\t\t\t\tif err := s.sc.WaitClosed(); err != nil {\n\t\t\t\t\t\ts.logger.Error(\"Failed to close old server during reload\", zap.Error(err))\n\t\t\t\t\t}\n\t\t\t\t\t// 重新初始化 server\n\t\t\t\t\tnewS, err := NewServer(runEnv)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tbootstrapLogger.Error(\"failed to re-initialize service after config change\", zap.Error(err))\n\t\t\t\t\t\tcontinue // 尝试继续运行旧实例（如果可能）或再次等待配置修复\n\t\t\t\t\t}\n\t\t\t\t\ts = newS\n\t\t\t\t\ts.logger.Info(\"Server re-initialized successfully\")\n\t\t\t\t\tcontinue // 重新进入 select 监听新 s 的 UpgradeSignal\n\t\t\t\tcase newBinaryPath := <-s.GetApp().UpgradeSignal:\n\t\t\t\t\ts.logger.Info(\"Received upgrade/restart signal, starting smooth restart...\", zap.String(\"newBinary\", newBinaryPath))\n\n\t\t\t\t\tcurrentBinary, _ := os.Executable()\n\t\t\t\t\tisRestartOnly := newBinaryPath == currentBinary\n\n\t\t\t\t\tif !isRestartOnly {\n\t\t\t\t\t\t// 1. Perform file replacement (for upgrade)\n\t\t\t\t\t\toldBinary := currentBinary + \".old\"\n\t\t\t\t\t\t_ = os.Remove(oldBinary)\n\t\t\t\t\t\tif err := util.MoveFile(currentBinary, oldBinary); err != nil {\n\t\t\t\t\t\t\ts.logger.Error(\"Failed to backup current binary\", zap.Error(err))\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif err := util.MoveFile(newBinaryPath, currentBinary); err != nil {\n\t\t\t\t\t\t\ts.logger.Error(\"Failed to replace binary\", zap.Error(err))\n\t\t\t\t\t\t\t// Try to restore?\n\t\t\t\t\t\t\t_ = util.MoveFile(oldBinary, currentBinary)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif err := os.Chmod(currentBinary, 0755); err != nil {\n\t\t\t\t\t\t\ts.logger.Error(\"Failed to set executable permission\", zap.Error(err))\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// 1.1 Cleanup temp directory (where the tar.gz and temporary binary were)\n\t\t\t\t\t\ttempDir := filepath.Dir(newBinaryPath)\n\t\t\t\t\t\tif err := os.RemoveAll(tempDir); err != nil {\n\t\t\t\t\t\t\ts.logger.Warn(\"Failed to cleanup upgrade temp directory\", zap.String(\"path\", tempDir), zap.Error(err))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// 2. Graceful shutdown (close servers, release ports)\n\t\t\t\t\ts.sc.SendCloseSignal(nil)\n\t\t\t\t\tif err := s.sc.WaitClosed(); err != nil {\n\t\t\t\t\t\ts.logger.Error(\"Shutdown failed before restart\", zap.Error(err))\n\t\t\t\t\t}\n\n\t\t\t\t\t// 3. Restart\n\t\t\t\t\tenv := os.Environ()\n\t\t\t\t\targs := os.Args\n\t\t\t\t\tif err := internalApp.RestartProcess(currentBinary, args, env); err != nil {\n\t\t\t\t\t\ts.logger.Error(\"Failed to restart process\", zap.Error(err))\n\t\t\t\t\t}\n\t\t\t\t\treturn // 退出主循环\n\t\t\t\t}\n\t\t\t}\n\n\t\twait_and_exit:\n\t\t\t// Wait for all shutdown handlers to complete (including App Container graceful shutdown)\n\t\t\t// 等待所有关闭处理器完成（包括 App Container 的优雅关闭）\n\t\t\tif err := s.sc.WaitClosed(); err != nil {\n\t\t\t\ts.logger.Error(\"Shutdown completed with error\", zap.Error(err))\n\t\t\t} else {\n\t\t\t\ts.logger.Info(\"Service has been shut down gracefully.\")\n\t\t\t}\n\n\t\t},\n\t}\n\n\trootCmd.AddCommand(runCommand)\n\tfs := runCommand.Flags()\n\tfs.StringVarP(&runEnv.dir, \"dir\", \"d\", \"\", \"run dir\")\n\tfs.StringVarP(&runEnv.port, \"port\", \"p\", \"\", \"run port\")\n\tfs.StringVarP(&runEnv.runMode, \"mode\", \"m\", \"\", \"run mode\")\n\tfs.StringVarP(&runEnv.config, \"config\", \"c\", \"\", \"config file\")\n\n}\n"
  },
  {
    "path": "cmd/run_server.go",
    "content": "package cmd\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"strings\"\n\t\"time\"\n\n\tinternalApp \"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dao\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/routers\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/task\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/upgrade\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/logger\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/safe_close\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/validator\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/gin-gonic/gin/binding\"\n\t\"github.com/go-playground/locales/en\"\n\t\"github.com/go-playground/locales/zh\"\n\tut \"github.com/go-playground/universal-translator\"\n\tvalidatorV10 \"github.com/go-playground/validator/v10\"\n\ten_translations \"github.com/go-playground/validator/v10/translations/en\"\n\tzh_translations \"github.com/go-playground/validator/v10/translations/zh\"\n\t\"go.uber.org/zap\"\n\t\"gorm.io/gorm\"\n)\n\n// defaultSecretKeys defines the list of default secret keys to be detected\n// defaultSecretKeys 定义需要检测的默认密钥列表\nvar defaultSecretKeys = []string{\n\t\"6666\",\n\t\"fast-note-sync-Auth-Token\",\n\t\"\",\n}\n\n// DefaultShutdownTimeout default shutdown timeout duration\n// DefaultShutdownTimeout 默认关闭超时时间\nconst DefaultShutdownTimeout = 30 * time.Second\n\ntype Server struct {\n\tlogger            *zap.Logger             // Logger // 日志对象\n\tconfig            *internalApp.AppConfig  // App configuration (injected dependency) // 应用配置（注入的依赖）\n\tdb                *gorm.DB                // Database connection // 数据库连接\n\tut                *ut.UniversalTranslator // Translator // 翻译器\n\thttpServer        *http.Server\n\tprivateHttpServer *http.Server\n\tsc                *safe_close.SafeClose\n\tapp               *internalApp.App // App Container\n}\n\n// checkSecurityConfigWithConfig checks security configuration, outputs warning if using default keys\n// checkSecurityConfig 检查安全配置，如果使用默认密钥则输出警告\nfunc checkSecurityConfigWithConfig(cfg *internalApp.AppConfig, lg *zap.Logger) {\n\tisDefault := false\n\tfor _, key := range defaultSecretKeys {\n\t\tif cfg.Security.AuthTokenKey == key {\n\t\t\tisDefault = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif isDefault {\n\t\t// Output to console\n\t\t// 输出到控制台\n\t\tfmt.Println()\n\t\tfmt.Println(strings.Repeat(\"=\", 60))\n\t\tfmt.Println(\"⚠️  SECURITY WARNING: Using default secret key!\")\n\t\tfmt.Println()\n\t\tfmt.Println(\"Please modify 'security.auth-token-key' in config.yaml\")\n\t\tfmt.Println(\"Generate a secure key with:\")\n\t\tfmt.Println(\"  openssl rand -base64 32\")\n\t\tfmt.Println(strings.Repeat(\"=\", 60))\n\t\tfmt.Println()\n\n\t\t// Record to log\n\t\t// 记录到日志\n\t\tif lg != nil {\n\t\t\tlg.Warn(\"Using default secret key - please change security.auth-token-key in config.yaml\")\n\t\t}\n\t}\n}\n\nfunc NewServer(runEnv *runFlags) (*Server, error) {\n\n\t// Use LoadConfig to directly load config into AppConfig\n\t// 使用 LoadConfig 直接加载配置到 AppConfig\n\tappConfig, configRealpath, err := internalApp.LoadConfig(runEnv.config)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to load config: %w\", err)\n\t}\n\n\t// Determine run mode\n\t// 确定运行模式\n\trunMode := runEnv.runMode\n\tif len(runMode) <= 0 {\n\t\trunMode = appConfig.Server.RunMode\n\t}\n\n\tif len(runMode) > 0 {\n\t\tgin.SetMode(runMode)\n\t} else {\n\t\tgin.SetMode(gin.ReleaseMode)\n\t}\n\n\ts := &Server{\n\t\tconfig: appConfig,\n\t\tsc:     safe_close.NewSafeClose(),\n\t}\n\n\t// Initialize logger (using injected config)\n\t// 初始化日志器（使用注入的配置）\n\tif err := initLoggerWithConfig(s, appConfig); err != nil {\n\t\treturn nil, fmt.Errorf(\"initLogger: %w\", err)\n\t}\n\n\t// Check security configuration (using injected config)\n\t// 检查安全配置（使用注入的配置）\n\tcheckSecurityConfigWithConfig(appConfig, s.logger)\n\n\t// Initialize storage directory (using injected config)\n\t// 初始化存储目录（使用注入的配置）\n\tif err := initStorageWithConfig(appConfig); err != nil {\n\t\treturn nil, fmt.Errorf(\"initStorage: %w\", err)\n\t}\n\n\t// Initialize database (using injected config)\n\t// 初始化数据库（使用注入的配置）\n\tdb, err := initDatabaseWithConfig(appConfig, s.logger)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"initDatabase: %w\", err)\n\t}\n\ts.db = db\n\n\t// Initialize App Container (using AppConfig directly)\n\t// 初始化 App Container（直接使用 AppConfig）\n\tapp, err := internalApp.NewApp(appConfig, s.logger, db, frontendFiles)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create app container: %w\", err)\n\t}\n\ts.app = app\n\n\t// Auto-execute migration tasks (using injected config)\n\t// 自动执行迁移任务（使用注入的配置）\n\tif err := upgrade.Execute(\n\t\tdb,\n\t\ts.logger,\n\t\tinternalApp.Version,\n\t\t&appConfig.Database,\n\t\t&appConfig.UserDatabase,\n\t); err != nil {\n\t\treturn nil, fmt.Errorf(\"upgrade.Execute: %w\", err)\n\t}\n\n\t// Initialize validator\n\t// 初始化验证器\n\tuni, err := initValidatorWithLogger(s.logger)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"initValidator: %w\", err)\n\t}\n\ts.ut = uni\n\n\tvalidator.RegisterCustom()\n\n\t// Start scheduler\n\t// 启动调度器\n\tinitScheduler(s)\n\n\tbanner := `\n    ______           __     _   __      __          _____\n   / ____/___ ______/ /_   / | / /___  / /____     / ___/__  ______  _____\n  / /_  / __  / ___/ __/  /  |/ / __ \\/ __/ _ \\    \\__ \\/ / / / __ \\/ ___/\n / __/ / /_/ (__  ) /_   / /|  / /_/ / /_/  __/   ___/ / /_/ / / / / /__\n/_/    \\__,_/____/\\__/  /_/ |_/\\____/\\__/\\___/   /____/\\__, /_/ /_/\\___/\n                                                      /____/              `\n\ts.logger.Warn(fmt.Sprintf(\"%s\\n\\n%s v%s\\nGit: %s\\nBuildTime: %s\\n\", banner, internalApp.Name, internalApp.Version, internalApp.GitTag, internalApp.BuildTime))\n\n\ts.logger.Warn(\"config loaded\", zap.String(\"path\", configRealpath))\n\n\t// Start HTTP API server\n\t// 启动 HTTP API 服务器\n\tif httpAddr := appConfig.Server.HttpPort; len(httpAddr) > 0 {\n\t\ts.logger.Warn(\"api_router\", zap.String(\"config.server.HttpPort\", appConfig.Server.HttpPort))\n\t\ts.httpServer = &http.Server{\n\t\t\tAddr:           appConfig.Server.HttpPort,\n\t\t\tHandler:        routers.NewRouter(frontendFiles, s.app, s.ut),\n\t\t\tReadTimeout:    time.Duration(appConfig.Server.ReadTimeout) * time.Second,\n\t\t\tWriteTimeout:   time.Duration(appConfig.Server.WriteTimeout) * time.Second,\n\t\t\tMaxHeaderBytes: 1 << 20,\n\t\t}\n\t\ts.sc.Attach(func(done func(), closeSignal <-chan struct{}) {\n\t\t\tdefer done()\n\t\t\terrChan := make(chan error, 1)\n\t\t\tgo func() {\n\t\t\t\terrChan <- s.httpServer.ListenAndServe()\n\t\t\t}()\n\t\t\tselect {\n\t\t\tcase err := <-errChan:\n\t\t\t\ts.logger.Error(\"api service err\", zap.Error(err))\n\t\t\t\ts.sc.SendCloseSignal(err)\n\t\t\tcase <-closeSignal:\n\n\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\t\t\t\tdefer cancel()\n\n\t\t\t\t// 停止HTTP服务器\n\t\t\t\tif err := s.httpServer.Shutdown(ctx); err != nil {\n\t\t\t\t\ts.logger.Error(\"api service shutdown error\", zap.Error(err))\n\t\t\t\t}\n\n\t\t\t\t// _ = s.httpServer.Close()\n\t\t\t}\n\t\t})\n\t}\n\n\tif httpAddr := appConfig.Server.PrivateHttpListen; len(httpAddr) > 0 {\n\n\t\ts.logger.Info(\"api_router\", zap.String(\"config.server.PrivateHttpListen\", appConfig.Server.PrivateHttpListen))\n\t\ts.privateHttpServer = &http.Server{\n\t\t\tAddr:           appConfig.Server.PrivateHttpListen,\n\t\t\tHandler:        routers.NewPrivateRouterWithLogger(appConfig.Server.RunMode, s.logger),\n\t\t\tReadTimeout:    time.Duration(appConfig.Server.ReadTimeout) * time.Second,\n\t\t\tWriteTimeout:   time.Duration(appConfig.Server.WriteTimeout) * time.Second,\n\t\t\tMaxHeaderBytes: 1 << 20,\n\t\t}\n\n\t\ts.sc.Attach(func(done func(), closeSignal <-chan struct{}) {\n\t\t\tdefer done()\n\t\t\terrChan := make(chan error, 1)\n\t\t\tgo func() {\n\t\t\t\terrChan <- s.privateHttpServer.ListenAndServe()\n\t\t\t}()\n\t\t\tselect {\n\t\t\tcase err := <-errChan:\n\t\t\t\ts.logger.Error(\"private api service err\", zap.Error(err))\n\t\t\t\ts.sc.SendCloseSignal(err)\n\t\t\tcase <-closeSignal:\n\n\t\t\t\t// _ = s.httpServer.Close()\n\n\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\t\t\t\tdefer cancel()\n\n\t\t\t\t// Stop HTTP server\n\t\t\t\t// 停止 HTTP 服务器\n\t\t\t\tif err := s.privateHttpServer.Shutdown(ctx); err != nil {\n\t\t\t\t\ts.logger.Error(\"private api service shutdown error\", zap.Error(err))\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n\n\t// Register App Container graceful shutdown (using Shutdown method)\n\t// 注册 App Container 的优雅关闭（使用 Shutdown 方法）\n\ts.sc.Attach(func(done func(), closeSignal <-chan struct{}) {\n\t\tdefer done()\n\t\t<-closeSignal\n\t\tif s.app != nil {\n\t\t\t// Use graceful shutdown with timeout\n\t\t\t// 使用带超时的优雅关闭\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), DefaultShutdownTimeout)\n\t\t\tdefer cancel()\n\n\t\t\tif err := s.app.Shutdown(ctx); err != nil {\n\t\t\t\ts.logger.Error(\"failed to shutdown app container\", zap.Error(err))\n\t\t\t} else {\n\t\t\t\ts.logger.Info(\"App container shutdown gracefully\")\n\t\t\t}\n\t\t}\n\t})\n\n\t// Start ngrok tunnel if enabled\n\tif appConfig.Ngrok.Enabled && appConfig.Ngrok.AuthToken != \"\" {\n\t\ts.sc.Attach(func(done func(), closeSignal <-chan struct{}) {\n\t\t\tdefer done()\n\n\t\t\ts.logger.Info(\"Starting ngrok tunnel...\")\n\t\t\terr := s.app.NgrokService.Start(context.Background(), appConfig.Server.HttpPort)\n\t\t\tif err != nil {\n\t\t\t\ts.logger.Error(\"failed to start ngrok tunnel\", zap.Error(err))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\ts.logger.Info(\"Ngrok tunnel started\", zap.String(\"url\", s.app.NgrokService.TunnelURL()))\n\n\t\t\t// Stay attached until close signal\n\t\t\t<-closeSignal\n\t\t})\n\t}\n\n\t// Start Cloudflare tunnel if enabled\n\tif appConfig.Cloudflare.Enabled && appConfig.Cloudflare.Token != \"\" {\n\t\ts.sc.Attach(func(done func(), closeSignal <-chan struct{}) {\n\t\t\tdefer done()\n\n\t\t\ts.logger.Info(\"Starting Cloudflare tunnel...\")\n\t\t\tif err := s.app.CloudflareService.Start(context.Background(), appConfig.Cloudflare.Token, appConfig.Cloudflare.LogEnabled); err != nil {\n\t\t\t\ts.logger.Error(\"failed to start cloudflare tunnel\", zap.Error(err))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\ts.logger.Info(\"Cloudflare tunnel started\", zap.String(\"url\", s.app.CloudflareService.TunnelURL()))\n\n\t\t\t// Stay attached until close signal\n\t\t\t<-closeSignal\n\t\t})\n\t}\n\n\treturn s, nil\n}\n\nfunc initScheduler(s *Server) {\n\t// Create task manager\n\t// 创建任务管理器\n\tmanager := task.NewManager(s.logger, s.sc, s.app)\n\n\t// Register all tasks (business layer control)\n\t// 注册所有任务(业务层控制)\n\tif err := manager.RegisterTasks(); err != nil {\n\t\ts.logger.Error(\"failed to register tasks\", zap.Error(err))\n\t\treturn\n\t}\n\n\t// Start task scheduler\n\t// 启动任务调度器\n\tmanager.Start()\n}\n\n// initLoggerWithConfig initializes logger (using injected config)\n// initLoggerWithConfig 初始化日志器（使用注入的配置）\nfunc initLoggerWithConfig(s *Server, cfg *internalApp.AppConfig) error {\n\tlg, err := logger.NewLogger(logger.Config{\n\t\tLevel:      cfg.Log.Level,\n\t\tFile:       cfg.Log.File,\n\t\tProduction: cfg.Log.Production,\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to init logger: %w\", err)\n\t}\n\ts.logger = lg\n\n\treturn nil\n}\n\n// initValidatorWithLogger initializes validator, returns UniversalTranslator\n// initValidatorWithLogger 初始化验证器，返回 UniversalTranslator\nfunc initValidatorWithLogger(lg *zap.Logger) (*ut.UniversalTranslator, error) {\n\tcustomValidator := validator.NewCustomValidator()\n\tcustomValidator.Engine()\n\tbinding.Validator = customValidator\n\n\tvar uni *ut.UniversalTranslator\n\n\tvalidate, ok := binding.Validator.Engine().(*validatorV10.Validate)\n\tif ok {\n\n\t\tvalidate.RegisterTagNameFunc(func(fld reflect.StructField) string {\n\t\t\tname := strings.SplitN(fld.Tag.Get(\"json\"), \",\", 2)[0]\n\t\t\tif name == \"-\" {\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t\treturn name\n\t\t})\n\n\t\tuni = ut.New(en.New(), en.New(), zh.New())\n\n\t\tzhTran, _ := uni.GetTranslator(\"zh\")\n\t\tenTran, _ := uni.GetTranslator(\"en\")\n\n\t\terr := zh_translations.RegisterDefaultTranslations(validate, zhTran)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\terr = en_translations.RegisterDefaultTranslations(validate, enTran)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn uni, nil\n}\n\nfunc initDatabaseWithConfig(cfg *internalApp.AppConfig, lg *zap.Logger) (*gorm.DB, error) {\n\t// Convert AppConfig.DatabaseConfig to config.DatabaseConfig\n\t// 转换 AppConfig.DatabaseConfig 为 config.DatabaseConfig\n\tdbConfig := cfg.Database\n\tdbConfig.RunMode = cfg.Server.RunMode\n\n\tdb, err := dao.NewEngine(dbConfig, lg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn db, nil\n}\n\n// initStorageWithConfig initializes storage directory (using injected config)\n// initStorageWithConfig 初始化存储目录（使用注入的配置）\nfunc initStorageWithConfig(cfg *internalApp.AppConfig) error {\n\tdirs := []string{\n\t\tfilepath.Dir(cfg.Log.File),\n\t\tcfg.App.TempPath,\n\t\tcfg.Storage.LocalFS.SavePath,\n\t\tfilepath.Dir(cfg.Database.Path),\n\t}\n\n\t// 如果 UserDatabase 配置了独立的路径且为 sqlite，也需要初始化目录\n\tif cfg.UserDatabase.Type == \"sqlite\" && cfg.UserDatabase.Path != \"\" {\n\t\tdirs = append(dirs, filepath.Dir(cfg.UserDatabase.Path))\n\t}\n\n\tfor _, dir := range dirs {\n\t\tif dir == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif err := os.MkdirAll(dir, 0754); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create directory %s: %w\", dir, err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// GetApp gets App Container\n// GetApp 获取 App Container\nfunc (s *Server) GetApp() *internalApp.App {\n\treturn s.app\n}\n\n// GetConfig gets app configuration\n// GetConfig 获取应用配置\nfunc (s *Server) GetConfig() *internalApp.AppConfig {\n\treturn s.config\n}\n"
  },
  {
    "path": "cmd/upgrade.go",
    "content": "package cmd\n\nimport (\n\t\"os\"\n\n\tinternalApp \"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dao\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/upgrade\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/logger\"\n\t\"github.com/spf13/cobra\"\n\t\"go.uber.org/zap\"\n)\n\nvar upgradeCmd = &cobra.Command{\n\tUse:   \"upgrade\",\n\tShort: \"Upgrade legacy database schema and other data to the latest version\",\n\tLong: `Upgrade legacy database schema and other data to the latest version.\n\nThis command will check the current database version and apply all pending migrations.\nIt is safe to run this command multiple times - already applied migrations will be skipped.`,\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\t// Load configuration\n\t\t// 加载配置\n\t\tconfigPath, _ := cmd.Flags().GetString(\"config\")\n\t\tif len(configPath) <= 0 {\n\t\t\tconfigPath = \"config/config.yaml\"\n\t\t}\n\n\t\t// Use LoadConfig to directly load config into AppConfig\n\t\t// 使用 LoadConfig 直接加载配置到 AppConfig\n\t\tappConfig, configRealpath, err := internalApp.LoadConfig(configPath)\n\t\tif err != nil {\n\t\t\tbootstrapLogger.Error(\"Failed to load config\", zap.Error(err))\n\t\t\tos.Exit(1)\n\t\t}\n\n\t\tbootstrapLogger.Info(\"Loading config\", zap.String(\"path\", configRealpath))\n\n\t\t// Initialize log\n\t\t// 初始化日志\n\t\tlg, err := logger.NewLogger(logger.Config{\n\t\t\tLevel:      appConfig.Log.Level,\n\t\t\tFile:       appConfig.Log.File,\n\t\t\tProduction: appConfig.Log.Production,\n\t\t})\n\t\tif err != nil {\n\t\t\tbootstrapLogger.Error(\"Failed to init logger\", zap.Error(err))\n\t\t\tos.Exit(1)\n\t\t}\n\n\t\t// Initialize database (using injected config)\n\t\t// 初始化数据库（使用注入的配置）\n\t\tdbConfig := appConfig.Database\n\t\tdbConfig.RunMode = appConfig.Server.RunMode\n\n\t\tdb, err := dao.NewEngine(dbConfig, lg)\n\t\tif err != nil {\n\t\t\tbootstrapLogger.Error(\"Failed to init database\", zap.Error(err))\n\t\t\tos.Exit(1)\n\t\t}\n\n\t\tbootstrapLogger.Info(\"Starting database upgrade...\")\n\n\t\t// Execute upgrade\n\t\t// 执行升级\n\t\tif err := upgrade.Execute(\n\t\t\tdb,\n\t\t\tlg,\n\t\t\tinternalApp.Version,\n\t\t\t&appConfig.Database,\n\t\t\t&appConfig.UserDatabase,\n\t\t); err != nil {\n\t\t\tbootstrapLogger.Error(\"Upgrade failed\", zap.Error(err))\n\t\t\tos.Exit(1)\n\t\t}\n\n\t\tbootstrapLogger.Info(\"Database upgrade completed successfully!\")\n\t},\n}\n\nfunc init() {\n\trootCmd.AddCommand(upgradeCmd)\n\tupgradeCmd.Flags().StringP(\"config\", \"c\", \"\", \"config file path\")\n}\n"
  },
  {
    "path": "cmd/version.go",
    "content": "package cmd\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\n\t\"github.com/spf13/cobra\"\n)\n\nvar versionCmd = &cobra.Command{\n\tUse:   \"version\",\n\tShort: \"Print out version info and exit. // 打印版本信息并退出。\",\n\tRun: func(cmd *cobra.Command, args []string) {\n\t\tfmt.Printf(\"v%s ( Git:%s ) BuidTime:%s\\n\", app.Version, app.GitTag, app.BuildTime)\n\t},\n}\n\nfunc init() {\n\trootCmd.AddCommand(versionCmd)\n}\n"
  },
  {
    "path": "config/config.yaml",
    "content": "server:\n  # 运行模式: release | debug\n  # Running mode: release | debug\n  run-mode: release\n  # HTTP 端口 (默认 :9000)。格式为 :port 或 IP:port\n  # HTTP Port (default :9000). Format: :port or IP:port\n  http-port: :9000\n  # 读取超时时间(秒)\n  # Read timeout duration (seconds)\n  read-timeout: 60\n  # 写入超时时间(秒)\n  # Write timeout duration (seconds)\n  write-timeout: 60\n  # 私有 HTTP 监听地址，主要用于监控/度量。留空则不开启。格式: :port\n  # Private HTTP listen address, used for monitoring/metrics. Leave empty to disable. Format: :port\n  private-http-listen: \"\"\n  # SSE 保活心跳间隔（秒）\n  # MCP SSE ping interval (seconds)\n  mcp-sse-ping-interval: 30\n\napp:\n  # 默认每页显示的项目数\n  # Default items per page\n  default-page-size: 10\n  # 每页显示的最大项目数限制\n  # Maximum items limit per page\n  max-page-size: 100\n  # 默认请求上下文超时时间(秒)\n  # Default request context timeout (seconds)\n  default-context-timeout: 60\n  # 临时文件存储路径\n  # Temporary file storage path\n  temp-path: storage/temp\n  # 是否在响应中返回成功详情消息\n  # Whether to return success detail message in response\n  is-return-sussess: false\n  # 软删除笔记保留时长。例如: 7d, 24h。0 表示永久保留。\n  # Retention duration for soft deleted notes. e.g., 7d, 24h. 0 means keep forever.\n  soft-delete-retention-time: \"7d\"\n  # 同步日志保留时长。例如: 30d, 7d。\n  # Retention duration for sync logs. e.g., 30d, 7d.\n  sync-log-retention-time: \"30d\"\n  # 历史记录保留的最大版本数\n  # Maximum number of history versions to keep\n  history-keep-versions: 100\n  # 历史记录保存延迟。支持格式: 10s, 1m。\n  # delay for saving history records. Supports: 10s, 1m.\n  history-save-delay: \"10s\"\n  # 文件上传会话超时时长\n  # Timeout duration for file upload sessions\n  upload-session-timeout: \"1d\"\n  # 文件上传/下载的分块大小。例如: 512KB, 1MB\n  # Chunk size for file upload/download. e.g., 512KB, 1MB\n  file-chunk-size: \"512KB\"\n  # 文件分片下载超时时长\n  # Timeout duration for file chunk downloading\n  download-session-timeout: \"1h\"\n  # Worker Pool 最大工作协程数\n  # Worker Pool maximum number of worker goroutines\n  worker-pool-max-workers: 100\n  # Worker Pool 任务队列大小\n  # Worker Pool task queue capacity\n  worker-pool-queue-size: 1000\n  # 写入队列容量 (每个用户)\n  # Write queue capacity (per user)\n  write-queue-capacity: 1000\n  # 写入队列操作超时时长\n  # Timeout duration for write queue operations\n  write-queue-timeout: \"30s\"\n  # 写入队列空闲清理时长\n  # Idle cleanup duration for write queue\n  write-queue-idle-time: \"10m\"\n  # WebSocket 读取最大负载大小。例如: 128MB\n  # WebSocket maximum read payload size. e.g., 128MB\n  ws-read-max-payload-size: \"128MB\"\n  # WebSocket 写入最大负载大小。例如: 128MB\n  # WebSocket maximum write payload size. e.g., 128MB\n  ws-write-max-payload-size: \"128MB\"\n  # 是否开启 WebSocket 并行处理\n  # Whether to enable WebSocket parallel processing\n  ws-parallel-enabled: true\n  # WebSocket 并行处理的最大协程限制\n  # Maximum goroutine limit for WebSocket parallel processing\n  ws-parallel-golimit: 8\n  # 是否对 WebSocket 消息进行 UTF-8 校验\n  # Whether to perform UTF-8 validation on WebSocket messages\n  ws-check-utf8-enabled: true\n  # 是否开启 WebSocket 消息压缩\n  # Whether to enable WebSocket message compression\n  ws-compression-enabled: true\n  # WebSocket 压缩级别 (1-9)\n  # WebSocket compression level (1-9)\n  ws-compression-level: 1\n  # 触发 WebSocket 压缩的最小载荷大小(字节)\n  # Minimum payload size (bytes) to trigger WebSocket compression\n  ws-compression-threshold: 512\n  # 日志保存路径\n  # Directory path for log output\n  log-save-fileurl: storage/logs/\n  # 日志文件名\n  # Log filename\n  log-file: log.log\n  # 数据拉取源设置: auto(自动检测) | github | cnb\n  # Data pull source setting: auto(detect) | github | cnb\n  pull-source: auto\n\nsecurity:\n  # 认证令牌加密混淆 Key\n  # Internal key for auth token encryption and obfuscation\n  auth-token-key: fast-note-sync-Auth-Token\n  # 认证令牌过期时间。例如: 365d, 7d, 24h\n  # Expiry duration for authentication tokens. e.g., 365d, 7d, 24h\n  token-expiry: \"365d\"\n  # 分享令牌加密混淆 Key\n  # Internal key for share token encryption and obfuscation\n  share-token-key: fns\n  # 分享令牌过期时间。例如: 30d, 7d\n  # Expiry duration for share tokens. e.g., 30d, 7d\n  share-token-expiry: \"30d\"\n\n# 主数据库配置\n# Main database configuration\ndatabase:\n  # 数据库类型: sqlite | mysql | postgres\n  # Database type: sqlite | mysql | postgres\n  type: sqlite\n  # 数据库文件路径 (针对 sqlite)\n  # Database file path (for sqlite)\n  path: storage/database/db.sqlite3\n  # 数据库连接地址 (针对 mysql/postgres)\n  # Database host (for mysql/postgres)\n  host:\n  # 数据库端口 (针对 mysql/postgres)\n  # Database port (for mysql/postgres)\n  port:\n  # 数据库登录用户名\n  # Database login username\n  username:\n  # 数据库登录密码\n  # Database login password\n  password: \"\"\n  # 数据库名称\n  # Database name\n  name:\n  # SSL 模式 (仅限 postgres)\n  # SSL mode (postgres only)\n  ssl-mode:\n  # 数据库表前缀\n  # Prefix for all database tables\n  table-prefix: \"\"\n  # 数据库 Schema (仅限 postgres)\n  # Database schema (postgres only)\n  schema:\n  # 是否开启自动数据库迁移\n  # Whether to enable automatic database migration\n  auto-migrate: true\n  # 数据库字符集 (默认 utf8mb4)\n  # Database charset (default utf8mb4)\n  charset:\n  # 是否解析时间字段 (针对 mysql)\n  # Whether to parse time fields (for mysql)\n  parse-time: true\n  # 数据库连接池最大打开连接数\n  # Maximum number of open connections in the pool\n  max-open-conns: 100\n  # 数据库连接池最大空闲连接数\n  # Maximum number of idle connections in the pool\n  max-idle-conns: 10\n  # 连接最大可重用时长\n  # Maximum duration a connection can be reused\n  conn-max-lifetime: \"30m\"\n  # 连接处于空闲状态的最大时长\n  # Maximum duration a connection can remain idle\n  conn-max-idle-time: \"10m\"\n  # 是否启用数据库异步写入队列\n  # Whether to enable asynchronous database write queue\n  enable-write-queue: true\n  # 最大并发写入数限制 (当 enable-write-queue 为 false 时)\n  # Maximum concurrent write operations limit (when enable-write-queue is false)\n  max-write-concurrency: 0\n\n# 用户隔离数据库设置, 如果不设置(Type设置为空), 则使用主数据库\n# User isolation database settings, if not set(Type is empty), use the main database\nuser-database:\n  # 数据库类型: sqlite | mysql | postgres\n  # Database type: sqlite | mysql | postgres\n  type:\n  # 数据库文件路径 (针对 sqlite)\n  # Database file path (for sqlite)\n  path:\n  # 数据库连接地址 (针对 mysql/postgres)\n  # Database host (for mysql/postgres)\n  host:\n  # 数据库端口 (针对 mysql/postgres)\n  # Database port (for mysql/postgres)\n  port:\n  # 数据库登录用户名\n  # Database login username\n  username:\n  # 数据库登录密码\n  # Database login password\n  password: \"\"\n  # 数据库名称\n  # Database name\n  name:\n  # SSL 模式 (仅限 postgres)\n  # SSL mode (postgres only)\n  ssl-mode:\n  # 数据库表前缀\n  # Prefix for all database tables\n  table-prefix: \"\"\n  # 数据库 Schema (仅限 postgres)\n  # Database schema (postgres only)\n  schema: public\n  # 是否开启自动数据库迁移\n  # Whether to enable automatic database migration\n  auto-migrate: true\n  # 数据库字符集 (默认 utf8mb4)\n  # Database charset (default utf8mb4)\n  charset:\n  # 是否解析时间字段 (针对 mysql)\n  # Whether to parse time fields (for mysql)\n  parse-time: true\n  # 数据库连接池最大打开连接数\n  # Maximum number of open connections in the pool\n  max-open-conns: 100\n  # 数据库连接池最大空闲连接数\n  # Maximum number of idle connections in the pool\n  max-idle-conns: 10\n  # 连接最大可重用时长\n  # Maximum duration a connection can be reused\n  conn-max-lifetime: \"30m\"\n  # 连接处于空闲状态的最大时长\n  # Maximum duration a connection can remain idle\n  conn-max-idle-time: \"10m\"\n  # 是否启用数据库异步写入队列\n  # Whether to enable asynchronous database write queue\n  enable-write-queue: true\n  # 最大并发写入数限制 (当 enable-write-queue 为 false 时)\n  # Maximum concurrent write operations limit (when enable-write-queue is false)\n  max-write-concurrency: 0\n\nlog:\n  # 日志级别: debug | info | warn | error\n  # Log level: debug | info | warn | error\n  level: warn\n  # 日志输出文件路径\n  # File path for log output\n  file: storage/logs/log.log\n  # 是否为生产环境 (开启后使用 JSON 格式输出)\n  # Whether this is a production environment (uses JSON output if true)\n  production: true\n\nuser:\n  # 是否开启用户注册功能\n  # Whether to enable user registration\n  register-is-enable: true\n  # 管理员 UID。0 表示任何用户都不能作为超级管理员或是未指定。\n  # Administrator UID. 0 means no user is designated or restricted.\n  admin-uid: 0\n\ntracer:\n  # 是否开启请求链路追踪\n  # Whether to enable request tracing\n  enabled: true\n  # 请求头中的 Trace ID 字段名\n  # Header name for the Trace ID\n  header: \"X-Trace-ID\"\n\nshort-link:\n  # 短链服务的基础 URL\n  # Base URL of the short link service\n  base-url: \"https://sink.cool\"\n  # 短链服务的 API Key\n  # API Key for the short link service\n  api-key: \"SinkCool\"\n  # 短链服务的访问密码 (可选)\n  # Access password for the short link service (optional)\n  password: \"\"\n  # 是否开启短链隐藏重定向 (Cloaking)\n  # Whether to enable short link URL cloaking\n  cloaking: false\n\nstorage:\n  local-fs:\n    # 是否启用本地文件系统存储\n    # Whether to enable local file system storage\n    is-enable: false\n    # 是否启用 HTTP 文件服务 (用于提供文件的 HTTP 直接访问)\n    # Whether to enable HTTP file server for direct access\n    httpfs-is-enable: true\n    # 文件保存路径\n    # Directory path for saved files\n    save-path: \"storage/uploads\"\n  aliyun-oss:\n    # 是否启用 阿里云 OSS 存储\n    # Whether to enable Aliyun OSS storage\n    is-enable: true\n  aws-s3:\n    # 是否启用 AWS S3 存储\n    # Whether to enable AWS S3 storage\n    is-enable: true\n  cloudflare-r2:\n    # 是否启用 Cloudflare R2 存储\n    # Whether to enable Cloudflare R2 storage\n    is-enable: true\n  minio:\n    # 是否启用 MinIO 存储\n    # Whether to enable MinIO storage\n    is-enable: true\n  webdav:\n    # 是否启用 WebDAV 存储\n    # Whether to enable WebDAV storage\n    is-enable: true\n\ngit:\n  # Git 提交记录中的作者名称\n  # Author name used in git commits\n  name: \"FNS Service\"\n  # Git 提交记录中的作者邮箱\n  # Author email used in git commits\n  email: \"fns@email.com\"\n\nwebgui:\n  # Web 界面字体设置。留空使用默认，\"local\" 使用本地字体，或填入字体链接。\n  # Web GUI font settings. Leave blank for default, \"local\" for local fonts, or a font URL.\n  font-set: \"local\"\n\nngrok:\n  # 是否启用 ngrok 内网穿透隧道\n  # Whether to enable ngrok tunnel\n  enabled: false\n  # ngrok 认证令牌 (Authtoken)\n  # ngrok authentication token\n  auth-token: \"\"\n  # ngrok 自定义域名 (需付费计划支持)\n  # ngrok custom domain (restricted to paid plans)\n  domain: \"\"\n\ncloudflare:\n  # 是否启用 Cloudflare Tunnel (穿透)\n  # Whether to enable Cloudflare Tunnel\n  enabled: false\n  # Cloudflare Tunnel 访问令牌 (Token)\n  # Cloudflare Tunnel access token\n  token: \"\"\n  # 是否开启 Cloudflare 隧道相关的详细日志\n  # Whether to enable detailed logs for Cloudflare Tunnel\n  log-enabled: false\n"
  },
  {
    "path": "docker/Dockerfile",
    "content": "FROM woahbase/alpine-glibc:latest\nMAINTAINER HaierKeys <haierkeys@gmail.com>\nARG TARGETOS\nARG TARGETARCH\nARG VERSION\nARG BUILD_DATE\nARG GIT_COMMIT\n\nARG VERSION=${VERSION}\nARG BUILD_DATE=${BUILD_DATE}\nARG GIT_COMMIT=${GIT_COMMIT}\n\n\n\nLABEL name=\"fast-note-sync-service\"\nLABEL version=${VERSION}\nLABEL description=\"Provide image resizing, cropping, upload/download, and cloud storage features for Obsidian CIAU.\"\nLABEL maintainer=\"HaierKeys <haierkeys@gmail.com>\"\n\n\nLABEL org.opencontainers.image.title=\"Fast Note Sync Service\"\nLABEL org.opencontainers.image.created=${BUILD_DATE}\nLABEL org.opencontainers.image.authors=\"HaierKeys <haierkeys@gmail.com>\"\nLABEL org.opencontainers.image.version=${VERSION}\nLABEL org.opencontainers.image.description=\"Provide image resizing, cropping, upload/download, and cloud storage features for Obsidian CIAU.\"\nLABEL org.opencontainers.image.url=\"https://github.com/haierkeys/fast-note-sync-service\"\nLABEL org.opencontainers.image.source=\"https://github.com/haierkeys/fast-note-sync-service\"\nLABEL org.opencontainers.image.documentation=\"https://raw.githubusercontent.com/haierkeys/fast-note-sync-service/refs/heads/main/README.md\"\nLABEL org.opencontainers.image.revision=${GIT_COMMIT}\nLABEL org.opencontainers.image.licenses=\"Apache-2.0\"\nLABEL org.opencontainers.image.vendor=\"HaierKeys\"\n\n\n\nENV TZ=Asia/Shanghai\nENV P_NAME=fast-note-sync\nENV P_BIN=fast-note-sync-service\nRUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \\\n    apk --no-cache add --no-progress libstdc++ curl ca-certificates bash gcompat tzdata && \\\n    cp /usr/share/zoneinfo/${TZ} /etc/localtime && \\\n    echo ${TZ} > /etc/timezone && \\\n    mkdir -p /${P_NAME}/\n\nEXPOSE 9000 9001\nVOLUME /${P_NAME}/config\nVOLUME /${P_NAME}/storage\nCOPY ./build/${TARGETOS}_${TARGETARCH}/${P_BIN} /${P_NAME}/\nCOPY ./docker/entrypoint.sh  /entrypoint.sh\n\nRUN chmod +x /entrypoint.sh /${P_NAME}/${P_BIN}\n\nENTRYPOINT [\"/entrypoint.sh\"]"
  },
  {
    "path": "docker/docker-compose.yaml",
    "content": "services:\n  fast-note-sync-service:\n    image: haierkeys/fast-note-sync-service:latest\n    container_name: fast-note-sync-service\n    ports:\n      - \"9000:9000\"\n      - \"9001:9001\"\n    volumes:\n      - /data/fast-note-sync/storage/:/fast-note-sync/storage/\n      - /data/fast-note-sync/config/:/fast-note-sync/config/\n    networks:\n      - app-network  # 与 image-api 在同一网络1\n\n"
  },
  {
    "path": "docker/docker_image_clean.sh",
    "content": "#!/bin/sh\necho \"docker images clean shell\"\n\nprojectName=$(basename \"$(pwd)\")\n\n# 删除匹配 repository 名称（任意 tag）的镜像\ndockerrm=$(docker images --filter \"reference=${projectName}:*\" -q | sort -u)\nif [ -n \"$dockerrm\" ]; then\n    echo \"$dockerrm\" | xargs -r docker rmi -f\n    echo \"docker images ${projectName} clean OK\"\nfi\n\n# 删除 dangling (none) 镜像\ndockerrm=$(docker images -f \"dangling=true\" -q | sort -u)\nif [ -n \"$dockerrm\" ]; then\n    echo \"$dockerrm\" | xargs -r docker rmi -f\n    echo \"docker images none clean OK\"\nfi\n"
  },
  {
    "path": "docker/docker_redeploy.sh",
    "content": "#!/bin/bash\n\nProjectRegistry=\"registry.cn-shanghai.aliyuncs.com/xxx/xxxxx\"\nProjectPath=`pwd`\nProjectName=\"xxxxx\"\n\nUsage() {\n    echo \"Usage:\"\n    echo \"test.sh [-t Git tag]\"\n    echo \"Description:\"\n    exit\n}\n\nwhile getopts ':t:h:' OPT; do\n    case $OPT in\n        t) TAG=\"$OPTARG\";;\n        h) Usage;;\n        ?) Usage;;\n    esac\ndone\n\nif [ ${TAG} ];then\n    docker pull $ProjectRegistry:$TAG\n\n    echo \"Stop \"$ProjectName\n\n    docker stop $ProjectName\n    #docker rm -v\n    docker rm -f $ProjectName\n    echo \"Start new xxxxx\"\n    docker run -tid --name $ProjectName \\\n        -p 8000:8000 -p 8001:8001 -p 8002:8002 \\\n        -v $ProjectPath/storage/:/api/storage/ \\\n        -v $ProjectPath/configs/:/api/configs/ \\\n        $ProjectRegistry:$TAG\nfi\n\n\n"
  },
  {
    "path": "docker/entrypoint.sh",
    "content": "#!/bin/sh\n\n# 检查环境变量\nif [ -z \"$P_NAME\" ] || [ -z \"$P_BIN\" ]; then\n    echo \"Error: P_NAME or P_BIN not set\"\n    exit 1\nfi\n\n# 切换目录\ncd \"/${P_NAME}/\" || { echo \"Failed to cd to /${P_NAME}/\"; exit 1; }\n\n# 创建日志目录和文件\nmkdir -p storage/logs || { echo \"Failed to create logs dir\"; exit 1; }\ntouch storage/logs/c.log || { echo \"Failed to create c.log\"; exit 1; }\n\n# 备份旧日志，统一后缀为 .log\nmv storage/logs/c.log \"storage/logs/c_$(date '+%Y%m%d%H%M%S').log\" || { echo \"Failed to rename log\"; exit 1; }\n\n# 运行程序并记录日志到 c.log\n\"/${P_NAME}/${P_BIN}\" run 2>&1 | tee storage/logs/c.log"
  },
  {
    "path": "docs/API-EXTENSIONS.md",
    "content": "# API Extensions for Fast Note Sync Service\n\nThis document describes the new API endpoints added in the `feature/api-extensions` branch.\n\n## New Endpoints\n\n### Health Check\n```\nGET /api/health\n```\nReturns server health status including database connectivity and uptime.\n\n**Response:**\n```json\n{\n  \"status\": \"healthy\",\n  \"version\": \"1.11.1\",\n  \"uptime\": 123.45,\n  \"database\": \"connected\"\n}\n```\n\n### Note Operations\n\n#### Patch Frontmatter\n```\nPATCH /api/note/frontmatter?vault=<vault>&path=<path>\n```\nUpdate or remove YAML frontmatter fields without modifying note body.\n\n**Body:**\n```json\n{\n  \"updates\": {\"title\": \"New Title\", \"tags\": [\"a\", \"b\"]},\n  \"remove\": [\"oldField\"]\n}\n```\n\n#### Append Content\n```\nPOST /api/note/append?vault=<vault>&path=<path>\n```\nAppend content to the end of a note.\n\n**Body:**\n```json\n{\n  \"content\": \"\\n\\n## New Section\\nAppended content\"\n}\n```\n\n#### Prepend Content\n```\nPOST /api/note/prepend?vault=<vault>&path=<path>\n```\nPrepend content after frontmatter (if present) or at the beginning.\n\n**Body:**\n```json\n{\n  \"content\": \"Prepended content\\n\\n\"\n}\n```\n\n#### Find and Replace\n```\nPOST /api/note/replace?vault=<vault>&path=<path>\n```\nFind and replace text in a note. Supports regex.\n\n**Body:**\n```json\n{\n  \"find\": \"old text\",\n  \"replace\": \"new text\",\n  \"regex\": false,\n  \"all\": true,\n  \"failIfNoMatch\": false\n}\n```\n\n**Response includes `matchCount`:**\n```json\n{\n  \"matchCount\": 3,\n  \"note\": { ... }\n}\n```\n\n#### Move Note\n```\nPOST /api/note/move?vault=<vault>&path=<path>\n```\nMove/rename a note to a new path.\n\n**Body:**\n```json\n{\n  \"destination\": \"new/path/note.md\",\n  \"overwrite\": false\n}\n```\n\n### Link Operations\n\n#### Get Backlinks\n```\nGET /api/note/backlinks?vault=<vault>&path=<path>\n```\nGet all notes that link TO this note.\n\n**Response:**\n```json\n{\n  \"data\": [\n    {\n      \"path\": \"other-note.md\",\n      \"linkText\": \"alias\",\n      \"context\": \"...surrounding text [[link]]...\"\n    }\n  ]\n}\n```\n\n#### Get Outlinks\n```\nGET /api/note/outlinks?vault=<vault>&path=<path>\n```\nGet all links FROM this note.\n\n**Response:**\n```json\n{\n  \"data\": [\n    {\n      \"path\": \"target-note\",\n      \"linkText\": \"display text\",\n      \"context\": \"...[[target-note|display text]]...\"\n    }\n  ]\n}\n```\n\n### Note Creation\n\n#### Create Only (Don't Update)\n```\nPOST /api/note?vault=<vault>&path=<path>\n```\nWith `createOnly: true`, returns error 430 if note already exists.\n\n**Body:**\n```json\n{\n  \"content\": \"...\",\n  \"createOnly\": true\n}\n```\n\n## New Error Codes\n\n| Code | Message |\n|------|---------|\n| 460 | Destination note already exists |\n| 461 | No match found |\n| 462 | Invalid regex pattern |\n\n## Configuration\n\nNew config option in `config.yaml` (also editable via Admin Settings API):\n```yaml\napp:\n  default-api-folder: \"\"  # Optional: prepend this folder to note paths without /\n```\n\n## Known Limitations\n\n### Backlinks\n\n1. **Exact path matching only**: Backlinks match the stored link path exactly. Obsidian's \"shortest path when possible\" resolution is not fully replicated server-side.\n\n2. **Extension handling**: Links are stored without `.md` extension (e.g., `[[Note1]]` stores \"Note1\"). Queries with full paths (e.g., \"Note1.md\") are normalized by stripping `.md`.\n\n3. **Heading anchors**: Links with `#heading` (e.g., `[[note#section]]`) are stored as-is. Querying backlinks for \"note.md\" won't find links to \"note#section\".\n\n4. **Relative paths**: Links using relative paths (e.g., `[[../folder/note]]`) are stored as-is. Cross-folder resolution is not performed.\n\n### Link Indexing\n\n- Links are indexed when notes are saved via the API\n- Existing notes need to be re-saved to populate the link index\n- Link parsing uses regex: `\\[\\[([^\\]|]+)(?:\\|([^\\]]+))?\\]\\]`\n\n### Version History\n\n- Version numbers increment on each content change\n- History entries are created asynchronously (with delay)\n- Move operations migrate history from source to destination note\n\n## Testing\n\nRun the test scripts:\n```bash\n# Basic API tests (41 tests)\n./test-api.sh\n\n# Edge case tests\n./test-edge-cases.sh\n```\n\n## Files Changed\n\n### New Files\n- `internal/routers/api_router/handler_health.go`\n- `internal/domain/domain_note_link.go`\n- `internal/model/note_link.gen.go`\n- `internal/dao/note_link_repository.go`\n- `internal/service/note_link_service.go`\n- `pkg/util/frontmatter.go`\n- `pkg/util/link_parser.go`\n- `pkg/util/path.go`\n- `test-api.sh`\n- `test-edge-cases.sh`\n\n### Modified Files\n- `config/config.yaml`\n- `internal/app/app.go`\n- `internal/app/config.go`\n- `internal/dao/note_repository.go`\n- `internal/domain/repository.go`\n- `internal/dto/note_dto.go`\n- `internal/model/model.go`\n- `internal/routers/api_router/handler_note.go`\n- `internal/routers/router.go`\n- `internal/service/note_service.go`\n- `pkg/code/common.go`\n"
  },
  {
    "path": "docs/CHANGELOG.en.md",
    "content": "# CHANGELOG\n\nAll notable changes to this project will be documented in this file.\n\nThe project adheres to [Keep a Changelog](https://keepachangelog.com/en/0.3.0/) guidelines.\n\n---\n\n## v1.16.2\n> *2026/02/14*\n\n### 🚀 Optimized\n\n- **WebGui**: Adjusted WebGui interface position.\n- **Features**: Added display of server information.\n- **Performance**: Optimized list height for zero-copy access to fix issues with low display height in various lists.\n- **Sync**: Optimized note/attachment sync logic.\n\n---\n\n## v1.16.1\n> *2026/02/14*\n\n### 🚀 Optimized\n\n- **WebGui**: Adjusted WebGui interface position.\n- **Features**: Added display of server information.\n\n---\n\n## v1.15.11\n> *2026/02/14*\n\n### 🚀 Optimized\n\n- **WebGui**: Optimized WebGui interface and added URL support.\n\n---\n\n## v1.15.10\n> *2026/02/14*\n\n### 🚀 Optimized\n\n- **Architecture**: Adjusted service toolkit.\n- **API**: Adjusted API response structure.\n\n---\n\n## v1.15.9\n> *2026/02/14*\n\n### ✨ Added\n\n- **Tools**: Added access entry for fns docs and ws debug tools.\n\n---\n\n## v1.15.8\n> *2026/02/13*\n\n### 🛠️ Fixed\n\n- **Stability**: Fixed minor BUG in time processing.\n\n---\n\n## v1.15.7\n> *2026/02/13*\n\n### 🛠️ Fixed\n\n- **Sync**: Fixed issue with offline deletion not clearing local hash table.\n\n---\n\n## v1.15.6\n> *2026/02/13*\n\n### 🛠️ Fixed\n\n- **Scripts**: Fixed fns shortcut script running issue on macOS.\n- **Logging**: Fixed log printing content.\n\n---\n\n## v1.15.5\n> *2026/02/12*\n\n### 🚀 Optimized\n\n- **CI/CD**: Adjusted GitHub Action to use go mod version for building and publishing.\n\n---\n\n## v1.15.4\n> *2026/02/12*\n\n### ✨ Added\n\n- **Sync**: Added feature to clear note configuration related messages.\n\n---\n\n## v1.15.3\n> *2026/02/10*\n\n### 🛠️ Fixed\n\n- **Folder**: Added fallback solution for duplicate folders and startup task to clear duplicates.\n\n---\n\n## v1.15.2\n> *2026/02/09*\n\n### 🚀 Optimized\n\n- **Database**: Optimized DB performance and structure, performed batch formatting.\n\n---\n\n## v1.15.1\n> *2026/02/07*\n\n### ✨ Added\n\n- **Folder**: Added folder management features, including models and related logic.\n- **Sync**: Fixed potential data race issues and optimized note/attachment renaming.\n\n---\n\n## v1.14.1\n> *2026/01/31*\n\n### ✨ Added\n\n- **Trash**: Added trash and batch recovery for attachment management.\n\n### 🛠️ Fixed\n\n- **Stability**: Fixed issue where resources were not created correctly due to identical modified time and content in attachments/config files.\n\n### 🚀 Optimized\n\n- **API**: Optimized attachment view/download interfaces with zero-copy access.\n- **WebGui**: Fixed low display height issues in various lists.\n\n---\n\n## v1.14.0\n> *2026/01/31*\n\n### ✨ Added\n\n- **Trash**: Added trash for attachment management.\n- **WebGui**: Added display of server information.\n- **Sync**: Added note and attachment renaming features.\n\n### 🛠️ Fixed\n\n- **Stability**: Fixed potential data race issues.\n\n---\n\n## v1.13.0\n> *2026/01/30*\n\n### ✨ Added\n- **Sync**: Added offline deletion synchronization for attachments, notes, and configs.\n- **Sync**: Added auto-download of missing files in incremental sync mode.\n\n---\n\n## v1.12.0\n> *2026/01/29*\n\n### 🚀 Optimized\n- **Language**: Translated/updated all code comments and documentation to bilingual (CN/EN) or English.\n- **API**: Improved internationalization (i18n) for API response messages.\n- **Stability**: Fixed automatic resource prefix issues.\n- **API**: Added API extensions: edit operations, backlinks, and health checks.\n\n---\n\n## v1.11.3\n> *2026/01/27*\n\n### 🛠️ Fixed\n- **Attachment**: Fixed attachment download timeout (30s) error; now configurable, default is 1 hour.\n\n---\n\n## v1.11.2\n> *2026/01/27*\n\n### ✨ Added\n- **WebGui**: Added Obsidian SSO auto-authorization mechanism.\n\n### 🚀 Optimized\n- **WebGui**: Improved authorization configuration UI.\n\n---\n\n## v1.11.1\n> *2026/01/26*\n\n### 🚀 Optimized\n- **Release**: Adjusted version release workflow.\n\n---\n\n## v1.11.0\n> *2026/01/26*\n\n### ✨ Added\n- **Feature**: Added version detection and version information retrieval features.\n\n---\n\n## v1.10.8\n> *2026/01/26*\n\n### ✨ Added\n- **API**: Added attachment status detection interface.\n\n---\n\n## v1.10.7\n> *2026/01/25*\n\n### 🛠️ Fixed\n- **Stability**: Fixed server crash caused by consistency checks during file uploads.\n\n---\n\n## v1.10.6\n> *2026/01/24*\n\n### ✨ Added\n- **WebGui**: Added pagination for the attachment management page.\n\n---\n\n## v1.10.5\n> *2026/01/23*\n\n### 🛠️ Fixed\n- **Trash**: Fixed issues when restoring notes/versions from the trash and history.\n\n---\n\n## v1.10.4\n> *2026/01/23*\n\n### 🛠️ Fixed\n- **Attachment**: Fixed connection drops during attachment uploads and lowered error logging level for shard upload failures.\n\n---\n\n## v1.10.3\n> *2026/01/20*\n\n### 🚀 Optimized\n- **WebGui**: Replaced zoom effect in note vault list with a selected shadow effect.\n\n### 🛠️ Fixed\n- **WebGui**: Fixed a bug where note vaults with special characters in their names were inaccessible.\n\n---\n\n## v1.10.2\n> *2026/01/20*\n\n### 🛠️ Fixed\n- **Admin**: Fixed bugs preventing new user registration and the ability to disable user registration.\n\n---\n\n## v1.10.1\n> *2026/01/20*\n\n### 🛠️ Fixed\n- **Admin**: Fixed issues with new user registration.\n\n---\n\n## v1.10.0\n> *2026/01/19*\n\n### ✨ Added\n- **Attachment**: Added attachment management functionality.\n- **Auth**: Added configuration for Token expiration time.\n- **Share**: Added interfaces for sharing functionality.\n- **Docs**: Added Swagger API documentation.\n\n### 🚀 Optimized\n- **WebGui**: Adjusted WebGui deployment path.\n- **API**: Refined API error messages.\n\n### 🛠️ Fixed\n- **WebGui**: Fixed notice issues caused by WebGui auto-translation.\n\n---\n\n## v1.9.1\n> *2026/01/14*\n\n### 🚀 Optimized\n- **WebGui**: Added blue color scheme and optimized editor display.\n\n---\n\n## v1.9.0\n> *2026/01/14*\n\n### ✨ Added\n- **WebGui**: Complete UI refactor (contributed by @ZyphrZero).\n- **WebGui**: Replaced editor with Vditor, supporting rich text and Markdown real-time rendering.\n- **WebGui**: Supported custom note search, list field sorting, and color themes.\n- **WebGui**: Added dark mode, online version detection, and trash restoration.\n- **Settings**: Added historical version retention and save delay settings.\n\n### 🚀 Optimized\n- **Security**: Optimized service token encryption obfuscation characters.\n\n---\n\n## v1.8.1\n> *2026/01/12*\n\n### 🔄 Changed\n- **Architecture**: Introduced DDD layered architecture (contributed by @ZyphrZero), removed global variables, and implemented Dependency Injection pattern.\n\n### 🚀 Optimized\n- **Sync**: Optimized offline note merging with line-level conflict detection and 3-way merge.\n- **Performance**: Added Worker Pool and Per-User Write Queue to solve SQLite concurrency lock issues.\n- **WebSocket**: Optimized Context lifecycle management and enhanced TraceID tracking.\n\n### 🛠️ Fixed\n- **Logic**: Fixed a bug where note renaming could lead to note loss and errors.\n\n---\n\n## v1.7.3\n> *2026/01/09*\n\n### 🛠️ Fixed\n- **Database**: Added友好 error message for database creation failures.\n\n---\n\n## v1.7.2\n> *2026/01/09*\n\n### ✨ Added\n- **WebGui**: Added configuration settings functionality and related interfaces.\n- **Admin**: Added Admin ID setting.\n\n---\n\n## v1.7.1\n> *2026/01/09*\n\n### ✨ Added\n- **Sync**: Added offline device note editing merge functionality (requires plugin v1.7+).\n\n---\n\n## v1.6.3\n> *2026/01/08*\n\n### 🚀 Optimized\n- **WebGui**: Optimized note list search.\n- **WebGui**: Added icon display.\n- **WebGui**: Added attachment display and refresh button in note vault.\n\n### 🛠️ Fixed\n- **Stability**: Fixed potential exceptions during concurrent queries.\n\n---\n\n## v1.6.1\n> *2026/01/07*\n\n### 🚀 Optimized\n- **Performance**: Optimized sync efficiency and data processing for large note vaults (requires plugin v1.6+).\n- **Cache**: Added browser caching mechanism for static content.\n\n> [!CAUTION]\n> This version involves database structure optimization. It is recommended to delete the DB file under `storage/database` on the server; note modification history will be regenerated.\n\n---\n\n## v1.5.4\n> *2026/01/06*\n\n### 🛠️ Fixed\n- **Attachment**: Fixed occasional errors when uploading attachments.\n\n---\n\n## v1.5.3\n> *2026/01/06*\n\n### 🚀 Optimized\n- **WebGui**: Lazy-loaded editing features to improve home page loading speed.\n\n---\n\n## v1.5.2\n> *2026/01/05*\n\n### 🛠️ Fixed\n- **Sync**: Fixed inaccurate sync task progress display.\n\n---\n\n## v1.5.1\n> *2026/01/04*\n\n### 🛠️ Fixed\n- **Logic**: Fixed a bug where notes couldn't be deleted properly after renaming.\n- **Stability**: Fixed WebSocket connection resets during large-scale note synchronization.\n- **i18n**: Fixed WebGui API language errors.\n\n---\n\n## v1.5.0\n> *2026/01/04*\n\n### ✨ Added\n- **Trash**: Added note trash bin feature.\n- **WebGui**: Added user status detection.\n- **WebGui**: Added registration closed detection on the sign-up page.\n- **WebGui**: Added keyboard shortcut support for operation confirmations.\n\n### 🚀 Optimized\n- **WebGui**: Improved note editing user experience.\n- **Database**: Optimized and resolved database concurrent access issues.\n\n### 🛠️ Fixed\n- **Script**: Fixed a bug where shortcut scripts might overwrite configuration files.\n\n---\n\n## v1.4.7\n> *2026/01/03*\n\n### 🛠️ Fixed\n- **Database**: Attempted to solve SQLite concurrency issues and corrected internal error codes.\n\n---\n\n## v1.4.6\n> *2026/01/03*\n\n### 🛠️ Fixed\n- **Docker**: Fixed an issue where the `temp` directory did not exist in Docker environments.\n\n---\n\n## v1.4.5\n> *2026/01/03*\n\n### 🛠️ Fixed\n- **Sync**: Fixed an issue where attachments couldn't be synced during initial or full sync (requires plugin v1.5.14+).\n\n---\n\n## v1.4.4\n> *2026/01/02*\n\n### 🛠️ Fixed\n- **Access**: Fixed accessibility issues with titles containing Emojis.\n\n### ✨ Added\n- **Docs**: Added help file.\n\n---\n\n## v1.4.3\n> *2026/01/02*\n\n### 🔄 Changed\n- **Vault**: Note vault deletion operation changed to soft delete.\n\n---\n\n## v1.4.2\n> *2026/01/01*\n\n### ✨ Added\n- **WebGui**: Added a red confirmation popup for note deletions to prevent accidental deletion.\n\n---\n\n## v1.4.1\n> *2025/12/31*\n\n### 🚀 Optimized\n- **API**: Added ETag browser caching for note resource (images, etc.) download interface to improve loading speed.\n\n---\n\n## v1.4.0\n> *2025/12/31*\n\n### ✨ Added\n- **WebGui**: Added maximize button to enhance full-screen editing experience.\n- **WebGui**: Supported display of Obsidian embedded images, PDFs, and other attachments in note view.\n- **API**: Added resource download interface.\n\n---\n\n## v1.3.8\n> *2025/12/31*\n\n### 🚀 Optimized\n- **Server**: Established a content hash version repository for notes to facilitate future tracing, comparison, and merging.\n\n---\n\n## v1.3.7\n> *2025/12/30*\n\n### 🛠️ Fixed\n- **Stability**: Added panic recovery for tasks and upgrade scripts to prevent service crashes.\n- **Stability**: Fixed Nil Pointer Panic issues in various layers.\n\n---\n\n## v1.3.6\n> *2025/12/30*\n\n### 🛠️ Fixed\n- **Task Management**: Fixed errors in the task manager.\n\n---\n\n## v1.3.5\n> *2025/12/30*\n\n### 🚀 Optimized\n- **WebGui**: Optimized note viewing display.\n- **Script**: Optimized one-click installation/management script.\n\n---\n\n## v1.3.4\n> *2025/12/30*\n\n### 🛠️ Fixed\n- **Sync**: Fixed sync command processing errors leading to incorrect file synchronization across clients.\n- **Script**: Fixed one-click scripts closing the service upon `Ctrl+C`.\n\n---\n\n## v1.3.3\n> *2025/12/29*\n\n### 🛠️ Fixed\n- **Sync**: Resolved potential update confusion across multiple note vaults for a single user.\n\n---\n\n## v1.3.2\n> *2025/12/28*\n\n### ✨ Added\n- **i18n**: Added support for multi-language environments.\n\n### 🚀 Optimized\n- **WebGui**: Optimized note version diff display.\n\n---\n\n## v1.3.1\n> *2025/12/28*\n\n### 🚀 Optimized\n- **Logic**: Optimized logic for note title modification.\n\n---\n\n## v1.3.0\n> *2025/12/28*\n\n### ✨ Added\n- **WebGui**: Added setting for users to control WebGui font settings.\n\n---\n\n## v1.2.6\n> *2025/12/27*\n\n### 🚀 Optimized\n- **WebGui**: Optimized font loading logic to avoid UI stuttering.\n\n---\n\n## v1.2.5\n> *2025/12/27*\n\n### ✨ Added\n- **Client**: Added record support for client names.\n\n### 🚀 Optimized\n- **Cleanup**: Added sync cleanup logic after note renaming.\n\n---\n\n## v1.2.4\n> *2025/12/27*\n\n### 🛠️ Fixed\n- **WebGui**: Fixed display bug when history version content is empty.\n\n---\n\n## v1.2.3\n> *2025/12/27*\n\n### ✨ Added\n- **API**: Added note history related interfaces and functions.\n\n### 🚀 Optimized\n- **Database**: Optimized database query efficiency.\n- **WebGui**: Changed WebGui display font and fixed various display bugs.\n\n### 🛠️ Fixed\n- **Stability**: Fixed issues during high concurrent access.\n\n---\n\n## v1.2.2\n> *2025/12/27*\n\n### 🛠️ Fixed\n- **WebGui**: Fixed blank page issues caused by empty note history.\n\n---\n\n## v1.2.1\n> *2025/12/27*\n\n### ✨ Added\n- **API**: Added note history related interfaces and functions.\n\n### 🚀 Optimized\n- **Database**: Optimized database query efficiency.\n- **Stability**: Resolved stability issues during high concurrent access.\n\n---\n\n## v1.0.4\n> *2025/12/26*\n\n### 🛠️ Fixed\n- **WebGui**: Fixed blank display issues caused by WebGui build exceptions.\n\n---\n\n## v1.0.3\n> *2025/12/25*\n\n### 🛠️ Fixed\n- **WebGui**: Resolved layout issues caused by long note titles.\n\n---\n\n## v1.0.2\n> *2025/12/25*\n\n### 🚀 Optimized\n- **Attachment**: Optimized attachment upload logic, significantly reducing upload time.\n\n### 🛠️ Fixed\n- **CI/CD**: Corrected GitHub Action update limits.\n\n---\n\n## v1.0.1\n> *2025/12/23*\n\n### 🛠️ Fixed\n- **Permission**: Fixed permission issues during upload on some systems.\n\n---\n\n## v1.0.0\n> *2025/12/22*\n\n### ✨ Added\n- **Sync**: Added configuration file synchronization features and interfaces.\n\n### 🚀 Optimized\n- **Script**: Optimized script output display.\n\n### 🛠️ Fixed\n- **Script**: Fixed script execution control failures.\n\n---\n\n## v0.11.5\n> *2025/12/19*\n\n### 🛠️ Fixed\n- **Docker**: Fixed Docker image execution issues.\n\n---\n\n## v0.11.4\n> *2025/12/18*\n\n### ✨ Added\n- **Auth**: Added version information downlink in the authorization validation interface.\n\n---\n\n## v0.11.3\n> *2025/12/16*\n\n### ✨ Added\n- **Cleanup**: Added auto-cleanup tasks on startup and Session auto-cleanup logic.\n\n### 🛠️ Fixed\n- **Stability**: Fixed abnormal exit issues during high concurrency due to connection closures.\n\n---\n\n## v0.11.2\n> *2025/12/15*\n\n### 🛠️ Fixed\n- **Stability**: Fixed abnormal exit issues during concurrency due to connection closures.\n\n---\n\n## v0.11.1\n> *2025/12/14*\n\n### ✨ Added\n- **Architecture**: Added prefix to messages for future business expansion.\n\n---\n\n## v0.10.2\n> *2025/12/12*\n\n### ✨ Added\n- **Settings**: Added shard settings for upload/download (default 512KB).\n\n---\n\n## v0.10.1\n> *2025/12/12*\n\n### ✨ Added\n- **Feature**: Added binary file download feature.\n- **Feature**: Added WebSocket chunked download feature.\n- **Feature**: Added version control management.\n\n---\n\n## v0.9.6\n> *2025/12/11*\n\n- Initial release (recording started).\n"
  },
  {
    "path": "docs/CHANGELOG.ja.md",
    "content": "# 更新履歴 (CHANGELOG)\n\nこのプロジェクトのすべての重要な変更がこのファイルに記録されます。\n\nこのプロジェクトは [Keep a Changelog](https://keepachangelog.com/ja/0.3.0/) 規範に従っています。\n\n---\n\n## v1.16.2\n> *2026/02/14*\n\n### 🚀 改善\n\n- **WebGui**: WebGui インターフェースの位置を調整。\n- **機能**: サーバー情報の表示を追加。\n- **パフォーマンス**: 各種リストの表示高さが低すぎる問題に対し、ゼロコピーアクセスによる最適化を実施。\n- **同期**: ノート/添付ファイルの同期ロジックを最適化。\n\n---\n\n## v1.16.1\n> *2026/02/14*\n\n### 🚀 改善\n\n- **WebGui**: WebGui インターフェースの位置を調整。\n- **機能**: サーバー情報の表示を追加。\n\n---\n\n## v1.15.11\n> *2026/02/14*\n\n### 🚀 改善\n\n- **WebGui**: WebGui インターフェースを最適化し、URL サポートを追加。\n\n---\n\n## v1.15.10\n> *2026/02/14*\n\n### 🚀 改善\n\n- **アーキテクチャ**: サービスツールセットを調整。\n- **API**: インターフェースのレスポンス構造を調整。\n\n---\n\n## v1.15.9\n> *2026/02/14*\n\n### ✨ 新機能\n\n- **ツール**: fns に docs および ws デバッグツールのアクセスエントリを追加。\n\n---\n\n## v1.15.8\n> *2026/02/13*\n\n### 🛠️ 修正\n\n- **安定性**: 時間処理の軽微なバグを修正。\n\n---\n\n## v1.15.7\n> *2026/02/13*\n\n### 🛠️ 修正\n\n- **同期**: オフライン削除時にローカルハッシュテーブルがクリアされない問題を修正。\n\n---\n\n## v1.15.6\n> *2026/02/13*\n\n### 🛠️ 修正\n\n- **スクリプト**: macOS 下での fns ショートカットスクリプトの実行問題を修正。\n- **ログ**: ログ出力内容を修正。\n\n---\n\n## v1.15.5\n> *2026/02/12*\n\n### 🚀 改善\n\n- **CI/CD**: GitHub Action で go mod バージョンを使用してビルド・リリースするように調整。\n\n---\n\n## v1.15.4\n> *2026/02/12*\n\n### ✨ 新機能\n\n- **同期**: ノート設定関連メッセージのクリア機能を追加。\n\n---\n\n## v1.15.3\n> *2026/02/10*\n\n### 🛠️ 修正\n\n- **ディレクトリ**: 重複ディレクトリに対する回避策を追加し、起動時に重複ディレクトリをクリーンアップする機能を追加。\n\n---\n\n## v1.15.2\n> *2026/02/09*\n\n### 🚀 改善\n\n- **データベース**: DB のパフォーマンスと構造を最適化し、一括フォーマットを実施。\n\n---\n\n## v1.15.1\n> *2026/02/07*\n\n### ✨ 新機能\n\n- **ディレクトリ**: ディレクトリ（Folder）管理機能を追加（Model および関連ロジックを含む）。\n- **同期**: 潜在的なデータ競合問題を修正し、ノートと添付ファイルのリネーム機能を最適化。\n\n---\n\n## v1.14.1\n> *2026/01/31*\n\n### ✨ 新機能\n\n- **ゴミ箱**: 添付ファイル管理でゴミ箱および一括復元機能をサポート。\n\n### 🛠️ 修正\n\n- **安定性**: 添付ファイル/設定ファイルで更新時間と内容が一致する場合にリソースが正しく作成されない問題を修正。\n\n### 🚀 改善\n\n- **API**: 添付ファイルの閲覧/ダウンロードインターフェースをゼロコピーアクセスに最適化。\n- **WebGui**: 各種リストの表示高さが低すぎる問題を最適化。\n\n---\n\n## v1.14.0\n> *2026/01/31*\n\n### ✨ 新機能\n\n- **ゴミ箱**: 添付ファイル管理にゴミ箱を追加。\n- **WebGui**: サーバー情報の表示を追加。\n- **同期**: ノートと添付ファイルのリネーム機能を追加。\n\n### 🛠️ 修正\n\n- **安定性**: 潜在的なデータ競合問題を修正。\n\n---\n\n## v1.13.0\n> *2026/01/30*\n\n### ✨ 新機能\n- **同期**: 添付ファイル、ノート、設定のオフライン削除同期機能を追加。\n- **同期**: 増分同期モードで欠落ファイルの自動ダウンロード機能を追加。\n\n---\n\n## v1.12.0\n> *2026/01/29*\n\n### 🚀 改善\n- **言語**: すべてのコードコメントとドキュメントを中英併記または英語に統一翻訳/更新。\n- **API**: APIレスポンスメッセージの国際化（i18n）対応を改善。\n- **安定性**: 自動リソースプレフィックスの問題を修正。\n- **API**: API拡張を追加：編集操作、バックリンク（Backlinks）、ヘルスチェック。\n\n---\n\n## v1.11.3\n> *2026/01/27*\n\n### 🛠️ 修正\n- **添付ファイル**: 添付ファイルダウンロードのタイムアウト（30秒）エラーを修正。設定可能になり、デフォルトは1時間。\n\n---\n\n## v1.11.2\n> *2026/01/27*\n\n### ✨ 新機能\n- **WebGui**: Obsidian SSO自動認証メカニズムを追加。\n\n### 🚀 改善\n- **WebGui**: 認証設定インターフェースのUIを改善。\n\n---\n\n## v1.11.1\n> *2026/01/26*\n\n### 🚀 改善\n- **リリース**: バージョンリリースワークフローを調整。\n\n---\n\n## v1.11.0\n> *2026/01/26*\n\n### ✨ 新機能\n- **機能**: バージョン検出およびバージョン情報取得機能を追加。\n\n---\n\n## v1.10.8\n> *2026/01/26*\n\n### ✨ 新機能\n- **API**: 添付ファイル状態検出インターフェースを追加。\n\n---\n\n## v1.10.7\n> *2026/01/25*\n\n### 🛠️ 修正\n- **安定性**: ファイルアップロード時の整合性チェックによるサーバークラッシュを修正。\n\n---\n\n## v1.10.6\n> *2026/01/24*\n\n### ✨ 新機能\n- **WebGui**: 添付ファイル管理ページにページネーションを追加。\n\n---\n\n## v1.10.5\n> *2026/01/23*\n\n### 🛠️ 修正\n- **ゴミ箱**: ゴミ箱および履歴バージョンからのノート/バージョンの復元に関する問題を修正。\n\n---\n\n## v1.10.4\n> *2026/01/23*\n\n### 🛠️ 修正\n- **添付ファイル**: 添付ファイルアップロード中のネットワーク切断による異常を修正し、エラーログレベルを下げました。\n\n---\n\n## v1.10.3\n> *2026/01/20*\n\n### 🚀 改善\n- **WebGui**: ノートリポジトリリストのズーム効果を、選択時のシャドウ効果に変更。\n\n### 🛠️ 修正\n- **WebGui**: ノートリポジトリ名に特殊文字が含まれる場合にアクセスできないバグを修正。\n\n---\n\n## v1.10.2\n> *2026/01/20*\n\n### 🛠️ 修正\n- **管理**: 新規ユーザー登録およびユーザー登録無効化設定が機能しないバグを修正。\n\n---\n\n## v1.10.1\n> *2026/01/20*\n\n### 🛠️ 修正\n- **管理**: 新規ユーザー登録の問題を修正。\n\n---\n\n## v1.10.0\n> *2026/01/19*\n\n### ✨ 新機能\n- **添付ファイル**: 添付ファイル管理機能を追加。\n- **認証**: Token有効期限の設定を追加。\n- **共有**: 共有機能関連のインターフェースを追加。\n- **ドキュメント**: Swagger APIドキュメントを追加。\n\n### 🚀 改善\n- **WebGui**: WebGuiのデプロイパスを調整。\n- **API**: APIエラーメッセージを詳細化。\n\n### 🛠️ 修正\n- **WebGui**: WebGui自動翻訳による通知の問題を修正。\n\n---\n\n## v1.9.1\n> *2026/01/14*\n\n### 🚀 改善\n- **WebGui**: ブルー配色スキームを追加し、エディタの表示を最適化。\n\n---\n\n## v1.9.0\n> *2026/01/14*\n\n### ✨ 新機能\n- **WebGui**: UIを全面刷新（@ZyphrZero による貢献）。\n- **WebGui**: エディタをVditorに変更し、リッチテキストとMarkdownのリアルタイムレンダリングをサポート。\n- **WebGui**: カスタムノート検索、リスト項目のソート、カラーテーマをサポート。\n- **WebGui**: ダークモード、オンラインバージョン検出、ゴミ箱からの復元機能を追加。\n- **設定**: 履歴バージョンの保持数および保存遅延設定を追加。\n\n### 🚀 改善\n- **セキュリティ**: サービスプロークンの暗号化難読化文字を最適化。\n\n---\n\n## v1.8.1\n> *2026/01/12*\n\n### 🔄 変更\n- **アーキテクチャ**: DDDレイヤードアーキテクチャを導入（@ZyphrZero による貢献）、グローバル変数を削除し、依存性注入（DI）パターンを実装。\n\n### 🚀 改善\n- **同期**: オフラインノートのマージを最適化し、行レベルの競合検出と3-wayマージを実現。\n- **パフォーマンス**: Worker PoolとPer-User Write Queueを追加し、SQLiteの並行書き込みロック問題を解決。\n- **WebSocket**: Contextのライフサイクル管理を最適化し、TraceIDの追跡能力を強化。\n\n### 🛠️ 修正\n- **ロジック**: ノートの名称変更によるノートの紛失およびエラーの問題を修正。\n\n---\n\n## v1.7.3\n> *2026/01/09*\n\n### 🛠️ 修正\n- **データベース**: データベース作成失敗時のフレンドリーなエラー表示を追加。\n\n---\n\n## v1.7.2\n> *2026/01/09*\n\n### ✨ 新機能\n- **WebGui**: 設定機能および関連インターフェースを追加。\n- **管理**: 管理者ID設定を追加。\n\n---\n\n## v1.7.1\n> *2026/01/09*\n\n### ✨ 新機能\n- **同期**: オフラインデバイスのノート編集マージ機能を追加（プラグインv1.7+が必要）。\n\n---\n\n## v1.6.3\n> *2026/01/08*\n\n### 🚀 改善\n- **WebGui**: ノートリストの検索機能を最適化。\n- **WebGui**: アイコン表示を追加。\n- **WebGui**: ノートリポジトリに添付ファイル表示と更新ボタンを追加。\n\n### 🛠️ 修正\n- **安定性**: 並行クエリ時の潜在的な例外を修正。\n\n---\n\n## v1.6.1\n> *2026/01/07*\n\n### 🚀 改善\n- **パフォーマンス**: 大規模なノートリポジトリの同期効率とデータ処理を最適化（プラグインv1.6+が必要）。\n- **キャッシュ**: 静的コンテンツにブラウザキャッシュメカニズムを追加。\n\n> [!CAUTION]\n> このバージョンではデータベース構造の最適化が行われています。サーバー上の `storage/database` にあるDBファイルを削除することをお勧めします。ノートの変更履歴は再生成されます。\n\n---\n\n## v1.5.4\n> *2026/01/06*\n\n### 🛠️ 修正\n- **添付ファイル**: 添付ファイルのアップロード時に稀に発生するエラーを修正。\n\n---\n\n## v1.5.3\n> *2026/01/06*\n\n### 🚀 改善\n- **WebGui**: 編集機能を遅延読み込みし、ホームページの読み込み速度を向上。\n\n---\n\n## v1.5.2\n> *2026/01/05*\n\n### 🛠️ 修正\n- **同期**: 同期タスクの進捗表示が不正確な問題を修正。\n\n---\n\n## v1.5.1\n> *2026/01/04*\n\n### 🛠️ 修正\n- **ロジック**: ノートの名称変更後に正常に削除できない問題を修正。\n- **安定性**: 大規模なノート同期時にWebSocket接続がリセットされる問題を修正。\n- **多言語**: WebGui APIの言語エラーを修正。\n\n---\n\n## v1.5.0\n> *2026/01/04*\n\n### ✨ 新機能\n- **ゴミ箱**: ノートゴミ箱機能を追加。\n- **WebGui**: ユーザー状態検出を追加。\n- **WebGui**: 登録ページに登録受付終了の検出を追加。\n- **WebGui**: 操作確認のキーボードショートカットをサポート。\n\n### 🚀 改善\n- **WebGui**: ノート編集のユーザーエクスペリエンスを向上。\n- **データベース**: データベースの並行アクセス問題を最適化し解決。\n\n### 🛠️ 修正\n- **スクリプト**: ショートカットスクリプトが設定ファイルを上書きする問題を修正。\n\n---\n\n## v1.4.7\n> *2026/01/03*\n\n### 🛠️ 修正\n- **データベース**: SQLiteの並行問題を解決し、内部エラーコードを修正。\n\n---\n\n## v1.4.6\n> *2026/01/03*\n\n### 🛠️ 修正\n- **Docker**: Docker環境で `temp` ディレクトリが存在しない問題を修正。\n\n---\n\n## v1.4.5\n> *2026/01/03*\n\n### 🛠️ 修正\n- **同期**: 初回同期または全量同期時に添付ファイルが同期されない問題を修正（プラグインv1.5.14+が必要）。\n\n---\n\n## v1.4.4\n> *2026/01/02*\n\n### 🛠️ 修正\n- **アクセス**: タイトルにEmojiが含まれる場合のアクセス問題を修正。\n\n### ✨ 新機能\n- **ドキュメント**: ヘルプファイルを追加。\n\n---\n\n## v1.4.3\n> *2026/01/02*\n\n### 🔄 変更\n- **リポジトリ**: ノートリポジトリの削除操作を論理削除に変更。\n\n---\n\n## v1.4.2\n> *2026/01/01*\n\n### ✨ 新機能\n- **WebGui**: ノート削除時に赤色の確認ポップアップを表示し、誤削除を防止。\n\n---\n\n## v1.4.1\n> *2025/12/31*\n\n### 🚀 改善\n- **API**: ノートリソース（画像など）のダウンロードインターフェースにETagブラウザキャッシュを追加し、読み込み速度を向上。\n\n---\n\n## v1.4.0\n> *2025/12/31*\n\n### ✨ 新機能\n- **WebGui**: 最大化ボタンを追加し、全画面編集のエクスペリエンスを向上。\n- **WebGui**: ノート表示ページでObsidian埋め込み画像、PDF、およびその他の添付ファイルの表示をサポート。\n- **API**: リソースダウンロードインターフェースを追加。\n\n---\n\n## v1.3.8\n> *2025/12/31*\n\n### 🚀 改善\n- **サーバー**: ノートのコンテンツハッシュバージョン管理を確立し、今後の追跡、比較、マージを容易にしました。\n\n---\n\n## v1.3.7\n> *2025/12/30*\n\n### 🛠️ 修正\n- **安定性**: タスクおよびアップグレードスクリプトにリカバリ機能を追加し、サービスダウンを防止。\n- **安定性**: 各レイヤーで発生していたNilポインタによるPanicを修正。\n\n---\n\n## v1.3.6\n> *2025/12/30*\n\n### 🛠️ 修正\n- **タスク管理**: タスクマネージャーのエラーを修正。\n\n---\n\n## v1.3.5\n> *2025/12/30*\n\n### 🚀 改善\n- **WebGui**: ノート表示ページを最適化。\n- **スクリプト**: 一件インストール/管理スクリプトを最適化。\n\n---\n\n## v1.3.4\n> *2025/12/30*\n\n### 🛠️ 修正\n- **同期**: 同期コマンドの処理エラーによる、不正確なファイル同期の問題を修正。\n- **スクリプト**: 一件スクリプトで `Ctrl+C` 時にサービスが一緒に終了する問題を修正。\n\n---\n\n## v1.3.3\n> *2025/12/29*\n\n### 🛠️ 修正\n- **同期**: 単一ユーザーによる複数ノートリポジトリ更新時の混乱の問題を解決。\n\n---\n\n## v1.3.2\n> *2025/12/28*\n\n### ✨ 新機能\n- **多言語**: 多言語環境のサポートを追加。\n\n### 🚀 改善\n- **WebGui**: ノート履歴の差異表示を最適化。\n\n---\n\n## v1.3.1\n> *2025/12/28*\n\n### 🚀 改善\n- **ロジック**: ノートタイトル変更時のロジック処理を最適化。\n\n---\n\n## v1.3.0\n> *2025/12/28*\n\n### ✨ 新機能\n- **WebGui**: WebGuiのフォント設定をユーザーが制御できる設定を追加。\n\n---\n\n## v1.2.6\n> *2025/12/27*\n\n### 🚀 改善\n- **WebGui**: フォント読み込みロジックを最適化し、UIのスタッタリングを回避。\n\n---\n\n## v1.2.5\n> *2025/12/27*\n\n### ✨ 新機能\n- **クライアント**: クライアント名の記録をサポート。\n\n### 🚀 改善\n- **クリーンアップ**: ノートの名称変更後の同期クリーンアップロジックを追加。\n\n---\n\n## v1.2.4\n> *2025/12/27*\n\n### 🛠️ 修正\n- **WebGui**: 履歴バージョンが空の場合の表示バグを修正。\n\n---\n\n## v1.2.3\n> *2025/12/27*\n\n### ✨ 新機能\n- **API**: ノート履歴関連のインターフェースおよび機能を追加。\n\n### 🚀 改善\n- **データベース**: データベースのクエリ効率を最適化。\n- **WebGui**: WebGuiの表示フォントを変更し、各種表示バグを修正。\n\n### 🛠️ 修正\n- **安定性**: 高並行アクセス時の問題を修正。\n\n---\n\n## v1.2.2\n> *2025/12/27*\n\n### 🛠️ 修正\n- **WebGui**: ノート履歴が空の場合にページが白くなる問題を修正。\n\n---\n\n## v1.2.1\n> *2025/12/27*\n\n### ✨ 新機能\n- **API**: ノート履歴関連のインターフェースおよび機能を追加。\n\n### 🚀 改善\n- **データベース**: データベースのクエリ効率を最適化。\n- **安定性**: 高並行アクセス時の安定性問題を解決。\n\n---\n\n## v1.0.4\n> *2025/12/26*\n\n### 🛠️ 修正\n- **WebGui**: WebGuiビルド時の例外による表示の問題を修正。\n\n---\n\n## v1.0.3\n> *2025/12/25*\n\n### 🛠️ 修正\n- **WebGui**: ノートタイトルが長い場合のレイアウトの問題を修正。\n\n---\n\n## v1.0.2\n> *2025/12/25*\n\n### 🚀 改善\n- **添付ファイル**: 添付ファイルのアップロードロジックを最適化し、アップロード時間を大幅に短縮。\n\n### 🛠️ 修正\n- **CI/CD**: GitHub Actionの更新制限を修正。\n\n---\n\n## v1.0.1\n> *2025/12/23*\n\n### 🛠️ 修正\n- **権限**: 一部のシステムでアップロード時に権限不足が発生する問題を修正。\n\n---\n\n## v1.0.0\n> *2025/12/22*\n\n### ✨ 新機能\n- **同期**: 設定ファイルの同期機能およびインターフェースを追加。\n\n### 🚀 改善\n- **スクリプト**: スクリプトの出力表示を最適化。\n\n### 🛠️ 修正\n- **スクリプト**: スクリプトの実行制御の失敗を修正。\n\n---\n\n## v0.11.5\n> *2025/12/19*\n\n### 🛠️ 修正\n- **Docker**: Dockerイメージの実行問題を修正。\n\n---\n\n## v0.11.4\n> *2025/12/18*\n\n### ✨ 新機能\n- **認証**: 認証検証インターフェースでバージョン情報の返却を追加。\n\n---\n\n## v0.11.3\n> *2025/12/16*\n\n### ✨ 新機能\n- **クリーンアップ**: 起動時の自動クリーンアップタスクおよびSession自動クリーンアップロジックを追加。\n\n### 🛠️ 修正\n- **安定性**: 高並行時の接続切断による異常終了問題を修正。\n\n---\n\n## v0.11.2\n> *2025/12/15*\n\n### 🛠️ 修正\n- **安定性**: 並行時の接続切断による異常終了問題を修正。\n\n---\n\n## v0.11.1\n> *2025/12/14*\n\n### ✨ 新機能\n- **アーキテクチャ**: メッセージにプレフィックスを追加し、今後の機能拡張を容易に。\n\n---\n\n## v0.10.2\n> *2025/12/12*\n\n### ✨ 新機能\n- **設定**: アップロード/ダウンロードのチャンク設定を追加（デフォルト 512KB）。\n\n---\n\n## v0.10.1\n> *2025/12/12*\n\n### ✨ 新機能\n- **機能**: バイナリファイルのダウンロード機能を追加。\n- **機能**: WebSocketによるチャンクダウンロード機能を追加。\n- **機能**: バージョン管理を追加。\n\n---\n\n## v0.9.6\n> *2025/12/11*\n\n- 初期バージョン（記録開始）。\n"
  },
  {
    "path": "docs/CHANGELOG.ko.md",
    "content": "# 변경 로그 (CHANGELOG)\n\n이 프로젝트의 모든 주요 변경 사항은 이 파일에 기록됩니다.\n\n이 프로젝트는 [Keep a Changelog](https://keepachangelog.com/en/0.3.0/) 규격에 따라 관리됩니다.\n\n---\n\n## v1.16.2\n> *2026/02/14*\n\n### 🚀 최적화\n\n- **WebGui**: WebGui 인터페이스 위치 조정.\n- **기능**: 서버 정보 표시 추가.\n- **성능**: 각종 리스트 표시 높이가 너무 낮은 문제에 대해 제로 카피(Zero-copy) 접근으로 최적화.\n- **동기화**: 노트/첨부 파일 동기화 로직 최적화.\n\n---\n\n## v1.16.1\n> *2026/02/14*\n\n### 🚀 최적화\n\n- **WebGui**: WebGui 인터페이스 위치 조정.\n- **기능**: 서버 정보 표시 추가.\n\n---\n\n## v1.15.11\n> *2026/02/14*\n\n### 🚀 최적화\n\n- **WebGui**: WebGui 인터페이스 최적화 및 URL 지원 추가.\n\n---\n\n## v1.15.10\n> *2026/02/14*\n\n### 🚀 최적화\n\n- **아키텍처**: 서비스 도구 세트 조정.\n- **API**: 인터페이스 응답 구조 조정.\n\n---\n\n## v1.15.9\n> *2026/02/14*\n\n### ✨ 새 기능\n\n- **도구**: fns에 docs 및 ws 디버깅 도구 접근 항목 추가.\n\n---\n\n## v1.15.8\n> *2026/02/13*\n\n### 🛠️ 수정\n\n- **안정성**: 시간 처리 관련 경미한 버그 수정.\n\n---\n\n## v1.15.7\n> *2026/02/13*\n\n### 🛠️ 수정\n\n- **동기화**: 오프라인 삭제 시 로컬 해시 테이블이 정리되지 않던 문제 수정.\n\n---\n\n## v1.15.6\n> *2026/02/13*\n\n### 🛠️ 수정\n\n- **스크립트**: macOS 환경에서 fns 바로가기 스크립트 실행 문제 수정.\n- **로그**: 로그 출력 내용 수정.\n\n---\n\n## v1.15.5\n> *2026/02/12*\n\n### 🚀 최적화\n\n- **CI/CD**: GitHub Action에서 go mod 버전을 사용하여 빌드 및 릴리스하도록 조정.\n\n---\n\n## v1.15.4\n> *2026/02/12*\n\n### ✨ 새 기능\n\n- **동기화**: 노트 설정 관련 메시지 정리 기능 추가.\n\n---\n\n## v1.15.3\n> *2026/02/10*\n\n### 🛠️ 수정\n\n- **디렉토리**: 중복 디렉토리에 대한 회피책 추가 및 시작 시 중복 디렉토리 정리 기능 추가.\n\n---\n\n## v1.15.2\n> *2026/02/09*\n\n### 🚀 최적화\n\n- **데이터베이스**: DB 성능 및 구조 최적화, 일괄 포맷팅 실시.\n\n---\n\n## v1.15.1\n> *2026/02/07*\n\n### ✨ 새 기능\n\n- **디렉토리**: 디렉토리(Folder) 관리 기능 추가(Model 및 관련 로직 포함).\n- **동기화**: 잠재적인 데이터 경합 문제 수정 및 노트/첨부 파일 이름 변경 기능 최적화.\n\n---\n\n## v1.14.1\n> *2026/01/31*\n\n### ✨ 새 기능\n\n- **휴지통**: 첨부 파일 관리에서 휴지통 및 일괄 복구 기능 지원.\n\n### 🛠️ 수정\n\n- **안정성**: 첨부 파일/설정 파일 업데이트 시간과 내용이 일치할 때 리소스가 정상적으로 생성되지 않던 문제 수정.\n\n### 🚀 최적화\n\n- **API**: 첨부 파일 조회/다운로드 인터페이스를 제로 카피 접근으로 최적화.\n- **WebGui**: 각종 리스트 표시 높이가 너무 낮은 문제 최적화.\n\n---\n\n## v1.14.0\n> *2026/01/31*\n\n### ✨ 새 기능\n\n- **휴지통**: 첨부 파일 관리에 휴지통 추가.\n- **WebGui**: 서버 정보 표시 추가.\n- **동기화**: 노트 및 첨부 파일 이름 변경 기능 추가.\n\n### 🛠️ 수정\n\n- **안정성**: 잠재적인 데이터 경합 문제 수정.\n\n---\n\n## v1.13.0\n> *2026/01/30*\n\n### ✨ 새 기능\n- **동기화**: 첨부 파일, 노트, 설정의 오프라인 삭제 동기화 기능을 추가했습니다.\n- **동기화**: 증분 동기화 모드에서 누락된 파일 자동 다운로드 기능을 추가했습니다.\n\n---\n\n## v1.12.0\n> *2026/01/29*\n\n### 🚀 최적화\n- **언어**: 모든 코드 주석 및 문서를 한/영 병기 또는 영어로 번역/업데이트했습니다.\n- **API**: API 응답 메시지에 대한 국제화(i18n) 지원을 개선했습니다.\n- **안정성**: 자동 리소스 접두사 문제를 수정했습니다.\n- **API**: API 확장 기능 추가: 편집 작업, 백링크(Backlinks), 상태 확인(Health Check).\n\n---\n\n## v1.11.3\n> *2026/01/27*\n\n### 🛠️ 수정\n- **첨부 파일**: 첨부 파일 다운로드 요청 시 타임아웃(30초) 오류가 발생하던 문제를 수정했습니다. 이제 설정이 가능하며 기본값은 1시간입니다.\n\n---\n\n## v1.11.2\n> *2026/01/27*\n\n### ✨ 새 기능\n- **WebGui**: Obsidian SSO 자동 인증 메커니즘을 추가했습니다.\n\n### 🚀 최적화\n- **WebGui**: 인증 설정 인터페이스 UI를 개선했습니다.\n\n---\n\n## v1.11.1\n> *2026/01/26*\n\n### 🚀 최적화\n- **배포**: 버전 배포 워크플로우를 조정했습니다.\n\n---\n\n## v1.11.0\n> *2026/01/26*\n\n### ✨ 새 기능\n- **기능**: 버전 감지 및 버전 정보 가져오기 기능을 추가했습니다.\n\n---\n\n## v1.10.8\n> *2026/01/26*\n\n### ✨ 새 기능\n- **API**: 첨부 파일 상태 감지 인터페이스를 추가했습니다.\n\n---\n\n## v1.10.7\n> *2026/01/25*\n\n### 🛠️ 수정\n- **안정성**: 파일 업로드 시 일관성 검사로 인해 서버가 다운되던 문제를 수정했습니다.\n\n---\n\n## v1.10.6\n> *2026/01/24*\n\n### ✨ 새 기능\n- **WebGui**: 첨부 파일 관리 페이지에 페이지네이션을 추가했습니다.\n\n---\n\n## v1.10.5\n> *2026/01/23*\n\n### 🛠️ 수정\n- **휴지통**: 휴지통 및 히스토리 버전에서 노트나 버전을 복구할 때 발생하던 문제를 수정했습니다.\n\n---\n\n## v1.10.4\n> *2026/01/23*\n\n### 🛠️ 수정\n- **첨부 파일**: 첨부 파일 업로드 중 네트워크 연결이 끊길 때의 이상 현상을 수정하고 오류 로그 수준을 낮췄습니다.\n\n---\n\n## v1.10.3\n> *2026/01/20*\n\n### 🚀 최적화\n- **WebGui**: 노트 보관소 리스트의 확대 효과를 선택 시 그림자 효과로 변경했습니다.\n\n### 🛠️ 수정\n- **WebGui**: 노트 보관소 이름에 특수 문자가 포함된 경우 접근할 수 없던 버그를 수정했습니다.\n\n---\n\n## v1.10.2\n> *2026/01/20*\n\n### 🛠️ 수정\n- **관리**: 새 사용자 등록 및 사용자 등록 비활성화 설정이 작동하지 않던 버그를 수정했습니다.\n\n---\n\n## v1.10.1\n> *2026/01/20*\n\n### 🛠️ 수정\n- **관리**: 새 사용자 등록 문제를 수정했습니다.\n\n---\n\n## v1.10.0\n> *2026/01/19*\n\n### ✨ 새 기능\n- **첨부 파일**: 첨부 파일 관리 기능을 추가했습니다.\n- **인증**: Token 만료 시간 설정을 추가했습니다.\n- **공유**: 공유 기능 관련 인터페이스를 추가했습니다.\n- **문서**: Swagger API 문서를 추가했습니다.\n\n### 🚀 최적화\n- **WebGui**: WebGui 배포 경로를 조정했습니다.\n- **API**: API 오류 메시지를 상세화했습니다.\n\n### 🛠️ 수정\n- **WebGui**: WebGui 자동 번역으로 인한 알림 문제를 수정했습니다.\n\n---\n\n## v1.9.1\n> *2026/01/14*\n\n### 🚀 최적화\n- **WebGui**: 블루 색상 테마를 추가하고 에디터 표시 효과를 최적화했습니다.\n\n---\n\n## v1.9.0\n> *2026/01/14*\n\n### ✨ 새 기능\n- **WebGui**: UI를 전면 개편했습니다 (@ZyphrZero 기여).\n- **WebGui**: 에디터를 Vditor로 변경하여 리치 텍스트 및 Markdown 실시간 렌더링을 지원합니다.\n- **WebGui**: 사용자 지정 노트 검색, 리스트 필드 정렬 및 색상 테마를 지원합니다.\n- **WebGui**: 다크 모드, 온라인 버전 감지 및 휴지통 복구 기능을 추가했습니다.\n- **설정**: 히스토리 버전 보존 개수 및 저장 지연 설정을 추가했습니다.\n\n### 🚀 최적화\n- **보안**: 서비스 토큰 암호화 난독화 문자를 최적화했습니다.\n\n---\n\n## v1.8.1\n> *2026/01/12*\n\n### 🔄 변경\n- **아키텍처**: DDD 계층형 아키텍처를 도입하고 (@ZyphrZero 기여), 전역 변수를 제거하며 의존성 주입(DI) 패턴을 구현했습니다.\n\n### 🚀 최적화\n- **동기화**: 오프라인 노트 병합을 최적화하여 행 단위의 충돌 감지 및 3-way 병합을 구현했습니다.\n- **성능**: Worker Pool과 Per-User Write Queue를 도입하여 SQLite 동시 쓰기 잠금 문제를 해결했습니다.\n- **WebSocket**: Context 생명주기 관리를 최적화하고 TraceID 추적 기능을 강화했습니다.\n\n### 🛠️ 수정\n- **로직**: 노트 이름 변경으로 인해 노트가 유실되거나 오류가 발생하던 문제를 수정했습니다.\n\n---\n\n## v1.7.3\n> *2026/01/09*\n\n### 🛠️ 수정\n- **데이터베이스**: 데이터베이스 생성 실패 시 사용자 친화적인 오류 메시지를 추가했습니다.\n\n---\n\n## v1.7.2\n> *2026/01/09*\n\n### ✨ 새 기능\n- **WebGui**: 설정 기능 및 관련 인터페이스를 추가했습니다.\n- **관리**: 관리자 ID 설정을 추가했습니다.\n\n---\n\n## v1.7.1\n> *2026/01/09*\n\n### ✨ 새 기능\n- **동기화**: 오프라인 기기의 노트 편집 병합 기능을 추가했습니다 (플러그인 v1.7+ 필요).\n\n---\n\n## v1.6.3\n> *2026/01/08*\n\n### 🚀 최적화\n- **WebGui**: 노트 리스트 검색 기능을 최적화했습니다.\n- **WebGui**: 아이콘 표시를 추가했습니다.\n- **WebGui**: 노트 보관소에 첨부 파일 표시 및 새로고침 버튼을 추가했습니다.\n\n### 🛠️ 수정\n- **안정성**: 동시 쿼리 시 발생할 수 있는 예외 상황을 수정했습니다.\n\n---\n\n## v1.6.1\n> *2026/01/07*\n\n### 🚀 최적화\n- **성능**: 대규모 노트 보관소의 동기화 효율 및 데이터 처리를 최적화했습니다 (플러그인 v1.6+ 필요).\n- **캐시**: 정적 콘텐츠에 대해 브라우저 캐싱 메커니즘을 추가했습니다.\n\n> [!CAUTION]\n> 이 버전은 데이터베이스 구조 최적화가 포함되어 있습니다. 서버의 `storage/database` 디렉토리에 있는 DB 파일을 삭제할 것을 권장합니다. 노트 수정 이력은 다시 생성됩니다.\n\n---\n\n## v1.5.4\n> *2026/01/06*\n\n### 🛠️ 수정\n- **첨부 파일**: 첨부 파일 업로드 시 간헐적으로 발생하던 오류를 수정했습니다.\n\n---\n\n## v1.5.3\n> *2026/01/06*\n\n### 🚀 최적화\n- **WebGui**: 편집 기능을 지연 로딩하여 홈 페이지 로딩 속도를 향상했습니다.\n\n---\n\n## v1.5.2\n> *2026/01/05*\n\n### 🛠️ 수정\n- **동기화**: 동기화 작업의 진행률 표시가 부정확하던 문제를 수정했습니다.\n\n---\n\n## v1.5.1\n> *2026/01/04*\n\n### 🛠️ 수정\n- **로직**: 노트 이름 변경 후 정상적으로 삭제되지 않던 문제를 수정했습니다.\n- **안정성**: 대규모 노트 동기화 시 WebSocket 연결이 리셋되던 문제를 수정했습니다.\n- **다국어**: WebGui API 언어 오류를 수정했습니다.\n\n---\n\n## v1.5.0\n> *2026/01/04*\n\n### ✨ 새 기능\n- **휴지통**: 노트 휴지통 기능을 추가했습니다.\n- **WebGui**: 사용자 상태 감지 기능을 추가했습니다.\n- **WebGui**: 가입 페이지에 가입 중단 감지 기능을 추가했습니다.\n- **WebGui**: 작업 확인을 위한 키보드 단축키 지원을 추가했습니다.\n\n### 🚀 최적화\n- **WebGui**: 노트 편집 사용자 경험을 개선했습니다.\n- **데이터베이스**: 데이터베이스 동시 액세스 문제를 최적화하고 해결했습니다.\n\n### 🛠️ 수정\n- **스크립트**: 바로가기 스크립트가 설정 파일을 덮어쓰던 문제를 수정했습니다.\n\n---\n\n## v1.4.7\n> *2026/01/03*\n\n### 🛠️ 수정\n- **데이터베이스**: SQLite 동시성 문제를 시도하고 내부 오류 코드를 수정했습니다.\n\n---\n\n## v1.4.6\n> *2026/01/03*\n\n### 🛠️ 수정\n- **Docker**: Docker 환경에서 `temp` 디렉토리가 존재하지 않던 문제를 수정했습니다.\n\n---\n\n## v1.4.5\n> *2026/01/03*\n\n### 🛠️ 수정\n- **동기화**: 초기 동기화 또는 전체 동기화 시 첨부 파일이 동기화되지 않던 문제를 수정했습니다 (플러그인 v1.5.14+ 필요).\n\n---\n\n## v1.4.4\n> *2026/01/02*\n\n### 🛠️ 수정\n- **접근**: 제목에 Emoji가 포함된 경우의 접근성 문제를 수정했습니다.\n\n### ✨ 새 기능\n- **문서**: 도움말 파일을 추가했습니다.\n\n---\n\n## v1.4.3\n> *2026/01/02*\n\n### 🔄 변경\n- **보관소**: 노트 보관소 삭제 작업을 소프트 삭제로 변경했습니다.\n\n---\n\n## v1.4.2\n> *2026/01/01*\n\n### ✨ 새 기능\n- **WebGui**: 노트 삭제 시 실수로 삭제하는 것을 방지하기 위해 빨간색 확인 팝업을 추가했습니다.\n\n---\n\n## v1.4.1\n> *2025/12/31*\n\n### 🚀 최적화\n- **API**: 노트 리소스(이미지 등) 다운로드 인터페이스에 ETag 브라우저 캐싱을 추가하여 로딩 속도를 개선했습니다.\n\n---\n\n## v1.4.0\n> *2025/12/31*\n\n### ✨ 새 기능\n- **WebGui**: 전체 화면 편집 경험을 높이기 위해 최대화 버튼을 추가했습니다.\n- **WebGui**: 노트 보기 페이지에서 Obsidian 임베디드 이미지, PDF 및 기타 첨부 파일의 정상 표시를 지원합니다.\n- **API**: 리소스 다운로드 인터페이스를 추가했습니다.\n\n---\n\n## v1.3.8\n> *2025/12/31*\n\n### 🚀 최적화\n- **서버**: 향후 추적, 비교 및 병합이 용이하도록 노트용 콘텐츠 해시 버전 저장소를 구축했습니다.\n\n---\n\n## v1.3.7\n> *2025/12/30*\n\n### 🛠️ 수정\n- **안정성**: 작업 및 업그레이드 스크립트에 복구 기능을 추가하여 서비스 장애를 방지했습니다.\n- **안정성**: 각 레이어에서 발생하던 Nil 포인터로 인한 Panic을 수정했습니다.\n\n---\n\n## v1.3.6\n> *2025/12/30*\n\n### 🛠️ 수정\n- **작업 관리**: 작업 관리자의 오류를 수정했습니다.\n\n---\n\n## v1.3.5\n> *2025/12/30*\n\n### 🚀 최적화\n- **WebGui**: 노트 보기 표시를 최적화했습니다.\n- **스크립트**: 원클릭 설치/관리 스크립트를 최적화했습니다.\n\n---\n\n## v1.3.4\n> *2025/12/30*\n\n### 🛠️ 수정\n- **동기화**: 동기화 명령 처리 오류로 인해 잘못된 파일 동기화가 발생하는 문제를 수정했습니다.\n- **스크립트**: 원클릭 스크립트에서 `Ctrl+C` 시 서비스가 함께 종료되던 문제를 수정했습니다.\n\n---\n\n## v1.3.3\n> *2025/12/29*\n\n### 🛠️ 수정\n- **동기화**: 단일 사용자의 여러 노트 보관소 업데이트 시 발생할 수 있는 혼란 문제를 해결했습니다.\n\n---\n\n## v1.3.2\n> *2025/12/28*\n\n### ✨ 새 기능\n- **다국어**: 다국어 환경 지원을 추가했습니다.\n\n### 🚀 최적화\n- **WebGui**: 노트 이력의 차이 표시를 최적화했습니다.\n\n---\n\n## v1.3.1\n> *2025/12/28*\n\n### 🚀 최적화\n- **로직**: 노트 제목 변경 시의 로직 처리를 최적화했습니다.\n\n---\n\n## v1.3.0\n> *2025/12/28*\n\n### ✨ 새 기능\n- **WebGui**: 사용자가 WebGui 폰트 설정을 제어할 수 있는 설정을 추가했습니다.\n\n---\n\n## v1.2.6\n> *2025/12/27*\n\n### 🚀 최적화\n- **WebGui**: UI 끊김 현상을 방지하기 위해 폰트 로딩 로직을 최적화했습니다.\n\n---\n\n## v1.2.5\n> *2025/12/27*\n\n### ✨ 새 기능\n- **클라이언트**: 클라이언트 이름 기록 지원을 추가했습니다.\n\n### 🚀 최적화\n- **정리**: 노트 이름 변경 후의 동기화 정리 로직을 추가했습니다.\n\n---\n\n## v1.2.4\n> *2025/12/27*\n\n### 🛠️ 수정\n- **WebGui**: 이력 버전 내용이 비어 있을 때의 표시 버그를 수정했습니다.\n\n---\n\n## v1.2.3\n> *2025/12/27*\n\n### ✨ 새 기능\n- **API**: 노트 이력 관련 인터페이스 및 기능을 추가했습니다.\n\n### 🚀 최적화\n- **데이터베이스**: 데이터베이스 쿼리 효율을 최적화했습니다.\n- **WebGui**: WebGui 표시 폰트를 변경하고 각종 표시 버그를 수정했습니다.\n\n### 🛠️ 수정\n- **안정성**: 고동시성 액세스 시의 문제를 수정했습니다.\n\n---\n\n## v1.2.2\n> *2025/12/27*\n\n### 🛠️ 수정\n- **WebGui**: 노트 이력이 비어 있을 때 페이지가 하얗게 나오던 문제를 수정했습니다.\n\n---\n\n## v1.2.1\n> *2025/12/27*\n\n### ✨ 새 기능\n- **API**: 노트 이력 관련 인터페이스 및 기능을 추가했습니다.\n\n### 🚀 최적화\n- **데이터베이스**: 데이터베이스 쿼리 효율을 최적화했습니다.\n- **안정성**: 고동시성 액세스 시의 안정성 문제를 해결했습니다.\n\n---\n\n## v1.0.4\n> *2025/12/26*\n\n### 🛠️ 수정\n- **WebGui**: WebGui 빌드 시의 예외로 인한 표시 문제를 수정했습니다.\n\n---\n\n## v1.0.3\n> *2025/12/25*\n\n### 🛠️ 수정\n- **WebGui**: 노트 제목이 긴 경우의 레이아웃 문제를 해결했습니다.\n\n---\n\n## v1.0.2\n> *2025/12/25*\n\n### 🚀 최적화\n- **첨부 파일**: 첨부 파일 업로드 로직을 최적화하여 업로드 시간을 대폭 단축했습니다.\n\n### 🛠️ 수정\n- **CI/CD**: GitHub Action 업데이트 제한을 수정했습니다.\n\n---\n\n## v1.0.1\n> *2025/12/23*\n\n### 🛠️ 수정\n- **권한**: 일부 시스템에서 업로드 시 권한 부족으로 인해 발생하던 문제를 수정했습니다.\n\n---\n\n## v1.0.0\n> *2025/12/22*\n\n### ✨ 새 기능\n- **동기화**: 설정 파일 동기화 기능 및 인터페이스를 추가했습니다.\n\n### 🚀 최적화\n- **스크립트**: 스크립트 출력 표시를 최적화했습니다.\n\n### 🛠️ 수정\n- **스크립트**: 스크립트 실행 제어 실패 문제를 수정했습니다.\n\n---\n\n## v0.11.5\n> *2025/12/19*\n\n### 🛠️ 수정\n- **Docker**: Docker 이미지 실행 문제를 수정했습니다.\n\n---\n\n## v0.11.4\n> *2025/12/18*\n\n### ✨ 새 기능\n- **인증**: 인증 확인 인터페이스에서 버전 정보 반환 기능을 추가했습니다.\n\n---\n\n## v0.11.3\n> *2025/12/16*\n\n### ✨ 새 기능\n- **정리**: 시작 시 자동 정리 작업 및 Session 자동 정리 로직을 추가했습니다.\n\n### 🛠️ 수정\n- **안정성**: 고동시성 상황에서 연결 종료로 인한 비정상 종료 문제를 수정했습니다.\n\n---\n\n## v0.11.2\n> *2025/12/15*\n\n### 🛠️ 수정\n- **안정성**: 동시성 상황에서 연결 종료로 인한 비정상 종료 문제를 수정했습니다.\n\n---\n\n## v0.11.1\n> *2025/12/14*\n\n### ✨ 새 기능\n- **아키텍처**: 향후 기능 확장을 용이하게 하기 위해 메시지에 접두사를 추가했습니다.\n\n---\n\n## v0.10.2\n> *2025/12/12*\n\n### ✨ 새 기능\n- **설정**: 업로드/다운로드 청크 설정을 추가했습니다 (기본 512KB).\n\n---\n\n## v0.10.1\n> *2025/12/12*\n\n### ✨ 새 기능\n- **기능**: 바이너리 파일 다운로드 기능을 추가했습니다.\n- **기능**: WebSocket을 통한 청크 다운로드 기능을 추가했습니다.\n- **기능**: 버전 관리 기능을 추가했습니다.\n\n---\n\n## v0.9.6\n> *2025/12/11*\n\n- 초기 버전 (기록 시작).\n"
  },
  {
    "path": "docs/CHANGELOG.zh-CN.md",
    "content": "# 更新日志 (CHANGELOG)\n\n本项目的所有重大变更都将记录在此文件中。\n\n本项目遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/0.3.0/) 规范。\n\n---\n\n## v1.16.2\n> *2026/02/14*\n\n### 🚀 优化\n\n- **WebGui**: 调整 WebGui 界面位置。\n- **功能**: 增加服务器信息的展示。\n- **性能**: 针对各类列表显示高度过低的问题，优化为零拷贝访问。\n- **同步**: 优化笔记/附件同步逻辑。\n\n---\n\n## v1.16.1\n> *2026/02/14*\n\n### 🚀 优化\n\n- **WebGui**: 调整 WebGui 界面位置。\n- **功能**: 增加服务器信息的展示。\n\n---\n\n## v1.15.11\n> *2026/02/14*\n\n### 🚀 优化\n\n- **WebGui**: 优化 WebGui 界面，增加对 URL 的支持。\n\n---\n\n## v1.15.10\n> *2026/02/14*\n\n### 🚀 优化\n\n- **架构**: 调整服务工具集。\n- **API**: 调整接口返回的结构。\n\n---\n\n## v1.15.9\n> *2026/02/14*\n\n### ✨ 新增\n\n- **工具**: 为 fns 增加 docs 和 ws 调试工具访问入口。\n\n---\n\n## v1.15.8\n> *2026/02/13*\n\n### 🛠️ 修复\n\n- **稳定性**: 修复时间处理的小 BUG。\n\n---\n\n## v1.15.7\n> *2026/02/13*\n\n### 🛠️ 修复\n\n- **同步**: 修复离线删除清理本地哈希表的问题。\n\n---\n\n## v1.15.6\n> *2026/02/13*\n\n### 🛠️ 修复\n\n- **脚本**: 修复 fns 快捷脚本在 macOS 下的运行问题。\n- **日志**: 修正日志打印内容。\n\n---\n\n## v1.15.5\n> *2026/02/12*\n\n### 🚀 优化\n\n- **CI/CD**: 调整 GitHub Action 使用 go mod 版本进行编译发布。\n\n---\n\n## v1.15.4\n> *2026/02/12*\n\n### ✨ 新增\n\n- **同步**: 增加清理笔记配置相关消息功能。\n\n---\n\n## v1.15.3\n> *2026/02/10*\n\n### 🛠️ 修复\n\n- **目录**: 给重复目录做一层兜底方案，增加启动服务清理重复目录功能。\n\n---\n\n## v1.15.2\n> *2026/02/09*\n\n### 🚀 优化\n\n- **数据库**: 优化 DB 性能及结构，进行批量格式化。\n\n---\n\n## v1.15.1\n> *2026/02/07*\n\n### ✨ 新增\n\n- **目录**: 新增目录（Folder）管理功能，包括 Model 及相关逻辑。\n- **同步**: 修复潜在的数据竞争问题，优化笔记和附件重命名功能。\n\n---\n\n## v1.14.1\n> *2026/01/31*\n\n### ✨ 新增\n\n- **回收站**: 附件管理支持回收站及批量恢复功能。\n\n### 🛠️ 修复\n\n- **稳定性**: 修复附件/配置文件因修改时间与内容一致导致的资源未正常创建问题。\n\n### 🚀 优化\n\n- **API**: 优化附件查看/下载接口，改为零拷贝访问。\n- **WebGui**: 优化各类列表显示高度过低的问题。\n\n---\n\n## v1.14.0\n> *2026/01/31*\n\n### ✨ 新增\n\n- **回收站**: 增加附件管理回收站。\n- **WebGui**: 增加服务器信息的展示。\n- **同步**: 增加笔记和附件重命名功能。\n\n### 🛠️ 修复\n\n- **稳定性**: 修复潜在的数据竞争问题。\n\n---\n\n## v1.13.0\n> *2026/01/30*\n\n### ✨ 新增\n\n- **同步**: 新增附件、笔记、配置离线删除同步功能。\n- **同步**: 增量同步模式下增加自动下载缺失文件功能。\n\n---\n\n## v1.12.0\n> *2026/01/29*\n\n### 🚀 优化\n\n- **语言**: 将所有代码注释及文档统一翻译/更新为中英双语或英文。\n- **API**: 优化项目返回信息的国际化支持。\n- **稳定性**: 修正自动资源前缀问题。\n- **API**: 新增 API 扩展：编辑操作、反向链接 (Backlinks)、健康检查。\n\n---\n\n## v1.11.3\n> *2026/01/27*\n\n### 🛠️ 修复\n- **附件**: 修正附件下载请求超时（30s）导致报错的问题，调整为可配置且默认 1 小时。\n\n---\n\n## v1.11.2\n> *2026/01/27*\n\n### ✨ 新增\n- **WebGui**: 增加 Obsidian SSO 自动授权机制。\n\n### 🚀 优化\n- **WebGui**: 优化授权配置界面 UI。\n\n---\n\n## v1.11.1\n> *2026/01/26*\n\n### 🚀 优化\n- **发布**: 调整版本发布流程。\n\n---\n\n## v1.11.0\n> *2026/01/26*\n\n### ✨ 新增\n- **功能**: 新增版本检测及版本信息获取功能。\n\n---\n\n## v1.10.8\n> *2026/01/26*\n\n### ✨ 新增\n- **API**: 新增附件状态检测接口。\n\n---\n\n## v1.10.7\n> *2026/01/25*\n\n### 🛠️ 修复\n- **稳定性**: 修复由于上传文件校验一致性判断引发的服务崩溃问题。\n\n---\n\n## v1.10.6\n> *2026/01/24*\n\n### ✨ 新增\n- **WebGui**: 附件管理页面增加分页功能。\n\n---\n\n## v1.10.5\n> *2026/01/23*\n\n### 🛠️ 修复\n- **回收站**: 修正从回收站和历史版本中恢复笔记/版本的问题。\n\n---\n\n## v1.10.4\n> *2026/01/23*\n\n### 🛠️ 修复\n- **附件**: 修复附件上传过程中网络断开导致的异常，降低报错等级。\n\n---\n\n## v1.10.3\n> *2026/01/20*\n\n### 🚀 优化\n- **WebGui**: 取消笔记仓库放大效果，改为选中阴影效果。\n\n### 🛠️ 修复\n- **WebGui**: 修复笔记仓库名称含特殊字符时无法访问的问题。\n\n---\n\n## v1.10.2\n> *2026/01/20*\n\n### 🛠️ 修复\n- **管理**: 修复新用户无法注册及无法关闭用户注册设置的 Bug。\n\n---\n\n## v1.10.1\n> *2026/01/20*\n\n### 🛠️ 修复\n- **管理**: 修复新用户无法注册的问题。\n\n---\n\n## v1.10.0\n> *2026/01/19*\n\n### ✨ 新增\n- **附件**: 增加附件管理相关功能。\n- **鉴权**: 增加 Token 过期时间配置。\n- **分享**: 增加分享功能相关接口。\n- **文档**: 增加 Swagger API 文档。\n\n### 🚀 优化\n- **WebGui**: 调整 WebGui 部署路径。\n- **API**: 细化接口错误提示。\n\n### 🛠️ 修复\n- **WebGui**: 修正 WebGui 自动翻译导致的提示问题。\n\n---\n\n## v1.9.1\n> *2026/01/14*\n\n### 🚀 优化\n- **WebGui**: 增加蓝色配色方案，优化编辑器显示效果。\n\n---\n\n## v1.9.0\n> *2026/01/14*\n\n### ✨ 新增\n- **WebGui**: 界面全新重构（由 @ZyphrZero 贡献）。\n- **WebGui**: 更换编辑器为 Vditor，支持富文本和 Markdown 即时渲染。\n- **WebGui**: 支持自定义笔记搜索、列表字段排序及颜色主题。\n- **WebGui**: 新增暗黑模式、在线版本检测及回收站笔记恢复功能。\n- **设置**: 新增历史记录保留版本数及保存延迟设置。\n\n### 🚀 优化\n- **安全**: 优化服务令牌加密混淆字符。\n\n---\n\n## v1.8.1\n> *2026/01/12*\n\n### 🔄 变更\n- **架构**: 引入 DDD 分层架构（由 @ZyphrZero 贡献），移除全局变量，实现依赖注入模式。\n\n### 🚀 优化\n- **同步**: 离线笔记合并优化，实现基于行级的冲突检测与三方合并。\n- **性能**: 新增 Worker Pool 和 Per-User Write Queue，解决 SQLite 并发锁定问题。\n- **WebSocket**: 优化 Context 生命周期管理，增强 TraceID 追踪能力。\n\n### 🛠️ 修复\n- **逻辑**: 修复笔记重命名导致的笔记丢失和报错问题。\n\n---\n\n## v1.7.3\n> *2026/01/09*\n\n### 🛠️ 修复\n- **数据库**: 增加数据库创建失败时的友好报错提示。\n\n---\n\n## v1.7.2\n> *2026/01/09*\n\n### ✨ 新增\n- **WebGui**: 增加配置设置功能及相关接口。\n- **管理**: 增加管理员 ID 设置。\n\n---\n\n## v1.7.1\n> *2026/01/09*\n\n### ✨ 新增\n- **同步**: 新增离线设备笔记编辑合并功能（需插件端 v1.7+ 开启）。\n\n---\n\n## v1.6.3\n> *2026/01/08*\n\n### 🚀 优化\n- **WebGui**: 优化笔记列表搜索功能。\n- **WebGui**: 增加图标显示。\n- **WebGui**: 笔记仓库增加附件显示和刷新按钮。\n\n### 🛠️ 修复\n- **稳定性**: 修复并发查询时可能出现的异常。\n\n---\n\n## v1.6.1\n> *2026/01/07*\n\n### 🚀 优化\n- **性能**: 优化大笔记库同步效率及数据处理（需插件端 v1.6+）。\n- **缓存**: 为静态内容增加浏览器缓存机制。\n\n> [!CAUTION]\n> 本版本涉及数据库结构优化，建议删除原服务端 `storage/database` 目录下的 DB 文件，笔记修改历史将重新生成。\n\n---\n\n## v1.5.4\n> *2026/01/06*\n\n### 🛠️ 修复\n- **附件**: 修正上传附件时偶尔出现的报错问题。\n\n---\n\n## v1.5.3\n> *2026/01/06*\n\n### 🚀 优化\n- **WebGui**: 对编辑功能进行延时加载，提升首页加载速度。\n\n---\n\n## v1.5.2\n> *2026/01/05*\n\n### 🛠️ 修复\n- **同步**: 修正同步任务进度显示不准确的问题。\n\n---\n\n## v1.5.1\n> *2026/01/04*\n\n### 🛠️ 修复\n- **逻辑**: 修正笔记重命名后无法正常删除的问题。\n- **稳定性**: 修正大规模笔记同步时导致 WebSocket 连接重置的问题。\n- **多语言**: 修复 WebGui 接口语言错误。\n\n---\n\n## v1.5.0\n> *2026/01/04*\n\n### ✨ 新增\n- **回收站**: 增加笔记回收站功能。\n- **WebGui**: 增加用户状态检测。\n- **WebGui**: 注册页面增加关闭注册的检测。\n- **WebGui**: 增加操作确认的键盘快捷键支持。\n\n### 🚀 优化\n- **WebGui**: 优化笔记编辑页面的使用体验。\n- **数据库**: 优化并解决数据库并发访问问题。\n\n### 🛠️ 修复\n- **脚本**: 修正快捷脚本可能覆盖配置文件的问题。\n\n---\n\n## v1.4.7\n> *2026/01/03*\n\n### 🛠️ 修复\n- **数据库**: 尝试解决 SQLite 并发问题，修正内部错误码。\n\n---\n\n## v1.4.6\n> *2026/01/03*\n\n### 🛠️ 修复\n- **Docker**: 修正 Docker 环境下运行报 `temp` 目录不存在的问题。\n\n---\n\n## v1.4.5\n> *2026/01/03*\n\n### 🛠️ 修复\n- **同步**: 修正首次同步或全量同步时无法同步附件的问题（需插件端 v1.5.14+）。\n\n---\n\n## v1.4.4\n> *2026/01/02*\n\n### 🛠️ 修复\n- **访问**: 修正 Emoji 标题无法访问的问题。\n\n### ✨ 新增\n- **文档**: 增加帮助文件。\n\n---\n\n## v1.4.3\n> *2026/01/02*\n\n### 🔄 变更\n- **仓库**: 笔记仓库删除操作改为软删除。\n\n---\n\n## v1.4.2\n> *2026/01/01*\n\n### ✨ 新增\n- **WebGui**: 笔记删除操作增加红色的二次确认弹窗，防止误删。\n\n---\n\n## v1.4.1\n> *2025/12/31*\n\n### 🚀 优化\n- **API**: 笔记资源（图片等）下载接口增加 ETag 浏览器缓存机制，提升加载速度。\n\n---\n\n## v1.4.0\n> *2025/12/31*\n\n### ✨ 新增\n- **WebGui**: 增加最大化按钮，提升全屏编辑体验。\n- **WebGui**: 笔记查看页面增加对 Obsidian 内嵌图片、PDF 以及其他附件的正常显示支持。\n- **API**: 增加资源下载接口。\n\n---\n\n## v1.3.8\n> *2025/12/31*\n\n### 🚀 优化\n- **服务端**: 为笔记建立内容哈希版本库，方便后续进行溯源、对比和合并操作。\n\n---\n\n## v1.3.7\n> *2025/12/30*\n\n### 🛠️ 修复\n- **稳定性**: 为任务和升级脚本增加宕机恢复机制，避免单个任务报错导致整个服务崩溃。\n- **稳定性**: 修复原有各层级出现的空指针（nil pointer）导致的 Panic 问题。\n\n---\n\n## v1.3.6\n> *2025/12/30*\n\n### 🛠️ 修复\n- **任务管理**: 修复任务管理器中存在的报错问题。\n\n---\n\n## v1.3.5\n> *2025/12/30*\n\n### 🚀 优化\n- **WebGui**: 优化笔记查看页面的显示效果。\n- **脚本**: 优化一键安装/管理脚本。\n\n---\n\n## v1.3.4\n> *2025/12/30*\n\n### 🛠️ 修复\n- **同步**: 修复同步命令处理错误导致的文件错误同步创建到所有客户端的问题。\n- **脚本**: 修复一键脚本在 `Ctrl+C` 时导致已启动的服务被同步关闭的问题。\n\n---\n\n## v1.3.3\n> *2025/12/29*\n\n### 🛠️ 修复\n- **同步**: 解决单用户多笔记仓库时可能出现的更新混淆问题。\n\n---\n\n## v1.3.2\n> *2025/12/28*\n\n### ✨ 新增\n- **多语言**: 增加对多语言环境的支持。\n\n### 🚀 优化\n- **WebGui**: 优化笔记历史差异对比的显示效果。\n\n---\n\n## v1.3.1\n> *2025/12/28*\n\n### 🚀 优化\n- **逻辑处理**: 优化笔记修改标题时的逻辑处理流程。\n\n---\n\n## v1.3.0\n> *2025/12/28*\n\n### ✨ 新增\n- **WebGui**: 增加设置项，允许用户控制 WebGui 的字体设置。\n\n---\n\n## v1.2.6\n> *2025/12/27*\n\n### 🚀 优化\n- **WebGui**: 优化字体加载逻辑，避免字体加载导致的界面卡顿。\n\n---\n\n## v1.2.5\n> *2025/12/27*\n\n### ✨ 新增\n- **客户端**: 增加对客户端名称的记录支持。\n\n### 🚀 优化\n- **清理逻辑**: 增加笔记重命名后的同步清理逻辑。\n\n---\n\n## v1.2.4\n> *2025/12/27*\n\n### 🛠️ 修复\n- **WebGui**: 修复历史版本内容为空时导致的显示 Bug。\n\n---\n\n## v1.2.3\n> *2025/12/27*\n\n### ✨ 新增\n- **API**: 增加笔记历史相关的接口和功能。\n\n### 🚀 优化\n- **数据库**: 优化数据库查询效率。\n- **WebGui**: 修改 WebGui 显示字体并修复各类显示 Bug。\n\n### 🛠️ 修复\n- **稳定性**: 修复高并发访问时出现的问题。\n\n---\n\n## v1.2.2\n> *2025/12/27*\n\n### 🛠️ 修复\n- **WebGui**: 修正笔记历史为空时导致的页面空白问题。\n\n---\n\n## v1.2.1\n> *2025/12/27*\n\n### ✨ 新增\n- **API**: 增加笔记历史相关接口和功能。\n\n### 🚀 优化\n- **数据库**: 优化数据库查询效率。\n- **稳定性**: 解决大量并发访问时的稳定性问题。\n\n---\n\n## v1.0.4\n> *2025/12/26*\n\n### 🛠️ 修复\n- **WebGui**: 修正 WebGui 页面构建（Build）异常导致的空白显示问题。\n\n---\n\n## v1.0.3\n> *2025/12/25*\n\n### 🛠️ 修复\n- **WebGui**: 解决笔记标题过长导致的排版显示问题。\n\n---\n\n## v1.0.2\n> *2025/12/25*\n\n### 🚀 优化\n- **附件**: 优化附件上传逻辑，显著减少上传时间。\n\n### 🛠️ 修复\n- **CI/CD**: 修正 GitHub Action 的更新限制问题。\n\n---\n\n## v1.0.1\n> *2025/12/23*\n\n### 🛠️ 修复\n- **权限**: 修复部分系统在上传时由于权限不足导致的问题。\n\n---\n\n## v1.0.0\n> *2025/12/22*\n\n### ✨ 新增\n- **同步**: 新增配置文件同步相关功能及接口。\n\n### 🚀 优化\n- **脚本**: 优化显示脚本的输出。\n\n### 🛠️ 修复\n- **脚本**: 修正脚本控制运行失败的问题。\n\n---\n\n## v0.11.5\n> *2025/12/19*\n\n### 🛠️ 修复\n- **Docker**: 修正 Docker 镜像无法执行的问题。\n\n---\n\n## v0.11.4\n> *2025/12/18*\n\n### ✨ 新增\n- **鉴权**: 在验证授权接口中增加下发版本信息的功能。\n\n---\n\n## v0.11.3\n> *2025/12/16*\n\n### ✨ 新增\n- **清理**: 增加程序启动时的自动清理任务以及 Session 自动清理逻辑。\n\n### 🛠️ 修复\n- **稳定性**: 修正高并发下由于连接关闭导致的异常退出问题。\n\n---\n\n## v0.11.2\n> *2025/12/15*\n\n### 🛠️ 修复\n- **稳定性**: 修正并发下由于连接关闭导致的程序异常退出。\n\n---\n\n## v0.11.1\n> *2025/12/14*\n\n### ✨ 新增\n- **架构**: 给消息增加前缀，方便后续业务功能扩容。\n\n---\n\n## v0.10.2\n> *2025/12/12*\n\n### ✨ 新增\n- **设置**: 增加上传下载分片设置项（默认 512KB）。\n\n---\n\n## v0.10.1\n> *2025/12/12*\n\n### ✨ 新增\n- **功能**: 增加二进制文件下载功能。\n- **功能**: 增加 WebSocket 分块下载功能。\n- **功能**: 增加版本控制管理。\n\n---\n\n## v0.9.6\n> *2025/12/11*\n\n- 初始版本（记录开始）。\n"
  },
  {
    "path": "docs/CHANGELOG.zh-TW.md",
    "content": "# 更新日誌 (CHANGELOG)\n\n本專案的所有重大變更都將記錄在此文件中。\n\n本專案遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/0.3.0/) 規範。\n\n---\n\n## v1.16.2\n> *2026/02/14*\n\n### 🚀 優化\n\n- **WebGui**: 調整 WebGui 介面位置。\n- **功能**: 新增伺服器資訊顯示。\n- **性能**: 針對各類列表顯示高度過低的問題，採用零拷貝（Zero-copy）方式進行優化。\n- **同步**: 優化筆記/附件同步邏輯。\n\n---\n\n## v1.16.1\n> *2026/02/14*\n\n### 🚀 優化\n\n- **WebGui**: 調整 WebGui 介面位置。\n- **功能**: 新增伺服器資訊顯示。\n\n---\n\n## v1.15.11\n> *2026/02/14*\n\n### 🚀 優化\n\n- **WebGui**: 優化 WebGui 介面並增加 URL 支援。\n\n---\n\n## v1.15.10\n> *2026/02/14*\n\n### 🚀 優化\n\n- **架構**: 調整服務工具集。\n- **API**: 調整介面回應結構。\n\n---\n\n## v1.15.9\n> *2026/02/14*\n\n### ✨ 新功能\n\n- **工具**: 在 fns 中增加 docs 及 ws 調試工具的存取入口。\n\n---\n\n## v1.15.8\n> *2026/02/13*\n\n### 🛠️ 修復\n\n- **穩定性**: 修復時間處理相關的輕微 Bug。\n\n---\n\n## v1.15.7\n> *2026/02/13*\n\n### 🛠️ 修復\n\n- **同步**: 修復離線刪除時，本地雜湊表未清理的問題。\n\n---\n\n## v1.15.6\n> *2026/02/13*\n\n### 🛠️ 修復\n\n- **指令碼**: 修復 macOS 環境下 fns 捷徑指令碼的執行問題。\n- **日誌**: 修正日誌輸出內容。\n\n---\n\n## v1.15.5\n> *2026/02/12*\n\n### 🚀 優化\n\n- **CI/CD**: 調整 GitHub Action 使用 go mod 版本進行構建與發佈。\n\n---\n\n## v1.15.4\n> *2026/02/12*\n\n### ✨ 新功能\n\n- **同步**: 新增筆記配置相關訊息清理功能。\n\n---\n\n## v1.15.3\n> *2026/02/10*\n\n### 🛠️ 修復\n\n- **目錄**: 增加重複目錄的規避方案，並在啟動時增加重複目錄清理功能。\n\n---\n\n## v1.15.2\n> *2026/02/09*\n\n### 🚀 優化\n\n- **資料庫**: DB 性能及結構優化，並進行批次格式化。\n\n---\n\n## v1.15.1\n> *2026/02/07*\n\n### ✨ 新功能\n\n- **目錄**: 新增目錄（Folder）管理功能（含 Model 及相關邏輯）。\n- **同步**: 修復潛在的資料競爭問題，優化筆記/附件重新命名功能。\n\n---\n\n## v1.14.1\n> *2026/01/31*\n\n### ✨ 新功能\n\n- **回收站**: 附件管理支援回收站及批次恢復功能。\n\n### 🛠️ 修復\n\n- **穩定性**: 修復附件/配置文件更新時間與內容一致時資源未平滑產生的問題。\n\n### 🚀 優化\n\n- **API**: 採用零拷貝方式優化附件查詢/下載介面。\n- **WebGui**: 優化各類列表顯示高度過低的問題。\n\n---\n\n## v1.14.0\n> *2026/01/31*\n\n### ✨ 新功能\n\n- **回收站**: 附件管理新增回收站。\n- **WebGui**: 新增伺服器資訊顯示。\n- **同步**: 新增筆記及附件重新命名功能。\n\n### 🛠️ 修復\n\n- **穩定性**: 修復潛在的資料競爭問題。\n\n---\n\n## v1.13.0\n> *2026/01/30*\n\n### ✨ 新增\n- **同步**: 新增附件、筆記、配置離線刪除同步功能。\n- **同步**: 增量同步模式下增加自動下載缺失文件功能。\n\n---\n\n## v1.12.0\n> *2026/01/29*\n\n### 🚀 優化\n- **語言**: 將所有代碼註釋及文檔統一翻譯/更新為中英雙語或英文。\n- **API**: 優化項目返回信息的國際化支持。\n- **穩定性**: 修正自動資源前綴問題。\n- **API**: 新增 API 擴展：編輯操作、反連結 (Backlinks)、健康檢查。\n\n---\n\n## v1.11.3\n> *2026/01/27*\n\n### 🛠️ 修復\n- **附件**: 修正附件下載請求超時（30s）導致報錯的問題，調整為可配置且默認 1 小時。\n\n---\n\n## v1.11.2\n> *2026/01/27*\n\n### ✨ 新增\n- **WebGui**: 增加 Obsidian SSO 自動授權機制。\n\n### 🚀 優化\n- **WebGui**: 優化授權配置界面 UI。\n\n---\n\n## v1.11.1\n> *2026/01/26*\n\n### 🚀 優化\n- **發佈**: 調整版本發佈流程。\n\n---\n\n## v1.11.0\n> *2026/01/26*\n\n### ✨ 新增\n- **功能**: 新增版本檢測及版本信息獲取功能。\n\n---\n\n## v1.10.8\n> *2026/01/26*\n\n### ✨ 新增\n- **API**: 新增附件狀態檢測接口。\n\n---\n\n## v1.10.7\n> *2026/01/25*\n\n### 🛠️ 修復\n- **穩定性**: 修復由於上傳文件校驗一致性判斷引發的服務崩潰問題。\n\n---\n\n## v1.10.6\n> *2026/01/24*\n\n### ✨ 新增\n- **WebGui**: 附件管理頁面增加分頁功能。\n\n---\n\n## v1.10.5\n> *2026/01/23*\n\n### 🛠️ 修復\n- **回收站**: 修正從回收站和歷史版本中恢復筆記/版本的問題。\n\n---\n\n## v1.10.4\n> *2026/01/23*\n\n### 🛠️ 修復\n- **附件**: 修復附件上傳過程中網絡斷開導致的異常，降低報錯等級。\n\n---\n\n## v1.10.3\n> *2026/01/20*\n\n### 🚀 優化\n- **WebGui**: 取消筆記倉庫放大效果，改為選中陰影效果。\n\n### 🛠️ 修復\n- **WebGui**: 修復筆記倉庫名稱含特殊字符時無法訪問的 Bug。\n\n---\n\n## v1.10.2\n> *2026/01/20*\n\n### 🛠️ 修復\n- **管理**: 修復新用戶無法註冊及無法關閉用戶註冊設置的 Bug。\n\n---\n\n## v1.10.1\n> *2026/01/20*\n\n### 🛠️ 修復\n- **管理**: 修復新用戶無法註冊的問題。\n\n---\n\n## v1.10.0\n> *2026/01/19*\n\n### ✨ 新增\n- **附件**: 增加附件管理相關功能。\n- **鑑權**: 增加 Token 過期時間配置。\n- **分享**: 增加分享功能相關接口。\n- **文檔**: 增加 Swagger API 文檔。\n\n### 🚀 優化\n- **WebGui**: 調整 WebGui 部署路徑。\n- **API**: 細化接口錯誤提示。\n\n### 🛠️ 修復\n- **WebGui**: 修正 WebGui 自動翻譯導致的提示問題。\n\n---\n\n## v1.9.1\n> *2026/01/14*\n\n### 🚀 優化\n- **WebGui**: 增加藍色配色方案，優化編輯器顯示效果。\n\n---\n\n## v1.9.0\n> *2026/01/14*\n\n### ✨ 新增\n- **WebGui**: 界面全新重構（由 @ZyphrZero 貢獻）。\n- **WebGui**: 更換編輯器為 Vditor，支持富文本和 Markdown 即時渲染。\n- **WebGui**: 支持自定義筆記搜索、列表字段排序及顏色主題。\n- **WebGui**: 新增暗黑模式、在線版本檢測及回收站筆記恢復功能。\n- **設置**: 新增歷史記錄保留版本數及保存延遲設置。\n\n### 🚀 優化\n- **安全**: 優化服務令牌加密混淆字符。\n\n---\n\n## v1.8.1\n> *2026/01/12*\n\n### 🔄 變更\n- **架構**: 引入 DDD 分層架構（由 @ZyphrZero 貢獻），移除全局變量，實現依賴注入模式。\n\n### 🚀 優化\n- **同步**: 離線筆記合併優化，實現基於行級的衝突檢測與三方合併。\n- **性能**: 新增 Worker Pool 和 Per-User Write Queue，解決 SQLite 併發鎖定問題。\n- **WebSocket**: 優化 Context 生命周期管理，增強 TraceID 追蹤能力。\n\n### 🛠️ 修復\n- **邏輯**: 修復筆記重命名導致的筆記丟失和報錯問題。\n\n---\n\n## v1.7.3\n> *2026/01/09*\n\n### 🛠️ 修復\n- **數據庫**: 增加數據庫創建失敗時的友好報錯提示。\n\n---\n\n## v1.7.2\n> *2026/01/09*\n\n### ✨ 新增\n- **WebGui**: 增加配置設置功能及相關接口。\n- **管理**: 增加管理員 ID 設置。\n\n---\n\n## v1.7.1\n> *2026/01/09*\n\n### ✨ 新增\n- **同步**: 新增離線設備筆記編輯合併功能（需插件端 v1.7+ 開啟）。\n\n---\n\n## v1.6.3\n> *2026/01/08*\n\n### 🚀 優化\n- **WebGui**: 優化筆記列表搜索功能。\n- **WebGui**: 增加圖標顯示。\n- **WebGui**: 筆記倉庫增加附件顯示和刷新按鈕。\n\n### 🛠️ 修復\n- **穩定性**: 修復併發查詢時可能出現的異常。\n\n---\n\n## v1.6.1\n> *2026/01/07*\n\n### 🚀 優化\n- **性能**: 優化大筆記庫同步效率及數據處理（需插件端 v1.6+）。\n- **緩存**: 為靜態內容增加瀏覽器緩存機制。\n\n> [!CAUTION]\n> 本版本涉及數據庫結構優化，建議刪除原服務端 `storage/database` 目錄下的 DB 文件，筆記修改歷史將重新生成。\n\n---\n\n## v1.5.4\n> *2026/01/06*\n\n### 🛠️ 修復\n- **附件**: 修正上傳附件時偶爾出現的報錯問題。\n\n---\n\n## v1.5.3\n> *2026/01/06*\n\n### 🚀 優化\n- **WebGui**: 對編輯功能進行延時加載，提升首頁加載速度。\n\n---\n\n## v1.5.2\n> *2026/01/05*\n\n### 🛠️ 修復\n- **同步**: 修正同步任務進度顯示不準確的問題。\n\n---\n\n## v1.5.1\n> *2026/01/04*\n\n### 🛠️ 修復\n- **邏輯**: 修正筆記重命名後無法正常刪除的問題。\n- **穩定性**: 修正大規模筆記同步時導致 WebSocket 連接重置的問題。\n- **多語言**: 修復 WebGui 接口語言錯誤。\n\n---\n\n## v1.5.0\n> *2026/01/04*\n\n### ✨ 新增\n- **回收站**: 增加筆記回收站功能。\n- **WebGui**: 增加用戶狀態檢測。\n- **WebGui**: 註冊頁面增加關閉註冊的檢測。\n- **WebGui**: 增加操作確認的鍵盤快捷鍵支持。\n\n### 🚀 優化\n- **WebGui**: 優化筆記編輯頁面的使用體驗。\n- **數據庫**: 優化並解決數據庫併發訪問問題。\n\n### 🛠️ 修復\n- **腳本**: 修正快捷腳本可能覆蓋配置文件的問題。\n\n---\n\n## v1.4.7\n> *2026/01/03*\n\n### 🛠️ 修復\n- **數據庫**: 嘗試解決 SQLite 併發問題，修正內部錯誤碼。\n\n---\n\n## v1.4.6\n> *2026/01/03*\n\n### 🛠️ 修復\n- **Docker**: 修正 Docker 環境下運行報 `temp` 目錄不存在的問題。\n\n---\n\n## v1.4.5\n> *2026/01/03*\n\n### 🛠️ 修復\n- **同步**: 修正首次同步或全量同步時無法同步附件的問題（需插件端 v1.5.14+）。\n\n---\n\n## v1.4.4\n> *2026/01/02*\n\n### 🛠️ 修復\n- **訪問**: 修正 Emoji 標題無法訪問的問題。\n\n### ✨ 新增\n- **文檔**: 增加幫助文件。\n\n---\n\n## v1.4.3\n> *2026/01/02*\n\n### 🔄 變更\n- **倉庫**: 筆記倉庫刪除操作改為軟刪除。\n\n---\n\n## v1.4.2\n> *2026/01/01*\n\n### ✨ 新增\n- **WebGui**: 筆記刪除操作增加紅色的二次確認彈窗，防止誤刪。\n\n---\n\n## v1.4.1\n> *2025/12/31*\n\n### 🚀 優化\n- **API**: 筆記資源（圖片等）下載接口增加 ETag 瀏覽器緩存機制，提升加載速度。\n\n---\n\n## v1.4.0\n> *2025/12/31*\n\n### ✨ 新增\n- **WebGui**: 增加最大化按鈕，提升全屏編輯體驗。\n- **WebGui**: 筆記查看頁面增加對 Obsidian 內嵌圖片、PDF 以及其他附件的正常顯示支持。\n- **API**: 增加資源下載接口。\n\n---\n\n## v1.3.8\n> *2025/12/31*\n\n### 🚀 優化\n- **服務端**: 為筆記建立內容哈希版本庫，方便後續進行溯源、對比和合併操作。\n\n---\n\n## v1.3.7\n> *2025/12/30*\n\n### 🛠️ 修復\n- **穩定性**: 為任務和升級腳本增加宕機恢復機制，避免單個任務報錯導致整個服務崩潰。\n- **穩定性**: 修復原有各層級出現的空指針（nil pointer）導致的 Panic 問題。\n\n---\n\n## v1.3.6\n> *2025/12/30*\n\n### 🛠️ 修復\n- **任務管理**: 修復任務管理器中存在的報錯問題。\n\n---\n\n## v1.3.5\n> *2025/12/30*\n\n### 🚀 優化\n- **WebGui**: 優化筆記查看頁面的顯示效果。\n- **腳本**: 優化一鍵安裝/管理腳本。\n\n---\n\n## v1.3.4\n> *2025/12/30*\n\n### 🛠️ 修復\n- **同步**: 修復同步命令處理錯誤導致的文件錯誤同步創建到所有客戶端的問題。\n- **腳本**: 修復一鍵腳本在 `Ctrl+C` 時導致已啟動的服務被同步關閉的問題。\n\n---\n\n## v1.3.3\n> *2025/12/29*\n\n### 🛠️ 修復\n- **同步**: 解決單用戶多筆記倉庫時可能出現的更新混淆問題。\n\n---\n\n## v1.3.2\n> *2025/12/28*\n\n### ✨ 新增\n- **多語言**: 增加對多語言環境的支持。\n\n### 🚀 優化\n- **WebGui**: 優化筆記歷史差異對比的顯示效果。\n\n---\n\n## v1.3.1\n> *2025/12/28*\n\n### 🚀 優化\n- **邏輯處理**: 優化筆記修改標題時的邏輯處理流程。\n\n---\n\n## v1.3.0\n> *2025/12/28*\n\n### ✨ 新增\n- **WebGui**: 增加設置項，允許用戶控制 WebGui 的字體設置。\n\n---\n\n## v1.2.6\n> *2025/12/27*\n\n### 🚀 優化\n- **WebGui**: 優化字體加載邏輯，避免字體加載導致的界面卡頓。\n\n---\n\n## v1.2.5\n> *2025/12/27*\n\n### ✨ 新增\n- **客戶端**: 增加對客戶端名稱的記錄支持。\n\n### 🚀 優化\n- **清理邏輯**: 增加筆記重命名後的同步清理邏輯。\n\n---\n\n## v1.2.4\n> *2025/12/27*\n\n### 🛠️ 修復\n- **WebGui**: 修復歷史版本內容為空時導致的顯示 Bug。\n\n---\n\n## v1.2.3\n> *2025/12/27*\n\n### ✨ 新增\n- **API**: 增加筆記歷史相關的接口和功能。\n\n### 🚀 優化\n- **數據庫**: 優化數據庫查詢效率。\n- **WebGui**: 修改 WebGui 顯示字體並修復各類顯示 Bug。\n\n### 🛠️ 修復\n- **穩定性**: 修復高併發訪問時出現的問題。\n\n---\n\n## v1.2.2\n> *2025/12/27*\n\n### 🛠️ 修復\n- **WebGui**: 修正筆記歷史為空時導致的頁面空白問題。\n\n---\n\n## v1.2.1\n> *2025/12/27*\n\n### ✨ 新增\n- **API**: 增加筆記歷史相關接口和功能。\n\n### 🚀 優化\n- **數據庫**: 優化數據庫查詢效率。\n- **穩定性**: 解決大量併發訪問時的穩定性問題。\n\n---\n\n## v1.0.4\n> *2025/12/26*\n\n### 🛠️ 修復\n- **WebGui**: 修正 WebGui 頁面構建（Build）異常導致的空白顯示問題。\n\n---\n\n## v1.0.3\n> *2025/12/25*\n\n### 🛠️ 修復\n- **WebGui**: 解決筆記標題過長導致的排版顯示問題。\n\n---\n\n## v1.0.2\n> *2025/12/25*\n\n### 🚀 優化\n- **附件**: 優化附件上傳邏輯，顯著減少上傳時間。\n\n### 🛠️ 修復\n- **CI/CD**: 修正 GitHub Action 的更新限制問題。\n\n---\n\n## v1.0.1\n> *2025/12/23*\n\n### 🛠️ 修復\n- **權限**: 修復部分系統在上傳時由於權限不足導致的問題。\n\n---\n\n## v1.0.0\n> *2025/12/22*\n\n### ✨ 新增\n- **同步**: 新增配置文件同步相關功能及接口。\n\n### 🚀 優化\n- **腳本**: 優化顯示腳本的輸出。\n\n### 🛠️ 修復\n- **腳本**: 修正腳本控制運行失敗的問題。\n\n---\n\n## v0.11.5\n> *2025/12/19*\n\n### 🛠️ 修復\n- **Docker**: 修正 Docker 鏡像無法執行的問題。\n\n---\n\n## v0.11.4\n> *2025/12/18*\n\n### ✨ 新增\n- **鑑權**: 在驗證授權接口中增加下發版本信息的功能。\n\n---\n\n## v0.11.3\n> *2025/12/16*\n\n### ✨ 新增\n- **清理**: 增加程序啟動時的自動清理任務以及 Session 自動清理邏輯。\n\n### 🛠️ 修復\n- **穩定性**: 修正高併發下由於連接關閉導致的異常退出問題。\n\n---\n\n## v0.11.2\n> *2025/12/15*\n\n### 🛠️ 修復\n- **穩定性**: 修正併發下由於連接關閉導致的程序異常退出。\n\n---\n\n## v0.11.1\n> *2025/12/14*\n\n### ✨ 新增\n- **架構**: 給消息增加前綴，方便後續業務功能擴容。\n\n---\n\n## v0.10.2\n> *2025/12/12*\n\n### ✨ 新增\n- **設置**: 增加上傳下載分片設置項（默認 512KB）。\n\n---\n\n## v0.10.1\n> *2025/12/12*\n\n### ✨ 新增\n- **功能**: 增加二進制文件下載功能。\n- **功能**: 增加 WebSocket 分塊下載功能。\n- **功能**: 增加版本控制管理。\n\n---\n\n## v0.9.6\n> *2025/12/11*\n\n- 初始版本（記錄開始）。\n"
  },
  {
    "path": "docs/CHANGELOG_GUIDELINE.md",
    "content": "# CHANGELOG 维护规范\n\n本文档定义了本项目 `docs/CHANGELOG.md` 的维护标准，确保变更记录清晰、专业且符合行业通用规范。\n\n## 1. 基本准则\n- **面向用户**：变更日志应让用户（开发者或最终用户）轻松了解每个版本的具体变化。\n- **分类清晰**：所有变更必须归入特定的类别。\n- **版本对齐**：每个版本号必须对应 Git 中的一个 Release Tag。\n- **日期格式**：使用 `YYYY/MM/DD` 格式。\n\n## 2. 核心架构\n本项目遵循 [Keep a Changelog (0.3.0)](https://keepachangelog.com/zh-CN/0.3.0/) 核心思想，并结合 Emoji 进行视觉增强。\n\n### 修改类别定义\n在每个版本标题下，按以下固定类别组织内容：\n\n| 类别 (Category) | 标识符 (Emoji) | 适用场景 |\n| :--- | :---: | :--- |\n| **新增** | ✨ | 实现了全新的功能或特性。 |\n| **优化** | 🚀 | 对现有功能的改进、性能提升或体验优化。 |\n| **修复** | 🛠️ | 修复了 Bug、崩溃或非预期的行为。 |\n| **变更** | 🔄 | 对现有功能进行了重大调整（可能涉及破坏性变更）。 |\n| **废弃** | 🗑️ | 标记即将在后续版本中移除的功能。 |\n| **安全** | 🔒 | 修复了漏洞或提升了系统安全性。 |\n\n## 3. 编写格式规范\n\n### 版本标题\n使用二级标题，格式为：\n```\n## v版本号\n> *YYYY/MM/DD*\n```\n> 示例：\n```\n## v1.3.8\n> *2025/12/31*\n```\n\n### 条目描述\n- **模块加粗**：每个条目开头应标明所属模块（如 **WebGui: ...** 或 **API: ...**）。\n- **动词开头**：描述应以“增加”、“优化”、“修复”、“调整”等动词开头。\n- **简洁有力**：单行描述不宜过长，复杂的变更可使用子列表。\n\n```markdown\n### ✨ 新增\n- **WebGui**: 增加最大化按钮，提升全屏编辑体验。\n- **API**: 新增资源下载接口 `/api/v1/download`。\n\n### 🛠️ 修复\n- **稳定性**: 修复高并发下可能导致的内存泄漏问题。\n```\n\n## 4. 自动化与工具建议\n1. **手动同步**：在发布新 Tag 前，手动根据 Git Commits 整理内容并更新 `CHANGELOG.md`。\n2. **Commit 信息**：建议在提交代码时遵循 [Conventional Commits](https://www.conventionalcommits.org/)，以便未来实现自动生成日志。\n\n---\n\n> [!NOTE]\n> 本规范旨在通过统一的视觉和语境语言，让项目的发展脉络一目了然。\n"
  },
  {
    "path": "docs/PR-DESCRIPTION.md",
    "content": "# PR: Folder Tree Endpoint + Folder API Bug Fixes\n\n## Summary\n\nAdds a new `GET /api/folder/tree` endpoint that returns the complete folder hierarchy with note/file counts, and fixes duplicate folder bugs in the existing folder API endpoints.\n\n## New Feature: Folder Tree Endpoint\n\n```\nGET /api/folder/tree?vault=<name>&depth=<optional>\n```\n\nReturns the full folder tree structure with note and file counts per folder. Supports optional `depth` parameter to limit tree depth.\n\n**Response example:**\n```json\n{\n  \"folders\": [\n    {\n      \"path\": \"projects\",\n      \"name\": \"projects\",\n      \"noteCount\": 3,\n      \"fileCount\": 0,\n      \"children\": [\n        {\n          \"path\": \"projects/golf-email-series\",\n          \"name\": \"golf-email-series\",\n          \"noteCount\": 6,\n          \"fileCount\": 0,\n          \"children\": [...]\n        }\n      ]\n    }\n  ],\n  \"rootNoteCount\": 26,\n  \"rootFileCount\": 2\n}\n```\n\n## Bug Fixes\n\n### 1. Folder AutoMigrate missing (upstream bug)\n\n`folder_repository.go` was using `UseQuery()` instead of `UseQueryWithOnceFunc()`, so the folder table was never auto-created on fresh databases. Added `folder()` helper method with `model.AutoMigrate(g, \"Folder\")`, matching the pattern used in `note_repository.go`.\n\n### 2. Duplicate folders in API responses\n\n**Root cause:** `EnsurePathFID` has a check-then-create race condition. When multiple notes sync concurrently (even from a single device), each goroutine independently checks if a folder exists and creates it if not. Without atomicity, multiple goroutines can all see \"not found\" and all insert a record for the same path. Confirmed via direct DB inspection — e.g. 3 rows for \"projects\" path in a single-device setup.\n\n**Query-side fix applied to all affected endpoints:**\n\n| Endpoint | Fix |\n|----------|-----|\n| `GET /api/folders` (List) | Resolves all folder IDs per path via `GetAllByPathHash`, queries children across all matching parent FIDs, deduplicates results by PathHash |\n| `GET /api/folder/notes` (ListNotes) | Resolves all folder IDs per path, uses `FID IN (...)` query to find notes across all duplicate folder records |\n| `GET /api/folder/files` (ListFiles) | Same approach as ListNotes for files |\n| `GET /api/folder/tree` (GetTree) | Path-based deduplication, merges note/file counts across all duplicate folder records |\n\n**Note:** The `ListByUpdatedTimestamp` method (used by the sync endpoint) already had PathHash deduplication — the same pattern was missing from List, ListNotes, and ListFiles.\n\n**Root cause not fixed in this PR.** A proper fix would require either a `singleflight` keyed by `(vaultID, path)` in `EnsurePathFID`, or a `UNIQUE` constraint on `(vault_id, path_hash)` in the folder table. See comment on `EnsurePathFID` in `folder_service.go` for details. The query-side fixes make the API correct regardless of duplicate rows.\n\n### 3. Swagger docs regenerated\n\nUpdated generated Swagger files to include the new `/api/folder/tree` endpoint.\n\n## Files Changed\n\n| File | Change |\n|------|--------|\n| `internal/dto/folder_dto.go` | Added FolderTreeRequest, FolderTreeNode, FolderTreeResponse DTOs |\n| `internal/domain/repository.go` | Added `GetAllByPathHash` (FolderRepo), `ListByFIDs`/`ListByFIDsCount` (NoteRepo, FileRepo) |\n| `internal/dao/folder_repository.go` | Fixed AutoMigrate, implemented `GetAllByPathHash` |\n| `internal/dao/note_repository.go` | Implemented `ListByFIDs`, `ListByFIDsCount` |\n| `internal/dao/file_repository.go` | Implemented `ListByFIDs`, `ListByFIDsCount` |\n| `internal/service/folder_service.go` | Added `GetTree`, fixed `List`/`ListNotes`/`ListFiles` dedup, documented race condition |\n| `internal/routers/api_router/handler_folder.go` | Added `Tree` handler with Swagger annotations |\n| `internal/routers/router.go` | Registered `/folder/tree` route |\n| `docs/docs.go`, `docs/swagger.json`, `docs/swagger.yaml` | Regenerated Swagger docs |\n\n## Testing\n\n23/23 API tests pass (test-folder-api.sh), including:\n- All existing folder CRUD endpoints\n- New folder tree endpoint (full tree + depth-limited)\n- Note/file listing within folders\n- Backlinks, outlinks, note edit operations\n\n## Breaking Changes\n\nNone. All changes are additive. Existing API behavior is preserved (with duplicates now correctly deduplicated).\n"
  },
  {
    "path": "docs/README.ja.md",
    "content": "[简体中文](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.zh-CN.md) / [English](https://github.com/haierkeys/fast-note-sync-service/blob/master/README.md) / [日本語](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.ja.md) / [한국어](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.ko.md) / [繁體中文](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.zh-TW.md)\n\nご質問がある場合は、新しい [issue](https://github.com/haierkeys/fast-note-sync-service/issues/new) を作成するか、Telegramの交流グループに参加して助けを求めてください: [https://t.me/obsidian_users](https://t.me/obsidian_users)\n\n中国本土地域では、Tencentの `cnb.cool` ミラーリポジトリの使用を推奨します: [https://cnb.cool/haierkeys/fast-note-sync-service](https://cnb.cool/haierkeys/fast-note-sync-service)\n\n\n<h1 align=\"center\">Fast Note Sync Service</h1>\n\n<p align=\"center\">\n    <a href=\"https://github.com/haierkeys/fast-note-sync-service/releases\"><img src=\"https://img.shields.io/github/release/haierkeys/fast-note-sync-service?style=flat-square\" alt=\"release\"></a>\n    <a href=\"https://github.com/haierkeys/fast-note-sync-service/releases\"><img src=\"https://img.shields.io/github/v/tag/haierkeys/fast-note-sync-service?label=release-alpha&style=flat-square\" alt=\"alpha-release\"></a>\n    <a href=\"https://github.com/haierkeys/fast-note-sync-service/blob/master/LICENSE\"><img src=\"https://img.shields.io/github/license/haierkeys/fast-note-sync-service?style=flat-square\" alt=\"license\"></a>\n    <img src=\"https://img.shields.io/badge/Language-Go-00ADD8?style=flat-square\" alt=\"Go\">\n</p>\n\n<p align=\"center\">\n  <strong>高性能・低遅延なノート同期、オンライン管理、リモートREST APIサービスプラットフォーム</strong>\n  <br>\n  <em>Golang + Websocket + Reactで構築</em>\n</p>\n\n<p align=\"center\">\n  データを利用するには、クライアントプラグインを併用する必要があります：<a href=\"https://github.com/haierkeys/obsidian-fast-note-sync\">Obsidian Fast Note Sync Plugin</a>\n</p>\n\n<div align=\"center\">\n  <div align=\"center\">\n    <a href=\"/docs/images/vault.png\"><img src=\"/docs/images/vault.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n    <a href=\"/docs/images/attach.png\"><img src=\"/docs/images/attach.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n    </div>\n  <div align=\"center\">\n    <a href=\"/docs/images/note.png\"><img src=\"/docs/images/note.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n    <a href=\"/docs/images/setting.png\"><img src=\"/docs/images/setting.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n  </div>\n</div>\n\n---\n\n## 🎯 コア機能\n\n* **🧰 MCP (Model Context Protocol) のネイティブサポート**：\n  * `FNS` はMCPサーバーとして `Cherry Studio` や `Cursor` などの互換性のあるAIクライアントに接続できます。これにより、AIはあなたのプライベートノートや添付ファイルの読み書き能力を持ち、すべての変更が即座に各デバイスに同期されます。\n* **🚀 REST APIのサポート**：\n  * 標準的なREST APIインターフェースを提供し、プログラム（自動化スクリプトやAIアシスタントの統合など）によるObsidianノートの作成、読み取り、更新、削除をサポートします。\n  * 詳細は [RESTful API ドキュメント](/docs/REST_API.md) または [OpenAPI ドキュメント](/docs/swagger.yaml) を参照してください。\n* **💻 Web管理パネル**：\n  * モダンな管理インターフェースを内蔵し、ユーザーの作成、プラグイン設定の生成、Vaultやノート内容の管理を簡単に行うことができます。\n* **🔄 マルチデバイスによるノート同期**：\n  * **Vault (保管庫)** の自動作成をサポート。\n  * ノート管理（追加、削除、変更、検索）をサポートし、変更内容をミリ秒レベルでオンラインの全デバイスにリアルタイム配信します。\n* **🖼️ 添付ファイル同期のサポート**：\n  * 画像などの非ノートファイルの同期を完全にサポート。\n  * 大規模な添付ファイルの分割アップロード・ダウンロードをサポートし、分割サイズの設定も可能で、同期効率を向上させます。\n* **⚙️ 設定の同期**：\n  * `.obsidian` 設定ファイルの同期をサポート。\n  * `PDF` の進行状況の同期をサポート。\n* **📝 ノートの履歴機能**：\n  * Webページの管理画面およびプラグイン側から、各ノートの歴史的な変更バージョンを確認できます。\n  * (サーバー v1.2+ が必要)\n* **🗑️ ごみ箱機能**：\n  * ノート削除後、自動的にごみ箱に移動させることができます。\n  * ごみ箱からのノート復元をサポートします。(添付ファイルの復元機能は順次追加予定)\n\n* **🚫 オフライン同期戦略**：\n  * オフラインでのノート編集の自動マージをサポート。(プラグイン側での設定が必要)\n  * オフラインでの削除に対し、再接続時に自動的に補完、あるいは削除同期を実行。(プラグイン側での設定が必要)\n\n* **🔗 共有機能**：\n  * ノートの共有の作成/取り消しが可能。\n  * 共有ノート内で参照されている画像、音声、動画などの添付ファイルを自動で解析します。\n  * 共有アクセスの統計機能を提供。\n  * 共有ノートにパスワードを設定可能。\n  * 共有ノートへの短縮URL（ショートリンク）を生成可能。\n* **📂 ディレクトリ同期**：\n  * フォルダの作成/名前変更/移動/削除の同期をサポート。\n\n* **🌳 Gitの自動化**：\n  * 添付ファイルやノートに変更があった際、自動的にリモートGitリポジトリへ更新およびプッシュを実行。\n  * タスク終了後に自動的にシステムのメモリを解放。\n\n* **☁️ マルチストレージバックアップと一方向ミラー同期**：\n  * S3/OSS/R2/WebDAV/ローカル など、複数のストレージプロトコルに対応。\n  * 全体/差分ZIPスケジュールアーカイブバックアップをサポート。\n  * Vaultリソースのリモートストレージへの一方向ミラー同期をサポート。\n  * 有効期限切れのバックアップの自動クリーンアップに対応し、保存期間のカスタマイズが可能。\n\n* **🗄️ マルチデータベース対応**：\n  * SQLite、MySQL、PostgreSQL など主流データベースをネイティブでサポートし、個人からチームまでの多様なデプロイニーズに応えます。\n\n## ☕ スポンサーとサポート\n\n- このプラグインが非常に役立つと感じ、今後も開発を継続してほしい場合は、以下の方法でサポートをご検討ください：\n\n  | Ko-fi *中国以外の地域*                                                                               |    | WeChat QRコード *中国地域*                        |\n  |--------------------------------------------------------------------------------------------------|----|------------------------------------------------|\n  | [<img src=\"/docs/images/kofi.png\" alt=\"BuyMeACoffee\" height=\"150\">](https://ko-fi.com/haierkeys) | または | <img src=\"/docs/images/wxds.png\" height=\"150\"> |\n\n  - サポートリスト：\n    - <a href=\"https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/Support.ja.md\">Support.ja.md</a>\n    - <a href=\"https://cnb.cool/haierkeys/fast-note-sync-service/-/blob/master/docs/Support.ja.md\">Support.ja.md (cnb.cool ミラー)</a>\n\n## ⏱️ 更新履歴 (Changelog)\n\n- ♨️ [更新履歴を確認する](/docs/CHANGELOG.ja.md)\n\n## 🗺️ ロードマップ (Roadmap)\n\n- [ ] 各階層を網羅する **Mock** テストの追加。\n- [ ] WebSocketの `Protobuf` 転送フォーマットのサポート追加し、同期転送効率を強化。\n- [ ] バックエンドに同期ログや操作ログなど、各種ログデータの参照機能を追加。\n- [ ] 既存の認証メカニズムの分離および最適化を行い、全体的なセキュリティの向上を図る。\n- [ ] WebGuiのノートのリアルタイム更新機能を追加。\n- [ ] クライアント間のピアツーピアメッセージ転送機能の追加（ノートおよび添付ファイル以外。localsendのような機能。クライアント側での保存はサポートせず、サーバー側のみ保存可能）。\n- [ ] 各種ヘルプドキュメントの充実。\n- [ ] より多くの内向きネットワーク接続（中継ゲートウェイ）のサポート。\n- [ ] 迅速なデプロイ計画：\n  * サーバーアドレス（パブリックネットワーク）とアカウント、パスワードを提供するだけでFNSサーバーのデプロイが完了する仕組みの構築。\n- [ ] 現在のオフラインでのノートの自動統合アルゴリズムを最適化し、競合の解決メカニズムを追加。\n\n継続的に改善を行っており、以下の将来の開発計画があります：\n\n> **改善の提案や新しいアイデアがある場合は、issueを送信して私たちと共有してください。内容を慎重に評価し、適切な提案を採用させていただきます。**\n\n## 🚀 迅速なデプロイ(Quick Deployment)\n\n複数のインストール方法が提供されています。**ワンクリックインストールスクリプト** または **Docker** の使用を推奨します。\n\n### 方法1：ワンクリックインストールスクリプト（推奨）\n\nシステム環境を自動検出し、インストールとサービスの登録を完了します。\n\n```bash\nbash <(curl -fsSL https://raw.githubusercontent.com/haierkeys/fast-note-sync-service/master/scripts/quest_install.sh)\n```\n\n中国地域の場合は、Tencentの `cnb.cool` ミラーリポジトリを使用できます。\n```bash\nbash <(curl -fsSL https://cnb.cool/haierkeys/fast-note-sync-service/-/git/raw/master/scripts/quest_install.sh) --cnb\n```\n\n\n**スクリプトの主な動作：**\n\n  * 現在のシステムに最適なReleaseバイナリファイルを自動的にダウンロードします。\n  * デフォルトで `/opt/fast-note` にインストールされ、`/usr/local/bin/fns` にグローバルなショートカットコマンド `fns` を生成します。\n  * Systemd（Linux）または Launchd（macOS）のサービスを設定・起動し、PC起動時の自動起動を実現します。\n  * **管理用コマンド**: `fns [install|uninstall|start|stop|status|update|menu]`\n  * **インタラクティブメニュー**: `fns` を直接実行することで、インタラクティブなメニュー画面を呼び出し、インストール/アップグレード、コントロール、自動起動設定、GitHub / CNBミラー間の切り替えなどをサポートします。\n\n-----\n\n### 方法2：Dockerでのデプロイ\n\n#### Docker Run\n\n```bash\n# 1. イメージのプル\ndocker pull haierkeys/fast-note-sync-service:latest\n\n# 2. コンテナの起動\ndocker run -tid --name fast-note-sync-service \\\n    -p 9000:9000 \\\n    -v /data/fast-note-sync/storage/:/fast-note-sync/storage/ \\\n    -v /data/fast-note-sync/config/:/fast-note-sync/config/ \\\n    haierkeys/fast-note-sync-service:latest\n```\n\n#### Docker Compose\n\n`docker-compose.yaml` ファイルを作成します：\n\n```yaml\nversion: '3'\nservices:\n  fast-note-sync-service:\n    image: haierkeys/fast-note-sync-service:latest\n    container_name: fast-note-sync-service\n    restart: always\n    ports:\n      - \"9000:9000\"  # RESTful API & WebSocketポート（ /api/user/sync がWebSocketインターフェースのアドレスになります）\n    volumes:\n      - ./storage:/fast-note-sync/storage  # データストレージ領域\n      - ./config:/fast-note-sync/config    # 設定ファイル領域\n```\n\nサービスを起動します：\n\n```bash\ndocker compose up -d\n```\n\n-----\n\n### 方法3：手動でのバイナリインストール\n\nご使用のシステムに対応する最新バージョンを [Releases](https://github.com/haierkeys/fast-note-sync-service/releases) からダウンロードし、解凍して以下を実行します：\n\n```bash\n./fast-note-sync-service run -c config/config.yaml\n```\n\n## 📖 ご利用ガイド\n\n1.  **管理パネルへのアクセス**：\n    ブラウザで `http://{サーバーIP}:9000` にアクセスします。\n2.  **初期設定**：\n    初回アクセス時はアカウントの登録が必要です。*(登録機能をオフにしたい場合は、設定ファイルで `user.register-is-enable: false` に設定してください)*\n3.  **クライアントの設定**：\n    管理パネルにログインし、**「API設定をコピー(Copy API Configuration)」** をクリックします。\n4.  **Obsidianへの接続**：\n    Obsidianのプラグイン設定画面を開き、先ほどコピーした設定情報を貼り付けて適用します。\n\n\n## ⚙️ 設定に関する説明\n\nデフォルトの設定ファイル「`config.yaml`」は、プログラムによって **ルートディレクトリ** または **config/** ディレクトリで自動的に検索されます。\n\n完全な設定の例を確認する: [config/config.yaml](https://github.com/haierkeys/fast-note-sync-service/blob/master/config/config.yaml)\n\n## 🌐 Nginxリバースプロキシ設定の例\n\n完全な設定の例を確認する: [https-nginx-example.conf](https://github.com/haierkeys/fast-note-sync-service/blob/master/scripts/https-nginx-example.conf)\n\n## 🧰 MCP (Model Context Protocol) サポート\n\nFNSは **MCP (Model Context Protocol)** をネイティブサポートしています。\n\nFNSをMCPサーバーとして、Cherry Studio、Cursorなどの互換性のあるAIクライアントに直接接続できます。接続後、AIはプライベートノートや添付ファイルの読み書き能力を備えます。さらに、MCPから発生したすべての変更は、WebSocketを通じてリアルタイムで各デバイス端末に同期されます。\n\n### アクセス設定 (SSEモード)\n\nFNSは **SSEプロトコル** を通じてMCPインターフェースを提供します。一般的なパラメータの要件は次のとおりです：\n- **インターフェースアドレス**: `http://<あなたのサーバーIPまたはドメイン>:<ポート>/api/mcp/sse`\n- **認証Header**: `Authorization: Bearer <あなたのAPIトークン>`（WebGUIの「API設定をコピー」機能から取得できます）\n- **オプションのHeader**: `X-Default-Vault-Name: <ノート Vault の名前>`（MCP 操作でのデフォルトのノート Vault を指定するために使用されます。ツール呼び出し時に `vault` パラメータが指定されていない場合は、これが使用されます）\n- **オプションのHeader**: `X-Client: <クライアントの種類>`（MCPへの接続に使用するクライアントの種類。例：Cherry Studio / OpenClaw）\n- **オプションのHeader**: `X-Client-Version: <クライアントのバージョン>`（MCPへの接続に使用するクライアントのバージョン。例：1.1）\n- **オプションのHeader**: `X-Client-Name: <クライアント名>`（MCPへの接続に指定されたクライアント名。例：Mac）\n\n\n\n#### 例：Cherry Studio / Cursor / Cline など\n\nご自身のMCPクライアント設定にて、以下の記述をご参考ください：\n*(注： `<ServerIP>`、`<Port>`、`<Token>`、および `<VaultName>` を各自の実際の情報に置き換えてください)*\n\n```json\n{\n  \"mcpServers\": {\n    \"fns\": {\n      \"url\": \"http://<ServerIP>:<Port>/api/mcp/sse\",\n      \"type\": \"sse\",\n      \"headers\": {\n        \"Content-Type\": \"application/json\",\n        \"Authorization\": \"Bearer <Token>\",\n        \"X-Default-Vault-Name\": \"<VaultName>\",\n        \"X-Client\": \"<Client>\",\n        \"X-Client-Version\": \"<ClientVersion>\",\n        \"X-Client-Name\": \"<ClientName>\"\n      }\n    }\n  }\n}\n```\n\n## 🔗 クライアントとクライアントプラグイン\n\n* Obsidian Fast Note Sync プラグイン\n  * [Obsidian Fast Note Sync Plugin](https://github.com/haierkeys/obsidian-fast-note-sync) / [cnb.cool ミラー](https://cnb.cool/haierkeys/obsidian-fast-note-sync)\n* サードパーティクライアント\n  * [FastNodeSync-CLI ](https://github.com/Go1c/FastNodeSync-CLI) PythonおよびFNS WS APIを利用した双方向リアルタイム同期コマンドラインクライアント。GUIを持たないLinuxサーバー（OpenClawなど）向けに特化しており、Obsidianデスクトップやモバイル版に相当する完全な同期能力を提供します。\n"
  },
  {
    "path": "docs/README.ko.md",
    "content": "[简体中文](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.zh-CN.md) / [English](https://github.com/haierkeys/fast-note-sync-service/blob/master/README.md) / [日本語](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.ja.md) / [한국어](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.ko.md) / [繁體中文](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.zh-TW.md)\n\n문제가 발생한 경우, 새로운 [issue](https://github.com/haierkeys/fast-note-sync-service/issues/new)를 생성하시거나 Telegram 커뮤니티 그룹에 가입하여 도움을 요청해 주세요: [https://t.me/obsidian_users](https://t.me/obsidian_users)\n\n중국 본토 지역의 경우, Tencent `cnb.cool`의 미러 리포지토리 사용을 권장합니다: [https://cnb.cool/haierkeys/fast-note-sync-service](https://cnb.cool/haierkeys/fast-note-sync-service)\n\n\n<h1 align=\"center\">Fast Note Sync Service</h1>\n\n<p align=\"center\">\n    <a href=\"https://github.com/haierkeys/fast-note-sync-service/releases\"><img src=\"https://img.shields.io/github/release/haierkeys/fast-note-sync-service?style=flat-square\" alt=\"release\"></a>\n    <a href=\"https://github.com/haierkeys/fast-note-sync-service/releases\"><img src=\"https://img.shields.io/github/v/tag/haierkeys/fast-note-sync-service?label=release-alpha&style=flat-square\" alt=\"alpha-release\"></a>\n    <a href=\"https://github.com/haierkeys/fast-note-sync-service/blob/master/LICENSE\"><img src=\"https://img.shields.io/github/license/haierkeys/fast-note-sync-service?style=flat-square\" alt=\"license\"></a>\n    <img src=\"https://img.shields.io/badge/Language-Go-00ADD8?style=flat-square\" alt=\"Go\">\n</p>\n\n<p align=\"center\">\n  <strong>고성능, 저지연의 노트 동기화, 온라인 관리, 원격 REST API 서비스 플랫폼</strong>\n  <br>\n  <em>Golang + Websocket + React 기반으로 구축</em>\n</p>\n\n<p align=\"center\">\n  데이터를 사용하려면 클라이언트 플러그인과 함께 사용해야 합니다: <a href=\"https://github.com/haierkeys/obsidian-fast-note-sync\">Obsidian Fast Note Sync Plugin</a>\n</p>\n\n<div align=\"center\">\n  <div align=\"center\">\n    <a href=\"/docs/images/vault.png\"><img src=\"/docs/images/vault.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n    <a href=\"/docs/images/attach.png\"><img src=\"/docs/images/attach.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n    </div>\n  <div align=\"center\">\n    <a href=\"/docs/images/note.png\"><img src=\"/docs/images/note.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n    <a href=\"/docs/images/setting.png\"><img src=\"/docs/images/setting.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n  </div>\n</div>\n\n---\n\n## 🎯 핵심 기능\n\n* **🧰 MCP (Model Context Protocol) 기본 지원**:\n  * `FNS`는 MCP 서버로서 `Cherry Studio`, `Cursor`와 같은 호환 가능한 AI 클라이언트에 연결할 수 있습니다. 이를 통해 AI가 사용자 개인 노트 및 첨부 파일을 읽고 쓸 수 있는 기능을 갖게 되며, 모든 변경 사항은 실시간으로 각 클라이언트 디바이스와 동기화됩니다.\n* **🚀 REST API 지원**:\n  * 표준 REST API 인터페이스를 제공하며, 자동화 스크립트나 AI 어시스턴트 통합 같은 프로그래밍 방식으로 Obsidian 노트를 생성, 읽기, 수정 및 삭제(CRUD)할 수 있도록 지원합니다.\n  * 자세한 내용은 [RESTful API 문서](/docs/REST_API.md) 또는 [OpenAPI 문서](/docs/swagger.yaml)를 참조해 주세요.\n* **💻 웹 관리 패널**:\n  * 최신 관리 인터페이스가 내장되어 있어, 사용자 생성, 플러그인 구성 생성, Vault(저장소) 및 노트 내용을 쉽게 관리할 수 있습니다.\n* **🔄 다중 장치 노트 동기화**:\n  * **Vault (저장소)** 자동 생성을 지원합니다.\n  * 노트 관리(추가, 삭제, 수정, 조회)를 지원하며, 변경 사항은 밀리초 단위로 온라인 상태의 모든 장치에 실시간 분산 전송됩니다.\n* **🖼️ 첨부 파일 동기화 지원**:\n  * 이미지 등 노트 외 파일의 동기화를 완벽하게 지원합니다.\n  * 큰 첨부 파일의 분할 업로드/다운로드를 지원하며 분할 크기도 설정할 수 있으므로, 동기화 효율을 향상시켰습니다.\n* **⚙️ 설정 동기화**:\n  * `.obsidian` 설정 파일의 동기화를 지원합니다.\n  * `PDF` 진행 상태 동기화를 지원합니다.\n* **📝 노트 내역 기능**:\n  * 웹 관리 화면과 클라이언트 플러그인 애플리케이션 내에서 각 노트의 과거 수정 버전을 확인할 수 있습니다.\n  * (서버 v1.2+ 버전 필요)\n* **🗑️ 휴지통 기능**:\n  * 노트를 삭제한 후 이가 휴지통에 자동으로 들어가는 것을 지원합니다.\n  * 휴지통에서 노트를 복구하는 기능을 지원합니다. (첨부 파일의 복구 기능도 지속적으로 추가될 예정입니다)\n\n* **🚫 오프라인 동기화 모드 전략**:\n  * 오프라인 상태에서 노트 편집에 대해 자동으로 병합하는 것을 지원합니다. (플러그인 옵션 설정이 필요합니다)\n  * 오프라인에서 항목을 삭제한 경우, 재연결 후 서버측에서 자동으로 삭제된 노트를 보완하거나 삭제된 로컬과 동기화합니다. (플러그인 설정 필요)\n\n* **🔗 공유 기능**:\n  * 노트의 '공유'에 대한 생성 및 취소가 가능합니다.\n  * 공유 노트에 삽입된 이미지, 오디오, 비디오 등 첨부 파일을 자동으로 분석합니다.\n  * 공유 노트 접속 통계 기능을 제공합니다.\n  * 공유 노트에 접속 암호를 설정할 수 있습니다.\n  * 공유된 노트에 대한 짧은 링크 생성을 지원합니다.\n* **📂 디렉토리(폴더) 동기화**:\n  * 폴더의 생성, 이름 변경, 이동, 삭제 항목의 동기화를 지원합니다.\n\n* **🌳 Git 자동화**:\n  * 노트 및 파일에 변경이 있을 때 자동으로 Git 원격 저장소로 내역을 업데이트하고 푸시 작업을 수행합니다.\n  * 모든 동기화 작업이 종료된 후, 메모리를 반납하여 시스템 활용성을 높입니다.\n\n* **☁️ 다중 스토리지 백업 및 단방향 미러 동기화**:\n  * S3, OSS, R2, WebDAV, 로컬 볼륨 등의 다양한 스토리지 프로토콜과 호환됩니다.\n  * 전체 / 증분 ZIP 예약 보관 데이터 백업을 수행할 수 있습니다.\n  * Vault 리소스를 원격 스토리지에 대해 단방향 미러 동기화가 가능합니다.\n  * 기간이 만료된 백업을 자동으로 정리하고, 사용자가 필요한 보존 날짜(유지 보관일수) 지정을 지원합니다.\n\n* **🗄️ 다중 데이터베이스 지원**:\n  * SQLite, MySQL, PostgreSQL 등 주류 데이터베이스를 네이티브로 지원하여, 개인 단위부터 팀 협업 모델까지 필요한 배포 요구를 수용합니다.\n\n## ☕ 후원 및 지원\n\n- 이 플러그인이 유용하며 개발이 지속되기를 원하신다면 다음 채널을 통해 후원 부탁드립니다:\n\n  | Ko-fi *중국 이외 지역*                                                                               |    | WeChat QR 기부 *중국 지역*                        |\n  |--------------------------------------------------------------------------------------------------|----|------------------------------------------------|\n  | [<img src=\"/docs/images/kofi.png\" alt=\"BuyMeACoffee\" height=\"150\">](https://ko-fi.com/haierkeys) | 또는 | <img src=\"/docs/images/wxds.png\" height=\"150\"> |\n\n  - 스폰서 목록:\n    - <a href=\"https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/Support.ko.md\">Support.ko.md</a>\n    - <a href=\"https://cnb.cool/haierkeys/fast-note-sync-service/-/blob/master/docs/Support.ko.md\">Support.ko.md (cnb.cool 미러)</a>\n\n## ⏱️ 업데이트 로그 (Changelog)\n\n- ♨️ [업데이트 로그 확인하기](/docs/CHANGELOG.ko.md)\n\n## 🗺️ 로드맵 (Roadmap)\n\n- [ ] 각 레이어에 대대적인 **Mock** 테스트 추가.\n- [ ] WebSocket 계층에서 `Protobuf` 통신 프로토콜을 구현하여 동기화 전송 효율을 향상.\n- [ ] 백엔드 단에서 동기화 로그 및 동작 로그 등, 시스템 및 유저 감사/운영 기록에 대해 조회가 가능한 기능 생성.\n- [ ] 전체 보안을 최상위 상태로 끌어올리도록, 현행 인증 메커니즘을 격리하고 최적화를 달성.\n- [ ] WebGui에서도 노트 업데이트 내역을 실시간으로 반영 가능.\n- [ ] 클라이언트 간 P2P (Peer-to-Peer) 메시지 전송 기능 추가 (노트 & 첨부파일이 아님, localsend 방식. 클라이언트 간의 저장 없이 서버에 기록 보전됨)\n- [ ] 각 분야의 도움말 / 사용 설명서 개선.\n- [ ] 인트라넷 환경에서의 우회 액세스 기능 강화 (포트 포워딩 릴레이 지원 등 포함).\n- [ ] 빠른 시스템 구축 및 배포 계획:\n  * 인프라 서버의 IP(공용 IP)와 계정 패스워드만을 공급하면 단숨에 FNS 서버를 런칭하고 운용.\n- [ ] 현존하는 오프라인 노트 병합 전략을 대폭 개선하고 충돌 처리 알고리즘 추가 반영.\n\n우리는 지속적으로 개선 작업을 진행 중이며, 앞으로의 개발 계획은 다음과 같습니다:\n\n> **개선 아이디어나 새로운 제안이 있을 때 Issue를 남겨서 우리에게 의견을 남겨주시면 감사드리겠습니다. 우리는 제출받은 각 항목에 대해 신중히 평가하여 적합한 의견을 도용할 것입니다.**\n\n## 🚀 빠른 배포 가이드\n\n우리는 사용자의 설치 편의성을 고려하여 다양한 옵션을 수록하였습니다.가장 권장하는 방식은 **원클릭 스크립트**와 **Docker** 설치 방식입니다.\n\n### 첫 번째: 원클릭 스크립트 (권장)\n\n시스템 환경 요소의 사양을 자체적으로 검색하여 필수 프로그램을 설치 및 최적의 서비스 등록을 구성합니다.\n\n```bash\nbash <(curl -fsSL https://raw.githubusercontent.com/haierkeys/fast-note-sync-service/master/scripts/quest_install.sh)\n```\n\n중국 대륙 내부에서는 Tencent `cnb.cool`의 미러 다운로드 권한을 사용하십시오:\n```bash\nbash <(curl -fsSL https://cnb.cool/haierkeys/fast-note-sync-service/-/git/raw/master/scripts/quest_install.sh) --cnb\n```\n\n\n**스크립트 동작 가이드 프로세스:**\n\n  * 가장 최신의 설치 환경 내장 Release 바이너리 응용프로그램을 다운로드합니다.\n  * 기본 디폴트 경로(/opt/fast-note)로 파일들이 압축 해제되며, 시스템 환경변수에 단축 명령인 `fns`(/usr/local/bin/fns)를 저장합니다.\n  * Systemd (Linux 기반) 및 Launchd (macOS 환경) 등에 알맞게 서비스가 환경에 저장되어 부수적인 절차 없이 운영 체제가 런칭할 때 함께 자사 프로그램이 동작합니다.\n  * **전역 애플리케이션 명령어**: `fns [install|uninstall|start|stop|status|update|menu]`\n  * **대화식 콘솔창 지원 기능**: 구동 중인 앱에다 추가 파라미터가 배제된 `fns` 명령을 입력하게 되면 대화형의 화면 기반 콘솔이 뜹니다. 설치, 업데이트/앱 제어/자동 실행 컨테이너 적용 외 GitHub 및 CNB 클라우드 리포지토리 미러 간 변경이 손쉽게 작동 가능합니다.\n\n-----\n\n### 두 번째: Docker 배포 구성방식\n\n#### Docker Run\n\n```bash\n# 1. 컨테이너 이미지 불러오기\ndocker pull haierkeys/fast-note-sync-service:latest\n\n# 2. 이미지 컨테이너 기동시키기\ndocker run -tid --name fast-note-sync-service \\\n    -p 9000:9000 \\\n    -v /data/fast-note-sync/storage/:/fast-note-sync/storage/ \\\n    -v /data/fast-note-sync/config/:/fast-note-sync/config/ \\\n    haierkeys/fast-note-sync-service:latest\n```\n\n#### Docker Compose\n\n로컬 컴퓨터에 `docker-compose.yaml` 파일을 하나 구축합니다：\n\n```yaml\nversion: '3'\nservices:\n  fast-note-sync-service:\n    image: haierkeys/fast-note-sync-service:latest\n    container_name: fast-note-sync-service\n    restart: always\n    ports:\n      - \"9000:9000\"  # RESTful API & WebSocket 통신 포트 / 내부 설정상 /api/user/sync를 웹소켓 파이프라인으로 운용합니다\n    volumes:\n      - ./storage:/fast-note-sync/storage  # 데이터/볼륨 스토리지 연동영역\n      - ./config:/fast-note-sync/config    # 클라우드/컨테이너 시스템을 위한 Config 마운트\n```\n\n서비스 작동:\n\n```bash\ndocker compose up -d\n```\n\n-----\n\n### 세 번째: 수동 바이너리 설치\n\n본인의 운영 체제 버전과 일치하는 팩을 [Releases](https://github.com/haierkeys/fast-note-sync-service/releases)에서 다운로드 받은 후 적절한 디렉토리에서 스크립트를 기동하십시오:\n\n```bash\n./fast-note-sync-service run -c config/config.yaml\n```\n\n## 📖 사용 설명서\n\n1.  **관리자 화면 접속**:\n    각자의 브라우저 주소창에 `http://{운용 중인 서버 IP 주소}:9000` 로컬 네트워크 연결.\n2.  **프로젝트 가동 세팅 구성**:\n    최초 접속 시 계정 생성이 필수로 적용됩니다. *(혹시 외부 가입 통제를 기획 중인 관리자는 서버 Config 파일에 `user.register-is-enable: false` 입력 권장)*\n3.  **클라이언트와의 API 조인 연동법**:\n    서버 관리 콘솔/로그인 후 우측의 **“API 복사 (Copy API Configuration)”** 버튼 누름.\n4.  **Obsidian 옵션 조립 및 세팅**:\n    Obsidian 플러그인 설정 창의 셋업 페이지로 향한 후 방금 복사해 둔 환경 데이터를 그대로 붙여 넣기.\n\n\n## ⚙️ 설정 파일 정보\n\n서버 엔진의 기본 파일 양식 설정 명칭은 « `config.yaml` »이며, 이 프로그램은 환경 디렉터리를 가동 중인 **메인 Root 공간**이거나 자체 **config/**라는 환경 설정 디렉터리 내에서 자동 스캐닝하게 됩니다.\n\nConfiguration 샘플 코드를 점검 및 학습하십시오: [config/config.yaml](https://github.com/haierkeys/fast-note-sync-service/blob/master/config/config.yaml)\n\n## 🌐 Nginx 리버스 프록시 적용 예제\n\n리버스 프록시(HTTPS 접속 용도) 전체 적용 사례 열람 안내: [https-nginx-example.conf](https://github.com/haierkeys/fast-note-sync-service/blob/master/scripts/https-nginx-example.conf)\n\n## 🧰 MCP (Model Context Protocol) 지원\n\nFNS는 현재 **MCP (Model Context Protocol)**를 기본적으로 지원합니다.\n\nFNS를 MCP 서버로 설치하여 Cherry Studio, Cursor 등과 같이 호환되는 AI 클라이언트에 직접 접속하게 연동시킬 수 있습니다. 연동 시점 직후, AI 개체 및 서비스 엔진은 사유 데이터(개인 비메모 및 관련 어태치먼트)의 읽기, 편집 역할을 갖게 됩니다. 더불어 MCP로부터 송부되는 일련의 작업 결과들은 웹 소켓을 경유해 사용 중인 기타의 모든 장치 환경에 실시간 주입 및 저장 단계를 밟습니다.\n\n### 접속 구성 안내 (SSE 모드)\n\nFNS 엔진은 다용도 서버로서 MCP 호출 및 기능을 원격지의 사용자에게 제공할수 있게 **SSE 프로토콜**을 할당합니다. 접속 통신의 요구 파라미터는 다음과 같이 지정되었습니다:\n- **인터페이스 주소(URL)**: `http://<호스트 서버의 IP 주소 및 도메인 주소명>:<포트>/api/mcp/sse`\n- **강화 인증 (Header)**: `Authorization: Bearer <WebGUI 패널 내부 API 복사 영역에서 배급받은 각자의 API 토큰>`\n- **추가 옵션 (Header)**: `X-Default-Vault-Name: <선택한 노트 Vault 명칭>` (MCP 동작 시의 기본 타깃 Vault 구성 목적 / 호출 시점에서 `vault` 변수가 공백일 케이스에 이 구성값이 대체적용)\n- **추가 옵션 (Header)**: `X-Client: <클라이언트 종류>` (MCP 연결에 활용하는 구체적인 클라이언트 유형. 예: Cherry Studio / OpenClaw)\n- **추가 옵션 (Header)**: `X-Client-Version: <클라이언트 버전>` (MCP에 연결되는 클라이언트 서비스 종류의 버전값. 예: 1.1)\n- **추가 옵션 (Header)**: `X-Client-Name: <클라이언트 이름>` (서버 연결을 기동시키는 플랫폼/클라이언트의 운영 명칭. 예: Mac)\n\n\n\n#### 예시: Cherry Studio / Cursor / Cline 등 세팅 가이드\n\n운용하시려는 MCP 도메인 서비스 측의 애플리케이션 접속 클라이언트 측에 하단의 설정 코드를 병기 및 수정 후 추가 기입할 것을 권장합니다:\n*(안내: 파라미터로 지정된 코드 베이스 내의 `<ServerIP>`, `<Port>`, `<Token>`, 그리고 `<VaultName>`의 위치에 이용자 각자의 적합도 정보를 적으십시오)*\n\n```json\n{\n  \"mcpServers\": {\n    \"fns\": {\n      \"url\": \"http://<ServerIP>:<Port>/api/mcp/sse\",\n      \"type\": \"sse\",\n      \"headers\": {\n        \"Content-Type\": \"application/json\",\n        \"Authorization\": \"Bearer <Token>\",\n        \"X-Default-Vault-Name\": \"<VaultName>\",\n        \"X-Client\": \"<Client>\",\n        \"X-Client-Version\": \"<ClientVersion>\",\n        \"X-Client-Name\": \"<ClientName>\"\n      }\n    }\n  }\n}\n```\n\n## 🔗 클라이언트 및 클라이언트 플러그인 지원 목록\n\n* Obsidian Fast Note Sync 플러그인\n  * [Obsidian Fast Note Sync Plugin](https://github.com/haierkeys/obsidian-fast-note-sync) / [cnb.cool 미러 저장소](https://cnb.cool/haierkeys/obsidian-fast-note-sync)\n* 서드 파티 연동 플러그인\n  * [FastNodeSync-CLI ](https://github.com/Go1c/FastNodeSync-CLI) 파이썬(Python)의 기능과 백엔드 FNS WS 인터페이스 기술에 결합되어 제작된, 쌍방향 실시간 동기화를 달성한 CLI(명령 프롬프트 베이스)의 클라이언트 운영 체제 모듈입니다. 이는 디스플레이나 구체적 인터페이스 화면(GUI 환경)을 미처 지원하지 못해 구동 환경이 제한되는 서버 콘솔 환경(OpenClaw) 내에서 데스크톱 기반 Obsidian 동기화 능력을 확보하는데 최적화되었습니다.\n"
  },
  {
    "path": "docs/README.zh-CN.md",
    "content": "[简体中文](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.zh-CN.md) / [English](https://github.com/haierkeys/fast-note-sync-service/blob/master/README.md) / [日本語](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.ja.md) / [한국어](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.ko.md) / [繁體中文](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.zh-TW.md)\n\n有问题请新建 [issue](https://github.com/haierkeys/fast-note-sync-service/issues/new) , 或加入电报交流群寻求帮助: [https://t.me/obsidian_users](https://t.me/obsidian_users)\n\n中国大陆地区，推荐使用腾讯 `cnb.cool` 镜像库: [https://cnb.cool/haierkeys/fast-note-sync-service](https://cnb.cool/haierkeys/fast-note-sync-service)\n\n\n<h1 align=\"center\">Fast Note Sync Service</h1>\n\n<p align=\"center\">\n    <a href=\"https://github.com/haierkeys/fast-note-sync-service/releases\"><img src=\"https://img.shields.io/github/release/haierkeys/fast-note-sync-service?style=flat-square\" alt=\"release\"></a>\n    <a href=\"https://github.com/haierkeys/fast-note-sync-service/releases\"><img src=\"https://img.shields.io/github/v/tag/haierkeys/fast-note-sync-service?label=release-alpha&style=flat-square\" alt=\"alpha-release\"></a>\n    <a href=\"https://github.com/haierkeys/fast-note-sync-service/blob/master/LICENSE\"><img src=\"https://img.shields.io/github/license/haierkeys/fast-note-sync-service?style=flat-square\" alt=\"license\"></a>\n    <img src=\"https://img.shields.io/badge/Language-Go-00ADD8?style=flat-square\" alt=\"Go\">\n</p>\n\n<p align=\"center\">\n  <strong>高性能、低延迟的笔记同步, 在线管理, 远端 REST API 服务平台</strong>\n  <br>\n  <em>基于 Golang + Websocket + React 构建</em>\n</p>\n\n<p align=\"center\">\n  数据提供需配合客户端插件使用：<a href=\"https://github.com/haierkeys/obsidian-fast-note-sync\">Obsidian Fast Note Sync Plugin</a>\n</p>\n\n<div align=\"center\">\n  <div align=\"center\">\n    <a href=\"/docs/images/vault.png\"><img src=\"/docs/images/vault.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n    <a href=\"/docs/images/attach.png\"><img src=\"/docs/images/attach.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n    </div>\n  <div align=\"center\">\n    <a href=\"/docs/images/note.png\"><img src=\"/docs/images/note.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n    <a href=\"/docs/images/setting.png\"><img src=\"/docs/images/setting.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n  </div>\n</div>\n\n---\n\n## 🎯 核心功能\n\n* **🧰 MCP (Model Context Protocol) 原生支持**：\n  * `FNS` 可以作为 MCP 服务端接入 `Cherry Studio`、`Cursor` 等兼容的 AI 客户端，即可让 AI 具备读写私人笔记与附件的能力，且所有变更实时同步到各端。\n* **🚀 REST API 支持**：\n  * 提供标准的 REST API 接口，支持通过编程方式（如自动化脚本、AI 助手集成）对 Obsidian 笔记进行增删改查。\n  * 详情请参阅 [RESTful API 文档](/docs/REST_API.md) 或 [OpenAPI 文档](/docs/swagger.yaml)。\n* **💻 Web 管理面板**：\n  * 内置现代化管理界面，轻松创建用户、生成插件配置、管理仓库及笔记内容。\n* **🔄 多端笔记同步**：\n  * 支持 **Vault (仓库)** 自动创建。\n  * 支持笔记管理（增、删、改、查），变更毫秒级实时分发至所有在线设备。\n* **🖼️ 附件同步支持**：\n  * 完美支持图片等非笔记文件同步。\n  * 支持大附件 分片上传下载，分片大小可配置，提升同步效率。\n* **⚙️ 配置同步**：\n  * 支持 `.obsidian` 配置文件的同步。\n  * 支持 `PDF` 进度状态同步。\n* **📝 笔记历史**：\n  * 可以在 Web 页面，插件端查看每一个笔记的 历史修改版本。\n  * (需服务端 v1.2+ )\n* **🗑️ 回收站**：\n  * 支持笔记删除后，自动进入回收站。\n  * 支持从回收站恢复笔记。(后续会陆续新增附件恢复功能)\n\n* **🚫 离线同步策略**：\n  * 支持笔记离线编辑自动合并。(需要插件端设置)\n  * 离线删除，重连之后自动补全或删除同步。(需要插件端设置)\n\n* **🔗 分享功能**：\n  * 可以 创建/取消 笔记分享。\n  * 自动解析分享笔记中引用的图片、音视频等附件。\n  * 提供分享访问统计功能。\n  * 可以设置分享笔记的访问密码。\n  * 可以对分享笔记生成短链接。\n* **📂 目录同步**：\n  * 支持文件夹的 创建/重命名/移动/删除 同步。\n\n* **🌳 Git 自动化**：\n  * 当附件和笔记发生变更时，自动更新并推送至远程 Git 仓库。\n  * 任务结束后自动释放系统内存。\n\n* **☁️ 多存储备份与单向镜像同步**：\n  * 适配 S3/OSS/R2/WebDAV/本地 等多种存储协议。\n  * 支持全量/增量 ZIP 定时归档备份。\n  * 支持 Vault 资源单向镜像同步至远程存储。\n  * 自动清理过期备份，支持自定义保留天数。\n\n* **🗄️ 多数据库支持**：\n  * 原生支持 SQLite、MySQL、PostgreSQL 等多种主流数据库，满足从个人到团队的不同部署需求。\n\n## ☕ 赞助与支持\n\n- 如果觉得这个插件很有用，并且想要它继续开发，请在以下方式支持我:\n\n  | Ko-fi *非中国地区*                                                                               |    | 微信扫码打赏 *中国地区*                        |\n  |--------------------------------------------------------------------------------------------------|----|------------------------------------------------|\n  | [<img src=\"/docs/images/kofi.png\" alt=\"BuyMeACoffee\" height=\"150\">](https://ko-fi.com/haierkeys) | 或 | <img src=\"/docs/images/wxds.png\" height=\"150\"> |\n\n  - 已支持名单：\n    - <a href=\"https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/Support.zh-CN.md\">Support.zh-CN.md</a>\n    - <a href=\"https://cnb.cool/haierkeys/fast-note-sync-service/-/blob/master/docs/Support.zh-CN.md\">Support.zh-CN.md (cnb.cool 镜像库)</a>\n\n## ⏱️ 更新日志\n\n- ♨️ [访问查看更新日志](/docs/CHANGELOG.zh-CN.md)\n\n## 🗺️ 路线图 (Roadmap)\n\n- [ ] 增加 **Mock**测试, 覆盖到 各层级.\n- [ ] 增加 WebSocket `Protobuf` 传输格式的支持, 强化同步传输效率.\n- [ ] 后端增加 同步日志 & 操作日志 等各类操作日志的查询.\n- [ ] 对现有授权机制进行隔离以及优化,  提升整体安全性.\n- [ ] 增加 WebGui 笔记实时更新\n- [ ] 增加客户端 点对点 消息传送(非笔记&附件,类似localsend功能,不支持客户端保存, 可保存到服务端)\n- [ ] 各类帮助文档完善\n- [ ] 更多的内网穿透(中继网关)的支持\n- [ ] 快速部署计划\n  * 只需要提供服务器地址(公网), 账号密码 即可完成 FNS 服务端的部署\n- [ ] 优化现有的离线笔记合并方案, 增加冲突处理机制\n\n我们正在持续改进，以下是未来的开发计划：\n\n> **如果您有改进建议或新想法，欢迎通过提交 issue 与我们分享——我们会认真评估并采纳合适的建议。**\n\n## 🚀 快速部署\n\n我们提供多种安装方式，推荐使用 **一键脚本** 或 **Docker**。\n\n### 方式一：一键脚本（推荐）\n\n自动检测系统环境并完成安装、服务注册。\n\n```bash\nbash <(curl -fsSL https://raw.githubusercontent.com/haierkeys/fast-note-sync-service/master/scripts/quest_install.sh)\n```\n\n中国地区可以使用腾讯 `cnb.cool` 镜像源\n```bash\nbash <(curl -fsSL https://cnb.cool/haierkeys/fast-note-sync-service/-/git/raw/master/scripts/quest_install.sh) --cnb\n```\n\n\n**脚本主要行为：**\n\n  * 自动下载适配当前系统的 Release 二进制文件。\n  * 默认安装至 `/opt/fast-note`，并在 `/usr/local/bin/fns` 创建全局快捷命令 `fns`。\n  * 配置并启动 Systemd（Linux）或 Launchd（macOS）服务，实现开机自启。\n  * **管理命令**：`fns [install|uninstall|start|stop|status|update|menu]`\n  * **交互菜单**：直接运行 `fns` 可进入交互菜单，支持安装/升级、服务控制、开机自启配置，以及在 GitHub / CNB 镜像之间切换。\n\n-----\n\n### 方式二：Docker 部署\n\n#### Docker Run\n\n```bash\n# 1. 拉取镜像\ndocker pull haierkeys/fast-note-sync-service:latest\n\n# 2. 启动容器\ndocker run -tid --name fast-note-sync-service \\\n    -p 9000:9000 \\\n    -v /data/fast-note-sync/storage/:/fast-note-sync/storage/ \\\n    -v /data/fast-note-sync/config/:/fast-note-sync/config/ \\\n    haierkeys/fast-note-sync-service:latest\n```\n\n#### Docker Compose\n\n创建 `docker-compose.yaml` 文件：\n\n```yaml\nversion: '3'\nservices:\n  fast-note-sync-service:\n    image: haierkeys/fast-note-sync-service:latest\n    container_name: fast-note-sync-service\n    restart: always\n    ports:\n      - \"9000:9000\"  # RESTful API & WebSocket 端口 其中 /api/user/sync 为 WebSocket 接口地址\n    volumes:\n      - ./storage:/fast-note-sync/storage  # 数据存储\n      - ./config:/fast-note-sync/config    # 配置文件\n```\n\n启动服务：\n\n```bash\ndocker compose up -d\n```\n\n-----\n\n### 方式三：手动二进制安装\n\n从 [Releases](https://github.com/haierkeys/fast-note-sync-service/releases) 下载对应系统的最新版本，解压后运行：\n\n```bash\n./fast-note-sync-service run -c config/config.yaml\n```\n\n## 📖 使用指南\n\n1.  **访问管理面板**：\n    在浏览器打开 `http://{服务器IP}:9000`。\n2.  **初始化设置**：\n    首次访问需注册账号。*(如需关闭注册功能，请在配置文件中设置 `user.register-is-enable: false`)*\n3.  **配置客户端**：\n    登录管理面板，点击 **“复制 API 配置”**。\n4.  **连接 Obsidian**：\n    打开 Obsidian 插件设置页面，粘贴刚才复制的配置信息即可。\n\n\n## ⚙️ 配置说明\n\n默认配置文件为 `config.yaml`，程序会自动在 **根目录** 或 **config/** 目录下查找。\n\n查看完整配置示例：[config/config.yaml](https://github.com/haierkeys/fast-note-sync-service/blob/master/config/config.yaml)\n\n## 🌐 Nginx 反代配置示例\n\n查看完整配置示例：[https-nginx-example.conf](https://github.com/haierkeys/fast-note-sync-service/blob/master/scripts/https-nginx-example.conf)\n\n## 🧰 MCP (模型上下文协议) 支持\n\nFNS 现已原生支持 **MCP (Model Context Protocol)**。\n\n您可以将 FNS 作为 MCP 服务端直接接入 Cherry Studio、Cursor 等兼容的 AI 客户端。接入后，AI 即可具备读写私人笔记和附件的能力。同时，所有由 MCP 产生的修改，都会通过 WebSocket 实时同步到您的各个设备终端。\n\n### 接入配置 (SSE 模式)\n\nFNS 通过 **SSE 协议**提供 MCP 接口，通用参数要求如下：\n- **接口地址**：`http://<您的服务器IP或域名>:<端口>/api/mcp/sse`\n- **鉴权 Header**：`Authorization: Bearer <您的 API Token>`（在 WebGUI 的复制 API 配置中获取）\n- **可选 Header**：`X-Default-Vault-Name: <笔记库名称>`（用于指定 MCP 操作的默认笔记库，若工具调用时未指定 `vault` 参数，则使用此值）\n- **可选 Header**：`X-Client: <客户端类型>`（用于连接MCP的客户端类型，如：Cherry Studio / OpenClaw）\n- **可选 Header**：`X-Client-Version: <客户端类型版本>`（用于连接MCP的客户端类型版本，如：1.1）\n- **可选 Header**：`X-Client-Name: <客户端名称>`（用于连接MCP的客户端名称，如： Mac）\n\n\n\n#### 示例：Cherry Studio / Cursor / Cline 等\n\n请在您的 MCP 客户端配置中参考如下配置：\n*(注：请将 `<ServerIP>`、`<Port>`、`<Token>` 和 `<VaultName>` 替换为您自己的实际信息)*\n\n```json\n{\n  \"mcpServers\": {\n    \"fns\": {\n      \"url\": \"http://<ServerIP>:<Port>/api/mcp/sse\",\n      \"type\": \"sse\",\n      \"headers\": {\n        \"Content-Type\": \"application/json\",\n        \"Authorization\": \"Bearer <Token>\",\n        \"X-Default-Vault-Name\": \"<VaultName>\",\n        \"X-Client\": \"<Client>\",\n        \"X-Client-Version\": \"<ClientVersion>\",\n        \"X-Client-Name\": \"<ClientName>\"\n      }\n    }\n  }\n}\n```\n\n## 🔗 客户端 & 客户端插件\n\n* Obsidian Fast Note Sync 插件\n  * [Obsidian Fast Note Sync Plugin](https://github.com/haierkeys/obsidian-fast-note-sync) / [cnb.cool 镜像库](https://cnb.cool/haierkeys/obsidian-fast-note-sync)\n* 三方客户端\n  * [FastNodeSync-CLI ](https://github.com/Go1c/FastNodeSync-CLI) 基于 Python 和 FNS WS接口实现的双向实时同步的命令行客户端, 适用于无 GUI 的 Linux 服务器环境（如 OpenClaw），实现与 Obsidian 桌面/移动端等价的同步能力。\n"
  },
  {
    "path": "docs/README.zh-TW.md",
    "content": "[简体中文](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.zh-CN.md) / [English](https://github.com/haierkeys/fast-note-sync-service/blob/master/README.md) / [日本語](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.ja.md) / [한국어](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.ko.md) / [繁體中文](https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/README.zh-TW.md)\n\n有問題請新建 [issue](https://github.com/haierkeys/fast-note-sync-service/issues/new) , 或加入電報交流群尋求幫助: [https://t.me/obsidian_users](https://t.me/obsidian_users)\n\n中國大陸地區，推薦使用騰訊 `cnb.cool` 鏡像庫: [https://cnb.cool/haierkeys/fast-note-sync-service](https://cnb.cool/haierkeys/fast-note-sync-service)\n\n\n<h1 align=\"center\">Fast Note Sync Service</h1>\n\n<p align=\"center\">\n    <a href=\"https://github.com/haierkeys/fast-note-sync-service/releases\"><img src=\"https://img.shields.io/github/release/haierkeys/fast-note-sync-service?style=flat-square\" alt=\"release\"></a>\n    <a href=\"https://github.com/haierkeys/fast-note-sync-service/releases\"><img src=\"https://img.shields.io/github/v/tag/haierkeys/fast-note-sync-service?label=release-alpha&style=flat-square\" alt=\"alpha-release\"></a>\n    <a href=\"https://github.com/haierkeys/fast-note-sync-service/blob/master/LICENSE\"><img src=\"https://img.shields.io/github/license/haierkeys/fast-note-sync-service?style=flat-square\" alt=\"license\"></a>\n    <img src=\"https://img.shields.io/badge/Language-Go-00ADD8?style=flat-square\" alt=\"Go\">\n</p>\n\n<p align=\"center\">\n  <strong>高效能、低延遲的筆記同步，線上管理，遠端 REST API 服務平台</strong>\n  <br>\n  <em>基於 Golang + Websocket + React 建構</em>\n</p>\n\n<p align=\"center\">\n  資料提供需配合用戶端外掛程式使用：<a href=\"https://github.com/haierkeys/obsidian-fast-note-sync\">Obsidian Fast Note Sync Plugin</a>\n</p>\n\n<div align=\"center\">\n  <div align=\"center\">\n    <a href=\"/docs/images/vault.png\"><img src=\"/docs/images/vault.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n    <a href=\"/docs/images/attach.png\"><img src=\"/docs/images/attach.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n    </div>\n  <div align=\"center\">\n    <a href=\"/docs/images/note.png\"><img src=\"/docs/images/note.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n    <a href=\"/docs/images/setting.png\"><img src=\"/docs/images/setting.png\" alt=\"fast-note-sync-service-preview\" width=\"400\" /></a>\n  </div>\n</div>\n\n---\n\n## 🎯 核心功能\n\n* **🧰 MCP (Model Context Protocol) 原生支援**：\n  * `FNS` 可以作為 MCP 伺服器端接入 `Cherry Studio`、`Cursor` 等相容的 AI 用戶端，即可讓 AI 具備讀寫私人筆記與附件的能力，且所有變更會即時同步到各端。\n* **🚀 REST API 支援**：\n  * 提供標準的 REST API 介面，支援透過程式語言方式（如自動化腳本、AI 助手整合）對 Obsidian 筆記進行增刪改查。\n  * 詳情請參閱 [RESTful API 文件](/docs/REST_API.md) 或 [OpenAPI 文件](/docs/swagger.yaml)。\n* **💻 Web 管理面板**：\n  * 內建現代化管理介面，輕鬆建立使用者、產生外掛程式設定、管理倉庫及筆記內容。\n* **🔄 多端筆記同步**：\n  * 支援 **Vault (筆記庫)** 自動建立。\n  * 支援筆記管理（增、刪、改、查），變更毫秒級即時分發至所有線上設備。\n* **🖼️ 附件同步支援**：\n  * 完美支援圖片等非筆記檔案同步。\n  * 支援大附件 分區塊上傳下載，區塊大小可設定，提升同步效率。\n* **⚙️ 設定同步**：\n  * 支援 `.obsidian` 設定檔的同步。\n  * 支援 `PDF` 閱讀進度狀態同步。\n* **📝 筆記歷史**：\n  * 可以在 Web 頁面，外掛程式端查看每一個筆記的 歷史修改版本。\n  * (需伺服器端 v1.2+ 支援)\n* **🗑️ 資源回收筒**：\n  * 支援筆記刪除後，自動進入資源回收筒。\n  * 支援從資源回收筒復原筆記。(後續會陸續新增附件復原功能)\n\n* **🚫 離線同步策略**：\n  * 支援筆記離線編輯自動合併。(需要外掛程式端設定)\n  * 離線刪除，重連之後自動補全或刪除同步。(需要外掛程式端設定)\n\n* **🔗 分享功能**：\n  * 可以 建立/取消 筆記分享。\n  * 自動解析分享筆記中引用的圖片、音訊與視訊等附件。\n  * 提供分享存取統計功能。\n  * 可以設定分享筆記的存取密碼。\n  * 可以對分享筆記產生短連結。\n* **📂 目錄同步**：\n  * 支援資料夾的 建立/重新命名/移動/刪除 同步。\n\n* **🌳 Git 自動化**：\n  * 當附件和筆記發生變更時，自動更新並推播至遠端 Git 倉庫。\n  * 任務結束後自動釋放系統記憶體。\n\n* **☁️ 多儲存備份與單向鏡像同步**：\n  * 適配 S3/OSS/R2/WebDAV/本地端 等多種儲存協定。\n  * 支援全量/增量 ZIP 定時封存備份。\n  * 支援 Vault 資源單向鏡像同步至遠端儲存。\n  * 自動清理過期備份，支援自訂保留天數。\n\n* **🗄️ 多資料庫支援**：\n  * 原生支援 SQLite、MySQL、PostgreSQL 等多種主流資料庫，滿足從個人到團隊的不同部署需求。\n\n## ☕ 贊助與支援\n\n- 如果覺得這個外掛程式很有用，並且想要它繼續開發，請在以下方式支持我:\n\n  | Ko-fi *非中國地區*                                                                               |    | 微信掃碼打賞 *中國地區*                        |\n  |--------------------------------------------------------------------------------------------------|----|------------------------------------------------|\n  | [<img src=\"/docs/images/kofi.png\" alt=\"BuyMeACoffee\" height=\"150\">](https://ko-fi.com/haierkeys) | 或 | <img src=\"/docs/images/wxds.png\" height=\"150\"> |\n\n  - 已支持名單：\n    - <a href=\"https://github.com/haierkeys/fast-note-sync-service/blob/master/docs/Support.zh-TW.md\">Support.zh-TW.md</a>\n    - <a href=\"https://cnb.cool/haierkeys/fast-note-sync-service/-/blob/master/docs/Support.zh-TW.md\">Support.zh-TW.md (cnb.cool 鏡像庫)</a>\n\n## ⏱️ 更新日誌\n\n- ♨️ [點擊檢視更新日誌](/docs/CHANGELOG.zh-TW.md)\n\n## 🗺️ 路線圖 (Roadmap)\n\n- [ ] 增加 **Mock**測試, 覆蓋到 各層級。\n- [ ] 增加 WebSocket `Protobuf` 傳輸格式的支援, 強化同步傳輸效率。\n- [ ] 後端增加 同步日誌 & 操作日誌 等各類操作日誌的查詢。\n- [ ] 對現有授權機制進行隔離以及最佳化, 提升整體安全性。\n- [ ] 增加 WebGui 筆記即時更新\n- [ ] 增加用戶端 點對點 訊息傳送 (非筆記 & 附件, 類似 localsend 功能, 不支援用戶端保存, 可保存到伺服器端)\n- [ ] 各類幫助文件完善\n- [ ] 更多的內網穿透 (中繼閘道)的支援\n- [ ] 快速部署計畫\n  * 只需要提供伺服器網址 (公網)，帳號密碼 即可完成 FNS 伺服器端的部署\n- [ ] 最佳化現有的離線筆記合併方案, 增加衝突處理機制\n\n我們正在持續改進，以下是未來的開發計畫：\n\n> **如果您有改進建議或新想法，歡迎透過提交 issue 與我們分享——我們會認真評估並採納合適的建議。**\n\n## 🚀 快速部署\n\n我們提供多種安裝方式，推薦使用 **一鍵腳本** 或 **Docker**。\n\n### 方式一：一鍵腳本（推薦）\n\n自動檢測系統環境並完成安裝、服務註冊。\n\n```bash\nbash <(curl -fsSL https://raw.githubusercontent.com/haierkeys/fast-note-sync-service/master/scripts/quest_install.sh)\n```\n\n中國地區可以使用騰訊 `cnb.cool` 鏡像源\n```bash\nbash <(curl -fsSL https://cnb.cool/haierkeys/fast-note-sync-service/-/git/raw/master/scripts/quest_install.sh) --cnb\n```\n\n**腳本主要行為：**\n\n  * 自動下載適配當前系統的 Release 二進位檔案。\n  * 預設安裝至 `/opt/fast-note`，並在 `/usr/local/bin/fns` 建立全域快捷命令 `fns`。\n  * 設定並啟動 Systemd (Linux) 或 Launchd (macOS) 服務，實現開機自啟動。\n  * **管理命令**：`fns [install|uninstall|start|stop|status|update|menu]`\n  * **互動式選單**：直接執行 `fns` 可進入互動式選單，支援安裝/升級、服務控制、開機自啟動設定，以及在 GitHub / CNB 鏡像來源之間切換。\n\n-----\n\n### 方式二：Docker 部署\n\n#### Docker Run\n\n```bash\n# 1. 抓取映像檔\ndocker pull haierkeys/fast-note-sync-service:latest\n\n# 2. 啟動容器\ndocker run -tid --name fast-note-sync-service \\\n    -p 9000:9000 \\\n    -v /data/fast-note-sync/storage/:/fast-note-sync/storage/ \\\n    -v /data/fast-note-sync/config/:/fast-note-sync/config/ \\\n    haierkeys/fast-note-sync-service:latest\n```\n\n#### Docker Compose\n\n建立 `docker-compose.yaml` 檔案：\n\n```yaml\nversion: '3'\nservices:\n  fast-note-sync-service:\n    image: haierkeys/fast-note-sync-service:latest\n    container_name: fast-note-sync-service\n    restart: always\n    ports:\n      - \"9000:9000\"  # RESTful API & WebSocket 連接埠 其中 /api/user/sync 為 WebSocket API 網址\n    volumes:\n      - ./storage:/fast-note-sync/storage  # 資料儲存\n      - ./config:/fast-note-sync/config    # 設定檔\n```\n\n啟動服務：\n\n```bash\ndocker compose up -d\n```\n\n-----\n\n### 方式三：手動二進位安裝\n\n從 [Releases](https://github.com/haierkeys/fast-note-sync-service/releases) 下載對應系統的最新版本，解壓縮後執行：\n\n```bash\n./fast-note-sync-service run -c config/config.yaml\n```\n\n## 📖 使用指南\n\n1.  **存取管理面板**：\n    在瀏覽器開啟 `http://{伺服器IP}:9000`。\n2.  **初始化設定**：\n    首次存取需註冊帳號。*(如需關閉註冊功能，請在設定檔中設定 `user.register-is-enable: false`)*\n3.  **設定用戶端**：\n    登入管理面板，點擊 **「複製 API 設定」**。\n4.  **連接 Obsidian**：\n    打開 Obsidian 外掛程式設定頁面，貼上剛才複製的設定資訊即可。\n\n\n## ⚙️ 設定說明\n\n預設設定檔為 `config.yaml`，程式會自動在 **根目錄** 或 **config/** 目錄下尋找。\n\n查看完整設定範例：[config/config.yaml](https://github.com/haierkeys/fast-note-sync-service/blob/master/config/config.yaml)\n\n## 🌐 Nginx 反向代理設定範例\n\n查看完整設定範例：[https-nginx-example.conf](https://github.com/haierkeys/fast-note-sync-service/blob/master/scripts/https-nginx-example.conf)\n\n## 🧰 MCP (模型上下文協定) 支援\n\nFNS 現已原生支援 **MCP (Model Context Protocol)**。\n\n您可以將 FNS 作為 MCP 伺服器端直接接入 Cherry Studio、Cursor 等相容的 AI 用戶端。接入後，AI 即可具備讀寫私人筆記和附件的能力。同時，所有由 MCP 產生的修改，都會透過 WebSocket 即時同步到您的各個設備終端。\n\n### 接入設定 (SSE 模式)\n\nFNS 透過 **SSE 協定**提供 MCP 介面，通用參數要求如下：\n- **介面網址**：`http://<您的伺服器IP或網域>:<連接埠>/api/mcp/sse`\n- **鑑權 Header**：`Authorization: Bearer <您的 API Token>`（在 WebGUI 的複製 API 設定中取得）\n- **選填 Header**：`X-Default-Vault-Name: <筆記庫名稱>`（用於指定 MCP 操作的預設筆記庫，若工具呼叫時未指定 `vault` 參數，則使用此值）\n- **選填 Header**：`X-Client: <用戶端類型>`（用於連接 MCP 的用戶端類型，如：Cherry Studio / OpenClaw）\n- **選填 Header**：`X-Client-Version: <用戶端類型版本>`（用於連接 MCP 的用戶端類型版本，如：1.1）\n- **選填 Header**：`X-Client-Name: <用戶端名稱>`（用於連接 MCP 的用戶端名稱，如：Mac）\n\n\n\n#### 範例：Cherry Studio / Cursor / Cline 等\n\n請在您的 MCP 用戶端設定中參考如下設定：\n*(註：請將 `<ServerIP>`、`<Port>`、`<Token>` 和 `<VaultName>` 替換為您自己的實際資訊)*\n\n```json\n{\n  \"mcpServers\": {\n    \"fns\": {\n      \"url\": \"http://<ServerIP>:<Port>/api/mcp/sse\",\n      \"type\": \"sse\",\n      \"headers\": {\n        \"Content-Type\": \"application/json\",\n        \"Authorization\": \"Bearer <Token>\",\n        \"X-Default-Vault-Name\": \"<VaultName>\",\n        \"X-Client\": \"<Client>\",\n        \"X-Client-Version\": \"<ClientVersion>\",\n        \"X-Client-Name\": \"<ClientName>\"\n      }\n    }\n  }\n}\n```\n\n## 🔗 用戶端 & 用戶端外掛程式\n\n* Obsidian Fast Note Sync 外掛程式\n  * [Obsidian Fast Note Sync Plugin](https://github.com/haierkeys/obsidian-fast-note-sync) / [cnb.cool 鏡像庫](https://cnb.cool/haierkeys/obsidian-fast-note-sync)\n* 第三方用戶端\n  * [FastNodeSync-CLI ](https://github.com/Go1c/FastNodeSync-CLI) 基於 Python 和 FNS WS 介面實現的雙向即時同步的命令列用戶端, 適用於無 GUI 的 Linux 伺服器環境（如 OpenClaw），實現與 Obsidian 桌面端/行動端等價的同步能力。\n"
  },
  {
    "path": "docs/REST_API.md",
    "content": "# Fast Note Sync Service - REST API Documentation\n\nThis document is generated from `swagger.json` and provides the latest API definitions.\n\n---\n\n## General Information\n\n### Base URL\n```\nhttp://{host}:9000/api\n```\n\n### Authentication\nEndpoints requiring authentication must include the Token in the request header:\n```\nAuthorization: {token}\n```\nThe Token is obtained via the login interface.\n\n### Standard Response Structure\n```typescript\ninterface Response<T> {\n  code: number;      // Status code (0=fail, 1+=success)\n  status: boolean;   // Operation status\n  message: string;   // Status message\n  data: T;           // Business data\n  details?: string[]; // Error details (optional)\n}\n```\n\n### Paginated Response Structure\n```typescript\ninterface ListResponse<T> {\n  code: number;\n  status: boolean;\n  message: string;\n  data: {\n    list: T[];\n    pager: {\n      page: number;\n      pageSize: number;\n      totalRows: number;\n    }\n  }\n}\n```\n\n### Pagination Parameters\n| Parameter | Type | Description | Default |\n|-----------|------|-------------|---------|\n| page | number | Page number | 1 |\n| page_size | number | Items per page | 10 (Max 100) |\n\n---\n\n## Error Codes Reference\n\n| Code | Description |\n|------|-------------|\n| 0 | Failure |\n| 1-6 | Success states |\n| 400-446 | Business errors |\n| 500-534 | System/Sync errors |\n\n\n### Common Error Codes\n| Code | Description |\n|------|-------------|\n| 405 | User registration is closed |\n| 407 | Username does not exist |\n| 408 | Username already exists |\n| 414 | Note Vault does not exist |\n| 428 | Note does not exist |\n| 445 | This operation requires administrator privileges |\n| 505 | Invalid Params |\n| 507 | Not logged in |\n| 508 | Session expired |\n\n---\n\n## Backup APIs\n\n### Update backup configuration\n**Endpoint**: `POST /api/backup/config`\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.BackupConfigRequest | ✓ | Backup Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Delete backup configuration\n**Endpoint**: `DELETE /api/backup/config`\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| id | query | integer | - |  |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get backup configurations\n**Endpoint**: `GET /api/backup/configs`\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Trigger a backup manually\n**Endpoint**: `POST /api/backup/execute`\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.BackupExecuteRequest | ✓ | Backup Execute Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get backup history list\n**Endpoint**: `GET /api/backup/historys`\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| configId | query | integer | ✓ |  |\n| page | query | integer | - |  |\n| pageSize | query | integer | - |  |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n## Config APIs\n\n### Get full admin config\n**Endpoint**: `GET /api/admin/config`\n\nGet full system configuration information, requires admin privileges\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Update admin config\n**Endpoint**: `POST /api/admin/config`\n\nModify full system configuration information, requires admin privileges\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | api_router.adminConfig | ✓ | Config Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get Cloudflare config\n**Endpoint**: `GET /api/admin/config/cloudflare`\n\nGet Cloudflare tunnel configuration, requires admin privileges\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Update Cloudflare config\n**Endpoint**: `POST /api/admin/config/cloudflare`\n\nModify Cloudflare tunnel configuration, requires admin privileges\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | api_router.cloudflareConfig | ✓ | Config Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get Ngrok config\n**Endpoint**: `GET /api/admin/config/ngrok`\n\nGet Ngrok tunnel configuration, requires admin privileges\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Update Ngrok config\n**Endpoint**: `POST /api/admin/config/ngrok`\n\nModify Ngrok tunnel configuration, requires admin privileges\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | api_router.ngrokConfig | ✓ | Config Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get WebGUI basic config\n**Endpoint**: `GET /api/webgui/config`\n\nGet non-sensitive configuration required for frontend display, such as font settings, registration status, etc.\n\n**Parameters**:\nNone\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n## File APIs\n\n### Get attachment content\n**Endpoint**: `GET /api/file`\n\nGet raw binary data of an attachment by path, supports strong cache control\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| isRecycle | query | boolean | - | Is in recycle bin // 是否在回收站 |\n| path | query | string | ✓ | File path // 文件路径 |\n| pathHash | query | string | - | Path hash // 路径哈希 |\n| vault | query | string | ✓ | Vault name // 保险库名称 |\n\n**Success Response (200)**:\nSchema: `file`\n\n---\n\n### Delete attachment\n**Endpoint**: `DELETE /api/file`\n\nPermanently delete a specific attachment record and its physical file\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| path | query | string | ✓ | File path // 文件路径 |\n| pathHash | query | string | ✓ | Path hash // 路径哈希 |\n| vault | query | string | ✓ | Vault name // 保险库名称 |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get attachment info\n**Endpoint**: `GET /api/file/info`\n\nGet attachment metadata (FileDTO) by path\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| isRecycle | query | boolean | - | Is in recycle bin // 是否在回收站 |\n| path | query | string | ✓ | File path // 文件路径 |\n| pathHash | query | string | - | Path hash // 路径哈希 |\n| vault | query | string | ✓ | Vault name // 保险库名称 |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Clear recycle bin\n**Endpoint**: `DELETE /api/file/recycle-clear`\n\nPermanently clear selected files from recycle bin\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.FileRecycleClearRequest | ✓ | Clear Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Rename attachment\n**Endpoint**: `POST /api/file/rename`\n\nRename an attachment to a new path\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.FileRenameRequest | ✓ | Rename Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Restore attachment\n**Endpoint**: `PUT /api/file/restore`\n\nRestore deleted attachment from trash\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.FileRestoreRequest | ✓ | Restore Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get file list\n**Endpoint**: `GET /api/files`\n\nGet attachment list for current user with pagination, search, filter, and sort support\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| isRecycle | query | boolean | - | Is in recycle bin // 是否在回收站 |\n| keyword | query | string | - | Search keyword // 搜索关键词 |\n| sortBy | query | string | - | Sort by field // 排序字段 |\n| sortOrder | query | string | - | Sort order // 排序顺序 |\n| vault | query | string | ✓ | Vault name // 保险库名称 |\n| page | query | integer | - | Page number // 页码 |\n| pageSize | query | integer | - | Page size // 每页数量 |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n## Folder APIs\n\n### Get folder info\n**Endpoint**: `GET /api/folder`\n\nGet folder info for current user by path or pathHash\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| path | query | string | - | Folder path // 文件夹路径 |\n| pathHash | query | string | - | Path hash // 路径哈希 |\n| vault | query | string | ✓ | Vault name // 保险库名称 |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Create folder\n**Endpoint**: `POST /api/folder`\n\nCreate a new folder or restore a deleted one by path\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.FolderCreateRequest | ✓ | Create Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Delete folder\n**Endpoint**: `DELETE /api/folder`\n\nSoft delete a folder by path or pathHash\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.FolderDeleteRequest | ✓ | Delete Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### List files in folder\n**Endpoint**: `GET /api/folder/files`\n\nList non-deleted files in a specific folder with pagination and sorting\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| path | query | string | - | Folder path // 文件夹路径 |\n| pathHash | query | string | - | Path hash // 路径哈希 |\n| sortBy | query | string | - | Sort by field // 排序字段 |\n| sortOrder | query | string | - | Sort order // 排序顺序 |\n| vault | query | string | ✓ | Vault name // 保险库名称 |\n| page | query | integer | - | Page number // 页码 |\n| pageSize | query | integer | - | Page size // 每页数量 |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### List notes in folder\n**Endpoint**: `GET /api/folder/notes`\n\nList non-deleted notes in a specific folder with pagination and sorting\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| path | query | string | - | Folder path // 文件夹路径 |\n| pathHash | query | string | - | Path hash // 路径哈希 |\n| sortBy | query | string | - | Sort by field // 排序字段 |\n| sortOrder | query | string | - | Sort order // 排序顺序 |\n| vault | query | string | ✓ | Vault name // 保险库名称 |\n| page | query | integer | - | Page number // 页码 |\n| pageSize | query | integer | - | Page size // 每页数量 |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get folder tree\n**Endpoint**: `GET /api/folder/tree`\n\nGet the complete folder tree structure for a vault\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| depth | query | integer | - | Tree depth // 树深度 |\n| vault | query | string | ✓ | Vault name // 保险库名称 |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get folder list\n**Endpoint**: `GET /api/folders`\n\nGet folder list for current user by parent path or pathHash\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| path | query | string | - | Folder path // 文件夹路径 |\n| pathHash | query | string | - | Path hash // 路径哈希 |\n| vault | query | string | ✓ | Vault name // 保险库名称 |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n## GitSync APIs\n\n### Update git sync configuration\n**Endpoint**: `POST /api/git-sync/config`\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.GitSyncConfigRequest | ✓ | Git Sync Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Delete git sync configuration\n**Endpoint**: `DELETE /api/git-sync/config`\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.GitSyncDeleteRequest | ✓ | Git Sync ID |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Clean local git workspace\n**Endpoint**: `DELETE /api/git-sync/config/clean`\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.GitSyncCleanRequest | ✓ | Clean Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Trigger a manual git sync\n**Endpoint**: `POST /api/git-sync/config/execute`\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.GitSyncExecuteRequest | ✓ | Execute Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get git sync configurations\n**Endpoint**: `GET /api/git-sync/configs`\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get git sync histories\n**Endpoint**: `GET /api/git-sync/histories`\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| configId | query | integer | - |  |\n| page | query | integer | - |  |\n| pageSize | query | integer | - |  |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Validate git sync parameters\n**Endpoint**: `POST /api/git-sync/validate`\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.GitSyncValidateRequest | ✓ | Validation Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n## Note APIs\n\n### Get note details\n**Endpoint**: `GET /api/note`\n\nGet specific note content and metadata by path or path hash\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| isRecycle | query | boolean | - | Is in recycle bin // 是否在回收站 |\n| path | query | string | ✓ | Note path // 笔记路径 |\n| pathHash | query | string | - | Path hash // 路径哈希 |\n| vault | query | string | ✓ | Vault name // 保险库名称 |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Create or update note\n**Endpoint**: `POST /api/note`\n\nHandle note creation, modification, or renaming (identified by path change)\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.NoteModifyOrCreateRequest | ✓ | Note Content |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Delete note\n**Endpoint**: `DELETE /api/note`\n\nMove note to trash\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| path | query | string | ✓ | Note path // 笔记路径 |\n| pathHash | query | string | - | Path hash // 路径哈希 |\n| vault | query | string | ✓ | Vault name // 保险库名称 |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Append content to note\n**Endpoint**: `POST /api/note/append`\n\nAppend content to the end of a note\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.NoteAppendRequest | ✓ | Append Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get backlinks\n**Endpoint**: `GET /api/note/backlinks`\n\nGet all other notes that link to the specified note\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| path | query | string | ✓ | Note path // 笔记路径 |\n| pathHash | query | string | - | Path hash // 路径哈希 |\n| vault | query | string | ✓ | Vault name // 保险库名称 |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Modify note frontmatter\n**Endpoint**: `PATCH /api/note/frontmatter`\n\nUpdate or delete note frontmatter fields\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.NotePatchFrontmatterRequest | ✓ | Frontmatter Modification Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Move note\n**Endpoint**: `POST /api/note/move`\n\nMove a note to a new path\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.NoteMoveRequest | ✓ | Move Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get outgoing links\n**Endpoint**: `GET /api/note/outlinks`\n\nGet other notes that the specified note links to\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| path | query | string | ✓ | Note path // 笔记路径 |\n| pathHash | query | string | - | Path hash // 路径哈希 |\n| vault | query | string | ✓ | Vault name // 保险库名称 |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Prepend content to note\n**Endpoint**: `POST /api/note/prepend`\n\nInsert content at the beginning of a note (after frontmatter)\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.NotePrependRequest | ✓ | Prepend Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Clear recycle bin\n**Endpoint**: `DELETE /api/note/recycle-clear`\n\nPermanently clear selected notes from recycle bin\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.NoteRecycleClearRequest | ✓ | Clear Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Rename note\n**Endpoint**: `POST /api/note/rename`\n\nRename a note to a new path\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.NoteRenameRequest | ✓ | Rename Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Find and replace in note\n**Endpoint**: `POST /api/note/replace`\n\nPerform find and replace operation in a note, supporting regular expressions\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.NoteReplaceRequest | ✓ | Find and Replace Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Restore note\n**Endpoint**: `PUT /api/note/restore`\n\nRestore deleted note from trash\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.NoteRestoreRequest | ✓ | Restore Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get note list\n**Endpoint**: `GET /api/notes`\n\nGet note list for current user with pagination\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| isRecycle | query | boolean | - | Is in recycle bin // 是否在回收站 |\n| keyword | query | string | - | Search keyword // 搜索关键词 |\n| searchContent | query | boolean | - | Whether to search content // 是否搜索内容 |\n| searchMode | query | string | - | Search mode (path, content, regex) // 搜索模式（路径、内容、正则） |\n| sortBy | query | string | - | Sort by field // 排序字段 |\n| sortOrder | query | string | - | Sort order // 排序顺序 |\n| vault | query | string | ✓ | Vault name // 保险库名称 |\n| page | query | integer | - | Page number // 页码 |\n| pageSize | query | integer | - | Page size // 每页数量 |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n## Note History APIs\n\n### Get note history list\n**Endpoint**: `GET /api/note/histories`\n\nGet all history records for a specific note with pagination\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| isRecycle | query | boolean | - | Is in recycle bin // 是否在回收站 |\n| path | query | string | ✓ | Note path // 笔记路径 |\n| pathHash | query | string | - | Path hash // 路径哈希 |\n| vault | query | string | ✓ | Vault name // 保险库名称 |\n| page | query | integer | - | Page number // 页码 |\n| pageSize | query | integer | - | Page size // 每页数量 |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get note history details\n**Endpoint**: `GET /api/note/history`\n\nGet specific note history content by history record ID\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| id | query | integer | ✓ | History Record ID |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Restore note from history\n**Endpoint**: `PUT /api/note/history/restore`\n\nRestore note content to a specific history version\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.NoteHistoryRestoreRequest | ✓ | Restore Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n## Setting APIs\n\n### Get setting info\n**Endpoint**: `GET /api/setting`\n\nGet setting info for current user by path or pathHash\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| path | query | string | - | Setting path // 配置路径 |\n| pathHash | query | string | - | Path hash // 路径哈希 |\n| vault | query | string | ✓ | Vault name // 保险库名称 |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Create or update setting\n**Endpoint**: `POST /api/setting`\n\nCreate a new setting or update an existing one\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.SettingModifyOrCreateRequest | ✓ | Create/Update Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Delete setting\n**Endpoint**: `DELETE /api/setting`\n\nSoft delete a setting by path or pathHash\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.SettingDeleteRequest | ✓ | Delete Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Rename setting\n**Endpoint**: `POST /api/setting/rename`\n\nRename a setting and update its path and pathHash\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.SettingRenameRequest | ✓ | Rename Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get setting list\n**Endpoint**: `GET /api/settings`\n\nGet setting list for current user with pagination and keyword filtering\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| keyword | query | string | - | Keyword // 关键词 |\n| vault | query | string | ✓ | Vault name // 保险库名称 |\n| page | query | integer | - | Page number // 页码 |\n| pageSize | query | integer | - | Page size // 每页数量 |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n## Share APIs\n\n### Query share by path\n**Endpoint**: `GET /api/share`\n\nGet share token and info by vault and path\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| path | query | string | ✓ | Resource path // 资源路径 |\n| pathHash | query | string | ✓ | Resource path Hash // 资源路径哈希 |\n| vault | query | string | ✓ | Vault name // 保险库名称 |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Create resource share\n**Endpoint**: `POST /api/share`\n\nCreate a share token for a specific note or attachment, automatically resolve attachment references and authorize\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.ShareCreateRequest | ✓ | Share Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Cancel share\n**Endpoint**: `DELETE /api/share`\n\nCancel a share by ID or path parameters\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.ShareCancelRequest | ✓ | Cancel Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get shared attachment content\n**Endpoint**: `GET /api/share/file`\n\nGet raw binary data of a specific attachment via share token\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| Share-Token | header | string | ✓ | Auth Token |\n| id | query | integer | ✓ | Resource ID // 资源 ID |\n\n**Success Response (200)**:\nSchema: `file`\n\n---\n\n### Get shared note details\n**Endpoint**: `GET /api/share/note`\n\nGet specific note content (restricted read-only access) via share token\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| Share-Token | header | string | ✓ | Auth Token |\n| id | query | integer | ✓ | Resource ID // 资源 ID |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### List shares\n**Endpoint**: `GET /api/shares`\n\nGet all active and inactive shares of the user, supports sorting and pagination\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| sort_by | query | string | - | Sort field: created_at, updated_at, expires_at (default: created_at) |\n| sort_order | query | string | - | Sort direction: asc or desc (default: desc) |\n| page | query | integer | - | Page number |\n| pageSize | query | integer | - | Page size |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n## Storage APIs\n\n### Get storage configuration list\n**Endpoint**: `GET /api/storage`\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Create or update storage configuration\n**Endpoint**: `POST /api/storage`\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.StoragePostRequest | ✓ | Storage Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Delete storage configuration\n**Endpoint**: `DELETE /api/storage`\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| id | query | integer | ✓ | Storage ID |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get enabled storage types\n**Endpoint**: `GET /api/storage/enabled_types`\n\nGet list of enabled storage types. Possible values: localfs, oss, s3, r2, minio, webdav\n\n**Parameters**:\nNone\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Validate storage connection\n**Endpoint**: `POST /api/storage/validate`\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.StoragePostRequest | ✓ | Storage Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n## System APIs\n\n### Download cloudflared binary\n**Endpoint**: `GET /api/admin/cloudflared_tunnel_download`\n\nTrigger the download of cloudflared binary for the current platform\n\n**Parameters**:\nNone\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Trigger manual GC\n**Endpoint**: `GET /api/admin/gc`\n\nManually run Go runtime GC and release memory to OS, requires admin privileges\n\n**Parameters**:\nNone\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Trigger server restart\n**Endpoint**: `GET /api/admin/restart`\n\nGracefully restart the server\n\n**Parameters**:\nNone\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get system and runtime info\n**Endpoint**: `GET /api/admin/systeminfo`\n\nGet system information and Go runtime data, requires admin privileges\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Trigger server upgrade\n**Endpoint**: `GET /api/admin/upgrade`\n\nDownload latest version and restart server\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| version | query | string | ✓ | Version to upgrade (e.g. 2.0.10 or latest) |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get connected WebSocket clients\n**Endpoint**: `GET /api/admin/ws_clients`\n\nGet a list of all current WebSocket connections, requires admin privileges\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Health check\n**Endpoint**: `GET /api/health`\n\nCheck service health status, including database connection\n\n**Parameters**:\nNone\n\n**Success Response (200)**:\nSchema: `api_router.HealthResponse`\n\n---\n\n### Get support records\n**Endpoint**: `GET /api/support`\n\nGet support records for the specified language with pagination and sorting\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| lang | query | string | - | Language code (default: en) |\n| sortBy | query | string | - | Sort by field (amount, time, name, item) |\n| sortOrder | query | string | - | Sort order (asc, desc) |\n| page | query | integer | - | Page number |\n| pageSize | query | integer | - | Page size |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get server version info\n**Endpoint**: `GET /api/version`\n\nGet current server software version, Git tag, and build time\n\n**Parameters**:\nNone\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n## User APIs\n\n### Change user password\n**Endpoint**: `POST /api/user/change_password`\n\nHandle password change request for current user, validate old password and update new password.\n处理当前用户的修改密码请求，验证旧密码并更新新密码。\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.UserChangePasswordRequest | ✓ | Change Password Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get user info\n**Endpoint**: `GET /api/user/info`\n\nHandle request to get current user info.\n处理获取当前用户信息的请求。\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### User login\n**Endpoint**: `POST /api/user/login`\n\nHandle user login HTTP request, validate parameters and return auth token.\n处理用户登录 HTTP 请求，验证参数并返回认证 Token。\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| params | body | dto.UserLoginRequest | ✓ | Login Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### User registration\n**Endpoint**: `POST /api/user/register`\n\nHandle user registration HTTP request, validate parameters and call UserService. Registration may be disabled in server settings.\n处理用户注册 HTTP 请求，验证参数并调用 UserService。注册功能可能在服务器设置中被禁用。\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| params | body | dto.UserCreateRequest | ✓ | Register Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n## Vault APIs\n\n### Get vault list\n**Endpoint**: `GET /api/vault`\n\nGet all note vaults for current user\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Create or update vault\n**Endpoint**: `POST /api/vault`\n\nBe used to create a new vault or update an existing vault configuration based on the ID in the request parameters\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| params | body | dto.VaultPostRequest | ✓ | Vault Parameters |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Delete vault\n**Endpoint**: `DELETE /api/vault`\n\nPermanently delete a specific note vault and all associated notes and attachments\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| id | query | integer | ✓ | Vault ID // 保险库 ID |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n### Get vault details\n**Endpoint**: `GET /api/vault/get`\n\nGet specific vault configuration details by vault ID\n\n**Parameters**:\n| Name | In | Type | Required | Description |\n|------|----|------|----------|-------------|\n| token | header | string | ✓ | Auth Token |\n| id | query | integer | ✓ | Vault ID |\n\n**Success Response (200)**:\nSchema: `app.Res`\n\n---\n\n## Timestamp Format\n\nAll timestamp fields (`ctime`, `mtime`, `updatedTimestamp`, `lastTime`) are **Unix timestamps in milliseconds**.\n\n---\n\n## Hash Algorithms\n\n`pathHash` and `contentHash` use a 32-bit hash algorithm (e.g., FNV-1a). Clients can compute these automatically or receive them from the server.\n\n---\n\n## Full-Text Search (FTS)\n\nThe server includes a built-in full-text search engine based on SQLite FTS5 for efficient searching of note paths and content.\n"
  },
  {
    "path": "docs/Support.csv",
    "content": "收款时间,收款项,金额,单位,留言,昵称\n2026/03/27 00:36:52,任意打赏,128.00,¥,特别棒，一直在用，希望越做越好。,Geeson\n2026/04/18 21:15:46,四杯咖啡☕,100.00,¥,支持一下[抱拳],lien\n2026/03/29 11:20:42,四杯咖啡☕,100.00,¥,支持！加油！,猛将兄\n2026/03/27 15:05:35,四杯咖啡☕,100.00,¥,做得太棒了,Bais\n2026/03/24 09:02:45,四杯咖啡☕,100.00,¥,辛苦了,cc\n2026/03/22 12:16:07,四杯咖啡☕,100.00,¥,强烈支持版本迭代,cw\n2026/03/19 13:41:05,四杯咖啡☕,100.00,¥,快去加班更新,背背背疼\n2026/03/13 17:28:40,四杯咖啡☕,100.00,¥,非常感谢你的开源与付出，插件超实用，小小支持，继续加油！💪,一世风霜\n2026/03/02 09:38:26,四杯咖啡☕,100.00,¥,非常好，开发不易，支持一下。,xuhsu\n2026/01/14 15:58:04,任意打赏,88.00,¥,能力有限，不成敬意,wutay\n2026/03/02 14:50:25,任意打赏,66.00,¥,感谢大佬！非常好用！,Patrick\n2026/03/01 22:56:17,任意打赏,66.00,¥,随喜赞叹！,xday\n2026/03/01 22:40:23,任意打赏,66.00,¥,大佬nb，插件很好用ヽ(*≧ω≦)ﾉ,HanHaocheng\n2026/02/16 21:34:33,任意打赏,66.00,¥,新年快乐,Jack\n2026/04/02 19:16:44,任意打赏,51.55,¥,很好的插件,小七的小洋\n2026/04/21 14:32:40,两杯咖啡☕,50.00,¥,太棒了，宝藏插件啊，加油,安度\n2026/04/09 13:45:07,两杯咖啡☕,50.00,¥,电脑同步成功，安卓报错  code=305，message=参数验证失败 Details=context is requ,亲 yexizhu811\n2026/04/08 03:18:40,两杯咖啡☕,50.00,¥,感谢作者，这一同步方式解决了多设备配置一致性的麻烦。,彼岸花\n2026/04/07 07:52:09,两杯咖啡☕,50.00,¥,太棒了，很需要，感谢大佬。,tom\n2026/04/03 10:50:44,两杯咖啡☕,50.00,¥,Good work,David\n2026/04/02 03:03:20,两杯咖啡☕,50.00,¥,为牛逼付费！,狗带带子\n2026/03/27 10:15:04,两杯咖啡☕,50.00,¥,好人一生平安,卿\n2026/03/18 22:57:03,两杯咖啡☕,50.00,¥,感谢telegram上的指导,灰风\n2026/03/15 20:09:05,两杯咖啡☕,50.00,¥,感谢🙏，插件好用,红星 RedStar\n2026/03/14 23:46:58,两杯咖啡☕,50.00,¥,非常好整套架构，让我进入21世纪,fbeis\n2026/03/02 21:00:43,两杯咖啡☕,50.00,¥,谢谢大佬,南科大小魏\n2026/02/28 13:56:18,两杯咖啡☕,50.00,¥,太勤劳了，必须支持,xenon\n2026/02/24 16:37:58,两杯咖啡☕,50.00,¥,很不错的同步方案,熙熙煦煦\n2026/02/16 12:14:32,两杯咖啡☕,50.00,¥,感谢作者，新年快乐！,红殇\n2026/02/14 18:01:55,两杯咖啡☕,50.00,¥,感谢开发这么棒的插件，解决了同步问题,Jacky龙\n2026/02/04 10:41:49,两杯咖啡☕,50.00,¥,同步功能很好用，希望继续迭代完善，以笔记安全为主,咕咕咕\n2026/01/31 16:59:55,两杯咖啡☕,50.00,¥,好好开发，确实解决了 Obsidian 最大的一个痛点！,vulnnull\n2026/01/21 09:37:19,两杯咖啡☕,50.00,¥,谢谢你的Obsidian同步很好用👍🏻,Mojo抖音\n2026/01/09 16:34:10,两杯咖啡☕,50.00,¥,谢谢男菩萨的 OB 插件造福世人(^🙏^)，小小心意不成敬意。,喆\n2026/03/04 21:42:57,任意打赏,30.00,¥,插件很好用，感谢开发者,X\n2026/02/25 10:55:32,任意打赏,30.00,¥,大佬的项目帮了我大忙！非常感谢！希望大佬继续加油,jeanlaw\n2026/03/13 13:43:33,任意打赏,29.00,¥,好作品，加油！,rocku\n2026/02/24 18:36:51,任意打赏,25.00,¥,\"一直找不到理想的obsidian的同步方案,感谢作者 加油！\",淇淇\n2026/04/21 13:45:19,一杯咖啡☕,20.00,¥,很好用，加油,蓬歌\n2026/04/20 15:37:57,一杯咖啡☕,20.00,¥,给大佬跪了🧎🏻‍♂️，希望后续多多更新,riding-a-colt\n2026/04/17 00:15:35,一杯咖啡☕,20.00,¥,好用，支持一下,稻草人\n2026/04/15 13:41:41,一杯咖啡☕,20.00,¥,谢谢大佬,hitomi\n2026/04/15 11:22:39,一杯咖啡☕,20.00,¥,忘记密码如何找回,woshiug\n2026/04/15 06:38:14,一杯咖啡☕,20.00,¥,试试,ke1078\n2026/04/12 23:54:45,一杯咖啡☕,20.00,¥,很棒的软件，感谢作者付出和开源分享。,Nikki\n2026/04/12 23:40:17,一杯咖啡☕,20.00,¥,感谢🙏🏻,月非明\n2026/04/10 23:00:36,一杯咖啡☕,20.00,¥,棒棒哒，真好用,wdysjy\n2026/04/04 01:33:37,一杯咖啡☕,20.00,¥,来杯咖啡，辛苦了,hsonghao\n2026/04/03 23:42:51,一杯咖啡☕,20.00,¥,牛逼，好用,kakaa\n2026/04/03 13:28:02,一杯咖啡☕,20.00,¥,非常好用，感谢,晴天小嘉\n2026/04/02 23:08:13,一杯咖啡☕,20.00,¥,感谢大佬，希望能继续坚持,畅\n2026/04/02 15:13:46,一杯咖啡☕,20.00,¥,请您喝咖啡，这个项目非常有用,dove\n2026/03/30 23:34:12,一杯咖啡☕,20.00,¥,加油⛽️,andie\n2026/03/28 12:46:23,一杯咖啡☕,20.00,¥,感谢大大，辛苦啦,zhengbiubiu\n2026/03/27 19:45:57,一杯咖啡☕,20.00,¥,fast note sync👍,IsaacSuo\n2026/03/26 13:22:50,一杯咖啡☕,20.00,¥,牛哇👍,dawn\n2026/03/24 14:45:21,一杯咖啡☕,20.00,¥,感谢，软件很方便。,Bean\n2026/03/22 23:08:14,一杯咖啡☕,20.00,¥,respect,拾感\n2026/03/19 22:47:54,一杯咖啡☕,20.00,¥,谢谢🙏！很赞！,jediknight\n2026/03/19 20:20:17,一杯咖啡☕,20.00,¥,感谢开发者，太好用了，加油！,Fcjd\n2026/03/17 01:17:02,一杯咖啡☕,20.00,¥,做的太好了，大道至简，希望继续优化~,southzen\n2026/03/16 16:24:11,一杯咖啡☕,20.00,¥,非常好用的喵～谢谢喵～,长筱团子\n2026/03/16 09:22:14,一杯咖啡☕,20.00,¥,不成敬意^_^,barry\n2026/03/16 01:01:12,一杯咖啡☕,20.00,¥,感谢你对开源世界的贡献,Stone\n2026/03/14 17:15:45,一杯咖啡☕,20.00,¥,感谢作者，不成敬意,R M\n2026/03/14 00:39:54,一杯咖啡☕,20.00,¥,大佬NB，感谢感谢,T0_欣\n2026/03/11 20:05:58,一杯咖啡☕,20.00,¥,感谢大佬做的插件，很好用很方便,Ucat\n2026/03/10 01:08:20,一杯咖啡☕,20.00,¥,牛逼,la\n2026/03/09 09:59:44,一杯咖啡☕,20.00,¥,好用，支持,耀/\n2026/03/07 23:36:38,一杯咖啡☕,20.00,¥,很好的插件 支持,阿叶\n2026/03/06 09:58:58,一杯咖啡☕,20.00,¥,谢谢,Dylan\n2026/03/03 00:53:09,一杯咖啡☕,20.00,¥,感谢作者日夜辛苦的写代码，并开源,Alan\n2026/03/01 21:10:27,一杯咖啡☕,20.00,¥,希望能更完善，可以在云端查看ob的其他格式的文件,Jack ☑️\n2026/02/28 09:50:58,一杯咖啡☕,20.00,¥,这个插件思路很好，加油,tangdh\n2026/02/27 12:48:50,一杯咖啡☕,20.00,¥,很好的同步插件,aban\n2026/02/27 11:37:18,一杯咖啡☕,20.00,¥,感谢您的工作,三岁\n2026/02/27 11:19:13,一杯咖啡☕,20.00,¥,太强了,行长\n2026/02/26 11:16:25,一杯咖啡☕,20.00,¥,这么好的东西应该让更多人知道，加大宣传力度啊！,fausto\n2026/02/25 20:17:42,一杯咖啡☕,20.00,¥,非常好用，感谢作者开发这么好的工具！,woloin\n2026/02/24 18:29:45,一杯咖啡☕,20.00,¥,感谢作者开发那么好用的插件，让我的obsidian旋转🥰,kimi\n2026/02/24 10:01:37,一杯咖啡☕,20.00,¥,插件好用,ccsir\n2026/02/23 20:53:08,一杯咖啡☕,20.00,¥,用了一段时间了，真的太棒了。,KevinYAN\n2026/02/23 19:32:03,一杯咖啡☕,20.00,¥,马年快乐,繁星影月\n2026/02/23 12:30:55,一杯咖啡☕,20.00,¥,谢谢辛苦了,ahto\n2026/02/23 06:24:55,一杯咖啡☕,20.00,¥,很有帮助，加油！,大学生\n2026/02/15 09:25:17,一杯咖啡☕,20.00,¥,支持，加油~，很实用，强需求的功能。在AI时代大有用处,sfsun67\n2026/02/10 23:21:38,一杯咖啡☕,20.00,¥,感谢大佬做的同步插件，非常好用，请大佬先喝一杯咖啡，后续还会再继续打赏的,toby\n2026/02/10 11:41:36,一杯咖啡☕,20.00,¥,大佬的同步插件非常棒，我会一直持续支持的,WONG\n2026/02/08 22:02:32,一杯咖啡☕,20.00,¥,非常好用，感谢,小迪\n2026/02/06 08:42:49,一杯咖啡☕,20.00,¥,加油💪web端的图片编辑功能整一下呗,Max\n2026/01/28 10:48:02,一杯咖啡☕,20.00,¥,加油,通\n2026/01/26 17:21:27,一杯咖啡☕,20.00,¥,😘,CloseCV\n2026/01/16 11:47:13,一杯咖啡☕,20.00,¥,很好用，期待后续的开发与优化。感谢。,苏\n2026/01/15 14:51:11,一杯咖啡☕,20.00,¥,非常好用感谢！,灰风\n2026/01/09 18:12:17,一杯咖啡☕,20.00,¥,插件思路太对了,xix\n2026/01/03 22:44:43,一杯咖啡☕,20.00,¥,希望越来越好👌🏻,姚朝伟\n2026/01/03 14:58:43,一杯咖啡☕,20.00,¥,很棒的同步方案，未来可期！非常感谢开源！加油！,roao\n2026/03/11 09:58:20,任意打赏,18.00,¥,绵薄之力，以表敬意,下鞅\n2026/03/02 21:15:39,任意打赏,10.00,¥,加油大神,路过打酱\n2026/02/28 12:27:51,任意打赏,10.00,¥,希望越来越好,白芷\n2026/02/27 15:54:55,任意打赏,10.00,¥,谢谢作者开发，感谢开源，祝越来越好。,柴特\n2026/02/23 15:34:53,任意打赏,10.00,¥,感谢，比官方同步还好用,Joe M\n2026/02/20 10:37:02,任意打赏,10.00,¥,感谢做了这么便捷的同步软件,羽山猫四叶\n2026/03/22 15:09:43,任意打赏,9.90,¥,好软件，感谢作者。,Shifuwang\n2026/01/28 12:03:03,任意打赏,9.90,¥,牛🐮,华星\n2026/04/09 13:51:11,任意打赏,8.88,¥,加油……你这个绝对有发展前途，推荐其他朋友看都觉得很不错。,散装白酒🍶\n2026/03/07 20:29:42,任意打赏,8.88,¥,感谢大佬，非常好用，点赞,皮皮\n2026/01/28 02:52:15,任意打赏,8.88,¥,感谢分享,obsidian\n2026/02/28 19:51:59,任意打赏,8.00,¥,很完善，甚至服务器界面也做得非常好看,yang\n2026/03/25 11:52:09,任意打赏,6.66,¥,装好了 无敌 哈哈哈哈哈哈,东\n2026/03/23 01:02:18,任意打赏,6.66,¥,一定要坚持开发呀！！！,wishyuwill\n2026/03/02 17:10:28,任意打赏,6.66,¥,感谢开发，插件很好用，更新很快[强],马孔多的旅人\n2026/02/01 23:44:27,任意打赏,6.66,¥,爱你,爱你\n2026/01/09 22:22:25,任意打赏,6.66,¥,老哥写的插件很棒，继续努力吧！,kane\n2026/04/20 16:34:57,任意打赏,5.00,¥,这也太好用了，折腾这么久感觉终于毕业了(╥╯﹏╰╥)ง,david\n2026/04/15 00:48:38,任意打赏,5.00,¥,不易,ben\n2026/04/06 20:57:36,任意打赏,5.00,¥,感谢你的项目帮助到我，obsidian 同步从此变得容易,octobersky\n2026/03/01 00:28:19,任意打赏,5.00,¥,开发的太棒了,colorednoise\n2026/02/27 15:18:26,任意打赏,5.00,¥,支持,wudibaolong\n2026/02/14 02:32:50,任意打赏,5.00,¥,感谢开发这么好用的插件,支持开源精神\n2026/02/13 02:13:10,任意打赏,5.00,¥,打赏,xxx\n2026/02/11 17:07:02,任意打赏,5.00,¥,加油,Acckion\n2026/01/11 14:20:34,任意打赏,5.00,¥,很好用，希望能把 git 做出来,安宁\n2026/04/18 16:59:14,任意打赏,3.66,¥,继续开发，作出好产品,jeremy\n2026/02/23 17:47:46,任意打赏,3.00,¥,新年快乐,LL\n2026/04/11 12:28:18,任意打赏,1.00,¥,试试,ke1078\n2026/03/26 15:03:40,任意打赏,1.00,¥,感谢，十分有用,guanyingquan\n2026/02/24 20:15:19,任意打赏,1.00,¥,感恩您的 Obs 同步插件非常有帮助！,Jimmy\n2026/01/08 15:18:06,任意打赏,1.00,¥,从发现部署到使用，好多年了，从未有过的流畅丝滑的感觉，真的太好了！[强][强][强],用户\n"
  },
  {
    "path": "docs/Support.en.json",
    "content": "[\n  {\n    \"time\": \"2026/03/27 00:36:52\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"128.00\",\n    \"unit\": \"¥\",\n    \"message\": \"It’s great, I’ve been using it, and I hope it gets better and better.\",\n    \"name\": \"Geeson\"\n  },\n  {\n    \"time\": \"2026/04/18 21:15:46\",\n    \"item\": \"Four cups of coffee☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Support me [hold fist]\",\n    \"name\": \"lien\"\n  },\n  {\n    \"time\": \"2026/03/29 11:20:42\",\n    \"item\": \"Four cups of coffee☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"support! come on!\",\n    \"name\": \"猛将兄\"\n  },\n  {\n    \"time\": \"2026/03/27 15:05:35\",\n    \"item\": \"Four cups of coffee☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Great job\",\n    \"name\": \"Bais\"\n  },\n  {\n    \"time\": \"2026/03/24 09:02:45\",\n    \"item\": \"Four cups of coffee☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks for your hard work\",\n    \"name\": \"cc\"\n  },\n  {\n    \"time\": \"2026/03/22 12:16:07\",\n    \"item\": \"Four cups of coffee☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Strong support for version iteration\",\n    \"name\": \"cw\"\n  },\n  {\n    \"time\": \"2026/03/19 13:41:05\",\n    \"item\": \"Four cups of coffee☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Go and work overtime to update\",\n    \"name\": \"背背背疼\"\n  },\n  {\n    \"time\": \"2026/03/13 17:28:40\",\n    \"item\": \"Four cups of coffee☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thank you very much for your open source and efforts. The plug-in is super practical. I appreciate your support. Keep up the good work! 💪\",\n    \"name\": \"一世风霜\"\n  },\n  {\n    \"time\": \"2026/03/02 09:38:26\",\n    \"item\": \"Four cups of coffee☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Very good, development is not easy, please support it.\",\n    \"name\": \"xuhsu\"\n  },\n  {\n    \"time\": \"2026/01/14 15:58:04\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"88.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Limited ability, no respect\",\n    \"name\": \"wutay\"\n  },\n  {\n    \"time\": \"2026/03/02 14:50:25\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks man! Very easy to use!\",\n    \"name\": \"Patrick\"\n  },\n  {\n    \"time\": \"2026/03/01 22:56:17\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Congratulations!\",\n    \"name\": \"xday\"\n  },\n  {\n    \"time\": \"2026/03/01 22:40:23\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Boss nb, the plug-in is very usefulヽ(*≧ω≦)ﾉ\",\n    \"name\": \"HanHaocheng\"\n  },\n  {\n    \"time\": \"2026/02/16 21:34:33\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Happy New Year\",\n    \"name\": \"Jack\"\n  },\n  {\n    \"time\": \"2026/04/02 19:16:44\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"51.55\",\n    \"unit\": \"¥\",\n    \"message\": \"Very good plugin\",\n    \"name\": \"小七的小洋\"\n  },\n  {\n    \"time\": \"2026/04/21 14:32:40\",\n    \"item\": \"Two cups of coffee☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Great, treasure plug-in, come on\",\n    \"name\": \"安度\"\n  },\n  {\n    \"time\": \"2026/04/09 13:45:07\",\n    \"item\": \"Two cups of coffee☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Computer synchronization is successful, Android error code=305, message=parameter verification failed Details=context is requ\",\n    \"name\": \"亲 yexizhu811\"\n  },\n  {\n    \"time\": \"2026/04/08 03:18:40\",\n    \"item\": \"Two cups of coffee☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks to the author, this synchronization method solves the trouble of multi-device configuration consistency.\",\n    \"name\": \"彼岸花\"\n  },\n  {\n    \"time\": \"2026/04/07 07:52:09\",\n    \"item\": \"Two cups of coffee☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Awesome, much needed, thank you sir.\",\n    \"name\": \"tom\"\n  },\n  {\n    \"time\": \"2026/04/03 10:50:44\",\n    \"item\": \"Two cups of coffee☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Good work\",\n    \"name\": \"David\"\n  },\n  {\n    \"time\": \"2026/04/02 03:03:20\",\n    \"item\": \"Two cups of coffee☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Pay for awesomeness!\",\n    \"name\": \"狗带带子\"\n  },\n  {\n    \"time\": \"2026/03/27 10:15:04\",\n    \"item\": \"Two cups of coffee☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Good people have a safe life\",\n    \"name\": \"卿\"\n  },\n  {\n    \"time\": \"2026/03/18 22:57:03\",\n    \"item\": \"Two cups of coffee☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks for the guidance on telegram\",\n    \"name\": \"灰风\"\n  },\n  {\n    \"time\": \"2026/03/15 20:09:05\",\n    \"item\": \"Two cups of coffee☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks 🙏, the plug-in is easy to use\",\n    \"name\": \"红星 RedStar\"\n  },\n  {\n    \"time\": \"2026/03/14 23:46:58\",\n    \"item\": \"Two cups of coffee☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"The whole structure is very good and brings me into the 21st century.\",\n    \"name\": \"fbeis\"\n  },\n  {\n    \"time\": \"2026/03/02 21:00:43\",\n    \"item\": \"Two cups of coffee☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thank you, boss\",\n    \"name\": \"南科大小魏\"\n  },\n  {\n    \"time\": \"2026/02/28 13:56:18\",\n    \"item\": \"Two cups of coffee☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Too hardworking and must be supported\",\n    \"name\": \"xenon\"\n  },\n  {\n    \"time\": \"2026/02/24 16:37:58\",\n    \"item\": \"Two cups of coffee☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Very good synchronization solution\",\n    \"name\": \"熙熙煦煦\"\n  },\n  {\n    \"time\": \"2026/02/16 12:14:32\",\n    \"item\": \"Two cups of coffee☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks to the author and Happy New Year!\",\n    \"name\": \"红殇\"\n  },\n  {\n    \"time\": \"2026/02/14 18:01:55\",\n    \"item\": \"Two cups of coffee☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks for developing such a great plug-in, which solves the synchronization problem\",\n    \"name\": \"Jacky龙\"\n  },\n  {\n    \"time\": \"2026/02/04 10:41:49\",\n    \"item\": \"Two cups of coffee☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"The synchronization function is very useful. I hope to continue to iterate and improve it, focusing on note safety.\",\n    \"name\": \"咕咕咕\"\n  },\n  {\n    \"time\": \"2026/01/31 16:59:55\",\n    \"item\": \"Two cups of coffee☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Well developed, it has indeed solved one of Obsidian’s biggest pain points!\",\n    \"name\": \"vulnnull\"\n  },\n  {\n    \"time\": \"2026/01/21 09:37:19\",\n    \"item\": \"Two cups of coffee☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thank you for the Obsidian synchronization which is very useful👍🏻\",\n    \"name\": \"Mojo抖音\"\n  },\n  {\n    \"time\": \"2026/01/09 16:34:10\",\n    \"item\": \"Two cups of coffee☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thank you Male Bodhisattva for your OB plug-in for benefiting the world (^🙏^), being petty is disrespectful.\",\n    \"name\": \"喆\"\n  },\n  {\n    \"time\": \"2026/03/04 21:42:57\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"30.00\",\n    \"unit\": \"¥\",\n    \"message\": \"The plug-in is very useful, thank you to the developer\",\n    \"name\": \"X\"\n  },\n  {\n    \"time\": \"2026/02/25 10:55:32\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"30.00\",\n    \"unit\": \"¥\",\n    \"message\": \"The boss’s project helped me a lot! Thank you so much! I hope you guys will continue to work hard\",\n    \"name\": \"jeanlaw\"\n  },\n  {\n    \"time\": \"2026/03/13 13:43:33\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"29.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Great work, keep it up!\",\n    \"name\": \"rocku\"\n  },\n  {\n    \"time\": \"2026/02/24 18:36:51\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"25.00\",\n    \"unit\": \"¥\",\n    \"message\": \"I have never been able to find an ideal obsidian synchronization solution. Thank you to the author. Come on!\",\n    \"name\": \"淇淇\"\n  },\n  {\n    \"time\": \"2026/04/21 13:45:19\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Very useful, keep going\",\n    \"name\": \"蓬歌\"\n  },\n  {\n    \"time\": \"2026/04/20 15:37:57\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Kneel down to the boss 🧎🏻‍♂️, I hope there will be more updates in the future\",\n    \"name\": \"riding-a-colt\"\n  },\n  {\n    \"time\": \"2026/04/17 00:15:35\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"It’s easy to use, support it\",\n    \"name\": \"稻草人\"\n  },\n  {\n    \"time\": \"2026/04/15 13:41:41\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thank you, boss\",\n    \"name\": \"hitomi\"\n  },\n  {\n    \"time\": \"2026/04/15 11:22:39\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"How to retrieve forgotten password\",\n    \"name\": \"woshiug\"\n  },\n  {\n    \"time\": \"2026/04/15 06:38:14\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"try\",\n    \"name\": \"ke1078\"\n  },\n  {\n    \"time\": \"2026/04/12 23:54:45\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Great software, thank the author for his contribution and open source sharing.\",\n    \"name\": \"Nikki\"\n  },\n  {\n    \"time\": \"2026/04/12 23:40:17\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thank you🙏🏻\",\n    \"name\": \"月非明\"\n  },\n  {\n    \"time\": \"2026/04/10 23:00:36\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Awesome, really useful\",\n    \"name\": \"wdysjy\"\n  },\n  {\n    \"time\": \"2026/04/04 01:33:37\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Have a cup of coffee. Thank you for your hard work.\",\n    \"name\": \"hsonghao\"\n  },\n  {\n    \"time\": \"2026/04/03 23:42:51\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Awesome, easy to use\",\n    \"name\": \"kakaa\"\n  },\n  {\n    \"time\": \"2026/04/03 13:28:02\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Very useful, thank you\",\n    \"name\": \"晴天小嘉\"\n  },\n  {\n    \"time\": \"2026/04/02 23:08:13\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks man, I hope you can continue to persevere\",\n    \"name\": \"畅\"\n  },\n  {\n    \"time\": \"2026/04/02 15:13:46\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Buy you a coffee, this project is very useful\",\n    \"name\": \"dove\"\n  },\n  {\n    \"time\": \"2026/03/30 23:34:12\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Come on⛽️\",\n    \"name\": \"andie\"\n  },\n  {\n    \"time\": \"2026/03/28 12:46:23\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thank you very much, thank you for your hard work\",\n    \"name\": \"zhengbiubiu\"\n  },\n  {\n    \"time\": \"2026/03/27 19:45:57\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"fast note sync👍\",\n    \"name\": \"IsaacSuo\"\n  },\n  {\n    \"time\": \"2026/03/26 13:22:50\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Wow 👍\",\n    \"name\": \"dawn\"\n  },\n  {\n    \"time\": \"2026/03/24 14:45:21\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thank you, the software is very convenient.\",\n    \"name\": \"Bean\"\n  },\n  {\n    \"time\": \"2026/03/22 23:08:14\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"respect\",\n    \"name\": \"拾感\"\n  },\n  {\n    \"time\": \"2026/03/19 22:47:54\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thank you 🙏! Great!\",\n    \"name\": \"jediknight\"\n  },\n  {\n    \"time\": \"2026/03/19 20:20:17\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks to the developer, it’s so easy to use, come on!\",\n    \"name\": \"Fcjd\"\n  },\n  {\n    \"time\": \"2026/03/17 01:17:02\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Great job, simple and simple, I hope to continue to optimize~\",\n    \"name\": \"southzen\"\n  },\n  {\n    \"time\": \"2026/03/16 16:24:11\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Very useful, meow~Thank you, meow~\",\n    \"name\": \"长筱团子\"\n  },\n  {\n    \"time\": \"2026/03/16 09:22:14\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Not respectful^_^\",\n    \"name\": \"barry\"\n  },\n  {\n    \"time\": \"2026/03/16 01:01:12\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thank you for your contributions to the open source world\",\n    \"name\": \"Stone\"\n  },\n  {\n    \"time\": \"2026/03/14 17:15:45\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks to the author, no disrespect\",\n    \"name\": \"R M\"\n  },\n  {\n    \"time\": \"2026/03/14 00:39:54\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Boss NB, thank you thank you\",\n    \"name\": \"T0_欣\"\n  },\n  {\n    \"time\": \"2026/03/11 20:05:58\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks for the plug-in, it’s very useful and convenient.\",\n    \"name\": \"Ucat\"\n  },\n  {\n    \"time\": \"2026/03/10 01:08:20\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Awesome\",\n    \"name\": \"la\"\n  },\n  {\n    \"time\": \"2026/03/09 09:59:44\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Easy to use, support\",\n    \"name\": \"耀/\"\n  },\n  {\n    \"time\": \"2026/03/07 23:36:38\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Very good plugin support\",\n    \"name\": \"阿叶\"\n  },\n  {\n    \"time\": \"2026/03/06 09:58:58\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks\",\n    \"name\": \"Dylan\"\n  },\n  {\n    \"time\": \"2026/03/03 00:53:09\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks to the author for working hard day and night to write the code and open source it\",\n    \"name\": \"Alan\"\n  },\n  {\n    \"time\": \"2026/03/01 21:10:27\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"I hope it will be more complete and you can view ob files in other formats on the cloud.\",\n    \"name\": \"Jack ☑️\"\n  },\n  {\n    \"time\": \"2026/02/28 09:50:58\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"The idea of ​​this plug-in is very good, keep up the good work\",\n    \"name\": \"tangdh\"\n  },\n  {\n    \"time\": \"2026/02/27 12:48:50\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Very good sync plugin\",\n    \"name\": \"aban\"\n  },\n  {\n    \"time\": \"2026/02/27 11:37:18\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"thank you for your work\",\n    \"name\": \"三岁\"\n  },\n  {\n    \"time\": \"2026/02/27 11:19:13\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Too strong\",\n    \"name\": \"行长\"\n  },\n  {\n    \"time\": \"2026/02/26 11:16:25\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Such a good thing should be made known to more people and publicity efforts should be increased!\",\n    \"name\": \"fausto\"\n  },\n  {\n    \"time\": \"2026/02/25 20:17:42\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Very easy to use, thank the author for developing such a good tool!\",\n    \"name\": \"woloin\"\n  },\n  {\n    \"time\": \"2026/02/24 18:29:45\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks to the author for developing such a useful plug-in, which makes my obsidian rotate🥰\",\n    \"name\": \"kimi\"\n  },\n  {\n    \"time\": \"2026/02/24 10:01:37\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Plug-in is easy to use\",\n    \"name\": \"ccsir\"\n  },\n  {\n    \"time\": \"2026/02/23 20:53:08\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Been using it for a while and it's really great.\",\n    \"name\": \"KevinYAN\"\n  },\n  {\n    \"time\": \"2026/02/23 19:32:03\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Happy Year of the Horse\",\n    \"name\": \"繁星影月\"\n  },\n  {\n    \"time\": \"2026/02/23 12:30:55\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thank you for your hard work\",\n    \"name\": \"ahto\"\n  },\n  {\n    \"time\": \"2026/02/23 06:24:55\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Very helpful, come on!\",\n    \"name\": \"大学生\"\n  },\n  {\n    \"time\": \"2026/02/15 09:25:17\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Support, come on~, it is very practical and a highly demanded function. Very useful in the AI ​​era\",\n    \"name\": \"sfsun67\"\n  },\n  {\n    \"time\": \"2026/02/10 23:21:38\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks for the synchronization plug-in made by the boss. It is very easy to use. Please drink a cup of coffee first and I will continue to reward you later.\",\n    \"name\": \"toby\"\n  },\n  {\n    \"time\": \"2026/02/10 11:41:36\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"The synchronization plug-in of the boss is very good. I will continue to support it.\",\n    \"name\": \"WONG\"\n  },\n  {\n    \"time\": \"2026/02/08 22:02:32\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Very useful, thank you\",\n    \"name\": \"小迪\"\n  },\n  {\n    \"time\": \"2026/02/06 08:42:49\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Come on💪 improve the image editing function on the web\",\n    \"name\": \"Max\"\n  },\n  {\n    \"time\": \"2026/01/28 10:48:02\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"come on\",\n    \"name\": \"通\"\n  },\n  {\n    \"time\": \"2026/01/26 17:21:27\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"-\",\n    \"name\": \"CloseCV\"\n  },\n  {\n    \"time\": \"2026/01/16 11:47:13\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Very useful, looking forward to subsequent development and optimization. grateful.\",\n    \"name\": \"苏\"\n  },\n  {\n    \"time\": \"2026/01/15 14:51:11\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Very useful. Thanks!\",\n    \"name\": \"灰风\"\n  },\n  {\n    \"time\": \"2026/01/09 18:12:17\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"The plug-in idea is so right\",\n    \"name\": \"xix\"\n  },\n  {\n    \"time\": \"2026/01/03 22:44:43\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Hope it gets better and better👌🏻\",\n    \"name\": \"姚朝伟\"\n  },\n  {\n    \"time\": \"2026/01/03 14:58:43\",\n    \"item\": \"A cup of coffee☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Great synchronization solution, look forward to the future! Thank you very much for the open source! come on!\",\n    \"name\": \"roao\"\n  },\n  {\n    \"time\": \"2026/03/11 09:58:20\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"18.00\",\n    \"unit\": \"¥\",\n    \"message\": \"A small effort to show respect\",\n    \"name\": \"下鞅\"\n  },\n  {\n    \"time\": \"2026/03/02 21:15:39\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Come on, God\",\n    \"name\": \"路过打酱\"\n  },\n  {\n    \"time\": \"2026/02/28 12:27:51\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Hope it gets better and better\",\n    \"name\": \"白芷\"\n  },\n  {\n    \"time\": \"2026/02/27 15:54:55\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thank you to the author for the development, thank you for the open source, and wish it better and better.\",\n    \"name\": \"柴特\"\n  },\n  {\n    \"time\": \"2026/02/23 15:34:53\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks, it’s easier to use than the official synchronization\",\n    \"name\": \"Joe M\"\n  },\n  {\n    \"time\": \"2026/02/20 10:37:02\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks for making such a convenient synchronization software\",\n    \"name\": \"羽山猫四叶\"\n  },\n  {\n    \"time\": \"2026/03/22 15:09:43\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"9.90\",\n    \"unit\": \"¥\",\n    \"message\": \"Great software, thanks to the author.\",\n    \"name\": \"Shifuwang\"\n  },\n  {\n    \"time\": \"2026/01/28 12:03:03\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"9.90\",\n    \"unit\": \"¥\",\n    \"message\": \"Cow🐮\",\n    \"name\": \"华星\"\n  },\n  {\n    \"time\": \"2026/04/09 13:51:11\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"8.88\",\n    \"unit\": \"¥\",\n    \"message\": \"Come on...you definitely have a bright future. I would recommend it to other friends and think it would be great.\",\n    \"name\": \"散装白酒🍶\"\n  },\n  {\n    \"time\": \"2026/03/07 20:29:42\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"8.88\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks man, it’s very useful, like it\",\n    \"name\": \"皮皮\"\n  },\n  {\n    \"time\": \"2026/01/28 02:52:15\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"8.88\",\n    \"unit\": \"¥\",\n    \"message\": \"thanks for sharing\",\n    \"name\": \"obsidian\"\n  },\n  {\n    \"time\": \"2026/02/28 19:51:59\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"8.00\",\n    \"unit\": \"¥\",\n    \"message\": \"It's very complete, even the server interface is very nice.\",\n    \"name\": \"yang\"\n  },\n  {\n    \"time\": \"2026/03/25 11:52:09\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"Pretended to be invincible hahahahahaha\",\n    \"name\": \"东\"\n  },\n  {\n    \"time\": \"2026/03/23 01:02:18\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"You must keep developing! ! !\",\n    \"name\": \"wishyuwill\"\n  },\n  {\n    \"time\": \"2026/03/02 17:10:28\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks for the development, the plug-in is very useful and will be updated quickly [strong]\",\n    \"name\": \"马孔多的旅人\"\n  },\n  {\n    \"time\": \"2026/02/01 23:44:27\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"Love you\",\n    \"name\": \"爱你\"\n  },\n  {\n    \"time\": \"2026/01/09 22:22:25\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"The plug-in I wrote is great, keep up the good work!\",\n    \"name\": \"kane\"\n  },\n  {\n    \"time\": \"2026/04/20 16:34:57\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"This is so useful. I feel like I have finally graduated after struggling for so long (╥╯﹏╰╥)ง\",\n    \"name\": \"david\"\n  },\n  {\n    \"time\": \"2026/04/15 00:48:38\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Not easy\",\n    \"name\": \"ben\"\n  },\n  {\n    \"time\": \"2026/04/06 20:57:36\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks for helping me with your project, obsidian synchronization becomes easy from now on\",\n    \"name\": \"octobersky\"\n  },\n  {\n    \"time\": \"2026/03/01 00:28:19\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Great development\",\n    \"name\": \"colorednoise\"\n  },\n  {\n    \"time\": \"2026/02/27 15:18:26\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"support\",\n    \"name\": \"wudibaolong\"\n  },\n  {\n    \"time\": \"2026/02/14 02:32:50\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks for developing such a useful plug-in\",\n    \"name\": \"支持开源精神\"\n  },\n  {\n    \"time\": \"2026/02/13 02:13:10\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"reward\",\n    \"name\": \"xxx\"\n  },\n  {\n    \"time\": \"2026/02/11 17:07:02\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"come on\",\n    \"name\": \"Acckion\"\n  },\n  {\n    \"time\": \"2026/01/11 14:20:34\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"It’s very useful. I hope I can use git to build it.\",\n    \"name\": \"安宁\"\n  },\n  {\n    \"time\": \"2026/04/18 16:59:14\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"3.66\",\n    \"unit\": \"¥\",\n    \"message\": \"Continue to develop and make good products\",\n    \"name\": \"jeremy\"\n  },\n  {\n    \"time\": \"2026/02/23 17:47:46\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"3.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Happy New Year\",\n    \"name\": \"LL\"\n  },\n  {\n    \"time\": \"2026/04/11 12:28:18\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"try\",\n    \"name\": \"ke1078\"\n  },\n  {\n    \"time\": \"2026/03/26 15:03:40\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks, very useful\",\n    \"name\": \"guanyingquan\"\n  },\n  {\n    \"time\": \"2026/02/24 20:15:19\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Thanks for the very helpful Obs sync plugin!\",\n    \"name\": \"Jimmy\"\n  },\n  {\n    \"time\": \"2026/01/08 15:18:06\",\n    \"item\": \"Tip as you like\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"From discovery and deployment to use, it has been many years, and it feels smooth and silky like never before. It is really great! [strong][strong][strong]\",\n    \"name\": \"用户\"\n  }\n]"
  },
  {
    "path": "docs/Support.en.md",
    "content": "# Supporters List\n\n> Thank you very much for supporting this project! Every donation is the driving force for my continuous maintenance and iteration. ❤️\n\n### 📜 Acknowledgement List\n\n| Time | Item | Amount | Name | Message |\n| :--- | :--- | :--- | :--- | :--- |\n| 2026/03/27 00:36:52 | Tip as you like | **¥128.00** | Geeson | It’s great, I’ve been using it, and I hope it gets better and better. |\n| 2026/04/18 21:15:46 | Four cups of coffee☕ | **¥100.00** | lien | Support me [hold fist] |\n| 2026/03/29 11:20:42 | Four cups of coffee☕ | **¥100.00** | 猛将兄 | support! come on! |\n| 2026/03/27 15:05:35 | Four cups of coffee☕ | **¥100.00** | Bais | Great job |\n| 2026/03/24 09:02:45 | Four cups of coffee☕ | **¥100.00** | cc | Thanks for your hard work |\n| 2026/03/22 12:16:07 | Four cups of coffee☕ | **¥100.00** | cw | Strong support for version iteration |\n| 2026/03/19 13:41:05 | Four cups of coffee☕ | **¥100.00** | 背背背疼 | Go and work overtime to update |\n| 2026/03/13 17:28:40 | Four cups of coffee☕ | **¥100.00** | 一世风霜 | Thank you very much for your open source and efforts. The plug-in is super practical. I appreciate your support. Keep up the good work! 💪 |\n| 2026/03/02 09:38:26 | Four cups of coffee☕ | **¥100.00** | xuhsu | Very good, development is not easy, please support it. |\n| 2026/01/14 15:58:04 | Tip as you like | **¥88.00** | wutay | Limited ability, no respect |\n| 2026/03/02 14:50:25 | Tip as you like | **¥66.00** | Patrick | Thanks man! Very easy to use! |\n| 2026/03/01 22:56:17 | Tip as you like | **¥66.00** | xday | Congratulations! |\n| 2026/03/01 22:40:23 | Tip as you like | **¥66.00** | HanHaocheng | Boss nb, the plug-in is very usefulヽ(*≧ω≦)ﾉ |\n| 2026/02/16 21:34:33 | Tip as you like | **¥66.00** | Jack | Happy New Year |\n| 2026/04/02 19:16:44 | Tip as you like | **¥51.55** | 小七的小洋 | Very good plugin |\n| 2026/04/21 14:32:40 | Two cups of coffee☕ | **¥50.00** | 安度 | Great, treasure plug-in, come on |\n| 2026/04/09 13:45:07 | Two cups of coffee☕ | **¥50.00** | 亲 yexizhu811 | Computer synchronization is successful, Android error code=305, message=parameter verification failed Details=context is requ |\n| 2026/04/08 03:18:40 | Two cups of coffee☕ | **¥50.00** | 彼岸花 | Thanks to the author, this synchronization method solves the trouble of multi-device configuration consistency. |\n| 2026/04/07 07:52:09 | Two cups of coffee☕ | **¥50.00** | tom | Awesome, much needed, thank you sir. |\n| 2026/04/03 10:50:44 | Two cups of coffee☕ | **¥50.00** | David | Good work |\n| 2026/04/02 03:03:20 | Two cups of coffee☕ | **¥50.00** | 狗带带子 | Pay for awesomeness! |\n| 2026/03/27 10:15:04 | Two cups of coffee☕ | **¥50.00** | 卿 | Good people have a safe life |\n| 2026/03/18 22:57:03 | Two cups of coffee☕ | **¥50.00** | 灰风 | Thanks for the guidance on telegram |\n| 2026/03/15 20:09:05 | Two cups of coffee☕ | **¥50.00** | 红星 RedStar | Thanks 🙏, the plug-in is easy to use |\n| 2026/03/14 23:46:58 | Two cups of coffee☕ | **¥50.00** | fbeis | The whole structure is very good and brings me into the 21st century. |\n| 2026/03/02 21:00:43 | Two cups of coffee☕ | **¥50.00** | 南科大小魏 | Thank you, boss |\n| 2026/02/28 13:56:18 | Two cups of coffee☕ | **¥50.00** | xenon | Too hardworking and must be supported |\n| 2026/02/24 16:37:58 | Two cups of coffee☕ | **¥50.00** | 熙熙煦煦 | Very good synchronization solution |\n| 2026/02/16 12:14:32 | Two cups of coffee☕ | **¥50.00** | 红殇 | Thanks to the author and Happy New Year! |\n| 2026/02/14 18:01:55 | Two cups of coffee☕ | **¥50.00** | Jacky龙 | Thanks for developing such a great plug-in, which solves the synchronization problem |\n| 2026/02/04 10:41:49 | Two cups of coffee☕ | **¥50.00** | 咕咕咕 | The synchronization function is very useful. I hope to continue to iterate and improve it, focusing on note safety. |\n| 2026/01/31 16:59:55 | Two cups of coffee☕ | **¥50.00** | vulnnull | Well developed, it has indeed solved one of Obsidian’s biggest pain points! |\n| 2026/01/21 09:37:19 | Two cups of coffee☕ | **¥50.00** | Mojo抖音 | Thank you for the Obsidian synchronization which is very useful👍🏻 |\n| 2026/01/09 16:34:10 | Two cups of coffee☕ | **¥50.00** | 喆 | Thank you Male Bodhisattva for your OB plug-in for benefiting the world (^🙏^), being petty is disrespectful. |\n| 2026/03/04 21:42:57 | Tip as you like | **¥30.00** | X | The plug-in is very useful, thank you to the developer |\n| 2026/02/25 10:55:32 | Tip as you like | **¥30.00** | jeanlaw | The boss’s project helped me a lot! Thank you so much! I hope you guys will continue to work hard |\n| 2026/03/13 13:43:33 | Tip as you like | **¥29.00** | rocku | Great work, keep it up! |\n| 2026/02/24 18:36:51 | Tip as you like | **¥25.00** | 淇淇 | I have never been able to find an ideal obsidian synchronization solution. Thank you to the author. Come on! |\n| 2026/04/21 13:45:19 | A cup of coffee☕ | **¥20.00** | 蓬歌 | Very useful, keep going |\n| 2026/04/20 15:37:57 | A cup of coffee☕ | **¥20.00** | riding-a-colt | Kneel down to the boss 🧎🏻‍♂️, I hope there will be more updates in the future |\n| 2026/04/17 00:15:35 | A cup of coffee☕ | **¥20.00** | 稻草人 | It’s easy to use, support it |\n| 2026/04/15 13:41:41 | A cup of coffee☕ | **¥20.00** | hitomi | Thank you, boss |\n| 2026/04/15 11:22:39 | A cup of coffee☕ | **¥20.00** | woshiug | How to retrieve forgotten password |\n| 2026/04/15 06:38:14 | A cup of coffee☕ | **¥20.00** | ke1078 | try |\n| 2026/04/12 23:54:45 | A cup of coffee☕ | **¥20.00** | Nikki | Great software, thank the author for his contribution and open source sharing. |\n| 2026/04/12 23:40:17 | A cup of coffee☕ | **¥20.00** | 月非明 | Thank you🙏🏻 |\n| 2026/04/10 23:00:36 | A cup of coffee☕ | **¥20.00** | wdysjy | Awesome, really useful |\n| 2026/04/04 01:33:37 | A cup of coffee☕ | **¥20.00** | hsonghao | Have a cup of coffee. Thank you for your hard work. |\n| 2026/04/03 23:42:51 | A cup of coffee☕ | **¥20.00** | kakaa | Awesome, easy to use |\n| 2026/04/03 13:28:02 | A cup of coffee☕ | **¥20.00** | 晴天小嘉 | Very useful, thank you |\n| 2026/04/02 23:08:13 | A cup of coffee☕ | **¥20.00** | 畅 | Thanks man, I hope you can continue to persevere |\n| 2026/04/02 15:13:46 | A cup of coffee☕ | **¥20.00** | dove | Buy you a coffee, this project is very useful |\n| 2026/03/30 23:34:12 | A cup of coffee☕ | **¥20.00** | andie | Come on⛽️ |\n| 2026/03/28 12:46:23 | A cup of coffee☕ | **¥20.00** | zhengbiubiu | Thank you very much, thank you for your hard work |\n| 2026/03/27 19:45:57 | A cup of coffee☕ | **¥20.00** | IsaacSuo | fast note sync👍 |\n| 2026/03/26 13:22:50 | A cup of coffee☕ | **¥20.00** | dawn | Wow 👍 |\n| 2026/03/24 14:45:21 | A cup of coffee☕ | **¥20.00** | Bean | Thank you, the software is very convenient. |\n| 2026/03/22 23:08:14 | A cup of coffee☕ | **¥20.00** | 拾感 | respect |\n| 2026/03/19 22:47:54 | A cup of coffee☕ | **¥20.00** | jediknight | Thank you 🙏! Great! |\n| 2026/03/19 20:20:17 | A cup of coffee☕ | **¥20.00** | Fcjd | Thanks to the developer, it’s so easy to use, come on! |\n| 2026/03/17 01:17:02 | A cup of coffee☕ | **¥20.00** | southzen | Great job, simple and simple, I hope to continue to optimize~ |\n| 2026/03/16 16:24:11 | A cup of coffee☕ | **¥20.00** | 长筱团子 | Very useful, meow~Thank you, meow~ |\n| 2026/03/16 09:22:14 | A cup of coffee☕ | **¥20.00** | barry | Not respectful^_^ |\n| 2026/03/16 01:01:12 | A cup of coffee☕ | **¥20.00** | Stone | Thank you for your contributions to the open source world |\n| 2026/03/14 17:15:45 | A cup of coffee☕ | **¥20.00** | R M | Thanks to the author, no disrespect |\n| 2026/03/14 00:39:54 | A cup of coffee☕ | **¥20.00** | T0_欣 | Boss NB, thank you thank you |\n| 2026/03/11 20:05:58 | A cup of coffee☕ | **¥20.00** | Ucat | Thanks for the plug-in, it’s very useful and convenient. |\n| 2026/03/10 01:08:20 | A cup of coffee☕ | **¥20.00** | la | Awesome |\n| 2026/03/09 09:59:44 | A cup of coffee☕ | **¥20.00** | 耀/ | Easy to use, support |\n| 2026/03/07 23:36:38 | A cup of coffee☕ | **¥20.00** | 阿叶 | Very good plugin support |\n| 2026/03/06 09:58:58 | A cup of coffee☕ | **¥20.00** | Dylan | Thanks |\n| 2026/03/03 00:53:09 | A cup of coffee☕ | **¥20.00** | Alan | Thanks to the author for working hard day and night to write the code and open source it |\n| 2026/03/01 21:10:27 | A cup of coffee☕ | **¥20.00** | Jack ☑️ | I hope it will be more complete and you can view ob files in other formats on the cloud. |\n| 2026/02/28 09:50:58 | A cup of coffee☕ | **¥20.00** | tangdh | The idea of ​​this plug-in is very good, keep up the good work |\n| 2026/02/27 12:48:50 | A cup of coffee☕ | **¥20.00** | aban | Very good sync plugin |\n| 2026/02/27 11:37:18 | A cup of coffee☕ | **¥20.00** | 三岁 | thank you for your work |\n| 2026/02/27 11:19:13 | A cup of coffee☕ | **¥20.00** | 行长 | Too strong |\n| 2026/02/26 11:16:25 | A cup of coffee☕ | **¥20.00** | fausto | Such a good thing should be made known to more people and publicity efforts should be increased! |\n| 2026/02/25 20:17:42 | A cup of coffee☕ | **¥20.00** | woloin | Very easy to use, thank the author for developing such a good tool! |\n| 2026/02/24 18:29:45 | A cup of coffee☕ | **¥20.00** | kimi | Thanks to the author for developing such a useful plug-in, which makes my obsidian rotate🥰 |\n| 2026/02/24 10:01:37 | A cup of coffee☕ | **¥20.00** | ccsir | Plug-in is easy to use |\n| 2026/02/23 20:53:08 | A cup of coffee☕ | **¥20.00** | KevinYAN | Been using it for a while and it's really great. |\n| 2026/02/23 19:32:03 | A cup of coffee☕ | **¥20.00** | 繁星影月 | Happy Year of the Horse |\n| 2026/02/23 12:30:55 | A cup of coffee☕ | **¥20.00** | ahto | Thank you for your hard work |\n| 2026/02/23 06:24:55 | A cup of coffee☕ | **¥20.00** | 大学生 | Very helpful, come on! |\n| 2026/02/15 09:25:17 | A cup of coffee☕ | **¥20.00** | sfsun67 | Support, come on~, it is very practical and a highly demanded function. Very useful in the AI ​​era |\n| 2026/02/10 23:21:38 | A cup of coffee☕ | **¥20.00** | toby | Thanks for the synchronization plug-in made by the boss. It is very easy to use. Please drink a cup of coffee first and I will continue to reward you later. |\n| 2026/02/10 11:41:36 | A cup of coffee☕ | **¥20.00** | WONG | The synchronization plug-in of the boss is very good. I will continue to support it. |\n| 2026/02/08 22:02:32 | A cup of coffee☕ | **¥20.00** | 小迪 | Very useful, thank you |\n| 2026/02/06 08:42:49 | A cup of coffee☕ | **¥20.00** | Max | Come on💪 improve the image editing function on the web |\n| 2026/01/28 10:48:02 | A cup of coffee☕ | **¥20.00** | 通 | come on |\n| 2026/01/26 17:21:27 | A cup of coffee☕ | **¥20.00** | CloseCV | 😘 |\n| 2026/01/16 11:47:13 | A cup of coffee☕ | **¥20.00** | 苏 | Very useful, looking forward to subsequent development and optimization. grateful. |\n| 2026/01/15 14:51:11 | A cup of coffee☕ | **¥20.00** | 灰风 | Very useful. Thanks! |\n| 2026/01/09 18:12:17 | A cup of coffee☕ | **¥20.00** | xix | The plug-in idea is so right |\n| 2026/01/03 22:44:43 | A cup of coffee☕ | **¥20.00** | 姚朝伟 | Hope it gets better and better👌🏻 |\n| 2026/01/03 14:58:43 | A cup of coffee☕ | **¥20.00** | roao | Great synchronization solution, look forward to the future! Thank you very much for the open source! come on! |\n| 2026/03/11 09:58:20 | Tip as you like | **¥18.00** | 下鞅 | A small effort to show respect |\n| 2026/03/02 21:15:39 | Tip as you like | **¥10.00** | 路过打酱 | Come on, God |\n| 2026/02/28 12:27:51 | Tip as you like | **¥10.00** | 白芷 | Hope it gets better and better |\n| 2026/02/27 15:54:55 | Tip as you like | **¥10.00** | 柴特 | Thank you to the author for the development, thank you for the open source, and wish it better and better. |\n| 2026/02/23 15:34:53 | Tip as you like | **¥10.00** | Joe M | Thanks, it’s easier to use than the official synchronization |\n| 2026/02/20 10:37:02 | Tip as you like | **¥10.00** | 羽山猫四叶 | Thanks for making such a convenient synchronization software |\n| 2026/03/22 15:09:43 | Tip as you like | **¥9.90** | Shifuwang | Great software, thanks to the author. |\n| 2026/01/28 12:03:03 | Tip as you like | **¥9.90** | 华星 | Cow🐮 |\n| 2026/04/09 13:51:11 | Tip as you like | **¥8.88** | 散装白酒🍶 | Come on...you definitely have a bright future. I would recommend it to other friends and think it would be great. |\n| 2026/03/07 20:29:42 | Tip as you like | **¥8.88** | 皮皮 | Thanks man, it’s very useful, like it |\n| 2026/01/28 02:52:15 | Tip as you like | **¥8.88** | obsidian | thanks for sharing |\n| 2026/02/28 19:51:59 | Tip as you like | **¥8.00** | yang | It's very complete, even the server interface is very nice. |\n| 2026/03/25 11:52:09 | Tip as you like | **¥6.66** | 东 | Pretended to be invincible hahahahahaha |\n| 2026/03/23 01:02:18 | Tip as you like | **¥6.66** | wishyuwill | You must keep developing! ! ! |\n| 2026/03/02 17:10:28 | Tip as you like | **¥6.66** | 马孔多的旅人 | Thanks for the development, the plug-in is very useful and will be updated quickly [strong] |\n| 2026/02/01 23:44:27 | Tip as you like | **¥6.66** | 爱你 | Love you |\n| 2026/01/09 22:22:25 | Tip as you like | **¥6.66** | kane | The plug-in I wrote is great, keep up the good work! |\n| 2026/04/20 16:34:57 | Tip as you like | **¥5.00** | david | This is so useful. I feel like I have finally graduated after struggling for so long (╥╯﹏╰╥)ง |\n| 2026/04/15 00:48:38 | Tip as you like | **¥5.00** | ben | Not easy |\n| 2026/04/06 20:57:36 | Tip as you like | **¥5.00** | octobersky | Thanks for helping me with your project, obsidian synchronization becomes easy from now on |\n| 2026/03/01 00:28:19 | Tip as you like | **¥5.00** | colorednoise | Great development |\n| 2026/02/27 15:18:26 | Tip as you like | **¥5.00** | wudibaolong | support |\n| 2026/02/14 02:32:50 | Tip as you like | **¥5.00** | 支持开源精神 | Thanks for developing such a useful plug-in |\n| 2026/02/13 02:13:10 | Tip as you like | **¥5.00** | xxx | reward |\n| 2026/02/11 17:07:02 | Tip as you like | **¥5.00** | Acckion | come on |\n| 2026/01/11 14:20:34 | Tip as you like | **¥5.00** | 安宁 | It’s very useful. I hope I can use git to build it. |\n| 2026/04/18 16:59:14 | Tip as you like | **¥3.66** | jeremy | Continue to develop and make good products |\n| 2026/02/23 17:47:46 | Tip as you like | **¥3.00** | LL | Happy New Year |\n| 2026/04/11 12:28:18 | Tip as you like | **¥1.00** | ke1078 | try |\n| 2026/03/26 15:03:40 | Tip as you like | **¥1.00** | guanyingquan | Thanks, very useful |\n| 2026/02/24 20:15:19 | Tip as you like | **¥1.00** | Jimmy | Thanks for the very helpful Obs sync plugin! |\n| 2026/01/08 15:18:06 | Tip as you like | **¥1.00** | 用户 | From discovery and deployment to use, it has been many years, and it feels smooth and silky like never before. It is really great! [strong][strong][strong] |\n\n\n--- \n*Last updated on: Tue, 21 Apr 2026 13:06:19 GMT*"
  },
  {
    "path": "docs/Support.ja.json",
    "content": "[\n  {\n    \"time\": \"2026/03/27 00:36:52\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"128.00\",\n    \"unit\": \"¥\",\n    \"message\": \"素晴らしいです、私はそれを使っています、そしてそれがどんどん良くなることを願っています。\",\n    \"name\": \"Geeson\"\n  },\n  {\n    \"time\": \"2026/04/18 21:15:46\",\n    \"item\": \"コーヒー4杯☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"サポートしてください[拳を握ります]\",\n    \"name\": \"lien\"\n  },\n  {\n    \"time\": \"2026/03/29 11:20:42\",\n    \"item\": \"コーヒー4杯☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"サポート！来て！\",\n    \"name\": \"猛将兄\"\n  },\n  {\n    \"time\": \"2026/03/27 15:05:35\",\n    \"item\": \"コーヒー4杯☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"素晴らしい仕事だ\",\n    \"name\": \"Bais\"\n  },\n  {\n    \"time\": \"2026/03/24 09:02:45\",\n    \"item\": \"コーヒー4杯☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"お疲れ様でした\",\n    \"name\": \"cc\"\n  },\n  {\n    \"time\": \"2026/03/22 12:16:07\",\n    \"item\": \"コーヒー4杯☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"バージョンの反復に対する強力なサポート\",\n    \"name\": \"cw\"\n  },\n  {\n    \"time\": \"2026/03/19 13:41:05\",\n    \"item\": \"コーヒー4杯☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"残業して更新してください\",\n    \"name\": \"背背背疼\"\n  },\n  {\n    \"time\": \"2026/03/13 17:28:40\",\n    \"item\": \"コーヒー4杯☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"オープンソースとご尽力に心より感謝いたします。このプラグインは非常に実用的です。ご支援に感謝いたします。これからも頑張ってください！ 💪\",\n    \"name\": \"一世风霜\"\n  },\n  {\n    \"time\": \"2026/03/02 09:38:26\",\n    \"item\": \"コーヒー4杯☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"とても良いです、開発は簡単ではありません、サポートしてください。\",\n    \"name\": \"xuhsu\"\n  },\n  {\n    \"time\": \"2026/01/14 15:58:04\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"88.00\",\n    \"unit\": \"¥\",\n    \"message\": \"限られた能力、敬意なし\",\n    \"name\": \"wutay\"\n  },\n  {\n    \"time\": \"2026/03/02 14:50:25\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"ありがとう！とても使いやすいです！\",\n    \"name\": \"Patrick\"\n  },\n  {\n    \"time\": \"2026/03/01 22:56:17\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"おめでとう！\",\n    \"name\": \"xday\"\n  },\n  {\n    \"time\": \"2026/03/01 22:40:23\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"ボス、プラグインはとても便利ですよヽ(*≧ω≦)ﾉ\",\n    \"name\": \"HanHaocheng\"\n  },\n  {\n    \"time\": \"2026/02/16 21:34:33\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"あけましておめでとう\",\n    \"name\": \"Jack\"\n  },\n  {\n    \"time\": \"2026/04/02 19:16:44\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"51.55\",\n    \"unit\": \"¥\",\n    \"message\": \"とても良いプラグイン\",\n    \"name\": \"小七的小洋\"\n  },\n  {\n    \"time\": \"2026/04/21 14:32:40\",\n    \"item\": \"コーヒー2杯☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"素晴らしい、宝のプラグイン、さあ\",\n    \"name\": \"安度\"\n  },\n  {\n    \"time\": \"2026/04/09 13:45:07\",\n    \"item\": \"コーヒー2杯☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"コンピュータの同期は成功しました、Android エラー コード = 305、メッセージ = パラメータの検証に失敗しました 詳細 = コンテキストが要求されています\",\n    \"name\": \"亲 yexizhu811\"\n  },\n  {\n    \"time\": \"2026/04/08 03:18:40\",\n    \"item\": \"コーヒー2杯☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"作者のおかげで、この同期方法により、マルチデバイス構成の一貫性の問題が解決されました。\",\n    \"name\": \"彼岸花\"\n  },\n  {\n    \"time\": \"2026/04/07 07:52:09\",\n    \"item\": \"コーヒー2杯☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"素晴らしいですね、本当に必要でした、ありがとうございます。\",\n    \"name\": \"tom\"\n  },\n  {\n    \"time\": \"2026/04/03 10:50:44\",\n    \"item\": \"コーヒー2杯☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"お疲れ様でした\",\n    \"name\": \"David\"\n  },\n  {\n    \"time\": \"2026/04/02 03:03:20\",\n    \"item\": \"コーヒー2杯☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"素晴らしさに対してお金を払いましょう！\",\n    \"name\": \"狗带带子\"\n  },\n  {\n    \"time\": \"2026/03/27 10:15:04\",\n    \"item\": \"コーヒー2杯☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"善良な人は安全な生活を送っている\",\n    \"name\": \"卿\"\n  },\n  {\n    \"time\": \"2026/03/18 22:57:03\",\n    \"item\": \"コーヒー2杯☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"電報のご指導ありがとうございます\",\n    \"name\": \"灰风\"\n  },\n  {\n    \"time\": \"2026/03/15 20:09:05\",\n    \"item\": \"コーヒー2杯☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"ありがとう 🙏、プラグインは使いやすいです\",\n    \"name\": \"红星 RedStar\"\n  },\n  {\n    \"time\": \"2026/03/14 23:46:58\",\n    \"item\": \"コーヒー2杯☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"全体の構成がとても良く、21世紀を感じさせてくれます。\",\n    \"name\": \"fbeis\"\n  },\n  {\n    \"time\": \"2026/03/02 21:00:43\",\n    \"item\": \"コーヒー2杯☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"ありがとう、ボス\",\n    \"name\": \"南科大小魏\"\n  },\n  {\n    \"time\": \"2026/02/28 13:56:18\",\n    \"item\": \"コーヒー2杯☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"働き者すぎるのでサポートが必要\",\n    \"name\": \"xenon\"\n  },\n  {\n    \"time\": \"2026/02/24 16:37:58\",\n    \"item\": \"コーヒー2杯☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常に優れた同期ソリューション\",\n    \"name\": \"熙熙煦煦\"\n  },\n  {\n    \"time\": \"2026/02/16 12:14:32\",\n    \"item\": \"コーヒー2杯☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"著者に感謝します、そして明けましておめでとうございます!\",\n    \"name\": \"红殇\"\n  },\n  {\n    \"time\": \"2026/02/14 18:01:55\",\n    \"item\": \"コーヒー2杯☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"同期の問題を解決する素晴らしいプラグインを開発していただきありがとうございます\",\n    \"name\": \"Jacky龙\"\n  },\n  {\n    \"time\": \"2026/02/04 10:41:49\",\n    \"item\": \"コーヒー2杯☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"同期機能はとても便利です。今後もnoteの安全性を重視して繰り返し改善していきたいと思っています。\",\n    \"name\": \"咕咕咕\"\n  },\n  {\n    \"time\": \"2026/01/31 16:59:55\",\n    \"item\": \"コーヒー2杯☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"よく開発されており、Obsidian の最大の問題点の 1 つが実際に解決されました。\",\n    \"name\": \"vulnnull\"\n  },\n  {\n    \"time\": \"2026/01/21 09:37:19\",\n    \"item\": \"コーヒー2杯☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"とても便利なObsidian同期をありがとうございます👍🏻\",\n    \"name\": \"Mojo抖音\"\n  },\n  {\n    \"time\": \"2026/01/09 16:34:10\",\n    \"item\": \"コーヒー2杯☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"男菩薩様、OBプラグインを世の中に役立たせていただきありがとうございます(^🙏^)、ケチなことは失礼です。\",\n    \"name\": \"喆\"\n  },\n  {\n    \"time\": \"2026/03/04 21:42:57\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"30.00\",\n    \"unit\": \"¥\",\n    \"message\": \"プラグインは非常に便利です、開発者に感謝します\",\n    \"name\": \"X\"\n  },\n  {\n    \"time\": \"2026/02/25 10:55:32\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"30.00\",\n    \"unit\": \"¥\",\n    \"message\": \"上司のプロジェクトはとても役に立ちました!どうもありがとうございます！皆さんもこれからも頑張ってほしいと思います\",\n    \"name\": \"jeanlaw\"\n  },\n  {\n    \"time\": \"2026/03/13 13:43:33\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"29.00\",\n    \"unit\": \"¥\",\n    \"message\": \"素晴らしい仕事です、これからも頑張ってください！\",\n    \"name\": \"rocku\"\n  },\n  {\n    \"time\": \"2026/02/24 18:36:51\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"25.00\",\n    \"unit\": \"¥\",\n    \"message\": \"私は理想的な黒曜石の同期ソリューションを見つけることができませんでした。著者に感謝します。来て！\",\n    \"name\": \"淇淇\"\n  },\n  {\n    \"time\": \"2026/04/21 13:45:19\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"とても便利です、続けてください\",\n    \"name\": \"蓬歌\"\n  },\n  {\n    \"time\": \"2026/04/20 15:37:57\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"ボスに跪きなさい🧎🏻‍♂️、今後もっとアップデートがあることを願っています\",\n    \"name\": \"riding-a-colt\"\n  },\n  {\n    \"time\": \"2026/04/17 00:15:35\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"使いやすいのでサポートしてください\",\n    \"name\": \"稻草人\"\n  },\n  {\n    \"time\": \"2026/04/15 13:41:41\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"ありがとう、ボス\",\n    \"name\": \"hitomi\"\n  },\n  {\n    \"time\": \"2026/04/15 11:22:39\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"忘れたパスワードを回復する方法\",\n    \"name\": \"woshiug\"\n  },\n  {\n    \"time\": \"2026/04/15 06:38:14\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"試す\",\n    \"name\": \"ke1078\"\n  },\n  {\n    \"time\": \"2026/04/12 23:54:45\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"素晴らしいソフトウェアです。作者の貢献とオープンソースの共有に感謝します。\",\n    \"name\": \"Nikki\"\n  },\n  {\n    \"time\": \"2026/04/12 23:40:17\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"ありがとうございます🙏🏻\",\n    \"name\": \"月非明\"\n  },\n  {\n    \"time\": \"2026/04/10 23:00:36\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"すごい、本当に便利\",\n    \"name\": \"wdysjy\"\n  },\n  {\n    \"time\": \"2026/04/04 01:33:37\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"コーヒーを一杯飲んでください。お疲れ様でした。\",\n    \"name\": \"hsonghao\"\n  },\n  {\n    \"time\": \"2026/04/03 23:42:51\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"素晴らしい、使いやすい\",\n    \"name\": \"kakaa\"\n  },\n  {\n    \"time\": \"2026/04/03 13:28:02\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"とても便利です、ありがとう\",\n    \"name\": \"晴天小嘉\"\n  },\n  {\n    \"time\": \"2026/04/02 23:08:13\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"ありがとう、これからも頑張ってほしい\",\n    \"name\": \"畅\"\n  },\n  {\n    \"time\": \"2026/04/02 15:13:46\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"コーヒーをおごってください、このプロジェクトはとても役に立ちます\",\n    \"name\": \"dove\"\n  },\n  {\n    \"time\": \"2026/03/30 23:34:12\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"さあ⛽️\",\n    \"name\": \"andie\"\n  },\n  {\n    \"time\": \"2026/03/28 12:46:23\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"本当にありがとう、お疲れ様でした\",\n    \"name\": \"zhengbiubiu\"\n  },\n  {\n    \"time\": \"2026/03/27 19:45:57\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"高速ノート同期👍\",\n    \"name\": \"IsaacSuo\"\n  },\n  {\n    \"time\": \"2026/03/26 13:22:50\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"うわー👍\",\n    \"name\": \"dawn\"\n  },\n  {\n    \"time\": \"2026/03/24 14:45:21\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"ありがとう、このソフトウェアはとても便利です。\",\n    \"name\": \"Bean\"\n  },\n  {\n    \"time\": \"2026/03/22 23:08:14\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"尊敬\",\n    \"name\": \"拾感\"\n  },\n  {\n    \"time\": \"2026/03/19 22:47:54\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"ありがとうございます🙏！素晴らしい！\",\n    \"name\": \"jediknight\"\n  },\n  {\n    \"time\": \"2026/03/19 20:20:17\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"開発者のおかげで、とても使いやすくなりました。ぜひ！\",\n    \"name\": \"Fcjd\"\n  },\n  {\n    \"time\": \"2026/03/17 01:17:02\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"素晴らしい仕事です。シンプルかつシンプルです。引き続き最適化を続けていきたいと思います~\",\n    \"name\": \"southzen\"\n  },\n  {\n    \"time\": \"2026/03/16 16:24:11\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"とても便利ですにゃ～ありがとうにゃ～\",\n    \"name\": \"长筱团子\"\n  },\n  {\n    \"time\": \"2026/03/16 09:22:14\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"敬意がない^_^\",\n    \"name\": \"barry\"\n  },\n  {\n    \"time\": \"2026/03/16 01:01:12\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"オープンソースの世界への貢献に感謝します\",\n    \"name\": \"Stone\"\n  },\n  {\n    \"time\": \"2026/03/14 17:15:45\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"作者に感謝します、失礼ではありません\",\n    \"name\": \"R M\"\n  },\n  {\n    \"time\": \"2026/03/14 00:39:54\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"ボスNB、ありがとう、ありがとう\",\n    \"name\": \"T0_欣\"\n  },\n  {\n    \"time\": \"2026/03/11 20:05:58\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"プラグインをありがとう。とても便利で便利です。\",\n    \"name\": \"Ucat\"\n  },\n  {\n    \"time\": \"2026/03/10 01:08:20\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"素晴らしい\",\n    \"name\": \"la\"\n  },\n  {\n    \"time\": \"2026/03/09 09:59:44\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"使いやすさ、サポート\",\n    \"name\": \"耀/\"\n  },\n  {\n    \"time\": \"2026/03/07 23:36:38\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常に優れたプラグインのサポート\",\n    \"name\": \"阿叶\"\n  },\n  {\n    \"time\": \"2026/03/06 09:58:58\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"ありがとう\",\n    \"name\": \"Dylan\"\n  },\n  {\n    \"time\": \"2026/03/03 00:53:09\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"コードを書いてオープンソースにするために昼も夜も懸命に働いてくれた作者に感謝します\",\n    \"name\": \"Alan\"\n  },\n  {\n    \"time\": \"2026/03/01 21:10:27\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"もっと完全になって、クラウド上で他の形式の ob ファイルを表示できるようになることを願っています。\",\n    \"name\": \"Jack ☑️\"\n  },\n  {\n    \"time\": \"2026/02/28 09:50:58\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"このプラグインのアイデアはとても良いです、これからも頑張ってください\",\n    \"name\": \"tangdh\"\n  },\n  {\n    \"time\": \"2026/02/27 12:48:50\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常に優れた同期プラグイン\",\n    \"name\": \"aban\"\n  },\n  {\n    \"time\": \"2026/02/27 11:37:18\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"お疲れ様です\",\n    \"name\": \"三岁\"\n  },\n  {\n    \"time\": \"2026/02/27 11:19:13\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"強すぎる\",\n    \"name\": \"行长\"\n  },\n  {\n    \"time\": \"2026/02/26 11:16:25\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"このような良いことをもっと多くの人に知ってもらい、宣伝活動を強化すべきです！\",\n    \"name\": \"fausto\"\n  },\n  {\n    \"time\": \"2026/02/25 20:17:42\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"とても使いやすいです。このような良いツールを開発してくれた作者に感謝します。\",\n    \"name\": \"woloin\"\n  },\n  {\n    \"time\": \"2026/02/24 18:29:45\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"私の黒曜石を回転させる、このような便利なプラグインを開発してくれた作者に感謝します🥰\",\n    \"name\": \"kimi\"\n  },\n  {\n    \"time\": \"2026/02/24 10:01:37\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"プラグインは使いやすい\",\n    \"name\": \"ccsir\"\n  },\n  {\n    \"time\": \"2026/02/23 20:53:08\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"しばらく使っていますが、本当に素晴らしいです。\",\n    \"name\": \"KevinYAN\"\n  },\n  {\n    \"time\": \"2026/02/23 19:32:03\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"午年おめでとう\",\n    \"name\": \"繁星影月\"\n  },\n  {\n    \"time\": \"2026/02/23 12:30:55\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"お疲れ様でした\",\n    \"name\": \"ahto\"\n  },\n  {\n    \"time\": \"2026/02/23 06:24:55\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"とても助かりました、ぜひ！\",\n    \"name\": \"大学生\"\n  },\n  {\n    \"time\": \"2026/02/15 09:25:17\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"サポート、さあ～、とても実用的で需要の高い機能です。 AI時代にとても役立つ\",\n    \"name\": \"sfsun67\"\n  },\n  {\n    \"time\": \"2026/02/10 23:21:38\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"ボスが作成した同期プラグインに感謝します。使い方はとても簡単です。まずはコーヒーを一杯飲んでください。後ほどご褒美をあげます。\",\n    \"name\": \"toby\"\n  },\n  {\n    \"time\": \"2026/02/10 11:41:36\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Bossの同期プラグインは非常に優れています。これからも応援していきます。\",\n    \"name\": \"WONG\"\n  },\n  {\n    \"time\": \"2026/02/08 22:02:32\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"とても便利です、ありがとう\",\n    \"name\": \"小迪\"\n  },\n  {\n    \"time\": \"2026/02/06 08:42:49\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"さあ💪ウェブ上の画像編集機能を改善してください\",\n    \"name\": \"Max\"\n  },\n  {\n    \"time\": \"2026/01/28 10:48:02\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"来て\",\n    \"name\": \"通\"\n  },\n  {\n    \"time\": \"2026/01/26 17:21:27\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"-\",\n    \"name\": \"CloseCV\"\n  },\n  {\n    \"time\": \"2026/01/16 11:47:13\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常に便利で、今後の開発と最適化が楽しみです。ありがたい。\",\n    \"name\": \"苏\"\n  },\n  {\n    \"time\": \"2026/01/15 14:51:11\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"とても便利です。ありがとう！\",\n    \"name\": \"灰风\"\n  },\n  {\n    \"time\": \"2026/01/09 18:12:17\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"プラグインのアイデアはとても正しいです\",\n    \"name\": \"xix\"\n  },\n  {\n    \"time\": \"2026/01/03 22:44:43\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"どんどん良くなっていきますように👌🏻\",\n    \"name\": \"姚朝伟\"\n  },\n  {\n    \"time\": \"2026/01/03 14:58:43\",\n    \"item\": \"コーヒー一杯☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"素晴らしい同期ソリューションです。今後に期待してください。オープンソースをありがとうございます!来て！\",\n    \"name\": \"roao\"\n  },\n  {\n    \"time\": \"2026/03/11 09:58:20\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"18.00\",\n    \"unit\": \"¥\",\n    \"message\": \"敬意を示すための小さな努力\",\n    \"name\": \"下鞅\"\n  },\n  {\n    \"time\": \"2026/03/02 21:15:39\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"さあ、神様\",\n    \"name\": \"路过打酱\"\n  },\n  {\n    \"time\": \"2026/02/28 12:27:51\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"どんどん良くなることを願っています\",\n    \"name\": \"白芷\"\n  },\n  {\n    \"time\": \"2026/02/27 15:54:55\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"開発してくれた作者に感謝し、オープンソースに感謝し、それがますます良くなることを願っています。\",\n    \"name\": \"柴特\"\n  },\n  {\n    \"time\": \"2026/02/23 15:34:53\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"ありがとう、公式同期より使いやすい\",\n    \"name\": \"Joe M\"\n  },\n  {\n    \"time\": \"2026/02/20 10:37:02\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"こんなに便利な同期ソフトを作ってくれてありがとう\",\n    \"name\": \"羽山猫四叶\"\n  },\n  {\n    \"time\": \"2026/03/22 15:09:43\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"9.90\",\n    \"unit\": \"¥\",\n    \"message\": \"素晴らしいソフトウェアです、作者に感謝します。\",\n    \"name\": \"Shifuwang\"\n  },\n  {\n    \"time\": \"2026/01/28 12:03:03\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"9.90\",\n    \"unit\": \"¥\",\n    \"message\": \"牛🐮\",\n    \"name\": \"华星\"\n  },\n  {\n    \"time\": \"2026/04/09 13:51:11\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"8.88\",\n    \"unit\": \"¥\",\n    \"message\": \"さあ…あなたには間違いなく明るい未来があります。他の友達にも勧めたいですし、素晴らしいと思います。\",\n    \"name\": \"散装白酒🍶\"\n  },\n  {\n    \"time\": \"2026/03/07 20:29:42\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"8.88\",\n    \"unit\": \"¥\",\n    \"message\": \"ありがとう、とても便利です、いいね\",\n    \"name\": \"皮皮\"\n  },\n  {\n    \"time\": \"2026/01/28 02:52:15\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"8.88\",\n    \"unit\": \"¥\",\n    \"message\": \"共有してくれてありがとう\",\n    \"name\": \"obsidian\"\n  },\n  {\n    \"time\": \"2026/02/28 19:51:59\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"8.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常に完成度が高く、サーバーインターフェイスも非常に優れています。\",\n    \"name\": \"yang\"\n  },\n  {\n    \"time\": \"2026/03/25 11:52:09\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"無敵のふりをした、はははははは\",\n    \"name\": \"东\"\n  },\n  {\n    \"time\": \"2026/03/23 01:02:18\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"開発を続けなければなりません! ！ ！\",\n    \"name\": \"wishyuwill\"\n  },\n  {\n    \"time\": \"2026/03/02 17:10:28\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"開発に感謝します。プラグインは非常に便利で、すぐに更新されます [strong]\",\n    \"name\": \"马孔多的旅人\"\n  },\n  {\n    \"time\": \"2026/02/01 23:44:27\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"愛している\",\n    \"name\": \"爱你\"\n  },\n  {\n    \"time\": \"2026/01/09 22:22:25\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"私が書いたプラグインは素晴らしいです、これからも頑張ってください!\",\n    \"name\": \"kane\"\n  },\n  {\n    \"time\": \"2026/04/20 16:34:57\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"これはとても便利です。長い間苦労してやっと卒業できた気がします(╥╯﹏╰╥)ง\",\n    \"name\": \"david\"\n  },\n  {\n    \"time\": \"2026/04/15 00:48:38\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"簡単ではない\",\n    \"name\": \"ben\"\n  },\n  {\n    \"time\": \"2026/04/06 20:57:36\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"プロジェクトにご協力いただきありがとうございます。これからは黒曜石の同期が簡単になります。\",\n    \"name\": \"octobersky\"\n  },\n  {\n    \"time\": \"2026/03/01 00:28:19\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"素晴らしい発展\",\n    \"name\": \"colorednoise\"\n  },\n  {\n    \"time\": \"2026/02/27 15:18:26\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"サポート\",\n    \"name\": \"wudibaolong\"\n  },\n  {\n    \"time\": \"2026/02/14 02:32:50\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"このような便利なプラグインを開発していただきありがとうございます\",\n    \"name\": \"支持开源精神\"\n  },\n  {\n    \"time\": \"2026/02/13 02:13:10\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"褒美\",\n    \"name\": \"xxx\"\n  },\n  {\n    \"time\": \"2026/02/11 17:07:02\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"来て\",\n    \"name\": \"Acckion\"\n  },\n  {\n    \"time\": \"2026/01/11 14:20:34\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"とても便利です。 git を使用してビルドできれば幸いです。\",\n    \"name\": \"安宁\"\n  },\n  {\n    \"time\": \"2026/04/18 16:59:14\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"3.66\",\n    \"unit\": \"¥\",\n    \"message\": \"これからも良い製品を開発し、作り続けてください\",\n    \"name\": \"jeremy\"\n  },\n  {\n    \"time\": \"2026/02/23 17:47:46\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"3.00\",\n    \"unit\": \"¥\",\n    \"message\": \"あけましておめでとう\",\n    \"name\": \"LL\"\n  },\n  {\n    \"time\": \"2026/04/11 12:28:18\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"試す\",\n    \"name\": \"ke1078\"\n  },\n  {\n    \"time\": \"2026/03/26 15:03:40\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"ありがとう、とても便利です\",\n    \"name\": \"guanyingquan\"\n  },\n  {\n    \"time\": \"2026/02/24 20:15:19\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常に役立つ Obs 同期プラグインをありがとう!\",\n    \"name\": \"Jimmy\"\n  },\n  {\n    \"time\": \"2026/01/08 15:18:06\",\n    \"item\": \"チップはお好みで\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"発見から展開、使用まで何年も経ちましたが、今までにないほど滑らかで滑らかな感触です。本当に素晴らしいです！ [強い][強い][強い]\",\n    \"name\": \"用户\"\n  }\n]"
  },
  {
    "path": "docs/Support.ja.md",
    "content": "# サポーターリスト\n\n> このプロジェクトを応援していただき、誠にありがとうございます！皆様からのご支援は、継続的なメンテナンスと開発の原動力となっています。 ❤️\n\n### 📜 謝辞リスト\n\n| 受領时间 | 项目 | 金额 | 昵称 | メッセージ |\n| :--- | :--- | :--- | :--- | :--- |\n| 2026/03/27 00:36:52 | チップはお好みで | **¥128.00** | Geeson | 素晴らしいです、私はそれを使っています、そしてそれがどんどん良くなることを願っています。 |\n| 2026/04/18 21:15:46 | コーヒー4杯☕ | **¥100.00** | lien | サポートしてください[拳を握ります] |\n| 2026/03/29 11:20:42 | コーヒー4杯☕ | **¥100.00** | 猛将兄 | サポート！来て！ |\n| 2026/03/27 15:05:35 | コーヒー4杯☕ | **¥100.00** | Bais | 素晴らしい仕事だ |\n| 2026/03/24 09:02:45 | コーヒー4杯☕ | **¥100.00** | cc | お疲れ様でした |\n| 2026/03/22 12:16:07 | コーヒー4杯☕ | **¥100.00** | cw | バージョンの反復に対する強力なサポート |\n| 2026/03/19 13:41:05 | コーヒー4杯☕ | **¥100.00** | 背背背疼 | 残業して更新してください |\n| 2026/03/13 17:28:40 | コーヒー4杯☕ | **¥100.00** | 一世风霜 | オープンソースとご尽力に心より感謝いたします。このプラグインは非常に実用的です。ご支援に感謝いたします。これからも頑張ってください！ 💪 |\n| 2026/03/02 09:38:26 | コーヒー4杯☕ | **¥100.00** | xuhsu | とても良いです、開発は簡単ではありません、サポートしてください。 |\n| 2026/01/14 15:58:04 | チップはお好みで | **¥88.00** | wutay | 限られた能力、敬意なし |\n| 2026/03/02 14:50:25 | チップはお好みで | **¥66.00** | Patrick | ありがとう！とても使いやすいです！ |\n| 2026/03/01 22:56:17 | チップはお好みで | **¥66.00** | xday | おめでとう！ |\n| 2026/03/01 22:40:23 | チップはお好みで | **¥66.00** | HanHaocheng | ボス、プラグインはとても便利ですよヽ(*≧ω≦)ﾉ |\n| 2026/02/16 21:34:33 | チップはお好みで | **¥66.00** | Jack | あけましておめでとう |\n| 2026/04/02 19:16:44 | チップはお好みで | **¥51.55** | 小七的小洋 | とても良いプラグイン |\n| 2026/04/21 14:32:40 | コーヒー2杯☕ | **¥50.00** | 安度 | 素晴らしい、宝のプラグイン、さあ |\n| 2026/04/09 13:45:07 | コーヒー2杯☕ | **¥50.00** | 亲 yexizhu811 | コンピュータの同期は成功しました、Android エラー コード = 305、メッセージ = パラメータの検証に失敗しました 詳細 = コンテキストが要求されています |\n| 2026/04/08 03:18:40 | コーヒー2杯☕ | **¥50.00** | 彼岸花 | 作者のおかげで、この同期方法により、マルチデバイス構成の一貫性の問題が解決されました。 |\n| 2026/04/07 07:52:09 | コーヒー2杯☕ | **¥50.00** | tom | 素晴らしいですね、本当に必要でした、ありがとうございます。 |\n| 2026/04/03 10:50:44 | コーヒー2杯☕ | **¥50.00** | David | お疲れ様でした |\n| 2026/04/02 03:03:20 | コーヒー2杯☕ | **¥50.00** | 狗带带子 | 素晴らしさに対してお金を払いましょう！ |\n| 2026/03/27 10:15:04 | コーヒー2杯☕ | **¥50.00** | 卿 | 善良な人は安全な生活を送っている |\n| 2026/03/18 22:57:03 | コーヒー2杯☕ | **¥50.00** | 灰风 | 電報のご指導ありがとうございます |\n| 2026/03/15 20:09:05 | コーヒー2杯☕ | **¥50.00** | 红星 RedStar | ありがとう 🙏、プラグインは使いやすいです |\n| 2026/03/14 23:46:58 | コーヒー2杯☕ | **¥50.00** | fbeis | 全体の構成がとても良く、21世紀を感じさせてくれます。 |\n| 2026/03/02 21:00:43 | コーヒー2杯☕ | **¥50.00** | 南科大小魏 | ありがとう、ボス |\n| 2026/02/28 13:56:18 | コーヒー2杯☕ | **¥50.00** | xenon | 働き者すぎるのでサポートが必要 |\n| 2026/02/24 16:37:58 | コーヒー2杯☕ | **¥50.00** | 熙熙煦煦 | 非常に優れた同期ソリューション |\n| 2026/02/16 12:14:32 | コーヒー2杯☕ | **¥50.00** | 红殇 | 著者に感謝します、そして明けましておめでとうございます! |\n| 2026/02/14 18:01:55 | コーヒー2杯☕ | **¥50.00** | Jacky龙 | 同期の問題を解決する素晴らしいプラグインを開発していただきありがとうございます |\n| 2026/02/04 10:41:49 | コーヒー2杯☕ | **¥50.00** | 咕咕咕 | 同期機能はとても便利です。今後もnoteの安全性を重視して繰り返し改善していきたいと思っています。 |\n| 2026/01/31 16:59:55 | コーヒー2杯☕ | **¥50.00** | vulnnull | よく開発されており、Obsidian の最大の問題点の 1 つが実際に解決されました。 |\n| 2026/01/21 09:37:19 | コーヒー2杯☕ | **¥50.00** | Mojo抖音 | とても便利なObsidian同期をありがとうございます👍🏻 |\n| 2026/01/09 16:34:10 | コーヒー2杯☕ | **¥50.00** | 喆 | 男菩薩様、OBプラグインを世の中に役立たせていただきありがとうございます(^🙏^)、ケチなことは失礼です。 |\n| 2026/03/04 21:42:57 | チップはお好みで | **¥30.00** | X | プラグインは非常に便利です、開発者に感謝します |\n| 2026/02/25 10:55:32 | チップはお好みで | **¥30.00** | jeanlaw | 上司のプロジェクトはとても役に立ちました!どうもありがとうございます！皆さんもこれからも頑張ってほしいと思います |\n| 2026/03/13 13:43:33 | チップはお好みで | **¥29.00** | rocku | 素晴らしい仕事です、これからも頑張ってください！ |\n| 2026/02/24 18:36:51 | チップはお好みで | **¥25.00** | 淇淇 | 私は理想的な黒曜石の同期ソリューションを見つけることができませんでした。著者に感謝します。来て！ |\n| 2026/04/21 13:45:19 | コーヒー一杯☕ | **¥20.00** | 蓬歌 | とても便利です、続けてください |\n| 2026/04/20 15:37:57 | コーヒー一杯☕ | **¥20.00** | riding-a-colt | ボスに跪きなさい🧎🏻‍♂️、今後もっとアップデートがあることを願っています |\n| 2026/04/17 00:15:35 | コーヒー一杯☕ | **¥20.00** | 稻草人 | 使いやすいのでサポートしてください |\n| 2026/04/15 13:41:41 | コーヒー一杯☕ | **¥20.00** | hitomi | ありがとう、ボス |\n| 2026/04/15 11:22:39 | コーヒー一杯☕ | **¥20.00** | woshiug | 忘れたパスワードを回復する方法 |\n| 2026/04/15 06:38:14 | コーヒー一杯☕ | **¥20.00** | ke1078 | 試す |\n| 2026/04/12 23:54:45 | コーヒー一杯☕ | **¥20.00** | Nikki | 素晴らしいソフトウェアです。作者の貢献とオープンソースの共有に感謝します。 |\n| 2026/04/12 23:40:17 | コーヒー一杯☕ | **¥20.00** | 月非明 | ありがとうございます🙏🏻 |\n| 2026/04/10 23:00:36 | コーヒー一杯☕ | **¥20.00** | wdysjy | すごい、本当に便利 |\n| 2026/04/04 01:33:37 | コーヒー一杯☕ | **¥20.00** | hsonghao | コーヒーを一杯飲んでください。お疲れ様でした。 |\n| 2026/04/03 23:42:51 | コーヒー一杯☕ | **¥20.00** | kakaa | 素晴らしい、使いやすい |\n| 2026/04/03 13:28:02 | コーヒー一杯☕ | **¥20.00** | 晴天小嘉 | とても便利です、ありがとう |\n| 2026/04/02 23:08:13 | コーヒー一杯☕ | **¥20.00** | 畅 | ありがとう、これからも頑張ってほしい |\n| 2026/04/02 15:13:46 | コーヒー一杯☕ | **¥20.00** | dove | コーヒーをおごってください、このプロジェクトはとても役に立ちます |\n| 2026/03/30 23:34:12 | コーヒー一杯☕ | **¥20.00** | andie | さあ⛽️ |\n| 2026/03/28 12:46:23 | コーヒー一杯☕ | **¥20.00** | zhengbiubiu | 本当にありがとう、お疲れ様でした |\n| 2026/03/27 19:45:57 | コーヒー一杯☕ | **¥20.00** | IsaacSuo | 高速ノート同期👍 |\n| 2026/03/26 13:22:50 | コーヒー一杯☕ | **¥20.00** | dawn | うわー👍 |\n| 2026/03/24 14:45:21 | コーヒー一杯☕ | **¥20.00** | Bean | ありがとう、このソフトウェアはとても便利です。 |\n| 2026/03/22 23:08:14 | コーヒー一杯☕ | **¥20.00** | 拾感 | 尊敬 |\n| 2026/03/19 22:47:54 | コーヒー一杯☕ | **¥20.00** | jediknight | ありがとうございます🙏！素晴らしい！ |\n| 2026/03/19 20:20:17 | コーヒー一杯☕ | **¥20.00** | Fcjd | 開発者のおかげで、とても使いやすくなりました。ぜひ！ |\n| 2026/03/17 01:17:02 | コーヒー一杯☕ | **¥20.00** | southzen | 素晴らしい仕事です。シンプルかつシンプルです。引き続き最適化を続けていきたいと思います~ |\n| 2026/03/16 16:24:11 | コーヒー一杯☕ | **¥20.00** | 长筱团子 | とても便利ですにゃ～ありがとうにゃ～ |\n| 2026/03/16 09:22:14 | コーヒー一杯☕ | **¥20.00** | barry | 敬意がない^_^ |\n| 2026/03/16 01:01:12 | コーヒー一杯☕ | **¥20.00** | Stone | オープンソースの世界への貢献に感謝します |\n| 2026/03/14 17:15:45 | コーヒー一杯☕ | **¥20.00** | R M | 作者に感謝します、失礼ではありません |\n| 2026/03/14 00:39:54 | コーヒー一杯☕ | **¥20.00** | T0_欣 | ボスNB、ありがとう、ありがとう |\n| 2026/03/11 20:05:58 | コーヒー一杯☕ | **¥20.00** | Ucat | プラグインをありがとう。とても便利で便利です。 |\n| 2026/03/10 01:08:20 | コーヒー一杯☕ | **¥20.00** | la | 素晴らしい |\n| 2026/03/09 09:59:44 | コーヒー一杯☕ | **¥20.00** | 耀/ | 使いやすさ、サポート |\n| 2026/03/07 23:36:38 | コーヒー一杯☕ | **¥20.00** | 阿叶 | 非常に優れたプラグインのサポート |\n| 2026/03/06 09:58:58 | コーヒー一杯☕ | **¥20.00** | Dylan | ありがとう |\n| 2026/03/03 00:53:09 | コーヒー一杯☕ | **¥20.00** | Alan | コードを書いてオープンソースにするために昼も夜も懸命に働いてくれた作者に感謝します |\n| 2026/03/01 21:10:27 | コーヒー一杯☕ | **¥20.00** | Jack ☑️ | もっと完全になって、クラウド上で他の形式の ob ファイルを表示できるようになることを願っています。 |\n| 2026/02/28 09:50:58 | コーヒー一杯☕ | **¥20.00** | tangdh | このプラグインのアイデアはとても良いです、これからも頑張ってください |\n| 2026/02/27 12:48:50 | コーヒー一杯☕ | **¥20.00** | aban | 非常に優れた同期プラグイン |\n| 2026/02/27 11:37:18 | コーヒー一杯☕ | **¥20.00** | 三岁 | お疲れ様です |\n| 2026/02/27 11:19:13 | コーヒー一杯☕ | **¥20.00** | 行长 | 強すぎる |\n| 2026/02/26 11:16:25 | コーヒー一杯☕ | **¥20.00** | fausto | このような良いことをもっと多くの人に知ってもらい、宣伝活動を強化すべきです！ |\n| 2026/02/25 20:17:42 | コーヒー一杯☕ | **¥20.00** | woloin | とても使いやすいです。このような良いツールを開発してくれた作者に感謝します。 |\n| 2026/02/24 18:29:45 | コーヒー一杯☕ | **¥20.00** | kimi | 私の黒曜石を回転させる、このような便利なプラグインを開発してくれた作者に感謝します🥰 |\n| 2026/02/24 10:01:37 | コーヒー一杯☕ | **¥20.00** | ccsir | プラグインは使いやすい |\n| 2026/02/23 20:53:08 | コーヒー一杯☕ | **¥20.00** | KevinYAN | しばらく使っていますが、本当に素晴らしいです。 |\n| 2026/02/23 19:32:03 | コーヒー一杯☕ | **¥20.00** | 繁星影月 | 午年おめでとう |\n| 2026/02/23 12:30:55 | コーヒー一杯☕ | **¥20.00** | ahto | お疲れ様でした |\n| 2026/02/23 06:24:55 | コーヒー一杯☕ | **¥20.00** | 大学生 | とても助かりました、ぜひ！ |\n| 2026/02/15 09:25:17 | コーヒー一杯☕ | **¥20.00** | sfsun67 | サポート、さあ～、とても実用的で需要の高い機能です。 AI時代にとても役立つ |\n| 2026/02/10 23:21:38 | コーヒー一杯☕ | **¥20.00** | toby | ボスが作成した同期プラグインに感謝します。使い方はとても簡単です。まずはコーヒーを一杯飲んでください。後ほどご褒美をあげます。 |\n| 2026/02/10 11:41:36 | コーヒー一杯☕ | **¥20.00** | WONG | Bossの同期プラグインは非常に優れています。これからも応援していきます。 |\n| 2026/02/08 22:02:32 | コーヒー一杯☕ | **¥20.00** | 小迪 | とても便利です、ありがとう |\n| 2026/02/06 08:42:49 | コーヒー一杯☕ | **¥20.00** | Max | さあ💪ウェブ上の画像編集機能を改善してください |\n| 2026/01/28 10:48:02 | コーヒー一杯☕ | **¥20.00** | 通 | 来て |\n| 2026/01/26 17:21:27 | コーヒー一杯☕ | **¥20.00** | CloseCV | 😘 |\n| 2026/01/16 11:47:13 | コーヒー一杯☕ | **¥20.00** | 苏 | 非常に便利で、今後の開発と最適化が楽しみです。ありがたい。 |\n| 2026/01/15 14:51:11 | コーヒー一杯☕ | **¥20.00** | 灰风 | とても便利です。ありがとう！ |\n| 2026/01/09 18:12:17 | コーヒー一杯☕ | **¥20.00** | xix | プラグインのアイデアはとても正しいです |\n| 2026/01/03 22:44:43 | コーヒー一杯☕ | **¥20.00** | 姚朝伟 | どんどん良くなっていきますように👌🏻 |\n| 2026/01/03 14:58:43 | コーヒー一杯☕ | **¥20.00** | roao | 素晴らしい同期ソリューションです。今後に期待してください。オープンソースをありがとうございます!来て！ |\n| 2026/03/11 09:58:20 | チップはお好みで | **¥18.00** | 下鞅 | 敬意を示すための小さな努力 |\n| 2026/03/02 21:15:39 | チップはお好みで | **¥10.00** | 路过打酱 | さあ、神様 |\n| 2026/02/28 12:27:51 | チップはお好みで | **¥10.00** | 白芷 | どんどん良くなることを願っています |\n| 2026/02/27 15:54:55 | チップはお好みで | **¥10.00** | 柴特 | 開発してくれた作者に感謝し、オープンソースに感謝し、それがますます良くなることを願っています。 |\n| 2026/02/23 15:34:53 | チップはお好みで | **¥10.00** | Joe M | ありがとう、公式同期より使いやすい |\n| 2026/02/20 10:37:02 | チップはお好みで | **¥10.00** | 羽山猫四叶 | こんなに便利な同期ソフトを作ってくれてありがとう |\n| 2026/03/22 15:09:43 | チップはお好みで | **¥9.90** | Shifuwang | 素晴らしいソフトウェアです、作者に感謝します。 |\n| 2026/01/28 12:03:03 | チップはお好みで | **¥9.90** | 华星 | 牛🐮 |\n| 2026/04/09 13:51:11 | チップはお好みで | **¥8.88** | 散装白酒🍶 | さあ…あなたには間違いなく明るい未来があります。他の友達にも勧めたいですし、素晴らしいと思います。 |\n| 2026/03/07 20:29:42 | チップはお好みで | **¥8.88** | 皮皮 | ありがとう、とても便利です、いいね |\n| 2026/01/28 02:52:15 | チップはお好みで | **¥8.88** | obsidian | 共有してくれてありがとう |\n| 2026/02/28 19:51:59 | チップはお好みで | **¥8.00** | yang | 非常に完成度が高く、サーバーインターフェイスも非常に優れています。 |\n| 2026/03/25 11:52:09 | チップはお好みで | **¥6.66** | 东 | 無敵のふりをした、はははははは |\n| 2026/03/23 01:02:18 | チップはお好みで | **¥6.66** | wishyuwill | 開発を続けなければなりません! ！ ！ |\n| 2026/03/02 17:10:28 | チップはお好みで | **¥6.66** | 马孔多的旅人 | 開発に感謝します。プラグインは非常に便利で、すぐに更新されます [strong] |\n| 2026/02/01 23:44:27 | チップはお好みで | **¥6.66** | 爱你 | 愛している |\n| 2026/01/09 22:22:25 | チップはお好みで | **¥6.66** | kane | 私が書いたプラグインは素晴らしいです、これからも頑張ってください! |\n| 2026/04/20 16:34:57 | チップはお好みで | **¥5.00** | david | これはとても便利です。長い間苦労してやっと卒業できた気がします(╥╯﹏╰╥)ง |\n| 2026/04/15 00:48:38 | チップはお好みで | **¥5.00** | ben | 簡単ではない |\n| 2026/04/06 20:57:36 | チップはお好みで | **¥5.00** | octobersky | プロジェクトにご協力いただきありがとうございます。これからは黒曜石の同期が簡単になります。 |\n| 2026/03/01 00:28:19 | チップはお好みで | **¥5.00** | colorednoise | 素晴らしい発展 |\n| 2026/02/27 15:18:26 | チップはお好みで | **¥5.00** | wudibaolong | サポート |\n| 2026/02/14 02:32:50 | チップはお好みで | **¥5.00** | 支持开源精神 | このような便利なプラグインを開発していただきありがとうございます |\n| 2026/02/13 02:13:10 | チップはお好みで | **¥5.00** | xxx | 褒美 |\n| 2026/02/11 17:07:02 | チップはお好みで | **¥5.00** | Acckion | 来て |\n| 2026/01/11 14:20:34 | チップはお好みで | **¥5.00** | 安宁 | とても便利です。 git を使用してビルドできれば幸いです。 |\n| 2026/04/18 16:59:14 | チップはお好みで | **¥3.66** | jeremy | これからも良い製品を開発し、作り続けてください |\n| 2026/02/23 17:47:46 | チップはお好みで | **¥3.00** | LL | あけましておめでとう |\n| 2026/04/11 12:28:18 | チップはお好みで | **¥1.00** | ke1078 | 試す |\n| 2026/03/26 15:03:40 | チップはお好みで | **¥1.00** | guanyingquan | ありがとう、とても便利です |\n| 2026/02/24 20:15:19 | チップはお好みで | **¥1.00** | Jimmy | 非常に役立つ Obs 同期プラグインをありがとう! |\n| 2026/01/08 15:18:06 | チップはお好みで | **¥1.00** | 用户 | 発見から展開、使用まで何年も経ちましたが、今までにないほど滑らかで滑らかな感触です。本当に素晴らしいです！ [強い][強い][強い] |\n\n\n--- \n*最終更新日：2026/4/21 21:07:54*"
  },
  {
    "path": "docs/Support.ko.json",
    "content": "[\n  {\n    \"time\": \"2026/03/27 00:36:52\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"128.00\",\n    \"unit\": \"¥\",\n    \"message\": \"훌륭해요. 계속 사용하고 있는데, 점점 더 좋아지길 바랍니다.\",\n    \"name\": \"Geeson\"\n  },\n  {\n    \"time\": \"2026/04/18 21:15:46\",\n    \"item\": \"커피 네 잔 ☺\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"지지해줘 [주먹잡기]\",\n    \"name\": \"lien\"\n  },\n  {\n    \"time\": \"2026/03/29 11:20:42\",\n    \"item\": \"커피 네 잔 ☺\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"지원하다! 어서 해봐요!\",\n    \"name\": \"猛将兄\"\n  },\n  {\n    \"time\": \"2026/03/27 15:05:35\",\n    \"item\": \"커피 네 잔 ☺\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"잘했어요\",\n    \"name\": \"Bais\"\n  },\n  {\n    \"time\": \"2026/03/24 09:02:45\",\n    \"item\": \"커피 네 잔 ☺\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"당신의 노고에 감사드립니다\",\n    \"name\": \"cc\"\n  },\n  {\n    \"time\": \"2026/03/22 12:16:07\",\n    \"item\": \"커피 네 잔 ☺\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"버전 반복에 대한 강력한 지원\",\n    \"name\": \"cw\"\n  },\n  {\n    \"time\": \"2026/03/19 13:41:05\",\n    \"item\": \"커피 네 잔 ☺\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"가서 야근해서 업데이트해\",\n    \"name\": \"背背背疼\"\n  },\n  {\n    \"time\": \"2026/03/13 17:28:40\",\n    \"item\": \"커피 네 잔 ☺\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"여러분의 오픈소스와 노력에 진심으로 감사드립니다. 플러그인은 매우 실용적입니다. 귀하의 지원에 감사드립니다. 계속 좋은 일을 하세요! 💪\",\n    \"name\": \"一世风霜\"\n  },\n  {\n    \"time\": \"2026/03/02 09:38:26\",\n    \"item\": \"커피 네 잔 ☺\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"아주 좋습니다. 개발이 쉽지 않습니다. 지원해 주세요.\",\n    \"name\": \"xuhsu\"\n  },\n  {\n    \"time\": \"2026/01/14 15:58:04\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"88.00\",\n    \"unit\": \"¥\",\n    \"message\": \"제한된 능력, 존중 없음\",\n    \"name\": \"wutay\"\n  },\n  {\n    \"time\": \"2026/03/02 14:50:25\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"고마워요! 사용하기 매우 쉽습니다!\",\n    \"name\": \"Patrick\"\n  },\n  {\n    \"time\": \"2026/03/01 22:56:17\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"축하해요!\",\n    \"name\": \"xday\"\n  },\n  {\n    \"time\": \"2026/03/01 22:40:23\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"형님, 플러그인이 매우 유용해요ヽ(*≧ΩDF)ﾉ\",\n    \"name\": \"HanHaocheng\"\n  },\n  {\n    \"time\": \"2026/02/16 21:34:33\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"새해 복 많이 받으세요\",\n    \"name\": \"Jack\"\n  },\n  {\n    \"time\": \"2026/04/02 19:16:44\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"51.55\",\n    \"unit\": \"¥\",\n    \"message\": \"아주 좋은 플러그인\",\n    \"name\": \"小七的小洋\"\n  },\n  {\n    \"time\": \"2026/04/21 14:32:40\",\n    \"item\": \"커피 두 잔 ☺\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"훌륭해, 보물 플러그인, 어서\",\n    \"name\": \"安度\"\n  },\n  {\n    \"time\": \"2026/04/09 13:45:07\",\n    \"item\": \"커피 두 잔 ☺\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"컴퓨터 동기화가 성공했습니다. Android 오류 코드=305, 메시지=매개변수 확인 실패 세부정보=컨텍스트가 필요합니다.\",\n    \"name\": \"亲 yexizhu811\"\n  },\n  {\n    \"time\": \"2026/04/08 03:18:40\",\n    \"item\": \"커피 두 잔 ☺\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"저자 덕분에 이 동기화 방법은 다중 장치 구성 일관성 문제를 해결합니다.\",\n    \"name\": \"彼岸花\"\n  },\n  {\n    \"time\": \"2026/04/07 07:52:09\",\n    \"item\": \"커피 두 잔 ☺\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"정말 필요합니다. 감사합니다.\",\n    \"name\": \"tom\"\n  },\n  {\n    \"time\": \"2026/04/03 10:50:44\",\n    \"item\": \"커피 두 잔 ☺\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"잘했어\",\n    \"name\": \"David\"\n  },\n  {\n    \"time\": \"2026/04/02 03:03:20\",\n    \"item\": \"커피 두 잔 ☺\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"굉장함을 위해 지불하십시오!\",\n    \"name\": \"狗带带子\"\n  },\n  {\n    \"time\": \"2026/03/27 10:15:04\",\n    \"item\": \"커피 두 잔 ☺\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"좋은 사람들은 안전한 삶을 살고 있습니다\",\n    \"name\": \"卿\"\n  },\n  {\n    \"time\": \"2026/03/18 22:57:03\",\n    \"item\": \"커피 두 잔 ☺\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"텔레그램으로 안내해주셔서 감사합니다\",\n    \"name\": \"灰风\"\n  },\n  {\n    \"time\": \"2026/03/15 20:09:05\",\n    \"item\": \"커피 두 잔 ☺\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"감사합니다 🙏 플러그인은 사용하기 쉽습니다\",\n    \"name\": \"红星 RedStar\"\n  },\n  {\n    \"time\": \"2026/03/14 23:46:58\",\n    \"item\": \"커피 두 잔 ☺\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"전체적인 구조가 매우 좋고 나를 21세기로 데려다준다.\",\n    \"name\": \"fbeis\"\n  },\n  {\n    \"time\": \"2026/03/02 21:00:43\",\n    \"item\": \"커피 두 잔 ☺\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"감사합니다, 사장님\",\n    \"name\": \"南科大小魏\"\n  },\n  {\n    \"time\": \"2026/02/28 13:56:18\",\n    \"item\": \"커피 두 잔 ☺\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"너무 열심히 일하고 지원해야합니다\",\n    \"name\": \"xenon\"\n  },\n  {\n    \"time\": \"2026/02/24 16:37:58\",\n    \"item\": \"커피 두 잔 ☺\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"매우 우수한 동기화 솔루션\",\n    \"name\": \"熙熙煦煦\"\n  },\n  {\n    \"time\": \"2026/02/16 12:14:32\",\n    \"item\": \"커피 두 잔 ☺\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"작가님께 감사드리며, 새해 복 많이 받으세요!\",\n    \"name\": \"红殇\"\n  },\n  {\n    \"time\": \"2026/02/14 18:01:55\",\n    \"item\": \"커피 두 잔 ☺\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"동기화 문제를 해결하는 훌륭한 플러그인을 개발해 주셔서 감사합니다.\",\n    \"name\": \"Jacky龙\"\n  },\n  {\n    \"time\": \"2026/02/04 10:41:49\",\n    \"item\": \"커피 두 잔 ☺\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"동기화 기능은 매우 유용합니다. 노트 안전성에 중점을 두고 계속해서 반복하고 개선해 나가고 싶습니다.\",\n    \"name\": \"咕咕咕\"\n  },\n  {\n    \"time\": \"2026/01/31 16:59:55\",\n    \"item\": \"커피 두 잔 ☺\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"잘 개발되어 실제로 Obsidian의 가장 큰 문제점 중 하나를 해결했습니다!\",\n    \"name\": \"vulnnull\"\n  },\n  {\n    \"time\": \"2026/01/21 09:37:19\",\n    \"item\": \"커피 두 잔 ☺\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"매우 유용한 Obsidian 동기화에 감사드립니다👍🏻\",\n    \"name\": \"Mojo抖音\"\n  },\n  {\n    \"time\": \"2026/01/09 16:34:10\",\n    \"item\": \"커피 두 잔 ☺\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"세상을 이롭게 하는 OB 플러그인을 주신 남보살님께 감사드립니다(^🙏^), 사소한 것은 무례한 일입니다.\",\n    \"name\": \"喆\"\n  },\n  {\n    \"time\": \"2026/03/04 21:42:57\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"30.00\",\n    \"unit\": \"¥\",\n    \"message\": \"플러그인이 매우 유용합니다. 개발자님께 감사드립니다.\",\n    \"name\": \"X\"\n  },\n  {\n    \"time\": \"2026/02/25 10:55:32\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"30.00\",\n    \"unit\": \"¥\",\n    \"message\": \"사장님의 프로젝트가 저에게 많은 도움이 되었어요! 매우 감사합니다! 앞으로도 열심히 해주시기 바랍니다\",\n    \"name\": \"jeanlaw\"\n  },\n  {\n    \"time\": \"2026/03/13 13:43:33\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"29.00\",\n    \"unit\": \"¥\",\n    \"message\": \"잘했어요. 계속해서 노력하세요!\",\n    \"name\": \"rocku\"\n  },\n  {\n    \"time\": \"2026/02/24 18:36:51\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"25.00\",\n    \"unit\": \"¥\",\n    \"message\": \"나는 이상적인 흑요석 동기화 솔루션을 찾지 못했습니다. 저자에게 감사드립니다. 어서 해봐요!\",\n    \"name\": \"淇淇\"\n  },\n  {\n    \"time\": \"2026/04/21 13:45:19\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"매우 유용합니다. 계속하세요.\",\n    \"name\": \"蓬歌\"\n  },\n  {\n    \"time\": \"2026/04/20 15:37:57\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"사장님께 무릎꿇어🧎🏻‍♂️ 앞으로 더 좋은 소식이 있었으면 좋겠습니다\",\n    \"name\": \"riding-a-colt\"\n  },\n  {\n    \"time\": \"2026/04/17 00:15:35\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"사용하기 쉽고 지원하세요\",\n    \"name\": \"稻草人\"\n  },\n  {\n    \"time\": \"2026/04/15 13:41:41\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"감사합니다, 사장님\",\n    \"name\": \"hitomi\"\n  },\n  {\n    \"time\": \"2026/04/15 11:22:39\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"잊어버린 비밀번호를 찾는 방법\",\n    \"name\": \"woshiug\"\n  },\n  {\n    \"time\": \"2026/04/15 06:38:14\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"노력하다\",\n    \"name\": \"ke1078\"\n  },\n  {\n    \"time\": \"2026/04/12 23:54:45\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"훌륭한 소프트웨어입니다. 작성자의 기여와 오픈 소스 공유에 감사드립니다.\",\n    \"name\": \"Nikki\"\n  },\n  {\n    \"time\": \"2026/04/12 23:40:17\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"감사합니다🙏🏻\",\n    \"name\": \"月非明\"\n  },\n  {\n    \"time\": \"2026/04/10 23:00:36\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"대단해요 정말 유용해요\",\n    \"name\": \"wdysjy\"\n  },\n  {\n    \"time\": \"2026/04/04 01:33:37\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"커피 한잔하세요. 여러분의 노고에 감사드립니다.\",\n    \"name\": \"hsonghao\"\n  },\n  {\n    \"time\": \"2026/04/03 23:42:51\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"훌륭하고 사용하기 쉽습니다.\",\n    \"name\": \"kakaa\"\n  },\n  {\n    \"time\": \"2026/04/03 13:28:02\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"매우 유용합니다. 감사합니다.\",\n    \"name\": \"晴天小嘉\"\n  },\n  {\n    \"time\": \"2026/04/02 23:08:13\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"고마워요, 계속해서 인내할 수 있기를 바라요\",\n    \"name\": \"畅\"\n  },\n  {\n    \"time\": \"2026/04/02 15:13:46\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"커피 사주세요. 이 프로젝트는 매우 유용합니다.\",\n    \"name\": \"dove\"\n  },\n  {\n    \"time\": \"2026/03/30 23:34:12\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"어서⛽️\",\n    \"name\": \"andie\"\n  },\n  {\n    \"time\": \"2026/03/28 12:46:23\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"정말 감사합니다, 열심히 일해주셔서 감사합니다\",\n    \"name\": \"zhengbiubiu\"\n  },\n  {\n    \"time\": \"2026/03/27 19:45:57\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"빠른 노트 동기화👍\",\n    \"name\": \"IsaacSuo\"\n  },\n  {\n    \"time\": \"2026/03/26 13:22:50\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"와 👍\",\n    \"name\": \"dawn\"\n  },\n  {\n    \"time\": \"2026/03/24 14:45:21\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"감사합니다. 소프트웨어가 매우 편리합니다.\",\n    \"name\": \"Bean\"\n  },\n  {\n    \"time\": \"2026/03/22 23:08:14\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"존경\",\n    \"name\": \"拾感\"\n  },\n  {\n    \"time\": \"2026/03/19 22:47:54\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"감사합니다 🙏! 엄청난!\",\n    \"name\": \"jediknight\"\n  },\n  {\n    \"time\": \"2026/03/19 20:20:17\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"개발자님 덕분에 사용하기 너무 쉽습니다.\",\n    \"name\": \"Fcjd\"\n  },\n  {\n    \"time\": \"2026/03/17 01:17:02\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"훌륭하고 간단하고 간단합니다. 계속해서 최적화하기를 바랍니다 ~\",\n    \"name\": \"southzen\"\n  },\n  {\n    \"time\": \"2026/03/16 16:24:11\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"아주 유용해요, 야옹~고마워요, 야옹~\",\n    \"name\": \"长筱团子\"\n  },\n  {\n    \"time\": \"2026/03/16 09:22:14\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"무례해요^_^\",\n    \"name\": \"barry\"\n  },\n  {\n    \"time\": \"2026/03/16 01:01:12\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"오픈 소스 세계에 기여해 주셔서 감사합니다\",\n    \"name\": \"Stone\"\n  },\n  {\n    \"time\": \"2026/03/14 17:15:45\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"작가님 덕분에 무례함은 없어요\",\n    \"name\": \"R M\"\n  },\n  {\n    \"time\": \"2026/03/14 00:39:54\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"NB보스님, 감사합니다. 감사합니다.\",\n    \"name\": \"T0_欣\"\n  },\n  {\n    \"time\": \"2026/03/11 20:05:58\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"플러그인을 주셔서 감사합니다. 매우 유용하고 편리합니다.\",\n    \"name\": \"Ucat\"\n  },\n  {\n    \"time\": \"2026/03/10 01:08:20\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"엄청난\",\n    \"name\": \"la\"\n  },\n  {\n    \"time\": \"2026/03/09 09:59:44\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"사용하기 쉽고 지원\",\n    \"name\": \"耀/\"\n  },\n  {\n    \"time\": \"2026/03/07 23:36:38\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"아주 좋은 플러그인 지원\",\n    \"name\": \"阿叶\"\n  },\n  {\n    \"time\": \"2026/03/06 09:58:58\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"감사해요\",\n    \"name\": \"Dylan\"\n  },\n  {\n    \"time\": \"2026/03/03 00:53:09\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"코드를 작성하고 소스를 공개하기 위해 밤낮으로 열심히 일한 작성자에게 감사드립니다.\",\n    \"name\": \"Alan\"\n  },\n  {\n    \"time\": \"2026/03/01 21:10:27\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"좀 더 완벽해지고 클라우드에서 다른 형식의 ob 파일을 볼 수 있기를 바랍니다.\",\n    \"name\": \"Jack ☑️\"\n  },\n  {\n    \"time\": \"2026/02/28 09:50:58\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"이 플러그인의 아이디어는 매우 좋습니다. 계속해서 좋은 작업을 해주세요.\",\n    \"name\": \"tangdh\"\n  },\n  {\n    \"time\": \"2026/02/27 12:48:50\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"아주 좋은 동기화 플러그인\",\n    \"name\": \"aban\"\n  },\n  {\n    \"time\": \"2026/02/27 11:37:18\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"당신의 일에 감사드립니다\",\n    \"name\": \"三岁\"\n  },\n  {\n    \"time\": \"2026/02/27 11:19:13\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"너무 강하다\",\n    \"name\": \"行长\"\n  },\n  {\n    \"time\": \"2026/02/26 11:16:25\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"이런 좋은 사실을 더 많은 사람들에게 알리고 홍보에 더욱 힘써야겠습니다!\",\n    \"name\": \"fausto\"\n  },\n  {\n    \"time\": \"2026/02/25 20:17:42\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"사용하기 매우 쉽습니다. 이렇게 좋은 도구를 개발해 주신 작성자에게 감사드립니다!\",\n    \"name\": \"woloin\"\n  },\n  {\n    \"time\": \"2026/02/24 18:29:45\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"흑요석을 회전하게 해주는 유용한 플러그인을 개발해주신 작성자님께 감사드립니다🥰\",\n    \"name\": \"kimi\"\n  },\n  {\n    \"time\": \"2026/02/24 10:01:37\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"플러그인은 사용하기 쉽습니다\",\n    \"name\": \"ccsir\"\n  },\n  {\n    \"time\": \"2026/02/23 20:53:08\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"한동안 사용해봤는데 정말 좋습니다.\",\n    \"name\": \"KevinYAN\"\n  },\n  {\n    \"time\": \"2026/02/23 19:32:03\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"말의 해를 맞아\",\n    \"name\": \"繁星影月\"\n  },\n  {\n    \"time\": \"2026/02/23 12:30:55\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"당신의 노고에 감사드립니다\",\n    \"name\": \"ahto\"\n  },\n  {\n    \"time\": \"2026/02/23 06:24:55\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"매우 도움이 됩니다. 어서요!\",\n    \"name\": \"大学生\"\n  },\n  {\n    \"time\": \"2026/02/15 09:25:17\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"지원하세요~ 매우 실용적이고 수요가 많은 기능입니다. AI 시대에 매우 유용\",\n    \"name\": \"sfsun67\"\n  },\n  {\n    \"time\": \"2026/02/10 23:21:38\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"사장님이 만들어주신 동기화 플러그인 감사합니다. 사용하기가 매우 쉽습니다. 먼저 커피 한잔 드시고 나중에 계속 보답해드리겠습니다.\",\n    \"name\": \"toby\"\n  },\n  {\n    \"time\": \"2026/02/10 11:41:36\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"보스의 동기화 플러그인은 매우 좋습니다. 나는 그것을 계속 지원할 것입니다.\",\n    \"name\": \"WONG\"\n  },\n  {\n    \"time\": \"2026/02/08 22:02:32\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"매우 유용합니다. 감사합니다.\",\n    \"name\": \"小迪\"\n  },\n  {\n    \"time\": \"2026/02/06 08:42:49\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"어서💪 웹에서 이미지 편집 기능을 개선해보세요\",\n    \"name\": \"Max\"\n  },\n  {\n    \"time\": \"2026/01/28 10:48:02\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"어서 해봐요\",\n    \"name\": \"通\"\n  },\n  {\n    \"time\": \"2026/01/26 17:21:27\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"-\",\n    \"name\": \"CloseCV\"\n  },\n  {\n    \"time\": \"2026/01/16 11:47:13\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"매우 유용하며 후속 개발 및 최적화를 기대합니다. 고마워하는.\",\n    \"name\": \"苏\"\n  },\n  {\n    \"time\": \"2026/01/15 14:51:11\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"매우 유용합니다. 감사해요!\",\n    \"name\": \"灰风\"\n  },\n  {\n    \"time\": \"2026/01/09 18:12:17\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"플러그인 아이디어가 너무 옳습니다\",\n    \"name\": \"xix\"\n  },\n  {\n    \"time\": \"2026/01/03 22:44:43\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"점점 더 좋아지길 바랍니다👌🏻\",\n    \"name\": \"姚朝伟\"\n  },\n  {\n    \"time\": \"2026/01/03 14:58:43\",\n    \"item\": \"커피한잔☺\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"훌륭한 동기화 솔루션, 미래를 기대합니다! 오픈소스 정말 감사드립니다! 어서 해봐요!\",\n    \"name\": \"roao\"\n  },\n  {\n    \"time\": \"2026/03/11 09:58:20\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"18.00\",\n    \"unit\": \"¥\",\n    \"message\": \"존경심을 표현하기 위한 작은 노력\",\n    \"name\": \"下鞅\"\n  },\n  {\n    \"time\": \"2026/03/02 21:15:39\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"어서, 하느님\",\n    \"name\": \"路过打酱\"\n  },\n  {\n    \"time\": \"2026/02/28 12:27:51\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"점점 좋아지길 바라요\",\n    \"name\": \"白芷\"\n  },\n  {\n    \"time\": \"2026/02/27 15:54:55\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"개발을 해주신 작성자에게 감사드리며, 오픈 소스에 감사드리며, 더욱 더 좋아지기를 바랍니다.\",\n    \"name\": \"柴特\"\n  },\n  {\n    \"time\": \"2026/02/23 15:34:53\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"감사합니다. 공식 동기화보다 사용하기가 더 쉽습니다.\",\n    \"name\": \"Joe M\"\n  },\n  {\n    \"time\": \"2026/02/20 10:37:02\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"이렇게 편리한 동기화 소프트웨어를 만들어주셔서 감사합니다\",\n    \"name\": \"羽山猫四叶\"\n  },\n  {\n    \"time\": \"2026/03/22 15:09:43\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"9.90\",\n    \"unit\": \"¥\",\n    \"message\": \"작성자에게 감사드립니다. 훌륭한 소프트웨어입니다.\",\n    \"name\": \"Shifuwang\"\n  },\n  {\n    \"time\": \"2026/01/28 12:03:03\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"9.90\",\n    \"unit\": \"¥\",\n    \"message\": \"소🐮\",\n    \"name\": \"华星\"\n  },\n  {\n    \"time\": \"2026/04/09 13:51:11\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"8.88\",\n    \"unit\": \"¥\",\n    \"message\": \"어서...당신의 미래는 분명 밝습니다. 다른 친구들에게도 추천하고 좋을 것 같아요.\",\n    \"name\": \"散装白酒🍶\"\n  },\n  {\n    \"time\": \"2026/03/07 20:29:42\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"8.88\",\n    \"unit\": \"¥\",\n    \"message\": \"고마워요, 정말 유용해요. 좋아요\",\n    \"name\": \"皮皮\"\n  },\n  {\n    \"time\": \"2026/01/28 02:52:15\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"8.88\",\n    \"unit\": \"¥\",\n    \"message\": \"공유해주셔서 감사합니다\",\n    \"name\": \"obsidian\"\n  },\n  {\n    \"time\": \"2026/02/28 19:51:59\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"8.00\",\n    \"unit\": \"¥\",\n    \"message\": \"매우 완벽하고 서버 인터페이스도 매우 훌륭합니다.\",\n    \"name\": \"yang\"\n  },\n  {\n    \"time\": \"2026/03/25 11:52:09\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"무적인 척 하하하하하하\",\n    \"name\": \"东\"\n  },\n  {\n    \"time\": \"2026/03/23 01:02:18\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"계속 발전해야 해요! ! !\",\n    \"name\": \"wishyuwill\"\n  },\n  {\n    \"time\": \"2026/03/02 17:10:28\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"개발해주셔서 감사합니다. 플러그인은 매우 유용하며 빠르게 업데이트될 예정입니다. [strong]\",\n    \"name\": \"马孔多的旅人\"\n  },\n  {\n    \"time\": \"2026/02/01 23:44:27\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"사랑해요\",\n    \"name\": \"爱你\"\n  },\n  {\n    \"time\": \"2026/01/09 22:22:25\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"제가 작성한 플러그인은 훌륭합니다. 계속해서 좋은 일을 해주세요!\",\n    \"name\": \"kane\"\n  },\n  {\n    \"time\": \"2026/04/20 16:34:57\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"이것은 매우 유용합니다. 오랜 시간 고생 끝에 드디어 졸업한 기분이에요 (╥╯﹏╰╥)§\",\n    \"name\": \"david\"\n  },\n  {\n    \"time\": \"2026/04/15 00:48:38\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"쉽지 않다\",\n    \"name\": \"ben\"\n  },\n  {\n    \"time\": \"2026/04/06 20:57:36\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"프로젝트에 도움을 주셔서 감사합니다. 이제부터 흑요석 동기화가 쉬워집니다.\",\n    \"name\": \"octobersky\"\n  },\n  {\n    \"time\": \"2026/03/01 00:28:19\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"대단한 발전\",\n    \"name\": \"colorednoise\"\n  },\n  {\n    \"time\": \"2026/02/27 15:18:26\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"지원하다\",\n    \"name\": \"wudibaolong\"\n  },\n  {\n    \"time\": \"2026/02/14 02:32:50\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"유용한 플러그인을 개발해주셔서 감사합니다\",\n    \"name\": \"支持开源精神\"\n  },\n  {\n    \"time\": \"2026/02/13 02:13:10\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"보상\",\n    \"name\": \"xxx\"\n  },\n  {\n    \"time\": \"2026/02/11 17:07:02\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"어서 해봐요\",\n    \"name\": \"Acckion\"\n  },\n  {\n    \"time\": \"2026/01/11 14:20:34\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"매우 유용합니다. git을 사용하여 빌드할 수 있기를 바랍니다.\",\n    \"name\": \"安宁\"\n  },\n  {\n    \"time\": \"2026/04/18 16:59:14\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"3.66\",\n    \"unit\": \"¥\",\n    \"message\": \"앞으로도 좋은 제품을 개발하고 만들어주세요\",\n    \"name\": \"jeremy\"\n  },\n  {\n    \"time\": \"2026/02/23 17:47:46\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"3.00\",\n    \"unit\": \"¥\",\n    \"message\": \"새해 복 많이 받으세요\",\n    \"name\": \"LL\"\n  },\n  {\n    \"time\": \"2026/04/11 12:28:18\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"노력하다\",\n    \"name\": \"ke1078\"\n  },\n  {\n    \"time\": \"2026/03/26 15:03:40\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"고마워요, 매우 유용해요\",\n    \"name\": \"guanyingquan\"\n  },\n  {\n    \"time\": \"2026/02/24 20:15:19\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"매우 유용한 Obs 동기화 플러그인에 감사드립니다!\",\n    \"name\": \"Jimmy\"\n  },\n  {\n    \"time\": \"2026/01/08 15:18:06\",\n    \"item\": \"원하는 대로 팁을 주세요\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"발견과 배포부터 사용까지 수년이 걸렸으며 이전과는 비교할 수 없을 정도로 부드럽고 매끄러운 느낌을 줍니다. 정말 훌륭해요! [강함][강함][강함]\",\n    \"name\": \"用户\"\n  }\n]"
  },
  {
    "path": "docs/Support.ko.md",
    "content": "# 후원자 명단\n\n> 이 프로젝트를 지원해 주셔서 정말 감사합니다! 여러분의 모든 후원은 지속적인 유지보수와 개발의 원동력이 됩니다. ❤️\n\n### 📜 감사 명단\n\n| 수령 시간 | 항목 | 금액 | 닉네임 | 메시지 |\n| :--- | :--- | :--- | :--- | :--- |\n| 2026/03/27 00:36:52 | 원하는 대로 팁을 주세요 | **¥128.00** | Geeson | 훌륭해요. 계속 사용하고 있는데, 점점 더 좋아지길 바랍니다. |\n| 2026/04/18 21:15:46 | 커피 네 잔 ☺ | **¥100.00** | lien | 지지해줘 [주먹잡기] |\n| 2026/03/29 11:20:42 | 커피 네 잔 ☺ | **¥100.00** | 猛将兄 | 지원하다! 어서 해봐요! |\n| 2026/03/27 15:05:35 | 커피 네 잔 ☺ | **¥100.00** | Bais | 잘했어요 |\n| 2026/03/24 09:02:45 | 커피 네 잔 ☺ | **¥100.00** | cc | 당신의 노고에 감사드립니다 |\n| 2026/03/22 12:16:07 | 커피 네 잔 ☺ | **¥100.00** | cw | 버전 반복에 대한 강력한 지원 |\n| 2026/03/19 13:41:05 | 커피 네 잔 ☺ | **¥100.00** | 背背背疼 | 가서 야근해서 업데이트해 |\n| 2026/03/13 17:28:40 | 커피 네 잔 ☺ | **¥100.00** | 一世风霜 | 여러분의 오픈소스와 노력에 진심으로 감사드립니다. 플러그인은 매우 실용적입니다. 귀하의 지원에 감사드립니다. 계속 좋은 일을 하세요! 💪 |\n| 2026/03/02 09:38:26 | 커피 네 잔 ☺ | **¥100.00** | xuhsu | 아주 좋습니다. 개발이 쉽지 않습니다. 지원해 주세요. |\n| 2026/01/14 15:58:04 | 원하는 대로 팁을 주세요 | **¥88.00** | wutay | 제한된 능력, 존중 없음 |\n| 2026/03/02 14:50:25 | 원하는 대로 팁을 주세요 | **¥66.00** | Patrick | 고마워요! 사용하기 매우 쉽습니다! |\n| 2026/03/01 22:56:17 | 원하는 대로 팁을 주세요 | **¥66.00** | xday | 축하해요! |\n| 2026/03/01 22:40:23 | 원하는 대로 팁을 주세요 | **¥66.00** | HanHaocheng | 형님, 플러그인이 매우 유용해요ヽ(*≧ΩDF)ﾉ |\n| 2026/02/16 21:34:33 | 원하는 대로 팁을 주세요 | **¥66.00** | Jack | 새해 복 많이 받으세요 |\n| 2026/04/02 19:16:44 | 원하는 대로 팁을 주세요 | **¥51.55** | 小七的小洋 | 아주 좋은 플러그인 |\n| 2026/04/21 14:32:40 | 커피 두 잔 ☺ | **¥50.00** | 安度 | 훌륭해, 보물 플러그인, 어서 |\n| 2026/04/09 13:45:07 | 커피 두 잔 ☺ | **¥50.00** | 亲 yexizhu811 | 컴퓨터 동기화가 성공했습니다. Android 오류 코드=305, 메시지=매개변수 확인 실패 세부정보=컨텍스트가 필요합니다. |\n| 2026/04/08 03:18:40 | 커피 두 잔 ☺ | **¥50.00** | 彼岸花 | 저자 덕분에 이 동기화 방법은 다중 장치 구성 일관성 문제를 해결합니다. |\n| 2026/04/07 07:52:09 | 커피 두 잔 ☺ | **¥50.00** | tom | 정말 필요합니다. 감사합니다. |\n| 2026/04/03 10:50:44 | 커피 두 잔 ☺ | **¥50.00** | David | 잘했어 |\n| 2026/04/02 03:03:20 | 커피 두 잔 ☺ | **¥50.00** | 狗带带子 | 굉장함을 위해 지불하십시오! |\n| 2026/03/27 10:15:04 | 커피 두 잔 ☺ | **¥50.00** | 卿 | 좋은 사람들은 안전한 삶을 살고 있습니다 |\n| 2026/03/18 22:57:03 | 커피 두 잔 ☺ | **¥50.00** | 灰风 | 텔레그램으로 안내해주셔서 감사합니다 |\n| 2026/03/15 20:09:05 | 커피 두 잔 ☺ | **¥50.00** | 红星 RedStar | 감사합니다 🙏 플러그인은 사용하기 쉽습니다 |\n| 2026/03/14 23:46:58 | 커피 두 잔 ☺ | **¥50.00** | fbeis | 전체적인 구조가 매우 좋고 나를 21세기로 데려다준다. |\n| 2026/03/02 21:00:43 | 커피 두 잔 ☺ | **¥50.00** | 南科大小魏 | 감사합니다, 사장님 |\n| 2026/02/28 13:56:18 | 커피 두 잔 ☺ | **¥50.00** | xenon | 너무 열심히 일하고 지원해야합니다 |\n| 2026/02/24 16:37:58 | 커피 두 잔 ☺ | **¥50.00** | 熙熙煦煦 | 매우 우수한 동기화 솔루션 |\n| 2026/02/16 12:14:32 | 커피 두 잔 ☺ | **¥50.00** | 红殇 | 작가님께 감사드리며, 새해 복 많이 받으세요! |\n| 2026/02/14 18:01:55 | 커피 두 잔 ☺ | **¥50.00** | Jacky龙 | 동기화 문제를 해결하는 훌륭한 플러그인을 개발해 주셔서 감사합니다. |\n| 2026/02/04 10:41:49 | 커피 두 잔 ☺ | **¥50.00** | 咕咕咕 | 동기화 기능은 매우 유용합니다. 노트 안전성에 중점을 두고 계속해서 반복하고 개선해 나가고 싶습니다. |\n| 2026/01/31 16:59:55 | 커피 두 잔 ☺ | **¥50.00** | vulnnull | 잘 개발되어 실제로 Obsidian의 가장 큰 문제점 중 하나를 해결했습니다! |\n| 2026/01/21 09:37:19 | 커피 두 잔 ☺ | **¥50.00** | Mojo抖音 | 매우 유용한 Obsidian 동기화에 감사드립니다👍🏻 |\n| 2026/01/09 16:34:10 | 커피 두 잔 ☺ | **¥50.00** | 喆 | 세상을 이롭게 하는 OB 플러그인을 주신 남보살님께 감사드립니다(^🙏^), 사소한 것은 무례한 일입니다. |\n| 2026/03/04 21:42:57 | 원하는 대로 팁을 주세요 | **¥30.00** | X | 플러그인이 매우 유용합니다. 개발자님께 감사드립니다. |\n| 2026/02/25 10:55:32 | 원하는 대로 팁을 주세요 | **¥30.00** | jeanlaw | 사장님의 프로젝트가 저에게 많은 도움이 되었어요! 매우 감사합니다! 앞으로도 열심히 해주시기 바랍니다 |\n| 2026/03/13 13:43:33 | 원하는 대로 팁을 주세요 | **¥29.00** | rocku | 잘했어요. 계속해서 노력하세요! |\n| 2026/02/24 18:36:51 | 원하는 대로 팁을 주세요 | **¥25.00** | 淇淇 | 나는 이상적인 흑요석 동기화 솔루션을 찾지 못했습니다. 저자에게 감사드립니다. 어서 해봐요! |\n| 2026/04/21 13:45:19 | 커피한잔☺ | **¥20.00** | 蓬歌 | 매우 유용합니다. 계속하세요. |\n| 2026/04/20 15:37:57 | 커피한잔☺ | **¥20.00** | riding-a-colt | 사장님께 무릎꿇어🧎🏻‍♂️ 앞으로 더 좋은 소식이 있었으면 좋겠습니다 |\n| 2026/04/17 00:15:35 | 커피한잔☺ | **¥20.00** | 稻草人 | 사용하기 쉽고 지원하세요 |\n| 2026/04/15 13:41:41 | 커피한잔☺ | **¥20.00** | hitomi | 감사합니다, 사장님 |\n| 2026/04/15 11:22:39 | 커피한잔☺ | **¥20.00** | woshiug | 잊어버린 비밀번호를 찾는 방법 |\n| 2026/04/15 06:38:14 | 커피한잔☺ | **¥20.00** | ke1078 | 노력하다 |\n| 2026/04/12 23:54:45 | 커피한잔☺ | **¥20.00** | Nikki | 훌륭한 소프트웨어입니다. 작성자의 기여와 오픈 소스 공유에 감사드립니다. |\n| 2026/04/12 23:40:17 | 커피한잔☺ | **¥20.00** | 月非明 | 감사합니다🙏🏻 |\n| 2026/04/10 23:00:36 | 커피한잔☺ | **¥20.00** | wdysjy | 대단해요 정말 유용해요 |\n| 2026/04/04 01:33:37 | 커피한잔☺ | **¥20.00** | hsonghao | 커피 한잔하세요. 여러분의 노고에 감사드립니다. |\n| 2026/04/03 23:42:51 | 커피한잔☺ | **¥20.00** | kakaa | 훌륭하고 사용하기 쉽습니다. |\n| 2026/04/03 13:28:02 | 커피한잔☺ | **¥20.00** | 晴天小嘉 | 매우 유용합니다. 감사합니다. |\n| 2026/04/02 23:08:13 | 커피한잔☺ | **¥20.00** | 畅 | 고마워요, 계속해서 인내할 수 있기를 바라요 |\n| 2026/04/02 15:13:46 | 커피한잔☺ | **¥20.00** | dove | 커피 사주세요. 이 프로젝트는 매우 유용합니다. |\n| 2026/03/30 23:34:12 | 커피한잔☺ | **¥20.00** | andie | 어서⛽️ |\n| 2026/03/28 12:46:23 | 커피한잔☺ | **¥20.00** | zhengbiubiu | 정말 감사합니다, 열심히 일해주셔서 감사합니다 |\n| 2026/03/27 19:45:57 | 커피한잔☺ | **¥20.00** | IsaacSuo | 빠른 노트 동기화👍 |\n| 2026/03/26 13:22:50 | 커피한잔☺ | **¥20.00** | dawn | 와 👍 |\n| 2026/03/24 14:45:21 | 커피한잔☺ | **¥20.00** | Bean | 감사합니다. 소프트웨어가 매우 편리합니다. |\n| 2026/03/22 23:08:14 | 커피한잔☺ | **¥20.00** | 拾感 | 존경 |\n| 2026/03/19 22:47:54 | 커피한잔☺ | **¥20.00** | jediknight | 감사합니다 🙏! 엄청난! |\n| 2026/03/19 20:20:17 | 커피한잔☺ | **¥20.00** | Fcjd | 개발자님 덕분에 사용하기 너무 쉽습니다. |\n| 2026/03/17 01:17:02 | 커피한잔☺ | **¥20.00** | southzen | 훌륭하고 간단하고 간단합니다. 계속해서 최적화하기를 바랍니다 ~ |\n| 2026/03/16 16:24:11 | 커피한잔☺ | **¥20.00** | 长筱团子 | 아주 유용해요, 야옹~고마워요, 야옹~ |\n| 2026/03/16 09:22:14 | 커피한잔☺ | **¥20.00** | barry | 무례해요^_^ |\n| 2026/03/16 01:01:12 | 커피한잔☺ | **¥20.00** | Stone | 오픈 소스 세계에 기여해 주셔서 감사합니다 |\n| 2026/03/14 17:15:45 | 커피한잔☺ | **¥20.00** | R M | 작가님 덕분에 무례함은 없어요 |\n| 2026/03/14 00:39:54 | 커피한잔☺ | **¥20.00** | T0_欣 | NB보스님, 감사합니다. 감사합니다. |\n| 2026/03/11 20:05:58 | 커피한잔☺ | **¥20.00** | Ucat | 플러그인을 주셔서 감사합니다. 매우 유용하고 편리합니다. |\n| 2026/03/10 01:08:20 | 커피한잔☺ | **¥20.00** | la | 엄청난 |\n| 2026/03/09 09:59:44 | 커피한잔☺ | **¥20.00** | 耀/ | 사용하기 쉽고 지원 |\n| 2026/03/07 23:36:38 | 커피한잔☺ | **¥20.00** | 阿叶 | 아주 좋은 플러그인 지원 |\n| 2026/03/06 09:58:58 | 커피한잔☺ | **¥20.00** | Dylan | 감사해요 |\n| 2026/03/03 00:53:09 | 커피한잔☺ | **¥20.00** | Alan | 코드를 작성하고 소스를 공개하기 위해 밤낮으로 열심히 일한 작성자에게 감사드립니다. |\n| 2026/03/01 21:10:27 | 커피한잔☺ | **¥20.00** | Jack ☑️ | 좀 더 완벽해지고 클라우드에서 다른 형식의 ob 파일을 볼 수 있기를 바랍니다. |\n| 2026/02/28 09:50:58 | 커피한잔☺ | **¥20.00** | tangdh | 이 플러그인의 아이디어는 매우 좋습니다. 계속해서 좋은 작업을 해주세요. |\n| 2026/02/27 12:48:50 | 커피한잔☺ | **¥20.00** | aban | 아주 좋은 동기화 플러그인 |\n| 2026/02/27 11:37:18 | 커피한잔☺ | **¥20.00** | 三岁 | 당신의 일에 감사드립니다 |\n| 2026/02/27 11:19:13 | 커피한잔☺ | **¥20.00** | 行长 | 너무 강하다 |\n| 2026/02/26 11:16:25 | 커피한잔☺ | **¥20.00** | fausto | 이런 좋은 사실을 더 많은 사람들에게 알리고 홍보에 더욱 힘써야겠습니다! |\n| 2026/02/25 20:17:42 | 커피한잔☺ | **¥20.00** | woloin | 사용하기 매우 쉽습니다. 이렇게 좋은 도구를 개발해 주신 작성자에게 감사드립니다! |\n| 2026/02/24 18:29:45 | 커피한잔☺ | **¥20.00** | kimi | 흑요석을 회전하게 해주는 유용한 플러그인을 개발해주신 작성자님께 감사드립니다🥰 |\n| 2026/02/24 10:01:37 | 커피한잔☺ | **¥20.00** | ccsir | 플러그인은 사용하기 쉽습니다 |\n| 2026/02/23 20:53:08 | 커피한잔☺ | **¥20.00** | KevinYAN | 한동안 사용해봤는데 정말 좋습니다. |\n| 2026/02/23 19:32:03 | 커피한잔☺ | **¥20.00** | 繁星影月 | 말의 해를 맞아 |\n| 2026/02/23 12:30:55 | 커피한잔☺ | **¥20.00** | ahto | 당신의 노고에 감사드립니다 |\n| 2026/02/23 06:24:55 | 커피한잔☺ | **¥20.00** | 大学生 | 매우 도움이 됩니다. 어서요! |\n| 2026/02/15 09:25:17 | 커피한잔☺ | **¥20.00** | sfsun67 | 지원하세요~ 매우 실용적이고 수요가 많은 기능입니다. AI 시대에 매우 유용 |\n| 2026/02/10 23:21:38 | 커피한잔☺ | **¥20.00** | toby | 사장님이 만들어주신 동기화 플러그인 감사합니다. 사용하기가 매우 쉽습니다. 먼저 커피 한잔 드시고 나중에 계속 보답해드리겠습니다. |\n| 2026/02/10 11:41:36 | 커피한잔☺ | **¥20.00** | WONG | 보스의 동기화 플러그인은 매우 좋습니다. 나는 그것을 계속 지원할 것입니다. |\n| 2026/02/08 22:02:32 | 커피한잔☺ | **¥20.00** | 小迪 | 매우 유용합니다. 감사합니다. |\n| 2026/02/06 08:42:49 | 커피한잔☺ | **¥20.00** | Max | 어서💪 웹에서 이미지 편집 기능을 개선해보세요 |\n| 2026/01/28 10:48:02 | 커피한잔☺ | **¥20.00** | 通 | 어서 해봐요 |\n| 2026/01/26 17:21:27 | 커피한잔☺ | **¥20.00** | CloseCV | 😘 |\n| 2026/01/16 11:47:13 | 커피한잔☺ | **¥20.00** | 苏 | 매우 유용하며 후속 개발 및 최적화를 기대합니다. 고마워하는. |\n| 2026/01/15 14:51:11 | 커피한잔☺ | **¥20.00** | 灰风 | 매우 유용합니다. 감사해요! |\n| 2026/01/09 18:12:17 | 커피한잔☺ | **¥20.00** | xix | 플러그인 아이디어가 너무 옳습니다 |\n| 2026/01/03 22:44:43 | 커피한잔☺ | **¥20.00** | 姚朝伟 | 점점 더 좋아지길 바랍니다👌🏻 |\n| 2026/01/03 14:58:43 | 커피한잔☺ | **¥20.00** | roao | 훌륭한 동기화 솔루션, 미래를 기대합니다! 오픈소스 정말 감사드립니다! 어서 해봐요! |\n| 2026/03/11 09:58:20 | 원하는 대로 팁을 주세요 | **¥18.00** | 下鞅 | 존경심을 표현하기 위한 작은 노력 |\n| 2026/03/02 21:15:39 | 원하는 대로 팁을 주세요 | **¥10.00** | 路过打酱 | 어서, 하느님 |\n| 2026/02/28 12:27:51 | 원하는 대로 팁을 주세요 | **¥10.00** | 白芷 | 점점 좋아지길 바라요 |\n| 2026/02/27 15:54:55 | 원하는 대로 팁을 주세요 | **¥10.00** | 柴特 | 개발을 해주신 작성자에게 감사드리며, 오픈 소스에 감사드리며, 더욱 더 좋아지기를 바랍니다. |\n| 2026/02/23 15:34:53 | 원하는 대로 팁을 주세요 | **¥10.00** | Joe M | 감사합니다. 공식 동기화보다 사용하기가 더 쉽습니다. |\n| 2026/02/20 10:37:02 | 원하는 대로 팁을 주세요 | **¥10.00** | 羽山猫四叶 | 이렇게 편리한 동기화 소프트웨어를 만들어주셔서 감사합니다 |\n| 2026/03/22 15:09:43 | 원하는 대로 팁을 주세요 | **¥9.90** | Shifuwang | 작성자에게 감사드립니다. 훌륭한 소프트웨어입니다. |\n| 2026/01/28 12:03:03 | 원하는 대로 팁을 주세요 | **¥9.90** | 华星 | 소🐮 |\n| 2026/04/09 13:51:11 | 원하는 대로 팁을 주세요 | **¥8.88** | 散装白酒🍶 | 어서...당신의 미래는 분명 밝습니다. 다른 친구들에게도 추천하고 좋을 것 같아요. |\n| 2026/03/07 20:29:42 | 원하는 대로 팁을 주세요 | **¥8.88** | 皮皮 | 고마워요, 정말 유용해요. 좋아요 |\n| 2026/01/28 02:52:15 | 원하는 대로 팁을 주세요 | **¥8.88** | obsidian | 공유해주셔서 감사합니다 |\n| 2026/02/28 19:51:59 | 원하는 대로 팁을 주세요 | **¥8.00** | yang | 매우 완벽하고 서버 인터페이스도 매우 훌륭합니다. |\n| 2026/03/25 11:52:09 | 원하는 대로 팁을 주세요 | **¥6.66** | 东 | 무적인 척 하하하하하하 |\n| 2026/03/23 01:02:18 | 원하는 대로 팁을 주세요 | **¥6.66** | wishyuwill | 계속 발전해야 해요! ! ! |\n| 2026/03/02 17:10:28 | 원하는 대로 팁을 주세요 | **¥6.66** | 马孔多的旅人 | 개발해주셔서 감사합니다. 플러그인은 매우 유용하며 빠르게 업데이트될 예정입니다. [strong] |\n| 2026/02/01 23:44:27 | 원하는 대로 팁을 주세요 | **¥6.66** | 爱你 | 사랑해요 |\n| 2026/01/09 22:22:25 | 원하는 대로 팁을 주세요 | **¥6.66** | kane | 제가 작성한 플러그인은 훌륭합니다. 계속해서 좋은 일을 해주세요! |\n| 2026/04/20 16:34:57 | 원하는 대로 팁을 주세요 | **¥5.00** | david | 이것은 매우 유용합니다. 오랜 시간 고생 끝에 드디어 졸업한 기분이에요 (╥╯﹏╰╥)§ |\n| 2026/04/15 00:48:38 | 원하는 대로 팁을 주세요 | **¥5.00** | ben | 쉽지 않다 |\n| 2026/04/06 20:57:36 | 원하는 대로 팁을 주세요 | **¥5.00** | octobersky | 프로젝트에 도움을 주셔서 감사합니다. 이제부터 흑요석 동기화가 쉬워집니다. |\n| 2026/03/01 00:28:19 | 원하는 대로 팁을 주세요 | **¥5.00** | colorednoise | 대단한 발전 |\n| 2026/02/27 15:18:26 | 원하는 대로 팁을 주세요 | **¥5.00** | wudibaolong | 지원하다 |\n| 2026/02/14 02:32:50 | 원하는 대로 팁을 주세요 | **¥5.00** | 支持开源精神 | 유용한 플러그인을 개발해주셔서 감사합니다 |\n| 2026/02/13 02:13:10 | 원하는 대로 팁을 주세요 | **¥5.00** | xxx | 보상 |\n| 2026/02/11 17:07:02 | 원하는 대로 팁을 주세요 | **¥5.00** | Acckion | 어서 해봐요 |\n| 2026/01/11 14:20:34 | 원하는 대로 팁을 주세요 | **¥5.00** | 安宁 | 매우 유용합니다. git을 사용하여 빌드할 수 있기를 바랍니다. |\n| 2026/04/18 16:59:14 | 원하는 대로 팁을 주세요 | **¥3.66** | jeremy | 앞으로도 좋은 제품을 개발하고 만들어주세요 |\n| 2026/02/23 17:47:46 | 원하는 대로 팁을 주세요 | **¥3.00** | LL | 새해 복 많이 받으세요 |\n| 2026/04/11 12:28:18 | 원하는 대로 팁을 주세요 | **¥1.00** | ke1078 | 노력하다 |\n| 2026/03/26 15:03:40 | 원하는 대로 팁을 주세요 | **¥1.00** | guanyingquan | 고마워요, 매우 유용해요 |\n| 2026/02/24 20:15:19 | 원하는 대로 팁을 주세요 | **¥1.00** | Jimmy | 매우 유용한 Obs 동기화 플러그인에 감사드립니다! |\n| 2026/01/08 15:18:06 | 원하는 대로 팁을 주세요 | **¥1.00** | 用户 | 발견과 배포부터 사용까지 수년이 걸렸으며 이전과는 비교할 수 없을 정도로 부드럽고 매끄러운 느낌을 줍니다. 정말 훌륭해요! [강함][강함][강함] |\n\n\n--- \n*마지막 업데이트:2026/4/21 21:09:29*"
  },
  {
    "path": "docs/Support.zh-CN.json",
    "content": "[\n  {\n    \"time\": \"2026/03/27 00:36:52\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"128.00\",\n    \"unit\": \"¥\",\n    \"message\": \"特别棒，一直在用，希望越做越好。\",\n    \"name\": \"Geeson\"\n  },\n  {\n    \"time\": \"2026/04/18 21:15:46\",\n    \"item\": \"四杯咖啡☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"支持一下[抱拳]\",\n    \"name\": \"lien\"\n  },\n  {\n    \"time\": \"2026/03/29 11:20:42\",\n    \"item\": \"四杯咖啡☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"支持！加油！\",\n    \"name\": \"猛将兄\"\n  },\n  {\n    \"time\": \"2026/03/27 15:05:35\",\n    \"item\": \"四杯咖啡☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"做得太棒了\",\n    \"name\": \"Bais\"\n  },\n  {\n    \"time\": \"2026/03/24 09:02:45\",\n    \"item\": \"四杯咖啡☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"辛苦了\",\n    \"name\": \"cc\"\n  },\n  {\n    \"time\": \"2026/03/22 12:16:07\",\n    \"item\": \"四杯咖啡☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"强烈支持版本迭代\",\n    \"name\": \"cw\"\n  },\n  {\n    \"time\": \"2026/03/19 13:41:05\",\n    \"item\": \"四杯咖啡☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"快去加班更新\",\n    \"name\": \"背背背疼\"\n  },\n  {\n    \"time\": \"2026/03/13 17:28:40\",\n    \"item\": \"四杯咖啡☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常感谢你的开源与付出，插件超实用，小小支持，继续加油！💪\",\n    \"name\": \"一世风霜\"\n  },\n  {\n    \"time\": \"2026/03/02 09:38:26\",\n    \"item\": \"四杯咖啡☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常好，开发不易，支持一下。\",\n    \"name\": \"xuhsu\"\n  },\n  {\n    \"time\": \"2026/01/14 15:58:04\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"88.00\",\n    \"unit\": \"¥\",\n    \"message\": \"能力有限，不成敬意\",\n    \"name\": \"wutay\"\n  },\n  {\n    \"time\": \"2026/03/02 14:50:25\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢大佬！非常好用！\",\n    \"name\": \"Patrick\"\n  },\n  {\n    \"time\": \"2026/03/01 22:56:17\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"随喜赞叹！\",\n    \"name\": \"xday\"\n  },\n  {\n    \"time\": \"2026/03/01 22:40:23\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"大佬nb，插件很好用ヽ(*≧ω≦)ﾉ\",\n    \"name\": \"HanHaocheng\"\n  },\n  {\n    \"time\": \"2026/02/16 21:34:33\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"新年快乐\",\n    \"name\": \"Jack\"\n  },\n  {\n    \"time\": \"2026/04/02 19:16:44\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"51.55\",\n    \"unit\": \"¥\",\n    \"message\": \"很好的插件\",\n    \"name\": \"小七的小洋\"\n  },\n  {\n    \"time\": \"2026/04/21 14:32:40\",\n    \"item\": \"两杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"太棒了，宝藏插件啊，加油\",\n    \"name\": \"安度\"\n  },\n  {\n    \"time\": \"2026/04/09 13:45:07\",\n    \"item\": \"两杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"电脑同步成功，安卓报错  code=305，message=参数验证失败 Details=context is requ\",\n    \"name\": \"亲 yexizhu811\"\n  },\n  {\n    \"time\": \"2026/04/08 03:18:40\",\n    \"item\": \"两杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢作者，这一同步方式解决了多设备配置一致性的麻烦。\",\n    \"name\": \"彼岸花\"\n  },\n  {\n    \"time\": \"2026/04/07 07:52:09\",\n    \"item\": \"两杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"太棒了，很需要，感谢大佬。\",\n    \"name\": \"tom\"\n  },\n  {\n    \"time\": \"2026/04/03 10:50:44\",\n    \"item\": \"两杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"Good work\",\n    \"name\": \"David\"\n  },\n  {\n    \"time\": \"2026/04/02 03:03:20\",\n    \"item\": \"两杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"为牛逼付费！\",\n    \"name\": \"狗带带子\"\n  },\n  {\n    \"time\": \"2026/03/27 10:15:04\",\n    \"item\": \"两杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"好人一生平安\",\n    \"name\": \"卿\"\n  },\n  {\n    \"time\": \"2026/03/18 22:57:03\",\n    \"item\": \"两杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢telegram上的指导\",\n    \"name\": \"灰风\"\n  },\n  {\n    \"time\": \"2026/03/15 20:09:05\",\n    \"item\": \"两杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢🙏，插件好用\",\n    \"name\": \"红星 RedStar\"\n  },\n  {\n    \"time\": \"2026/03/14 23:46:58\",\n    \"item\": \"两杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常好整套架构，让我进入21世纪\",\n    \"name\": \"fbeis\"\n  },\n  {\n    \"time\": \"2026/03/02 21:00:43\",\n    \"item\": \"两杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"谢谢大佬\",\n    \"name\": \"南科大小魏\"\n  },\n  {\n    \"time\": \"2026/02/28 13:56:18\",\n    \"item\": \"两杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"太勤劳了，必须支持\",\n    \"name\": \"xenon\"\n  },\n  {\n    \"time\": \"2026/02/24 16:37:58\",\n    \"item\": \"两杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很不错的同步方案\",\n    \"name\": \"熙熙煦煦\"\n  },\n  {\n    \"time\": \"2026/02/16 12:14:32\",\n    \"item\": \"两杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢作者，新年快乐！\",\n    \"name\": \"红殇\"\n  },\n  {\n    \"time\": \"2026/02/14 18:01:55\",\n    \"item\": \"两杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢开发这么棒的插件，解决了同步问题\",\n    \"name\": \"Jacky龙\"\n  },\n  {\n    \"time\": \"2026/02/04 10:41:49\",\n    \"item\": \"两杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"同步功能很好用，希望继续迭代完善，以笔记安全为主\",\n    \"name\": \"咕咕咕\"\n  },\n  {\n    \"time\": \"2026/01/31 16:59:55\",\n    \"item\": \"两杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"好好开发，确实解决了 Obsidian 最大的一个痛点！\",\n    \"name\": \"vulnnull\"\n  },\n  {\n    \"time\": \"2026/01/21 09:37:19\",\n    \"item\": \"两杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"谢谢你的Obsidian同步很好用👍🏻\",\n    \"name\": \"Mojo抖音\"\n  },\n  {\n    \"time\": \"2026/01/09 16:34:10\",\n    \"item\": \"两杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"谢谢男菩萨的 OB 插件造福世人(^🙏^)，小小心意不成敬意。\",\n    \"name\": \"喆\"\n  },\n  {\n    \"time\": \"2026/03/04 21:42:57\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"30.00\",\n    \"unit\": \"¥\",\n    \"message\": \"插件很好用，感谢开发者\",\n    \"name\": \"X\"\n  },\n  {\n    \"time\": \"2026/02/25 10:55:32\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"30.00\",\n    \"unit\": \"¥\",\n    \"message\": \"大佬的项目帮了我大忙！非常感谢！希望大佬继续加油\",\n    \"name\": \"jeanlaw\"\n  },\n  {\n    \"time\": \"2026/03/13 13:43:33\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"29.00\",\n    \"unit\": \"¥\",\n    \"message\": \"好作品，加油！\",\n    \"name\": \"rocku\"\n  },\n  {\n    \"time\": \"2026/02/24 18:36:51\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"25.00\",\n    \"unit\": \"¥\",\n    \"message\": \"一直找不到理想的obsidian的同步方案,感谢作者 加油！\",\n    \"name\": \"淇淇\"\n  },\n  {\n    \"time\": \"2026/04/21 13:45:19\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很好用，加油\",\n    \"name\": \"蓬歌\"\n  },\n  {\n    \"time\": \"2026/04/20 15:37:57\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"给大佬跪了🧎🏻‍♂️，希望后续多多更新\",\n    \"name\": \"riding-a-colt\"\n  },\n  {\n    \"time\": \"2026/04/17 00:15:35\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"好用，支持一下\",\n    \"name\": \"稻草人\"\n  },\n  {\n    \"time\": \"2026/04/15 13:41:41\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"谢谢大佬\",\n    \"name\": \"hitomi\"\n  },\n  {\n    \"time\": \"2026/04/15 11:22:39\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"忘记密码如何找回\",\n    \"name\": \"woshiug\"\n  },\n  {\n    \"time\": \"2026/04/15 06:38:14\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"试试\",\n    \"name\": \"ke1078\"\n  },\n  {\n    \"time\": \"2026/04/12 23:54:45\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很棒的软件，感谢作者付出和开源分享。\",\n    \"name\": \"Nikki\"\n  },\n  {\n    \"time\": \"2026/04/12 23:40:17\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢🙏🏻\",\n    \"name\": \"月非明\"\n  },\n  {\n    \"time\": \"2026/04/10 23:00:36\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"棒棒哒，真好用\",\n    \"name\": \"wdysjy\"\n  },\n  {\n    \"time\": \"2026/04/04 01:33:37\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"来杯咖啡，辛苦了\",\n    \"name\": \"hsonghao\"\n  },\n  {\n    \"time\": \"2026/04/03 23:42:51\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"牛逼，好用\",\n    \"name\": \"kakaa\"\n  },\n  {\n    \"time\": \"2026/04/03 13:28:02\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常好用，感谢\",\n    \"name\": \"晴天小嘉\"\n  },\n  {\n    \"time\": \"2026/04/02 23:08:13\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢大佬，希望能继续坚持\",\n    \"name\": \"畅\"\n  },\n  {\n    \"time\": \"2026/04/02 15:13:46\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"请您喝咖啡，这个项目非常有用\",\n    \"name\": \"dove\"\n  },\n  {\n    \"time\": \"2026/03/30 23:34:12\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"加油⛽️\",\n    \"name\": \"andie\"\n  },\n  {\n    \"time\": \"2026/03/28 12:46:23\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢大大，辛苦啦\",\n    \"name\": \"zhengbiubiu\"\n  },\n  {\n    \"time\": \"2026/03/27 19:45:57\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"fast note sync👍\",\n    \"name\": \"IsaacSuo\"\n  },\n  {\n    \"time\": \"2026/03/26 13:22:50\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"牛哇👍\",\n    \"name\": \"dawn\"\n  },\n  {\n    \"time\": \"2026/03/24 14:45:21\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢，软件很方便。\",\n    \"name\": \"Bean\"\n  },\n  {\n    \"time\": \"2026/03/22 23:08:14\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"respect\",\n    \"name\": \"拾感\"\n  },\n  {\n    \"time\": \"2026/03/19 22:47:54\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"谢谢🙏！很赞！\",\n    \"name\": \"jediknight\"\n  },\n  {\n    \"time\": \"2026/03/19 20:20:17\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢开发者，太好用了，加油！\",\n    \"name\": \"Fcjd\"\n  },\n  {\n    \"time\": \"2026/03/17 01:17:02\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"做的太好了，大道至简，希望继续优化~\",\n    \"name\": \"southzen\"\n  },\n  {\n    \"time\": \"2026/03/16 16:24:11\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常好用的喵～谢谢喵～\",\n    \"name\": \"长筱团子\"\n  },\n  {\n    \"time\": \"2026/03/16 09:22:14\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"不成敬意^_^\",\n    \"name\": \"barry\"\n  },\n  {\n    \"time\": \"2026/03/16 01:01:12\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢你对开源世界的贡献\",\n    \"name\": \"Stone\"\n  },\n  {\n    \"time\": \"2026/03/14 17:15:45\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢作者，不成敬意\",\n    \"name\": \"R M\"\n  },\n  {\n    \"time\": \"2026/03/14 00:39:54\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"大佬NB，感谢感谢\",\n    \"name\": \"T0_欣\"\n  },\n  {\n    \"time\": \"2026/03/11 20:05:58\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢大佬做的插件，很好用很方便\",\n    \"name\": \"Ucat\"\n  },\n  {\n    \"time\": \"2026/03/10 01:08:20\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"牛逼\",\n    \"name\": \"la\"\n  },\n  {\n    \"time\": \"2026/03/09 09:59:44\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"好用，支持\",\n    \"name\": \"耀/\"\n  },\n  {\n    \"time\": \"2026/03/07 23:36:38\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很好的插件 支持\",\n    \"name\": \"阿叶\"\n  },\n  {\n    \"time\": \"2026/03/06 09:58:58\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"谢谢\",\n    \"name\": \"Dylan\"\n  },\n  {\n    \"time\": \"2026/03/03 00:53:09\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢作者日夜辛苦的写代码，并开源\",\n    \"name\": \"Alan\"\n  },\n  {\n    \"time\": \"2026/03/01 21:10:27\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"希望能更完善，可以在云端查看ob的其他格式的文件\",\n    \"name\": \"Jack ☑️\"\n  },\n  {\n    \"time\": \"2026/02/28 09:50:58\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"这个插件思路很好，加油\",\n    \"name\": \"tangdh\"\n  },\n  {\n    \"time\": \"2026/02/27 12:48:50\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很好的同步插件\",\n    \"name\": \"aban\"\n  },\n  {\n    \"time\": \"2026/02/27 11:37:18\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢您的工作\",\n    \"name\": \"三岁\"\n  },\n  {\n    \"time\": \"2026/02/27 11:19:13\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"太强了\",\n    \"name\": \"行长\"\n  },\n  {\n    \"time\": \"2026/02/26 11:16:25\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"这么好的东西应该让更多人知道，加大宣传力度啊！\",\n    \"name\": \"fausto\"\n  },\n  {\n    \"time\": \"2026/02/25 20:17:42\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常好用，感谢作者开发这么好的工具！\",\n    \"name\": \"woloin\"\n  },\n  {\n    \"time\": \"2026/02/24 18:29:45\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢作者开发那么好用的插件，让我的obsidian旋转🥰\",\n    \"name\": \"kimi\"\n  },\n  {\n    \"time\": \"2026/02/24 10:01:37\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"插件好用\",\n    \"name\": \"ccsir\"\n  },\n  {\n    \"time\": \"2026/02/23 20:53:08\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"用了一段时间了，真的太棒了。\",\n    \"name\": \"KevinYAN\"\n  },\n  {\n    \"time\": \"2026/02/23 19:32:03\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"马年快乐\",\n    \"name\": \"繁星影月\"\n  },\n  {\n    \"time\": \"2026/02/23 12:30:55\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"谢谢辛苦了\",\n    \"name\": \"ahto\"\n  },\n  {\n    \"time\": \"2026/02/23 06:24:55\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很有帮助，加油！\",\n    \"name\": \"大学生\"\n  },\n  {\n    \"time\": \"2026/02/15 09:25:17\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"支持，加油~，很实用，强需求的功能。在AI时代大有用处\",\n    \"name\": \"sfsun67\"\n  },\n  {\n    \"time\": \"2026/02/10 23:21:38\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢大佬做的同步插件，非常好用，请大佬先喝一杯咖啡，后续还会再继续打赏的\",\n    \"name\": \"toby\"\n  },\n  {\n    \"time\": \"2026/02/10 11:41:36\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"大佬的同步插件非常棒，我会一直持续支持的\",\n    \"name\": \"WONG\"\n  },\n  {\n    \"time\": \"2026/02/08 22:02:32\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常好用，感谢\",\n    \"name\": \"小迪\"\n  },\n  {\n    \"time\": \"2026/02/06 08:42:49\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"加油💪web端的图片编辑功能整一下呗\",\n    \"name\": \"Max\"\n  },\n  {\n    \"time\": \"2026/01/28 10:48:02\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"加油\",\n    \"name\": \"通\"\n  },\n  {\n    \"time\": \"2026/01/26 17:21:27\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"😘\",\n    \"name\": \"CloseCV\"\n  },\n  {\n    \"time\": \"2026/01/16 11:47:13\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很好用，期待后续的开发与优化。感谢。\",\n    \"name\": \"苏\"\n  },\n  {\n    \"time\": \"2026/01/15 14:51:11\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常好用感谢！\",\n    \"name\": \"灰风\"\n  },\n  {\n    \"time\": \"2026/01/09 18:12:17\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"插件思路太对了\",\n    \"name\": \"xix\"\n  },\n  {\n    \"time\": \"2026/01/03 22:44:43\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"希望越来越好👌🏻\",\n    \"name\": \"姚朝伟\"\n  },\n  {\n    \"time\": \"2026/01/03 14:58:43\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很棒的同步方案，未来可期！非常感谢开源！加油！\",\n    \"name\": \"roao\"\n  },\n  {\n    \"time\": \"2026/03/11 09:58:20\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"18.00\",\n    \"unit\": \"¥\",\n    \"message\": \"绵薄之力，以表敬意\",\n    \"name\": \"下鞅\"\n  },\n  {\n    \"time\": \"2026/03/02 21:15:39\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"加油大神\",\n    \"name\": \"路过打酱\"\n  },\n  {\n    \"time\": \"2026/02/28 12:27:51\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"希望越来越好\",\n    \"name\": \"白芷\"\n  },\n  {\n    \"time\": \"2026/02/27 15:54:55\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"谢谢作者开发，感谢开源，祝越来越好。\",\n    \"name\": \"柴特\"\n  },\n  {\n    \"time\": \"2026/02/23 15:34:53\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢，比官方同步还好用\",\n    \"name\": \"Joe M\"\n  },\n  {\n    \"time\": \"2026/02/20 10:37:02\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢做了这么便捷的同步软件\",\n    \"name\": \"羽山猫四叶\"\n  },\n  {\n    \"time\": \"2026/03/22 15:09:43\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"9.90\",\n    \"unit\": \"¥\",\n    \"message\": \"好软件，感谢作者。\",\n    \"name\": \"Shifuwang\"\n  },\n  {\n    \"time\": \"2026/01/28 12:03:03\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"9.90\",\n    \"unit\": \"¥\",\n    \"message\": \"牛🐮\",\n    \"name\": \"华星\"\n  },\n  {\n    \"time\": \"2026/04/09 13:51:11\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"8.88\",\n    \"unit\": \"¥\",\n    \"message\": \"加油……你这个绝对有发展前途，推荐其他朋友看都觉得很不错。\",\n    \"name\": \"散装白酒🍶\"\n  },\n  {\n    \"time\": \"2026/03/07 20:29:42\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"8.88\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢大佬，非常好用，点赞\",\n    \"name\": \"皮皮\"\n  },\n  {\n    \"time\": \"2026/01/28 02:52:15\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"8.88\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢分享\",\n    \"name\": \"obsidian\"\n  },\n  {\n    \"time\": \"2026/02/28 19:51:59\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"8.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很完善，甚至服务器界面也做得非常好看\",\n    \"name\": \"yang\"\n  },\n  {\n    \"time\": \"2026/03/25 11:52:09\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"装好了 无敌 哈哈哈哈哈哈\",\n    \"name\": \"东\"\n  },\n  {\n    \"time\": \"2026/03/23 01:02:18\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"一定要坚持开发呀！！！\",\n    \"name\": \"wishyuwill\"\n  },\n  {\n    \"time\": \"2026/03/02 17:10:28\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢开发，插件很好用，更新很快[强]\",\n    \"name\": \"马孔多的旅人\"\n  },\n  {\n    \"time\": \"2026/02/01 23:44:27\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"爱你\",\n    \"name\": \"爱你\"\n  },\n  {\n    \"time\": \"2026/01/09 22:22:25\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"老哥写的插件很棒，继续努力吧！\",\n    \"name\": \"kane\"\n  },\n  {\n    \"time\": \"2026/04/20 16:34:57\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"这也太好用了，折腾这么久感觉终于毕业了(╥╯﹏╰╥)ง\",\n    \"name\": \"david\"\n  },\n  {\n    \"time\": \"2026/04/15 00:48:38\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"不易\",\n    \"name\": \"ben\"\n  },\n  {\n    \"time\": \"2026/04/06 20:57:36\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢你的项目帮助到我，obsidian 同步从此变得容易\",\n    \"name\": \"octobersky\"\n  },\n  {\n    \"time\": \"2026/03/01 00:28:19\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"开发的太棒了\",\n    \"name\": \"colorednoise\"\n  },\n  {\n    \"time\": \"2026/02/27 15:18:26\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"支持\",\n    \"name\": \"wudibaolong\"\n  },\n  {\n    \"time\": \"2026/02/14 02:32:50\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢开发这么好用的插件\",\n    \"name\": \"支持开源精神\"\n  },\n  {\n    \"time\": \"2026/02/13 02:13:10\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"打赏\",\n    \"name\": \"xxx\"\n  },\n  {\n    \"time\": \"2026/02/11 17:07:02\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"加油\",\n    \"name\": \"Acckion\"\n  },\n  {\n    \"time\": \"2026/01/11 14:20:34\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很好用，希望能把 git 做出来\",\n    \"name\": \"安宁\"\n  },\n  {\n    \"time\": \"2026/04/18 16:59:14\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"3.66\",\n    \"unit\": \"¥\",\n    \"message\": \"继续开发，作出好产品\",\n    \"name\": \"jeremy\"\n  },\n  {\n    \"time\": \"2026/02/23 17:47:46\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"3.00\",\n    \"unit\": \"¥\",\n    \"message\": \"新年快乐\",\n    \"name\": \"LL\"\n  },\n  {\n    \"time\": \"2026/04/11 12:28:18\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"试试\",\n    \"name\": \"ke1078\"\n  },\n  {\n    \"time\": \"2026/03/26 15:03:40\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感谢，十分有用\",\n    \"name\": \"guanyingquan\"\n  },\n  {\n    \"time\": \"2026/02/24 20:15:19\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感恩您的 Obs 同步插件非常有帮助！\",\n    \"name\": \"Jimmy\"\n  },\n  {\n    \"time\": \"2026/01/08 15:18:06\",\n    \"item\": \"任意打赏\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"从发现部署到使用，好多年了，从未有过的流畅丝滑的感觉，真的太好了！[强][强][强]\",\n    \"name\": \"用户\"\n  }\n]"
  },
  {
    "path": "docs/Support.zh-CN.md",
    "content": "# 支持者名单 (Thanks to Supporters)\n\n> 非常感谢大家对本项目的支持！每一份打赏都是我持续维护和迭代的动力。 ❤️\n\n### 📜 致谢列表\n\n| 收款时间 | 收款项 | 金额 | 昵称 | 留言 |\n| :--- | :--- | :--- | :--- | :--- |\n| 2026/03/27 00:36:52 | 任意打赏 | **¥128.00** | Geeson | 特别棒，一直在用，希望越做越好。 |\n| 2026/04/18 21:15:46 | 四杯咖啡☕ | **¥100.00** | lien | 支持一下[抱拳] |\n| 2026/03/29 11:20:42 | 四杯咖啡☕ | **¥100.00** | 猛将兄 | 支持！加油！ |\n| 2026/03/27 15:05:35 | 四杯咖啡☕ | **¥100.00** | Bais | 做得太棒了 |\n| 2026/03/24 09:02:45 | 四杯咖啡☕ | **¥100.00** | cc | 辛苦了 |\n| 2026/03/22 12:16:07 | 四杯咖啡☕ | **¥100.00** | cw | 强烈支持版本迭代 |\n| 2026/03/19 13:41:05 | 四杯咖啡☕ | **¥100.00** | 背背背疼 | 快去加班更新 |\n| 2026/03/13 17:28:40 | 四杯咖啡☕ | **¥100.00** | 一世风霜 | 非常感谢你的开源与付出，插件超实用，小小支持，继续加油！💪 |\n| 2026/03/02 09:38:26 | 四杯咖啡☕ | **¥100.00** | xuhsu | 非常好，开发不易，支持一下。 |\n| 2026/01/14 15:58:04 | 任意打赏 | **¥88.00** | wutay | 能力有限，不成敬意 |\n| 2026/03/02 14:50:25 | 任意打赏 | **¥66.00** | Patrick | 感谢大佬！非常好用！ |\n| 2026/03/01 22:56:17 | 任意打赏 | **¥66.00** | xday | 随喜赞叹！ |\n| 2026/03/01 22:40:23 | 任意打赏 | **¥66.00** | HanHaocheng | 大佬nb，插件很好用ヽ(*≧ω≦)ﾉ |\n| 2026/02/16 21:34:33 | 任意打赏 | **¥66.00** | Jack | 新年快乐 |\n| 2026/04/02 19:16:44 | 任意打赏 | **¥51.55** | 小七的小洋 | 很好的插件 |\n| 2026/04/21 14:32:40 | 两杯咖啡☕ | **¥50.00** | 安度 | 太棒了，宝藏插件啊，加油 |\n| 2026/04/09 13:45:07 | 两杯咖啡☕ | **¥50.00** | 亲 yexizhu811 | 电脑同步成功，安卓报错  code=305，message=参数验证失败 Details=context is requ |\n| 2026/04/08 03:18:40 | 两杯咖啡☕ | **¥50.00** | 彼岸花 | 感谢作者，这一同步方式解决了多设备配置一致性的麻烦。 |\n| 2026/04/07 07:52:09 | 两杯咖啡☕ | **¥50.00** | tom | 太棒了，很需要，感谢大佬。 |\n| 2026/04/03 10:50:44 | 两杯咖啡☕ | **¥50.00** | David | Good work |\n| 2026/04/02 03:03:20 | 两杯咖啡☕ | **¥50.00** | 狗带带子 | 为牛逼付费！ |\n| 2026/03/27 10:15:04 | 两杯咖啡☕ | **¥50.00** | 卿 | 好人一生平安 |\n| 2026/03/18 22:57:03 | 两杯咖啡☕ | **¥50.00** | 灰风 | 感谢telegram上的指导 |\n| 2026/03/15 20:09:05 | 两杯咖啡☕ | **¥50.00** | 红星 RedStar | 感谢🙏，插件好用 |\n| 2026/03/14 23:46:58 | 两杯咖啡☕ | **¥50.00** | fbeis | 非常好整套架构，让我进入21世纪 |\n| 2026/03/02 21:00:43 | 两杯咖啡☕ | **¥50.00** | 南科大小魏 | 谢谢大佬 |\n| 2026/02/28 13:56:18 | 两杯咖啡☕ | **¥50.00** | xenon | 太勤劳了，必须支持 |\n| 2026/02/24 16:37:58 | 两杯咖啡☕ | **¥50.00** | 熙熙煦煦 | 很不错的同步方案 |\n| 2026/02/16 12:14:32 | 两杯咖啡☕ | **¥50.00** | 红殇 | 感谢作者，新年快乐！ |\n| 2026/02/14 18:01:55 | 两杯咖啡☕ | **¥50.00** | Jacky龙 | 感谢开发这么棒的插件，解决了同步问题 |\n| 2026/02/04 10:41:49 | 两杯咖啡☕ | **¥50.00** | 咕咕咕 | 同步功能很好用，希望继续迭代完善，以笔记安全为主 |\n| 2026/01/31 16:59:55 | 两杯咖啡☕ | **¥50.00** | vulnnull | 好好开发，确实解决了 Obsidian 最大的一个痛点！ |\n| 2026/01/21 09:37:19 | 两杯咖啡☕ | **¥50.00** | Mojo抖音 | 谢谢你的Obsidian同步很好用👍🏻 |\n| 2026/01/09 16:34:10 | 两杯咖啡☕ | **¥50.00** | 喆 | 谢谢男菩萨的 OB 插件造福世人(^🙏^)，小小心意不成敬意。 |\n| 2026/03/04 21:42:57 | 任意打赏 | **¥30.00** | X | 插件很好用，感谢开发者 |\n| 2026/02/25 10:55:32 | 任意打赏 | **¥30.00** | jeanlaw | 大佬的项目帮了我大忙！非常感谢！希望大佬继续加油 |\n| 2026/03/13 13:43:33 | 任意打赏 | **¥29.00** | rocku | 好作品，加油！ |\n| 2026/02/24 18:36:51 | 任意打赏 | **¥25.00** | 淇淇 | 一直找不到理想的obsidian的同步方案,感谢作者 加油！ |\n| 2026/04/21 13:45:19 | 一杯咖啡☕ | **¥20.00** | 蓬歌 | 很好用，加油 |\n| 2026/04/20 15:37:57 | 一杯咖啡☕ | **¥20.00** | riding-a-colt | 给大佬跪了🧎🏻‍♂️，希望后续多多更新 |\n| 2026/04/17 00:15:35 | 一杯咖啡☕ | **¥20.00** | 稻草人 | 好用，支持一下 |\n| 2026/04/15 13:41:41 | 一杯咖啡☕ | **¥20.00** | hitomi | 谢谢大佬 |\n| 2026/04/15 11:22:39 | 一杯咖啡☕ | **¥20.00** | woshiug | 忘记密码如何找回 |\n| 2026/04/15 06:38:14 | 一杯咖啡☕ | **¥20.00** | ke1078 | 试试 |\n| 2026/04/12 23:54:45 | 一杯咖啡☕ | **¥20.00** | Nikki | 很棒的软件，感谢作者付出和开源分享。 |\n| 2026/04/12 23:40:17 | 一杯咖啡☕ | **¥20.00** | 月非明 | 感谢🙏🏻 |\n| 2026/04/10 23:00:36 | 一杯咖啡☕ | **¥20.00** | wdysjy | 棒棒哒，真好用 |\n| 2026/04/04 01:33:37 | 一杯咖啡☕ | **¥20.00** | hsonghao | 来杯咖啡，辛苦了 |\n| 2026/04/03 23:42:51 | 一杯咖啡☕ | **¥20.00** | kakaa | 牛逼，好用 |\n| 2026/04/03 13:28:02 | 一杯咖啡☕ | **¥20.00** | 晴天小嘉 | 非常好用，感谢 |\n| 2026/04/02 23:08:13 | 一杯咖啡☕ | **¥20.00** | 畅 | 感谢大佬，希望能继续坚持 |\n| 2026/04/02 15:13:46 | 一杯咖啡☕ | **¥20.00** | dove | 请您喝咖啡，这个项目非常有用 |\n| 2026/03/30 23:34:12 | 一杯咖啡☕ | **¥20.00** | andie | 加油⛽️ |\n| 2026/03/28 12:46:23 | 一杯咖啡☕ | **¥20.00** | zhengbiubiu | 感谢大大，辛苦啦 |\n| 2026/03/27 19:45:57 | 一杯咖啡☕ | **¥20.00** | IsaacSuo | fast note sync👍 |\n| 2026/03/26 13:22:50 | 一杯咖啡☕ | **¥20.00** | dawn | 牛哇👍 |\n| 2026/03/24 14:45:21 | 一杯咖啡☕ | **¥20.00** | Bean | 感谢，软件很方便。 |\n| 2026/03/22 23:08:14 | 一杯咖啡☕ | **¥20.00** | 拾感 | respect |\n| 2026/03/19 22:47:54 | 一杯咖啡☕ | **¥20.00** | jediknight | 谢谢🙏！很赞！ |\n| 2026/03/19 20:20:17 | 一杯咖啡☕ | **¥20.00** | Fcjd | 感谢开发者，太好用了，加油！ |\n| 2026/03/17 01:17:02 | 一杯咖啡☕ | **¥20.00** | southzen | 做的太好了，大道至简，希望继续优化~ |\n| 2026/03/16 16:24:11 | 一杯咖啡☕ | **¥20.00** | 长筱团子 | 非常好用的喵～谢谢喵～ |\n| 2026/03/16 09:22:14 | 一杯咖啡☕ | **¥20.00** | barry | 不成敬意^_^ |\n| 2026/03/16 01:01:12 | 一杯咖啡☕ | **¥20.00** | Stone | 感谢你对开源世界的贡献 |\n| 2026/03/14 17:15:45 | 一杯咖啡☕ | **¥20.00** | R M | 感谢作者，不成敬意 |\n| 2026/03/14 00:39:54 | 一杯咖啡☕ | **¥20.00** | T0_欣 | 大佬NB，感谢感谢 |\n| 2026/03/11 20:05:58 | 一杯咖啡☕ | **¥20.00** | Ucat | 感谢大佬做的插件，很好用很方便 |\n| 2026/03/10 01:08:20 | 一杯咖啡☕ | **¥20.00** | la | 牛逼 |\n| 2026/03/09 09:59:44 | 一杯咖啡☕ | **¥20.00** | 耀/ | 好用，支持 |\n| 2026/03/07 23:36:38 | 一杯咖啡☕ | **¥20.00** | 阿叶 | 很好的插件 支持 |\n| 2026/03/06 09:58:58 | 一杯咖啡☕ | **¥20.00** | Dylan | 谢谢 |\n| 2026/03/03 00:53:09 | 一杯咖啡☕ | **¥20.00** | Alan | 感谢作者日夜辛苦的写代码，并开源 |\n| 2026/03/01 21:10:27 | 一杯咖啡☕ | **¥20.00** | Jack ☑️ | 希望能更完善，可以在云端查看ob的其他格式的文件 |\n| 2026/02/28 09:50:58 | 一杯咖啡☕ | **¥20.00** | tangdh | 这个插件思路很好，加油 |\n| 2026/02/27 12:48:50 | 一杯咖啡☕ | **¥20.00** | aban | 很好的同步插件 |\n| 2026/02/27 11:37:18 | 一杯咖啡☕ | **¥20.00** | 三岁 | 感谢您的工作 |\n| 2026/02/27 11:19:13 | 一杯咖啡☕ | **¥20.00** | 行长 | 太强了 |\n| 2026/02/26 11:16:25 | 一杯咖啡☕ | **¥20.00** | fausto | 这么好的东西应该让更多人知道，加大宣传力度啊！ |\n| 2026/02/25 20:17:42 | 一杯咖啡☕ | **¥20.00** | woloin | 非常好用，感谢作者开发这么好的工具！ |\n| 2026/02/24 18:29:45 | 一杯咖啡☕ | **¥20.00** | kimi | 感谢作者开发那么好用的插件，让我的obsidian旋转🥰 |\n| 2026/02/24 10:01:37 | 一杯咖啡☕ | **¥20.00** | ccsir | 插件好用 |\n| 2026/02/23 20:53:08 | 一杯咖啡☕ | **¥20.00** | KevinYAN | 用了一段时间了，真的太棒了。 |\n| 2026/02/23 19:32:03 | 一杯咖啡☕ | **¥20.00** | 繁星影月 | 马年快乐 |\n| 2026/02/23 12:30:55 | 一杯咖啡☕ | **¥20.00** | ahto | 谢谢辛苦了 |\n| 2026/02/23 06:24:55 | 一杯咖啡☕ | **¥20.00** | 大学生 | 很有帮助，加油！ |\n| 2026/02/15 09:25:17 | 一杯咖啡☕ | **¥20.00** | sfsun67 | 支持，加油~，很实用，强需求的功能。在AI时代大有用处 |\n| 2026/02/10 23:21:38 | 一杯咖啡☕ | **¥20.00** | toby | 感谢大佬做的同步插件，非常好用，请大佬先喝一杯咖啡，后续还会再继续打赏的 |\n| 2026/02/10 11:41:36 | 一杯咖啡☕ | **¥20.00** | WONG | 大佬的同步插件非常棒，我会一直持续支持的 |\n| 2026/02/08 22:02:32 | 一杯咖啡☕ | **¥20.00** | 小迪 | 非常好用，感谢 |\n| 2026/02/06 08:42:49 | 一杯咖啡☕ | **¥20.00** | Max | 加油💪web端的图片编辑功能整一下呗 |\n| 2026/01/28 10:48:02 | 一杯咖啡☕ | **¥20.00** | 通 | 加油 |\n| 2026/01/26 17:21:27 | 一杯咖啡☕ | **¥20.00** | CloseCV | 😘 |\n| 2026/01/16 11:47:13 | 一杯咖啡☕ | **¥20.00** | 苏 | 很好用，期待后续的开发与优化。感谢。 |\n| 2026/01/15 14:51:11 | 一杯咖啡☕ | **¥20.00** | 灰风 | 非常好用感谢！ |\n| 2026/01/09 18:12:17 | 一杯咖啡☕ | **¥20.00** | xix | 插件思路太对了 |\n| 2026/01/03 22:44:43 | 一杯咖啡☕ | **¥20.00** | 姚朝伟 | 希望越来越好👌🏻 |\n| 2026/01/03 14:58:43 | 一杯咖啡☕ | **¥20.00** | roao | 很棒的同步方案，未来可期！非常感谢开源！加油！ |\n| 2026/03/11 09:58:20 | 任意打赏 | **¥18.00** | 下鞅 | 绵薄之力，以表敬意 |\n| 2026/03/02 21:15:39 | 任意打赏 | **¥10.00** | 路过打酱 | 加油大神 |\n| 2026/02/28 12:27:51 | 任意打赏 | **¥10.00** | 白芷 | 希望越来越好 |\n| 2026/02/27 15:54:55 | 任意打赏 | **¥10.00** | 柴特 | 谢谢作者开发，感谢开源，祝越来越好。 |\n| 2026/02/23 15:34:53 | 任意打赏 | **¥10.00** | Joe M | 感谢，比官方同步还好用 |\n| 2026/02/20 10:37:02 | 任意打赏 | **¥10.00** | 羽山猫四叶 | 感谢做了这么便捷的同步软件 |\n| 2026/03/22 15:09:43 | 任意打赏 | **¥9.90** | Shifuwang | 好软件，感谢作者。 |\n| 2026/01/28 12:03:03 | 任意打赏 | **¥9.90** | 华星 | 牛🐮 |\n| 2026/04/09 13:51:11 | 任意打赏 | **¥8.88** | 散装白酒🍶 | 加油……你这个绝对有发展前途，推荐其他朋友看都觉得很不错。 |\n| 2026/03/07 20:29:42 | 任意打赏 | **¥8.88** | 皮皮 | 感谢大佬，非常好用，点赞 |\n| 2026/01/28 02:52:15 | 任意打赏 | **¥8.88** | obsidian | 感谢分享 |\n| 2026/02/28 19:51:59 | 任意打赏 | **¥8.00** | yang | 很完善，甚至服务器界面也做得非常好看 |\n| 2026/03/25 11:52:09 | 任意打赏 | **¥6.66** | 东 | 装好了 无敌 哈哈哈哈哈哈 |\n| 2026/03/23 01:02:18 | 任意打赏 | **¥6.66** | wishyuwill | 一定要坚持开发呀！！！ |\n| 2026/03/02 17:10:28 | 任意打赏 | **¥6.66** | 马孔多的旅人 | 感谢开发，插件很好用，更新很快[强] |\n| 2026/02/01 23:44:27 | 任意打赏 | **¥6.66** | 爱你 | 爱你 |\n| 2026/01/09 22:22:25 | 任意打赏 | **¥6.66** | kane | 老哥写的插件很棒，继续努力吧！ |\n| 2026/04/20 16:34:57 | 任意打赏 | **¥5.00** | david | 这也太好用了，折腾这么久感觉终于毕业了(╥╯﹏╰╥)ง |\n| 2026/04/15 00:48:38 | 任意打赏 | **¥5.00** | ben | 不易 |\n| 2026/04/06 20:57:36 | 任意打赏 | **¥5.00** | octobersky | 感谢你的项目帮助到我，obsidian 同步从此变得容易 |\n| 2026/03/01 00:28:19 | 任意打赏 | **¥5.00** | colorednoise | 开发的太棒了 |\n| 2026/02/27 15:18:26 | 任意打赏 | **¥5.00** | wudibaolong | 支持 |\n| 2026/02/14 02:32:50 | 任意打赏 | **¥5.00** | 支持开源精神 | 感谢开发这么好用的插件 |\n| 2026/02/13 02:13:10 | 任意打赏 | **¥5.00** | xxx | 打赏 |\n| 2026/02/11 17:07:02 | 任意打赏 | **¥5.00** | Acckion | 加油 |\n| 2026/01/11 14:20:34 | 任意打赏 | **¥5.00** | 安宁 | 很好用，希望能把 git 做出来 |\n| 2026/04/18 16:59:14 | 任意打赏 | **¥3.66** | jeremy | 继续开发，作出好产品 |\n| 2026/02/23 17:47:46 | 任意打赏 | **¥3.00** | LL | 新年快乐 |\n| 2026/04/11 12:28:18 | 任意打赏 | **¥1.00** | ke1078 | 试试 |\n| 2026/03/26 15:03:40 | 任意打赏 | **¥1.00** | guanyingquan | 感谢，十分有用 |\n| 2026/02/24 20:15:19 | 任意打赏 | **¥1.00** | Jimmy | 感恩您的 Obs 同步插件非常有帮助！ |\n| 2026/01/08 15:18:06 | 任意打赏 | **¥1.00** | 用户 | 从发现部署到使用，好多年了，从未有过的流畅丝滑的感觉，真的太好了！[强][强][强] |\n\n\n--- \n*本数据最后更新于：2026/4/21 21:03:09*"
  },
  {
    "path": "docs/Support.zh-TW.json",
    "content": "[\n  {\n    \"time\": \"2026/03/27 00:36:52\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"128.00\",\n    \"unit\": \"¥\",\n    \"message\": \"特別棒，一直在用，希望越做越好。\",\n    \"name\": \"Geeson\"\n  },\n  {\n    \"time\": \"2026/04/18 21:15:46\",\n    \"item\": \"四杯咖啡☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"支持一下[抱拳]\",\n    \"name\": \"lien\"\n  },\n  {\n    \"time\": \"2026/03/29 11:20:42\",\n    \"item\": \"四杯咖啡☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"支持！加油！\",\n    \"name\": \"猛将兄\"\n  },\n  {\n    \"time\": \"2026/03/27 15:05:35\",\n    \"item\": \"四杯咖啡☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"做得太棒了\",\n    \"name\": \"Bais\"\n  },\n  {\n    \"time\": \"2026/03/24 09:02:45\",\n    \"item\": \"四杯咖啡☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"辛苦了\",\n    \"name\": \"cc\"\n  },\n  {\n    \"time\": \"2026/03/22 12:16:07\",\n    \"item\": \"四杯咖啡☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"強烈支持版本迭代\",\n    \"name\": \"cw\"\n  },\n  {\n    \"time\": \"2026/03/19 13:41:05\",\n    \"item\": \"四杯咖啡☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"快去加班更新\",\n    \"name\": \"背背背疼\"\n  },\n  {\n    \"time\": \"2026/03/13 17:28:40\",\n    \"item\": \"四杯咖啡☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常感謝你的開源與付出，插件超實用，小小支持，繼續加油！ 💪\",\n    \"name\": \"一世风霜\"\n  },\n  {\n    \"time\": \"2026/03/02 09:38:26\",\n    \"item\": \"四杯咖啡☕\",\n    \"amount\": \"100.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常好，開發不易，支援一下。\",\n    \"name\": \"xuhsu\"\n  },\n  {\n    \"time\": \"2026/01/14 15:58:04\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"88.00\",\n    \"unit\": \"¥\",\n    \"message\": \"能力有限，不成敬意\",\n    \"name\": \"wutay\"\n  },\n  {\n    \"time\": \"2026/03/02 14:50:25\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝大佬！非常好用！\",\n    \"name\": \"Patrick\"\n  },\n  {\n    \"time\": \"2026/03/01 22:56:17\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"隨喜讚歎！\",\n    \"name\": \"xday\"\n  },\n  {\n    \"time\": \"2026/03/01 22:40:23\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"大佬nb，插件很好用ヽ(*≧ω≦)ﾉ\",\n    \"name\": \"HanHaocheng\"\n  },\n  {\n    \"time\": \"2026/02/16 21:34:33\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"66.00\",\n    \"unit\": \"¥\",\n    \"message\": \"新年快樂\",\n    \"name\": \"Jack\"\n  },\n  {\n    \"time\": \"2026/04/02 19:16:44\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"51.55\",\n    \"unit\": \"¥\",\n    \"message\": \"很好的插件\",\n    \"name\": \"小七的小洋\"\n  },\n  {\n    \"time\": \"2026/04/21 14:32:40\",\n    \"item\": \"兩杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"太棒了，寶藏插件啊，加油\",\n    \"name\": \"安度\"\n  },\n  {\n    \"time\": \"2026/04/09 13:45:07\",\n    \"item\": \"兩杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"電腦同步成功，安卓報錯 code=305，message=參數驗證失敗 Details=context is requ\",\n    \"name\": \"亲 yexizhu811\"\n  },\n  {\n    \"time\": \"2026/04/08 03:18:40\",\n    \"item\": \"兩杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝作者，這同步方式解決了多設備配置一致性的麻煩。\",\n    \"name\": \"彼岸花\"\n  },\n  {\n    \"time\": \"2026/04/07 07:52:09\",\n    \"item\": \"兩杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"太棒了，很需要，感謝大佬。\",\n    \"name\": \"tom\"\n  },\n  {\n    \"time\": \"2026/04/03 10:50:44\",\n    \"item\": \"兩杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"幹得好\",\n    \"name\": \"David\"\n  },\n  {\n    \"time\": \"2026/04/02 03:03:20\",\n    \"item\": \"兩杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"為牛逼付費！\",\n    \"name\": \"狗带带子\"\n  },\n  {\n    \"time\": \"2026/03/27 10:15:04\",\n    \"item\": \"兩杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"好人一生平安\",\n    \"name\": \"卿\"\n  },\n  {\n    \"time\": \"2026/03/18 22:57:03\",\n    \"item\": \"兩杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝telegram上的指導\",\n    \"name\": \"灰风\"\n  },\n  {\n    \"time\": \"2026/03/15 20:09:05\",\n    \"item\": \"兩杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝🙏，插件好用\",\n    \"name\": \"红星 RedStar\"\n  },\n  {\n    \"time\": \"2026/03/14 23:46:58\",\n    \"item\": \"兩杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常好整套架構，讓我進入21世紀\",\n    \"name\": \"fbeis\"\n  },\n  {\n    \"time\": \"2026/03/02 21:00:43\",\n    \"item\": \"兩杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"謝謝大佬\",\n    \"name\": \"南科大小魏\"\n  },\n  {\n    \"time\": \"2026/02/28 13:56:18\",\n    \"item\": \"兩杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"太勤勞了，必須支持\",\n    \"name\": \"xenon\"\n  },\n  {\n    \"time\": \"2026/02/24 16:37:58\",\n    \"item\": \"兩杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很不錯的同步方案\",\n    \"name\": \"熙熙煦煦\"\n  },\n  {\n    \"time\": \"2026/02/16 12:14:32\",\n    \"item\": \"兩杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝作者，新年快樂！\",\n    \"name\": \"红殇\"\n  },\n  {\n    \"time\": \"2026/02/14 18:01:55\",\n    \"item\": \"兩杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝開發這麼棒的插件，解決了同步問題\",\n    \"name\": \"Jacky龙\"\n  },\n  {\n    \"time\": \"2026/02/04 10:41:49\",\n    \"item\": \"兩杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"同步功能很好用，希望繼續迭代完善，以筆記安全為主\",\n    \"name\": \"咕咕咕\"\n  },\n  {\n    \"time\": \"2026/01/31 16:59:55\",\n    \"item\": \"兩杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"好好開發，確實解決了 Obsidian 最大的一個痛點！\",\n    \"name\": \"vulnnull\"\n  },\n  {\n    \"time\": \"2026/01/21 09:37:19\",\n    \"item\": \"兩杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"謝謝你的Obsidian同步很好用👍🏻\",\n    \"name\": \"Mojo抖音\"\n  },\n  {\n    \"time\": \"2026/01/09 16:34:10\",\n    \"item\": \"兩杯咖啡☕\",\n    \"amount\": \"50.00\",\n    \"unit\": \"¥\",\n    \"message\": \"謝謝男菩薩的 OB 插件造福世人(^🙏^)，小小心意不成敬意。\",\n    \"name\": \"喆\"\n  },\n  {\n    \"time\": \"2026/03/04 21:42:57\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"30.00\",\n    \"unit\": \"¥\",\n    \"message\": \"插件很好用，感謝開發者\",\n    \"name\": \"X\"\n  },\n  {\n    \"time\": \"2026/02/25 10:55:32\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"30.00\",\n    \"unit\": \"¥\",\n    \"message\": \"大佬的專案幫了我大忙！非常感謝！希望大佬繼續加油\",\n    \"name\": \"jeanlaw\"\n  },\n  {\n    \"time\": \"2026/03/13 13:43:33\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"29.00\",\n    \"unit\": \"¥\",\n    \"message\": \"好作品，加油！\",\n    \"name\": \"rocku\"\n  },\n  {\n    \"time\": \"2026/02/24 18:36:51\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"25.00\",\n    \"unit\": \"¥\",\n    \"message\": \"一直找不到理想的obsidian的同步方案,感謝作者 加油！\",\n    \"name\": \"淇淇\"\n  },\n  {\n    \"time\": \"2026/04/21 13:45:19\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很好用，加油\",\n    \"name\": \"蓬歌\"\n  },\n  {\n    \"time\": \"2026/04/20 15:37:57\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"給大佬跪了🧎🏻‍♂️，希望後續多多更新\",\n    \"name\": \"riding-a-colt\"\n  },\n  {\n    \"time\": \"2026/04/17 00:15:35\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"好用，支持一下\",\n    \"name\": \"稻草人\"\n  },\n  {\n    \"time\": \"2026/04/15 13:41:41\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"謝謝大佬\",\n    \"name\": \"hitomi\"\n  },\n  {\n    \"time\": \"2026/04/15 11:22:39\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"忘記密碼如何找回\",\n    \"name\": \"woshiug\"\n  },\n  {\n    \"time\": \"2026/04/15 06:38:14\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"試試\",\n    \"name\": \"ke1078\"\n  },\n  {\n    \"time\": \"2026/04/12 23:54:45\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很棒的軟體，感謝作者付出和開源分享。\",\n    \"name\": \"Nikki\"\n  },\n  {\n    \"time\": \"2026/04/12 23:40:17\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝🙏🏻\",\n    \"name\": \"月非明\"\n  },\n  {\n    \"time\": \"2026/04/10 23:00:36\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"棒棒噠，真好用\",\n    \"name\": \"wdysjy\"\n  },\n  {\n    \"time\": \"2026/04/04 01:33:37\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"來杯咖啡，辛苦了\",\n    \"name\": \"hsonghao\"\n  },\n  {\n    \"time\": \"2026/04/03 23:42:51\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"牛逼，好用\",\n    \"name\": \"kakaa\"\n  },\n  {\n    \"time\": \"2026/04/03 13:28:02\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常好用，感謝\",\n    \"name\": \"晴天小嘉\"\n  },\n  {\n    \"time\": \"2026/04/02 23:08:13\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝大佬，希望能持續堅持\",\n    \"name\": \"畅\"\n  },\n  {\n    \"time\": \"2026/04/02 15:13:46\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"請您喝咖啡，這個項目非常有用\",\n    \"name\": \"dove\"\n  },\n  {\n    \"time\": \"2026/03/30 23:34:12\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"加油⛽️\",\n    \"name\": \"andie\"\n  },\n  {\n    \"time\": \"2026/03/28 12:46:23\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝大，辛苦啦\",\n    \"name\": \"zhengbiubiu\"\n  },\n  {\n    \"time\": \"2026/03/27 19:45:57\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"快速筆記同步👍\",\n    \"name\": \"IsaacSuo\"\n  },\n  {\n    \"time\": \"2026/03/26 13:22:50\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"牛哇👍\",\n    \"name\": \"dawn\"\n  },\n  {\n    \"time\": \"2026/03/24 14:45:21\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝，軟體很方便。\",\n    \"name\": \"Bean\"\n  },\n  {\n    \"time\": \"2026/03/22 23:08:14\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"尊重\",\n    \"name\": \"拾感\"\n  },\n  {\n    \"time\": \"2026/03/19 22:47:54\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"謝謝🙏！很讚！\",\n    \"name\": \"jediknight\"\n  },\n  {\n    \"time\": \"2026/03/19 20:20:17\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝開發者，太好用了，加油！\",\n    \"name\": \"Fcjd\"\n  },\n  {\n    \"time\": \"2026/03/17 01:17:02\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"做的太好了，大道至簡，希望繼續優化~\",\n    \"name\": \"southzen\"\n  },\n  {\n    \"time\": \"2026/03/16 16:24:11\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常好用的喵～謝謝喵～\",\n    \"name\": \"长筱团子\"\n  },\n  {\n    \"time\": \"2026/03/16 09:22:14\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"不成敬^_^\",\n    \"name\": \"barry\"\n  },\n  {\n    \"time\": \"2026/03/16 01:01:12\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝你對開源世界的貢獻\",\n    \"name\": \"Stone\"\n  },\n  {\n    \"time\": \"2026/03/14 17:15:45\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝作者，不成敬意\",\n    \"name\": \"R M\"\n  },\n  {\n    \"time\": \"2026/03/14 00:39:54\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"大佬NB，感謝感謝\",\n    \"name\": \"T0_欣\"\n  },\n  {\n    \"time\": \"2026/03/11 20:05:58\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝大佬做的插件，很好用很方便\",\n    \"name\": \"Ucat\"\n  },\n  {\n    \"time\": \"2026/03/10 01:08:20\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"牛逼\",\n    \"name\": \"la\"\n  },\n  {\n    \"time\": \"2026/03/09 09:59:44\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"好用，支持\",\n    \"name\": \"耀/\"\n  },\n  {\n    \"time\": \"2026/03/07 23:36:38\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很好的插件 支持\",\n    \"name\": \"阿叶\"\n  },\n  {\n    \"time\": \"2026/03/06 09:58:58\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"謝謝\",\n    \"name\": \"Dylan\"\n  },\n  {\n    \"time\": \"2026/03/03 00:53:09\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝作者日夜辛苦的寫程式碼，並開源\",\n    \"name\": \"Alan\"\n  },\n  {\n    \"time\": \"2026/03/01 21:10:27\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"希望能更完善，可以在雲端查看ob的其他格式的文件\",\n    \"name\": \"Jack ☑️\"\n  },\n  {\n    \"time\": \"2026/02/28 09:50:58\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"這個插件思路很好，加油\",\n    \"name\": \"tangdh\"\n  },\n  {\n    \"time\": \"2026/02/27 12:48:50\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很好的同步插件\",\n    \"name\": \"aban\"\n  },\n  {\n    \"time\": \"2026/02/27 11:37:18\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝您的工作\",\n    \"name\": \"三岁\"\n  },\n  {\n    \"time\": \"2026/02/27 11:19:13\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"太強了\",\n    \"name\": \"行长\"\n  },\n  {\n    \"time\": \"2026/02/26 11:16:25\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"這麼好的東西應該​​會讓更多人知道，加大宣傳力道啊！\",\n    \"name\": \"fausto\"\n  },\n  {\n    \"time\": \"2026/02/25 20:17:42\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常好用，感謝作者開發這麼好的工具！\",\n    \"name\": \"woloin\"\n  },\n  {\n    \"time\": \"2026/02/24 18:29:45\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝作者開發那麼好用的插件，讓我的obsidian旋轉🥰\",\n    \"name\": \"kimi\"\n  },\n  {\n    \"time\": \"2026/02/24 10:01:37\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"插件好用\",\n    \"name\": \"ccsir\"\n  },\n  {\n    \"time\": \"2026/02/23 20:53:08\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"用了一段時間了，真的太棒了。\",\n    \"name\": \"KevinYAN\"\n  },\n  {\n    \"time\": \"2026/02/23 19:32:03\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"馬年快樂\",\n    \"name\": \"繁星影月\"\n  },\n  {\n    \"time\": \"2026/02/23 12:30:55\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"謝謝辛苦了\",\n    \"name\": \"ahto\"\n  },\n  {\n    \"time\": \"2026/02/23 06:24:55\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很有幫助，加油！\",\n    \"name\": \"大学生\"\n  },\n  {\n    \"time\": \"2026/02/15 09:25:17\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"支持，加油~，很實用，強需求的功能。在AI時代大有用處\",\n    \"name\": \"sfsun67\"\n  },\n  {\n    \"time\": \"2026/02/10 23:21:38\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝大佬做的同步插件，非常好用，請大佬先喝一杯咖啡，後續還會再繼續打賞的\",\n    \"name\": \"toby\"\n  },\n  {\n    \"time\": \"2026/02/10 11:41:36\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"大佬的同步插件非常棒，我會一直持續支持的\",\n    \"name\": \"WONG\"\n  },\n  {\n    \"time\": \"2026/02/08 22:02:32\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常好用，感謝\",\n    \"name\": \"小迪\"\n  },\n  {\n    \"time\": \"2026/02/06 08:42:49\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"加油💪web端的圖片編輯功能整一下唄\",\n    \"name\": \"Max\"\n  },\n  {\n    \"time\": \"2026/01/28 10:48:02\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"加油\",\n    \"name\": \"通\"\n  },\n  {\n    \"time\": \"2026/01/26 17:21:27\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"-\",\n    \"name\": \"CloseCV\"\n  },\n  {\n    \"time\": \"2026/01/16 11:47:13\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很好用，期待後續的開發與優化。感謝。\",\n    \"name\": \"苏\"\n  },\n  {\n    \"time\": \"2026/01/15 14:51:11\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"非常好用感謝！\",\n    \"name\": \"灰风\"\n  },\n  {\n    \"time\": \"2026/01/09 18:12:17\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"插件思路太對了\",\n    \"name\": \"xix\"\n  },\n  {\n    \"time\": \"2026/01/03 22:44:43\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"希望越來越好👌🏻\",\n    \"name\": \"姚朝伟\"\n  },\n  {\n    \"time\": \"2026/01/03 14:58:43\",\n    \"item\": \"一杯咖啡☕\",\n    \"amount\": \"20.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很棒的同步方案，未來可期！非常感謝開源！加油！\",\n    \"name\": \"roao\"\n  },\n  {\n    \"time\": \"2026/03/11 09:58:20\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"18.00\",\n    \"unit\": \"¥\",\n    \"message\": \"綿薄之力，以表敬意\",\n    \"name\": \"下鞅\"\n  },\n  {\n    \"time\": \"2026/03/02 21:15:39\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"加油大神\",\n    \"name\": \"路过打酱\"\n  },\n  {\n    \"time\": \"2026/02/28 12:27:51\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"希望越來越好\",\n    \"name\": \"白芷\"\n  },\n  {\n    \"time\": \"2026/02/27 15:54:55\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"謝謝作者開發，謝謝開源，祝越來越好。\",\n    \"name\": \"柴特\"\n  },\n  {\n    \"time\": \"2026/02/23 15:34:53\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝，比官方同步還好用\",\n    \"name\": \"Joe M\"\n  },\n  {\n    \"time\": \"2026/02/20 10:37:02\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"10.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝做了這麼便捷的同步軟體\",\n    \"name\": \"羽山猫四叶\"\n  },\n  {\n    \"time\": \"2026/03/22 15:09:43\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"9.90\",\n    \"unit\": \"¥\",\n    \"message\": \"好軟體，感謝作者。\",\n    \"name\": \"Shifuwang\"\n  },\n  {\n    \"time\": \"2026/01/28 12:03:03\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"9.90\",\n    \"unit\": \"¥\",\n    \"message\": \"牛🐮\",\n    \"name\": \"华星\"\n  },\n  {\n    \"time\": \"2026/04/09 13:51:11\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"8.88\",\n    \"unit\": \"¥\",\n    \"message\": \"加油……你這個絕對有發展前途，推薦其他朋友看都覺得很不錯。\",\n    \"name\": \"散装白酒🍶\"\n  },\n  {\n    \"time\": \"2026/03/07 20:29:42\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"8.88\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝大佬，非常好用，按讚\",\n    \"name\": \"皮皮\"\n  },\n  {\n    \"time\": \"2026/01/28 02:52:15\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"8.88\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝分享\",\n    \"name\": \"obsidian\"\n  },\n  {\n    \"time\": \"2026/02/28 19:51:59\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"8.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很完善，連伺服器介面也做得非常好看\",\n    \"name\": \"yang\"\n  },\n  {\n    \"time\": \"2026/03/25 11:52:09\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"裝好了 無敵 哈哈哈哈哈哈\",\n    \"name\": \"东\"\n  },\n  {\n    \"time\": \"2026/03/23 01:02:18\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"一定要堅持開發呀！ ！ ！\",\n    \"name\": \"wishyuwill\"\n  },\n  {\n    \"time\": \"2026/03/02 17:10:28\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝開發，插件很好用，更新很快[強]\",\n    \"name\": \"马孔多的旅人\"\n  },\n  {\n    \"time\": \"2026/02/01 23:44:27\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"愛你\",\n    \"name\": \"爱你\"\n  },\n  {\n    \"time\": \"2026/01/09 22:22:25\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"6.66\",\n    \"unit\": \"¥\",\n    \"message\": \"老哥寫的插件很棒，繼續努力！\",\n    \"name\": \"kane\"\n  },\n  {\n    \"time\": \"2026/04/20 16:34:57\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"這也太好用了，折騰這麼久感覺終於畢業了(╥╯﹏╰╥)ง\",\n    \"name\": \"david\"\n  },\n  {\n    \"time\": \"2026/04/15 00:48:38\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"不易\",\n    \"name\": \"ben\"\n  },\n  {\n    \"time\": \"2026/04/06 20:57:36\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝你的專案幫助到我，obsidian 同步從此變得容易\",\n    \"name\": \"octobersky\"\n  },\n  {\n    \"time\": \"2026/03/01 00:28:19\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"開發的太棒了\",\n    \"name\": \"colorednoise\"\n  },\n  {\n    \"time\": \"2026/02/27 15:18:26\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"支援\",\n    \"name\": \"wudibaolong\"\n  },\n  {\n    \"time\": \"2026/02/14 02:32:50\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝開發這麼好用的插件\",\n    \"name\": \"支持开源精神\"\n  },\n  {\n    \"time\": \"2026/02/13 02:13:10\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"打賞\",\n    \"name\": \"xxx\"\n  },\n  {\n    \"time\": \"2026/02/11 17:07:02\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"加油\",\n    \"name\": \"Acckion\"\n  },\n  {\n    \"time\": \"2026/01/11 14:20:34\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"5.00\",\n    \"unit\": \"¥\",\n    \"message\": \"很好用，希望能把 git 做出來\",\n    \"name\": \"安宁\"\n  },\n  {\n    \"time\": \"2026/04/18 16:59:14\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"3.66\",\n    \"unit\": \"¥\",\n    \"message\": \"繼續開發，作出好產品\",\n    \"name\": \"jeremy\"\n  },\n  {\n    \"time\": \"2026/02/23 17:47:46\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"3.00\",\n    \"unit\": \"¥\",\n    \"message\": \"新年快樂\",\n    \"name\": \"LL\"\n  },\n  {\n    \"time\": \"2026/04/11 12:28:18\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"試試\",\n    \"name\": \"ke1078\"\n  },\n  {\n    \"time\": \"2026/03/26 15:03:40\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感謝，十分有用\",\n    \"name\": \"guanyingquan\"\n  },\n  {\n    \"time\": \"2026/02/24 20:15:19\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"感恩您的 Obs 同步外掛非常有幫助！\",\n    \"name\": \"Jimmy\"\n  },\n  {\n    \"time\": \"2026/01/08 15:18:06\",\n    \"item\": \"任一打賞\",\n    \"amount\": \"1.00\",\n    \"unit\": \"¥\",\n    \"message\": \"從發現部署到使用，好多年了，從未有過的流暢絲滑的感覺，真的太好了！ [強][強][強]\",\n    \"name\": \"用户\"\n  }\n]"
  },
  {
    "path": "docs/Support.zh-TW.md",
    "content": "# 支持者名單 (Thanks to Supporters)\n\n> 非常感謝大家對本項目的支持！每一份打賞都是我持續維護和迭代的動力。 ❤️\n\n### 📜 致謝列表\n\n| 收款時間 | 收款項 | 金額 | 昵稱 | 留言 | 備註 |\n| :--- | :--- | :--- | :--- | :--- | :--- |\n|  |  | **¥128.00** |  | 特別棒，一直在用，希望越做越好。 |  |\n|  |  | **¥100.00** |  | 支持一下[抱拳] |  |\n|  |  | **¥100.00** |  | 支持！加油！ |  |\n|  |  | **¥100.00** |  | 做得太棒了 |  |\n|  |  | **¥100.00** |  | 辛苦了 |  |\n|  |  | **¥100.00** |  | 強烈支持版本迭代 |  |\n|  |  | **¥100.00** |  | 快去加班更新 |  |\n|  |  | **¥100.00** |  | 非常感謝你的開源與付出，插件超實用，小小支持，繼續加油！ 💪 |  |\n|  |  | **¥100.00** |  | 非常好，開發不易，支援一下。 |  |\n|  |  | **¥88.00** |  | 能力有限，不成敬意 |  |\n|  |  | **¥66.00** |  | 感謝大佬！非常好用！ |  |\n|  |  | **¥66.00** |  | 隨喜讚歎！ |  |\n|  |  | **¥66.00** |  | 大佬nb，插件很好用ヽ(*≧ω≦)ﾉ |  |\n|  |  | **¥66.00** |  | 新年快樂 |  |\n|  |  | **¥51.55** |  | 很好的插件 |  |\n|  |  | **¥50.00** |  | 太棒了，寶藏插件啊，加油 |  |\n|  |  | **¥50.00** |  | 電腦同步成功，安卓報錯 code=305，message=參數驗證失敗 Details=context is requ |  |\n|  |  | **¥50.00** |  | 感謝作者，這同步方式解決了多設備配置一致性的麻煩。 |  |\n|  |  | **¥50.00** |  | 太棒了，很需要，感謝大佬。 |  |\n|  |  | **¥50.00** |  | 幹得好 |  |\n|  |  | **¥50.00** |  | 為牛逼付費！ |  |\n|  |  | **¥50.00** |  | 好人一生平安 |  |\n|  |  | **¥50.00** |  | 感謝telegram上的指導 |  |\n|  |  | **¥50.00** |  | 感謝🙏，插件好用 |  |\n|  |  | **¥50.00** |  | 非常好整套架構，讓我進入21世紀 |  |\n|  |  | **¥50.00** |  | 謝謝大佬 |  |\n|  |  | **¥50.00** |  | 太勤勞了，必須支持 |  |\n|  |  | **¥50.00** |  | 很不錯的同步方案 |  |\n|  |  | **¥50.00** |  | 感謝作者，新年快樂！ |  |\n|  |  | **¥50.00** |  | 感謝開發這麼棒的插件，解決了同步問題 |  |\n|  |  | **¥50.00** |  | 同步功能很好用，希望繼續迭代完善，以筆記安全為主 |  |\n|  |  | **¥50.00** |  | 好好開發，確實解決了 Obsidian 最大的一個痛點！ |  |\n|  |  | **¥50.00** |  | 謝謝你的Obsidian同步很好用👍🏻 |  |\n|  |  | **¥50.00** |  | 謝謝男菩薩的 OB 插件造福世人(^🙏^)，小小心意不成敬意。 |  |\n|  |  | **¥30.00** |  | 插件很好用，感謝開發者 |  |\n|  |  | **¥30.00** |  | 大佬的專案幫了我大忙！非常感謝！希望大佬繼續加油 |  |\n|  |  | **¥29.00** |  | 好作品，加油！ |  |\n|  |  | **¥25.00** |  | 一直找不到理想的obsidian的同步方案,感謝作者 加油！ |  |\n|  |  | **¥20.00** |  | 很好用，加油 |  |\n|  |  | **¥20.00** |  | 給大佬跪了🧎🏻‍♂️，希望後續多多更新 |  |\n|  |  | **¥20.00** |  | 好用，支持一下 |  |\n|  |  | **¥20.00** |  | 謝謝大佬 |  |\n|  |  | **¥20.00** |  | 忘記密碼如何找回 |  |\n|  |  | **¥20.00** |  | 試試 |  |\n|  |  | **¥20.00** |  | 很棒的軟體，感謝作者付出和開源分享。 |  |\n|  |  | **¥20.00** |  | 感謝🙏🏻 |  |\n|  |  | **¥20.00** |  | 棒棒噠，真好用 |  |\n|  |  | **¥20.00** |  | 來杯咖啡，辛苦了 |  |\n|  |  | **¥20.00** |  | 牛逼，好用 |  |\n|  |  | **¥20.00** |  | 非常好用，感謝 |  |\n|  |  | **¥20.00** |  | 感謝大佬，希望能持續堅持 |  |\n|  |  | **¥20.00** |  | 請您喝咖啡，這個項目非常有用 |  |\n|  |  | **¥20.00** |  | 加油⛽️ |  |\n|  |  | **¥20.00** |  | 感謝大，辛苦啦 |  |\n|  |  | **¥20.00** |  | 快速筆記同步👍 |  |\n|  |  | **¥20.00** |  | 牛哇👍 |  |\n|  |  | **¥20.00** |  | 感謝，軟體很方便。 |  |\n|  |  | **¥20.00** |  | 尊重 |  |\n|  |  | **¥20.00** |  | 謝謝🙏！很讚！ |  |\n|  |  | **¥20.00** |  | 感謝開發者，太好用了，加油！ |  |\n|  |  | **¥20.00** |  | 做的太好了，大道至簡，希望繼續優化~ |  |\n|  |  | **¥20.00** |  | 非常好用的喵～謝謝喵～ |  |\n|  |  | **¥20.00** |  | 不成敬^_^ |  |\n|  |  | **¥20.00** |  | 感謝你對開源世界的貢獻 |  |\n|  |  | **¥20.00** |  | 感謝作者，不成敬意 |  |\n|  |  | **¥20.00** |  | 大佬NB，感謝感謝 |  |\n|  |  | **¥20.00** |  | 感謝大佬做的插件，很好用很方便 |  |\n|  |  | **¥20.00** |  | 牛逼 |  |\n|  |  | **¥20.00** |  | 好用，支持 |  |\n|  |  | **¥20.00** |  | 很好的插件 支持 |  |\n|  |  | **¥20.00** |  | 謝謝 |  |\n|  |  | **¥20.00** |  | 感謝作者日夜辛苦的寫程式碼，並開源 |  |\n|  |  | **¥20.00** |  | 希望能更完善，可以在雲端查看ob的其他格式的文件 |  |\n|  |  | **¥20.00** |  | 這個插件思路很好，加油 |  |\n|  |  | **¥20.00** |  | 很好的同步插件 |  |\n|  |  | **¥20.00** |  | 感謝您的工作 |  |\n|  |  | **¥20.00** |  | 太強了 |  |\n|  |  | **¥20.00** |  | 這麼好的東西應該​​會讓更多人知道，加大宣傳力道啊！ |  |\n|  |  | **¥20.00** |  | 非常好用，感謝作者開發這麼好的工具！ |  |\n|  |  | **¥20.00** |  | 感謝作者開發那麼好用的插件，讓我的obsidian旋轉🥰 |  |\n|  |  | **¥20.00** |  | 插件好用 |  |\n|  |  | **¥20.00** |  | 用了一段時間了，真的太棒了。 |  |\n|  |  | **¥20.00** |  | 馬年快樂 |  |\n|  |  | **¥20.00** |  | 謝謝辛苦了 |  |\n|  |  | **¥20.00** |  | 很有幫助，加油！ |  |\n|  |  | **¥20.00** |  | 支持，加油~，很實用，強需求的功能。在AI時代大有用處 |  |\n|  |  | **¥20.00** |  | 感謝大佬做的同步插件，非常好用，請大佬先喝一杯咖啡，後續還會再繼續打賞的 |  |\n|  |  | **¥20.00** |  | 大佬的同步插件非常棒，我會一直持續支持的 |  |\n|  |  | **¥20.00** |  | 非常好用，感謝 |  |\n|  |  | **¥20.00** |  | 加油💪web端的圖片編輯功能整一下唄 |  |\n|  |  | **¥20.00** |  | 加油 |  |\n|  |  | **¥20.00** |  | 😘 |  |\n|  |  | **¥20.00** |  | 很好用，期待後續的開發與優化。感謝。 |  |\n|  |  | **¥20.00** |  | 非常好用感謝！ |  |\n|  |  | **¥20.00** |  | 插件思路太對了 |  |\n|  |  | **¥20.00** |  | 希望越來越好👌🏻 |  |\n|  |  | **¥20.00** |  | 很棒的同步方案，未來可期！非常感謝開源！加油！ |  |\n|  |  | **¥18.00** |  | 綿薄之力，以表敬意 |  |\n|  |  | **¥10.00** |  | 加油大神 |  |\n|  |  | **¥10.00** |  | 希望越來越好 |  |\n|  |  | **¥10.00** |  | 謝謝作者開發，謝謝開源，祝越來越好。 |  |\n|  |  | **¥10.00** |  | 感謝，比官方同步還好用 |  |\n|  |  | **¥10.00** |  | 感謝做了這麼便捷的同步軟體 |  |\n|  |  | **¥9.90** |  | 好軟體，感謝作者。 |  |\n|  |  | **¥9.90** |  | 牛🐮 |  |\n|  |  | **¥8.88** |  | 加油……你這個絕對有發展前途，推薦其他朋友看都覺得很不錯。 |  |\n|  |  | **¥8.88** |  | 感謝大佬，非常好用，按讚 |  |\n|  |  | **¥8.88** |  | 感謝分享 |  |\n|  |  | **¥8.00** |  | 很完善，連伺服器介面也做得非常好看 |  |\n|  |  | **¥6.66** |  | 裝好了 無敵 哈哈哈哈哈哈 |  |\n|  |  | **¥6.66** |  | 一定要堅持開發呀！ ！ ！ |  |\n|  |  | **¥6.66** |  | 感謝開發，插件很好用，更新很快[強] |  |\n|  |  | **¥6.66** |  | 愛你 |  |\n|  |  | **¥6.66** |  | 老哥寫的插件很棒，繼續努力！ |  |\n|  |  | **¥5.00** |  | 這也太好用了，折騰這麼久感覺終於畢業了(╥╯﹏╰╥)ง |  |\n|  |  | **¥5.00** |  | 不易 |  |\n|  |  | **¥5.00** |  | 感謝你的專案幫助到我，obsidian 同步從此變得容易 |  |\n|  |  | **¥5.00** |  | 開發的太棒了 |  |\n|  |  | **¥5.00** |  | 支援 |  |\n|  |  | **¥5.00** |  | 感謝開發這麼好用的插件 |  |\n|  |  | **¥5.00** |  | 打賞 |  |\n|  |  | **¥5.00** |  | 加油 |  |\n|  |  | **¥5.00** |  | 很好用，希望能把 git 做出來 |  |\n|  |  | **¥3.66** |  | 繼續開發，作出好產品 |  |\n|  |  | **¥3.00** |  | 新年快樂 |  |\n|  |  | **¥1.00** |  | 試試 |  |\n|  |  | **¥1.00** |  | 感謝，十分有用 |  |\n|  |  | **¥1.00** |  | 感恩您的 Obs 同步外掛非常有幫助！ |  |\n|  |  | **¥1.00** |  | 從發現部署到使用，好多年了，從未有過的流暢絲滑的感覺，真的太好了！ [強][強][強] |  |\n\n\n--- \n*本數據最後更新於：2026/4/21 21:04:46*"
  },
  {
    "path": "docs/SyncProtocol.md",
    "content": "# WebSocket 同步协议前端对接说明 (版本 1.1)\n\n本协议描述了最新调整后的同步流程，前端在对接 `NoteSync`, `FolderSync`, `SettingSync` 和 `FileSync` 时需遵循以下规范。\n\n## 1. 核心变更概览\n\n- **Request**: 所有同步请求需携带 `context` 字符串。\n- **Response**: 同步结果不再合并返回，改为 **先返回统计结束消息 (End)，后发送逐条详情消息**。\n- **Context**: 所有下发的同步响应都将原样透传请求中的 `context`。\n\n## 2. 交互流程示例\n\n以 **笔记同步 (NoteSync)** 为例：\n\n### Step 1: 前端发起同步请求\n前端需生成一个唯一的 `context`（如随机 UUID 或时间戳），用于标识本次同步任务。\n\n**Action**: `NoteSync`\n**Data**:\n```json\n{\n  \"context\": \"sync_task_001\",\n  \"vault\": \"MyNotes\",\n  \"lastTime\": 1708800000000,\n  \"notes\": [...]\n}\n```\n\n### Step 2: 服务端返回统计消息 (End)\n服务端在扫描完变更后，会立刻发送一个 End 确认消息。该消息**不再包含明细列表**，仅用于告知统计数据。\n\n**ActionType**: `NoteSyncEnd`\n**Response**:\n```json\n{\n  \"code\": 200,\n  \"status\": true,\n  \"message\": \"success\",\n  \"vault\": \"MyNotes\",\n  \"context\": \"sync_task_001\",\n  \"data\": {\n    \"lastTime\": 1708900000000,\n    \"needUploadCount\": 2,\n    \"needModifyCount\": 1,\n    \"needSyncMtimeCount\": 0,\n    \"needDeleteCount\": 1\n  }\n}\n```\n\n### Step 3: 服务端逐条推送明细消息\n随后，服务端会将具体的变更动作通过独立的 WebSocket 消息下发。\n\n- **明细消息 1 (修改笔记)**\n  **ActionType**: `NoteSyncModify`\n  **Response**: `{ \"context\": \"sync_task_001\", \"data\": { \"path\": \"test.md\", \"content\": \"...\" }, ... }`\n\n- **明细消息 2 (删除笔记)**\n  **ActionType**: `NoteSyncDelete`\n  **Response**: `{ \"context\": \"sync_task_001\", \"data\": { \"path\": \"old.md\" }, ... }`\n\n## 3. 响应消息结构 (Res)\n\n所有 WebSocket 响应均遵循以下标准结构：\n\n| 字段 | 类型 | 说明 |\n| :--- | :--- | :--- |\n| `code` | int | 业务状态码 (200 为成功) |\n| `status` | bool | 成功状态 |\n| `message` | string | 状态描述 |\n| `data` | any | 业务数据载体 |\n| `vault` | string | 保险库名称 (透传) |\n| `context` | string | **任务上下文标识 (透传)** |\n\n## 4. 前端集成建议\n\n1. **并行处理**: 由于统计消息 (End) 提前到达，前端可以先更新同步进度 UI，随后监听后续的明细推送并动态更新本地缓存。\n2. **任务匹配**: 在 WebSocket 的全局消息监听器中，建议通过响应体中的 `context` 字段来匹配本次同步请求的回调逻辑或状态。\n3. **计数校验**: 前端可以通过 `SyncEnd` 消息中的 `needXXXCount` 来验证后续是否收到了足额的详情推送。\n\n## 5. 受影响的接口 Action\n\n| 模块 | 同步请求 Action | 统计结束消息 Type | 明细推送消息 Type |\n| :--- | :--- | :--- | :--- |\n| **笔记** | `NoteSync` | `NoteSyncEnd` | `NoteSyncModify`, `NoteSyncDelete`, `NoteSyncMtime`, `NoteSyncNeedPush` |\n| **文件夹** | `FolderSync` | `FolderSyncEnd` | `FolderSyncModify`, `FolderSyncDelete` |\n| **设置** | `SettingSync` | `SettingSyncEnd` | `SettingSyncModify`, `SettingSyncDelete`, `SettingSyncMtime`, `SettingSyncNeedUpload` |\n| **文件/附件** | `FileSync` | `FileSyncEnd` | `FileSyncUpdate`, `FileSyncDelete`, `FileSyncMtime`, `FileUpload` |\n\n---\n*注：请确保前端代码能够兼容处理同一 `context` 下接连收到的多条消息。*\n"
  },
  {
    "path": "docs/admin_config_api.md",
    "content": "# 管理员配置接口文档 (`/api/admin/config`)\n\n本文档描述了管理员用于获取和更新系统配置的 API 接口。配置参数同步自 `config/config.yaml`。\n\n---\n\n## 1. 概述\n\n该接口允许具有管理员权限的用户查看和修改系统的核心配置。配置修改后会即时生效并持久化到服务器的配置文件中。\n\n- **基础路径**: `/api/admin/config`\n- **认证方式**: 需要 `Authorization` 请求头传递 `Token`。\n- **权限要求**: 仅限管理员（`AdminUID` 匹配的用户）访问。如果 `adminUid` 设置为 `0`，则默认不限制管理员访问权限（慎用）。\n\n---\n\n## 2. 获取配置 (`GET`)\n\n获取当前的系统配置信息。\n\n### 请求头\n| 参数名          | 类型     | 是否必选 | 说明                 |\n|:----------------|:---------|:---------|:---------------------|\n| `Authorization` | `string` | 是       | 用户登录令牌 (Token) |\n\n### 响应示例\n**状态码**: `200 OK`\n\n```json\n{\n  \"code\": 1,\n  \"status\": true,\n  \"message\": \"成功\",\n  \"data\": {\n    \"fontSet\": \"local\",\n    \"registerIsEnable\": true,\n    \"fileChunkSize\": \"512KB\",\n    \"softDeleteRetentionTime\": \"7d\",\n    \"uploadSessionTimeout\": \"1d\",\n    \"adminUid\": 0\n  }\n}\n```\n\n---\n\n## 3. 更新配置 (`POST`)\n\n更新系统配置信息。\n\n### 请求头\n| 参数名          | 类型     | 是否必选 | 说明                 |\n|:----------------|:---------|:---------|:---------------------|\n| `Authorization` | `string` | 是       | 用户登录令牌 (Token) |\n| `Content-Type`  | `string` | 是       | `application/json`   |\n\n### 请求参数 (JSON)\n| 参数名                    | 类型      | 示例值    | 说明               |\n|:--------------------------|:----------|:----------|:-------------------|\n| `fontSet`                 | `string`  | `\"local\"` | 界面字体设置       |\n| `registerIsEnable`        | `boolean` | `true`    | 是否开启用户注册   |\n| `fileChunkSize`           | `string`  | `\"1MB\"`   | 文件同步分块大小   |\n| `softDeleteRetentionTime` | `string`  | `\"30d\"`   | 软删除笔记保留时长 |\n| `uploadSessionTimeout`    | `string`  | `\"24h\"`   | 上传会话过期时间   |\n| `adminUid`                | `integer` | `1`       | 管理员 UID         |\n\n---\n\n## 4. 参数详情与默认值\n\n| 字段                      | 类型      | 默认值    | 详细描述                                                                                                                                       |\n|:--------------------------|:----------|:----------|:-----------------------------------------------------------------------------------------------------------------------------------------------|\n| `fontSet`                 | `string`  | `\"local\"` | **字体设置**：<br>• 留空：不设置特定字体。<br>• `local`：使用系统本地字体。<br>• 字体链接：从网络加载特定字体。                                       |\n| `registerIsEnable`        | `boolean` | `true`    | **注册开关**：控制是否允许新用户注册。若关闭，注册 API 将返回错误。                                                                                |\n| `fileChunkSize`           | `string`  | `\"512KB\"` | **分块大小**：文件上传和下载时的分块大小。支持单位：`B`, `KB`, `MB`, `GB`（如 `1MB`, `1024`）。`0` 表示默认 512KB。                                   |\n| `softDeleteRetentionTime` | `string`  | `\"7d\"`    | **软删除保留时长**：笔记删除后进入回收站的保留时间（如 `30d`, `12h`）。超过此时间将被物理删除。建议设置足够长以确保离线设备同步。`0` 表示不自动清理。 |\n| `uploadSessionTimeout`    | `string`  | `\"1d\"`    | **上传会话超时**：文件分块上传会话的有效期（如 `5m`, `1d`）。`0` 表示永不超时。                                                                     |\n| `adminUid`                | `integer` | `0`       | **管理员 UID**：指定特定的用户 UID 作为管理员。`0` 表示不启用特定的管理员权限校验逻辑（建议在初始化后设置为实际的管理员 UID）。                     |\n\n---\n\n## 5. 常见错误\n\n| 状态码 | 业务代码 (code) | 说明                                                              |\n|:-------|:----------------|:------------------------------------------------------------------|\n| `401`  | `508`           | 登录状态失效，请重新登录。                                          |\n| `200`  | `445`           | **此操作需要管理员权限**：当前登录 UID 与配置的 `adminUid` 不匹配。 |\n| `200`  | `505`           | **参数验证失败**：请求数据格式不正确，或时间/容量格式无法解析。      |\n"
  },
  {
    "path": "docs/docs.go",
    "content": "// Package docs Code generated by swaggo/swag. DO NOT EDIT\npackage docs\n\nimport \"github.com/swaggo/swag\"\n\nconst docTemplate = `{\n    \"schemes\": {{ marshal .Schemes }},\n    \"swagger\": \"2.0\",\n    \"info\": {\n        \"description\": \"{{escape .Description}}\",\n        \"title\": \"{{.Title}}\",\n        \"contact\": {\n            \"name\": \"Haierkeys\",\n            \"url\": \"https://github.com/haierkeys\",\n            \"email\": \"haierkeys@gmail.com\"\n        },\n        \"license\": {\n            \"name\": \"Apache 2.0\",\n            \"url\": \"http://www.apache.org/licenses/LICENSE-2.0.html\"\n        },\n        \"version\": \"{{.Version}}\"\n    },\n    \"host\": \"{{.Host}}\",\n    \"basePath\": \"{{.BasePath}}\",\n    \"paths\": {\n        \"/api/admin/check\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Check if the current logged-in user has system admin privileges\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Check admin permission\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminCheckResponse\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/cloudflared_tunnel_download\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Trigger the download of cloudflared binary for the current platform\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"System\"\n                ],\n                \"summary\": \"Download cloudflared binary\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/config\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get full system configuration information, requires admin privileges\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Get full admin config\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminConfig\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Modify full system configuration information, requires admin privileges\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Update admin config\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Config Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.AdminConfig\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminConfig\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/config/cloudflare\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get Cloudflare tunnel configuration, requires admin privileges\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Get Cloudflare config\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminCloudflareConfig\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Modify Cloudflare tunnel configuration, requires admin privileges\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Update Cloudflare config\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Config Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.AdminCloudflareConfig\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminCloudflareConfig\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/config/ngrok\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get Ngrok tunnel configuration, requires admin privileges\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Get Ngrok config\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminNgrokConfig\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Modify Ngrok tunnel configuration, requires admin privileges\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Update Ngrok config\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Config Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.AdminNgrokConfig\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminNgrokConfig\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/config/user_database\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get user database configuration information, requires admin privileges\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Get user database config\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminUserDatabaseConfig\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Modify user database configuration information, requires admin privileges\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Update user database config\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Config Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.AdminUserDatabaseConfig\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminUserDatabaseConfig\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/config/user_database/test\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Test if the provided database configuration can connect successfully, requires admin privileges\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Test user database connection\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Config Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.AdminUserDatabaseConfig\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Connection failed\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/gc\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Manually run Go runtime GC and release memory to OS, requires admin privileges\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"System\"\n                ],\n                \"summary\": \"Trigger manual GC\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/restart\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Gracefully restart the server\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"System\"\n                ],\n                \"summary\": \"Trigger server restart\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/system/info\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get server runtime, CPU, memory, host and process info, requires admin privileges\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"System\"\n                ],\n                \"summary\": \"Get system stats\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminSystemInfo\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/upgrade\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Download latest version and restart server\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"System\"\n                ],\n                \"summary\": \"Trigger server upgrade\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Version to upgrade (e.g. 2.0.10 or latest)\",\n                        \"name\": \"version\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/ws_clients\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get a list of all current WebSocket connections, requires admin privileges\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"System\"\n                ],\n                \"summary\": \"Get connected WebSocket clients\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"$ref\": \"#/definitions/app.WSClientInfo\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/backup/config\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Backup\"\n                ],\n                \"summary\": \"Update backup configuration\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Backup Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.BackupConfigRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.BackupConfigDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            },\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Backup\"\n                ],\n                \"summary\": \"Delete backup configuration\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 1,\n                        \"description\": \"ID // ID\",\n                        \"name\": \"id\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/backup/configs\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Backup\"\n                ],\n                \"summary\": \"Get backup configurations\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"$ref\": \"#/definitions/dto.BackupConfigDTO\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/backup/execute\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Backup\"\n                ],\n                \"summary\": \"Trigger a backup manually\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Backup Execute Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.BackupExecuteRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/backup/historys\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Backup\"\n                ],\n                \"summary\": \"Get backup history list\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 1,\n                        \"description\": \"Config ID // 配置 ID\",\n                        \"name\": \"configId\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 1,\n                        \"description\": \"Page number // 页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 10,\n                        \"description\": \"Page size // 每页大小\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.BackupHistoryDTO\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/file\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get raw binary data of an attachment by path, supports strong cache control\",\n                \"produces\": [\n                    \"application/octet-stream\"\n                ],\n                \"tags\": [\n                    \"File\"\n                ],\n                \"summary\": \"Get attachment content\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"boolean\",\n                        \"example\": false,\n                        \"description\": \"Is in recycle bin // 是否在回收站\",\n                        \"name\": \"isRecycle\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"Image.png\",\n                        \"description\": \"File path // 文件路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"fhash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"type\": \"file\"\n                        }\n                    }\n                }\n            },\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Permanently delete a specific attachment record and its physical file\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"File\"\n                ],\n                \"summary\": \"Delete attachment\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"Image.png\",\n                        \"description\": \"File path // 文件路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"fhash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.FileDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/file/info\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get attachment metadata (FileDTO) by path\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"File\"\n                ],\n                \"summary\": \"Get attachment info\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"boolean\",\n                        \"example\": false,\n                        \"description\": \"Is in recycle bin // 是否在回收站\",\n                        \"name\": \"isRecycle\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"Image.png\",\n                        \"description\": \"File path // 文件路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"fhash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.FileDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/file/recycle-clear\": {\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Permanently clear selected files from recycle bin\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"File\"\n                ],\n                \"summary\": \"Clear recycle bin\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Clear Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.FileRecycleClearRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/file/rename\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Rename an attachment to a new path\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"File\"\n                ],\n                \"summary\": \"Rename attachment\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Rename Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.FileRenameRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.FileDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/file/restore\": {\n            \"put\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Restore deleted attachment from trash\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"File\"\n                ],\n                \"summary\": \"Restore attachment\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Restore Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.FileRestoreRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.FileDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/files\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get attachment list for current user with pagination, search, filter, and sort support\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"File\"\n                ],\n                \"summary\": \"Get file list\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"boolean\",\n                        \"example\": false,\n                        \"description\": \"Is in recycle bin // 是否在回收站\",\n                        \"name\": \"isRecycle\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"vacation\",\n                        \"description\": \"Search keyword // 搜索关键词\",\n                        \"name\": \"keyword\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"mtime\",\n                        \"description\": \"Sort by field // 排序字段\",\n                        \"name\": \"sortBy\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"desc\",\n                        \"description\": \"Sort order // 排序顺序\",\n                        \"name\": \"sortOrder\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page number // 页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page size // 每页数量\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.FileDTO\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/folder\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get folder info for current user by path or pathHash\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Folder\"\n                ],\n                \"summary\": \"Get folder info\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"Projects/Work\",\n                        \"description\": \"Folder path // 文件夹路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"fhash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.FolderDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Create a new folder or restore a deleted one by path\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Folder\"\n                ],\n                \"summary\": \"Create folder\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Create Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.FolderCreateRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.FolderDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Soft delete a folder by path or pathHash\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Folder\"\n                ],\n                \"summary\": \"Delete folder\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Delete Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.FolderDeleteRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/folder/files\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"List non-deleted files in a specific folder with pagination and sorting\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Folder\"\n                ],\n                \"summary\": \"List files in folder\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"Projects\",\n                        \"description\": \"Folder path // 文件夹路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"fhash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"mtime\",\n                        \"description\": \"Sort by field // 排序字段\",\n                        \"name\": \"sortBy\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"desc\",\n                        \"description\": \"Sort order // 排序顺序\",\n                        \"name\": \"sortOrder\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page number // 页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page size // 每页数量\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.FileDTO\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/folder/notes\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"List non-deleted notes in a specific folder with pagination and sorting\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Folder\"\n                ],\n                \"summary\": \"List notes in folder\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"Projects\",\n                        \"description\": \"Folder path // 文件夹路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"fhash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"mtime\",\n                        \"description\": \"Sort by field // 排序字段\",\n                        \"name\": \"sortBy\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"desc\",\n                        \"description\": \"Sort order // 排序顺序\",\n                        \"name\": \"sortOrder\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page number // 页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page size // 每页数量\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/folder/tree\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get the complete folder tree structure for a vault\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Folder\"\n                ],\n                \"summary\": \"Get folder tree\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 3,\n                        \"description\": \"Tree depth // 树深度\",\n                        \"name\": \"depth\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.FolderTreeResponse\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/folders\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get folder list for current user by parent path or pathHash\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Folder\"\n                ],\n                \"summary\": \"Get folder list\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"Projects\",\n                        \"description\": \"Folder path // 文件夹路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"fhash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"$ref\": \"#/definitions/dto.FolderDTO\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/git-sync/config\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"GitSync\"\n                ],\n                \"summary\": \"Update git sync configuration\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Git Sync Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.GitSyncConfigRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.GitSyncConfigDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            },\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"GitSync\"\n                ],\n                \"summary\": \"Delete git sync configuration\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Git Sync ID\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.GitSyncDeleteRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/git-sync/config/clean\": {\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"GitSync\"\n                ],\n                \"summary\": \"Clean local git workspace\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Clean Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.GitSyncCleanRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/git-sync/config/execute\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"GitSync\"\n                ],\n                \"summary\": \"Trigger a manual git sync\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Execute Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.GitSyncExecuteRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/git-sync/configs\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"GitSync\"\n                ],\n                \"summary\": \"Get git sync configurations\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"$ref\": \"#/definitions/dto.GitSyncConfigDTO\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/git-sync/histories\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"GitSync\"\n                ],\n                \"summary\": \"Get git sync histories\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"name\": \"configId\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.GitSyncHistoryDTO\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/git-sync/validate\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"GitSync\"\n                ],\n                \"summary\": \"Validate git sync parameters\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Validation Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.GitSyncValidateRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/health\": {\n            \"get\": {\n                \"description\": \"Check service health status, including database connection\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"System\"\n                ],\n                \"summary\": \"Health check\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/api_router.HealthResponse\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get specific note content and metadata by path or path hash\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Get note details\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"boolean\",\n                        \"example\": false,\n                        \"description\": \"Is in recycle bin // 是否在回收站\",\n                        \"name\": \"isRecycle\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"ReadMe.md\",\n                        \"description\": \"Note path // 笔记路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"hash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteWithFileLinksResponse\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Handle note creation, modification, or renaming (identified by path change)\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Create or update note\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Note Content\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NoteModifyOrCreateRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Move note to trash\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Delete note\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"ReadMe.md\",\n                        \"description\": \"Note path // 笔记路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"hash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/append\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Append content to the end of a note\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Append content to note\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Append Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NoteAppendRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/backlinks\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get all other notes that link to the specified note\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Get backlinks\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"ReadMe.md\",\n                        \"description\": \"Note path // 笔记路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"hash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"$ref\": \"#/definitions/dto.NoteLinkItem\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/frontmatter\": {\n            \"patch\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Update or delete note frontmatter fields\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Modify note frontmatter\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Frontmatter Modification Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NotePatchFrontmatterRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/histories\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get all history records for a specific note with pagination\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note History\"\n                ],\n                \"summary\": \"Get note history list\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"boolean\",\n                        \"example\": false,\n                        \"description\": \"Is in recycle bin // 是否在回收站\",\n                        \"name\": \"isRecycle\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"ReadMe.md\",\n                        \"description\": \"Note path // 笔记路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"hash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page number // 页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page size // 每页数量\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.NoteHistoryDTO\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/history\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get specific note history content by history record ID\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note History\"\n                ],\n                \"summary\": \"Get note history details\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"format\": \"int64\",\n                        \"description\": \"History Record ID\",\n                        \"name\": \"id\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteHistoryDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/history/restore\": {\n            \"put\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Restore note content to a specific history version\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note History\"\n                ],\n                \"summary\": \"Restore note from history\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Restore Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NoteHistoryRestoreRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/move\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Move a note to a new path\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Move note\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Move Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NoteMoveRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/outlinks\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get other notes that the specified note links to\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Get outgoing links\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"ReadMe.md\",\n                        \"description\": \"Note path // 笔记路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"hash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"$ref\": \"#/definitions/dto.NoteLinkItem\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/prepend\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Insert content at the beginning of a note (after frontmatter)\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Prepend content to note\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Prepend Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NotePrependRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/recycle-clear\": {\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Permanently clear selected notes from recycle bin\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Clear recycle bin\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Clear Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NoteRecycleClearRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/rename\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Rename a note to a new path\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Rename note\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Rename Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NoteRenameRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/replace\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Perform find and replace operation in a note, supporting regular expressions\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Find and replace in note\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Find and Replace Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NoteReplaceRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteReplaceResponse\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/restore\": {\n            \"put\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Restore deleted note from trash\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Restore note\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Restore Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NoteRestoreRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/notes\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get note list for current user with pagination\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Get note list\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"boolean\",\n                        \"example\": false,\n                        \"description\": \"Is in recycle bin // 是否在回收站\",\n                        \"name\": \"isRecycle\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"todo\",\n                        \"description\": \"Search keyword // 搜索关键词\",\n                        \"name\": \"keyword\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"note1.md,note2.md\",\n                        \"description\": \"Comma-separated exact path list for share filter // 逗号分隔的精确路径列表，用于分享筛选\",\n                        \"name\": \"paths\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"boolean\",\n                        \"example\": true,\n                        \"description\": \"Whether to search content // 是否搜索内容\",\n                        \"name\": \"searchContent\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"content\",\n                        \"description\": \"Search mode (path, content) // 搜索模式（路径、内容）\",\n                        \"name\": \"searchMode\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"mtime\",\n                        \"description\": \"Sort by field // 排序字段\",\n                        \"name\": \"sortBy\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"desc\",\n                        \"description\": \"Sort order // 排序顺序\",\n                        \"name\": \"sortOrder\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page number // 页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page size // 每页数量\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.NoteNoContentDTO\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/notes/share-paths\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"tags\": [\n                    \"Share\"\n                ],\n                \"summary\": \"Get active shared note paths\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Vault name\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"type\": \"string\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/setting\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get setting info for current user by path or pathHash\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Setting\"\n                ],\n                \"summary\": \"Get setting info\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"User/Theme\",\n                        \"description\": \"Setting path // 配置路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"hash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.SettingDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Create a new setting or update an existing one\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Setting\"\n                ],\n                \"summary\": \"Create or update setting\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Create/Update Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.SettingModifyOrCreateRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.SettingDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Soft delete a setting by path or pathHash\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Setting\"\n                ],\n                \"summary\": \"Delete setting\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Delete Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.SettingDeleteRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/setting/rename\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Rename a setting and update its path and pathHash\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Setting\"\n                ],\n                \"summary\": \"Rename setting\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Rename Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.SettingRenameRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.SettingDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/settings\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get setting list for current user with pagination and keyword filtering\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Setting\"\n                ],\n                \"summary\": \"Get setting list\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"User/\",\n                        \"description\": \"Keyword // 关键词\",\n                        \"name\": \"keyword\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page number // 页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page size // 每页数量\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.SettingDTO\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/share\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get share token and info by vault and path\",\n                \"tags\": [\n                    \"Share\"\n                ],\n                \"summary\": \"Query share by path\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"ReadMe.md\",\n                        \"description\": \"Resource path // 资源路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"hash123\",\n                        \"description\": \"Resource path Hash // 资源路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"defaultVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.ShareCreateResponse\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Create a share token for a specific note or attachment, automatically resolve attachment references and authorize\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Share\"\n                ],\n                \"summary\": \"Create resource share\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Share Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.ShareCreateRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.ShareCreateResponse\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Cancel a share by ID or path parameters\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Share\"\n                ],\n                \"summary\": \"Cancel share\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Cancel Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.ShareCancelRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/share/file\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"ShareAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get raw binary data of a specific attachment via share token\",\n                \"produces\": [\n                    \"application/octet-stream\"\n                ],\n                \"tags\": [\n                    \"Share\"\n                ],\n                \"summary\": \"Get shared attachment content\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"Share-Token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 1,\n                        \"description\": \"Resource ID // 资源 ID\",\n                        \"name\": \"id\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"123456\",\n                        \"description\": \"Share password // 分享密码\",\n                        \"name\": \"password\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"type\": \"file\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/share/note\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"ShareAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get specific note content (restricted read-only access) via share token\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Share\"\n                ],\n                \"summary\": \"Get shared note details\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"Share-Token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 1,\n                        \"description\": \"Resource ID // 资源 ID\",\n                        \"name\": \"id\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"123456\",\n                        \"description\": \"Share password // 分享密码\",\n                        \"name\": \"password\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/share/password\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Set or update password for a share record\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Share\"\n                ],\n                \"summary\": \"Update share password\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Update Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.SharePasswordUpdateRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/share/short_link\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Call sink.cool API to generate a short link for a given share record\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Share\"\n                ],\n                \"summary\": \"Create short link for share\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Short Link Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.ShareShortLinkCreateRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"string\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/shares\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get all active and inactive shares of the user, supports sorting and pagination\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Share\"\n                ],\n                \"summary\": \"List shares\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Sort field: created_at, updated_at, expires_at (default: created_at)\",\n                        \"name\": \"sort_by\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Sort direction: asc or desc (default: desc)\",\n                        \"name\": \"sort_order\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page number\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page size\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.ShareListItem\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/storage\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Storage\"\n                ],\n                \"summary\": \"Get storage configuration list\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"$ref\": \"#/definitions/dto.StorageDTO\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Storage\"\n                ],\n                \"summary\": \"Create or update storage configuration\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Storage Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.StoragePostRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.StorageDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Storage\"\n                ],\n                \"summary\": \"Delete storage configuration\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"format\": \"int64\",\n                        \"description\": \"Storage ID\",\n                        \"name\": \"id\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/storage/enabled_types\": {\n            \"get\": {\n                \"description\": \"Get list of enabled storage types. Possible values: localfs, oss, s3, r2, minio, webdav\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Storage\"\n                ],\n                \"summary\": \"Get enabled storage types\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success. Data contains: localfs, oss, s3, r2, minio, webdav\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"type\": \"string\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/storage/validate\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Storage\"\n                ],\n                \"summary\": \"Validate storage connection\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Storage Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.StoragePostRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/support\": {\n            \"get\": {\n                \"description\": \"Get support records for the specified language with pagination and sorting\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"System\"\n                ],\n                \"summary\": \"Get support records\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Language code (default: en)\",\n                        \"name\": \"lang\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Sort by field (amount, time, name, item)\",\n                        \"name\": \"sortBy\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Sort order (asc, desc)\",\n                        \"name\": \"sortOrder\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page number\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page size\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/app.ListRes\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/sync-logs\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get sync log list for current user with optional type/action filters and pagination\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Sync Log\"\n                ],\n                \"summary\": \"Get sync log list\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"modify\",\n                        \"description\": \"Action type // 操作类型\",\n                        \"name\": \"action\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"note\",\n                        \"description\": \"Resource type: note / file / setting / folder // 资源类型\",\n                        \"name\": \"type\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name (optional filter) // 保险库名称（可选过滤）\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page number // 页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page size // 每页数量\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.SyncLogDTO\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/user/change_password\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Handle password change request for current user, validate old password and update new password.\\n处理当前用户的修改密码请求，验证旧密码并更新新密码。\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"User\"\n                ],\n                \"summary\": \"Change user password\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Change Password Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.UserChangePasswordRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Parameters / Old Password Incorrect\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Unauthorized\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/user/info\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Handle request to get current user info.\\n处理获取当前用户信息的请求。\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"User\"\n                ],\n                \"summary\": \"Get user info\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.UserDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Unauthorized\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/user/login\": {\n            \"post\": {\n                \"description\": \"Handle user login HTTP request, validate parameters and return auth token.\\n处理用户登录 HTTP 请求，验证参数并返回认证 Token。\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"User\"\n                ],\n                \"summary\": \"User login\",\n                \"parameters\": [\n                    {\n                        \"description\": \"Login Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.UserLoginRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.UserDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Parameters / Invalid Credentials\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/user/register\": {\n            \"post\": {\n                \"description\": \"Handle user registration HTTP request, validate parameters and call UserService. Registration may be disabled in server settings.\\n处理用户注册 HTTP 请求，验证参数并调用 UserService。注册功能可能在服务器设置中被禁用。\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"User\"\n                ],\n                \"summary\": \"User registration\",\n                \"parameters\": [\n                    {\n                        \"description\": \"Register Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.UserCreateRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.UserDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Parameters / Registration Disabled / User Already Exists\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/vault\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get all note vaults for current user\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Vault\"\n                ],\n                \"summary\": \"Get vault list\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"$ref\": \"#/definitions/dto.VaultDTO\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Be used to create a new vault or update an existing vault configuration based on the ID in the request parameters\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Vault\"\n                ],\n                \"summary\": \"Create or update vault\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Vault Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.VaultPostRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.VaultDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Permanently delete a specific note vault and all associated notes and attachments\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Vault\"\n                ],\n                \"summary\": \"Delete vault\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"minimum\": 1,\n                        \"type\": \"integer\",\n                        \"example\": 1,\n                        \"description\": \"Vault ID // 保险库 ID\",\n                        \"name\": \"id\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/vault/get\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get specific vault configuration details by vault ID\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Vault\"\n                ],\n                \"summary\": \"Get vault details\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"format\": \"int64\",\n                        \"description\": \"Vault ID\",\n                        \"name\": \"id\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.VaultDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/version\": {\n            \"get\": {\n                \"description\": \"Get current server software version, Git tag, and build time\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"System\"\n                ],\n                \"summary\": \"Get server version info\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.VersionDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/webgui/config\": {\n            \"get\": {\n                \"description\": \"Get non-sensitive configuration required for frontend display, such as font settings, registration status, etc.\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Get WebGUI basic config\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminWebGUIConfig\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        }\n    },\n    \"definitions\": {\n        \"api_router.HealthResponse\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"database\": {\n                    \"description\": \"\\\"connected\\\" or \\\"error\\\" // \\\"connected\\\" 或 \\\"error\\\"\",\n                    \"type\": \"string\"\n                },\n                \"status\": {\n                    \"description\": \"\\\"healthy\\\" or \\\"unhealthy\\\" // \\\"healthy\\\" 或 \\\"unhealthy\\\"\",\n                    \"type\": \"string\"\n                },\n                \"uptime\": {\n                    \"description\": \"Uptime (seconds) // 运行时间（秒）\",\n                    \"type\": \"number\"\n                },\n                \"version\": {\n                    \"description\": \"Service version number // 服务版本号\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"app.ListRes\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"list\": {\n                    \"description\": \"Data list // 数据清单\"\n                },\n                \"pager\": {\n                    \"description\": \"Pagination info // 翻页信息\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/app.Pager\"\n                        }\n                    ]\n                }\n            }\n        },\n        \"app.Pager\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"page\": {\n                    \"description\": \"Page number // 页码\",\n                    \"type\": \"integer\"\n                },\n                \"pageSize\": {\n                    \"description\": \"Page size // 每页数量\",\n                    \"type\": \"integer\"\n                },\n                \"totalRows\": {\n                    \"description\": \"Total rows // 总行数\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"app.Res\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"code\": {\n                    \"type\": \"integer\"\n                },\n                \"context\": {},\n                \"data\": {},\n                \"details\": {},\n                \"message\": {},\n                \"status\": {\n                    \"type\": \"boolean\"\n                },\n                \"vault\": {}\n            }\n        },\n        \"app.WSClientInfo\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"clientName\": {\n                    \"type\": \"string\"\n                },\n                \"clientType\": {\n                    \"type\": \"string\"\n                },\n                \"clientVersion\": {\n                    \"type\": \"string\"\n                },\n                \"nickname\": {\n                    \"type\": \"string\"\n                },\n                \"platformInfo\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": {\n                        \"type\": \"boolean\"\n                    }\n                },\n                \"remoteAddr\": {\n                    \"type\": \"string\"\n                },\n                \"startTime\": {\n                    \"type\": \"string\"\n                },\n                \"traceId\": {\n                    \"type\": \"string\"\n                },\n                \"uid\": {\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"diffmatchpatch.Diff\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"text\": {\n                    \"type\": \"string\"\n                },\n                \"type\": {\n                    \"$ref\": \"#/definitions/diffmatchpatch.Operation\"\n                }\n            }\n        },\n        \"diffmatchpatch.Operation\": {\n            \"type\": \"integer\",\n            \"format\": \"int32\",\n            \"enum\": [\n                -1,\n                1,\n                0\n            ],\n            \"x-enum-varnames\": [\n                \"DiffDelete\",\n                \"DiffInsert\",\n                \"DiffEqual\"\n            ]\n        },\n        \"dto.AdminCPUInfo\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"loadAvg\": {\n                    \"description\": \"Load average // 平均负载\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/dto.AdminLoadInfo\"\n                        }\n                    ]\n                },\n                \"logicalCores\": {\n                    \"description\": \"Logical cores // 逻辑核心数\",\n                    \"type\": \"integer\"\n                },\n                \"modelName\": {\n                    \"description\": \"Model name // 型号\",\n                    \"type\": \"string\"\n                },\n                \"percent\": {\n                    \"description\": \"Usage percentage per core // 每个核心的使用率\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"type\": \"number\"\n                    }\n                },\n                \"physicalCores\": {\n                    \"description\": \"Physical cores // 物理核心数\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.AdminCheckResponse\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"isAdmin\": {\n                    \"description\": \"Whether have admin privileges // 是否具有管理员权限\",\n                    \"type\": \"boolean\"\n                }\n            }\n        },\n        \"dto.AdminCloudflareConfig\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"enabled\": {\n                    \"description\": \"Whether to enable cloudflare tunnel // 是否启用 cloudflare 隧道\",\n                    \"type\": \"boolean\"\n                },\n                \"logEnabled\": {\n                    \"description\": \"Whether to enable cloudflare tunnel logging // 是否开启 cloudflare 隧道日志\",\n                    \"type\": \"boolean\"\n                },\n                \"token\": {\n                    \"description\": \"cloudflare tunnel token // cloudflare 隧道令牌\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.AdminConfig\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"adminUid\": {\n                    \"description\": \"Admin UID // 管理员 UID\",\n                    \"type\": \"integer\"\n                },\n                \"authTokenKey\": {\n                    \"description\": \"Auth token key // 认证 Token 密钥\",\n                    \"type\": \"string\"\n                },\n                \"defaultApiFolder\": {\n                    \"description\": \"Default API folder // 默认 API 目录\",\n                    \"type\": \"string\"\n                },\n                \"fileChunkSize\": {\n                    \"description\": \"File chunk size // 文件分块大小\",\n                    \"type\": \"string\"\n                },\n                \"fontSet\": {\n                    \"description\": \"Font set // 字体设置\",\n                    \"type\": \"string\"\n                },\n                \"historyKeepVersions\": {\n                    \"description\": \"History versions to keep // 历史版本保留数\",\n                    \"type\": \"integer\"\n                },\n                \"historySaveDelay\": {\n                    \"description\": \"History save delay // 历史保存延迟\",\n                    \"type\": \"string\"\n                },\n                \"pullSource\": {\n                    \"description\": \"Data pull source: auto | github | cnb // 数据拉取源：auto | github | cnb\",\n                    \"type\": \"string\"\n                },\n                \"registerIsEnable\": {\n                    \"description\": \"Registration enablement // 是否开启注册\",\n                    \"type\": \"boolean\"\n                },\n                \"shareTokenExpiry\": {\n                    \"description\": \"Share token expiry // 分享 Token 有效期\",\n                    \"type\": \"string\"\n                },\n                \"shareTokenKey\": {\n                    \"description\": \"Share token key // 分享 Token 密钥\",\n                    \"type\": \"string\"\n                },\n                \"softDeleteRetentionTime\": {\n                    \"description\": \"Soft delete retention time // 软删除保留时间\",\n                    \"type\": \"string\"\n                },\n                \"tokenExpiry\": {\n                    \"description\": \"Token expiry // Token 有效期\",\n                    \"type\": \"string\"\n                },\n                \"uploadSessionTimeout\": {\n                    \"description\": \"Upload session timeout // 上传会话超时时间\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.AdminHostInfo\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"arch\": {\n                    \"description\": \"Architecture // 架构\",\n                    \"type\": \"string\"\n                },\n                \"currentTime\": {\n                    \"description\": \"Current system time // 当前系统时间\",\n                    \"type\": \"string\"\n                },\n                \"hostname\": {\n                    \"description\": \"Hostname // 主机名\",\n                    \"type\": \"string\"\n                },\n                \"kernelVersion\": {\n                    \"description\": \"Kernel version // 内核版本\",\n                    \"type\": \"string\"\n                },\n                \"os\": {\n                    \"description\": \"Operating system // 操作系统\",\n                    \"type\": \"string\"\n                },\n                \"osPretty\": {\n                    \"description\": \"Detailed OS name // 详细操作系统名称\",\n                    \"type\": \"string\"\n                },\n                \"platform\": {\n                    \"description\": \"Platform name // 平台\",\n                    \"type\": \"string\"\n                },\n                \"timezone\": {\n                    \"description\": \"Time zone name // 时区名称\",\n                    \"type\": \"string\"\n                },\n                \"timezoneOffset\": {\n                    \"description\": \"Time zone offset in seconds // 时区偏移（秒）\",\n                    \"type\": \"integer\"\n                },\n                \"uptime\": {\n                    \"description\": \"System uptime // 系统运行时间\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.AdminLoadInfo\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"load1\": {\n                    \"description\": \"Load 1 min // 1分钟负载\",\n                    \"type\": \"number\"\n                },\n                \"load15\": {\n                    \"description\": \"Load 15 min // 15分钟负载\",\n                    \"type\": \"number\"\n                },\n                \"load5\": {\n                    \"description\": \"Load 5 min // 5分钟负载\",\n                    \"type\": \"number\"\n                }\n            }\n        },\n        \"dto.AdminMemoryInfo\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"available\": {\n                    \"description\": \"Available memory // 可用内存\",\n                    \"type\": \"integer\"\n                },\n                \"swapTotal\": {\n                    \"description\": \"Total swap space // 交换区总量\",\n                    \"type\": \"integer\"\n                },\n                \"swapUsed\": {\n                    \"description\": \"Used swap space // 交换区已用\",\n                    \"type\": \"integer\"\n                },\n                \"swapUsedPercent\": {\n                    \"description\": \"Swap usage percentage // 交换区使用率\",\n                    \"type\": \"number\"\n                },\n                \"total\": {\n                    \"description\": \"Total physical memory // 系统总内存\",\n                    \"type\": \"integer\"\n                },\n                \"used\": {\n                    \"description\": \"Used memory // 已用内存\",\n                    \"type\": \"integer\"\n                },\n                \"usedPercent\": {\n                    \"description\": \"Memory usage percentage // 内存使用率\",\n                    \"type\": \"number\"\n                }\n            }\n        },\n        \"dto.AdminNgrokConfig\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"authToken\": {\n                    \"description\": \"ngrok auth token // ngrok 认证令牌\",\n                    \"type\": \"string\"\n                },\n                \"domain\": {\n                    \"description\": \"Custom domain // 自定义域名\",\n                    \"type\": \"string\"\n                },\n                \"enabled\": {\n                    \"description\": \"Whether to enable ngrok tunnel // 是否启用 ngrok 隧道\",\n                    \"type\": \"boolean\"\n                }\n            }\n        },\n        \"dto.AdminProcessInfo\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"cpuPercent\": {\n                    \"description\": \"CPU Usage percentage // CPU 使用率\",\n                    \"type\": \"number\"\n                },\n                \"memoryPercent\": {\n                    \"description\": \"Memory Usage percentage // 内存使用率\",\n                    \"type\": \"number\"\n                },\n                \"name\": {\n                    \"description\": \"Process Name // 进程名称\",\n                    \"type\": \"string\"\n                },\n                \"pid\": {\n                    \"description\": \"Process ID // 进程 ID\",\n                    \"type\": \"integer\"\n                },\n                \"ppid\": {\n                    \"description\": \"Parent Process ID // 父进程 ID\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.AdminRuntimeInfo\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"buckHashSys\": {\n                    \"description\": \"Memory obtained from system for profiling bucket hash table (bytes) // 分析桶哈希表占用的系统内存\",\n                    \"type\": \"integer\"\n                },\n                \"gcSys\": {\n                    \"description\": \"Memory obtained from system for metadata for GC (bytes) // GC 元数据占用的系统内存\",\n                    \"type\": \"integer\"\n                },\n                \"heapIdle\": {\n                    \"description\": \"Memory in idle spans (bytes) // 空闲 Span 占用的内存\",\n                    \"type\": \"integer\"\n                },\n                \"heapInuse\": {\n                    \"description\": \"Memory in in-use spans (bytes) // 正在使用的 Span 占用的内存\",\n                    \"type\": \"integer\"\n                },\n                \"heapReleased\": {\n                    \"description\": \"Memory released to OS (bytes) // 释放回操作系统的内存（字节）\",\n                    \"type\": \"integer\"\n                },\n                \"heapSys\": {\n                    \"description\": \"Memory obtained from system for heap (bytes) // 堆占用的系统内存\",\n                    \"type\": \"integer\"\n                },\n                \"mCacheSys\": {\n                    \"description\": \"Memory obtained from system for mcache (bytes) // mcache 占用的系统内存\",\n                    \"type\": \"integer\"\n                },\n                \"mSpanSys\": {\n                    \"description\": \"Memory obtained from system for mspan (bytes) // mspan 占用的系统内存\",\n                    \"type\": \"integer\"\n                },\n                \"memAlloc\": {\n                    \"description\": \"Allocated memory (bytes) // 已分配内存（字节）\",\n                    \"type\": \"integer\"\n                },\n                \"memSys\": {\n                    \"description\": \"Memory obtained from system (bytes) // 从系统获取的内存（字节）\",\n                    \"type\": \"integer\"\n                },\n                \"memTotal\": {\n                    \"description\": \"Total memory allocated (bytes) // 累计分配内存（字节）\",\n                    \"type\": \"integer\"\n                },\n                \"nextGc\": {\n                    \"description\": \"Target heap size for the next GC cycle // 下次 GC 的目标堆大小\",\n                    \"type\": \"integer\"\n                },\n                \"numGc\": {\n                    \"description\": \"Number of completed GC cycles // GC 次数\",\n                    \"type\": \"integer\"\n                },\n                \"numGoroutine\": {\n                    \"description\": \"Number of goroutines // Goroutine 数量\",\n                    \"type\": \"integer\"\n                },\n                \"otherSys\": {\n                    \"description\": \"Other system memory (bytes) // 其他系统内存\",\n                    \"type\": \"integer\"\n                },\n                \"stackSys\": {\n                    \"description\": \"Memory obtained from system for stack (bytes) // 栈占用的系统内存\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.AdminSystemInfo\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"cpu\": {\n                    \"description\": \"CPU information // CPU 信息\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/dto.AdminCPUInfo\"\n                        }\n                    ]\n                },\n                \"host\": {\n                    \"description\": \"Host information // 主机信息\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/dto.AdminHostInfo\"\n                        }\n                    ]\n                },\n                \"memory\": {\n                    \"description\": \"Memory information // 内存信息\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/dto.AdminMemoryInfo\"\n                        }\n                    ]\n                },\n                \"process\": {\n                    \"description\": \"Process information // 进程信息\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/dto.AdminProcessInfo\"\n                        }\n                    ]\n                },\n                \"runtimeStatus\": {\n                    \"description\": \"Go runtime status // Go 运行时状态\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/dto.AdminRuntimeInfo\"\n                        }\n                    ]\n                },\n                \"startTime\": {\n                    \"description\": \"Start time // 启动时间\",\n                    \"type\": \"string\"\n                },\n                \"uptime\": {\n                    \"description\": \"Uptime (seconds) // 运行时间（秒）\",\n                    \"type\": \"number\"\n                }\n            }\n        },\n        \"dto.AdminUserDatabaseConfig\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"charset\": {\n                    \"description\": \"Charset // 字符集\",\n                    \"type\": \"string\"\n                },\n                \"connMaxIdleTime\": {\n                    \"description\": \"Connection max idle time // 空闲连接最大生命周期\",\n                    \"type\": \"string\"\n                },\n                \"connMaxLifetime\": {\n                    \"description\": \"Connection max lifetime // 连接最大生命周期\",\n                    \"type\": \"string\"\n                },\n                \"host\": {\n                    \"description\": \"Host // 主机\",\n                    \"type\": \"string\"\n                },\n                \"maxIdleConns\": {\n                    \"description\": \"Max idle connections // 最大闲置连接数\",\n                    \"type\": \"integer\"\n                },\n                \"maxOpenConns\": {\n                    \"description\": \"Max open connections // 最大打开连接数\",\n                    \"type\": \"integer\"\n                },\n                \"maxWriteConcurrency\": {\n                    \"description\": \"Max write concurrency // 最大并发写入数\",\n                    \"type\": \"integer\"\n                },\n                \"name\": {\n                    \"description\": \"Database name // 数据库名\",\n                    \"type\": \"string\"\n                },\n                \"parseTime\": {\n                    \"description\": \"Parse time // 是否解析时间\",\n                    \"type\": \"boolean\"\n                },\n                \"password\": {\n                    \"description\": \"Password // 密码\",\n                    \"type\": \"string\"\n                },\n                \"path\": {\n                    \"description\": \"SQLite database file path // SQLite 数据库文件路径\",\n                    \"type\": \"string\"\n                },\n                \"port\": {\n                    \"description\": \"Port // 端口\",\n                    \"type\": \"integer\"\n                },\n                \"schema\": {\n                    \"description\": \"Database schema (postgres only) // 数据库 Schema\",\n                    \"type\": \"string\"\n                },\n                \"sslMode\": {\n                    \"description\": \"SSL mode (postgres only) // SSL 模式\",\n                    \"type\": \"string\"\n                },\n                \"type\": {\n                    \"description\": \"Database type (mysql, postgres, sqlite) // 数据库类型\",\n                    \"type\": \"string\",\n                    \"enum\": [\n                        \"mysql\",\n                        \"postgres\",\n                        \"sqlite\"\n                    ]\n                },\n                \"userName\": {\n                    \"description\": \"Username // 用户名\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.AdminWebGUIConfig\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"adminUid\": {\n                    \"description\": \"Admin UID // 管理员 UID\",\n                    \"type\": \"integer\"\n                },\n                \"fontSet\": {\n                    \"description\": \"Font set // 字体设置\",\n                    \"type\": \"string\"\n                },\n                \"registerIsEnable\": {\n                    \"description\": \"Registration enablement // 是否开启注册\",\n                    \"type\": \"boolean\"\n                }\n            }\n        },\n        \"dto.BackupConfigDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"createdAt\": {\n                    \"description\": \"Created at // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"cronExpression\": {\n                    \"description\": \"Cron expression // Cron表达式\",\n                    \"type\": \"string\"\n                },\n                \"cronStrategy\": {\n                    \"description\": \"Cron strategy // 定时策略\",\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"description\": \"Config ID // 配置ID\",\n                    \"type\": \"integer\"\n                },\n                \"includeVaultName\": {\n                    \"description\": \"Whether sync path includes vault name // 同步路径是否包含仓库名\",\n                    \"type\": \"boolean\"\n                },\n                \"isEnabled\": {\n                    \"description\": \"Is enabled // 是否启用\",\n                    \"type\": \"boolean\"\n                },\n                \"lastMessage\": {\n                    \"description\": \"Last run result message // 上次运行结果消息\",\n                    \"type\": \"string\"\n                },\n                \"lastRunTime\": {\n                    \"description\": \"Last run time // 上次运行时间\",\n                    \"type\": \"string\"\n                },\n                \"lastStatus\": {\n                    \"description\": \"Last status (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Stopped) // 上次状态 (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Stopped)\",\n                    \"type\": \"integer\"\n                },\n                \"nextRunTime\": {\n                    \"description\": \"Next run time // 下次运行时间\",\n                    \"type\": \"string\"\n                },\n                \"retentionDays\": {\n                    \"description\": \"Retention days // 保留天数\",\n                    \"type\": \"integer\"\n                },\n                \"storageIds\": {\n                    \"description\": \"Storage ID list // 存储ID列表\",\n                    \"type\": \"string\"\n                },\n                \"type\": {\n                    \"description\": \"Backup type (full, incremental, sync) // 备份类型 (full, incremental, sync)\",\n                    \"type\": \"string\"\n                },\n                \"uid\": {\n                    \"description\": \"User UID // 用户ID\",\n                    \"type\": \"integer\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at // 更新时间\",\n                    \"type\": \"string\"\n                },\n                \"vault\": {\n                    \"description\": \"Associated vault name // 关联库名称\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.BackupConfigRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"cronStrategy\",\n                \"storageIds\",\n                \"type\"\n            ],\n            \"properties\": {\n                \"cronExpression\": {\n                    \"description\": \"Cron expression // Cron 表达式\",\n                    \"type\": \"string\",\n                    \"example\": \"0 0 * * *\"\n                },\n                \"cronStrategy\": {\n                    \"description\": \"Cron strategy // 定时策略\",\n                    \"type\": \"string\",\n                    \"enum\": [\n                        \"daily\",\n                        \"weekly\",\n                        \"monthly\",\n                        \"custom\"\n                    ],\n                    \"example\": \"daily\"\n                },\n                \"id\": {\n                    \"description\": \"ID // ID\",\n                    \"type\": \"integer\",\n                    \"example\": 1\n                },\n                \"includeVaultName\": {\n                    \"description\": \"Include vault name // 同步路径是否包含仓库名\",\n                    \"type\": \"boolean\",\n                    \"example\": false\n                },\n                \"isEnabled\": {\n                    \"description\": \"Is enabled // 是否启用\",\n                    \"type\": \"boolean\",\n                    \"example\": true\n                },\n                \"retentionDays\": {\n                    \"description\": \"Retention days // 保留天数\",\n                    \"type\": \"integer\",\n                    \"minimum\": -1,\n                    \"example\": 7\n                },\n                \"storageIds\": {\n                    \"description\": \"Storage IDs // 存储 ID 列表\",\n                    \"type\": \"string\",\n                    \"example\": \"[1, 2]\"\n                },\n                \"type\": {\n                    \"description\": \"Backup type // 备份类型\",\n                    \"type\": \"string\",\n                    \"enum\": [\n                        \"full\",\n                        \"incremental\",\n                        \"sync\"\n                    ],\n                    \"example\": \"sync\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 仓库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"test\"\n                }\n            }\n        },\n        \"dto.BackupExecuteRequest\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"id\": {\n                    \"description\": \"ID // ID\",\n                    \"type\": \"integer\",\n                    \"example\": 1\n                }\n            }\n        },\n        \"dto.BackupHistoryDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"configId\": {\n                    \"description\": \"Config ID // 配置ID\",\n                    \"type\": \"integer\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Created at // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"endTime\": {\n                    \"description\": \"End time // 结束时间\",\n                    \"type\": \"string\"\n                },\n                \"fileCount\": {\n                    \"description\": \"File count // 文件数量\",\n                    \"type\": \"integer\"\n                },\n                \"filePath\": {\n                    \"description\": \"File path // 文件路径\",\n                    \"type\": \"string\"\n                },\n                \"fileSize\": {\n                    \"description\": \"File size // 文件大小\",\n                    \"type\": \"integer\"\n                },\n                \"id\": {\n                    \"description\": \"History record ID // 历史记录ID\",\n                    \"type\": \"integer\"\n                },\n                \"message\": {\n                    \"description\": \"Result message // 结果消息\",\n                    \"type\": \"string\"\n                },\n                \"startTime\": {\n                    \"description\": \"Start time // 开始时间\",\n                    \"type\": \"string\"\n                },\n                \"status\": {\n                    \"description\": \"Status (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Stopped) // 状态 (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Stopped)\",\n                    \"type\": \"integer\"\n                },\n                \"storageId\": {\n                    \"description\": \"Storage ID // 存储ID\",\n                    \"type\": \"integer\"\n                },\n                \"type\": {\n                    \"description\": \"Backup type // 备份类型\",\n                    \"type\": \"string\"\n                },\n                \"uid\": {\n                    \"description\": \"User UID // 用户ID\",\n                    \"type\": \"integer\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at // 更新时间\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.FileDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"contentHash\": {\n                    \"description\": \"Content hash // 内容哈希\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Created at time // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"ctime\": {\n                    \"description\": \"Creation timestamp // 创建时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"lastTime\": {\n                    \"description\": \"Updated timestamp // 更新时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"mtime\": {\n                    \"description\": \"Modification timestamp // 修改时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"path\": {\n                    \"description\": \"File path // 文件路径\",\n                    \"type\": \"string\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\"\n                },\n                \"rename\": {\n                    \"description\": \"Rename flag // 重命名标记\",\n                    \"type\": \"integer\"\n                },\n                \"size\": {\n                    \"description\": \"File size // 文件大小\",\n                    \"type\": \"integer\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at time // 更新时间\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.FileRecycleClearRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"vault\"\n            ],\n            \"properties\": {\n                \"path\": {\n                    \"description\": \"File path, empty for all // 文件路径，为空则清理全部\",\n                    \"type\": \"string\",\n                    \"example\": \"path/to/file.png\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"fhash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.FileRenameRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"oldPath\",\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"oldPath\": {\n                    \"description\": \"Old path // 旧路径\",\n                    \"type\": \"string\",\n                    \"example\": \"OldImage.png\"\n                },\n                \"oldPathHash\": {\n                    \"description\": \"Old path hash // 旧路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"ofhash456\"\n                },\n                \"path\": {\n                    \"description\": \"New path // 新路径\",\n                    \"type\": \"string\",\n                    \"example\": \"NewImage.png\"\n                },\n                \"pathHash\": {\n                    \"description\": \"New path hash // 新路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"nfhash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.FileRestoreRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"path\": {\n                    \"description\": \"File path // 文件路径\",\n                    \"type\": \"string\",\n                    \"example\": \"Image.png\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"fhash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.FolderCreateRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"path\": {\n                    \"description\": \"Folder path // 文件夹路径\",\n                    \"type\": \"string\",\n                    \"example\": \"NewFolder\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"fhash456\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.FolderDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"createdAt\": {\n                    \"description\": \"Created at time // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"ctime\": {\n                    \"description\": \"Creation timestamp // 创建时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"lastTime\": {\n                    \"description\": \"Record update timestamp // 记录更新时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"mtime\": {\n                    \"description\": \"Modification timestamp // 修改时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"path\": {\n                    \"description\": \"Folder path // 文件夹路径\",\n                    \"type\": \"string\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希值\",\n                    \"type\": \"string\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at time // 更新时间\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.FolderDeleteRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"path\": {\n                    \"description\": \"Folder path // 文件夹路径\",\n                    \"type\": \"string\",\n                    \"example\": \"OldFolder\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"fhash789\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.FolderTreeNode\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"children\": {\n                    \"description\": \"Child nodes // 子节点\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/dto.FolderTreeNode\"\n                    }\n                },\n                \"fileCount\": {\n                    \"description\": \"File count // 文件数量\",\n                    \"type\": \"integer\"\n                },\n                \"name\": {\n                    \"description\": \"Node name // 节点名称\",\n                    \"type\": \"string\"\n                },\n                \"noteCount\": {\n                    \"description\": \"Note count // 笔记数量\",\n                    \"type\": \"integer\"\n                },\n                \"path\": {\n                    \"description\": \"Node path // 节点路径\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.FolderTreeResponse\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"folders\": {\n                    \"description\": \"Folder tree // 文件夹树\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/dto.FolderTreeNode\"\n                    }\n                },\n                \"rootFileCount\": {\n                    \"description\": \"File count in root // 根目录中的文件数量\",\n                    \"type\": \"integer\"\n                },\n                \"rootNoteCount\": {\n                    \"description\": \"Note count in root // 根目录中的笔记数量\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.GitSyncCleanRequest\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"configId\": {\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.GitSyncConfigDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"branch\": {\n                    \"description\": \"Branch // 分支\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Created at // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"delay\": {\n                    \"description\": \"Delay time (seconds) // 延迟时间（秒）\",\n                    \"type\": \"integer\"\n                },\n                \"id\": {\n                    \"description\": \"Task ID // 任务ID\",\n                    \"type\": \"integer\"\n                },\n                \"isEnabled\": {\n                    \"description\": \"Is enabled // 是否启用\",\n                    \"type\": \"boolean\"\n                },\n                \"lastMessage\": {\n                    \"description\": \"Last run result message // 上次运行结果消息\",\n                    \"type\": \"string\"\n                },\n                \"lastStatus\": {\n                    \"description\": \"Last status (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Shutdown) // 上次状态 (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Shutdown)\",\n                    \"type\": \"integer\"\n                },\n                \"lastSyncTime\": {\n                    \"description\": \"Last sync time // 上次同步时间\",\n                    \"type\": \"string\"\n                },\n                \"password\": {\n                    \"description\": \"Password // 密码\",\n                    \"type\": \"string\"\n                },\n                \"repoUrl\": {\n                    \"description\": \"Repository URL // 仓库地址\",\n                    \"type\": \"string\"\n                },\n                \"retentionDays\": {\n                    \"description\": \"History retention days // 历史记录保留天数\",\n                    \"type\": \"integer\"\n                },\n                \"uid\": {\n                    \"description\": \"User ID // 用户ID\",\n                    \"type\": \"integer\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at // 更新时间\",\n                    \"type\": \"string\"\n                },\n                \"username\": {\n                    \"description\": \"Username // 用户名\",\n                    \"type\": \"string\"\n                },\n                \"vault\": {\n                    \"description\": \"Associated vault name // 关联库名称\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.GitSyncConfigRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"repoUrl\"\n            ],\n            \"properties\": {\n                \"branch\": {\n                    \"type\": \"string\"\n                },\n                \"delay\": {\n                    \"description\": \"Delay time (seconds) // 延迟时间（秒）\",\n                    \"type\": \"integer\"\n                },\n                \"id\": {\n                    \"type\": \"integer\"\n                },\n                \"isEnabled\": {\n                    \"type\": \"boolean\"\n                },\n                \"password\": {\n                    \"type\": \"string\"\n                },\n                \"repoUrl\": {\n                    \"type\": \"string\"\n                },\n                \"retentionDays\": {\n                    \"type\": \"integer\"\n                },\n                \"username\": {\n                    \"type\": \"string\"\n                },\n                \"vault\": {\n                    \"description\": \"Associated vault name // 关联笔记本名称\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.GitSyncDeleteRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"id\"\n            ],\n            \"properties\": {\n                \"id\": {\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.GitSyncExecuteRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"id\"\n            ],\n            \"properties\": {\n                \"id\": {\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.GitSyncHistoryDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"configId\": {\n                    \"type\": \"integer\"\n                },\n                \"createdAt\": {\n                    \"type\": \"string\"\n                },\n                \"endTime\": {\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"type\": \"integer\"\n                },\n                \"message\": {\n                    \"type\": \"string\"\n                },\n                \"startTime\": {\n                    \"type\": \"string\"\n                },\n                \"status\": {\n                    \"description\": \"0:Idle, 1:Running, 2:Success, 3:Failed, 4:Shutdown\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.GitSyncValidateRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"repoUrl\"\n            ],\n            \"properties\": {\n                \"branch\": {\n                    \"type\": \"string\"\n                },\n                \"password\": {\n                    \"type\": \"string\"\n                },\n                \"repoUrl\": {\n                    \"type\": \"string\"\n                },\n                \"username\": {\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.NoteAppendRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"content\",\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"content\": {\n                    \"description\": \"Content to append // 追加内容\",\n                    \"type\": \"string\",\n                    \"example\": \"Appended content\"\n                },\n                \"path\": {\n                    \"description\": \"Note path // 笔记路径\",\n                    \"type\": \"string\",\n                    \"example\": \"ReadMe.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NoteDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"clientName\": {\n                    \"description\": \"Client name // 客户端名称\",\n                    \"type\": \"string\"\n                },\n                \"clientType\": {\n                    \"description\": \"Client type // 客户端类型\",\n                    \"type\": \"string\"\n                },\n                \"clientVersion\": {\n                    \"description\": \"Client version // 客户端版本\",\n                    \"type\": \"string\"\n                },\n                \"content\": {\n                    \"description\": \"Note content // 笔记内容\",\n                    \"type\": \"string\"\n                },\n                \"contentHash\": {\n                    \"description\": \"Content hash // 内容哈希\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Created at time // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"ctime\": {\n                    \"description\": \"Creation timestamp // 创建时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"lastTime\": {\n                    \"description\": \"Record update timestamp // 记录更新时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"mtime\": {\n                    \"description\": \"Modification timestamp // 修改时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"path\": {\n                    \"description\": \"Note path // 笔记路径\",\n                    \"type\": \"string\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\"\n                },\n                \"size\": {\n                    \"description\": \"Note size // 笔记大小\",\n                    \"type\": \"integer\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at time // 更新时间\",\n                    \"type\": \"string\"\n                },\n                \"version\": {\n                    \"description\": \"Version number // 版本号\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.NoteHistoryDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"clientName\": {\n                    \"description\": \"Client that made changes // 产生变更的客户端\",\n                    \"type\": \"string\"\n                },\n                \"clientType\": {\n                    \"description\": \"Client type // 客户端类型\",\n                    \"type\": \"string\"\n                },\n                \"clientVersion\": {\n                    \"description\": \"Client version // 客户端版本\",\n                    \"type\": \"string\"\n                },\n                \"content\": {\n                    \"description\": \"Full historical content // 完整历史内容\",\n                    \"type\": \"string\"\n                },\n                \"contentHash\": {\n                    \"description\": \"Content hash // 内容哈希\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Creation time of this version // 此版本的创建时间\",\n                    \"type\": \"string\"\n                },\n                \"diffs\": {\n                    \"description\": \"Text differences // 文本差异内容\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/diffmatchpatch.Diff\"\n                    }\n                },\n                \"id\": {\n                    \"description\": \"History entry ID // 历史项 ID\",\n                    \"type\": \"integer\"\n                },\n                \"noteId\": {\n                    \"description\": \"Associated note ID // 笔记 ID\",\n                    \"type\": \"integer\"\n                },\n                \"path\": {\n                    \"description\": \"Note path at that time // 当时的笔记路径\",\n                    \"type\": \"string\"\n                },\n                \"vaultId\": {\n                    \"description\": \"Associated vault ID // 保险库 ID\",\n                    \"type\": \"integer\"\n                },\n                \"version\": {\n                    \"description\": \"Historical version number // 历史版本号\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.NoteHistoryRestoreRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"historyId\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"historyId\": {\n                    \"description\": \"History version ID // 历史版本 ID\",\n                    \"type\": \"integer\",\n                    \"example\": 1\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NoteLinkItem\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"context\": {\n                    \"description\": \"Text context around link // 链接文本上下文\",\n                    \"type\": \"string\"\n                },\n                \"isEmbed\": {\n                    \"description\": \"Is it an embed (![[...]]) // 是否为嵌入\",\n                    \"type\": \"boolean\"\n                },\n                \"linkText\": {\n                    \"description\": \"Raw link text (optional) // 原始链接文本（可选）\",\n                    \"type\": \"string\"\n                },\n                \"path\": {\n                    \"description\": \"Target path // 目标路径\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.NoteModifyOrCreateRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"baseHash\": {\n                    \"description\": \"Base hash for sync // 同步基准哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"bhash789\"\n                },\n                \"baseHashMissing\": {\n                    \"description\": \"Marks if baseHash is unavailable // 标记基准哈希是否缺失\",\n                    \"type\": \"boolean\",\n                    \"example\": false\n                },\n                \"content\": {\n                    \"description\": \"Note content // 笔记内容\",\n                    \"type\": \"string\",\n                    \"example\": \"# Hello World\"\n                },\n                \"contentHash\": {\n                    \"description\": \"Content hash // 内容哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"chash012\"\n                },\n                \"createOnly\": {\n                    \"description\": \"If true, fail if note already exists // 如果为 true，笔记已存在则失败\",\n                    \"type\": \"boolean\",\n                    \"example\": false\n                },\n                \"ctime\": {\n                    \"description\": \"Creation timestamp // 创建时间戳\",\n                    \"type\": \"integer\",\n                    \"example\": 1700000000\n                },\n                \"mtime\": {\n                    \"description\": \"Modification timestamp // 修改时间戳\",\n                    \"type\": \"integer\",\n                    \"example\": 1700000000\n                },\n                \"path\": {\n                    \"description\": \"Note path // 笔记路径\",\n                    \"type\": \"string\",\n                    \"example\": \"ReadMe.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NoteMoveRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"destination\",\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"destination\": {\n                    \"description\": \"Destination path // 目标路径\",\n                    \"type\": \"string\",\n                    \"example\": \"Folder/Source.md\"\n                },\n                \"overwrite\": {\n                    \"description\": \"Overwrite existing // 覆盖现有\",\n                    \"type\": \"boolean\",\n                    \"example\": false\n                },\n                \"path\": {\n                    \"description\": \"Current path // 当前路径\",\n                    \"type\": \"string\",\n                    \"example\": \"Source.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Current path hash // 当前路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"src_hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NoteNoContentDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"clientName\": {\n                    \"description\": \"Client name // 客户端名称\",\n                    \"type\": \"string\"\n                },\n                \"clientType\": {\n                    \"description\": \"Client type // 客户端类型\",\n                    \"type\": \"string\"\n                },\n                \"clientVersion\": {\n                    \"description\": \"Client version // 客户端版本\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Created at time // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"ctime\": {\n                    \"description\": \"Creation timestamp // 创建时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"lastTime\": {\n                    \"description\": \"Record update timestamp // 记录更新时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"mtime\": {\n                    \"description\": \"Modification timestamp // 修改时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"path\": {\n                    \"description\": \"Note path // 笔记路径\",\n                    \"type\": \"string\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\"\n                },\n                \"size\": {\n                    \"description\": \"Note size // 笔记大小\",\n                    \"type\": \"integer\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at time // 更新时间\",\n                    \"type\": \"string\"\n                },\n                \"version\": {\n                    \"description\": \"Version number // 版本号\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.NotePatchFrontmatterRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"path\": {\n                    \"description\": \"Note path // 笔记路径\",\n                    \"type\": \"string\",\n                    \"example\": \"ReadMe.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"remove\": {\n                    \"description\": \"Fields to remove // 待移除字段\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"type\": \"string\"\n                    },\n                    \"example\": [\n                        \"old_tag\"\n                    ]\n                },\n                \"updates\": {\n                    \"description\": \"Fields to update // 待更新字段\",\n                    \"type\": \"object\",\n                    \"additionalProperties\": {\n                        \"type\": \"array\",\n                        \"items\": {\n                            \"type\": \"string\"\n                        }\n                    }\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NotePrependRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"content\",\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"content\": {\n                    \"description\": \"Content to prepend // 头部添加内容\",\n                    \"type\": \"string\",\n                    \"example\": \"Prepended content\\n\"\n                },\n                \"path\": {\n                    \"description\": \"Note path // 笔记路径\",\n                    \"type\": \"string\",\n                    \"example\": \"ReadMe.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NoteRecycleClearRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"vault\"\n            ],\n            \"properties\": {\n                \"path\": {\n                    \"description\": \"Note path, empty for all // 笔记路径，为空则清理全部\",\n                    \"type\": \"string\",\n                    \"example\": \"path/to/note.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NoteRenameRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"oldPath\",\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"oldPath\": {\n                    \"description\": \"Old path // 旧路径\",\n                    \"type\": \"string\",\n                    \"example\": \"OldName.md\"\n                },\n                \"oldPathHash\": {\n                    \"description\": \"Old path hash // 旧路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"ohash456\"\n                },\n                \"path\": {\n                    \"description\": \"New path // 新路径\",\n                    \"type\": \"string\",\n                    \"example\": \"NewName.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"New path hash // 新路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"nhash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NoteReplaceRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"find\",\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"all\": {\n                    \"description\": \"Replace all matches // 替换所有\",\n                    \"type\": \"boolean\",\n                    \"example\": true\n                },\n                \"failIfNoMatch\": {\n                    \"description\": \"Fail if no match found // 若无匹配则失败\",\n                    \"type\": \"boolean\",\n                    \"example\": true\n                },\n                \"find\": {\n                    \"description\": \"String to find // 查找内容\",\n                    \"type\": \"string\",\n                    \"example\": \"old text\"\n                },\n                \"path\": {\n                    \"description\": \"Note path // 笔记路径\",\n                    \"type\": \"string\",\n                    \"example\": \"ReadMe.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"regex\": {\n                    \"description\": \"Use regex // 使用正则\",\n                    \"type\": \"boolean\",\n                    \"example\": false\n                },\n                \"replace\": {\n                    \"description\": \"String to replace with // 替换内容\",\n                    \"type\": \"string\",\n                    \"example\": \"new text\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NoteReplaceResponse\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"matchCount\": {\n                    \"description\": \"Number of matches found // 匹配数量\",\n                    \"type\": \"integer\"\n                },\n                \"note\": {\n                    \"description\": \"Updated note data // 更新后的笔记数据\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                        }\n                    ]\n                }\n            }\n        },\n        \"dto.NoteRestoreRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"path\": {\n                    \"description\": \"Note path // 笔记路径\",\n                    \"type\": \"string\",\n                    \"example\": \"ReadMe.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NoteWithFileLinksResponse\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"content\": {\n                    \"description\": \"Note content // 笔记内容\",\n                    \"type\": \"string\"\n                },\n                \"contentHash\": {\n                    \"description\": \"Content hash // 内容哈希\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Created at time // 创建时间\"\n                },\n                \"ctime\": {\n                    \"description\": \"Creation timestamp // 创建时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"fileLinks\": {\n                    \"description\": \"Map of file link to actual path // 文件链接到实际路径的映射\",\n                    \"type\": \"object\",\n                    \"additionalProperties\": {\n                        \"type\": \"string\"\n                    }\n                },\n                \"lastTime\": {\n                    \"description\": \"Record update timestamp // 记录更新时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"mtime\": {\n                    \"description\": \"Modification timestamp // 修改时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"path\": {\n                    \"description\": \"Note path // 笔记路径\",\n                    \"type\": \"string\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at time // 更新时间\"\n                },\n                \"version\": {\n                    \"description\": \"Version number // 版本号\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.SettingDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"content\": {\n                    \"description\": \"Setting content // 配置内容\",\n                    \"type\": \"string\"\n                },\n                \"contentHash\": {\n                    \"description\": \"Content hash // 内容哈希\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Created at time // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"ctime\": {\n                    \"description\": \"Creation timestamp // 创建时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"id\": {\n                    \"description\": \"Setting ID // 配置 ID\",\n                    \"type\": \"integer\"\n                },\n                \"lastTime\": {\n                    \"description\": \"Record update timestamp // 记录更新时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"mtime\": {\n                    \"description\": \"Modification timestamp // 修改时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"path\": {\n                    \"description\": \"Setting path // 配置路径\",\n                    \"type\": \"string\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希值\",\n                    \"type\": \"string\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at time // 更新时间\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.SettingDeleteRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"path\": {\n                    \"description\": \"Setting path // 配置路径\",\n                    \"type\": \"string\",\n                    \"example\": \"User/Theme\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.SettingModifyOrCreateRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"content\": {\n                    \"description\": \"Setting content // 配置内容\",\n                    \"type\": \"string\",\n                    \"example\": \"dark\"\n                },\n                \"contentHash\": {\n                    \"description\": \"Content hash // 内容哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"chash456\"\n                },\n                \"ctime\": {\n                    \"description\": \"Creation timestamp // 创建时间戳\",\n                    \"type\": \"integer\",\n                    \"example\": 1700000000\n                },\n                \"mtime\": {\n                    \"description\": \"Modification timestamp // 修改时间戳\",\n                    \"type\": \"integer\",\n                    \"example\": 1700000000\n                },\n                \"path\": {\n                    \"description\": \"Setting path // 配置路径\",\n                    \"type\": \"string\",\n                    \"example\": \"User/Theme\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.SettingRenameRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"newPath\",\n                \"oldPath\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"newPath\": {\n                    \"description\": \"New path // 新路径\",\n                    \"type\": \"string\",\n                    \"example\": \"New/Path\"\n                },\n                \"newPathHash\": {\n                    \"description\": \"New path hash // 新路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"newhash456\"\n                },\n                \"oldPath\": {\n                    \"description\": \"Old path // 旧路径\",\n                    \"type\": \"string\",\n                    \"example\": \"Old/Path\"\n                },\n                \"oldPathHash\": {\n                    \"description\": \"Old path hash // 旧路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"oldhash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.ShareCancelRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"vault\"\n            ],\n            \"properties\": {\n                \"id\": {\n                    \"description\": \"Share ID (optional) // 分享 ID (可选)\",\n                    \"type\": \"integer\",\n                    \"example\": 1\n                },\n                \"path\": {\n                    \"description\": \"Resource path (optional) // 资源路径 (可选)\",\n                    \"type\": \"string\",\n                    \"example\": \"ReadMe.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Resource path Hash (optional) // 资源路径哈希 (可选)\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"defaultVault\"\n                }\n            }\n        },\n        \"dto.ShareCreateRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"pathHash\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"password\": {\n                    \"description\": \"Share password // 分享密码\",\n                    \"type\": \"string\",\n                    \"example\": \"123456\"\n                },\n                \"path\": {\n                    \"description\": \"Resource path // 资源路径\",\n                    \"type\": \"string\",\n                    \"example\": \"ReadMe.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Resource path Hash // 资源路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"defaultVault\"\n                }\n            }\n        },\n        \"dto.ShareCreateResponse\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"expiresAt\": {\n                    \"description\": \"Expiration time // 过期时间\",\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"description\": \"ID of the note or file table (primary resource ID) // 笔记或文件表 ID（主资源 ID）\",\n                    \"type\": \"integer\"\n                },\n                \"isPassword\": {\n                    \"description\": \"Whether password is set // 是否设置了密码\",\n                    \"type\": \"boolean\"\n                },\n                \"shortLink\": {\n                    \"description\": \"Short link // 短链\",\n                    \"type\": \"string\"\n                },\n                \"token\": {\n                    \"description\": \"Share Token // 分享 Token\",\n                    \"type\": \"string\"\n                },\n                \"type\": {\n                    \"description\": \"Resource type: note or file // 资源类型：笔记（note）或文件（file）\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.ShareListItem\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"createdAt\": {\n                    \"description\": \"Created at // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"expiresAt\": {\n                    \"description\": \"Expiration time // 过期时间\",\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"description\": \"Share ID // 分享记录 ID\",\n                    \"type\": \"integer\"\n                },\n                \"isPassword\": {\n                    \"description\": \"Whether password is set // 是否设置了密码\",\n                    \"type\": \"boolean\"\n                },\n                \"lastViewedAt\": {\n                    \"description\": \"Last viewed time // 最后访问时间\",\n                    \"type\": \"string\"\n                },\n                \"notePath\": {\n                    \"description\": \"Note path, for frontend share filter matching // 笔记路径，用于前端分享筛选匹配\",\n                    \"type\": \"string\"\n                },\n                \"res\": {\n                    \"description\": \"Authorized resources // 资源授权列表\",\n                    \"type\": \"object\",\n                    \"additionalProperties\": {\n                        \"type\": \"array\",\n                        \"items\": {\n                            \"type\": \"string\"\n                        }\n                    }\n                },\n                \"shortLink\": {\n                    \"description\": \"Short link // 短链\",\n                    \"type\": \"string\"\n                },\n                \"status\": {\n                    \"description\": \"Status: 1-Active, 2-Cancelled // 状态: 1-有效, 2-已撤销\",\n                    \"type\": \"integer\"\n                },\n                \"title\": {\n                    \"description\": \"Resource title (note title or file name) // 资源标题（笔记标题或文件名）\",\n                    \"type\": \"string\"\n                },\n                \"uid\": {\n                    \"description\": \"User ID // 用户 ID\",\n                    \"type\": \"integer\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at // 更新时间\",\n                    \"type\": \"string\"\n                },\n                \"url\": {\n                    \"description\": \"Share URL (path format: /id/token) // 分享 URL (路径格式: /id/token)\",\n                    \"type\": \"string\"\n                },\n                \"vaultName\": {\n                    \"description\": \"Vault name where the note belongs // 笔记所属仓库名\",\n                    \"type\": \"string\"\n                },\n                \"viewCount\": {\n                    \"description\": \"View count // 访问次数\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.SharePasswordUpdateRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"pathHash\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"password\": {\n                    \"description\": \"New password // 新密码\",\n                    \"type\": \"string\",\n                    \"example\": \"123456\"\n                },\n                \"path\": {\n                    \"description\": \"Resource path // 资源路径\",\n                    \"type\": \"string\",\n                    \"example\": \"未命名.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Resource path Hash // 资源路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"-677306325\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"test\"\n                }\n            }\n        },\n        \"dto.ShareShortLinkCreateRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"pathHash\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"is_force\": {\n                    \"description\": \"Whether to force regeneration // 是否强制重新生成\",\n                    \"type\": \"boolean\",\n                    \"example\": false\n                },\n                \"path\": {\n                    \"description\": \"Path // 路径\",\n                    \"type\": \"string\",\n                    \"example\": \"notes/todo.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"...\"\n                },\n                \"url\": {\n                    \"description\": \"Full share URL from client; if provided, used directly without regenerating token // 客户端传入的完整分享链接，非空时直接使用，不重新生成 token\",\n                    \"type\": \"string\",\n                    \"example\": \"https://example.com/share/129/CNmkmQlq0s-4elT3NuZG2w\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 库名\",\n                    \"type\": \"string\",\n                    \"example\": \"work\"\n                }\n            }\n        },\n        \"dto.StorageDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"accessKeyId\": {\n                    \"description\": \"Access key ID // 访问密钥 ID\",\n                    \"type\": \"string\"\n                },\n                \"accessKeySecret\": {\n                    \"description\": \"Access key secret // 访问密钥秘密\",\n                    \"type\": \"string\"\n                },\n                \"accessUrlPrefix\": {\n                    \"description\": \"Access URL prefix // 访问地址前缀\",\n                    \"type\": \"string\"\n                },\n                \"accountId\": {\n                    \"description\": \"Account ID // 账户 ID\",\n                    \"type\": \"string\"\n                },\n                \"bucketName\": {\n                    \"description\": \"Bucket name // 存储桶名称\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Created at // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"customPath\": {\n                    \"description\": \"Custom path // 自定义路径\",\n                    \"type\": \"string\"\n                },\n                \"endpoint\": {\n                    \"description\": \"Endpoint // 访问端点\",\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"description\": \"ID // ID\",\n                    \"type\": \"integer\"\n                },\n                \"isEnabled\": {\n                    \"description\": \"Is enabled // 是否启用\",\n                    \"type\": \"boolean\"\n                },\n                \"password\": {\n                    \"description\": \"Password // 密码\",\n                    \"type\": \"string\"\n                },\n                \"region\": {\n                    \"description\": \"Region // 区域\",\n                    \"type\": \"string\"\n                },\n                \"type\": {\n                    \"description\": \"Storage type // 存储类型\",\n                    \"type\": \"string\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at // 更新时间\",\n                    \"type\": \"string\"\n                },\n                \"user\": {\n                    \"description\": \"Username // 用户名\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.StoragePostRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"accessUrlPrefix\",\n                \"type\"\n            ],\n            \"properties\": {\n                \"accessKeyId\": {\n                    \"description\": \"Access key ID // 访问密钥ID\",\n                    \"type\": \"string\",\n                    \"example\": \"\"\n                },\n                \"accessKeySecret\": {\n                    \"description\": \"Access key secret // 访问密钥秘密\",\n                    \"type\": \"string\",\n                    \"example\": \"\"\n                },\n                \"accessUrlPrefix\": {\n                    \"description\": \"Access URL prefix // 访问地址前缀\",\n                    \"type\": \"string\",\n                    \"maxLength\": 100,\n                    \"minLength\": 2,\n                    \"example\": \"https://cdn.com\"\n                },\n                \"accountId\": {\n                    \"description\": \"Account ID (R2) // 账户ID r2\",\n                    \"type\": \"string\",\n                    \"example\": \"123456789\"\n                },\n                \"bucketName\": {\n                    \"description\": \"Bucket name // 存储桶名称\",\n                    \"type\": \"string\",\n                    \"example\": \"my-bucket\"\n                },\n                \"customPath\": {\n                    \"description\": \"Custom path // 自定义路径\",\n                    \"type\": \"string\",\n                    \"example\": \"/backups\"\n                },\n                \"endpoint\": {\n                    \"description\": \"Endpoint (OSS) // 端点 oss\",\n                    \"type\": \"string\",\n                    \"example\": \"oss-cn-hangzhou.aliyuncs.com\"\n                },\n                \"id\": {\n                    \"description\": \"ID // ID\",\n                    \"type\": \"integer\",\n                    \"example\": 1\n                },\n                \"isEnabled\": {\n                    \"description\": \"Is enabled // 是否启用\",\n                    \"type\": \"integer\",\n                    \"example\": 1\n                },\n                \"password\": {\n                    \"description\": \"Password // 密码\",\n                    \"type\": \"string\",\n                    \"example\": \"secret_password\"\n                },\n                \"region\": {\n                    \"description\": \"Region (S3) // 区域 s3\",\n                    \"type\": \"string\",\n                    \"example\": \"us-east-1\"\n                },\n                \"type\": {\n                    \"description\": \"Storage type // 类型\",\n                    \"type\": \"string\",\n                    \"minLength\": 1,\n                    \"example\": \"local-fs\"\n                },\n                \"user\": {\n                    \"description\": \"Username // 访问用户名\",\n                    \"type\": \"string\",\n                    \"example\": \"admin\"\n                }\n            }\n        },\n        \"dto.SyncLogDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"action\": {\n                    \"description\": \"Action type // 操作类型\",\n                    \"type\": \"string\"\n                },\n                \"changedFields\": {\n                    \"description\": \"Changed fields // 变更字段\",\n                    \"type\": \"string\"\n                },\n                \"clientName\": {\n                    \"description\": \"Client name // 客户端名称\",\n                    \"type\": \"string\"\n                },\n                \"clientType\": {\n                    \"description\": \"Client type // 客户端类型\",\n                    \"type\": \"string\"\n                },\n                \"clientVersion\": {\n                    \"description\": \"Client version // 客户端版本\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Log creation time // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"message\": {\n                    \"description\": \"Additional message // 附加消息\",\n                    \"type\": \"string\"\n                },\n                \"path\": {\n                    \"description\": \"Resource path // 资源路径\",\n                    \"type\": \"string\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Resource path hash // 路径哈希\",\n                    \"type\": \"string\"\n                },\n                \"size\": {\n                    \"description\": \"Size in bytes // 大小（字节）\",\n                    \"type\": \"integer\"\n                },\n                \"status\": {\n                    \"description\": \"Status: 1 success, 2 failed // 状态\",\n                    \"type\": \"integer\"\n                },\n                \"type\": {\n                    \"description\": \"Resource type // 资源类型\",\n                    \"type\": \"string\"\n                },\n                \"vaultId\": {\n                    \"description\": \"Vault ID // 笔记本 ID\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.UserChangePasswordRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"confirmPassword\",\n                \"oldPassword\",\n                \"password\"\n            ],\n            \"properties\": {\n                \"confirmPassword\": {\n                    \"description\": \"Confirm password // 校验密码\",\n                    \"type\": \"string\",\n                    \"example\": \"new_password123\"\n                },\n                \"oldPassword\": {\n                    \"description\": \"Old password // 旧密码\",\n                    \"type\": \"string\",\n                    \"example\": \"old_password123\"\n                },\n                \"password\": {\n                    \"description\": \"New password // 新密码\",\n                    \"type\": \"string\",\n                    \"example\": \"new_password123\"\n                }\n            }\n        },\n        \"dto.UserCreateRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"confirmPassword\",\n                \"email\",\n                \"password\",\n                \"username\"\n            ],\n            \"properties\": {\n                \"confirmPassword\": {\n                    \"description\": \"Confirm password // 校验密码\",\n                    \"type\": \"string\",\n                    \"example\": \"password123\"\n                },\n                \"email\": {\n                    \"description\": \"User email // 用户邮件\",\n                    \"type\": \"string\",\n                    \"example\": \"user@example.com\"\n                },\n                \"password\": {\n                    \"description\": \"User password // 用户密码\",\n                    \"type\": \"string\",\n                    \"example\": \"password123\"\n                },\n                \"username\": {\n                    \"description\": \"User name // 用户名\",\n                    \"type\": \"string\",\n                    \"example\": \"username123\"\n                }\n            }\n        },\n        \"dto.UserDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"avatar\": {\n                    \"description\": \"Avatar URL or handle // 头像路径或名称\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Account created time // 账号创建时间\",\n                    \"type\": \"string\"\n                },\n                \"email\": {\n                    \"description\": \"Email address // 邮件地址\",\n                    \"type\": \"string\"\n                },\n                \"token\": {\n                    \"description\": \"Authentication Token // 认证 Token\",\n                    \"type\": \"string\"\n                },\n                \"uid\": {\n                    \"description\": \"User ID (primary key) // 用户唯一标识（主键）\",\n                    \"type\": \"integer\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Last updated time // 最后更新时间\",\n                    \"type\": \"string\"\n                },\n                \"username\": {\n                    \"description\": \"Username // 用户名\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.UserLoginRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"credentials\",\n                \"password\"\n            ],\n            \"properties\": {\n                \"credentials\": {\n                    \"description\": \"Username or Email // 登录凭证（用户名或邮件）\",\n                    \"type\": \"string\",\n                    \"example\": \"user@example.com\"\n                },\n                \"password\": {\n                    \"description\": \"Password // 密码\",\n                    \"type\": \"string\",\n                    \"example\": \"password123\"\n                }\n            }\n        },\n        \"dto.VaultDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"createdAt\": {\n                    \"description\": \"Creation time // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"fileCount\": {\n                    \"description\": \"Number of files // 文件数量\",\n                    \"type\": \"integer\"\n                },\n                \"fileSize\": {\n                    \"description\": \"Size of files // 文件大小\",\n                    \"type\": \"integer\"\n                },\n                \"id\": {\n                    \"description\": \"Vault ID // 保险库 ID\",\n                    \"type\": \"integer\"\n                },\n                \"noteCount\": {\n                    \"description\": \"Number of notes // 笔记数量\",\n                    \"type\": \"integer\"\n                },\n                \"noteSize\": {\n                    \"description\": \"Size of notes // 笔记大小\",\n                    \"type\": \"integer\"\n                },\n                \"size\": {\n                    \"description\": \"Total size // 总大小\",\n                    \"type\": \"integer\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated time // 更新时间\",\n                    \"type\": \"string\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.VaultPostRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"vault\"\n            ],\n            \"properties\": {\n                \"id\": {\n                    \"description\": \"Vault ID (optional for update) // 保险库 ID（可选，用于更新）\",\n                    \"type\": \"integer\",\n                    \"example\": 1\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.VersionDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"buildTime\": {\n                    \"description\": \"Build time // 构建时间\",\n                    \"type\": \"string\"\n                },\n                \"gitTag\": {\n                    \"description\": \"Git tag // Git 标签\",\n                    \"type\": \"string\"\n                },\n                \"pluginVersionNewChangelog\": {\n                    \"description\": \"New plugin version changelog link // 插件新版本更新日志链接\",\n                    \"type\": \"string\"\n                },\n                \"pluginVersionNewChangelogContent\": {\n                    \"description\": \"New plugin version changelog content // 插件新版本更新日志内容\",\n                    \"type\": \"string\"\n                },\n                \"pluginVersionNewLink\": {\n                    \"description\": \"New plugin version link // 插件新版本链接\",\n                    \"type\": \"string\"\n                },\n                \"pluginVersionNewName\": {\n                    \"description\": \"New plugin version name // 插件新版本名称\",\n                    \"type\": \"string\"\n                },\n                \"version\": {\n                    \"description\": \"Current version // 当前版本\",\n                    \"type\": \"string\"\n                },\n                \"versionIsNew\": {\n                    \"description\": \"Is there a new version // 是否有新版本\",\n                    \"type\": \"boolean\"\n                },\n                \"versionNewChangelog\": {\n                    \"description\": \"New version changelog link // 新版本更新日志链接\",\n                    \"type\": \"string\"\n                },\n                \"versionNewChangelogContent\": {\n                    \"description\": \"New version changelog content // 新版本更新日志内容\",\n                    \"type\": \"string\"\n                },\n                \"versionNewLink\": {\n                    \"description\": \"New version download link // 新版本下载链接\",\n                    \"type\": \"string\"\n                },\n                \"versionNewName\": {\n                    \"description\": \"New version name // 新版本名称\",\n                    \"type\": \"string\"\n                }\n            }\n        }\n    },\n    \"securityDefinitions\": {\n        \"ShareAuthToken\": {\n            \"type\": \"apiKey\",\n            \"name\": \"Share-Token\",\n            \"in\": \"header\"\n        },\n        \"UserAuthToken\": {\n            \"type\": \"apiKey\",\n            \"name\": \"token\",\n            \"in\": \"header\"\n        }\n    }\n}`\n\n// SwaggerInfo holds exported Swagger Info so clients can modify it\nvar SwaggerInfo = &swag.Spec{\n\tVersion:          \"1.0\",\n\tHost:             \"localhost:9000\",\n\tBasePath:         \"/\",\n\tSchemes:          []string{},\n\tTitle:            \"Fast Note Sync Service HTTP API\",\n\tDescription:      \"This is the Fast Note Sync Service HTTP API.\",\n\tInfoInstanceName: \"swagger\",\n\tSwaggerTemplate:  docTemplate,\n\tLeftDelim:        \"{{\",\n\tRightDelim:       \"}}\",\n}\n\nfunc init() {\n\tswag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)\n}\n"
  },
  {
    "path": "docs/skills/fns-mcp/SKILL.md",
    "content": "---\nname: fns-mcp\ndescription: Fast Note Sync Service MCP SSE Skill (Bilingual). Allows agents to access and manage notes, files, and vaults via the MCP protocol.\nversion: 1.0.0\n---\n\n# Fast Note Sync (FNS) MCP Skill (Bilingual/双语版)\n\nThis skill allows agents to interact with the Fast Note Sync Service using the Model Context Protocol (MCP).\n本技能允许 Agent 通过 Model Context Protocol (MCP) 与 Fast Note Sync Service 交互。\n\n## Core Capabilities / 核心能力\n\n- **Note Management / 笔记管理**: Listing, searching, CRUD, moving, renaming, restoring, and clearing recycle bin. (列表、检索、增删改查、移动、重命名、恢复、清理回收站)\n- **Note Enhancement / 笔记增强**: Frontmatter patching, appending/prepending, find & replace, and backlinks/outlinks. (Frontmatter 修补、追加/预置、查找替换、双链/外链)\n- **File Management / 文件管理**: Attachment listing, metadata retrieval, reading content (Base64), renaming, and deletion. (附件列表、元数据、读取内容、重命名、删除)\n- **Vault Management / 库管理**: Vault listing, creating/updating, and deletion. (库列表、创建/更新、删除)\n\n## Configuration Guide / 配置指南\n\nBefore using this skill, the agent needs to connect to the MCP SSE interface.\n在使用此技能前，Agent 需要连接到 MCP SSE 接口。\n\n### Interface Details / 接口信息\n- **SSE Endpoint**: `http://<YOUR_DOMAIN>:9000/api/mcp/sse`\n- **Message Endpoint**: `http://<YOUR_DOMAIN>:9000/api/mcp/message`\n- **Authentication**: Requires `Authorization: Bearer <YOUR_AUTH_TOKEN>` in headers.\n- **Client Identity (Optional)**: \n    - `X-Client`: `<ClientType>` (e.g., `CherryStudio`, `OpenClaw`)\n    - `X-Client-Name`: `<ClientName>` (e.g., `MyAgent`)\n    - `X-Client-Version`: `<Version>`\n- **Default Vault (Optional)**: `X-Default-Vault-Name: <VAULT_NAME>`\n\n### Platform Specific Config / 平台配置示例\n- [OpenClaw Configuration](configs/openclaw.json)\n- [Hermes Agent Configuration](configs/hermes.yaml)\n- [Cherry Studio Configuration Guide / 配置指南](configs/cherry-studio.md)\n\n---\n\n## Available Tools / 可用工具说明\n\n### 1. Note Tools / 笔记工具\n\n| Tool Name / 工具 | Description / 描述 | Arguments / 参数 |\n| :--- | :--- | :--- |\n| `note_list` | List notes in a vault / 列出笔记 | `vault`, `keyword` |\n| `note_get` | Get note content / 获取笔记内容 | `vault`, `path` |\n| `note_create_or_update` | Create/Update note / 创建或更新笔记 | `vault`, `path`, `content` |\n| `note_delete` | Delete note / 删除笔记 | `vault`, `path` |\n| `note_rename` | Rename note / 重命名笔记 | `vault`, `oldPath`, `newPath` |\n| `note_restore` | Restore note / 恢复笔记 | `vault`, `path` |\n| `note_append` | Append content / 追加内容 | `vault`, `path`, `content` |\n| `note_prepend` | Prepend content / 预置内容 | `vault`, `path`, `content` |\n| `note_replace` | Find & Replace / 查找替换 | `vault`, `path`, `find`, `replace`, `regex`, `all` |\n| `note_patch_frontmatter` | Patch Frontmatter / 修补前置参数 | `vault`, `path`, `updates` (JSON), `remove` (JSON) |\n| `note_get_backlinks` | Get backlinks / 获取反链 | `vault`, `path` |\n| `note_get_outlinks` | Get outlinks / 获取出链 | `vault`, `path` |\n\n### 2. File Tools / 文件工具\n\n| Tool Name / 工具 | Description / 描述 | Arguments / 参数 |\n| :--- | :--- | :--- |\n| `file_list` | List files / 列出文件 | `vault`, `keyword` |\n| `file_get_info` | Get metadata / 获取元数据 | `vault`, `path` |\n| `file_read` | Read content (Base64) / 读取内容 | `vault`, `path` |\n| `file_delete` | Delete file / 删除文件 | `vault`, `path` |\n| `file_rename` | Rename file / 重命名文件 | `vault`, `oldPath`, `newPath` |\n\n### 3. Vault Tools / 库工具\n\n| Tool Name / 工具 | Description / 描述 | Arguments / 参数 |\n| :--- | :--- | :--- |\n| `vault_list` | List vaults / 列出库 | None |\n| `vault_get` | Get vault details / 获取详情 | `id` |\n| `vault_create_or_update` | Create/Update vault / 创建或更新 | `vault`, `id` |\n| `vault_delete` | Delete vault / 删除库 | `id` |\n\n---\n\n## Best Practices / 最佳实践\n\n1. **Vault Selection**: Explicitly specify `vault` when possible. (尽可能明确指定 `vault` 参数)\n2. **Path Handling**: Paths are relative to vault root. (路径相对于库根目录)\n3. **Encoding**: `file_read` returns Base64. (文件读取返回 Base64 编码)\n4. **Consistency**: Be aware of path changes after rename/move. (重命名后注意路径变更)\n"
  },
  {
    "path": "docs/skills/fns-mcp/configs/cherry-studio.md",
    "content": "# Cherry Studio MCP Configuration Guide / 配置指南\n\nFollow these steps to use Fast Note Sync Service in Cherry Studio:\n按照以下步骤在 Cherry Studio 中使用 Fast Note Sync Service：\n\n### 1. Open MCP Settings / 进入 MCP 设置\n- Open **Cherry Studio**.\n- Click **Settings** (gear icon) / 点击 **设置**（齿轮图标）。\n- Select **MCP Servers** / 选择 **MCP 服务器**。\n\n### 2. Add New Server / 添加服务器\n- Click **Add Server** / 点击 **添加服务器**。\n- Fill out the form / 填写以下内容：\n    - **Name / 名称**: `FNS-Service`\n    - **Type / 类型**: `SSE (Server-Sent Events)`\n    - **URL**: `http://<YOUR_DOMAIN>:9000/api/mcp/sse`\n    - **Headers / 请求头**:\n        - **Key**: `Authorization`, **Value**: `Bearer <YOUR_AUTH_TOKEN>`\n        - (Optional) **Key**: `X-Client`, **Value**: `CherryStudio`\n        - (Optional) **Key**: `X-Client-Name`, **Value**: `Cherry Agent`\n        - (Optional) **Key**: `X-Client-Version`, **Value**: `1.0.0`\n        - (Optional) **Key**: `X-Default-Vault-Name`, **Value**: `Default`\n\n### 3. Save and Enable / 保存并启用\n- Click **Confirm** / 点击 **确定**。\n- Ensure it is **Enabled** / 确保处于 **启用** 状态。\n\n### 4. Use in Chat / 在聊天中使用\n- Start a new chat with a model supporting Tool Calling.\n- Confirm tools from `FNS-Service` are selected.\n- Now you can say: \"List my notes\" or \"在库中搜索 X\"。\n"
  },
  {
    "path": "docs/skills/fns-mcp/configs/hermes.yaml",
    "content": "mcp_servers:\n  fns-service:\n    url: \"http://<YOUR_DOMAIN>:9000/api/mcp/sse\"\n    headers:\n      Authorization: \"Bearer <YOUR_AUTH_TOKEN>\"\n      X-Default-Vault-Name: \"Default\"\n      X-Client: \"Hermes\"\n      X-Client-Name: \"Hermes Agent\"\n      X-Client-Version: \"1.0.0\"\n"
  },
  {
    "path": "docs/skills/fns-mcp/configs/openclaw.json",
    "content": "{\n  \"mcpServers\": {\n    \"fns-service\": {\n      \"url\": \"http://<YOUR_DOMAIN>:9000/api/mcp/sse\",\n      \"headers\": {\n        \"Authorization\": \"Bearer <YOUR_AUTH_TOKEN>\",\n        \"X-Default-Vault-Name\": \"Default\",\n        \"X-Client\": \"OpenClaw\",\n        \"X-Client-Name\": \"OpenClaw Agent\",\n        \"X-Client-Version\": \"1.0.0\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "docs/swagger.json",
    "content": "{\n    \"swagger\": \"2.0\",\n    \"info\": {\n        \"description\": \"This is the Fast Note Sync Service HTTP API.\",\n        \"title\": \"Fast Note Sync Service HTTP API\",\n        \"contact\": {\n            \"name\": \"Haierkeys\",\n            \"url\": \"https://github.com/haierkeys\",\n            \"email\": \"haierkeys@gmail.com\"\n        },\n        \"license\": {\n            \"name\": \"Apache 2.0\",\n            \"url\": \"http://www.apache.org/licenses/LICENSE-2.0.html\"\n        },\n        \"version\": \"1.0\"\n    },\n    \"host\": \"localhost:9000\",\n    \"basePath\": \"/\",\n    \"paths\": {\n        \"/api/admin/check\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Check if the current logged-in user has system admin privileges\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Check admin permission\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminCheckResponse\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/cloudflared_tunnel_download\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Trigger the download of cloudflared binary for the current platform\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"System\"\n                ],\n                \"summary\": \"Download cloudflared binary\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/config\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get full system configuration information, requires admin privileges\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Get full admin config\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminConfig\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Modify full system configuration information, requires admin privileges\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Update admin config\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Config Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.AdminConfig\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminConfig\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/config/cloudflare\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get Cloudflare tunnel configuration, requires admin privileges\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Get Cloudflare config\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminCloudflareConfig\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Modify Cloudflare tunnel configuration, requires admin privileges\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Update Cloudflare config\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Config Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.AdminCloudflareConfig\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminCloudflareConfig\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/config/ngrok\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get Ngrok tunnel configuration, requires admin privileges\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Get Ngrok config\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminNgrokConfig\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Modify Ngrok tunnel configuration, requires admin privileges\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Update Ngrok config\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Config Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.AdminNgrokConfig\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminNgrokConfig\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/config/user_database\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get user database configuration information, requires admin privileges\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Get user database config\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminUserDatabaseConfig\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Modify user database configuration information, requires admin privileges\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Update user database config\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Config Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.AdminUserDatabaseConfig\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminUserDatabaseConfig\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/config/user_database/test\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Test if the provided database configuration can connect successfully, requires admin privileges\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Test user database connection\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Config Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.AdminUserDatabaseConfig\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Connection failed\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/gc\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Manually run Go runtime GC and release memory to OS, requires admin privileges\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"System\"\n                ],\n                \"summary\": \"Trigger manual GC\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/restart\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Gracefully restart the server\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"System\"\n                ],\n                \"summary\": \"Trigger server restart\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/system/info\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get server runtime, CPU, memory, host and process info, requires admin privileges\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"System\"\n                ],\n                \"summary\": \"Get system stats\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminSystemInfo\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/upgrade\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Download latest version and restart server\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"System\"\n                ],\n                \"summary\": \"Trigger server upgrade\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Version to upgrade (e.g. 2.0.10 or latest)\",\n                        \"name\": \"version\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/admin/ws_clients\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get a list of all current WebSocket connections, requires admin privileges\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"System\"\n                ],\n                \"summary\": \"Get connected WebSocket clients\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"$ref\": \"#/definitions/app.WSClientInfo\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"403\": {\n                        \"description\": \"Insufficient privileges\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/backup/config\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Backup\"\n                ],\n                \"summary\": \"Update backup configuration\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Backup Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.BackupConfigRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.BackupConfigDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            },\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Backup\"\n                ],\n                \"summary\": \"Delete backup configuration\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 1,\n                        \"description\": \"ID // ID\",\n                        \"name\": \"id\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/backup/configs\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Backup\"\n                ],\n                \"summary\": \"Get backup configurations\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"$ref\": \"#/definitions/dto.BackupConfigDTO\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/backup/execute\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Backup\"\n                ],\n                \"summary\": \"Trigger a backup manually\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Backup Execute Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.BackupExecuteRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/backup/historys\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Backup\"\n                ],\n                \"summary\": \"Get backup history list\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 1,\n                        \"description\": \"Config ID // 配置 ID\",\n                        \"name\": \"configId\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 1,\n                        \"description\": \"Page number // 页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 10,\n                        \"description\": \"Page size // 每页大小\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.BackupHistoryDTO\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/file\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get raw binary data of an attachment by path, supports strong cache control\",\n                \"produces\": [\n                    \"application/octet-stream\"\n                ],\n                \"tags\": [\n                    \"File\"\n                ],\n                \"summary\": \"Get attachment content\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"boolean\",\n                        \"example\": false,\n                        \"description\": \"Is in recycle bin // 是否在回收站\",\n                        \"name\": \"isRecycle\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"Image.png\",\n                        \"description\": \"File path // 文件路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"fhash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"type\": \"file\"\n                        }\n                    }\n                }\n            },\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Permanently delete a specific attachment record and its physical file\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"File\"\n                ],\n                \"summary\": \"Delete attachment\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"Image.png\",\n                        \"description\": \"File path // 文件路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"fhash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.FileDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/file/info\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get attachment metadata (FileDTO) by path\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"File\"\n                ],\n                \"summary\": \"Get attachment info\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"boolean\",\n                        \"example\": false,\n                        \"description\": \"Is in recycle bin // 是否在回收站\",\n                        \"name\": \"isRecycle\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"Image.png\",\n                        \"description\": \"File path // 文件路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"fhash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.FileDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/file/recycle-clear\": {\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Permanently clear selected files from recycle bin\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"File\"\n                ],\n                \"summary\": \"Clear recycle bin\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Clear Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.FileRecycleClearRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/file/rename\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Rename an attachment to a new path\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"File\"\n                ],\n                \"summary\": \"Rename attachment\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Rename Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.FileRenameRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.FileDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/file/restore\": {\n            \"put\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Restore deleted attachment from trash\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"File\"\n                ],\n                \"summary\": \"Restore attachment\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Restore Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.FileRestoreRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.FileDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/files\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get attachment list for current user with pagination, search, filter, and sort support\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"File\"\n                ],\n                \"summary\": \"Get file list\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"boolean\",\n                        \"example\": false,\n                        \"description\": \"Is in recycle bin // 是否在回收站\",\n                        \"name\": \"isRecycle\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"vacation\",\n                        \"description\": \"Search keyword // 搜索关键词\",\n                        \"name\": \"keyword\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"mtime\",\n                        \"description\": \"Sort by field // 排序字段\",\n                        \"name\": \"sortBy\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"desc\",\n                        \"description\": \"Sort order // 排序顺序\",\n                        \"name\": \"sortOrder\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page number // 页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page size // 每页数量\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.FileDTO\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/folder\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get folder info for current user by path or pathHash\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Folder\"\n                ],\n                \"summary\": \"Get folder info\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"Projects/Work\",\n                        \"description\": \"Folder path // 文件夹路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"fhash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.FolderDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Create a new folder or restore a deleted one by path\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Folder\"\n                ],\n                \"summary\": \"Create folder\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Create Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.FolderCreateRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.FolderDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Soft delete a folder by path or pathHash\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Folder\"\n                ],\n                \"summary\": \"Delete folder\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Delete Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.FolderDeleteRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/folder/files\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"List non-deleted files in a specific folder with pagination and sorting\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Folder\"\n                ],\n                \"summary\": \"List files in folder\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"Projects\",\n                        \"description\": \"Folder path // 文件夹路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"fhash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"mtime\",\n                        \"description\": \"Sort by field // 排序字段\",\n                        \"name\": \"sortBy\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"desc\",\n                        \"description\": \"Sort order // 排序顺序\",\n                        \"name\": \"sortOrder\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page number // 页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page size // 每页数量\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.FileDTO\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/folder/notes\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"List non-deleted notes in a specific folder with pagination and sorting\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Folder\"\n                ],\n                \"summary\": \"List notes in folder\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"Projects\",\n                        \"description\": \"Folder path // 文件夹路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"fhash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"mtime\",\n                        \"description\": \"Sort by field // 排序字段\",\n                        \"name\": \"sortBy\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"desc\",\n                        \"description\": \"Sort order // 排序顺序\",\n                        \"name\": \"sortOrder\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page number // 页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page size // 每页数量\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/folder/tree\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get the complete folder tree structure for a vault\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Folder\"\n                ],\n                \"summary\": \"Get folder tree\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 3,\n                        \"description\": \"Tree depth // 树深度\",\n                        \"name\": \"depth\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.FolderTreeResponse\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/folders\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get folder list for current user by parent path or pathHash\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Folder\"\n                ],\n                \"summary\": \"Get folder list\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"Projects\",\n                        \"description\": \"Folder path // 文件夹路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"fhash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"$ref\": \"#/definitions/dto.FolderDTO\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/git-sync/config\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"GitSync\"\n                ],\n                \"summary\": \"Update git sync configuration\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Git Sync Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.GitSyncConfigRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.GitSyncConfigDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            },\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"GitSync\"\n                ],\n                \"summary\": \"Delete git sync configuration\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Git Sync ID\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.GitSyncDeleteRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/git-sync/config/clean\": {\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"GitSync\"\n                ],\n                \"summary\": \"Clean local git workspace\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Clean Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.GitSyncCleanRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/git-sync/config/execute\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"GitSync\"\n                ],\n                \"summary\": \"Trigger a manual git sync\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Execute Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.GitSyncExecuteRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/git-sync/configs\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"GitSync\"\n                ],\n                \"summary\": \"Get git sync configurations\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"$ref\": \"#/definitions/dto.GitSyncConfigDTO\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/git-sync/histories\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"GitSync\"\n                ],\n                \"summary\": \"Get git sync histories\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"name\": \"configId\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.GitSyncHistoryDTO\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/git-sync/validate\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"GitSync\"\n                ],\n                \"summary\": \"Validate git sync parameters\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Validation Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.GitSyncValidateRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/health\": {\n            \"get\": {\n                \"description\": \"Check service health status, including database connection\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"System\"\n                ],\n                \"summary\": \"Health check\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/api_router.HealthResponse\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get specific note content and metadata by path or path hash\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Get note details\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"boolean\",\n                        \"example\": false,\n                        \"description\": \"Is in recycle bin // 是否在回收站\",\n                        \"name\": \"isRecycle\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"ReadMe.md\",\n                        \"description\": \"Note path // 笔记路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"hash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteWithFileLinksResponse\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Handle note creation, modification, or renaming (identified by path change)\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Create or update note\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Note Content\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NoteModifyOrCreateRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Move note to trash\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Delete note\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"ReadMe.md\",\n                        \"description\": \"Note path // 笔记路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"hash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/append\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Append content to the end of a note\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Append content to note\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Append Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NoteAppendRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/backlinks\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get all other notes that link to the specified note\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Get backlinks\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"ReadMe.md\",\n                        \"description\": \"Note path // 笔记路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"hash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"$ref\": \"#/definitions/dto.NoteLinkItem\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/frontmatter\": {\n            \"patch\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Update or delete note frontmatter fields\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Modify note frontmatter\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Frontmatter Modification Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NotePatchFrontmatterRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/histories\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get all history records for a specific note with pagination\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note History\"\n                ],\n                \"summary\": \"Get note history list\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"boolean\",\n                        \"example\": false,\n                        \"description\": \"Is in recycle bin // 是否在回收站\",\n                        \"name\": \"isRecycle\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"ReadMe.md\",\n                        \"description\": \"Note path // 笔记路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"hash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page number // 页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page size // 每页数量\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.NoteHistoryDTO\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/history\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get specific note history content by history record ID\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note History\"\n                ],\n                \"summary\": \"Get note history details\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"format\": \"int64\",\n                        \"description\": \"History Record ID\",\n                        \"name\": \"id\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteHistoryDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/history/restore\": {\n            \"put\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Restore note content to a specific history version\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note History\"\n                ],\n                \"summary\": \"Restore note from history\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Restore Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NoteHistoryRestoreRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/move\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Move a note to a new path\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Move note\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Move Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NoteMoveRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/outlinks\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get other notes that the specified note links to\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Get outgoing links\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"ReadMe.md\",\n                        \"description\": \"Note path // 笔记路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"hash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"$ref\": \"#/definitions/dto.NoteLinkItem\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/prepend\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Insert content at the beginning of a note (after frontmatter)\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Prepend content to note\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Prepend Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NotePrependRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/recycle-clear\": {\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Permanently clear selected notes from recycle bin\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Clear recycle bin\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Clear Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NoteRecycleClearRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/rename\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Rename a note to a new path\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Rename note\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Rename Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NoteRenameRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/replace\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Perform find and replace operation in a note, supporting regular expressions\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Find and replace in note\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Find and Replace Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NoteReplaceRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteReplaceResponse\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/note/restore\": {\n            \"put\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Restore deleted note from trash\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Restore note\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Restore Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.NoteRestoreRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/notes\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get note list for current user with pagination\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Note\"\n                ],\n                \"summary\": \"Get note list\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"boolean\",\n                        \"example\": false,\n                        \"description\": \"Is in recycle bin // 是否在回收站\",\n                        \"name\": \"isRecycle\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"todo\",\n                        \"description\": \"Search keyword // 搜索关键词\",\n                        \"name\": \"keyword\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"note1.md,note2.md\",\n                        \"description\": \"Comma-separated exact path list for share filter // 逗号分隔的精确路径列表，用于分享筛选\",\n                        \"name\": \"paths\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"boolean\",\n                        \"example\": true,\n                        \"description\": \"Whether to search content // 是否搜索内容\",\n                        \"name\": \"searchContent\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"content\",\n                        \"description\": \"Search mode (path, content) // 搜索模式（路径、内容）\",\n                        \"name\": \"searchMode\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"mtime\",\n                        \"description\": \"Sort by field // 排序字段\",\n                        \"name\": \"sortBy\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"desc\",\n                        \"description\": \"Sort order // 排序顺序\",\n                        \"name\": \"sortOrder\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page number // 页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page size // 每页数量\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.NoteNoContentDTO\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/notes/share-paths\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"tags\": [\n                    \"Share\"\n                ],\n                \"summary\": \"Get active shared note paths\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Vault name\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"type\": \"string\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/setting\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get setting info for current user by path or pathHash\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Setting\"\n                ],\n                \"summary\": \"Get setting info\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"User/Theme\",\n                        \"description\": \"Setting path // 配置路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"hash123\",\n                        \"description\": \"Path hash // 路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.SettingDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Create a new setting or update an existing one\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Setting\"\n                ],\n                \"summary\": \"Create or update setting\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Create/Update Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.SettingModifyOrCreateRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.SettingDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Soft delete a setting by path or pathHash\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Setting\"\n                ],\n                \"summary\": \"Delete setting\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Delete Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.SettingDeleteRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/setting/rename\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Rename a setting and update its path and pathHash\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Setting\"\n                ],\n                \"summary\": \"Rename setting\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Rename Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.SettingRenameRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.SettingDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/settings\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get setting list for current user with pagination and keyword filtering\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Setting\"\n                ],\n                \"summary\": \"Get setting list\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"User/\",\n                        \"description\": \"Keyword // 关键词\",\n                        \"name\": \"keyword\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page number // 页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page size // 每页数量\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.SettingDTO\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/share\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get share token and info by vault and path\",\n                \"tags\": [\n                    \"Share\"\n                ],\n                \"summary\": \"Query share by path\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"ReadMe.md\",\n                        \"description\": \"Resource path // 资源路径\",\n                        \"name\": \"path\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"hash123\",\n                        \"description\": \"Resource path Hash // 资源路径哈希\",\n                        \"name\": \"pathHash\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"defaultVault\",\n                        \"description\": \"Vault name // 保险库名称\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.ShareCreateResponse\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Create a share token for a specific note or attachment, automatically resolve attachment references and authorize\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Share\"\n                ],\n                \"summary\": \"Create resource share\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Share Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.ShareCreateRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.ShareCreateResponse\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Cancel a share by ID or path parameters\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Share\"\n                ],\n                \"summary\": \"Cancel share\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Cancel Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.ShareCancelRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/share/file\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"ShareAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get raw binary data of a specific attachment via share token\",\n                \"produces\": [\n                    \"application/octet-stream\"\n                ],\n                \"tags\": [\n                    \"Share\"\n                ],\n                \"summary\": \"Get shared attachment content\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"Share-Token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 1,\n                        \"description\": \"Resource ID // 资源 ID\",\n                        \"name\": \"id\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"123456\",\n                        \"description\": \"Share password // 分享密码\",\n                        \"name\": \"password\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"type\": \"file\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/share/note\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"ShareAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get specific note content (restricted read-only access) via share token\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Share\"\n                ],\n                \"summary\": \"Get shared note details\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"Share-Token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"example\": 1,\n                        \"description\": \"Resource ID // 资源 ID\",\n                        \"name\": \"id\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"123456\",\n                        \"description\": \"Share password // 分享密码\",\n                        \"name\": \"password\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/share/password\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Set or update password for a share record\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Share\"\n                ],\n                \"summary\": \"Update share password\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Update Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.SharePasswordUpdateRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/share/short_link\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Call sink.cool API to generate a short link for a given share record\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Share\"\n                ],\n                \"summary\": \"Create short link for share\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Short Link Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.ShareShortLinkCreateRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"string\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/shares\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get all active and inactive shares of the user, supports sorting and pagination\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Share\"\n                ],\n                \"summary\": \"List shares\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Sort field: created_at, updated_at, expires_at (default: created_at)\",\n                        \"name\": \"sort_by\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Sort direction: asc or desc (default: desc)\",\n                        \"name\": \"sort_order\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page number\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page size\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.ShareListItem\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/storage\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Storage\"\n                ],\n                \"summary\": \"Get storage configuration list\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"$ref\": \"#/definitions/dto.StorageDTO\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Storage\"\n                ],\n                \"summary\": \"Create or update storage configuration\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Storage Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.StoragePostRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.StorageDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Storage\"\n                ],\n                \"summary\": \"Delete storage configuration\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"format\": \"int64\",\n                        \"description\": \"Storage ID\",\n                        \"name\": \"id\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/storage/enabled_types\": {\n            \"get\": {\n                \"description\": \"Get list of enabled storage types. Possible values: localfs, oss, s3, r2, minio, webdav\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Storage\"\n                ],\n                \"summary\": \"Get enabled storage types\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success. Data contains: localfs, oss, s3, r2, minio, webdav\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"type\": \"string\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/storage/validate\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Storage\"\n                ],\n                \"summary\": \"Validate storage connection\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Storage Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.StoragePostRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Params\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Token Required\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"500\": {\n                        \"description\": \"Internal Server Error\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/support\": {\n            \"get\": {\n                \"description\": \"Get support records for the specified language with pagination and sorting\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"System\"\n                ],\n                \"summary\": \"Get support records\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Language code (default: en)\",\n                        \"name\": \"lang\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Sort by field (amount, time, name, item)\",\n                        \"name\": \"sortBy\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Sort order (asc, desc)\",\n                        \"name\": \"sortOrder\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page number\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page size\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/app.ListRes\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/sync-logs\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get sync log list for current user with optional type/action filters and pagination\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Sync Log\"\n                ],\n                \"summary\": \"Get sync log list\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"modify\",\n                        \"description\": \"Action type // 操作类型\",\n                        \"name\": \"action\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"note\",\n                        \"description\": \"Resource type: note / file / setting / folder // 资源类型\",\n                        \"name\": \"type\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"string\",\n                        \"example\": \"MyVault\",\n                        \"description\": \"Vault name (optional filter) // 保险库名称（可选过滤）\",\n                        \"name\": \"vault\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page number // 页码\",\n                        \"name\": \"page\",\n                        \"in\": \"query\"\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"description\": \"Page size // 每页数量\",\n                        \"name\": \"pageSize\",\n                        \"in\": \"query\"\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"allOf\": [\n                                                {\n                                                    \"$ref\": \"#/definitions/app.ListRes\"\n                                                },\n                                                {\n                                                    \"type\": \"object\",\n                                                    \"properties\": {\n                                                        \"list\": {\n                                                            \"type\": \"array\",\n                                                            \"items\": {\n                                                                \"$ref\": \"#/definitions/dto.SyncLogDTO\"\n                                                            }\n                                                        }\n                                                    }\n                                                }\n                                            ]\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/user/change_password\": {\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Handle password change request for current user, validate old password and update new password.\\n处理当前用户的修改密码请求，验证旧密码并更新新密码。\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"User\"\n                ],\n                \"summary\": \"Change user password\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Change Password Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.UserChangePasswordRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Parameters / Old Password Incorrect\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Unauthorized\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/user/info\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Handle request to get current user info.\\n处理获取当前用户信息的请求。\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"User\"\n                ],\n                \"summary\": \"Get user info\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.UserDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"401\": {\n                        \"description\": \"Unauthorized\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/user/login\": {\n            \"post\": {\n                \"description\": \"Handle user login HTTP request, validate parameters and return auth token.\\n处理用户登录 HTTP 请求，验证参数并返回认证 Token。\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"User\"\n                ],\n                \"summary\": \"User login\",\n                \"parameters\": [\n                    {\n                        \"description\": \"Login Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.UserLoginRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.UserDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Parameters / Invalid Credentials\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/user/register\": {\n            \"post\": {\n                \"description\": \"Handle user registration HTTP request, validate parameters and call UserService. Registration may be disabled in server settings.\\n处理用户注册 HTTP 请求，验证参数并调用 UserService。注册功能可能在服务器设置中被禁用。\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"User\"\n                ],\n                \"summary\": \"User registration\",\n                \"parameters\": [\n                    {\n                        \"description\": \"Register Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.UserCreateRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.UserDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    },\n                    \"400\": {\n                        \"description\": \"Invalid Parameters / Registration Disabled / User Already Exists\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/vault\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get all note vaults for current user\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Vault\"\n                ],\n                \"summary\": \"Get vault list\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"type\": \"array\",\n                                            \"items\": {\n                                                \"$ref\": \"#/definitions/dto.VaultDTO\"\n                                            }\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"post\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Be used to create a new vault or update an existing vault configuration based on the ID in the request parameters\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Vault\"\n                ],\n                \"summary\": \"Create or update vault\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"description\": \"Vault Parameters\",\n                        \"name\": \"params\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/dto.VaultPostRequest\"\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.VaultDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            },\n            \"delete\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Permanently delete a specific note vault and all associated notes and attachments\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Vault\"\n                ],\n                \"summary\": \"Delete vault\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"minimum\": 1,\n                        \"type\": \"integer\",\n                        \"example\": 1,\n                        \"description\": \"Vault ID // 保险库 ID\",\n                        \"name\": \"id\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"$ref\": \"#/definitions/app.Res\"\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/vault/get\": {\n            \"get\": {\n                \"security\": [\n                    {\n                        \"UserAuthToken\": []\n                    }\n                ],\n                \"description\": \"Get specific vault configuration details by vault ID\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Vault\"\n                ],\n                \"summary\": \"Get vault details\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"Auth Token\",\n                        \"name\": \"token\",\n                        \"in\": \"header\",\n                        \"required\": true\n                    },\n                    {\n                        \"type\": \"integer\",\n                        \"format\": \"int64\",\n                        \"description\": \"Vault ID\",\n                        \"name\": \"id\",\n                        \"in\": \"query\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.VaultDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/version\": {\n            \"get\": {\n                \"description\": \"Get current server software version, Git tag, and build time\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"System\"\n                ],\n                \"summary\": \"Get server version info\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.VersionDTO\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        \"/api/webgui/config\": {\n            \"get\": {\n                \"description\": \"Get non-sensitive configuration required for frontend display, such as font settings, registration status, etc.\",\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"tags\": [\n                    \"Config\"\n                ],\n                \"summary\": \"Get WebGUI basic config\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"Success\",\n                        \"schema\": {\n                            \"allOf\": [\n                                {\n                                    \"$ref\": \"#/definitions/app.Res\"\n                                },\n                                {\n                                    \"type\": \"object\",\n                                    \"properties\": {\n                                        \"data\": {\n                                            \"$ref\": \"#/definitions/dto.AdminWebGUIConfig\"\n                                        }\n                                    }\n                                }\n                            ]\n                        }\n                    }\n                }\n            }\n        }\n    },\n    \"definitions\": {\n        \"api_router.HealthResponse\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"database\": {\n                    \"description\": \"\\\"connected\\\" or \\\"error\\\" // \\\"connected\\\" 或 \\\"error\\\"\",\n                    \"type\": \"string\"\n                },\n                \"status\": {\n                    \"description\": \"\\\"healthy\\\" or \\\"unhealthy\\\" // \\\"healthy\\\" 或 \\\"unhealthy\\\"\",\n                    \"type\": \"string\"\n                },\n                \"uptime\": {\n                    \"description\": \"Uptime (seconds) // 运行时间（秒）\",\n                    \"type\": \"number\"\n                },\n                \"version\": {\n                    \"description\": \"Service version number // 服务版本号\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"app.ListRes\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"list\": {\n                    \"description\": \"Data list // 数据清单\"\n                },\n                \"pager\": {\n                    \"description\": \"Pagination info // 翻页信息\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/app.Pager\"\n                        }\n                    ]\n                }\n            }\n        },\n        \"app.Pager\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"page\": {\n                    \"description\": \"Page number // 页码\",\n                    \"type\": \"integer\"\n                },\n                \"pageSize\": {\n                    \"description\": \"Page size // 每页数量\",\n                    \"type\": \"integer\"\n                },\n                \"totalRows\": {\n                    \"description\": \"Total rows // 总行数\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"app.Res\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"code\": {\n                    \"type\": \"integer\"\n                },\n                \"context\": {},\n                \"data\": {},\n                \"details\": {},\n                \"message\": {},\n                \"status\": {\n                    \"type\": \"boolean\"\n                },\n                \"vault\": {}\n            }\n        },\n        \"app.WSClientInfo\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"clientName\": {\n                    \"type\": \"string\"\n                },\n                \"clientType\": {\n                    \"type\": \"string\"\n                },\n                \"clientVersion\": {\n                    \"type\": \"string\"\n                },\n                \"nickname\": {\n                    \"type\": \"string\"\n                },\n                \"platformInfo\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": {\n                        \"type\": \"boolean\"\n                    }\n                },\n                \"remoteAddr\": {\n                    \"type\": \"string\"\n                },\n                \"startTime\": {\n                    \"type\": \"string\"\n                },\n                \"traceId\": {\n                    \"type\": \"string\"\n                },\n                \"uid\": {\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"diffmatchpatch.Diff\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"text\": {\n                    \"type\": \"string\"\n                },\n                \"type\": {\n                    \"$ref\": \"#/definitions/diffmatchpatch.Operation\"\n                }\n            }\n        },\n        \"diffmatchpatch.Operation\": {\n            \"type\": \"integer\",\n            \"format\": \"int32\",\n            \"enum\": [\n                -1,\n                1,\n                0\n            ],\n            \"x-enum-varnames\": [\n                \"DiffDelete\",\n                \"DiffInsert\",\n                \"DiffEqual\"\n            ]\n        },\n        \"dto.AdminCPUInfo\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"loadAvg\": {\n                    \"description\": \"Load average // 平均负载\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/dto.AdminLoadInfo\"\n                        }\n                    ]\n                },\n                \"logicalCores\": {\n                    \"description\": \"Logical cores // 逻辑核心数\",\n                    \"type\": \"integer\"\n                },\n                \"modelName\": {\n                    \"description\": \"Model name // 型号\",\n                    \"type\": \"string\"\n                },\n                \"percent\": {\n                    \"description\": \"Usage percentage per core // 每个核心的使用率\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"type\": \"number\"\n                    }\n                },\n                \"physicalCores\": {\n                    \"description\": \"Physical cores // 物理核心数\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.AdminCheckResponse\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"isAdmin\": {\n                    \"description\": \"Whether have admin privileges // 是否具有管理员权限\",\n                    \"type\": \"boolean\"\n                }\n            }\n        },\n        \"dto.AdminCloudflareConfig\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"enabled\": {\n                    \"description\": \"Whether to enable cloudflare tunnel // 是否启用 cloudflare 隧道\",\n                    \"type\": \"boolean\"\n                },\n                \"logEnabled\": {\n                    \"description\": \"Whether to enable cloudflare tunnel logging // 是否开启 cloudflare 隧道日志\",\n                    \"type\": \"boolean\"\n                },\n                \"token\": {\n                    \"description\": \"cloudflare tunnel token // cloudflare 隧道令牌\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.AdminConfig\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"adminUid\": {\n                    \"description\": \"Admin UID // 管理员 UID\",\n                    \"type\": \"integer\"\n                },\n                \"authTokenKey\": {\n                    \"description\": \"Auth token key // 认证 Token 密钥\",\n                    \"type\": \"string\"\n                },\n                \"defaultApiFolder\": {\n                    \"description\": \"Default API folder // 默认 API 目录\",\n                    \"type\": \"string\"\n                },\n                \"fileChunkSize\": {\n                    \"description\": \"File chunk size // 文件分块大小\",\n                    \"type\": \"string\"\n                },\n                \"fontSet\": {\n                    \"description\": \"Font set // 字体设置\",\n                    \"type\": \"string\"\n                },\n                \"historyKeepVersions\": {\n                    \"description\": \"History versions to keep // 历史版本保留数\",\n                    \"type\": \"integer\"\n                },\n                \"historySaveDelay\": {\n                    \"description\": \"History save delay // 历史保存延迟\",\n                    \"type\": \"string\"\n                },\n                \"pullSource\": {\n                    \"description\": \"Data pull source: auto | github | cnb // 数据拉取源：auto | github | cnb\",\n                    \"type\": \"string\"\n                },\n                \"registerIsEnable\": {\n                    \"description\": \"Registration enablement // 是否开启注册\",\n                    \"type\": \"boolean\"\n                },\n                \"shareTokenExpiry\": {\n                    \"description\": \"Share token expiry // 分享 Token 有效期\",\n                    \"type\": \"string\"\n                },\n                \"shareTokenKey\": {\n                    \"description\": \"Share token key // 分享 Token 密钥\",\n                    \"type\": \"string\"\n                },\n                \"softDeleteRetentionTime\": {\n                    \"description\": \"Soft delete retention time // 软删除保留时间\",\n                    \"type\": \"string\"\n                },\n                \"tokenExpiry\": {\n                    \"description\": \"Token expiry // Token 有效期\",\n                    \"type\": \"string\"\n                },\n                \"uploadSessionTimeout\": {\n                    \"description\": \"Upload session timeout // 上传会话超时时间\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.AdminHostInfo\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"arch\": {\n                    \"description\": \"Architecture // 架构\",\n                    \"type\": \"string\"\n                },\n                \"currentTime\": {\n                    \"description\": \"Current system time // 当前系统时间\",\n                    \"type\": \"string\"\n                },\n                \"hostname\": {\n                    \"description\": \"Hostname // 主机名\",\n                    \"type\": \"string\"\n                },\n                \"kernelVersion\": {\n                    \"description\": \"Kernel version // 内核版本\",\n                    \"type\": \"string\"\n                },\n                \"os\": {\n                    \"description\": \"Operating system // 操作系统\",\n                    \"type\": \"string\"\n                },\n                \"osPretty\": {\n                    \"description\": \"Detailed OS name // 详细操作系统名称\",\n                    \"type\": \"string\"\n                },\n                \"platform\": {\n                    \"description\": \"Platform name // 平台\",\n                    \"type\": \"string\"\n                },\n                \"timezone\": {\n                    \"description\": \"Time zone name // 时区名称\",\n                    \"type\": \"string\"\n                },\n                \"timezoneOffset\": {\n                    \"description\": \"Time zone offset in seconds // 时区偏移（秒）\",\n                    \"type\": \"integer\"\n                },\n                \"uptime\": {\n                    \"description\": \"System uptime // 系统运行时间\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.AdminLoadInfo\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"load1\": {\n                    \"description\": \"Load 1 min // 1分钟负载\",\n                    \"type\": \"number\"\n                },\n                \"load15\": {\n                    \"description\": \"Load 15 min // 15分钟负载\",\n                    \"type\": \"number\"\n                },\n                \"load5\": {\n                    \"description\": \"Load 5 min // 5分钟负载\",\n                    \"type\": \"number\"\n                }\n            }\n        },\n        \"dto.AdminMemoryInfo\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"available\": {\n                    \"description\": \"Available memory // 可用内存\",\n                    \"type\": \"integer\"\n                },\n                \"swapTotal\": {\n                    \"description\": \"Total swap space // 交换区总量\",\n                    \"type\": \"integer\"\n                },\n                \"swapUsed\": {\n                    \"description\": \"Used swap space // 交换区已用\",\n                    \"type\": \"integer\"\n                },\n                \"swapUsedPercent\": {\n                    \"description\": \"Swap usage percentage // 交换区使用率\",\n                    \"type\": \"number\"\n                },\n                \"total\": {\n                    \"description\": \"Total physical memory // 系统总内存\",\n                    \"type\": \"integer\"\n                },\n                \"used\": {\n                    \"description\": \"Used memory // 已用内存\",\n                    \"type\": \"integer\"\n                },\n                \"usedPercent\": {\n                    \"description\": \"Memory usage percentage // 内存使用率\",\n                    \"type\": \"number\"\n                }\n            }\n        },\n        \"dto.AdminNgrokConfig\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"authToken\": {\n                    \"description\": \"ngrok auth token // ngrok 认证令牌\",\n                    \"type\": \"string\"\n                },\n                \"domain\": {\n                    \"description\": \"Custom domain // 自定义域名\",\n                    \"type\": \"string\"\n                },\n                \"enabled\": {\n                    \"description\": \"Whether to enable ngrok tunnel // 是否启用 ngrok 隧道\",\n                    \"type\": \"boolean\"\n                }\n            }\n        },\n        \"dto.AdminProcessInfo\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"cpuPercent\": {\n                    \"description\": \"CPU Usage percentage // CPU 使用率\",\n                    \"type\": \"number\"\n                },\n                \"memoryPercent\": {\n                    \"description\": \"Memory Usage percentage // 内存使用率\",\n                    \"type\": \"number\"\n                },\n                \"name\": {\n                    \"description\": \"Process Name // 进程名称\",\n                    \"type\": \"string\"\n                },\n                \"pid\": {\n                    \"description\": \"Process ID // 进程 ID\",\n                    \"type\": \"integer\"\n                },\n                \"ppid\": {\n                    \"description\": \"Parent Process ID // 父进程 ID\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.AdminRuntimeInfo\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"buckHashSys\": {\n                    \"description\": \"Memory obtained from system for profiling bucket hash table (bytes) // 分析桶哈希表占用的系统内存\",\n                    \"type\": \"integer\"\n                },\n                \"gcSys\": {\n                    \"description\": \"Memory obtained from system for metadata for GC (bytes) // GC 元数据占用的系统内存\",\n                    \"type\": \"integer\"\n                },\n                \"heapIdle\": {\n                    \"description\": \"Memory in idle spans (bytes) // 空闲 Span 占用的内存\",\n                    \"type\": \"integer\"\n                },\n                \"heapInuse\": {\n                    \"description\": \"Memory in in-use spans (bytes) // 正在使用的 Span 占用的内存\",\n                    \"type\": \"integer\"\n                },\n                \"heapReleased\": {\n                    \"description\": \"Memory released to OS (bytes) // 释放回操作系统的内存（字节）\",\n                    \"type\": \"integer\"\n                },\n                \"heapSys\": {\n                    \"description\": \"Memory obtained from system for heap (bytes) // 堆占用的系统内存\",\n                    \"type\": \"integer\"\n                },\n                \"mCacheSys\": {\n                    \"description\": \"Memory obtained from system for mcache (bytes) // mcache 占用的系统内存\",\n                    \"type\": \"integer\"\n                },\n                \"mSpanSys\": {\n                    \"description\": \"Memory obtained from system for mspan (bytes) // mspan 占用的系统内存\",\n                    \"type\": \"integer\"\n                },\n                \"memAlloc\": {\n                    \"description\": \"Allocated memory (bytes) // 已分配内存（字节）\",\n                    \"type\": \"integer\"\n                },\n                \"memSys\": {\n                    \"description\": \"Memory obtained from system (bytes) // 从系统获取的内存（字节）\",\n                    \"type\": \"integer\"\n                },\n                \"memTotal\": {\n                    \"description\": \"Total memory allocated (bytes) // 累计分配内存（字节）\",\n                    \"type\": \"integer\"\n                },\n                \"nextGc\": {\n                    \"description\": \"Target heap size for the next GC cycle // 下次 GC 的目标堆大小\",\n                    \"type\": \"integer\"\n                },\n                \"numGc\": {\n                    \"description\": \"Number of completed GC cycles // GC 次数\",\n                    \"type\": \"integer\"\n                },\n                \"numGoroutine\": {\n                    \"description\": \"Number of goroutines // Goroutine 数量\",\n                    \"type\": \"integer\"\n                },\n                \"otherSys\": {\n                    \"description\": \"Other system memory (bytes) // 其他系统内存\",\n                    \"type\": \"integer\"\n                },\n                \"stackSys\": {\n                    \"description\": \"Memory obtained from system for stack (bytes) // 栈占用的系统内存\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.AdminSystemInfo\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"cpu\": {\n                    \"description\": \"CPU information // CPU 信息\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/dto.AdminCPUInfo\"\n                        }\n                    ]\n                },\n                \"host\": {\n                    \"description\": \"Host information // 主机信息\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/dto.AdminHostInfo\"\n                        }\n                    ]\n                },\n                \"memory\": {\n                    \"description\": \"Memory information // 内存信息\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/dto.AdminMemoryInfo\"\n                        }\n                    ]\n                },\n                \"process\": {\n                    \"description\": \"Process information // 进程信息\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/dto.AdminProcessInfo\"\n                        }\n                    ]\n                },\n                \"runtimeStatus\": {\n                    \"description\": \"Go runtime status // Go 运行时状态\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/dto.AdminRuntimeInfo\"\n                        }\n                    ]\n                },\n                \"startTime\": {\n                    \"description\": \"Start time // 启动时间\",\n                    \"type\": \"string\"\n                },\n                \"uptime\": {\n                    \"description\": \"Uptime (seconds) // 运行时间（秒）\",\n                    \"type\": \"number\"\n                }\n            }\n        },\n        \"dto.AdminUserDatabaseConfig\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"charset\": {\n                    \"description\": \"Charset // 字符集\",\n                    \"type\": \"string\"\n                },\n                \"connMaxIdleTime\": {\n                    \"description\": \"Connection max idle time // 空闲连接最大生命周期\",\n                    \"type\": \"string\"\n                },\n                \"connMaxLifetime\": {\n                    \"description\": \"Connection max lifetime // 连接最大生命周期\",\n                    \"type\": \"string\"\n                },\n                \"host\": {\n                    \"description\": \"Host // 主机\",\n                    \"type\": \"string\"\n                },\n                \"maxIdleConns\": {\n                    \"description\": \"Max idle connections // 最大闲置连接数\",\n                    \"type\": \"integer\"\n                },\n                \"maxOpenConns\": {\n                    \"description\": \"Max open connections // 最大打开连接数\",\n                    \"type\": \"integer\"\n                },\n                \"maxWriteConcurrency\": {\n                    \"description\": \"Max write concurrency // 最大并发写入数\",\n                    \"type\": \"integer\"\n                },\n                \"name\": {\n                    \"description\": \"Database name // 数据库名\",\n                    \"type\": \"string\"\n                },\n                \"parseTime\": {\n                    \"description\": \"Parse time // 是否解析时间\",\n                    \"type\": \"boolean\"\n                },\n                \"password\": {\n                    \"description\": \"Password // 密码\",\n                    \"type\": \"string\"\n                },\n                \"path\": {\n                    \"description\": \"SQLite database file path // SQLite 数据库文件路径\",\n                    \"type\": \"string\"\n                },\n                \"port\": {\n                    \"description\": \"Port // 端口\",\n                    \"type\": \"integer\"\n                },\n                \"schema\": {\n                    \"description\": \"Database schema (postgres only) // 数据库 Schema\",\n                    \"type\": \"string\"\n                },\n                \"sslMode\": {\n                    \"description\": \"SSL mode (postgres only) // SSL 模式\",\n                    \"type\": \"string\"\n                },\n                \"type\": {\n                    \"description\": \"Database type (mysql, postgres, sqlite) // 数据库类型\",\n                    \"type\": \"string\",\n                    \"enum\": [\n                        \"mysql\",\n                        \"postgres\",\n                        \"sqlite\"\n                    ]\n                },\n                \"userName\": {\n                    \"description\": \"Username // 用户名\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.AdminWebGUIConfig\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"adminUid\": {\n                    \"description\": \"Admin UID // 管理员 UID\",\n                    \"type\": \"integer\"\n                },\n                \"fontSet\": {\n                    \"description\": \"Font set // 字体设置\",\n                    \"type\": \"string\"\n                },\n                \"registerIsEnable\": {\n                    \"description\": \"Registration enablement // 是否开启注册\",\n                    \"type\": \"boolean\"\n                }\n            }\n        },\n        \"dto.BackupConfigDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"createdAt\": {\n                    \"description\": \"Created at // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"cronExpression\": {\n                    \"description\": \"Cron expression // Cron表达式\",\n                    \"type\": \"string\"\n                },\n                \"cronStrategy\": {\n                    \"description\": \"Cron strategy // 定时策略\",\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"description\": \"Config ID // 配置ID\",\n                    \"type\": \"integer\"\n                },\n                \"includeVaultName\": {\n                    \"description\": \"Whether sync path includes vault name // 同步路径是否包含仓库名\",\n                    \"type\": \"boolean\"\n                },\n                \"isEnabled\": {\n                    \"description\": \"Is enabled // 是否启用\",\n                    \"type\": \"boolean\"\n                },\n                \"lastMessage\": {\n                    \"description\": \"Last run result message // 上次运行结果消息\",\n                    \"type\": \"string\"\n                },\n                \"lastRunTime\": {\n                    \"description\": \"Last run time // 上次运行时间\",\n                    \"type\": \"string\"\n                },\n                \"lastStatus\": {\n                    \"description\": \"Last status (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Stopped) // 上次状态 (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Stopped)\",\n                    \"type\": \"integer\"\n                },\n                \"nextRunTime\": {\n                    \"description\": \"Next run time // 下次运行时间\",\n                    \"type\": \"string\"\n                },\n                \"retentionDays\": {\n                    \"description\": \"Retention days // 保留天数\",\n                    \"type\": \"integer\"\n                },\n                \"storageIds\": {\n                    \"description\": \"Storage ID list // 存储ID列表\",\n                    \"type\": \"string\"\n                },\n                \"type\": {\n                    \"description\": \"Backup type (full, incremental, sync) // 备份类型 (full, incremental, sync)\",\n                    \"type\": \"string\"\n                },\n                \"uid\": {\n                    \"description\": \"User UID // 用户ID\",\n                    \"type\": \"integer\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at // 更新时间\",\n                    \"type\": \"string\"\n                },\n                \"vault\": {\n                    \"description\": \"Associated vault name // 关联库名称\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.BackupConfigRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"cronStrategy\",\n                \"storageIds\",\n                \"type\"\n            ],\n            \"properties\": {\n                \"cronExpression\": {\n                    \"description\": \"Cron expression // Cron 表达式\",\n                    \"type\": \"string\",\n                    \"example\": \"0 0 * * *\"\n                },\n                \"cronStrategy\": {\n                    \"description\": \"Cron strategy // 定时策略\",\n                    \"type\": \"string\",\n                    \"enum\": [\n                        \"daily\",\n                        \"weekly\",\n                        \"monthly\",\n                        \"custom\"\n                    ],\n                    \"example\": \"daily\"\n                },\n                \"id\": {\n                    \"description\": \"ID // ID\",\n                    \"type\": \"integer\",\n                    \"example\": 1\n                },\n                \"includeVaultName\": {\n                    \"description\": \"Include vault name // 同步路径是否包含仓库名\",\n                    \"type\": \"boolean\",\n                    \"example\": false\n                },\n                \"isEnabled\": {\n                    \"description\": \"Is enabled // 是否启用\",\n                    \"type\": \"boolean\",\n                    \"example\": true\n                },\n                \"retentionDays\": {\n                    \"description\": \"Retention days // 保留天数\",\n                    \"type\": \"integer\",\n                    \"minimum\": -1,\n                    \"example\": 7\n                },\n                \"storageIds\": {\n                    \"description\": \"Storage IDs // 存储 ID 列表\",\n                    \"type\": \"string\",\n                    \"example\": \"[1, 2]\"\n                },\n                \"type\": {\n                    \"description\": \"Backup type // 备份类型\",\n                    \"type\": \"string\",\n                    \"enum\": [\n                        \"full\",\n                        \"incremental\",\n                        \"sync\"\n                    ],\n                    \"example\": \"sync\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 仓库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"test\"\n                }\n            }\n        },\n        \"dto.BackupExecuteRequest\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"id\": {\n                    \"description\": \"ID // ID\",\n                    \"type\": \"integer\",\n                    \"example\": 1\n                }\n            }\n        },\n        \"dto.BackupHistoryDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"configId\": {\n                    \"description\": \"Config ID // 配置ID\",\n                    \"type\": \"integer\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Created at // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"endTime\": {\n                    \"description\": \"End time // 结束时间\",\n                    \"type\": \"string\"\n                },\n                \"fileCount\": {\n                    \"description\": \"File count // 文件数量\",\n                    \"type\": \"integer\"\n                },\n                \"filePath\": {\n                    \"description\": \"File path // 文件路径\",\n                    \"type\": \"string\"\n                },\n                \"fileSize\": {\n                    \"description\": \"File size // 文件大小\",\n                    \"type\": \"integer\"\n                },\n                \"id\": {\n                    \"description\": \"History record ID // 历史记录ID\",\n                    \"type\": \"integer\"\n                },\n                \"message\": {\n                    \"description\": \"Result message // 结果消息\",\n                    \"type\": \"string\"\n                },\n                \"startTime\": {\n                    \"description\": \"Start time // 开始时间\",\n                    \"type\": \"string\"\n                },\n                \"status\": {\n                    \"description\": \"Status (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Stopped) // 状态 (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Stopped)\",\n                    \"type\": \"integer\"\n                },\n                \"storageId\": {\n                    \"description\": \"Storage ID // 存储ID\",\n                    \"type\": \"integer\"\n                },\n                \"type\": {\n                    \"description\": \"Backup type // 备份类型\",\n                    \"type\": \"string\"\n                },\n                \"uid\": {\n                    \"description\": \"User UID // 用户ID\",\n                    \"type\": \"integer\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at // 更新时间\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.FileDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"contentHash\": {\n                    \"description\": \"Content hash // 内容哈希\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Created at time // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"ctime\": {\n                    \"description\": \"Creation timestamp // 创建时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"lastTime\": {\n                    \"description\": \"Updated timestamp // 更新时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"mtime\": {\n                    \"description\": \"Modification timestamp // 修改时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"path\": {\n                    \"description\": \"File path // 文件路径\",\n                    \"type\": \"string\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\"\n                },\n                \"rename\": {\n                    \"description\": \"Rename flag // 重命名标记\",\n                    \"type\": \"integer\"\n                },\n                \"size\": {\n                    \"description\": \"File size // 文件大小\",\n                    \"type\": \"integer\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at time // 更新时间\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.FileRecycleClearRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"vault\"\n            ],\n            \"properties\": {\n                \"path\": {\n                    \"description\": \"File path, empty for all // 文件路径，为空则清理全部\",\n                    \"type\": \"string\",\n                    \"example\": \"path/to/file.png\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"fhash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.FileRenameRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"oldPath\",\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"oldPath\": {\n                    \"description\": \"Old path // 旧路径\",\n                    \"type\": \"string\",\n                    \"example\": \"OldImage.png\"\n                },\n                \"oldPathHash\": {\n                    \"description\": \"Old path hash // 旧路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"ofhash456\"\n                },\n                \"path\": {\n                    \"description\": \"New path // 新路径\",\n                    \"type\": \"string\",\n                    \"example\": \"NewImage.png\"\n                },\n                \"pathHash\": {\n                    \"description\": \"New path hash // 新路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"nfhash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.FileRestoreRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"path\": {\n                    \"description\": \"File path // 文件路径\",\n                    \"type\": \"string\",\n                    \"example\": \"Image.png\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"fhash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.FolderCreateRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"path\": {\n                    \"description\": \"Folder path // 文件夹路径\",\n                    \"type\": \"string\",\n                    \"example\": \"NewFolder\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"fhash456\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.FolderDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"createdAt\": {\n                    \"description\": \"Created at time // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"ctime\": {\n                    \"description\": \"Creation timestamp // 创建时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"lastTime\": {\n                    \"description\": \"Record update timestamp // 记录更新时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"mtime\": {\n                    \"description\": \"Modification timestamp // 修改时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"path\": {\n                    \"description\": \"Folder path // 文件夹路径\",\n                    \"type\": \"string\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希值\",\n                    \"type\": \"string\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at time // 更新时间\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.FolderDeleteRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"path\": {\n                    \"description\": \"Folder path // 文件夹路径\",\n                    \"type\": \"string\",\n                    \"example\": \"OldFolder\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"fhash789\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.FolderTreeNode\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"children\": {\n                    \"description\": \"Child nodes // 子节点\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/dto.FolderTreeNode\"\n                    }\n                },\n                \"fileCount\": {\n                    \"description\": \"File count // 文件数量\",\n                    \"type\": \"integer\"\n                },\n                \"name\": {\n                    \"description\": \"Node name // 节点名称\",\n                    \"type\": \"string\"\n                },\n                \"noteCount\": {\n                    \"description\": \"Note count // 笔记数量\",\n                    \"type\": \"integer\"\n                },\n                \"path\": {\n                    \"description\": \"Node path // 节点路径\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.FolderTreeResponse\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"folders\": {\n                    \"description\": \"Folder tree // 文件夹树\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/dto.FolderTreeNode\"\n                    }\n                },\n                \"rootFileCount\": {\n                    \"description\": \"File count in root // 根目录中的文件数量\",\n                    \"type\": \"integer\"\n                },\n                \"rootNoteCount\": {\n                    \"description\": \"Note count in root // 根目录中的笔记数量\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.GitSyncCleanRequest\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"configId\": {\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.GitSyncConfigDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"branch\": {\n                    \"description\": \"Branch // 分支\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Created at // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"delay\": {\n                    \"description\": \"Delay time (seconds) // 延迟时间（秒）\",\n                    \"type\": \"integer\"\n                },\n                \"id\": {\n                    \"description\": \"Task ID // 任务ID\",\n                    \"type\": \"integer\"\n                },\n                \"isEnabled\": {\n                    \"description\": \"Is enabled // 是否启用\",\n                    \"type\": \"boolean\"\n                },\n                \"lastMessage\": {\n                    \"description\": \"Last run result message // 上次运行结果消息\",\n                    \"type\": \"string\"\n                },\n                \"lastStatus\": {\n                    \"description\": \"Last status (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Shutdown) // 上次状态 (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Shutdown)\",\n                    \"type\": \"integer\"\n                },\n                \"lastSyncTime\": {\n                    \"description\": \"Last sync time // 上次同步时间\",\n                    \"type\": \"string\"\n                },\n                \"password\": {\n                    \"description\": \"Password // 密码\",\n                    \"type\": \"string\"\n                },\n                \"repoUrl\": {\n                    \"description\": \"Repository URL // 仓库地址\",\n                    \"type\": \"string\"\n                },\n                \"retentionDays\": {\n                    \"description\": \"History retention days // 历史记录保留天数\",\n                    \"type\": \"integer\"\n                },\n                \"uid\": {\n                    \"description\": \"User ID // 用户ID\",\n                    \"type\": \"integer\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at // 更新时间\",\n                    \"type\": \"string\"\n                },\n                \"username\": {\n                    \"description\": \"Username // 用户名\",\n                    \"type\": \"string\"\n                },\n                \"vault\": {\n                    \"description\": \"Associated vault name // 关联库名称\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.GitSyncConfigRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"repoUrl\"\n            ],\n            \"properties\": {\n                \"branch\": {\n                    \"type\": \"string\"\n                },\n                \"delay\": {\n                    \"description\": \"Delay time (seconds) // 延迟时间（秒）\",\n                    \"type\": \"integer\"\n                },\n                \"id\": {\n                    \"type\": \"integer\"\n                },\n                \"isEnabled\": {\n                    \"type\": \"boolean\"\n                },\n                \"password\": {\n                    \"type\": \"string\"\n                },\n                \"repoUrl\": {\n                    \"type\": \"string\"\n                },\n                \"retentionDays\": {\n                    \"type\": \"integer\"\n                },\n                \"username\": {\n                    \"type\": \"string\"\n                },\n                \"vault\": {\n                    \"description\": \"Associated vault name // 关联笔记本名称\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.GitSyncDeleteRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"id\"\n            ],\n            \"properties\": {\n                \"id\": {\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.GitSyncExecuteRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"id\"\n            ],\n            \"properties\": {\n                \"id\": {\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.GitSyncHistoryDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"configId\": {\n                    \"type\": \"integer\"\n                },\n                \"createdAt\": {\n                    \"type\": \"string\"\n                },\n                \"endTime\": {\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"type\": \"integer\"\n                },\n                \"message\": {\n                    \"type\": \"string\"\n                },\n                \"startTime\": {\n                    \"type\": \"string\"\n                },\n                \"status\": {\n                    \"description\": \"0:Idle, 1:Running, 2:Success, 3:Failed, 4:Shutdown\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.GitSyncValidateRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"repoUrl\"\n            ],\n            \"properties\": {\n                \"branch\": {\n                    \"type\": \"string\"\n                },\n                \"password\": {\n                    \"type\": \"string\"\n                },\n                \"repoUrl\": {\n                    \"type\": \"string\"\n                },\n                \"username\": {\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.NoteAppendRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"content\",\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"content\": {\n                    \"description\": \"Content to append // 追加内容\",\n                    \"type\": \"string\",\n                    \"example\": \"Appended content\"\n                },\n                \"path\": {\n                    \"description\": \"Note path // 笔记路径\",\n                    \"type\": \"string\",\n                    \"example\": \"ReadMe.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NoteDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"clientName\": {\n                    \"description\": \"Client name // 客户端名称\",\n                    \"type\": \"string\"\n                },\n                \"clientType\": {\n                    \"description\": \"Client type // 客户端类型\",\n                    \"type\": \"string\"\n                },\n                \"clientVersion\": {\n                    \"description\": \"Client version // 客户端版本\",\n                    \"type\": \"string\"\n                },\n                \"content\": {\n                    \"description\": \"Note content // 笔记内容\",\n                    \"type\": \"string\"\n                },\n                \"contentHash\": {\n                    \"description\": \"Content hash // 内容哈希\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Created at time // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"ctime\": {\n                    \"description\": \"Creation timestamp // 创建时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"lastTime\": {\n                    \"description\": \"Record update timestamp // 记录更新时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"mtime\": {\n                    \"description\": \"Modification timestamp // 修改时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"path\": {\n                    \"description\": \"Note path // 笔记路径\",\n                    \"type\": \"string\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\"\n                },\n                \"size\": {\n                    \"description\": \"Note size // 笔记大小\",\n                    \"type\": \"integer\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at time // 更新时间\",\n                    \"type\": \"string\"\n                },\n                \"version\": {\n                    \"description\": \"Version number // 版本号\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.NoteHistoryDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"clientName\": {\n                    \"description\": \"Client that made changes // 产生变更的客户端\",\n                    \"type\": \"string\"\n                },\n                \"clientType\": {\n                    \"description\": \"Client type // 客户端类型\",\n                    \"type\": \"string\"\n                },\n                \"clientVersion\": {\n                    \"description\": \"Client version // 客户端版本\",\n                    \"type\": \"string\"\n                },\n                \"content\": {\n                    \"description\": \"Full historical content // 完整历史内容\",\n                    \"type\": \"string\"\n                },\n                \"contentHash\": {\n                    \"description\": \"Content hash // 内容哈希\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Creation time of this version // 此版本的创建时间\",\n                    \"type\": \"string\"\n                },\n                \"diffs\": {\n                    \"description\": \"Text differences // 文本差异内容\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/diffmatchpatch.Diff\"\n                    }\n                },\n                \"id\": {\n                    \"description\": \"History entry ID // 历史项 ID\",\n                    \"type\": \"integer\"\n                },\n                \"noteId\": {\n                    \"description\": \"Associated note ID // 笔记 ID\",\n                    \"type\": \"integer\"\n                },\n                \"path\": {\n                    \"description\": \"Note path at that time // 当时的笔记路径\",\n                    \"type\": \"string\"\n                },\n                \"vaultId\": {\n                    \"description\": \"Associated vault ID // 保险库 ID\",\n                    \"type\": \"integer\"\n                },\n                \"version\": {\n                    \"description\": \"Historical version number // 历史版本号\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.NoteHistoryRestoreRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"historyId\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"historyId\": {\n                    \"description\": \"History version ID // 历史版本 ID\",\n                    \"type\": \"integer\",\n                    \"example\": 1\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NoteLinkItem\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"context\": {\n                    \"description\": \"Text context around link // 链接文本上下文\",\n                    \"type\": \"string\"\n                },\n                \"isEmbed\": {\n                    \"description\": \"Is it an embed (![[...]]) // 是否为嵌入\",\n                    \"type\": \"boolean\"\n                },\n                \"linkText\": {\n                    \"description\": \"Raw link text (optional) // 原始链接文本（可选）\",\n                    \"type\": \"string\"\n                },\n                \"path\": {\n                    \"description\": \"Target path // 目标路径\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.NoteModifyOrCreateRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"baseHash\": {\n                    \"description\": \"Base hash for sync // 同步基准哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"bhash789\"\n                },\n                \"baseHashMissing\": {\n                    \"description\": \"Marks if baseHash is unavailable // 标记基准哈希是否缺失\",\n                    \"type\": \"boolean\",\n                    \"example\": false\n                },\n                \"content\": {\n                    \"description\": \"Note content // 笔记内容\",\n                    \"type\": \"string\",\n                    \"example\": \"# Hello World\"\n                },\n                \"contentHash\": {\n                    \"description\": \"Content hash // 内容哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"chash012\"\n                },\n                \"createOnly\": {\n                    \"description\": \"If true, fail if note already exists // 如果为 true，笔记已存在则失败\",\n                    \"type\": \"boolean\",\n                    \"example\": false\n                },\n                \"ctime\": {\n                    \"description\": \"Creation timestamp // 创建时间戳\",\n                    \"type\": \"integer\",\n                    \"example\": 1700000000\n                },\n                \"mtime\": {\n                    \"description\": \"Modification timestamp // 修改时间戳\",\n                    \"type\": \"integer\",\n                    \"example\": 1700000000\n                },\n                \"path\": {\n                    \"description\": \"Note path // 笔记路径\",\n                    \"type\": \"string\",\n                    \"example\": \"ReadMe.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NoteMoveRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"destination\",\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"destination\": {\n                    \"description\": \"Destination path // 目标路径\",\n                    \"type\": \"string\",\n                    \"example\": \"Folder/Source.md\"\n                },\n                \"overwrite\": {\n                    \"description\": \"Overwrite existing // 覆盖现有\",\n                    \"type\": \"boolean\",\n                    \"example\": false\n                },\n                \"path\": {\n                    \"description\": \"Current path // 当前路径\",\n                    \"type\": \"string\",\n                    \"example\": \"Source.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Current path hash // 当前路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"src_hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NoteNoContentDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"clientName\": {\n                    \"description\": \"Client name // 客户端名称\",\n                    \"type\": \"string\"\n                },\n                \"clientType\": {\n                    \"description\": \"Client type // 客户端类型\",\n                    \"type\": \"string\"\n                },\n                \"clientVersion\": {\n                    \"description\": \"Client version // 客户端版本\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Created at time // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"ctime\": {\n                    \"description\": \"Creation timestamp // 创建时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"lastTime\": {\n                    \"description\": \"Record update timestamp // 记录更新时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"mtime\": {\n                    \"description\": \"Modification timestamp // 修改时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"path\": {\n                    \"description\": \"Note path // 笔记路径\",\n                    \"type\": \"string\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\"\n                },\n                \"size\": {\n                    \"description\": \"Note size // 笔记大小\",\n                    \"type\": \"integer\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at time // 更新时间\",\n                    \"type\": \"string\"\n                },\n                \"version\": {\n                    \"description\": \"Version number // 版本号\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.NotePatchFrontmatterRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"path\": {\n                    \"description\": \"Note path // 笔记路径\",\n                    \"type\": \"string\",\n                    \"example\": \"ReadMe.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"remove\": {\n                    \"description\": \"Fields to remove // 待移除字段\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"type\": \"string\"\n                    },\n                    \"example\": [\n                        \"old_tag\"\n                    ]\n                },\n                \"updates\": {\n                    \"description\": \"Fields to update // 待更新字段\",\n                    \"type\": \"object\",\n                    \"additionalProperties\": {\n                        \"type\": \"array\",\n                        \"items\": {\n                            \"type\": \"string\"\n                        }\n                    }\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NotePrependRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"content\",\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"content\": {\n                    \"description\": \"Content to prepend // 头部添加内容\",\n                    \"type\": \"string\",\n                    \"example\": \"Prepended content\\n\"\n                },\n                \"path\": {\n                    \"description\": \"Note path // 笔记路径\",\n                    \"type\": \"string\",\n                    \"example\": \"ReadMe.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NoteRecycleClearRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"vault\"\n            ],\n            \"properties\": {\n                \"path\": {\n                    \"description\": \"Note path, empty for all // 笔记路径，为空则清理全部\",\n                    \"type\": \"string\",\n                    \"example\": \"path/to/note.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NoteRenameRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"oldPath\",\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"oldPath\": {\n                    \"description\": \"Old path // 旧路径\",\n                    \"type\": \"string\",\n                    \"example\": \"OldName.md\"\n                },\n                \"oldPathHash\": {\n                    \"description\": \"Old path hash // 旧路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"ohash456\"\n                },\n                \"path\": {\n                    \"description\": \"New path // 新路径\",\n                    \"type\": \"string\",\n                    \"example\": \"NewName.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"New path hash // 新路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"nhash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NoteReplaceRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"find\",\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"all\": {\n                    \"description\": \"Replace all matches // 替换所有\",\n                    \"type\": \"boolean\",\n                    \"example\": true\n                },\n                \"failIfNoMatch\": {\n                    \"description\": \"Fail if no match found // 若无匹配则失败\",\n                    \"type\": \"boolean\",\n                    \"example\": true\n                },\n                \"find\": {\n                    \"description\": \"String to find // 查找内容\",\n                    \"type\": \"string\",\n                    \"example\": \"old text\"\n                },\n                \"path\": {\n                    \"description\": \"Note path // 笔记路径\",\n                    \"type\": \"string\",\n                    \"example\": \"ReadMe.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"regex\": {\n                    \"description\": \"Use regex // 使用正则\",\n                    \"type\": \"boolean\",\n                    \"example\": false\n                },\n                \"replace\": {\n                    \"description\": \"String to replace with // 替换内容\",\n                    \"type\": \"string\",\n                    \"example\": \"new text\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NoteReplaceResponse\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"matchCount\": {\n                    \"description\": \"Number of matches found // 匹配数量\",\n                    \"type\": \"integer\"\n                },\n                \"note\": {\n                    \"description\": \"Updated note data // 更新后的笔记数据\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/dto.NoteDTO\"\n                        }\n                    ]\n                }\n            }\n        },\n        \"dto.NoteRestoreRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"path\": {\n                    \"description\": \"Note path // 笔记路径\",\n                    \"type\": \"string\",\n                    \"example\": \"ReadMe.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.NoteWithFileLinksResponse\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"content\": {\n                    \"description\": \"Note content // 笔记内容\",\n                    \"type\": \"string\"\n                },\n                \"contentHash\": {\n                    \"description\": \"Content hash // 内容哈希\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Created at time // 创建时间\"\n                },\n                \"ctime\": {\n                    \"description\": \"Creation timestamp // 创建时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"fileLinks\": {\n                    \"description\": \"Map of file link to actual path // 文件链接到实际路径的映射\",\n                    \"type\": \"object\",\n                    \"additionalProperties\": {\n                        \"type\": \"string\"\n                    }\n                },\n                \"lastTime\": {\n                    \"description\": \"Record update timestamp // 记录更新时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"mtime\": {\n                    \"description\": \"Modification timestamp // 修改时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"path\": {\n                    \"description\": \"Note path // 笔记路径\",\n                    \"type\": \"string\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at time // 更新时间\"\n                },\n                \"version\": {\n                    \"description\": \"Version number // 版本号\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.SettingDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"content\": {\n                    \"description\": \"Setting content // 配置内容\",\n                    \"type\": \"string\"\n                },\n                \"contentHash\": {\n                    \"description\": \"Content hash // 内容哈希\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Created at time // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"ctime\": {\n                    \"description\": \"Creation timestamp // 创建时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"id\": {\n                    \"description\": \"Setting ID // 配置 ID\",\n                    \"type\": \"integer\"\n                },\n                \"lastTime\": {\n                    \"description\": \"Record update timestamp // 记录更新时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"mtime\": {\n                    \"description\": \"Modification timestamp // 修改时间戳\",\n                    \"type\": \"integer\"\n                },\n                \"path\": {\n                    \"description\": \"Setting path // 配置路径\",\n                    \"type\": \"string\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希值\",\n                    \"type\": \"string\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at time // 更新时间\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.SettingDeleteRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"path\": {\n                    \"description\": \"Setting path // 配置路径\",\n                    \"type\": \"string\",\n                    \"example\": \"User/Theme\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.SettingModifyOrCreateRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"content\": {\n                    \"description\": \"Setting content // 配置内容\",\n                    \"type\": \"string\",\n                    \"example\": \"dark\"\n                },\n                \"contentHash\": {\n                    \"description\": \"Content hash // 内容哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"chash456\"\n                },\n                \"ctime\": {\n                    \"description\": \"Creation timestamp // 创建时间戳\",\n                    \"type\": \"integer\",\n                    \"example\": 1700000000\n                },\n                \"mtime\": {\n                    \"description\": \"Modification timestamp // 修改时间戳\",\n                    \"type\": \"integer\",\n                    \"example\": 1700000000\n                },\n                \"path\": {\n                    \"description\": \"Setting path // 配置路径\",\n                    \"type\": \"string\",\n                    \"example\": \"User/Theme\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.SettingRenameRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"newPath\",\n                \"oldPath\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"newPath\": {\n                    \"description\": \"New path // 新路径\",\n                    \"type\": \"string\",\n                    \"example\": \"New/Path\"\n                },\n                \"newPathHash\": {\n                    \"description\": \"New path hash // 新路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"newhash456\"\n                },\n                \"oldPath\": {\n                    \"description\": \"Old path // 旧路径\",\n                    \"type\": \"string\",\n                    \"example\": \"Old/Path\"\n                },\n                \"oldPathHash\": {\n                    \"description\": \"Old path hash // 旧路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"oldhash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.ShareCancelRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"vault\"\n            ],\n            \"properties\": {\n                \"id\": {\n                    \"description\": \"Share ID (optional) // 分享 ID (可选)\",\n                    \"type\": \"integer\",\n                    \"example\": 1\n                },\n                \"path\": {\n                    \"description\": \"Resource path (optional) // 资源路径 (可选)\",\n                    \"type\": \"string\",\n                    \"example\": \"ReadMe.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Resource path Hash (optional) // 资源路径哈希 (可选)\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"defaultVault\"\n                }\n            }\n        },\n        \"dto.ShareCreateRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"pathHash\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"password\": {\n                    \"description\": \"Share password // 分享密码\",\n                    \"type\": \"string\",\n                    \"example\": \"123456\"\n                },\n                \"path\": {\n                    \"description\": \"Resource path // 资源路径\",\n                    \"type\": \"string\",\n                    \"example\": \"ReadMe.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Resource path Hash // 资源路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"hash123\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"defaultVault\"\n                }\n            }\n        },\n        \"dto.ShareCreateResponse\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"expiresAt\": {\n                    \"description\": \"Expiration time // 过期时间\",\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"description\": \"ID of the note or file table (primary resource ID) // 笔记或文件表 ID（主资源 ID）\",\n                    \"type\": \"integer\"\n                },\n                \"isPassword\": {\n                    \"description\": \"Whether password is set // 是否设置了密码\",\n                    \"type\": \"boolean\"\n                },\n                \"shortLink\": {\n                    \"description\": \"Short link // 短链\",\n                    \"type\": \"string\"\n                },\n                \"token\": {\n                    \"description\": \"Share Token // 分享 Token\",\n                    \"type\": \"string\"\n                },\n                \"type\": {\n                    \"description\": \"Resource type: note or file // 资源类型：笔记（note）或文件（file）\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.ShareListItem\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"createdAt\": {\n                    \"description\": \"Created at // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"expiresAt\": {\n                    \"description\": \"Expiration time // 过期时间\",\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"description\": \"Share ID // 分享记录 ID\",\n                    \"type\": \"integer\"\n                },\n                \"isPassword\": {\n                    \"description\": \"Whether password is set // 是否设置了密码\",\n                    \"type\": \"boolean\"\n                },\n                \"lastViewedAt\": {\n                    \"description\": \"Last viewed time // 最后访问时间\",\n                    \"type\": \"string\"\n                },\n                \"notePath\": {\n                    \"description\": \"Note path, for frontend share filter matching // 笔记路径，用于前端分享筛选匹配\",\n                    \"type\": \"string\"\n                },\n                \"res\": {\n                    \"description\": \"Authorized resources // 资源授权列表\",\n                    \"type\": \"object\",\n                    \"additionalProperties\": {\n                        \"type\": \"array\",\n                        \"items\": {\n                            \"type\": \"string\"\n                        }\n                    }\n                },\n                \"shortLink\": {\n                    \"description\": \"Short link // 短链\",\n                    \"type\": \"string\"\n                },\n                \"status\": {\n                    \"description\": \"Status: 1-Active, 2-Cancelled // 状态: 1-有效, 2-已撤销\",\n                    \"type\": \"integer\"\n                },\n                \"title\": {\n                    \"description\": \"Resource title (note title or file name) // 资源标题（笔记标题或文件名）\",\n                    \"type\": \"string\"\n                },\n                \"uid\": {\n                    \"description\": \"User ID // 用户 ID\",\n                    \"type\": \"integer\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at // 更新时间\",\n                    \"type\": \"string\"\n                },\n                \"url\": {\n                    \"description\": \"Share URL (path format: /id/token) // 分享 URL (路径格式: /id/token)\",\n                    \"type\": \"string\"\n                },\n                \"vaultName\": {\n                    \"description\": \"Vault name where the note belongs // 笔记所属仓库名\",\n                    \"type\": \"string\"\n                },\n                \"viewCount\": {\n                    \"description\": \"View count // 访问次数\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.SharePasswordUpdateRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"pathHash\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"password\": {\n                    \"description\": \"New password // 新密码\",\n                    \"type\": \"string\",\n                    \"example\": \"123456\"\n                },\n                \"path\": {\n                    \"description\": \"Resource path // 资源路径\",\n                    \"type\": \"string\",\n                    \"example\": \"未命名.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Resource path Hash // 资源路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"-677306325\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"test\"\n                }\n            }\n        },\n        \"dto.ShareShortLinkCreateRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"path\",\n                \"pathHash\",\n                \"vault\"\n            ],\n            \"properties\": {\n                \"is_force\": {\n                    \"description\": \"Whether to force regeneration // 是否强制重新生成\",\n                    \"type\": \"boolean\",\n                    \"example\": false\n                },\n                \"path\": {\n                    \"description\": \"Path // 路径\",\n                    \"type\": \"string\",\n                    \"example\": \"notes/todo.md\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Path hash // 路径哈希\",\n                    \"type\": \"string\",\n                    \"example\": \"...\"\n                },\n                \"url\": {\n                    \"description\": \"Full share URL from client; if provided, used directly without regenerating token // 客户端传入的完整分享链接，非空时直接使用，不重新生成 token\",\n                    \"type\": \"string\",\n                    \"example\": \"https://example.com/share/129/CNmkmQlq0s-4elT3NuZG2w\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 库名\",\n                    \"type\": \"string\",\n                    \"example\": \"work\"\n                }\n            }\n        },\n        \"dto.StorageDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"accessKeyId\": {\n                    \"description\": \"Access key ID // 访问密钥 ID\",\n                    \"type\": \"string\"\n                },\n                \"accessKeySecret\": {\n                    \"description\": \"Access key secret // 访问密钥秘密\",\n                    \"type\": \"string\"\n                },\n                \"accessUrlPrefix\": {\n                    \"description\": \"Access URL prefix // 访问地址前缀\",\n                    \"type\": \"string\"\n                },\n                \"accountId\": {\n                    \"description\": \"Account ID // 账户 ID\",\n                    \"type\": \"string\"\n                },\n                \"bucketName\": {\n                    \"description\": \"Bucket name // 存储桶名称\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Created at // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"customPath\": {\n                    \"description\": \"Custom path // 自定义路径\",\n                    \"type\": \"string\"\n                },\n                \"endpoint\": {\n                    \"description\": \"Endpoint // 访问端点\",\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"description\": \"ID // ID\",\n                    \"type\": \"integer\"\n                },\n                \"isEnabled\": {\n                    \"description\": \"Is enabled // 是否启用\",\n                    \"type\": \"boolean\"\n                },\n                \"password\": {\n                    \"description\": \"Password // 密码\",\n                    \"type\": \"string\"\n                },\n                \"region\": {\n                    \"description\": \"Region // 区域\",\n                    \"type\": \"string\"\n                },\n                \"type\": {\n                    \"description\": \"Storage type // 存储类型\",\n                    \"type\": \"string\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated at // 更新时间\",\n                    \"type\": \"string\"\n                },\n                \"user\": {\n                    \"description\": \"Username // 用户名\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.StoragePostRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"accessUrlPrefix\",\n                \"type\"\n            ],\n            \"properties\": {\n                \"accessKeyId\": {\n                    \"description\": \"Access key ID // 访问密钥ID\",\n                    \"type\": \"string\",\n                    \"example\": \"\"\n                },\n                \"accessKeySecret\": {\n                    \"description\": \"Access key secret // 访问密钥秘密\",\n                    \"type\": \"string\",\n                    \"example\": \"\"\n                },\n                \"accessUrlPrefix\": {\n                    \"description\": \"Access URL prefix // 访问地址前缀\",\n                    \"type\": \"string\",\n                    \"maxLength\": 100,\n                    \"minLength\": 2,\n                    \"example\": \"https://cdn.com\"\n                },\n                \"accountId\": {\n                    \"description\": \"Account ID (R2) // 账户ID r2\",\n                    \"type\": \"string\",\n                    \"example\": \"123456789\"\n                },\n                \"bucketName\": {\n                    \"description\": \"Bucket name // 存储桶名称\",\n                    \"type\": \"string\",\n                    \"example\": \"my-bucket\"\n                },\n                \"customPath\": {\n                    \"description\": \"Custom path // 自定义路径\",\n                    \"type\": \"string\",\n                    \"example\": \"/backups\"\n                },\n                \"endpoint\": {\n                    \"description\": \"Endpoint (OSS) // 端点 oss\",\n                    \"type\": \"string\",\n                    \"example\": \"oss-cn-hangzhou.aliyuncs.com\"\n                },\n                \"id\": {\n                    \"description\": \"ID // ID\",\n                    \"type\": \"integer\",\n                    \"example\": 1\n                },\n                \"isEnabled\": {\n                    \"description\": \"Is enabled // 是否启用\",\n                    \"type\": \"integer\",\n                    \"example\": 1\n                },\n                \"password\": {\n                    \"description\": \"Password // 密码\",\n                    \"type\": \"string\",\n                    \"example\": \"secret_password\"\n                },\n                \"region\": {\n                    \"description\": \"Region (S3) // 区域 s3\",\n                    \"type\": \"string\",\n                    \"example\": \"us-east-1\"\n                },\n                \"type\": {\n                    \"description\": \"Storage type // 类型\",\n                    \"type\": \"string\",\n                    \"minLength\": 1,\n                    \"example\": \"local-fs\"\n                },\n                \"user\": {\n                    \"description\": \"Username // 访问用户名\",\n                    \"type\": \"string\",\n                    \"example\": \"admin\"\n                }\n            }\n        },\n        \"dto.SyncLogDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"action\": {\n                    \"description\": \"Action type // 操作类型\",\n                    \"type\": \"string\"\n                },\n                \"changedFields\": {\n                    \"description\": \"Changed fields // 变更字段\",\n                    \"type\": \"string\"\n                },\n                \"clientName\": {\n                    \"description\": \"Client name // 客户端名称\",\n                    \"type\": \"string\"\n                },\n                \"clientType\": {\n                    \"description\": \"Client type // 客户端类型\",\n                    \"type\": \"string\"\n                },\n                \"clientVersion\": {\n                    \"description\": \"Client version // 客户端版本\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Log creation time // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"message\": {\n                    \"description\": \"Additional message // 附加消息\",\n                    \"type\": \"string\"\n                },\n                \"path\": {\n                    \"description\": \"Resource path // 资源路径\",\n                    \"type\": \"string\"\n                },\n                \"pathHash\": {\n                    \"description\": \"Resource path hash // 路径哈希\",\n                    \"type\": \"string\"\n                },\n                \"size\": {\n                    \"description\": \"Size in bytes // 大小（字节）\",\n                    \"type\": \"integer\"\n                },\n                \"status\": {\n                    \"description\": \"Status: 1 success, 2 failed // 状态\",\n                    \"type\": \"integer\"\n                },\n                \"type\": {\n                    \"description\": \"Resource type // 资源类型\",\n                    \"type\": \"string\"\n                },\n                \"vaultId\": {\n                    \"description\": \"Vault ID // 笔记本 ID\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"dto.UserChangePasswordRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"confirmPassword\",\n                \"oldPassword\",\n                \"password\"\n            ],\n            \"properties\": {\n                \"confirmPassword\": {\n                    \"description\": \"Confirm password // 校验密码\",\n                    \"type\": \"string\",\n                    \"example\": \"new_password123\"\n                },\n                \"oldPassword\": {\n                    \"description\": \"Old password // 旧密码\",\n                    \"type\": \"string\",\n                    \"example\": \"old_password123\"\n                },\n                \"password\": {\n                    \"description\": \"New password // 新密码\",\n                    \"type\": \"string\",\n                    \"example\": \"new_password123\"\n                }\n            }\n        },\n        \"dto.UserCreateRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"confirmPassword\",\n                \"email\",\n                \"password\",\n                \"username\"\n            ],\n            \"properties\": {\n                \"confirmPassword\": {\n                    \"description\": \"Confirm password // 校验密码\",\n                    \"type\": \"string\",\n                    \"example\": \"password123\"\n                },\n                \"email\": {\n                    \"description\": \"User email // 用户邮件\",\n                    \"type\": \"string\",\n                    \"example\": \"user@example.com\"\n                },\n                \"password\": {\n                    \"description\": \"User password // 用户密码\",\n                    \"type\": \"string\",\n                    \"example\": \"password123\"\n                },\n                \"username\": {\n                    \"description\": \"User name // 用户名\",\n                    \"type\": \"string\",\n                    \"example\": \"username123\"\n                }\n            }\n        },\n        \"dto.UserDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"avatar\": {\n                    \"description\": \"Avatar URL or handle // 头像路径或名称\",\n                    \"type\": \"string\"\n                },\n                \"createdAt\": {\n                    \"description\": \"Account created time // 账号创建时间\",\n                    \"type\": \"string\"\n                },\n                \"email\": {\n                    \"description\": \"Email address // 邮件地址\",\n                    \"type\": \"string\"\n                },\n                \"token\": {\n                    \"description\": \"Authentication Token // 认证 Token\",\n                    \"type\": \"string\"\n                },\n                \"uid\": {\n                    \"description\": \"User ID (primary key) // 用户唯一标识（主键）\",\n                    \"type\": \"integer\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Last updated time // 最后更新时间\",\n                    \"type\": \"string\"\n                },\n                \"username\": {\n                    \"description\": \"Username // 用户名\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.UserLoginRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"credentials\",\n                \"password\"\n            ],\n            \"properties\": {\n                \"credentials\": {\n                    \"description\": \"Username or Email // 登录凭证（用户名或邮件）\",\n                    \"type\": \"string\",\n                    \"example\": \"user@example.com\"\n                },\n                \"password\": {\n                    \"description\": \"Password // 密码\",\n                    \"type\": \"string\",\n                    \"example\": \"password123\"\n                }\n            }\n        },\n        \"dto.VaultDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"createdAt\": {\n                    \"description\": \"Creation time // 创建时间\",\n                    \"type\": \"string\"\n                },\n                \"fileCount\": {\n                    \"description\": \"Number of files // 文件数量\",\n                    \"type\": \"integer\"\n                },\n                \"fileSize\": {\n                    \"description\": \"Size of files // 文件大小\",\n                    \"type\": \"integer\"\n                },\n                \"id\": {\n                    \"description\": \"Vault ID // 保险库 ID\",\n                    \"type\": \"integer\"\n                },\n                \"noteCount\": {\n                    \"description\": \"Number of notes // 笔记数量\",\n                    \"type\": \"integer\"\n                },\n                \"noteSize\": {\n                    \"description\": \"Size of notes // 笔记大小\",\n                    \"type\": \"integer\"\n                },\n                \"size\": {\n                    \"description\": \"Total size // 总大小\",\n                    \"type\": \"integer\"\n                },\n                \"updatedAt\": {\n                    \"description\": \"Updated time // 更新时间\",\n                    \"type\": \"string\"\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"dto.VaultPostRequest\": {\n            \"type\": \"object\",\n            \"required\": [\n                \"vault\"\n            ],\n            \"properties\": {\n                \"id\": {\n                    \"description\": \"Vault ID (optional for update) // 保险库 ID（可选，用于更新）\",\n                    \"type\": \"integer\",\n                    \"example\": 1\n                },\n                \"vault\": {\n                    \"description\": \"Vault name // 保险库名称\",\n                    \"type\": \"string\",\n                    \"example\": \"MyVault\"\n                }\n            }\n        },\n        \"dto.VersionDTO\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"buildTime\": {\n                    \"description\": \"Build time // 构建时间\",\n                    \"type\": \"string\"\n                },\n                \"gitTag\": {\n                    \"description\": \"Git tag // Git 标签\",\n                    \"type\": \"string\"\n                },\n                \"pluginVersionNewChangelog\": {\n                    \"description\": \"New plugin version changelog link // 插件新版本更新日志链接\",\n                    \"type\": \"string\"\n                },\n                \"pluginVersionNewChangelogContent\": {\n                    \"description\": \"New plugin version changelog content // 插件新版本更新日志内容\",\n                    \"type\": \"string\"\n                },\n                \"pluginVersionNewLink\": {\n                    \"description\": \"New plugin version link // 插件新版本链接\",\n                    \"type\": \"string\"\n                },\n                \"pluginVersionNewName\": {\n                    \"description\": \"New plugin version name // 插件新版本名称\",\n                    \"type\": \"string\"\n                },\n                \"version\": {\n                    \"description\": \"Current version // 当前版本\",\n                    \"type\": \"string\"\n                },\n                \"versionIsNew\": {\n                    \"description\": \"Is there a new version // 是否有新版本\",\n                    \"type\": \"boolean\"\n                },\n                \"versionNewChangelog\": {\n                    \"description\": \"New version changelog link // 新版本更新日志链接\",\n                    \"type\": \"string\"\n                },\n                \"versionNewChangelogContent\": {\n                    \"description\": \"New version changelog content // 新版本更新日志内容\",\n                    \"type\": \"string\"\n                },\n                \"versionNewLink\": {\n                    \"description\": \"New version download link // 新版本下载链接\",\n                    \"type\": \"string\"\n                },\n                \"versionNewName\": {\n                    \"description\": \"New version name // 新版本名称\",\n                    \"type\": \"string\"\n                }\n            }\n        }\n    },\n    \"securityDefinitions\": {\n        \"ShareAuthToken\": {\n            \"type\": \"apiKey\",\n            \"name\": \"Share-Token\",\n            \"in\": \"header\"\n        },\n        \"UserAuthToken\": {\n            \"type\": \"apiKey\",\n            \"name\": \"token\",\n            \"in\": \"header\"\n        }\n    }\n}"
  },
  {
    "path": "docs/swagger.yaml",
    "content": "basePath: /\ndefinitions:\n  api_router.HealthResponse:\n    properties:\n      database:\n        description: '\"connected\" or \"error\" // \"connected\" 或 \"error\"'\n        type: string\n      status:\n        description: '\"healthy\" or \"unhealthy\" // \"healthy\" 或 \"unhealthy\"'\n        type: string\n      uptime:\n        description: Uptime (seconds) // 运行时间（秒）\n        type: number\n      version:\n        description: Service version number // 服务版本号\n        type: string\n    type: object\n  app.ListRes:\n    properties:\n      list:\n        description: Data list // 数据清单\n      pager:\n        allOf:\n        - $ref: '#/definitions/app.Pager'\n        description: Pagination info // 翻页信息\n    type: object\n  app.Pager:\n    properties:\n      page:\n        description: Page number // 页码\n        type: integer\n      pageSize:\n        description: Page size // 每页数量\n        type: integer\n      totalRows:\n        description: Total rows // 总行数\n        type: integer\n    type: object\n  app.Res:\n    properties:\n      code:\n        type: integer\n      context: {}\n      data: {}\n      details: {}\n      message: {}\n      status:\n        type: boolean\n      vault: {}\n    type: object\n  app.WSClientInfo:\n    properties:\n      clientName:\n        type: string\n      clientType:\n        type: string\n      clientVersion:\n        type: string\n      nickname:\n        type: string\n      platformInfo:\n        additionalProperties:\n          type: boolean\n        type: object\n      remoteAddr:\n        type: string\n      startTime:\n        type: string\n      traceId:\n        type: string\n      uid:\n        type: string\n    type: object\n  diffmatchpatch.Diff:\n    properties:\n      text:\n        type: string\n      type:\n        $ref: '#/definitions/diffmatchpatch.Operation'\n    type: object\n  diffmatchpatch.Operation:\n    enum:\n    - -1\n    - 1\n    - 0\n    format: int32\n    type: integer\n    x-enum-varnames:\n    - DiffDelete\n    - DiffInsert\n    - DiffEqual\n  dto.AdminCPUInfo:\n    properties:\n      loadAvg:\n        allOf:\n        - $ref: '#/definitions/dto.AdminLoadInfo'\n        description: Load average // 平均负载\n      logicalCores:\n        description: Logical cores // 逻辑核心数\n        type: integer\n      modelName:\n        description: Model name // 型号\n        type: string\n      percent:\n        description: Usage percentage per core // 每个核心的使用率\n        items:\n          type: number\n        type: array\n      physicalCores:\n        description: Physical cores // 物理核心数\n        type: integer\n    type: object\n  dto.AdminCheckResponse:\n    properties:\n      isAdmin:\n        description: Whether have admin privileges // 是否具有管理员权限\n        type: boolean\n    type: object\n  dto.AdminCloudflareConfig:\n    properties:\n      enabled:\n        description: Whether to enable cloudflare tunnel // 是否启用 cloudflare 隧道\n        type: boolean\n      logEnabled:\n        description: Whether to enable cloudflare tunnel logging // 是否开启 cloudflare\n          隧道日志\n        type: boolean\n      token:\n        description: cloudflare tunnel token // cloudflare 隧道令牌\n        type: string\n    type: object\n  dto.AdminConfig:\n    properties:\n      adminUid:\n        description: Admin UID // 管理员 UID\n        type: integer\n      authTokenKey:\n        description: Auth token key // 认证 Token 密钥\n        type: string\n      defaultApiFolder:\n        description: Default API folder // 默认 API 目录\n        type: string\n      fileChunkSize:\n        description: File chunk size // 文件分块大小\n        type: string\n      fontSet:\n        description: Font set // 字体设置\n        type: string\n      historyKeepVersions:\n        description: History versions to keep // 历史版本保留数\n        type: integer\n      historySaveDelay:\n        description: History save delay // 历史保存延迟\n        type: string\n      pullSource:\n        description: 'Data pull source: auto | github | cnb // 数据拉取源：auto | github\n          | cnb'\n        type: string\n      registerIsEnable:\n        description: Registration enablement // 是否开启注册\n        type: boolean\n      shareTokenExpiry:\n        description: Share token expiry // 分享 Token 有效期\n        type: string\n      shareTokenKey:\n        description: Share token key // 分享 Token 密钥\n        type: string\n      softDeleteRetentionTime:\n        description: Soft delete retention time // 软删除保留时间\n        type: string\n      tokenExpiry:\n        description: Token expiry // Token 有效期\n        type: string\n      uploadSessionTimeout:\n        description: Upload session timeout // 上传会话超时时间\n        type: string\n    type: object\n  dto.AdminHostInfo:\n    properties:\n      arch:\n        description: Architecture // 架构\n        type: string\n      currentTime:\n        description: Current system time // 当前系统时间\n        type: string\n      hostname:\n        description: Hostname // 主机名\n        type: string\n      kernelVersion:\n        description: Kernel version // 内核版本\n        type: string\n      os:\n        description: Operating system // 操作系统\n        type: string\n      osPretty:\n        description: Detailed OS name // 详细操作系统名称\n        type: string\n      platform:\n        description: Platform name // 平台\n        type: string\n      timezone:\n        description: Time zone name // 时区名称\n        type: string\n      timezoneOffset:\n        description: Time zone offset in seconds // 时区偏移（秒）\n        type: integer\n      uptime:\n        description: System uptime // 系统运行时间\n        type: integer\n    type: object\n  dto.AdminLoadInfo:\n    properties:\n      load1:\n        description: Load 1 min // 1分钟负载\n        type: number\n      load5:\n        description: Load 5 min // 5分钟负载\n        type: number\n      load15:\n        description: Load 15 min // 15分钟负载\n        type: number\n    type: object\n  dto.AdminMemoryInfo:\n    properties:\n      available:\n        description: Available memory // 可用内存\n        type: integer\n      swapTotal:\n        description: Total swap space // 交换区总量\n        type: integer\n      swapUsed:\n        description: Used swap space // 交换区已用\n        type: integer\n      swapUsedPercent:\n        description: Swap usage percentage // 交换区使用率\n        type: number\n      total:\n        description: Total physical memory // 系统总内存\n        type: integer\n      used:\n        description: Used memory // 已用内存\n        type: integer\n      usedPercent:\n        description: Memory usage percentage // 内存使用率\n        type: number\n    type: object\n  dto.AdminNgrokConfig:\n    properties:\n      authToken:\n        description: ngrok auth token // ngrok 认证令牌\n        type: string\n      domain:\n        description: Custom domain // 自定义域名\n        type: string\n      enabled:\n        description: Whether to enable ngrok tunnel // 是否启用 ngrok 隧道\n        type: boolean\n    type: object\n  dto.AdminProcessInfo:\n    properties:\n      cpuPercent:\n        description: CPU Usage percentage // CPU 使用率\n        type: number\n      memoryPercent:\n        description: Memory Usage percentage // 内存使用率\n        type: number\n      name:\n        description: Process Name // 进程名称\n        type: string\n      pid:\n        description: Process ID // 进程 ID\n        type: integer\n      ppid:\n        description: Parent Process ID // 父进程 ID\n        type: integer\n    type: object\n  dto.AdminRuntimeInfo:\n    properties:\n      buckHashSys:\n        description: Memory obtained from system for profiling bucket hash table (bytes)\n          // 分析桶哈希表占用的系统内存\n        type: integer\n      gcSys:\n        description: Memory obtained from system for metadata for GC (bytes) // GC\n          元数据占用的系统内存\n        type: integer\n      heapIdle:\n        description: Memory in idle spans (bytes) // 空闲 Span 占用的内存\n        type: integer\n      heapInuse:\n        description: Memory in in-use spans (bytes) // 正在使用的 Span 占用的内存\n        type: integer\n      heapReleased:\n        description: Memory released to OS (bytes) // 释放回操作系统的内存（字节）\n        type: integer\n      heapSys:\n        description: Memory obtained from system for heap (bytes) // 堆占用的系统内存\n        type: integer\n      mCacheSys:\n        description: Memory obtained from system for mcache (bytes) // mcache 占用的系统内存\n        type: integer\n      mSpanSys:\n        description: Memory obtained from system for mspan (bytes) // mspan 占用的系统内存\n        type: integer\n      memAlloc:\n        description: Allocated memory (bytes) // 已分配内存（字节）\n        type: integer\n      memSys:\n        description: Memory obtained from system (bytes) // 从系统获取的内存（字节）\n        type: integer\n      memTotal:\n        description: Total memory allocated (bytes) // 累计分配内存（字节）\n        type: integer\n      nextGc:\n        description: Target heap size for the next GC cycle // 下次 GC 的目标堆大小\n        type: integer\n      numGc:\n        description: Number of completed GC cycles // GC 次数\n        type: integer\n      numGoroutine:\n        description: Number of goroutines // Goroutine 数量\n        type: integer\n      otherSys:\n        description: Other system memory (bytes) // 其他系统内存\n        type: integer\n      stackSys:\n        description: Memory obtained from system for stack (bytes) // 栈占用的系统内存\n        type: integer\n    type: object\n  dto.AdminSystemInfo:\n    properties:\n      cpu:\n        allOf:\n        - $ref: '#/definitions/dto.AdminCPUInfo'\n        description: CPU information // CPU 信息\n      host:\n        allOf:\n        - $ref: '#/definitions/dto.AdminHostInfo'\n        description: Host information // 主机信息\n      memory:\n        allOf:\n        - $ref: '#/definitions/dto.AdminMemoryInfo'\n        description: Memory information // 内存信息\n      process:\n        allOf:\n        - $ref: '#/definitions/dto.AdminProcessInfo'\n        description: Process information // 进程信息\n      runtimeStatus:\n        allOf:\n        - $ref: '#/definitions/dto.AdminRuntimeInfo'\n        description: Go runtime status // Go 运行时状态\n      startTime:\n        description: Start time // 启动时间\n        type: string\n      uptime:\n        description: Uptime (seconds) // 运行时间（秒）\n        type: number\n    type: object\n  dto.AdminUserDatabaseConfig:\n    properties:\n      charset:\n        description: Charset // 字符集\n        type: string\n      connMaxIdleTime:\n        description: Connection max idle time // 空闲连接最大生命周期\n        type: string\n      connMaxLifetime:\n        description: Connection max lifetime // 连接最大生命周期\n        type: string\n      host:\n        description: Host // 主机\n        type: string\n      maxIdleConns:\n        description: Max idle connections // 最大闲置连接数\n        type: integer\n      maxOpenConns:\n        description: Max open connections // 最大打开连接数\n        type: integer\n      maxWriteConcurrency:\n        description: Max write concurrency // 最大并发写入数\n        type: integer\n      name:\n        description: Database name // 数据库名\n        type: string\n      parseTime:\n        description: Parse time // 是否解析时间\n        type: boolean\n      password:\n        description: Password // 密码\n        type: string\n      path:\n        description: SQLite database file path // SQLite 数据库文件路径\n        type: string\n      port:\n        description: Port // 端口\n        type: integer\n      schema:\n        description: Database schema (postgres only) // 数据库 Schema\n        type: string\n      sslMode:\n        description: SSL mode (postgres only) // SSL 模式\n        type: string\n      type:\n        description: Database type (mysql, postgres, sqlite) // 数据库类型\n        enum:\n        - mysql\n        - postgres\n        - sqlite\n        type: string\n      userName:\n        description: Username // 用户名\n        type: string\n    type: object\n  dto.AdminWebGUIConfig:\n    properties:\n      adminUid:\n        description: Admin UID // 管理员 UID\n        type: integer\n      fontSet:\n        description: Font set // 字体设置\n        type: string\n      registerIsEnable:\n        description: Registration enablement // 是否开启注册\n        type: boolean\n    type: object\n  dto.BackupConfigDTO:\n    properties:\n      createdAt:\n        description: Created at // 创建时间\n        type: string\n      cronExpression:\n        description: Cron expression // Cron表达式\n        type: string\n      cronStrategy:\n        description: Cron strategy // 定时策略\n        type: string\n      id:\n        description: Config ID // 配置ID\n        type: integer\n      includeVaultName:\n        description: Whether sync path includes vault name // 同步路径是否包含仓库名\n        type: boolean\n      isEnabled:\n        description: Is enabled // 是否启用\n        type: boolean\n      lastMessage:\n        description: Last run result message // 上次运行结果消息\n        type: string\n      lastRunTime:\n        description: Last run time // 上次运行时间\n        type: string\n      lastStatus:\n        description: Last status (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Stopped)\n          // 上次状态 (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Stopped)\n        type: integer\n      nextRunTime:\n        description: Next run time // 下次运行时间\n        type: string\n      retentionDays:\n        description: Retention days // 保留天数\n        type: integer\n      storageIds:\n        description: Storage ID list // 存储ID列表\n        type: string\n      type:\n        description: Backup type (full, incremental, sync) // 备份类型 (full, incremental,\n          sync)\n        type: string\n      uid:\n        description: User UID // 用户ID\n        type: integer\n      updatedAt:\n        description: Updated at // 更新时间\n        type: string\n      vault:\n        description: Associated vault name // 关联库名称\n        type: string\n    type: object\n  dto.BackupConfigRequest:\n    properties:\n      cronExpression:\n        description: Cron expression // Cron 表达式\n        example: 0 0 * * *\n        type: string\n      cronStrategy:\n        description: Cron strategy // 定时策略\n        enum:\n        - daily\n        - weekly\n        - monthly\n        - custom\n        example: daily\n        type: string\n      id:\n        description: ID // ID\n        example: 1\n        type: integer\n      includeVaultName:\n        description: Include vault name // 同步路径是否包含仓库名\n        example: false\n        type: boolean\n      isEnabled:\n        description: Is enabled // 是否启用\n        example: true\n        type: boolean\n      retentionDays:\n        description: Retention days // 保留天数\n        example: 7\n        minimum: -1\n        type: integer\n      storageIds:\n        description: Storage IDs // 存储 ID 列表\n        example: '[1, 2]'\n        type: string\n      type:\n        description: Backup type // 备份类型\n        enum:\n        - full\n        - incremental\n        - sync\n        example: sync\n        type: string\n      vault:\n        description: Vault name // 仓库名称\n        example: test\n        type: string\n    required:\n    - cronStrategy\n    - storageIds\n    - type\n    type: object\n  dto.BackupExecuteRequest:\n    properties:\n      id:\n        description: ID // ID\n        example: 1\n        type: integer\n    type: object\n  dto.BackupHistoryDTO:\n    properties:\n      configId:\n        description: Config ID // 配置ID\n        type: integer\n      createdAt:\n        description: Created at // 创建时间\n        type: string\n      endTime:\n        description: End time // 结束时间\n        type: string\n      fileCount:\n        description: File count // 文件数量\n        type: integer\n      filePath:\n        description: File path // 文件路径\n        type: string\n      fileSize:\n        description: File size // 文件大小\n        type: integer\n      id:\n        description: History record ID // 历史记录ID\n        type: integer\n      message:\n        description: Result message // 结果消息\n        type: string\n      startTime:\n        description: Start time // 开始时间\n        type: string\n      status:\n        description: Status (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Stopped) //\n          状态 (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Stopped)\n        type: integer\n      storageId:\n        description: Storage ID // 存储ID\n        type: integer\n      type:\n        description: Backup type // 备份类型\n        type: string\n      uid:\n        description: User UID // 用户ID\n        type: integer\n      updatedAt:\n        description: Updated at // 更新时间\n        type: string\n    type: object\n  dto.FileDTO:\n    properties:\n      contentHash:\n        description: Content hash // 内容哈希\n        type: string\n      createdAt:\n        description: Created at time // 创建时间\n        type: string\n      ctime:\n        description: Creation timestamp // 创建时间戳\n        type: integer\n      lastTime:\n        description: Updated timestamp // 更新时间戳\n        type: integer\n      mtime:\n        description: Modification timestamp // 修改时间戳\n        type: integer\n      path:\n        description: File path // 文件路径\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希\n        type: string\n      rename:\n        description: Rename flag // 重命名标记\n        type: integer\n      size:\n        description: File size // 文件大小\n        type: integer\n      updatedAt:\n        description: Updated at time // 更新时间\n        type: string\n    type: object\n  dto.FileRecycleClearRequest:\n    properties:\n      path:\n        description: File path, empty for all // 文件路径，为空则清理全部\n        example: path/to/file.png\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希\n        example: fhash123\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        example: MyVault\n        type: string\n    required:\n    - vault\n    type: object\n  dto.FileRenameRequest:\n    properties:\n      oldPath:\n        description: Old path // 旧路径\n        example: OldImage.png\n        type: string\n      oldPathHash:\n        description: Old path hash // 旧路径哈希\n        example: ofhash456\n        type: string\n      path:\n        description: New path // 新路径\n        example: NewImage.png\n        type: string\n      pathHash:\n        description: New path hash // 新路径哈希\n        example: nfhash123\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        example: MyVault\n        type: string\n    required:\n    - oldPath\n    - path\n    - vault\n    type: object\n  dto.FileRestoreRequest:\n    properties:\n      path:\n        description: File path // 文件路径\n        example: Image.png\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希\n        example: fhash123\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        example: MyVault\n        type: string\n    required:\n    - path\n    - vault\n    type: object\n  dto.FolderCreateRequest:\n    properties:\n      path:\n        description: Folder path // 文件夹路径\n        example: NewFolder\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希\n        example: fhash456\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        example: MyVault\n        type: string\n    required:\n    - path\n    - vault\n    type: object\n  dto.FolderDTO:\n    properties:\n      createdAt:\n        description: Created at time // 创建时间\n        type: string\n      ctime:\n        description: Creation timestamp // 创建时间戳\n        type: integer\n      lastTime:\n        description: Record update timestamp // 记录更新时间戳\n        type: integer\n      mtime:\n        description: Modification timestamp // 修改时间戳\n        type: integer\n      path:\n        description: Folder path // 文件夹路径\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希值\n        type: string\n      updatedAt:\n        description: Updated at time // 更新时间\n        type: string\n    type: object\n  dto.FolderDeleteRequest:\n    properties:\n      path:\n        description: Folder path // 文件夹路径\n        example: OldFolder\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希\n        example: fhash789\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        example: MyVault\n        type: string\n    required:\n    - path\n    - vault\n    type: object\n  dto.FolderTreeNode:\n    properties:\n      children:\n        description: Child nodes // 子节点\n        items:\n          $ref: '#/definitions/dto.FolderTreeNode'\n        type: array\n      fileCount:\n        description: File count // 文件数量\n        type: integer\n      name:\n        description: Node name // 节点名称\n        type: string\n      noteCount:\n        description: Note count // 笔记数量\n        type: integer\n      path:\n        description: Node path // 节点路径\n        type: string\n    type: object\n  dto.FolderTreeResponse:\n    properties:\n      folders:\n        description: Folder tree // 文件夹树\n        items:\n          $ref: '#/definitions/dto.FolderTreeNode'\n        type: array\n      rootFileCount:\n        description: File count in root // 根目录中的文件数量\n        type: integer\n      rootNoteCount:\n        description: Note count in root // 根目录中的笔记数量\n        type: integer\n    type: object\n  dto.GitSyncCleanRequest:\n    properties:\n      configId:\n        type: integer\n    type: object\n  dto.GitSyncConfigDTO:\n    properties:\n      branch:\n        description: Branch // 分支\n        type: string\n      createdAt:\n        description: Created at // 创建时间\n        type: string\n      delay:\n        description: Delay time (seconds) // 延迟时间（秒）\n        type: integer\n      id:\n        description: Task ID // 任务ID\n        type: integer\n      isEnabled:\n        description: Is enabled // 是否启用\n        type: boolean\n      lastMessage:\n        description: Last run result message // 上次运行结果消息\n        type: string\n      lastStatus:\n        description: Last status (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Shutdown)\n          // 上次状态 (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Shutdown)\n        type: integer\n      lastSyncTime:\n        description: Last sync time // 上次同步时间\n        type: string\n      password:\n        description: Password // 密码\n        type: string\n      repoUrl:\n        description: Repository URL // 仓库地址\n        type: string\n      retentionDays:\n        description: History retention days // 历史记录保留天数\n        type: integer\n      uid:\n        description: User ID // 用户ID\n        type: integer\n      updatedAt:\n        description: Updated at // 更新时间\n        type: string\n      username:\n        description: Username // 用户名\n        type: string\n      vault:\n        description: Associated vault name // 关联库名称\n        type: string\n    type: object\n  dto.GitSyncConfigRequest:\n    properties:\n      branch:\n        type: string\n      delay:\n        description: Delay time (seconds) // 延迟时间（秒）\n        type: integer\n      id:\n        type: integer\n      isEnabled:\n        type: boolean\n      password:\n        type: string\n      repoUrl:\n        type: string\n      retentionDays:\n        type: integer\n      username:\n        type: string\n      vault:\n        description: Associated vault name // 关联笔记本名称\n        type: string\n    required:\n    - repoUrl\n    type: object\n  dto.GitSyncDeleteRequest:\n    properties:\n      id:\n        type: integer\n    required:\n    - id\n    type: object\n  dto.GitSyncExecuteRequest:\n    properties:\n      id:\n        type: integer\n    required:\n    - id\n    type: object\n  dto.GitSyncHistoryDTO:\n    properties:\n      configId:\n        type: integer\n      createdAt:\n        type: string\n      endTime:\n        type: string\n      id:\n        type: integer\n      message:\n        type: string\n      startTime:\n        type: string\n      status:\n        description: 0:Idle, 1:Running, 2:Success, 3:Failed, 4:Shutdown\n        type: integer\n    type: object\n  dto.GitSyncValidateRequest:\n    properties:\n      branch:\n        type: string\n      password:\n        type: string\n      repoUrl:\n        type: string\n      username:\n        type: string\n    required:\n    - repoUrl\n    type: object\n  dto.NoteAppendRequest:\n    properties:\n      content:\n        description: Content to append // 追加内容\n        example: Appended content\n        type: string\n      path:\n        description: Note path // 笔记路径\n        example: ReadMe.md\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希\n        example: hash123\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        example: MyVault\n        type: string\n    required:\n    - content\n    - path\n    - vault\n    type: object\n  dto.NoteDTO:\n    properties:\n      clientName:\n        description: Client name // 客户端名称\n        type: string\n      clientType:\n        description: Client type // 客户端类型\n        type: string\n      clientVersion:\n        description: Client version // 客户端版本\n        type: string\n      content:\n        description: Note content // 笔记内容\n        type: string\n      contentHash:\n        description: Content hash // 内容哈希\n        type: string\n      createdAt:\n        description: Created at time // 创建时间\n        type: string\n      ctime:\n        description: Creation timestamp // 创建时间戳\n        type: integer\n      lastTime:\n        description: Record update timestamp // 记录更新时间戳\n        type: integer\n      mtime:\n        description: Modification timestamp // 修改时间戳\n        type: integer\n      path:\n        description: Note path // 笔记路径\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希\n        type: string\n      size:\n        description: Note size // 笔记大小\n        type: integer\n      updatedAt:\n        description: Updated at time // 更新时间\n        type: string\n      version:\n        description: Version number // 版本号\n        type: integer\n    type: object\n  dto.NoteHistoryDTO:\n    properties:\n      clientName:\n        description: Client that made changes // 产生变更的客户端\n        type: string\n      clientType:\n        description: Client type // 客户端类型\n        type: string\n      clientVersion:\n        description: Client version // 客户端版本\n        type: string\n      content:\n        description: Full historical content // 完整历史内容\n        type: string\n      contentHash:\n        description: Content hash // 内容哈希\n        type: string\n      createdAt:\n        description: Creation time of this version // 此版本的创建时间\n        type: string\n      diffs:\n        description: Text differences // 文本差异内容\n        items:\n          $ref: '#/definitions/diffmatchpatch.Diff'\n        type: array\n      id:\n        description: History entry ID // 历史项 ID\n        type: integer\n      noteId:\n        description: Associated note ID // 笔记 ID\n        type: integer\n      path:\n        description: Note path at that time // 当时的笔记路径\n        type: string\n      vaultId:\n        description: Associated vault ID // 保险库 ID\n        type: integer\n      version:\n        description: Historical version number // 历史版本号\n        type: integer\n    type: object\n  dto.NoteHistoryRestoreRequest:\n    properties:\n      historyId:\n        description: History version ID // 历史版本 ID\n        example: 1\n        type: integer\n      vault:\n        description: Vault name // 保险库名称\n        example: MyVault\n        type: string\n    required:\n    - historyId\n    - vault\n    type: object\n  dto.NoteLinkItem:\n    properties:\n      context:\n        description: Text context around link // 链接文本上下文\n        type: string\n      isEmbed:\n        description: Is it an embed (![[...]]) // 是否为嵌入\n        type: boolean\n      linkText:\n        description: Raw link text (optional) // 原始链接文本（可选）\n        type: string\n      path:\n        description: Target path // 目标路径\n        type: string\n    type: object\n  dto.NoteModifyOrCreateRequest:\n    properties:\n      baseHash:\n        description: Base hash for sync // 同步基准哈希\n        example: bhash789\n        type: string\n      baseHashMissing:\n        description: Marks if baseHash is unavailable // 标记基准哈希是否缺失\n        example: false\n        type: boolean\n      content:\n        description: Note content // 笔记内容\n        example: '# Hello World'\n        type: string\n      contentHash:\n        description: Content hash // 内容哈希\n        example: chash012\n        type: string\n      createOnly:\n        description: If true, fail if note already exists // 如果为 true，笔记已存在则失败\n        example: false\n        type: boolean\n      ctime:\n        description: Creation timestamp // 创建时间戳\n        example: 1700000000\n        type: integer\n      mtime:\n        description: Modification timestamp // 修改时间戳\n        example: 1700000000\n        type: integer\n      path:\n        description: Note path // 笔记路径\n        example: ReadMe.md\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希\n        example: hash123\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        example: MyVault\n        type: string\n    required:\n    - path\n    - vault\n    type: object\n  dto.NoteMoveRequest:\n    properties:\n      destination:\n        description: Destination path // 目标路径\n        example: Folder/Source.md\n        type: string\n      overwrite:\n        description: Overwrite existing // 覆盖现有\n        example: false\n        type: boolean\n      path:\n        description: Current path // 当前路径\n        example: Source.md\n        type: string\n      pathHash:\n        description: Current path hash // 当前路径哈希\n        example: src_hash123\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        example: MyVault\n        type: string\n    required:\n    - destination\n    - path\n    - vault\n    type: object\n  dto.NoteNoContentDTO:\n    properties:\n      clientName:\n        description: Client name // 客户端名称\n        type: string\n      clientType:\n        description: Client type // 客户端类型\n        type: string\n      clientVersion:\n        description: Client version // 客户端版本\n        type: string\n      createdAt:\n        description: Created at time // 创建时间\n        type: string\n      ctime:\n        description: Creation timestamp // 创建时间戳\n        type: integer\n      lastTime:\n        description: Record update timestamp // 记录更新时间戳\n        type: integer\n      mtime:\n        description: Modification timestamp // 修改时间戳\n        type: integer\n      path:\n        description: Note path // 笔记路径\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希\n        type: string\n      size:\n        description: Note size // 笔记大小\n        type: integer\n      updatedAt:\n        description: Updated at time // 更新时间\n        type: string\n      version:\n        description: Version number // 版本号\n        type: integer\n    type: object\n  dto.NotePatchFrontmatterRequest:\n    properties:\n      path:\n        description: Note path // 笔记路径\n        example: ReadMe.md\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希\n        example: hash123\n        type: string\n      remove:\n        description: Fields to remove // 待移除字段\n        example:\n        - old_tag\n        items:\n          type: string\n        type: array\n      updates:\n        additionalProperties:\n          items:\n            type: string\n          type: array\n        description: Fields to update // 待更新字段\n        type: object\n      vault:\n        description: Vault name // 保险库名称\n        example: MyVault\n        type: string\n    required:\n    - path\n    - vault\n    type: object\n  dto.NotePrependRequest:\n    properties:\n      content:\n        description: Content to prepend // 头部添加内容\n        example: |\n          Prepended content\n        type: string\n      path:\n        description: Note path // 笔记路径\n        example: ReadMe.md\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希\n        example: hash123\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        example: MyVault\n        type: string\n    required:\n    - content\n    - path\n    - vault\n    type: object\n  dto.NoteRecycleClearRequest:\n    properties:\n      path:\n        description: Note path, empty for all // 笔记路径，为空则清理全部\n        example: path/to/note.md\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希\n        example: hash123\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        example: MyVault\n        type: string\n    required:\n    - vault\n    type: object\n  dto.NoteRenameRequest:\n    properties:\n      oldPath:\n        description: Old path // 旧路径\n        example: OldName.md\n        type: string\n      oldPathHash:\n        description: Old path hash // 旧路径哈希\n        example: ohash456\n        type: string\n      path:\n        description: New path // 新路径\n        example: NewName.md\n        type: string\n      pathHash:\n        description: New path hash // 新路径哈希\n        example: nhash123\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        example: MyVault\n        type: string\n    required:\n    - oldPath\n    - path\n    - vault\n    type: object\n  dto.NoteReplaceRequest:\n    properties:\n      all:\n        description: Replace all matches // 替换所有\n        example: true\n        type: boolean\n      failIfNoMatch:\n        description: Fail if no match found // 若无匹配则失败\n        example: true\n        type: boolean\n      find:\n        description: String to find // 查找内容\n        example: old text\n        type: string\n      path:\n        description: Note path // 笔记路径\n        example: ReadMe.md\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希\n        example: hash123\n        type: string\n      regex:\n        description: Use regex // 使用正则\n        example: false\n        type: boolean\n      replace:\n        description: String to replace with // 替换内容\n        example: new text\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        example: MyVault\n        type: string\n    required:\n    - find\n    - path\n    - vault\n    type: object\n  dto.NoteReplaceResponse:\n    properties:\n      matchCount:\n        description: Number of matches found // 匹配数量\n        type: integer\n      note:\n        allOf:\n        - $ref: '#/definitions/dto.NoteDTO'\n        description: Updated note data // 更新后的笔记数据\n    type: object\n  dto.NoteRestoreRequest:\n    properties:\n      path:\n        description: Note path // 笔记路径\n        example: ReadMe.md\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希\n        example: hash123\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        example: MyVault\n        type: string\n    required:\n    - path\n    - vault\n    type: object\n  dto.NoteWithFileLinksResponse:\n    properties:\n      content:\n        description: Note content // 笔记内容\n        type: string\n      contentHash:\n        description: Content hash // 内容哈希\n        type: string\n      createdAt:\n        description: Created at time // 创建时间\n      ctime:\n        description: Creation timestamp // 创建时间戳\n        type: integer\n      fileLinks:\n        additionalProperties:\n          type: string\n        description: Map of file link to actual path // 文件链接到实际路径的映射\n        type: object\n      lastTime:\n        description: Record update timestamp // 记录更新时间戳\n        type: integer\n      mtime:\n        description: Modification timestamp // 修改时间戳\n        type: integer\n      path:\n        description: Note path // 笔记路径\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希\n        type: string\n      updatedAt:\n        description: Updated at time // 更新时间\n      version:\n        description: Version number // 版本号\n        type: integer\n    type: object\n  dto.SettingDTO:\n    properties:\n      content:\n        description: Setting content // 配置内容\n        type: string\n      contentHash:\n        description: Content hash // 内容哈希\n        type: string\n      createdAt:\n        description: Created at time // 创建时间\n        type: string\n      ctime:\n        description: Creation timestamp // 创建时间戳\n        type: integer\n      id:\n        description: Setting ID // 配置 ID\n        type: integer\n      lastTime:\n        description: Record update timestamp // 记录更新时间戳\n        type: integer\n      mtime:\n        description: Modification timestamp // 修改时间戳\n        type: integer\n      path:\n        description: Setting path // 配置路径\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希值\n        type: string\n      updatedAt:\n        description: Updated at time // 更新时间\n        type: string\n    type: object\n  dto.SettingDeleteRequest:\n    properties:\n      path:\n        description: Setting path // 配置路径\n        example: User/Theme\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希\n        example: hash123\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        example: MyVault\n        type: string\n    required:\n    - path\n    - vault\n    type: object\n  dto.SettingModifyOrCreateRequest:\n    properties:\n      content:\n        description: Setting content // 配置内容\n        example: dark\n        type: string\n      contentHash:\n        description: Content hash // 内容哈希\n        example: chash456\n        type: string\n      ctime:\n        description: Creation timestamp // 创建时间戳\n        example: 1700000000\n        type: integer\n      mtime:\n        description: Modification timestamp // 修改时间戳\n        example: 1700000000\n        type: integer\n      path:\n        description: Setting path // 配置路径\n        example: User/Theme\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希\n        example: hash123\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        example: MyVault\n        type: string\n    required:\n    - path\n    - vault\n    type: object\n  dto.SettingRenameRequest:\n    properties:\n      newPath:\n        description: New path // 新路径\n        example: New/Path\n        type: string\n      newPathHash:\n        description: New path hash // 新路径哈希\n        example: newhash456\n        type: string\n      oldPath:\n        description: Old path // 旧路径\n        example: Old/Path\n        type: string\n      oldPathHash:\n        description: Old path hash // 旧路径哈希\n        example: oldhash123\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        example: MyVault\n        type: string\n    required:\n    - newPath\n    - oldPath\n    - vault\n    type: object\n  dto.ShareCancelRequest:\n    properties:\n      id:\n        description: Share ID (optional) // 分享 ID (可选)\n        example: 1\n        type: integer\n      path:\n        description: Resource path (optional) // 资源路径 (可选)\n        example: ReadMe.md\n        type: string\n      pathHash:\n        description: Resource path Hash (optional) // 资源路径哈希 (可选)\n        example: hash123\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        example: defaultVault\n        type: string\n    required:\n    - vault\n    type: object\n  dto.ShareCreateRequest:\n    properties:\n      password:\n        description: Share password // 分享密码\n        example: \"123456\"\n        type: string\n      path:\n        description: Resource path // 资源路径\n        example: ReadMe.md\n        type: string\n      pathHash:\n        description: Resource path Hash // 资源路径哈希\n        example: hash123\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        example: defaultVault\n        type: string\n    required:\n    - path\n    - pathHash\n    - vault\n    type: object\n  dto.ShareCreateResponse:\n    properties:\n      expiresAt:\n        description: Expiration time // 过期时间\n        type: string\n      id:\n        description: ID of the note or file table (primary resource ID) // 笔记或文件表\n          ID（主资源 ID）\n        type: integer\n      isPassword:\n        description: Whether password is set // 是否设置了密码\n        type: boolean\n      shortLink:\n        description: Short link // 短链\n        type: string\n      token:\n        description: Share Token // 分享 Token\n        type: string\n      type:\n        description: 'Resource type: note or file // 资源类型：笔记（note）或文件（file）'\n        type: string\n    type: object\n  dto.ShareListItem:\n    properties:\n      createdAt:\n        description: Created at // 创建时间\n        type: string\n      expiresAt:\n        description: Expiration time // 过期时间\n        type: string\n      id:\n        description: Share ID // 分享记录 ID\n        type: integer\n      isPassword:\n        description: Whether password is set // 是否设置了密码\n        type: boolean\n      lastViewedAt:\n        description: Last viewed time // 最后访问时间\n        type: string\n      notePath:\n        description: Note path, for frontend share filter matching // 笔记路径，用于前端分享筛选匹配\n        type: string\n      res:\n        additionalProperties:\n          items:\n            type: string\n          type: array\n        description: Authorized resources // 资源授权列表\n        type: object\n      shortLink:\n        description: Short link // 短链\n        type: string\n      status:\n        description: 'Status: 1-Active, 2-Cancelled // 状态: 1-有效, 2-已撤销'\n        type: integer\n      title:\n        description: Resource title (note title or file name) // 资源标题（笔记标题或文件名）\n        type: string\n      uid:\n        description: User ID // 用户 ID\n        type: integer\n      updatedAt:\n        description: Updated at // 更新时间\n        type: string\n      url:\n        description: 'Share URL (path format: /id/token) // 分享 URL (路径格式: /id/token)'\n        type: string\n      vaultName:\n        description: Vault name where the note belongs // 笔记所属仓库名\n        type: string\n      viewCount:\n        description: View count // 访问次数\n        type: integer\n    type: object\n  dto.SharePasswordUpdateRequest:\n    properties:\n      password:\n        description: New password // 新密码\n        example: \"123456\"\n        type: string\n      path:\n        description: Resource path // 资源路径\n        example: 未命名.md\n        type: string\n      pathHash:\n        description: Resource path Hash // 资源路径哈希\n        example: \"-677306325\"\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        example: test\n        type: string\n    required:\n    - path\n    - pathHash\n    - vault\n    type: object\n  dto.ShareShortLinkCreateRequest:\n    properties:\n      is_force:\n        description: Whether to force regeneration // 是否强制重新生成\n        example: false\n        type: boolean\n      path:\n        description: Path // 路径\n        example: notes/todo.md\n        type: string\n      pathHash:\n        description: Path hash // 路径哈希\n        example: '...'\n        type: string\n      url:\n        description: Full share URL from client; if provided, used directly without\n          regenerating token // 客户端传入的完整分享链接，非空时直接使用，不重新生成 token\n        example: https://example.com/share/129/CNmkmQlq0s-4elT3NuZG2w\n        type: string\n      vault:\n        description: Vault name // 库名\n        example: work\n        type: string\n    required:\n    - path\n    - pathHash\n    - vault\n    type: object\n  dto.StorageDTO:\n    properties:\n      accessKeyId:\n        description: Access key ID // 访问密钥 ID\n        type: string\n      accessKeySecret:\n        description: Access key secret // 访问密钥秘密\n        type: string\n      accessUrlPrefix:\n        description: Access URL prefix // 访问地址前缀\n        type: string\n      accountId:\n        description: Account ID // 账户 ID\n        type: string\n      bucketName:\n        description: Bucket name // 存储桶名称\n        type: string\n      createdAt:\n        description: Created at // 创建时间\n        type: string\n      customPath:\n        description: Custom path // 自定义路径\n        type: string\n      endpoint:\n        description: Endpoint // 访问端点\n        type: string\n      id:\n        description: ID // ID\n        type: integer\n      isEnabled:\n        description: Is enabled // 是否启用\n        type: boolean\n      password:\n        description: Password // 密码\n        type: string\n      region:\n        description: Region // 区域\n        type: string\n      type:\n        description: Storage type // 存储类型\n        type: string\n      updatedAt:\n        description: Updated at // 更新时间\n        type: string\n      user:\n        description: Username // 用户名\n        type: string\n    type: object\n  dto.StoragePostRequest:\n    properties:\n      accessKeyId:\n        description: Access key ID // 访问密钥ID\n        example: \"\"\n        type: string\n      accessKeySecret:\n        description: Access key secret // 访问密钥秘密\n        example: \"\"\n        type: string\n      accessUrlPrefix:\n        description: Access URL prefix // 访问地址前缀\n        example: https://cdn.com\n        maxLength: 100\n        minLength: 2\n        type: string\n      accountId:\n        description: Account ID (R2) // 账户ID r2\n        example: \"123456789\"\n        type: string\n      bucketName:\n        description: Bucket name // 存储桶名称\n        example: my-bucket\n        type: string\n      customPath:\n        description: Custom path // 自定义路径\n        example: /backups\n        type: string\n      endpoint:\n        description: Endpoint (OSS) // 端点 oss\n        example: oss-cn-hangzhou.aliyuncs.com\n        type: string\n      id:\n        description: ID // ID\n        example: 1\n        type: integer\n      isEnabled:\n        description: Is enabled // 是否启用\n        example: 1\n        type: integer\n      password:\n        description: Password // 密码\n        example: secret_password\n        type: string\n      region:\n        description: Region (S3) // 区域 s3\n        example: us-east-1\n        type: string\n      type:\n        description: Storage type // 类型\n        example: local-fs\n        minLength: 1\n        type: string\n      user:\n        description: Username // 访问用户名\n        example: admin\n        type: string\n    required:\n    - accessUrlPrefix\n    - type\n    type: object\n  dto.SyncLogDTO:\n    properties:\n      action:\n        description: Action type // 操作类型\n        type: string\n      changedFields:\n        description: Changed fields // 变更字段\n        type: string\n      clientName:\n        description: Client name // 客户端名称\n        type: string\n      clientType:\n        description: Client type // 客户端类型\n        type: string\n      clientVersion:\n        description: Client version // 客户端版本\n        type: string\n      createdAt:\n        description: Log creation time // 创建时间\n        type: string\n      message:\n        description: Additional message // 附加消息\n        type: string\n      path:\n        description: Resource path // 资源路径\n        type: string\n      pathHash:\n        description: Resource path hash // 路径哈希\n        type: string\n      size:\n        description: Size in bytes // 大小（字节）\n        type: integer\n      status:\n        description: 'Status: 1 success, 2 failed // 状态'\n        type: integer\n      type:\n        description: Resource type // 资源类型\n        type: string\n      vaultId:\n        description: Vault ID // 笔记本 ID\n        type: integer\n    type: object\n  dto.UserChangePasswordRequest:\n    properties:\n      confirmPassword:\n        description: Confirm password // 校验密码\n        example: new_password123\n        type: string\n      oldPassword:\n        description: Old password // 旧密码\n        example: old_password123\n        type: string\n      password:\n        description: New password // 新密码\n        example: new_password123\n        type: string\n    required:\n    - confirmPassword\n    - oldPassword\n    - password\n    type: object\n  dto.UserCreateRequest:\n    properties:\n      confirmPassword:\n        description: Confirm password // 校验密码\n        example: password123\n        type: string\n      email:\n        description: User email // 用户邮件\n        example: user@example.com\n        type: string\n      password:\n        description: User password // 用户密码\n        example: password123\n        type: string\n      username:\n        description: User name // 用户名\n        example: username123\n        type: string\n    required:\n    - confirmPassword\n    - email\n    - password\n    - username\n    type: object\n  dto.UserDTO:\n    properties:\n      avatar:\n        description: Avatar URL or handle // 头像路径或名称\n        type: string\n      createdAt:\n        description: Account created time // 账号创建时间\n        type: string\n      email:\n        description: Email address // 邮件地址\n        type: string\n      token:\n        description: Authentication Token // 认证 Token\n        type: string\n      uid:\n        description: User ID (primary key) // 用户唯一标识（主键）\n        type: integer\n      updatedAt:\n        description: Last updated time // 最后更新时间\n        type: string\n      username:\n        description: Username // 用户名\n        type: string\n    type: object\n  dto.UserLoginRequest:\n    properties:\n      credentials:\n        description: Username or Email // 登录凭证（用户名或邮件）\n        example: user@example.com\n        type: string\n      password:\n        description: Password // 密码\n        example: password123\n        type: string\n    required:\n    - credentials\n    - password\n    type: object\n  dto.VaultDTO:\n    properties:\n      createdAt:\n        description: Creation time // 创建时间\n        type: string\n      fileCount:\n        description: Number of files // 文件数量\n        type: integer\n      fileSize:\n        description: Size of files // 文件大小\n        type: integer\n      id:\n        description: Vault ID // 保险库 ID\n        type: integer\n      noteCount:\n        description: Number of notes // 笔记数量\n        type: integer\n      noteSize:\n        description: Size of notes // 笔记大小\n        type: integer\n      size:\n        description: Total size // 总大小\n        type: integer\n      updatedAt:\n        description: Updated time // 更新时间\n        type: string\n      vault:\n        description: Vault name // 保险库名称\n        type: string\n    type: object\n  dto.VaultPostRequest:\n    properties:\n      id:\n        description: Vault ID (optional for update) // 保险库 ID（可选，用于更新）\n        example: 1\n        type: integer\n      vault:\n        description: Vault name // 保险库名称\n        example: MyVault\n        type: string\n    required:\n    - vault\n    type: object\n  dto.VersionDTO:\n    properties:\n      buildTime:\n        description: Build time // 构建时间\n        type: string\n      gitTag:\n        description: Git tag // Git 标签\n        type: string\n      pluginVersionNewChangelog:\n        description: New plugin version changelog link // 插件新版本更新日志链接\n        type: string\n      pluginVersionNewChangelogContent:\n        description: New plugin version changelog content // 插件新版本更新日志内容\n        type: string\n      pluginVersionNewLink:\n        description: New plugin version link // 插件新版本链接\n        type: string\n      pluginVersionNewName:\n        description: New plugin version name // 插件新版本名称\n        type: string\n      version:\n        description: Current version // 当前版本\n        type: string\n      versionIsNew:\n        description: Is there a new version // 是否有新版本\n        type: boolean\n      versionNewChangelog:\n        description: New version changelog link // 新版本更新日志链接\n        type: string\n      versionNewChangelogContent:\n        description: New version changelog content // 新版本更新日志内容\n        type: string\n      versionNewLink:\n        description: New version download link // 新版本下载链接\n        type: string\n      versionNewName:\n        description: New version name // 新版本名称\n        type: string\n    type: object\nhost: localhost:9000\ninfo:\n  contact:\n    email: haierkeys@gmail.com\n    name: Haierkeys\n    url: https://github.com/haierkeys\n  description: This is the Fast Note Sync Service HTTP API.\n  license:\n    name: Apache 2.0\n    url: http://www.apache.org/licenses/LICENSE-2.0.html\n  title: Fast Note Sync Service HTTP API\n  version: \"1.0\"\npaths:\n  /api/admin/check:\n    get:\n      description: Check if the current logged-in user has system admin privileges\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.AdminCheckResponse'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Check admin permission\n      tags:\n      - Config\n  /api/admin/cloudflared_tunnel_download:\n    get:\n      description: Trigger the download of cloudflared binary for the current platform\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Download cloudflared binary\n      tags:\n      - System\n  /api/admin/config:\n    get:\n      description: Get full system configuration information, requires admin privileges\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.AdminConfig'\n              type: object\n        \"403\":\n          description: Insufficient privileges\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Get full admin config\n      tags:\n      - Config\n    post:\n      consumes:\n      - application/json\n      description: Modify full system configuration information, requires admin privileges\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Config Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.AdminConfig'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.AdminConfig'\n              type: object\n        \"403\":\n          description: Insufficient privileges\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Update admin config\n      tags:\n      - Config\n  /api/admin/config/cloudflare:\n    get:\n      description: Get Cloudflare tunnel configuration, requires admin privileges\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.AdminCloudflareConfig'\n              type: object\n        \"403\":\n          description: Insufficient privileges\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Get Cloudflare config\n      tags:\n      - Config\n    post:\n      consumes:\n      - application/json\n      description: Modify Cloudflare tunnel configuration, requires admin privileges\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Config Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.AdminCloudflareConfig'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.AdminCloudflareConfig'\n              type: object\n        \"403\":\n          description: Insufficient privileges\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Update Cloudflare config\n      tags:\n      - Config\n  /api/admin/config/ngrok:\n    get:\n      description: Get Ngrok tunnel configuration, requires admin privileges\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.AdminNgrokConfig'\n              type: object\n        \"403\":\n          description: Insufficient privileges\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Get Ngrok config\n      tags:\n      - Config\n    post:\n      consumes:\n      - application/json\n      description: Modify Ngrok tunnel configuration, requires admin privileges\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Config Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.AdminNgrokConfig'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.AdminNgrokConfig'\n              type: object\n        \"403\":\n          description: Insufficient privileges\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Update Ngrok config\n      tags:\n      - Config\n  /api/admin/config/user_database:\n    get:\n      description: Get user database configuration information, requires admin privileges\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.AdminUserDatabaseConfig'\n              type: object\n        \"403\":\n          description: Insufficient privileges\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Get user database config\n      tags:\n      - Config\n    post:\n      consumes:\n      - application/json\n      description: Modify user database configuration information, requires admin\n        privileges\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Config Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.AdminUserDatabaseConfig'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.AdminUserDatabaseConfig'\n              type: object\n        \"403\":\n          description: Insufficient privileges\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Update user database config\n      tags:\n      - Config\n  /api/admin/config/user_database/test:\n    post:\n      consumes:\n      - application/json\n      description: Test if the provided database configuration can connect successfully,\n        requires admin privileges\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Config Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.AdminUserDatabaseConfig'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"400\":\n          description: Connection failed\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Test user database connection\n      tags:\n      - Config\n  /api/admin/gc:\n    get:\n      description: Manually run Go runtime GC and release memory to OS, requires admin\n        privileges\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"403\":\n          description: Insufficient privileges\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Trigger manual GC\n      tags:\n      - System\n  /api/admin/restart:\n    get:\n      description: Gracefully restart the server\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Trigger server restart\n      tags:\n      - System\n  /api/admin/system/info:\n    get:\n      description: Get server runtime, CPU, memory, host and process info, requires\n        admin privileges\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.AdminSystemInfo'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Get system stats\n      tags:\n      - System\n  /api/admin/upgrade:\n    get:\n      description: Download latest version and restart server\n      parameters:\n      - description: Version to upgrade (e.g. 2.0.10 or latest)\n        in: query\n        name: version\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Trigger server upgrade\n      tags:\n      - System\n  /api/admin/ws_clients:\n    get:\n      description: Get a list of all current WebSocket connections, requires admin\n        privileges\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  items:\n                    $ref: '#/definitions/app.WSClientInfo'\n                  type: array\n              type: object\n        \"403\":\n          description: Insufficient privileges\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Get connected WebSocket clients\n      tags:\n      - System\n  /api/backup/config:\n    delete:\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: ID // ID\n        example: 1\n        in: query\n        name: id\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"400\":\n          description: Invalid Params\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"401\":\n          description: Token Required\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"500\":\n          description: Internal Server Error\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Delete backup configuration\n      tags:\n      - Backup\n    post:\n      consumes:\n      - application/json\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Backup Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.BackupConfigRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.BackupConfigDTO'\n              type: object\n        \"400\":\n          description: Invalid Params\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"401\":\n          description: Token Required\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"500\":\n          description: Internal Server Error\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Update backup configuration\n      tags:\n      - Backup\n  /api/backup/configs:\n    get:\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  items:\n                    $ref: '#/definitions/dto.BackupConfigDTO'\n                  type: array\n              type: object\n        \"401\":\n          description: Token Required\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"500\":\n          description: Internal Server Error\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Get backup configurations\n      tags:\n      - Backup\n  /api/backup/execute:\n    post:\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Backup Execute Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.BackupExecuteRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"400\":\n          description: Invalid Params\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"401\":\n          description: Token Required\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"500\":\n          description: Internal Server Error\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Trigger a backup manually\n      tags:\n      - Backup\n  /api/backup/historys:\n    get:\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Config ID // 配置 ID\n        example: 1\n        in: query\n        name: configId\n        required: true\n        type: integer\n      - description: Page number // 页码\n        example: 1\n        in: query\n        name: page\n        type: integer\n      - description: Page size // 每页大小\n        example: 10\n        in: query\n        name: pageSize\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  allOf:\n                  - $ref: '#/definitions/app.ListRes'\n                  - properties:\n                      list:\n                        items:\n                          $ref: '#/definitions/dto.BackupHistoryDTO'\n                        type: array\n                    type: object\n              type: object\n        \"400\":\n          description: Invalid Params\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"401\":\n          description: Token Required\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"500\":\n          description: Internal Server Error\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Get backup history list\n      tags:\n      - Backup\n  /api/file:\n    delete:\n      description: Permanently delete a specific attachment record and its physical\n        file\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: File path // 文件路径\n        example: Image.png\n        in: query\n        name: path\n        required: true\n        type: string\n      - description: Path hash // 路径哈希\n        example: fhash123\n        in: query\n        name: pathHash\n        required: true\n        type: string\n      - description: Vault name // 保险库名称\n        example: MyVault\n        in: query\n        name: vault\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.FileDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Delete attachment\n      tags:\n      - File\n    get:\n      description: Get raw binary data of an attachment by path, supports strong cache\n        control\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Is in recycle bin // 是否在回收站\n        example: false\n        in: query\n        name: isRecycle\n        type: boolean\n      - description: File path // 文件路径\n        example: Image.png\n        in: query\n        name: path\n        required: true\n        type: string\n      - description: Path hash // 路径哈希\n        example: fhash123\n        in: query\n        name: pathHash\n        type: string\n      - description: Vault name // 保险库名称\n        example: MyVault\n        in: query\n        name: vault\n        required: true\n        type: string\n      produces:\n      - application/octet-stream\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            type: file\n      security:\n      - UserAuthToken: []\n      summary: Get attachment content\n      tags:\n      - File\n  /api/file/info:\n    get:\n      description: Get attachment metadata (FileDTO) by path\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Is in recycle bin // 是否在回收站\n        example: false\n        in: query\n        name: isRecycle\n        type: boolean\n      - description: File path // 文件路径\n        example: Image.png\n        in: query\n        name: path\n        required: true\n        type: string\n      - description: Path hash // 路径哈希\n        example: fhash123\n        in: query\n        name: pathHash\n        type: string\n      - description: Vault name // 保险库名称\n        example: MyVault\n        in: query\n        name: vault\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.FileDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Get attachment info\n      tags:\n      - File\n  /api/file/recycle-clear:\n    delete:\n      consumes:\n      - application/json\n      description: Permanently clear selected files from recycle bin\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Clear Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.FileRecycleClearRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Clear recycle bin\n      tags:\n      - File\n  /api/file/rename:\n    post:\n      consumes:\n      - application/json\n      description: Rename an attachment to a new path\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Rename Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.FileRenameRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.FileDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Rename attachment\n      tags:\n      - File\n  /api/file/restore:\n    put:\n      description: Restore deleted attachment from trash\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Restore Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.FileRestoreRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.FileDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Restore attachment\n      tags:\n      - File\n  /api/files:\n    get:\n      description: Get attachment list for current user with pagination, search, filter,\n        and sort support\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Is in recycle bin // 是否在回收站\n        example: false\n        in: query\n        name: isRecycle\n        type: boolean\n      - description: Search keyword // 搜索关键词\n        example: vacation\n        in: query\n        name: keyword\n        type: string\n      - description: Sort by field // 排序字段\n        example: mtime\n        in: query\n        name: sortBy\n        type: string\n      - description: Sort order // 排序顺序\n        example: desc\n        in: query\n        name: sortOrder\n        type: string\n      - description: Vault name // 保险库名称\n        example: MyVault\n        in: query\n        name: vault\n        required: true\n        type: string\n      - description: Page number // 页码\n        in: query\n        name: page\n        type: integer\n      - description: Page size // 每页数量\n        in: query\n        name: pageSize\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  allOf:\n                  - $ref: '#/definitions/app.ListRes'\n                  - properties:\n                      list:\n                        items:\n                          $ref: '#/definitions/dto.FileDTO'\n                        type: array\n                    type: object\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Get file list\n      tags:\n      - File\n  /api/folder:\n    delete:\n      consumes:\n      - application/json\n      description: Soft delete a folder by path or pathHash\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Delete Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.FolderDeleteRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Delete folder\n      tags:\n      - Folder\n    get:\n      description: Get folder info for current user by path or pathHash\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Folder path // 文件夹路径\n        example: Projects/Work\n        in: query\n        name: path\n        type: string\n      - description: Path hash // 路径哈希\n        example: fhash123\n        in: query\n        name: pathHash\n        type: string\n      - description: Vault name // 保险库名称\n        example: MyVault\n        in: query\n        name: vault\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.FolderDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Get folder info\n      tags:\n      - Folder\n    post:\n      consumes:\n      - application/json\n      description: Create a new folder or restore a deleted one by path\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Create Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.FolderCreateRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.FolderDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Create folder\n      tags:\n      - Folder\n  /api/folder/files:\n    get:\n      description: List non-deleted files in a specific folder with pagination and\n        sorting\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Folder path // 文件夹路径\n        example: Projects\n        in: query\n        name: path\n        type: string\n      - description: Path hash // 路径哈希\n        example: fhash123\n        in: query\n        name: pathHash\n        type: string\n      - description: Sort by field // 排序字段\n        example: mtime\n        in: query\n        name: sortBy\n        type: string\n      - description: Sort order // 排序顺序\n        example: desc\n        in: query\n        name: sortOrder\n        type: string\n      - description: Vault name // 保险库名称\n        example: MyVault\n        in: query\n        name: vault\n        required: true\n        type: string\n      - description: Page number // 页码\n        in: query\n        name: page\n        type: integer\n      - description: Page size // 每页数量\n        in: query\n        name: pageSize\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  allOf:\n                  - $ref: '#/definitions/app.ListRes'\n                  - properties:\n                      list:\n                        items:\n                          $ref: '#/definitions/dto.FileDTO'\n                        type: array\n                    type: object\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: List files in folder\n      tags:\n      - Folder\n  /api/folder/notes:\n    get:\n      description: List non-deleted notes in a specific folder with pagination and\n        sorting\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Folder path // 文件夹路径\n        example: Projects\n        in: query\n        name: path\n        type: string\n      - description: Path hash // 路径哈希\n        example: fhash123\n        in: query\n        name: pathHash\n        type: string\n      - description: Sort by field // 排序字段\n        example: mtime\n        in: query\n        name: sortBy\n        type: string\n      - description: Sort order // 排序顺序\n        example: desc\n        in: query\n        name: sortOrder\n        type: string\n      - description: Vault name // 保险库名称\n        example: MyVault\n        in: query\n        name: vault\n        required: true\n        type: string\n      - description: Page number // 页码\n        in: query\n        name: page\n        type: integer\n      - description: Page size // 每页数量\n        in: query\n        name: pageSize\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  allOf:\n                  - $ref: '#/definitions/app.ListRes'\n                  - properties:\n                      list:\n                        items:\n                          $ref: '#/definitions/dto.NoteDTO'\n                        type: array\n                    type: object\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: List notes in folder\n      tags:\n      - Folder\n  /api/folder/tree:\n    get:\n      description: Get the complete folder tree structure for a vault\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Tree depth // 树深度\n        example: 3\n        in: query\n        name: depth\n        type: integer\n      - description: Vault name // 保险库名称\n        example: MyVault\n        in: query\n        name: vault\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.FolderTreeResponse'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Get folder tree\n      tags:\n      - Folder\n  /api/folders:\n    get:\n      description: Get folder list for current user by parent path or pathHash\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Folder path // 文件夹路径\n        example: Projects\n        in: query\n        name: path\n        type: string\n      - description: Path hash // 路径哈希\n        example: fhash123\n        in: query\n        name: pathHash\n        type: string\n      - description: Vault name // 保险库名称\n        example: MyVault\n        in: query\n        name: vault\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  items:\n                    $ref: '#/definitions/dto.FolderDTO'\n                  type: array\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Get folder list\n      tags:\n      - Folder\n  /api/git-sync/config:\n    delete:\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Git Sync ID\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.GitSyncDeleteRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"400\":\n          description: Invalid Params\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"401\":\n          description: Token Required\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"500\":\n          description: Internal Server Error\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Delete git sync configuration\n      tags:\n      - GitSync\n    post:\n      consumes:\n      - application/json\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Git Sync Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.GitSyncConfigRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.GitSyncConfigDTO'\n              type: object\n        \"400\":\n          description: Invalid Params\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"401\":\n          description: Token Required\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"500\":\n          description: Internal Server Error\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Update git sync configuration\n      tags:\n      - GitSync\n  /api/git-sync/config/clean:\n    delete:\n      consumes:\n      - application/json\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Clean Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.GitSyncCleanRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"400\":\n          description: Invalid Params\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"401\":\n          description: Token Required\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"500\":\n          description: Internal Server Error\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Clean local git workspace\n      tags:\n      - GitSync\n  /api/git-sync/config/execute:\n    post:\n      consumes:\n      - application/json\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Execute Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.GitSyncExecuteRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"400\":\n          description: Invalid Params\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"401\":\n          description: Token Required\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"500\":\n          description: Internal Server Error\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Trigger a manual git sync\n      tags:\n      - GitSync\n  /api/git-sync/configs:\n    get:\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  items:\n                    $ref: '#/definitions/dto.GitSyncConfigDTO'\n                  type: array\n              type: object\n        \"401\":\n          description: Token Required\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"500\":\n          description: Internal Server Error\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Get git sync configurations\n      tags:\n      - GitSync\n  /api/git-sync/histories:\n    get:\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - in: query\n        name: configId\n        type: integer\n      - in: query\n        name: page\n        type: integer\n      - in: query\n        name: pageSize\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  allOf:\n                  - $ref: '#/definitions/app.ListRes'\n                  - properties:\n                      list:\n                        items:\n                          $ref: '#/definitions/dto.GitSyncHistoryDTO'\n                        type: array\n                    type: object\n              type: object\n        \"401\":\n          description: Token Required\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"500\":\n          description: Internal Server Error\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Get git sync histories\n      tags:\n      - GitSync\n  /api/git-sync/validate:\n    post:\n      consumes:\n      - application/json\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Validation Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.GitSyncValidateRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"400\":\n          description: Invalid Params\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"401\":\n          description: Token Required\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"500\":\n          description: Internal Server Error\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Validate git sync parameters\n      tags:\n      - GitSync\n  /api/health:\n    get:\n      description: Check service health status, including database connection\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: OK\n          schema:\n            $ref: '#/definitions/api_router.HealthResponse'\n      summary: Health check\n      tags:\n      - System\n  /api/note:\n    delete:\n      description: Move note to trash\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Note path // 笔记路径\n        example: ReadMe.md\n        in: query\n        name: path\n        required: true\n        type: string\n      - description: Path hash // 路径哈希\n        example: hash123\n        in: query\n        name: pathHash\n        type: string\n      - description: Vault name // 保险库名称\n        example: MyVault\n        in: query\n        name: vault\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.NoteDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Delete note\n      tags:\n      - Note\n    get:\n      description: Get specific note content and metadata by path or path hash\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Is in recycle bin // 是否在回收站\n        example: false\n        in: query\n        name: isRecycle\n        type: boolean\n      - description: Note path // 笔记路径\n        example: ReadMe.md\n        in: query\n        name: path\n        required: true\n        type: string\n      - description: Path hash // 路径哈希\n        example: hash123\n        in: query\n        name: pathHash\n        type: string\n      - description: Vault name // 保险库名称\n        example: MyVault\n        in: query\n        name: vault\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.NoteWithFileLinksResponse'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Get note details\n      tags:\n      - Note\n    post:\n      consumes:\n      - application/json\n      description: Handle note creation, modification, or renaming (identified by\n        path change)\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Note Content\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.NoteModifyOrCreateRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.NoteDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Create or update note\n      tags:\n      - Note\n  /api/note/append:\n    post:\n      consumes:\n      - application/json\n      description: Append content to the end of a note\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Append Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.NoteAppendRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.NoteDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Append content to note\n      tags:\n      - Note\n  /api/note/backlinks:\n    get:\n      description: Get all other notes that link to the specified note\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Note path // 笔记路径\n        example: ReadMe.md\n        in: query\n        name: path\n        required: true\n        type: string\n      - description: Path hash // 路径哈希\n        example: hash123\n        in: query\n        name: pathHash\n        type: string\n      - description: Vault name // 保险库名称\n        example: MyVault\n        in: query\n        name: vault\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  items:\n                    $ref: '#/definitions/dto.NoteLinkItem'\n                  type: array\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Get backlinks\n      tags:\n      - Note\n  /api/note/frontmatter:\n    patch:\n      consumes:\n      - application/json\n      description: Update or delete note frontmatter fields\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Frontmatter Modification Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.NotePatchFrontmatterRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.NoteDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Modify note frontmatter\n      tags:\n      - Note\n  /api/note/histories:\n    get:\n      description: Get all history records for a specific note with pagination\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Is in recycle bin // 是否在回收站\n        example: false\n        in: query\n        name: isRecycle\n        type: boolean\n      - description: Note path // 笔记路径\n        example: ReadMe.md\n        in: query\n        name: path\n        required: true\n        type: string\n      - description: Path hash // 路径哈希\n        example: hash123\n        in: query\n        name: pathHash\n        type: string\n      - description: Vault name // 保险库名称\n        example: MyVault\n        in: query\n        name: vault\n        required: true\n        type: string\n      - description: Page number // 页码\n        in: query\n        name: page\n        type: integer\n      - description: Page size // 每页数量\n        in: query\n        name: pageSize\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  allOf:\n                  - $ref: '#/definitions/app.ListRes'\n                  - properties:\n                      list:\n                        items:\n                          $ref: '#/definitions/dto.NoteHistoryDTO'\n                        type: array\n                    type: object\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Get note history list\n      tags:\n      - Note History\n  /api/note/history:\n    get:\n      description: Get specific note history content by history record ID\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: History Record ID\n        format: int64\n        in: query\n        name: id\n        required: true\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.NoteHistoryDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Get note history details\n      tags:\n      - Note History\n  /api/note/history/restore:\n    put:\n      consumes:\n      - application/json\n      description: Restore note content to a specific history version\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Restore Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.NoteHistoryRestoreRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.NoteDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Restore note from history\n      tags:\n      - Note History\n  /api/note/move:\n    post:\n      consumes:\n      - application/json\n      description: Move a note to a new path\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Move Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.NoteMoveRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.NoteDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Move note\n      tags:\n      - Note\n  /api/note/outlinks:\n    get:\n      description: Get other notes that the specified note links to\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Note path // 笔记路径\n        example: ReadMe.md\n        in: query\n        name: path\n        required: true\n        type: string\n      - description: Path hash // 路径哈希\n        example: hash123\n        in: query\n        name: pathHash\n        type: string\n      - description: Vault name // 保险库名称\n        example: MyVault\n        in: query\n        name: vault\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  items:\n                    $ref: '#/definitions/dto.NoteLinkItem'\n                  type: array\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Get outgoing links\n      tags:\n      - Note\n  /api/note/prepend:\n    post:\n      consumes:\n      - application/json\n      description: Insert content at the beginning of a note (after frontmatter)\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Prepend Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.NotePrependRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.NoteDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Prepend content to note\n      tags:\n      - Note\n  /api/note/recycle-clear:\n    delete:\n      consumes:\n      - application/json\n      description: Permanently clear selected notes from recycle bin\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Clear Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.NoteRecycleClearRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Clear recycle bin\n      tags:\n      - Note\n  /api/note/rename:\n    post:\n      consumes:\n      - application/json\n      description: Rename a note to a new path\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Rename Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.NoteRenameRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.NoteDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Rename note\n      tags:\n      - Note\n  /api/note/replace:\n    post:\n      consumes:\n      - application/json\n      description: Perform find and replace operation in a note, supporting regular\n        expressions\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Find and Replace Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.NoteReplaceRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.NoteReplaceResponse'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Find and replace in note\n      tags:\n      - Note\n  /api/note/restore:\n    put:\n      description: Restore deleted note from trash\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Restore Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.NoteRestoreRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.NoteDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Restore note\n      tags:\n      - Note\n  /api/notes:\n    get:\n      description: Get note list for current user with pagination\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Is in recycle bin // 是否在回收站\n        example: false\n        in: query\n        name: isRecycle\n        type: boolean\n      - description: Search keyword // 搜索关键词\n        example: todo\n        in: query\n        name: keyword\n        type: string\n      - description: Comma-separated exact path list for share filter // 逗号分隔的精确路径列表，用于分享筛选\n        example: note1.md,note2.md\n        in: query\n        name: paths\n        type: string\n      - description: Whether to search content // 是否搜索内容\n        example: true\n        in: query\n        name: searchContent\n        type: boolean\n      - description: Search mode (path, content) // 搜索模式（路径、内容）\n        example: content\n        in: query\n        name: searchMode\n        type: string\n      - description: Sort by field // 排序字段\n        example: mtime\n        in: query\n        name: sortBy\n        type: string\n      - description: Sort order // 排序顺序\n        example: desc\n        in: query\n        name: sortOrder\n        type: string\n      - description: Vault name // 保险库名称\n        example: MyVault\n        in: query\n        name: vault\n        required: true\n        type: string\n      - description: Page number // 页码\n        in: query\n        name: page\n        type: integer\n      - description: Page size // 每页数量\n        in: query\n        name: pageSize\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  allOf:\n                  - $ref: '#/definitions/app.ListRes'\n                  - properties:\n                      list:\n                        items:\n                          $ref: '#/definitions/dto.NoteNoContentDTO'\n                        type: array\n                    type: object\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Get note list\n      tags:\n      - Note\n  /api/notes/share-paths:\n    get:\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Vault name\n        in: query\n        name: vault\n        required: true\n        type: string\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  items:\n                    type: string\n                  type: array\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Get active shared note paths\n      tags:\n      - Share\n  /api/setting:\n    delete:\n      consumes:\n      - application/json\n      description: Soft delete a setting by path or pathHash\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Delete Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.SettingDeleteRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Delete setting\n      tags:\n      - Setting\n    get:\n      description: Get setting info for current user by path or pathHash\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Setting path // 配置路径\n        example: User/Theme\n        in: query\n        name: path\n        type: string\n      - description: Path hash // 路径哈希\n        example: hash123\n        in: query\n        name: pathHash\n        type: string\n      - description: Vault name // 保险库名称\n        example: MyVault\n        in: query\n        name: vault\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.SettingDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Get setting info\n      tags:\n      - Setting\n    post:\n      consumes:\n      - application/json\n      description: Create a new setting or update an existing one\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Create/Update Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.SettingModifyOrCreateRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.SettingDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Create or update setting\n      tags:\n      - Setting\n  /api/setting/rename:\n    post:\n      consumes:\n      - application/json\n      description: Rename a setting and update its path and pathHash\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Rename Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.SettingRenameRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.SettingDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Rename setting\n      tags:\n      - Setting\n  /api/settings:\n    get:\n      description: Get setting list for current user with pagination and keyword filtering\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Keyword // 关键词\n        example: User/\n        in: query\n        name: keyword\n        type: string\n      - description: Vault name // 保险库名称\n        example: MyVault\n        in: query\n        name: vault\n        required: true\n        type: string\n      - description: Page number // 页码\n        in: query\n        name: page\n        type: integer\n      - description: Page size // 每页数量\n        in: query\n        name: pageSize\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  allOf:\n                  - $ref: '#/definitions/app.ListRes'\n                  - properties:\n                      list:\n                        items:\n                          $ref: '#/definitions/dto.SettingDTO'\n                        type: array\n                    type: object\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Get setting list\n      tags:\n      - Setting\n  /api/share:\n    delete:\n      consumes:\n      - application/json\n      description: Cancel a share by ID or path parameters\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Cancel Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.ShareCancelRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Cancel share\n      tags:\n      - Share\n    get:\n      description: Get share token and info by vault and path\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Resource path // 资源路径\n        example: ReadMe.md\n        in: query\n        name: path\n        required: true\n        type: string\n      - description: Resource path Hash // 资源路径哈希\n        example: hash123\n        in: query\n        name: pathHash\n        required: true\n        type: string\n      - description: Vault name // 保险库名称\n        example: defaultVault\n        in: query\n        name: vault\n        required: true\n        type: string\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.ShareCreateResponse'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Query share by path\n      tags:\n      - Share\n    post:\n      consumes:\n      - application/json\n      description: Create a share token for a specific note or attachment, automatically\n        resolve attachment references and authorize\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Share Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.ShareCreateRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.ShareCreateResponse'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Create resource share\n      tags:\n      - Share\n  /api/share/file:\n    get:\n      description: Get raw binary data of a specific attachment via share token\n      parameters:\n      - description: Auth Token\n        in: header\n        name: Share-Token\n        required: true\n        type: string\n      - description: Resource ID // 资源 ID\n        example: 1\n        in: query\n        name: id\n        required: true\n        type: integer\n      - description: Share password // 分享密码\n        example: \"123456\"\n        in: query\n        name: password\n        type: string\n      produces:\n      - application/octet-stream\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            type: file\n      security:\n      - ShareAuthToken: []\n      summary: Get shared attachment content\n      tags:\n      - Share\n  /api/share/note:\n    get:\n      description: Get specific note content (restricted read-only access) via share\n        token\n      parameters:\n      - description: Auth Token\n        in: header\n        name: Share-Token\n        required: true\n        type: string\n      - description: Resource ID // 资源 ID\n        example: 1\n        in: query\n        name: id\n        required: true\n        type: integer\n      - description: Share password // 分享密码\n        example: \"123456\"\n        in: query\n        name: password\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.NoteDTO'\n              type: object\n      security:\n      - ShareAuthToken: []\n      summary: Get shared note details\n      tags:\n      - Share\n  /api/share/password:\n    post:\n      consumes:\n      - application/json\n      description: Set or update password for a share record\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Update Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.SharePasswordUpdateRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Update share password\n      tags:\n      - Share\n  /api/share/short_link:\n    post:\n      consumes:\n      - application/json\n      description: Call sink.cool API to generate a short link for a given share record\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Short Link Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.ShareShortLinkCreateRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  type: string\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Create short link for share\n      tags:\n      - Share\n  /api/shares:\n    get:\n      description: Get all active and inactive shares of the user, supports sorting\n        and pagination\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: 'Sort field: created_at, updated_at, expires_at (default: created_at)'\n        in: query\n        name: sort_by\n        type: string\n      - description: 'Sort direction: asc or desc (default: desc)'\n        in: query\n        name: sort_order\n        type: string\n      - description: Page number\n        in: query\n        name: page\n        type: integer\n      - description: Page size\n        in: query\n        name: pageSize\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  allOf:\n                  - $ref: '#/definitions/app.ListRes'\n                  - properties:\n                      list:\n                        items:\n                          $ref: '#/definitions/dto.ShareListItem'\n                        type: array\n                    type: object\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: List shares\n      tags:\n      - Share\n  /api/storage:\n    delete:\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Storage ID\n        format: int64\n        in: query\n        name: id\n        required: true\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Delete storage configuration\n      tags:\n      - Storage\n    get:\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  items:\n                    $ref: '#/definitions/dto.StorageDTO'\n                  type: array\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Get storage configuration list\n      tags:\n      - Storage\n    post:\n      consumes:\n      - application/json\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Storage Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.StoragePostRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.StorageDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Create or update storage configuration\n      tags:\n      - Storage\n  /api/storage/enabled_types:\n    get:\n      description: 'Get list of enabled storage types. Possible values: localfs, oss,\n        s3, r2, minio, webdav'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: 'Success. Data contains: localfs, oss, s3, r2, minio, webdav'\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  items:\n                    type: string\n                  type: array\n              type: object\n      summary: Get enabled storage types\n      tags:\n      - Storage\n  /api/storage/validate:\n    post:\n      consumes:\n      - application/json\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Storage Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.StoragePostRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"400\":\n          description: Invalid Params\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"401\":\n          description: Token Required\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"500\":\n          description: Internal Server Error\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Validate storage connection\n      tags:\n      - Storage\n  /api/support:\n    get:\n      description: Get support records for the specified language with pagination\n        and sorting\n      parameters:\n      - description: 'Language code (default: en)'\n        in: query\n        name: lang\n        type: string\n      - description: Sort by field (amount, time, name, item)\n        in: query\n        name: sortBy\n        type: string\n      - description: Sort order (asc, desc)\n        in: query\n        name: sortOrder\n        type: string\n      - description: Page number\n        in: query\n        name: page\n        type: integer\n      - description: Page size\n        in: query\n        name: pageSize\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/app.ListRes'\n              type: object\n      summary: Get support records\n      tags:\n      - System\n  /api/sync-logs:\n    get:\n      description: Get sync log list for current user with optional type/action filters\n        and pagination\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Action type // 操作类型\n        example: modify\n        in: query\n        name: action\n        type: string\n      - description: 'Resource type: note / file / setting / folder // 资源类型'\n        example: note\n        in: query\n        name: type\n        type: string\n      - description: Vault name (optional filter) // 保险库名称（可选过滤）\n        example: MyVault\n        in: query\n        name: vault\n        type: string\n      - description: Page number // 页码\n        in: query\n        name: page\n        type: integer\n      - description: Page size // 每页数量\n        in: query\n        name: pageSize\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  allOf:\n                  - $ref: '#/definitions/app.ListRes'\n                  - properties:\n                      list:\n                        items:\n                          $ref: '#/definitions/dto.SyncLogDTO'\n                        type: array\n                    type: object\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Get sync log list\n      tags:\n      - Sync Log\n  /api/user/change_password:\n    post:\n      consumes:\n      - application/json\n      description: |-\n        Handle password change request for current user, validate old password and update new password.\n        处理当前用户的修改密码请求，验证旧密码并更新新密码。\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Change Password Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.UserChangePasswordRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"400\":\n          description: Invalid Parameters / Old Password Incorrect\n          schema:\n            $ref: '#/definitions/app.Res'\n        \"401\":\n          description: Unauthorized\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Change user password\n      tags:\n      - User\n  /api/user/info:\n    get:\n      consumes:\n      - application/json\n      description: |-\n        Handle request to get current user info.\n        处理获取当前用户信息的请求。\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.UserDTO'\n              type: object\n        \"401\":\n          description: Unauthorized\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Get user info\n      tags:\n      - User\n  /api/user/login:\n    post:\n      consumes:\n      - application/json\n      description: |-\n        Handle user login HTTP request, validate parameters and return auth token.\n        处理用户登录 HTTP 请求，验证参数并返回认证 Token。\n      parameters:\n      - description: Login Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.UserLoginRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.UserDTO'\n              type: object\n        \"400\":\n          description: Invalid Parameters / Invalid Credentials\n          schema:\n            $ref: '#/definitions/app.Res'\n      summary: User login\n      tags:\n      - User\n  /api/user/register:\n    post:\n      consumes:\n      - application/json\n      description: |-\n        Handle user registration HTTP request, validate parameters and call UserService. Registration may be disabled in server settings.\n        处理用户注册 HTTP 请求，验证参数并调用 UserService。注册功能可能在服务器设置中被禁用。\n      parameters:\n      - description: Register Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.UserCreateRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.UserDTO'\n              type: object\n        \"400\":\n          description: Invalid Parameters / Registration Disabled / User Already Exists\n          schema:\n            $ref: '#/definitions/app.Res'\n      summary: User registration\n      tags:\n      - User\n  /api/vault:\n    delete:\n      description: Permanently delete a specific note vault and all associated notes\n        and attachments\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Vault ID // 保险库 ID\n        example: 1\n        in: query\n        minimum: 1\n        name: id\n        required: true\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            $ref: '#/definitions/app.Res'\n      security:\n      - UserAuthToken: []\n      summary: Delete vault\n      tags:\n      - Vault\n    get:\n      description: Get all note vaults for current user\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  items:\n                    $ref: '#/definitions/dto.VaultDTO'\n                  type: array\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Get vault list\n      tags:\n      - Vault\n    post:\n      consumes:\n      - application/json\n      description: Be used to create a new vault or update an existing vault configuration\n        based on the ID in the request parameters\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Vault Parameters\n        in: body\n        name: params\n        required: true\n        schema:\n          $ref: '#/definitions/dto.VaultPostRequest'\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.VaultDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Create or update vault\n      tags:\n      - Vault\n  /api/vault/get:\n    get:\n      description: Get specific vault configuration details by vault ID\n      parameters:\n      - description: Auth Token\n        in: header\n        name: token\n        required: true\n        type: string\n      - description: Vault ID\n        format: int64\n        in: query\n        name: id\n        required: true\n        type: integer\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.VaultDTO'\n              type: object\n      security:\n      - UserAuthToken: []\n      summary: Get vault details\n      tags:\n      - Vault\n  /api/version:\n    get:\n      description: Get current server software version, Git tag, and build time\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.VersionDTO'\n              type: object\n      summary: Get server version info\n      tags:\n      - System\n  /api/webgui/config:\n    get:\n      description: Get non-sensitive configuration required for frontend display,\n        such as font settings, registration status, etc.\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: Success\n          schema:\n            allOf:\n            - $ref: '#/definitions/app.Res'\n            - properties:\n                data:\n                  $ref: '#/definitions/dto.AdminWebGUIConfig'\n              type: object\n      summary: Get WebGUI basic config\n      tags:\n      - Config\nsecurityDefinitions:\n  ShareAuthToken:\n    in: header\n    name: Share-Token\n    type: apiKey\n  UserAuthToken:\n    in: header\n    name: token\n    type: apiKey\nswagger: \"2.0\"\n"
  },
  {
    "path": "docs/test_ws_debug.html",
    "content": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>FNSS Websocket Debug Platform</title>\n    <style>\n        :root {\n            --primary: #6366f1;\n            --primary-hover: #4f46e5;\n            --bg-dark: #0f172a;\n            --card-bg: rgba(30, 41, 59, 0.7);\n            --border: rgba(255, 255, 255, 0.1);\n            --text: #f8fafc;\n            --text-muted: #94a3b8;\n            --success: #10b981;\n            --error: #ef4444;\n            --warning: #f59e0b;\n        }\n\n        * {\n            box-sizing: border-box;\n            margin: 0;\n            padding: 0;\n            font-family: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", \"Liberation Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n            font-size: 14px;\n        }\n\n        body {\n            background-color: var(--bg-dark);\n            background-image:\n                radial-gradient(at 0% 0%, rgba(99, 102, 241, 0.15) 0px, transparent 50%),\n                radial-gradient(at 100% 100%, rgba(16, 185, 129, 0.1) 0px, transparent 50%);\n            color: var(--text);\n            min-height: 100vh;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            padding: 2rem;\n        }\n\n        h1 {\n            font-size: 2rem;\n            margin-bottom: 2rem;\n            background: linear-gradient(to right, #818cf8, #34d399);\n            -webkit-background-clip: text;\n            -webkit-text-fill-color: transparent;\n            font-weight: 800;\n        }\n\n        .container {\n            width: 100%;\n            max-width: 1400px;\n            display: grid;\n            grid-template-columns: 450px 1fr;\n            gap: 2rem;\n            align-items: start;\n        }\n\n        .left-column {\n            display: flex;\n            flex-direction: column;\n            gap: 2rem;\n        }\n\n        .card {\n            background: var(--card-bg);\n            backdrop-filter: blur(12px);\n            border: 1px solid var(--border);\n            border-radius: 1rem;\n            padding: 1.5rem;\n            box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3);\n        }\n\n        .tabs {\n            display: flex;\n            gap: 0.5rem;\n            margin-bottom: 1.5rem;\n            background: rgba(15, 23, 42, 0.4);\n            padding: 0.25rem;\n            border-radius: 0.75rem;\n            border: 1px solid var(--border);\n            flex-wrap: wrap;\n        }\n\n        .tab {\n            flex: 1;\n            padding: 0.6rem;\n            text-align: center;\n            border-radius: 0.5rem;\n            cursor: pointer;\n            font-size: 0.85rem;\n            font-weight: 600;\n            transition: all 0.2s;\n            color: var(--text-muted);\n            min-width: 80px;\n        }\n\n        .tab.active {\n            background: var(--primary);\n            color: white;\n            box-shadow: 0 4px 10px rgba(99, 102, 241, 0.3);\n        }\n\n        .card h2 {\n            margin-bottom: 1rem;\n            font-size: 1.05rem;\n            display: flex;\n            align-items: center;\n            gap: 0.5rem;\n            color: var(--text);\n        }\n\n        .form-group {\n            margin-bottom: 1rem;\n        }\n\n        label {\n            display: block;\n            margin-bottom: 0.3rem;\n            font-size: 0.75rem;\n            color: var(--text-muted);\n        }\n\n        input,\n        textarea,\n        select {\n            width: 100%;\n            background: rgba(15, 23, 42, 0.5);\n            border: 1px solid var(--border);\n            border-radius: 0.5rem;\n            padding: 0.6rem;\n            color: var(--text);\n            outline: none;\n            transition: border-color 0.2s;\n            font-size: 0.85rem;\n        }\n\n        input:focus,\n        textarea:focus {\n            border-color: var(--primary);\n        }\n\n        .btn-group {\n            display: grid;\n            grid-template-columns: 1fr 1fr;\n            gap: 0.75rem;\n            margin-top: 1rem;\n        }\n\n        button {\n            background: var(--primary);\n            color: white;\n            border: none;\n            border-radius: 0.5rem;\n            padding: 0.75rem 1rem;\n            font-weight: 600;\n            cursor: pointer;\n            transition: all 0.2s;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            gap: 0.4rem;\n            font-size: 0.9rem;\n        }\n\n        button:hover {\n            background: var(--primary-hover);\n            transform: translateY(-1px);\n        }\n\n        button:disabled {\n            opacity: 0.5;\n            cursor: not-allowed;\n        }\n\n        button.outline {\n            background: transparent;\n            border: 1px solid var(--primary);\n            color: var(--primary);\n        }\n\n        button.success {\n            background: var(--success);\n        }\n\n        button.danger {\n            background: var(--error);\n        }\n\n        button.warning {\n            background: var(--warning);\n        }\n\n        #log {\n            height: calc(100vh - 280px);\n            min-height: 1000px;\n            overflow-y: auto;\n            overflow-x: hidden;\n            font-family: 'Fira Code', 'Cascadia Code', monospace;\n            font-size: 0.8rem;\n            display: flex;\n            flex-direction: column;\n            gap: 0.5rem;\n        }\n\n        #log pre {\n            white-space: pre-wrap;\n            word-break: break-all;\n            margin-top: 0.5rem;\n            background: rgba(0, 0, 0, 0.2);\n            padding: 0.5rem;\n            border-radius: 0.25rem;\n            font-size: 0.75rem;\n            display: none;\n            /* Default hidden */\n            cursor: pointer;\n        }\n\n        #log .log-entry.expanded pre {\n            display: block;\n            /* Expanded state */\n        }\n\n        .log-entry {\n            padding: 0.5rem;\n            border-radius: 0.25rem;\n            border-left: 3px solid transparent;\n            cursor: pointer;\n            transition: background 0.2s;\n        }\n\n        .log-entry:hover {\n            background: rgba(255, 255, 255, 0.08) !important;\n        }\n\n        .log-entry.send {\n            border-left-color: var(--primary);\n            background: rgba(99, 102, 241, 0.05);\n        }\n\n        .log-entry.recv {\n            border-left-color: var(--success);\n            background: rgba(16, 185, 129, 0.05);\n        }\n\n        .log-entry.err {\n            border-left-color: var(--error);\n            background: rgba(239, 68, 68, 0.05);\n        }\n\n        .log-entry.info {\n            border-left-color: var(--text-muted);\n            background: rgba(255, 255, 255, 0.05);\n        }\n\n        .time {\n            color: var(--text-muted);\n            font-size: 0.7rem;\n            margin-right: 0.5rem;\n        }\n\n        .tag {\n            font-weight: bold;\n            margin-right: 0.5rem;\n        }\n\n        .send .tag {\n            color: var(--primary);\n        }\n\n        .recv .tag {\n            color: var(--success);\n        }\n\n        .err .tag {\n            color: var(--error);\n        }\n\n        .tag-arrow {\n            display: inline-block;\n            margin-right: 0.25rem;\n            font-weight: bold;\n        }\n\n        .status-badge {\n            display: inline-flex;\n            align-items: center;\n            padding: 0.25rem 0.75rem;\n            border-radius: 9999px;\n            font-size: 0.75rem;\n            font-weight: 600;\n        }\n\n        .status-connected {\n            background: rgba(16, 185, 129, 0.1);\n            color: var(--success);\n        }\n\n        .status-disconnected {\n            background: rgba(239, 68, 68, 0.1);\n            color: var(--error);\n        }\n\n        .progress-container {\n            margin-top: 1rem;\n            background: rgba(0, 0, 0, 0.3);\n            border-radius: 999px;\n            height: 8px;\n            overflow: hidden;\n            display: none;\n        }\n\n        #progressBar {\n            background: linear-gradient(90deg, var(--primary), var(--success));\n            height: 100%;\n            width: 0%;\n            transition: width 0.3s;\n        }\n\n        @media (max-width: 1100px) {\n            .container {\n                grid-template-columns: 1fr;\n            }\n\n            #log {\n                height: 500px;\n            }\n        }\n    </style>\n</head>\n\n<body>\n    <h1>FNSS Websocket Debug Platform</h1>\n\n    <div class=\"container\">\n        <!-- Left Column -->\n        <div class=\"left-column\">\n            <!-- Mode Tabs -->\n            <div class=\"tabs\">\n                <div class=\"tab active\" onclick=\"switchMode('setting')\">Config</div>\n                <div class=\"tab\" onclick=\"switchMode('note')\">Note</div>\n                <div class=\"tab\" onclick=\"switchMode('file')\">File</div>\n                <div class=\"tab\" onclick=\"switchMode('folder')\">Folder</div>\n            </div>\n\n            <!-- Auth Card -->\n            <div class=\"card\">\n                <h2>🔐 Connection & Auth</h2>\n                <div class=\"form-group\">\n                    <label>API URL</label>\n                    <input type=\"text\" id=\"apiUrl\" value=\"ws://localhost:9000/api/user/sync\">\n                </div>\n                <div id=\"authPanel\">\n                    <div class=\"form-group\">\n                        <label>Account (Email/User)</label>\n                        <input type=\"text\" id=\"credentials\" value=\"test@example.com\">\n                    </div>\n                    <div class=\"form-group\">\n                        <label>Password</label>\n                        <input type=\"password\" id=\"password\" value=\"123456\">\n                    </div>\n                    <button onclick=\"login()\" id=\"loginBtn\" style=\"width: 100%;\">Login & Get Token</button>\n                </div>\n\n                <div id=\"connPanel\" style=\"display: none; margin-top: 1.5rem;\">\n                    <div style=\"display: flex; justify-content: space-between; align-items: center;\">\n                        <div id=\"wsStatus\" class=\"status-badge status-disconnected\">Disconnected</div>\n                        <div style=\"display: flex; gap: 0.5rem;\">\n                            <button onclick=\"connectWS()\" id=\"connectBtn\">Connect WS</button>\n                            <button onclick=\"disconnectWS()\" class=\"danger\" id=\"disconnectBtn\"\n                                style=\"display:none\">Disconnect</button>\n                        </div>\n                    </div>\n                </div>\n            </div>\n\n            <!-- Operations Card -->\n            <div class=\"card\" id=\"opCard\">\n                <h2 id=\"opTitle\">⚙️ Setting Operations</h2>\n\n                <!-- Common Config -->\n                <div class=\"form-group\">\n                    <label>Vault</label>\n                    <input type=\"text\" id=\"vault\" value=\"DefaultVault\">\n                </div>\n                <div class=\"form-group\">\n                    <label id=\"pathLabel\">Remote Path</label>\n                    <input type=\"text\" id=\"path\" value=\"config/theme.json\">\n                </div>\n                 <div class=\"form-group\">\n                     <label>PathHash (Calculated: Hash32)</label>\n                     <input type=\"text\" id=\"commonPathHash\" readonly style=\"font-size: 0.7rem; opacity: 0.7; background: #1e293b;\">\n                 </div>\n\n                <!-- Setting Inputs -->\n                <div id=\"settingInputs\">\n                    <div class=\"form-group\">\n                        <label>Content (JSON/Text)</label>\n                        <textarea id=\"settingContent\" rows=\"4\">{\"theme\": \"dark\", \"fontSize\": 14}</textarea>\n                    </div>\n                     <div class=\"form-group\">\n                        <label>ContentHash (Calculated: Hash32)</label>\n                        <input type=\"text\" id=\"settingContentHash\" readonly style=\"font-size: 0.7rem; opacity: 0.7; background: #1e293b;\">\n                    </div>\n                     <div class=\"btn-group\">\n                        <button onclick=\"sendSettingWS('SettingModify')\" class=\"success\">Modify/Create</button>\n                        <button onclick=\"sendSettingWS('SettingModifyCheck')\" class=\"outline\">Check</button>\n                        <button onclick=\"sendSettingWS('SettingDelete')\" class=\"danger\">Delete</button>\n                        <button onclick=\"sendSettingWS('SettingSync')\" class=\"warning\">Sync</button>\n                        <button onclick=\"sendSettingWS('SettingClear')\" class=\"danger\">Clear All</button>\n                    </div>\n                </div>\n\n                <!-- Note Inputs -->\n                <div id=\"noteInputs\" style=\"display: none;\">\n                    <div class=\"form-group\">\n                        <label>Note Content</label>\n                        <textarea id=\"noteContent\" rows=\"4\" placeholder=\"Markdown content...\"># Hello World</textarea>\n                    </div>\n                    <div class=\"form-group\">\n                        <label>ContentHash (Calculated: Hash32)</label>\n                        <input type=\"text\" id=\"noteContentHash\" readonly style=\"font-size: 0.7rem; opacity: 0.7; background: #1e293b;\">\n                    </div>\n                     <div class=\"form-group\">\n                        <label>Old Path (For Rename)</label>\n                        <input type=\"text\" id=\"noteOldPath\" placeholder=\"Old path for rename...\">\n                    </div>\n                    <div class=\"btn-group\">\n                        <button onclick=\"sendNoteWS('NoteModify')\" class=\"success\">Modify/Create</button>\n                        <button onclick=\"sendNoteWS('NoteModifyCheck')\" class=\"outline\">Check</button>\n                        <button onclick=\"sendNoteWS('NoteDelete')\" class=\"danger\">Delete</button>\n                        <button onclick=\"sendNoteWS('NoteRename')\" class=\"warning\">Rename</button>\n                        <button onclick=\"sendNoteWS('NoteRePush')\" class=\"outline\">RePush (Get)</button>\n                        <button onclick=\"sendNoteWS('NoteSync')\" class=\"warning\">Sync</button>\n                    </div>\n                </div>\n\n                <!-- File Inputs -->\n                <div id=\"fileInputs\" style=\"display: none;\">\n                    <div class=\"tabs\" style=\"margin-bottom: 1rem; font-size: 0.8rem;\">\n                        <div class=\"tab active\"  onclick=\"toggleFileMode('upload')\">Upload</div>\n                        <div class=\"tab\" onclick=\"toggleFileMode('download')\">Download</div>\n                         <div class=\"tab\" onclick=\"toggleFileMode('manage')\">Manage</div>\n                    </div>\n\n                    <div id=\"fileUploadPanel\">\n                         <div class=\"form-group\">\n                            <label>Select File</label>\n                            <input type=\"file\" id=\"fileInput\">\n                        </div>\n                        <div class=\"form-group\">\n                             <label>ContentHash (SHA-256)</label>\n                             <input type=\"text\" id=\"fileContentHash\" readonly style=\"font-size: 0.7rem; opacity: 0.7; background: #1e293b;\">\n                        </div>\n                         <button onclick=\"startFileUpload()\" id=\"uploadBtn\" disabled style=\"width: 100%; background: var(--success);\">\n                            Start Upload\n                        </button>\n                    </div>\n\n                    <div id=\"fileDownloadPanel\" style=\"display: none;\">\n                         <button onclick=\"startFileDownload()\" id=\"downloadBtn\" style=\"width: 100%; background: var(--primary);\">\n                            Start Download\n                        </button>\n                    </div>\n\n                    <div id=\"fileManagePanel\" style=\"display: none;\">\n                         <div class=\"form-group\">\n                            <label>Old Path (For Rename)</label>\n                            <input type=\"text\" id=\"fileOldPath\">\n                        </div>\n                        <div class=\"btn-group\">\n                             <button onclick=\"sendFileWS('FileDelete')\" class=\"danger\">Delete</button>\n                             <button onclick=\"sendFileWS('FileRename')\" class=\"warning\">Rename</button>\n                             <button onclick=\"sendFileWS('FileSync')\" class=\"outline\">Sync List</button>\n                        </div>\n                    </div>\n\n                    <!-- Progress -->\n                    <div class=\"progress-container\" id=\"progressContainer\">\n                        <div id=\"progressBar\"></div>\n                    </div>\n                    <div id=\"progressText\" style=\"font-size: 0.75rem; text-align: center; margin-top: 0.5rem; color: var(--text-muted); min-height: 1rem;\"></div>\n                </div>\n\n\n                <!-- Folder Inputs -->\n                 <div id=\"folderInputs\" style=\"display: none;\">\n                     <div class=\"form-group\">\n                        <label>Old Path (For Rename)</label>\n                        <input type=\"text\" id=\"folderOldPath\">\n                    </div>\n                    <div class=\"btn-group\">\n                        <button onclick=\"sendFolderWS('FolderModify')\" class=\"success\">Create</button>\n                        <button onclick=\"sendFolderWS('FolderDelete')\" class=\"danger\">Delete</button>\n                        <button onclick=\"sendFolderWS('FolderRename')\" class=\"warning\">Rename</button>\n                        <button onclick=\"sendFolderWS('FolderSync')\" class=\"outline\">Sync</button>\n                    </div>\n                 </div>\n\n                 <!-- Common Timestamps -->\n                 <div style=\"display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 0.5rem; margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--border);\">\n                    <div class=\"form-group\">\n                        <label>Ctime (ms)</label>\n                        <input type=\"number\" id=\"ctime\" style=\"font-size: 0.7rem;\">\n                    </div>\n                    <div class=\"form-group\">\n                        <label>Mtime (ms)</label>\n                        <input type=\"number\" id=\"mtime\" style=\"font-size: 0.7rem;\">\n                    </div>\n                     <div class=\"form-group\">\n                        <label>LastTime (Sync)</label>\n                        <input type=\"number\" id=\"lastTime\" value=\"0\" style=\"font-size: 0.7rem;\">\n                    </div>\n                </div>\n\n            </div>\n        </div>\n\n        <!-- Log Panel -->\n        <div class=\"card\">\n            <div style=\"display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;\">\n                <h2>📜 Communication Log</h2>\n                <button onclick=\"clearLog()\" class=\"outline\" style=\"padding: 0.25rem 0.75rem; font-size: 0.75rem;\">Clear</button>\n            </div>\n            <div id=\"log\"></div>\n        </div>\n    </div>\n\n    <script>\n        let socket = null;\n        let token = '';\n        let activeMode = 'setting';\n        let fileUploadFile = null;\n\n        // Hash32 Implementation (Matches Go []rune iteration)\n        function getHash32(content) {\n            let hash = 0;\n            for (const char of content) {\n                const code = char.codePointAt(0);\n                hash = ((hash << 5) - hash) + code;\n                hash |= 0; // Force 32-bit integer\n            }\n            return String(hash);\n        }\n\n        async function getFileHash(file) {\n             const buffer = await file.arrayBuffer();\n             const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);\n             const hashArray = Array.from(new Uint8Array(hashBuffer));\n             const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n             return hashHex;\n        }\n\n        // --- UI Logic ---\n\n        function switchMode(mode) {\n            activeMode = mode;\n            // Update UI tabs\n            ['setting', 'note', 'file', 'folder'].forEach(m => {\n               // Simple toggle class logic could be added here if we assigned IDs to tabs\n            });\n            document.querySelectorAll('.tabs:first-child .tab').forEach(t => t.classList.remove('active'));\n            // Find index\n            const idx = ['setting', 'note', 'file', 'folder'].indexOf(mode);\n            if(idx >= 0) document.querySelectorAll('.tabs:first-child .tab')[idx].classList.add('active');\n\n            // Update Title\n            document.getElementById('opTitle').textContent = `⚙️ ${mode.charAt(0).toUpperCase() + mode.slice(1)} Operations`;\n\n            // Toggle Panels\n            document.getElementById('settingInputs').style.display = mode === 'setting' ? 'block' : 'none';\n            document.getElementById('noteInputs').style.display = mode === 'note' ? 'block' : 'none';\n            document.getElementById('fileInputs').style.display = mode === 'file' ? 'block' : 'none';\n            document.getElementById('folderInputs').style.display = mode === 'folder' ? 'block' : 'none';\n\n            updateHashes();\n        }\n\n        function toggleFileMode(mode) {\n            document.getElementById('fileUploadPanel').style.display = mode === 'upload' ? 'block' : 'none';\n            document.getElementById('fileDownloadPanel').style.display = mode === 'download' ? 'block' : 'none';\n            document.getElementById('fileManagePanel').style.display = mode === 'manage' ? 'block' : 'none';\n\n             // Update tabs state\n             const tabs = document.querySelectorAll('#fileInputs .tab');\n             tabs.forEach(t => t.classList.remove('active'));\n             if(mode === 'upload') tabs[0].classList.add('active');\n             if(mode === 'download') tabs[1].classList.add('active');\n             if(mode === 'manage') tabs[2].classList.add('active');\n        }\n\n        function updateHashes() {\n            const path = document.getElementById('path').value;\n            document.getElementById('commonPathHash').value = getHash32(path);\n\n            if (activeMode === 'setting') {\n                const content = document.getElementById('settingContent').value;\n                document.getElementById('settingContentHash').value = getHash32(content);\n            } else if (activeMode === 'note') {\n                 const content = document.getElementById('noteContent').value;\n                document.getElementById('noteContentHash').value = getHash32(content);\n            }\n            // File hash is updated on file select\n        }\n\n        // Auto update hashes on input\n        document.getElementById('path').addEventListener('input', updateHashes);\n        document.getElementById('settingContent').addEventListener('input', updateHashes);\n        document.getElementById('noteContent').addEventListener('input', updateHashes);\n\n        document.getElementById('fileInput').addEventListener('change', async (e) => {\n            if (e.target.files.length > 0) {\n                fileUploadFile = e.target.files[0];\n                document.getElementById('path').value = fileUploadFile.name; // Auto set path\n                document.getElementById('fileContentHash').value = \"Calculating...\";\n                document.getElementById('fileContentHash').value = await getFileHash(fileUploadFile);\n                document.getElementById('uploadBtn').disabled = false;\n                updateHashes(); // Update path hash\n            }\n        });\n\n        // Init timestamps\n        const now = Date.now();\n        document.getElementById('ctime').value = now;\n        document.getElementById('mtime').value = now;\n\n\n        // --- Networking ---\n        function addLog(type, tag, content) {\n            const logEl = document.getElementById('log');\n            const entry = document.createElement('div');\n            entry.className = `log-entry ${type}`;\n            const time = new Date().toLocaleTimeString();\n            const json = typeof content === 'string' ? content : JSON.stringify(content, null, 2);\n            entry.innerHTML = `<span class=\"time\">${time}</span> <span class=\"tag\">[${tag}]</span> <pre>${json}</pre>`;\n            entry.onclick = () => entry.classList.toggle('expanded');\n            logEl.appendChild(entry);\n            logEl.scrollTop = logEl.scrollHeight;\n        }\n        function clearLog() { document.getElementById('log').innerHTML = ''; }\n\n        async function login() {\n            const apiUrl = document.getElementById('apiUrl').value.replace('ws://', 'http://').replace('/api/user/sync', '/api/user/login');\n            const credentials = document.getElementById('credentials').value;\n            const password = document.getElementById('password').value;\n\n            try {\n                const res = await fetch(apiUrl, {\n                    method: 'POST',\n                    headers: {'Content-Type': 'application/json'},\n                    body: JSON.stringify({credentials, password})\n                });\n                const data = await res.json();\n                if(data.status && data.data.token) {\n                    token = data.data.token;\n                    addLog('recv', 'Login', 'Success, Token received.');\n                    document.getElementById('connPanel').style.display = 'block';\n                } else {\n                    addLog('err', 'Login', data);\n                }\n            } catch(e) {\n                addLog('err', 'Login', e.message);\n            }\n        }\n\n        function connectWS() {\n             if (!token) return alert('Login first');\n             const url = document.getElementById('apiUrl').value;\n             socket = new WebSocket(url);\n             socket.onopen = () => {\n                 document.getElementById('wsStatus').className = 'status-badge status-connected';\n                 document.getElementById('wsStatus').innerText = 'Connected';\n                 sendWS('Authorization', token);\n             };\n             socket.onmessage = (e) => {\n                 if (e.data instanceof Blob) {\n                     addLog('recv', 'Binary', `Received ${e.data.size} bytes`);\n                     return;\n                 }\n                 const raw = e.data;\n                 const idx = raw.indexOf('|');\n                 if(idx > 0) {\n                     const type = raw.substring(0, idx);\n                     const body = raw.substring(idx+1);\n                     try {\n                         const json = JSON.parse(body);\n                         addLog('recv', type, json);\n                         // Handle specific responses\n                         if(type === 'Authorization' && json.status) {\n                             document.getElementById('disconnectBtn').style.display = 'block';\n                             document.getElementById('connectBtn').style.display = 'none';\n                         }\n                         if (type === 'FileSyncUpload') { // Server allowed upload\n                              handleUploadChunks(json.data);\n                         }\n                         if (type === 'FileSyncChunkDownload') { // Server ready to download\n                              handleDownloadChunks(json.data);\n                         }\n\n                     } catch(e) {\n                         addLog('recv', type, body);\n                     }\n                 } else {\n                     addLog('recv', 'Unknown', raw);\n                 }\n             };\n             socket.onclose = () => {\n                 document.getElementById('wsStatus').className = 'status-badge status-disconnected';\n                 document.getElementById('wsStatus').innerText = 'Disconnected';\n                  document.getElementById('disconnectBtn').style.display = 'none';\n                  document.getElementById('connectBtn').style.display = 'block';\n             };\n        }\n\n        function disconnectWS() { if(socket) socket.close(); }\n\n        function sendWS(type, data) {\n            if(!socket) return;\n            const payload = typeof data === 'string' ? data : JSON.stringify(data);\n             socket.send(`${type}|${payload}`);\n             if(type !== 'Authorization') addLog('send', type, data);\n        }\n\n        // --- Action Handlers ---\n\n        function getCommonParams() {\n            return {\n                vault: document.getElementById('vault').value,\n                path: document.getElementById('path').value,\n                pathHash: getHash32(document.getElementById('path').value),\n                ctime: parseInt(document.getElementById('ctime').value),\n                mtime: parseInt(document.getElementById('mtime').value)\n            };\n        }\n\n        function sendSettingWS(action) {\n            const params = getCommonParams();\n            if(action === 'SettingModify' || action === 'SettingModifyCheck') {\n                const content = document.getElementById('settingContent').value;\n                params.contentHash = getHash32(content);\n                if (action === 'SettingModify') params.content = content;\n            }\n             if (action === 'SettingSync') {\n                sendWS(action, {\n                    vault: params.vault,\n                    lastTime: parseInt(document.getElementById('lastTime').value),\n                    settings: []\n                });\n                return;\n            }\n             if (action === 'SettingClear') {\n                 sendWS(action, { vault: params.vault });\n                 return;\n             }\n            sendWS(action, params);\n        }\n\n        function sendNoteWS(action) {\n            const params = getCommonParams();\n            if (action === 'NoteModify' || action === 'NoteModifyCheck') {\n                 const content = document.getElementById('noteContent').value;\n                 params.contentHash = getHash32(content);\n                 if (action === 'NoteModify') params.content = content;\n            }\n            if (action === 'NoteRename') {\n                const oldPath = document.getElementById('noteOldPath').value;\n                sendWS(action, {\n                    vault: params.vault,\n                    path: params.path,\n                    pathHash: params.pathHash,\n                    oldPath: oldPath,\n                    oldPathHash: getHash32(oldPath)\n                });\n                return;\n            }\n            if (action === 'NoteSync') {\n                 sendWS(action, {\n                    vault: params.vault,\n                    lastTime: parseInt(document.getElementById('lastTime').value),\n                    notes: []\n                });\n                return;\n            }\n             if (action === 'NoteRePush') {\n                 // NoteRePush uses NoteGetRequest structure\n                  sendWS(action, {\n                    vault: params.vault,\n                    path: params.path,\n                    pathHash: params.pathHash\n                });\n                return;\n             }\n            sendWS(action, params);\n        }\n\n        function sendFolderWS(action) {\n            const params = getCommonParams();\n             if (action === 'FolderRename') {\n                const oldPath = document.getElementById('folderOldPath').value;\n                sendWS(action, {\n                    vault: params.vault,\n                    path: params.path,\n                    pathHash: params.pathHash,\n                    oldPath: oldPath,\n                    oldPathHash: getHash32(oldPath)\n                });\n                return;\n            }\n             if (action === 'FolderSync') {\n                 sendWS(action, {\n                    vault: params.vault,\n                    lastTime: parseInt(document.getElementById('lastTime').value),\n                    folders: []\n                });\n                return;\n            }\n             // FolderCreateRequest / FolderDeleteRequest\n            sendWS(action, params);\n        }\n\n        function sendFileWS(action) {\n             const params = getCommonParams();\n             if (action === 'FileRename') {\n                const oldPath = document.getElementById('fileOldPath').value;\n                sendWS(action, {\n                    vault: params.vault,\n                    path: params.path,\n                    pathHash: params.pathHash,\n                    oldPath: oldPath,\n                    oldPathHash: getHash32(oldPath)\n                });\n                return;\n            }\n             if (action === 'FileSync') {\n                 sendWS(action, {\n                    vault: params.vault,\n                    lastTime: parseInt(document.getElementById('lastTime').value),\n                    files: []\n                });\n                return;\n            }\n             if (action === 'FileDelete') {\n                 sendWS(action, params);\n                 return;\n             }\n        }\n\n        // --- File Upload / Download Logic ---\n\n        function startFileUpload() {\n            if(!fileUploadFile) return alert('Select file first');\n            const params = getCommonParams();\n            params.size = fileUploadFile.size;\n            params.contentHash = document.getElementById('fileContentHash').value;\n            // First check\n            sendWS('FileUploadCheck', params);\n        }\n\n        function handleUploadChunks(sessionData) {\n            // sessionData: { sessionId, chunkSize, path }\n            const chunkSize = sessionData.chunkSize;\n            const sessionId = sessionData.sessionId;\n            const file = fileUploadFile;\n            let offset = 0;\n            let chunkIndex = 0;\n\n            addLog('info', 'Upload', `Starting upload for session ${sessionId}, chunk size ${chunkSize}`);\n\n            const reader = new FileReader();\n            reader.onload = function(e) {\n                const chunkBuffer = e.target.result; // ArrayBuffer\n                // Construct binary message: SessionID(36) + ChunkIndex(4 BigEndian) + Data\n                const header = new Uint8Array(40);\n                // SessionID string to bytes\n                for(let i=0; i<36; i++) header[i] = sessionId.charCodeAt(i);\n                // ChunkIndex BigEndian uint32\n                new DataView(header.buffer).setUint32(36, chunkIndex, false);\n\n                const chunkData = new Uint8Array(chunkBuffer);\n                const packet = new Uint8Array(header.length + chunkData.length);\n                packet.set(header, 0);\n                packet.set(chunkData, 40);\n\n                socket.send(packet);\n\n                offset += chunkData.length;\n                chunkIndex++;\n                updateProgress(offset, file.size);\n\n                if (offset < file.size) {\n                    readNextChunk();\n                } else {\n                    addLog('success', 'Upload', 'All chunks sent');\n                }\n            };\n\n            function readNextChunk() {\n                const slice = file.slice(offset, offset + chunkSize);\n                reader.readAsArrayBuffer(slice);\n            }\n            readNextChunk();\n        }\n\n        function startFileDownload() {\n             const params = getCommonParams();\n             sendWS('FileChunkDownload', params);\n        }\n\n        function handleDownloadChunks(sessionData) {\n            // sessionData: { sessionId, chunkSize, totalChunks, size, path }\n            // For now, browser receiving binary chunks via WebSocket is tricky if mixed with text frames.\n            // The server sends binary frames for file chunks?\n            // Checking server code: `c.Conn.WriteMessage(websocket.BinaryMessage, ...)`\n            // Yes.\n            addLog('info', 'Download', `Server prepared download: ${sessionData.size} bytes`);\n        }\n\n\n        function updateProgress(loaded, total) {\n            const percent = Math.floor((loaded / total) * 100);\n            document.getElementById('progressBar').style.width = `${percent}%`;\n            document.getElementById('progressText').innerText = `${percent}% (${loaded}/${total})`;\n            document.getElementById('progressContainer').style.display = 'block';\n        }\n\n    </script>\n</body>\n</html>"
  },
  {
    "path": "docs/websocket_integration.md",
    "content": "# WebSocket 同步协议更新说明 (2026-03-05)\n\n本文档详细说明了近期 WebSocket 协议的变更，主要涉及笔记、附件和配置同步消息中 `lastTime` 字段的补充。\n\n## 1. 核心变更：引入 `lastTime`\n\n为了增强前端增量同步的可靠性，我们在多个资源变更消息中补充了 `lastTime` 字段。\n\n- **字段名**: `lastTime` (对应后端 `UpdatedTimestamp`)\n- **数据类型**: `int64` (毫秒级时间戳)\n- **物理意义**: 该资源记录在数据库中的最后更新时间。前端在进行增量同步请求时，应记录此值，并作为下次同步请求的起点。\n\n---\n\n## 2. 字段更新详情\n\n### 2.1 笔记消息 (Note)\n\n| 消息 Action (Action) | 新增字段 | 说明 |\n| :--- | :--- | :--- |\n| `NoteSyncRename` | `lastTime` | 笔记重命名后的同步消息 |\n| `NoteSyncMtime` | `lastTime` | 笔记修改时间变更（无需下载内容时） |\n| `NoteSyncDelete` | `lastTime` | 笔记已删除的同步消息 |\n| `NoteSyncModify` | `lastTime` | (固有) 笔记创建或更新消息 |\n\n### 2.2 附件/文件消息 (File)\n\n| 消息 Action (Action) | 新增字段 | 说明 |\n| :--- | :--- | :--- |\n| `FileSyncRename` | `lastTime` | 附件重命名后的同步消息 |\n| `FileSyncMtime` | `lastTime` | 附件修改时间变更 |\n| `FileSyncDelete` | `lastTime` | 附件已删除的同步消息 |\n| `FileSyncUpdate` | `lastTime` | (固有) 附件创建或更新消息 |\n\n### 2.3 配置消息 (Setting)\n\n| 消息 Action (Action) | 新增字段 | 说明 |\n| :--- | :--- | :--- |\n| `SettingSyncMtime` | `lastTime` | 配置修改时间变更 |\n| `SettingSyncDelete` | `lastTime`, `pathHash`, `ctime`, `mtime` | 配置已删除同步（结构大幅增强以保持一致性） |\n| `SettingSyncModify` | `lastTime` | (固有) 配置创建或更新消息 |\n\n---\n\n## 3. 详细结构定义 (JSON 示例)\n\n### 配置删除消息示例 (结构增强)\n```json\n{\n    \"action\": \"SettingSyncDelete\",\n    \"data\": {\n        \"path\": \"User/Theme\",\n        \"pathHash\": \"shash789\",\n        \"ctime\": 1700000000,\n        \"mtime\": 1700000000,\n        \"lastTime\": 1700000001\n    }\n}\n```\n\n### 笔记重命名消息示例\n```json\n{\n    \"action\": \"NoteSyncRename\",\n    \"data\": {\n        \"path\": \"NewName.md\",\n        \"pathHash\": \"nfhash123\",\n        \"oldPath\": \"OldName.md\",\n        \"oldPathHash\": \"ofhash456\",\n        \"lastTime\": 1700001000,\n        ...\n    }\n}\n```\n\n---\n\n## 4. 前端对接建议\n\n1. **状态更新**: 当收到上述任何带有 `lastTime` 字段的消息时，前端应更新本地缓存中对应资源的 `lastTime` 属性。\n2. **同步基准**: 在调用 `NoteSync`, `FileSync`, `SettingSync` 时，参数中的 `lastTime` 应取本地所有资源（包含已逻辑删除的资源）中最大的那个 `lastTime` 值，以确保不遗漏任何服务端变更。\n3. **删除处理**: `SettingSyncDelete` 现在的字段更丰富（补全了 `pathHash` 等），前端可以更统一地处理各类资源的删除逻辑。\n"
  },
  {
    "path": "docs/ws_api.md",
    "content": "# WebSocket API 全量对接文档 (100% 完整版)\n\n本手册为前端开发人员提供服务端 WebSocket 接口的**完全定义**。涵盖所有模块（笔记、文件夹、文件、设置）的请求、响应、推送消息及详细字段结构。\n\n---\n\n## 1. 协议规范\n\n### 1.1 连接\n\n- **Endpoint**: `GET /api/user/sync`\n- **协议升级**: 标准 WebSocket (RFC 6455)。\n\n### 1.2 消息封装格式\n\nWebSocket 文本帧统一使用 `Action|JSON` 字符串格式。\n\n- **示例**: `Authorization|\"token_string_here\"`\n\n### 1.3 统一响应外壳 (`Res`)\n\n服务端发回的 JSON 消息体（管道符 `|` 之后的部分）结构如下：\n\n| JSON Key  | 类型       | 说明                                                          |\n|:----------|:-----------|:--------------------------------------------------------------|\n| `code`    | int        | 业务状态码 (1: 成功, 6: 无需同步, 441: 冲突, 305: 参数错误等) |\n| `status`  | bool       | 逻辑成功标识                                                  |\n| `message` | string     | 错误或提示信息 (i18n)                                         |\n| `data`    | object/any | 具体的业务负载内容                                            |\n| `details` | string     | 详细错误信息 (omitempty)                                      |\n| `vault`   | string     | 所属仓库名称 (omitempty)                                      |\n| `context` | string     | 上下文标识 (omitempty)                                        |\n\n---\n\n## 2. 基础控制消息\n\n### 2.1 鉴权 (Authorization)\n\n- **流向**: 客户端 -> 服务端\n- **Action**: `Authorization`\n- **内容 (Data)**: Token 字符串文本。\n- **响应 (Data)**:\n  - `version`: string (服务端版本号)\n  - `gitTag`: string (Git Tag 信息)\n  - `buildTime`: string (编译时间)\n\n### 2.2 客户端信息声明 (ClientInfo)\n\n- **流向**: 客户端 -> 服务端\n- **Action**: `ClientInfo`\n- **请求内容 (Data)**:\n\n| 字段                  | 类型   | 必填 | 说明                                     |\n|:----------------------|:-------|:-----|:-----------------------------------------|\n| `name`                | string | 是   | 客户端名称 (设备名)                      |\n| `version`             | string | 是   | 客户端当前版本                           |\n| `type`                | string | 是   | 客户端类型 (如 `obsidianPlugin`)         |\n| `offlineSyncStrategy` | string | 否   | 策略: `newTimeMerge` / `ignoreTimeMerge` |\n\n- **响应 (Data)**:\n\n| 字段                   | 类型   | 说明                 |\n|:-----------------------|:-------|:---------------------|\n| `versionIsNew`         | bool   | 服务端版本是否有更新 |\n| `versionNewName`       | string | 服务端新版本号       |\n| `versionNewLink`       | string | 服务端新版本下载链接 |\n| `pluginVersionIsNew`   | bool   | 插件版本是否有更新   |\n| `pluginVersionNewName` | string | 插件新版本号         |\n| `pluginVersionNewLink` | string | 插件新版本下载链接   |\n\n---\n\n## 3. 笔记模块 (Notes)\n\n### 3.1 核心动作对照表\n\n| 流向   | Action             | 说明               | 数据结构 (Data)             |\n|:-------|:-------------------|:-------------------|:----------------------------|\n| C -> S | `NoteSync`         | 请求笔记增量同步   | `NoteSyncRequest`           |\n| C -> S | `NoteModify`       | 提交修改/新建      | `NoteModifyOrCreateRequest` |\n| C -> S | `NoteDelete`       | 删除笔记           | `NoteDeleteRequest`         |\n| C -> S | `NoteRename`       | 重命名笔记         | `NoteRenameRequest`         |\n| C -> S | `NoteCheck`        | 检查笔记更新必要性 | `NoteUpdateCheckRequest`    |\n| C -> S | `NoteRePush`       | 请求重推某笔记     | `NoteGetRequest`            |\n| S -> C | `NoteSyncModify`   | 推送/同步笔记详情  | `NoteSyncModifyMessage`     |\n| S -> C | `NoteSyncDelete`   | 指示客户端删除     | `NoteSyncDeleteMessage`     |\n| S -> C | `NoteSyncRename`   | 指示客户端重命名   | `NoteSyncRenameMessage`     |\n| S -> C | `NoteSyncMtime`    | 仅同步修改时间     | `NoteSyncMtimeMessage`      |\n| S -> C | `NoteSyncNeedPush` | 要求客户端上传本地 | `NoteSyncNeedPushMessage`   |\n| S -> C | `NoteSyncEnd`      | 完成同步响应       | `NoteSyncEndMessage`        |\n\n### 3.2 详细 DTO 定义\n\n#### `NoteSyncEndMessage`\n\n| 字段                 | 类型  | 说明                                                |\n|:---------------------|:------|:----------------------------------------------------|\n| `lastTime`           | int64 | **[关键]** 本次同步后的最新时间戳 (毫秒)，下传传此值 |\n| `needUploadCount`    | int64 | 需要客户端上传的笔记总数                            |\n| `needModifyCount`    | int64 | 服务端下发修改的笔记总数                            |\n| `needSyncMtimeCount` | int64 | 仅同步时间的笔记总数                                |\n| `needDeleteCount`    | int64 | 指示删除的笔记总数                                  |\n| `messages`           | array | 变更消息队列，详见第 7 章节                          |\n\n#### `NoteModifyOrCreateRequest`\n\n| 字段          | 类型   | 说明                              |\n|:--------------|:-------|:----------------------------------|\n| `vault`       | string | **[必填]** 仓库名                 |\n| `path`        | string | **[必填]** 笔记完整路径           |\n| `pathHash`    | string | 路径哈希                          |\n| `content`     | string | 笔记文本全文                      |\n| `contentHash` | string | 内容哈希                          |\n| `baseHash`    | string | 修改前的基础哈希 (用于冲突合并)   |\n| `ctime`       | int64  | 创建时间 (秒)                     |\n| `mtime`       | int64  | 修改时间 (秒)                     |\n| `createOnly`  | bool   | 设置为 true 时，若笔记已存在则报错 |\n\n---\n\n## 4. 文件夹模块 (Folders)\n\n### 4.1 核心动作\n\n- `FolderSync` (C->S): `{ \"vault\": string, \"lastTime\": int64, \"folders\": Array<FolderSyncCheckRequest> }`\n- `FolderModify` (C->S): `{ \"vault\": string, \"path\": string }`\n- `FolderDelete` (C->S): `{ \"vault\": string, \"path\": string, \"pathHash\": string }`\n- `FolderRename` (C->S): `{ \"vault\": string, \"oldPath\": string, \"path\": string, ... }`\n- `FolderSyncEnd` (S->C): `{ \"lastTime\": int64, \"needModifyCount\": int, \"needDeleteCount\": int, \"messages\": [] }`\n\n---\n\n## 5. 文件模块 (Files)\n\n### 5.1 二进制分片上传逻辑 (BC Frame)\n\n1. 客户端发送 `FileUploadCheck` (JSON)。\n2. 服务端响应 `FileUpload` (JSON)，返回 `sessionId` 和 `chunkSize`。\n3. 客户端循环发送 **二进制帧**。帧前缀固定为 `BC` (ASCII 0x42 0x43)。\n   - **帧格式 (Binary)**: `[36字节 SessionID][4字节 uint32 大端序 ChunkIndex][原始分片数据]`\n\n### 5.2 文件同步动作汇总\n\n- `FileSyncUpdate` (推送): `{ \"path\", \"pathHash\", \"contentHash\", \"size\", \"ctime\", \"mtime\", \"lastTime\" }`\n- `FileSyncEnd` (推送): `{ \"lastTime\", \"needUploadCount\", \"needModifyCount\", \"needSyncMtimeCount\", \"needDeleteCount\", \"messages\" }`\n\n---\n\n## 6. 设置模块 (Settings)\n\n- `SettingSync`: `{ \"vault\": string, \"settings\": Array, \"cover\": bool }`\n- `SettingSyncEnd`: `{ \"lastTime\", \"needUploadCount\", \"needModifyCount\", \"needSyncMtimeCount\", \"needDeleteCount\", \"messages\" }`\n\n---\n\n## 7. 队列消息结构 (`WSQueuedMessage`)\n\n在所有 `*SyncEnd` 消息的 `messages` 数组中，每个项的结构为：\n\n```json\n{\n  \"action\": \"NoteSyncModify\", // 具体的推送 Action 名\n  \"data\": { ... }             // 该 Action 对应的具体 Data 结构\n}\n```\n\n---\n\n## 8. WebSocket 专属状态码汇总\n\n| Code  | 说明                                                           |\n|:------|:---------------------------------------------------------------|\n| `1`   | 成功 (Success)                                                 |\n| `6`   | 服务端数据与客户端一致，无需更新 (SuccessNoUpdate)              |\n| `305` | 客户端提交的 JSON 参数不符合 binding 校验 (ErrorInvalidParams) |\n| `433` | 笔记保存入库失败 (ErrorNoteModifyOrCreateFailed)               |\n| `441` | 发生内容冲突，且无法自动合并 (ErrorNoteConflict)                |\n| `463` | 分片上传 Session 已过期或无效 (ErrorFileUploadSessionNotFound) |\n| `490` | 同步逻辑冲突 (ErrorSyncConflict)                               |\n\n---\n\n*注：文档中所有 `int64` 时间戳除 `lastTime`(毫秒) 外均默认为 **秒**。*\n"
  },
  {
    "path": "docs/ws_setting_clear_api.md",
    "content": "# WebSocket API: 清理配置 (SettingClear) 对接文档\n\n本文档描述了如何通过 WebSocket 接口清理指定笔记库（Vault）的所有配置信息。\n\n## 1. 客户端请求 (Client -> Server)\n\n客户端发送一个 JSON 消息来请求清理指定笔记本的配置。\n\n- **Action**: `SettingClear`\n- **Data 结构**: `SettingClearRequest`\n\n### 消息示例\n```json\n{\n    \"action\": \"SettingClear\",\n    \"data\": {\n        \"vault\": \"我的笔记库\"\n    }\n}\n```\n\n### 字段说明\n| 字段名 | 类型   | 必填 | 说明                      |\n|:-------|:-------|:-----|:--------------------------|\n| vault  | string | 是   | 要清理的笔记本（Vault）名称 |\n\n---\n\n## 2. 服务端广播 (Server -> Other Clients)\n\n为了保持多端同步，服务端在处理成功后，会向该用户**除发起者外**的所有其他在线客户端广播清理消息。\n\n- **Action**: `SettingSyncClear`\n\n### 广播消息示例\n```json\n{\n    \"code\": 200,\n    \"msg\": \"success\",\n    \"action\": \"SettingSyncClear\",\n    \"vault\": \"我的笔记库\",\n    \"data\": null\n}\n```\n\n### 前端处理建议\n1. 当收到 `action: \"SettingSyncClear\"` 时，前端应清空本地对应 `vault` 的所有配置缓存或存储。\n2. 建议在发起 `SettingClear` 请求前，显式弹出确认框提示用户该操作为**物理清理**且不可逆。\n\n---\n\n## 3. 错误处理\n\n如果发生错误（如 Vault 不存在或数据库异常），服务端会返回对应的错误代码：\n\n| Code | 说明                        |\n|:-----|:----------------------------|\n| 400  | 参数错误 (如未传 vault)     |\n| 473  | 清理失败 (数据库或系统异常) |\n| 401  | 认证失效                    |\n"
  },
  {
    "path": "frontend/assets/alert-dialog-CfMssux5.js",
    "content": "import{c as a,ar as e,r as s,j as t,as as l,at as d,f as o,au as m,av as r,aw as i,ax as c,ay as f,az as n}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const p=a(\"Globe\",[[\"circle\",{cx:\"12\",cy:\"12\",r:\"10\",key:\"1mglay\"}],[\"path\",{d:\"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20\",key:\"13o1zl\"}],[\"path\",{d:\"M2 12h20\",key:\"9i4pu4\"}]]),x=a(\"Laptop\",[[\"path\",{d:\"M20 16V7a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v9m16 0H4m16 0 1.28 2.55a1 1 0 0 1-.9 1.45H3.62a1 1 0 0 1-.9-1.45L4 16\",key:\"tarvll\"}]]),y=a(\"Smartphone\",[[\"rect\",{width:\"14\",height:\"20\",x:\"5\",y:\"2\",rx:\"2\",ry:\"2\",key:\"1yt0o3\"}],[\"path\",{d:\"M12 18h.01\",key:\"mhygvu\"}]]),N=e,u=l,g=s.forwardRef(({className:a,...e},s)=>t.jsx(n,{className:o(\"fixed inset-0 z-50 bg-black/80  data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",a),...e,ref:s}));\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */g.displayName=n.displayName;const h=s.forwardRef(({className:a,...e},s)=>t.jsxs(u,{children:[t.jsx(g,{}),t.jsx(d,{ref:s,className:o(\"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg\",a),...e})]}));h.displayName=d.displayName;const j=({className:a,...e})=>t.jsx(\"div\",{className:o(\"flex flex-col space-y-2 text-center sm:text-left\",a),...e});j.displayName=\"AlertDialogHeader\";const w=({className:a,...e})=>t.jsx(\"div\",{className:o(\"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",a),...e});w.displayName=\"AlertDialogFooter\";const b=s.forwardRef(({className:a,...e},s)=>t.jsx(m,{ref:s,className:o(\"text-lg font-semibold\",a),...e}));b.displayName=m.displayName;const k=s.forwardRef(({className:a,...e},s)=>t.jsx(r,{ref:s,className:o(\"text-sm text-muted-foreground\",a),...e}));k.displayName=r.displayName;const v=s.forwardRef(({className:a,...e},s)=>t.jsx(f,{ref:s,className:o(c(),a),...e}));v.displayName=f.displayName;const z=s.forwardRef(({className:a,...e},s)=>t.jsx(i,{ref:s,className:o(c({variant:\"outline\"}),\"mt-2 sm:mt-0\",a),...e}));z.displayName=i.displayName;export{N as A,p as G,x as L,y as S,h as a,j as b,b as c,k as d,w as e,z as f,v as g};\n"
  },
  {
    "path": "frontend/assets/auth-form-BjZ9qVzL.js",
    "content": "import{r as e,u as s,a,e as t,b as r,G as i,j as o,N as l,S as n,M as c,H as d,K as m,I as u,t as h}from\"./font-loader-CIrh3KnA.js\";import{o as p,c as g,s as x,u as w,t as f}from\"./zod-B54Zg8Xp.js\";import{G as j}from\"./github-Bzk-4SPC.js\";import{W as b,A as N,m as v}from\"./main-BIi-kGYY.js\";const y=e=>x().min(6,e(\"ui.auth.passwordMinLength\"));p({oldPassword:x().optional(),password:x().optional(),confirmPassword:x().optional()});const S={hidden:{opacity:0,y:5},visible:{opacity:1,y:0,transition:{duration:.3,ease:\"easeOut\"}},exit:{opacity:0,y:-5,transition:{duration:.2}}};function k({onSuccess:k,registerIsEnable:P=!0}){const{t:I}=s(),{isLoading:L,login:F,registerUser:z}=function(){const[i,o]=e.useState(!1),{t:l}=s();return{isLoading:i,login:async e=>{o(!0);try{const s=await fetch(a(t.API_URL+\"/api/user/login\"),{method:\"POST\",body:JSON.stringify(e),headers:r({token:null})});if(!s.ok)throw new Error(\"Network response was not ok\");const i=await s.json();return i.code<100&&i.code>0?(localStorage.setItem(\"token\",i.data.token),localStorage.setItem(\"username\",i.data.username),localStorage.setItem(\"uid\",i.data.uid),localStorage.setItem(\"avatar\",i.data.avatar),localStorage.setItem(\"email\",i.data.email),{success:!0,message:i.data.message}):{success:!1,error:i.details?`${i.message}: ${i.details}`:i.message}}catch(s){return{success:!1,error:l(\"ui.auth.loginRequestFailed\")}}finally{o(!1)}},registerUser:async e=>{o(!0);try{const s=await fetch(a(t.API_URL+\"/api/user/register\"),{method:\"POST\",body:JSON.stringify(e),headers:r({token:null})});if(!s.ok)throw new Error(\"Network response was not ok\");const i=await s.json();return i.code<100&&i.code>0?(localStorage.setItem(\"token\",i.data.token),localStorage.setItem(\"username\",i.data.username),localStorage.setItem(\"uid\",i.data.uid),localStorage.setItem(\"avatar\",i.data.avatar),localStorage.setItem(\"email\",i.data.email),{success:!0}):{success:!1,error:i.details?`${i.message}: ${i.details}`:i.message}}catch(s){return{success:!1,error:l(\"ui.auth.registerRequestFailed\")}}finally{o(!1)}}}}(),{theme:M,setTheme:T,resolvedTheme:C}=i(),[O,R]=e.useState(\"login\"),$=(_=I,p({credentials:x().min(1,_(\"ui.auth.credentialsRequired\")),password:y(_),remember:g.boolean().optional().default(!1)}));var _;const A=(e=>p({username:x().min(3,e(\"ui.auth.usernameMinLength\")),email:x().email(e(\"ui.auth.emailInvalid\")),password:y(e),confirmPassword:x().min(6,e(\"ui.auth.passwordMinLength\"))}).refine(e=>e.password===e.confirmPassword,{message:e(\"ui.auth.passwordMismatch\"),path:[\"confirmPassword\"]}))(I),U=w({resolver:f($)}),q=w({resolver:f(A)}),E=e=>{e!==O&&(\"register\"!==e||P?R(e):h.info(I(\"ui.auth.registerClosed\")))};return o.jsxs(\"div\",{className:`auth-page-container ${C}`,children:[o.jsx(\"div\",{className:\"auth-background-layer\",children:o.jsx(l,{})}),o.jsxs(\"div\",{className:\"auth-floating-actions\",children:[o.jsx(\"button\",{onClick:()=>{window.open(\"https://github.com/haierkeys/fast-note-sync-service\",\"_blank\",\"noopener,noreferrer\")},className:\"auth-floating-switcher\",title:I(\"ui.common.sourceCode\"),children:o.jsx(j,{size:18})}),o.jsx(\"button\",{onClick:()=>{T(\"light\"===M?\"dark\":\"dark\"===M?\"auto\":\"light\")},className:\"auth-floating-switcher\",title:I(\"auto\"===M?\"ui.settings.themeAuto\":\"dark\"===C?\"ui.settings.themeDark\":\"ui.settings.themeLight\"),children:\"auto\"===M?o.jsx(n,{size:18}):\"dark\"===C?o.jsx(c,{size:18}):o.jsx(d,{size:18})}),o.jsx(m,{showText:!1,className:\"auth-floating-switcher\"})]}),o.jsxs(\"main\",{className:\"relative z-50 w-full px-6 py-12 flex flex-col items-center\",children:[o.jsxs(\"div\",{className:\"auth-logo-wrapper\",children:[o.jsx(\"div\",{className:\"auth-logo-box\",children:o.jsx(b,{size:40,className:\"auth-logo-icon\"})}),o.jsx(\"h1\",{className:\"auth-title\",children:\"Fast Note Sync\"}),o.jsx(\"p\",{className:\"auth-subtitle\",children:I(\"ui.common.subtitle\")})]}),o.jsxs(\"div\",{className:\"auth-card\",children:[o.jsx(\"div\",{className:\"auth-tabs-container\",children:o.jsxs(\"div\",{className:\"auth-tabs\",children:[o.jsx(\"button\",{onClick:()=>E(\"login\"),className:\"auth-tab \"+(\"login\"===O?\"active\":\"\"),children:I(\"ui.auth.login\")}),o.jsx(\"button\",{onClick:()=>E(\"register\"),className:\"auth-tab \"+(\"register\"===O?\"active\":\"\"),children:I(\"ui.auth.registerButton\")})]})}),o.jsx(N,{mode:\"wait\",children:\"login\"===O?o.jsxs(v.form,{variants:S,initial:\"hidden\",animate:\"visible\",exit:\"exit\",onSubmit:U.handleSubmit(async e=>{const s=await F(e);s.success?k():h.error(s.error)}),children:[o.jsxs(\"div\",{children:[o.jsxs(\"div\",{className:\"relative group\",children:[o.jsx(\"label\",{htmlFor:\"login-credentials\",className:\"sr-only\",children:I(\"ui.auth.credentials\")}),o.jsx(u,{id:\"login-credentials\",placeholder:I(\"ui.auth.credentialsPlaceholder\"),...U.register(\"credentials\"),className:\"auth-input\"}),U.formState.errors.credentials&&o.jsx(\"p\",{className:\"text-[10px] text-destructive/80 font-bold uppercase tracking-wider mt-1 ml-1 mb-2\",children:U.formState.errors.credentials.message})]}),o.jsxs(\"div\",{className:\"relative group\",children:[o.jsx(\"label\",{htmlFor:\"login-password\",className:\"sr-only\",children:I(\"ui.auth.password\")}),o.jsx(u,{id:\"login-password\",type:\"password\",placeholder:I(\"ui.auth.passwordPlaceholder\"),...U.register(\"password\"),className:\"auth-input\"}),U.formState.errors.password&&o.jsx(\"p\",{className:\"text-[10px] text-destructive/80 font-bold uppercase tracking-wider mt-1 ml-1 mb-2\",children:U.formState.errors.password.message})]})]}),o.jsx(\"button\",{type:\"submit\",disabled:L,className:\"auth-button-primary\",children:L?o.jsx(v.div,{animate:{rotate:360},transition:{duration:1,repeat:1/0,ease:\"linear\"},className:\"w-4 h-4 border-2 border-white/30 border-t-white rounded-full\"}):I(\"ui.auth.login\")})]},\"login\"):o.jsxs(v.form,{variants:S,initial:\"hidden\",animate:\"visible\",exit:\"exit\",onSubmit:q.handleSubmit(async e=>{const s=await z(e);s.success?k():h.error(s.error)}),children:[o.jsxs(\"div\",{className:\"space-y-0\",children:[o.jsxs(\"div\",{className:\"relative group\",children:[o.jsx(\"label\",{htmlFor:\"register-username\",className:\"sr-only\",children:I(\"ui.auth.username\")}),o.jsx(u,{id:\"register-username\",placeholder:I(\"ui.auth.usernamePlaceholder\"),...q.register(\"username\"),className:\"auth-input\"}),q.formState.errors.username&&o.jsx(\"p\",{className:\"text-[10px] text-destructive/80 font-bold uppercase tracking-wider mt-1 ml-1 mb-2\",children:q.formState.errors.username.message})]}),o.jsxs(\"div\",{className:\"relative group\",children:[o.jsx(u,{type:\"email\",placeholder:I(\"ui.auth.emailPlaceholder\"),...q.register(\"email\"),className:\"auth-input\"}),q.formState.errors.email&&o.jsx(\"p\",{className:\"text-[10px] text-destructive/80 font-bold uppercase tracking-wider mt-1 ml-1 mb-2\",children:q.formState.errors.email.message})]}),o.jsxs(\"div\",{className:\"relative group\",children:[o.jsx(\"label\",{htmlFor:\"register-password\",className:\"sr-only\",children:I(\"ui.auth.password\")}),o.jsx(u,{id:\"register-password\",type:\"password\",placeholder:I(\"ui.auth.passwordPlaceholder\"),...q.register(\"password\"),className:\"auth-input\"}),q.formState.errors.password&&o.jsx(\"p\",{className:\"text-[10px] text-destructive/80 font-bold uppercase tracking-wider mt-1 ml-1 mb-2\",children:q.formState.errors.password.message})]}),o.jsxs(\"div\",{className:\"relative group\",children:[o.jsx(\"label\",{htmlFor:\"register-confirm-password\",className:\"sr-only\",children:I(\"ui.auth.confirmPassword\")}),o.jsx(u,{id:\"register-confirm-password\",type:\"password\",placeholder:I(\"ui.auth.confirmPasswordPlaceholder\"),...q.register(\"confirmPassword\"),className:\"auth-input\"}),q.formState.errors.confirmPassword&&o.jsx(\"p\",{className:\"text-[10px] text-destructive/80 font-bold uppercase tracking-wider mt-1 ml-1 mb-2\",children:q.formState.errors.confirmPassword.message})]})]}),o.jsx(\"button\",{type:\"submit\",disabled:L,className:\"auth-button-primary\",children:L?o.jsx(v.div,{animate:{rotate:360},transition:{duration:1,repeat:1/0,ease:\"linear\"},className:\"w-4 h-4 border-2 border-white/30 border-t-white rounded-full\"}):I(\"ui.auth.registerButton\")})]},\"register\")})]}),o.jsx(\"div\",{className:\"auth-footer-wrapper\",children:o.jsx(\"footer\",{className:\"auth-brand-footer\",dangerouslySetInnerHTML:{__html:I(\"ui.common.footerTitle\")}})})]})]})}export{k as AuthForm};\n"
  },
  {
    "path": "frontend/assets/badge-C63ATniC.js",
    "content": "import{j as r,f as e,l as t}from\"./font-loader-CIrh3KnA.js\";const n=t(\"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2\",{variants:{variant:{default:\"border-transparent bg-primary text-primary-foreground hover:bg-primary/80\",secondary:\"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80\",destructive:\"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80\",outline:\"text-foreground\"}},defaultVariants:{variant:\"default\"}});function o({className:t,variant:o,...a}){return r.jsx(\"div\",{className:e(n({variant:o}),t),...a})}export{o as B};\n"
  },
  {
    "path": "frontend/assets/canvas-viewer-Bt8OKmt9.css",
    "content": ".canvas-toolbar{display:flex;align-items:center;justify-content:space-between;gap:.25rem;margin-bottom:.25rem}@media(min-width:640px){.canvas-toolbar{gap:1rem;margin-bottom:1rem}}.canvas-toolbar-left{display:flex;align-items:center;gap:.25rem;min-width:0;flex:1}@media(min-width:640px){.canvas-toolbar-left{gap:.75rem}}.canvas-toolbar-title{font-size:.875rem;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:hsl(var(--foreground))}@media(min-width:640px){.canvas-toolbar-title{font-size:1rem}}.canvas-toolbar-right{display:flex;align-items:center;gap:.125rem;flex-shrink:0}@media(min-width:640px){.canvas-toolbar-right{gap:.5rem}}.canvas-zoom-label{font-size:.75rem;color:hsl(var(--muted-foreground));min-width:3rem;text-align:center;-webkit-user-select:none;user-select:none}.canvas-container{position:relative;width:100%;height:100%;overflow:hidden;border-radius:.75rem;border:1px solid hsl(var(--border));background:hsl(var(--background));cursor:grab;touch-action:none}.canvas-container.is-dragging{cursor:grabbing}.canvas-transform-layer{position:absolute;top:0;left:0;transform-origin:0 0;will-change:transform}.canvas-node{position:absolute;border-radius:.5rem;border:2px solid hsl(var(--border));background:hsl(var(--card));box-shadow:0 2px 6px hsl(var(--foreground) / .1);overflow:hidden;box-sizing:border-box;transition:box-shadow .15s ease}.canvas-node:hover{box-shadow:0 4px 12px hsl(var(--foreground) / .15)}.canvas-node-text{padding:.75rem;overflow:auto;font-size:.875rem;line-height:1.5}.canvas-node-text .markdown-preview{max-height:100%}.canvas-node-file{display:flex;align-items:center;gap:.5rem;padding:.75rem;cursor:pointer;font-size:.875rem;color:hsl(var(--primary))}.canvas-node-file:hover{background:hsl(var(--primary) / .05)}.canvas-node-file-icon{flex-shrink:0;width:1rem;height:1rem}.canvas-node-file-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.canvas-node-link{display:flex;align-items:center;gap:.5rem;padding:.75rem;cursor:pointer;font-size:.875rem;color:hsl(var(--primary))}.canvas-node-link:hover{background:hsl(var(--primary) / .05)}.canvas-node-link-icon{flex-shrink:0;width:1rem;height:1rem}.canvas-node-link-url{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.canvas-group{position:absolute;border-radius:.75rem;border:3px dashed hsl(var(--border));background:hsl(var(--muted) / .3);box-sizing:border-box}.canvas-group-label{position:absolute;top:-1.5rem;left:.5rem;font-size:.75rem;font-weight:500;color:hsl(var(--muted-foreground));white-space:nowrap}.canvas-edge-layer{position:absolute;top:0;left:0;overflow:visible;pointer-events:none}.canvas-edge-path{fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}.canvas-edge-label{font-size:.75rem;fill:hsl(var(--muted-foreground));text-anchor:middle;dominant-baseline:central}.canvas-empty,.canvas-error{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:.5rem;color:hsl(var(--muted-foreground));font-size:.875rem}.canvas-error{color:hsl(var(--destructive))}.canvas-loading{display:flex;align-items:center;justify-content:center;height:100%;color:hsl(var(--muted-foreground))}\n"
  },
  {
    "path": "frontend/assets/canvas-viewer-Cxwbo1vR.js",
    "content": "import{c as e,r as t,b as n,e as s,a,t as r,a8 as o,j as i,B as l}from\"./font-loader-CIrh3KnA.js\";import{T as c}from\"./tooltip-Dr-qRlmI.js\";import{a as d}from\"./markdown-editor-CX5kQlgI.js\";import{F as h}from\"./format-CdHm7RWL.js\";import{E as u}from\"./main-BIi-kGYY.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const m=e(\"ArrowLeft\",[[\"path\",{d:\"m12 19-7-7 7-7\",key:\"1l729n\"}],[\"path\",{d:\"M19 12H5\",key:\"x3x0zl\"}]]),f=e(\"ArrowUpNarrowWide\",[[\"path\",{d:\"m3 8 4-4 4 4\",key:\"11wl7u\"}],[\"path\",{d:\"M7 4v16\",key:\"1glfcx\"}],[\"path\",{d:\"M11 12h4\",key:\"q8tih4\"}],[\"path\",{d:\"M11 16h7\",key:\"uosisv\"}],[\"path\",{d:\"M11 20h10\",key:\"jvxblo\"}]]),p=e(\"Folder\",[[\"path\",{d:\"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z\",key:\"1kt360\"}]]),y=e(\"Maximize\",[[\"path\",{d:\"M8 3H5a2 2 0 0 0-2 2v3\",key:\"1dcmit\"}],[\"path\",{d:\"M21 8V5a2 2 0 0 0-2-2h-3\",key:\"1e4gt3\"}],[\"path\",{d:\"M3 16v3a2 2 0 0 0 2 2h3\",key:\"wsl5sc\"}],[\"path\",{d:\"M16 21h3a2 2 0 0 0 2-2v-3\",key:\"18trek\"}]]),x=e(\"RotateCcw\",[[\"path\",{d:\"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\",key:\"1357e3\"}],[\"path\",{d:\"M3 3v5h5\",key:\"1xhq8a\"}]]),g=e(\"ZoomIn\",[[\"circle\",{cx:\"11\",cy:\"11\",r:\"8\",key:\"4ej97u\"}],[\"line\",{x1:\"21\",x2:\"16.65\",y1:\"21\",y2:\"16.65\",key:\"13gj7c\"}],[\"line\",{x1:\"11\",x2:\"11\",y1:\"8\",y2:\"14\",key:\"1vmskp\"}],[\"line\",{x1:\"8\",x2:\"14\",y1:\"11\",y2:\"11\",key:\"durymu\"}]]),k=e(\"ZoomOut\",[[\"circle\",{cx:\"11\",cy:\"11\",r:\"8\",key:\"4ej97u\"}],[\"line\",{x1:\"21\",x2:\"16.65\",y1:\"21\",y2:\"16.65\",key:\"13gj7c\"}],[\"line\",{x1:\"8\",x2:\"14\",y1:\"11\",y2:\"11\",key:\"durymu\"}]]);\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */function w(){const e=t.useCallback(()=>{const e=localStorage.getItem(\"token\")||\"\";return n({token:e})},[]),i=t.useCallback(()=>{localStorage.removeItem(\"token\"),window.location.reload()},[]),l=t.useCallback(async(t,n,o,l=!1,c=\"\",d=\"mtime\",h=\"desc\",u)=>{try{const m=s.API_URL.endsWith(\"/\")?s.API_URL.slice(0,-1):s.API_URL,f=Math.floor(n).toString(),p=Math.floor(o).toString();let y=`${m}/api/files?vault=${encodeURIComponent(t)}&page=${f}&pageSize=${p}`;l&&(y+=\"&isRecycle=1\"),c&&(y+=`&keyword=${encodeURIComponent(c)}`),d&&\"mtime\"!==d&&(y+=`&sortBy=${d}`),h&&\"desc\"!==h&&(y+=`&sortOrder=${h}`);const x=await fetch(a(y),{method:\"GET\",headers:e()});if(!x.ok)return 508===x.status?i():r.error(\"Network response was not ok\"),void u(null);const g=await x.json();if(g.code>0&&g.code<=200){u(g.data||{list:[],pager:{page:1,pageSize:o,totalRows:0,totalPages:0}})}else 508===g.code?i():r.error(g.message),u(null)}catch(m){r.error(m instanceof Error?m.message:String(m)),u(null)}},[e,i]),c=t.useCallback(async(t,n,o,i)=>{try{const l={vault:t,path:n,pathHash:o},c=s.API_URL.endsWith(\"/\")?s.API_URL.slice(0,-1):s.API_URL,d=await fetch(a(`${c}/api/file`),{method:\"DELETE\",body:JSON.stringify(l),headers:e()});if(!d.ok)throw new Error(\"Network response was not ok\");const h=await d.json();h.code>0&&h.code<=200?(r.success(h.message),i()):r.error(h.message+(h.details?\": \"+h.details.join(\", \"):\"\"))}catch(l){r.error(l instanceof Error?l.message:String(l))}},[e]),d=t.useCallback(async(t,n,o,i)=>{try{const l={vault:t,path:n,pathHash:o},c=s.API_URL.endsWith(\"/\")?s.API_URL.slice(0,-1):s.API_URL,d=await fetch(a(`${c}/api/file/recycle-clear`),{method:\"DELETE\",body:JSON.stringify(l),headers:e()});if(!d.ok)throw new Error(\"Network response was not ok\");const h=await d.json();h.code>0&&h.code<=200?(r.success(h.message),i()):r.error(h.message+(h.details?\": \"+h.details.join(\", \"):\"\"))}catch(l){r.error(l instanceof Error?l.message:String(l))}},[e]),h=t.useCallback(async(t,n)=>{try{const o={vault:t},i=s.API_URL.endsWith(\"/\")?s.API_URL.slice(0,-1):s.API_URL,l=await fetch(a(`${i}/api/file/recycle-clear`),{method:\"DELETE\",body:JSON.stringify(o),headers:e()});if(!l.ok)throw new Error(\"Network response was not ok\");const c=await l.json();c.code>0&&c.code<=200?(r.success(c.message),n()):r.error(c.message)}catch(o){r.error(o instanceof Error?o.message:String(o))}},[e]),u=t.useCallback(async(t,n,o,i)=>{try{const l={vault:t,path:n,pathHash:o},c=s.API_URL.endsWith(\"/\")?s.API_URL.slice(0,-1):s.API_URL,d=await fetch(a(`${c}/api/file/restore`),{method:\"PUT\",body:JSON.stringify(l),headers:e()});if(!d.ok)throw new Error(\"Network response was not ok\");const h=await d.json();h.code>0&&h.code<=200?(r.success(h.message),i()):r.error(h.message+(h.details?\": \"+h.details.join(\", \"):\"\"))}catch(l){r.error(l instanceof Error?l.message:String(l))}},[e]),m=t.useCallback(async(t,n)=>{try{const o=s.API_URL.endsWith(\"/\")?s.API_URL.slice(0,-1):s.API_URL,i=await fetch(a(`${o}/api/file/rename`),{method:\"POST\",body:JSON.stringify(t),headers:e()});if(!i.ok)throw new Error(\"Network response was not ok\");const l=await i.json();l.code>0&&l.code<=200?(r.success(l.message),n()):r.error(l.message)}catch(o){r.error(o instanceof Error?o.message:String(o))}},[e]),f=t.useCallback((e,t,n)=>{const a=s.API_URL.endsWith(\"/\")?s.API_URL.slice(0,-1):s.API_URL,r=localStorage.getItem(\"token\")||\"\",i=o();let l=`${a}/api/file?vault=${encodeURIComponent(e)}&path=${encodeURIComponent(t)}&token=${encodeURIComponent(r)}&lang=${i}`;return n&&(l+=`&pathHash=${n}`),l},[]),p=t.useCallback(async(t,n=\"\",o=\"\",l)=>{try{let c=`${s.API_URL.endsWith(\"/\")?s.API_URL.slice(0,-1):s.API_URL}/api/folders?vault=${encodeURIComponent(t)}`;n&&(c+=`&path=${encodeURIComponent(n)}`),o&&(c+=`&path_hash=${encodeURIComponent(o)}`);const d=await fetch(a(c),{method:\"GET\",headers:e()});if(!d.ok)return 508===d.status?i():r.error(\"Network response was not ok\"),void l(null);const h=await d.json();h.code>0&&h.code<=200?l(h.data||[]):(508===h.code?i():r.error(h.message),l(null))}catch(c){r.error(c instanceof Error?c.message:String(c)),l(null)}},[e,i]),y=t.useCallback(async(t,n=\"\",o=\"\",l,c,d=\"mtime\",h=\"desc\",u)=>{try{const m=s.API_URL.endsWith(\"/\")?s.API_URL.slice(0,-1):s.API_URL,f=Math.floor(l).toString(),p=Math.floor(c).toString();let y=`${m}/api/folder/files?vault=${encodeURIComponent(t)}&page=${f}&pageSize=${p}`;n&&(y+=`&path=${encodeURIComponent(n)}`),o&&(y+=`&path_hash=${encodeURIComponent(o)}`),d&&(y+=`&sortBy=${d}`),h&&(y+=`&sortOrder=${h}`);const x=await fetch(a(y),{method:\"GET\",headers:e()});if(!x.ok)return 508===x.status?i():r.error(\"Network response was not ok\"),void u(null);const g=await x.json();if(g.code>0&&g.code<=200){const e=g.data||{list:[],pager:{page:l,pageSize:c,totalRows:0,totalPages:0}};e.list||(e.list=[]),u(e)}else 508===g.code?i():r.error(g.message),u(null)}catch(m){r.error(m instanceof Error?m.message:String(m)),u(null)}},[e,i]);return t.useMemo(()=>({handleFileList:l,handleDeleteFile:c,handlePermanentDeleteFile:d,handleClearFileRecycle:h,handleRestoreFile:u,handleRenameFile:m,getRawFileUrl:f,handleFolderList:p,handleFolderFiles:y}),[l,c,d,h,u,m,f,p,y])}const v={1:\"#fb464c\",2:\"#e9973f\",3:\"#e0de71\",4:\"#44cf6e\",5:\"#53dfdd\",6:\"#a882ff\"};function j(e){if(e)return v[e]??e}function R(e,t){const n=e.replace(\"#\",\"\");if(/^[0-9a-fA-F]{6}$/.test(n)){return`rgba(${parseInt(n.slice(0,2),16)}, ${parseInt(n.slice(2,4),16)}, ${parseInt(n.slice(4,6),16)}, ${t})`}return e}function b(e,t){const n=e.x+e.width/2,s=e.y+e.height/2;switch(t){case\"top\":return{x:n,y:e.y};case\"bottom\":return{x:n,y:e.y+e.height};case\"left\":return{x:e.x,y:s};case\"right\":return{x:e.x+e.width,y:s};default:return{x:n,y:s}}}function C(e,t){switch(e){case\"top\":return{dx:0,dy:-t};case\"bottom\":return{dx:0,dy:t};case\"left\":return{dx:-t,dy:0};case\"right\":return{dx:t,dy:0};default:return{dx:0,dy:0}}}function N({edge:e,nodeMap:t}){const n=t.get(e.fromNode),s=t.get(e.toNode);if(!n||!s)return null;const a=b(n,e.fromSide),r=b(s,e.toSide),o=Math.abs(r.x-a.x),l=Math.abs(r.y-a.y),c=Math.min(o/2,l/2,100),d=Math.max(c,30),h=C(e.fromSide,d),u=C(e.toSide,d),m=`M ${a.x} ${a.y} C ${a.x+h.dx} ${a.y+h.dy}, ${r.x+u.dx} ${r.y+u.dy}, ${r.x} ${r.y}`,f=j(e.color),p=f??\"default\",y=\"none\"!==e.toEnd,x=\"arrow\"===e.fromEnd;return i.jsxs(\"g\",{children:[i.jsx(\"path\",{d:m,className:\"canvas-edge-path\",style:f?{stroke:f}:void 0,markerEnd:y?`url(#marker-to-${p})`:void 0,markerStart:x?`url(#marker-from-${p})`:void 0}),e.label&&i.jsx(\"text\",{x:(a.x+r.x)/2,y:(a.y+r.y)/2,className:\"canvas-edge-label\",dy:\"-6\",children:e.label})]})}function $({node:e,onNodeClick:t,onWikiLinkClick:n,isDragRef:s}){var a;const r=j(e.color),o=r?{borderColor:r,backgroundColor:R(r,.09)}:void 0;if(\"group\"===e.type)return i.jsx(\"div\",{className:\"canvas-group\",style:{left:e.x,top:e.y,width:e.width,height:e.height,...r?{borderColor:r,background:R(r,.08)}:{}},children:e.label&&i.jsx(\"span\",{className:\"canvas-group-label\",children:e.label})});return i.jsxs(\"div\",{className:\"canvas-node\",style:{left:e.x,top:e.y,width:e.width,height:e.height,...o},onClick:\"text\"!==e.type?()=>{s.current||(\"file\"===e.type&&e.file&&n?n(e.file):\"link\"===e.type&&e.url&&window.open(e.url,\"_blank\",\"noopener,noreferrer\"),null==t||t(e))}:void 0,children:[\"text\"===e.type&&i.jsx(\"div\",{className:\"canvas-node-text markdown-preview\",children:i.jsx(d,{content:e.text??\"\"})}),\"file\"===e.type&&i.jsxs(\"div\",{className:\"canvas-node-file\",children:[i.jsx(h,{className:\"canvas-node-file-icon\"}),i.jsx(\"span\",{className:\"canvas-node-file-name\",children:(null==(a=e.file)?void 0:a.split(\"/\").pop())??e.file})]}),\"link\"===e.type&&i.jsxs(\"div\",{className:\"canvas-node-link\",children:[i.jsx(u,{className:\"canvas-node-link-icon\"}),i.jsx(\"span\",{className:\"canvas-node-link-url\",children:e.url})]})]})}function I({nodes:e,edges:n,viewport:s,onViewportChange:a,onNodeClick:r,onWikiLinkClick:o}){const l=t.useRef(null),[c,d]=t.useState(!1),h=t.useRef(null),u=t.useRef(!1),m=t.useRef([]),f=t.useRef(null),p=t.useMemo(()=>{const t=new Map;for(const n of e)t.set(n.id,n);return t},[e]),y=t.useMemo(()=>e.filter(e=>\"group\"===e.type),[e]),x=t.useMemo(()=>e.filter(e=>\"group\"!==e.type),[e]),g=t.useMemo(()=>{if(0===e.length)return{minX:0,minY:0,w:100,h:100};let t=1/0,n=1/0,s=-1/0,a=-1/0;for(const o of e)t=Math.min(t,o.x),n=Math.min(n,o.y),s=Math.max(s,o.x+o.width),a=Math.max(a,o.y+o.height);const r=500;return{minX:t-r,minY:n-r,w:s-t+1e3,h:a-n+1e3}},[e]),k=t.useMemo(()=>{const e=new Map;e.set(\"default\",\"currentColor\");for(const t of n){const n=j(t.color);n&&e.set(n,n)}return e},[n]),w=e=>Math.min(3,Math.max(.1,e)),v=t.useCallback(e=>{var t;if(e.preventDefault(),e.ctrlKey||e.metaKey){const n=null==(t=l.current)?void 0:t.getBoundingClientRect();if(!n)return;const r=e.clientX-n.left,o=e.clientY-n.top,i=e.deltaY>0?.9:1.1,c=w(s.zoom*i),d=c/s.zoom;a({x:r-(r-s.x)*d,y:o-(o-s.y)*d,zoom:c})}else a({...s,x:s.x-e.deltaX,y:s.y-e.deltaY})},[s,a]),R=t.useCallback(e=>{var t,n;if(0!==e.button)return;const a=m.current;a.push(e.nativeEvent),1===a.length&&(d(!0),u.current=!1,h.current={x:e.clientX,y:e.clientY,vx:s.x,vy:s.y},null==(n=(t=e.target).setPointerCapture)||n.call(t,e.pointerId))},[s.x,s.y]),b=t.useCallback(e=>{var t;const n=m.current,r=n.findIndex(t=>t.pointerId===e.pointerId);if(r>=0&&(n[r]=e.nativeEvent),2===n.length){const e=Math.hypot(n[0].clientX-n[1].clientX,n[0].clientY-n[1].clientY);if(null!==f.current){const r=e/f.current,o=null==(t=l.current)?void 0:t.getBoundingClientRect();if(o){const e=(n[0].clientX+n[1].clientX)/2-o.left,t=(n[0].clientY+n[1].clientY)/2-o.top,i=w(s.zoom*r),l=i/s.zoom;a({x:e-(e-s.x)*l,y:t-(t-s.y)*l,zoom:i})}}f.current=e}else if(1===n.length&&h.current){const t=e.clientX-h.current.x,n=e.clientY-h.current.y;(Math.abs(t)>3||Math.abs(n)>3)&&(u.current=!0),a({...s,x:h.current.vx+t,y:h.current.vy+n})}},[s,a]),C=t.useCallback(e=>{const t=m.current,n=t.findIndex(t=>t.pointerId===e.pointerId);n>=0&&t.splice(n,1),t.length<2&&(f.current=null),0===t.length&&(d(!1),h.current=null)},[]);return t.useEffect(()=>{const e=l.current;if(!e)return;const t=e=>e.preventDefault();return e.addEventListener(\"wheel\",t,{passive:!1}),()=>e.removeEventListener(\"wheel\",t)},[]),i.jsx(\"div\",{ref:l,className:\"canvas-container\"+(c?\" is-dragging\":\"\"),onWheel:v,onPointerDown:R,onPointerMove:b,onPointerUp:C,onPointerCancel:C,children:i.jsxs(\"div\",{className:\"canvas-transform-layer\",style:{transform:`translate(${s.x}px, ${s.y}px) scale(${s.zoom})`},children:[y.map(e=>i.jsx($,{node:e,onNodeClick:r,onWikiLinkClick:o,isDragRef:u},e.id)),i.jsxs(\"svg\",{className:\"canvas-edge-layer\",style:{left:g.minX,top:g.minY,width:g.w,height:g.h,color:\"hsl(var(--foreground))\"},viewBox:`${g.minX} ${g.minY} ${g.w} ${g.h}`,children:[i.jsx(\"defs\",{children:Array.from(k.entries()).map(([e,t])=>i.jsxs(\"g\",{children:[i.jsx(\"marker\",{id:`marker-to-${e}`,markerWidth:\"12\",markerHeight:\"10\",refX:\"11\",refY:\"5\",orient:\"auto\",markerUnits:\"userSpaceOnUse\",children:i.jsx(\"polygon\",{points:\"0 0, 12 5, 0 10\",fill:t})}),i.jsx(\"marker\",{id:`marker-from-${e}`,markerWidth:\"12\",markerHeight:\"10\",refX:\"1\",refY:\"5\",orient:\"auto\",markerUnits:\"userSpaceOnUse\",children:i.jsx(\"polygon\",{points:\"12 0, 0 5, 12 10\",fill:t})})]},e))}),n.map(e=>i.jsx(N,{edge:e,nodeMap:p},e.id))]}),x.map(e=>i.jsx($,{node:e,onNodeClick:r,onWikiLinkClick:o,isDragRef:u},e.id))]})})}function M(e,t,n){if(0===e.length||0===t||0===n)return{x:0,y:0,zoom:1};let s=1/0,a=1/0,r=-1/0,o=-1/0;for(const h of e)s=Math.min(s,h.x),a=Math.min(a,h.y),r=Math.max(r,h.x+h.width),o=Math.max(o,h.y+h.height);const i=r-s,l=o-a,c=Math.min((t-80)/i,(n-80)/l,1.5),d=Math.max(.1,c);return{x:t/2-(s+r)/2*d,y:n/2-(a+o)/2*d,zoom:d}}function L({vault:e,note:s,onBack:a,onWikiLinkClick:r,isRecycle:o}){var d;const{getRawFileUrl:h}=w(),[u,f]=t.useState(null),[p,x]=t.useState(null),[v,j]=t.useState(!1),[R,b]=t.useState({x:0,y:0,zoom:1}),[C,N]=t.useState({w:0,h:0}),$=t.useRef(null),L=t.useRef(null);t.useEffect(()=>{const e=$.current;if(!e)return;const t=()=>{const t=e.getBoundingClientRect(),n=window.innerHeight-t.top-16;e.style.height=`${Math.max(200,n)}px`};return t(),window.addEventListener(\"resize\",t),()=>window.removeEventListener(\"resize\",t)},[]),t.useEffect(()=>{const e=L.current;if(!e)return;const t=()=>{const{clientWidth:t,clientHeight:n}=e;t>0&&n>0&&N(e=>e.w===t&&e.h===n?e:{w:t,h:n})};requestAnimationFrame(t);const n=new ResizeObserver(t);return n.observe(e),()=>n.disconnect()},[]),t.useEffect(()=>{if(!s)return;let t=!1;j(!0),x(null);let a=h(e,s.path,s.pathHash);return o&&(a+=(a.includes(\"?\")?\"&\":\"?\")+\"isRecycle=1\"),fetch(a,{cache:\"no-store\",headers:n({token:localStorage.getItem(\"token\"),includeContentType:!1,includeDomain:!1,includeLang:!1})}).then(e=>{if(!e.ok)throw new Error(`HTTP ${e.status}`);return e.text()}).then(e=>{if(!t)try{const t=function(e){const t=JSON.parse(e);return{nodes:Array.isArray(t.nodes)?t.nodes.map(e=>({...e,x:Number(e.x)||0,y:Number(e.y)||0,width:Number(e.width)||100,height:Number(e.height)||50})):[],edges:Array.isArray(t.edges)?t.edges:[]}}(e);f(t)}catch{x(\"Canvas JSON parse error\"),f(null)}}).catch(e=>{t||(x(e.message??\"Failed to load canvas\"),f(null))}).finally(()=>{t||j(!1)}),()=>{t=!0}},[e,s,h,o]),t.useEffect(()=>{u&&C.w>0&&C.h>0&&b(M(u.nodes,C.w,C.h))},[u,C]);const U=t.useCallback(()=>{u&&C.w>0&&C.h>0&&b(M(u.nodes,C.w,C.h))},[u,C]),E=t.useCallback(()=>{b(e=>{const t=Math.min(3,1.2*e.zoom),n=t/e.zoom,s=C.w/2,a=C.h/2;return{x:s-(s-e.x)*n,y:a-(a-e.y)*n,zoom:t}})},[C]),S=t.useCallback(()=>{b(e=>{const t=Math.max(.1,.8*e.zoom),n=t/e.zoom,s=C.w/2,a=C.h/2;return{x:s-(s-e.x)*n,y:a-(a-e.y)*n,zoom:t}})},[C]),A=(null==(d=null==s?void 0:s.path)?void 0:d.split(\"/\").pop())??\"canvas\";return i.jsxs(\"div\",{ref:$,className:\"w-full flex flex-col\",children:[i.jsxs(\"div\",{className:\"canvas-toolbar\",children:[i.jsxs(\"div\",{className:\"canvas-toolbar-left\",children:[i.jsx(l,{variant:\"ghost\",size:\"icon\",onClick:a,className:\"shrink-0 rounded-lg sm:rounded-xl h-7 w-7 sm:h-10 sm:w-10\",children:i.jsx(m,{className:\"h-4 w-4 sm:h-5 sm:w-5\"})}),i.jsx(\"span\",{className:\"canvas-toolbar-title\",children:A})]}),i.jsxs(\"div\",{className:\"canvas-toolbar-right\",children:[i.jsxs(\"span\",{className:\"canvas-zoom-label\",children:[Math.round(100*R.zoom),\"%\"]}),i.jsx(c,{content:\"Zoom Out\",side:\"bottom\",delay:200,children:i.jsx(l,{variant:\"outline\",size:\"icon\",onClick:S,className:\"rounded-lg sm:rounded-xl h-7 w-7 sm:h-10 sm:w-10\",children:i.jsx(k,{className:\"h-3.5 w-3.5 sm:h-4 sm:w-4\"})})}),i.jsx(c,{content:\"Zoom In\",side:\"bottom\",delay:200,children:i.jsx(l,{variant:\"outline\",size:\"icon\",onClick:E,className:\"rounded-lg sm:rounded-xl h-7 w-7 sm:h-10 sm:w-10\",children:i.jsx(g,{className:\"h-3.5 w-3.5 sm:h-4 sm:w-4\"})})}),i.jsx(c,{content:\"Fit to View\",side:\"bottom\",delay:200,children:i.jsx(l,{variant:\"outline\",size:\"icon\",onClick:U,className:\"rounded-lg sm:rounded-xl h-7 w-7 sm:h-10 sm:w-10\",children:i.jsx(y,{className:\"h-3.5 w-3.5 sm:h-4 sm:w-4\"})})})]})]}),i.jsxs(\"div\",{ref:L,className:\"flex-1 min-h-0\",children:[v&&i.jsx(\"div\",{className:\"canvas-loading\",children:i.jsxs(\"div\",{className:\"flex flex-col items-center gap-2\",children:[i.jsx(\"div\",{className:\"animate-spin rounded-full h-8 w-8 border-b-2 border-primary\"}),i.jsx(\"span\",{children:\"Loading...\"})]})}),p&&i.jsx(\"div\",{className:\"canvas-error\",children:i.jsx(\"span\",{children:p})}),!v&&!p&&u&&0===u.nodes.length&&i.jsx(\"div\",{className:\"canvas-empty\",children:i.jsx(\"span\",{children:\"Empty canvas\"})}),!v&&!p&&u&&u.nodes.length>0&&i.jsx(I,{nodes:u.nodes,edges:u.edges,vault:e,viewport:R,onViewportChange:b,onWikiLinkClick:r})]})]})}export{m as A,L as C,p as F,x as R,f as a,w as u};\n"
  },
  {
    "path": "frontend/assets/checkbox-DhTHgmeh.js",
    "content": "import{r as e,j as r,ah as t,ao as o,aj as n,ag as a,aB as s,an as c,aC as d,f as i,a5 as l}from\"./font-loader-CIrh3KnA.js\";import{u}from\"./select-CJF_alSt.js\";var f=\"Checkbox\",[p]=t(f),[h,b]=p(f);function m(t){const{__scopeCheckbox:o,checked:n,children:s,defaultChecked:c,disabled:d,form:i,name:l,onCheckedChange:u,required:p,value:b=\"on\",internal_do_not_use_render:m}=t,[k,C]=a({prop:n,defaultProp:c??!1,onChange:u,caller:f}),[x,v]=e.useState(null),[y,_]=e.useState(null),g=e.useRef(!1),R=!x||(!!i||!!x.closest(\"form\")),E={checked:k,disabled:d,setChecked:C,control:x,setControl:v,name:l,form:i,value:b,hasConsumerStoppedPropagationRef:g,required:p,defaultChecked:!w(c)&&c,isFormControl:R,bubbleInput:y,setBubbleInput:_};return r.jsx(h,{scope:o,...E,children:j(m)?m(E):s})}var k=\"CheckboxTrigger\",C=e.forwardRef(({__scopeCheckbox:t,onKeyDown:o,onClick:a,...d},i)=>{const{control:l,value:u,disabled:f,checked:p,required:h,setControl:m,setChecked:C,hasConsumerStoppedPropagationRef:x,isFormControl:v,bubbleInput:y}=b(k,t),_=s(i,m),g=e.useRef(p);return e.useEffect(()=>{const e=null==l?void 0:l.form;if(e){const r=()=>C(g.current);return e.addEventListener(\"reset\",r),()=>e.removeEventListener(\"reset\",r)}},[l,C]),r.jsx(n.button,{type:\"button\",role:\"checkbox\",\"aria-checked\":w(p)?\"mixed\":p,\"aria-required\":h,\"data-state\":R(p),\"data-disabled\":f?\"\":void 0,disabled:f,value:u,...d,ref:_,onKeyDown:c(o,e=>{\"Enter\"===e.key&&e.preventDefault()}),onClick:c(a,e=>{C(e=>!!w(e)||!e),y&&v&&(x.current=e.isPropagationStopped(),x.current||e.stopPropagation())})})});C.displayName=k;var x=e.forwardRef((e,t)=>{const{__scopeCheckbox:o,name:n,checked:a,defaultChecked:s,required:c,disabled:d,value:i,onCheckedChange:l,form:u,...f}=e;return r.jsx(m,{__scopeCheckbox:o,checked:a,defaultChecked:s,disabled:d,required:c,onCheckedChange:l,name:n,form:u,value:i,internal_do_not_use_render:({isFormControl:e})=>r.jsxs(r.Fragment,{children:[r.jsx(C,{...f,ref:t,__scopeCheckbox:o}),e&&r.jsx(g,{__scopeCheckbox:o})]})})});x.displayName=f;var v=\"CheckboxIndicator\",y=e.forwardRef((e,t)=>{const{__scopeCheckbox:a,forceMount:s,...c}=e,d=b(v,a);return r.jsx(o,{present:s||w(d.checked)||!0===d.checked,children:r.jsx(n.span,{\"data-state\":R(d.checked),\"data-disabled\":d.disabled?\"\":void 0,...c,ref:t,style:{pointerEvents:\"none\",...e.style}})})});y.displayName=v;var _=\"CheckboxBubbleInput\",g=e.forwardRef(({__scopeCheckbox:t,...o},a)=>{const{control:c,hasConsumerStoppedPropagationRef:i,checked:l,defaultChecked:f,required:p,disabled:h,name:m,value:k,form:C,bubbleInput:x,setBubbleInput:v}=b(_,t),y=s(a,v),g=u(l),j=d(c);e.useEffect(()=>{const e=x;if(!e)return;const r=window.HTMLInputElement.prototype,t=Object.getOwnPropertyDescriptor(r,\"checked\").set,o=!i.current;if(g!==l&&t){const r=new Event(\"click\",{bubbles:o});e.indeterminate=w(l),t.call(e,!w(l)&&l),e.dispatchEvent(r)}},[x,g,l,i]);const R=e.useRef(!w(l)&&l);return r.jsx(n.input,{type:\"checkbox\",\"aria-hidden\":!0,defaultChecked:f??R.current,required:p,disabled:h,name:m,value:k,form:C,...o,tabIndex:-1,ref:y,style:{...o.style,...j,position:\"absolute\",pointerEvents:\"none\",opacity:0,margin:0,transform:\"translateX(-100%)\"}})});function j(e){return\"function\"==typeof e}function w(e){return\"indeterminate\"===e}function R(e){return w(e)?\"indeterminate\":e?\"checked\":\"unchecked\"}g.displayName=_;const E=e.forwardRef(({className:e,...t},o)=>r.jsx(x,{ref:o,className:i(\"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground\",e),...t,children:r.jsx(y,{className:i(\"flex items-center justify-center text-current\"),children:r.jsx(l,{className:\"h-4 w-4\"})})}));E.displayName=x.displayName;export{E as C};\n"
  },
  {
    "path": "frontend/assets/circle-alert-EFzISefA.js",
    "content": "import{c as e}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const y=e(\"CircleAlert\",[[\"circle\",{cx:\"12\",cy:\"12\",r:\"10\",key:\"1mglay\"}],[\"line\",{x1:\"12\",x2:\"12\",y1:\"8\",y2:\"12\",key:\"1pkeuh\"}],[\"line\",{x1:\"12\",x2:\"12.01\",y1:\"16\",y2:\"16\",key:\"4dfq90\"}]]);export{y as C};\n"
  },
  {
    "path": "frontend/assets/circle-alert-EFzISefA.js.br",
    "content": "\u001b\u0001@lN_.\n\u0004\u0004\u0012pL}\"KӁtQzFY6xxm\u0018\u0016<tKp\u000f]Qz,\u0017UK-DQ\u0012RBڟ0I[¥<\u001b<dQ7mQ\u0006\u0001eFAb-4S\u0001(-8$E\"NWe\u0015Wxtf~+f]LJGx\u0007%Y{!C\u0014_#%/\u0017\u0015\u0015y撋\u0002rۗt\u0018\u0006qh'^\n{ɛ\u0012\u0001EO}yovQV젘^TgG\u000e\u001b+_Ao=\n"
  },
  {
    "path": "frontend/assets/clock-C9LPHszx.js",
    "content": "import{c as o}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const c=o(\"Clock\",[[\"circle\",{cx:\"12\",cy:\"12\",r:\"10\",key:\"1mglay\"}],[\"polyline\",{points:\"12 6 12 12 16 14\",key:\"68esgv\"}]]);export{c as C};\n"
  },
  {
    "path": "frontend/assets/copy-CEhXannp.js",
    "content": "import{c as a}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const t=a(\"Cloud\",[[\"path\",{d:\"M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z\",key:\"p7xjir\"}]]),e=a(\"Copy\",[[\"rect\",{width:\"14\",height:\"14\",x:\"8\",y:\"8\",rx:\"2\",ry:\"2\",key:\"17jyea\"}],[\"path\",{d:\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\",key:\"zix9uf\"}]]);\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */export{e as C,t as a};\n"
  },
  {
    "path": "frontend/assets/database-eyf5nvY6.js",
    "content": "import{c as e}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const a=e(\"ArrowDownWideNarrow\",[[\"path\",{d:\"m3 16 4 4 4-4\",key:\"1co6wj\"}],[\"path\",{d:\"M7 20V4\",key:\"1yoxec\"}],[\"path\",{d:\"M11 4h10\",key:\"1w87gc\"}],[\"path\",{d:\"M11 8h7\",key:\"djye34\"}],[\"path\",{d:\"M11 12h4\",key:\"q8tih4\"}]]),t=e(\"Database\",[[\"ellipse\",{cx:\"12\",cy:\"5\",rx:\"9\",ry:\"3\",key:\"msslwz\"}],[\"path\",{d:\"M3 5V19A9 3 0 0 0 21 19V5\",key:\"1wlel7\"}],[\"path\",{d:\"M3 12A9 3 0 0 0 21 12\",key:\"mv7ke4\"}]]);\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */export{a as A,t as D};\n"
  },
  {
    "path": "frontend/assets/download-CKtDCbjj.js",
    "content": "import{c as o}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const e=o(\"Download\",[[\"path\",{d:\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\",key:\"ih7n3h\"}],[\"polyline\",{points:\"7 10 12 15 17 10\",key:\"2ggqvy\"}],[\"line\",{x1:\"12\",x2:\"12\",y1:\"15\",y2:\"3\",key:\"1vk2je\"}]]);export{e as D};\n"
  },
  {
    "path": "frontend/assets/en-vU35wTjd.js",
    "content": "const e={\"ui.common.title\":\"Fast Note Sync\",\"ui.common.subtitle\":\"High-performance, low-latency note synchronization, management, REST service\",\"ui.common.footerTitle\":\"Built with Golang + Websocket + Sqlite + React\\nRequires <a href='https://github.com/haierkeys/obsidian-fast-note-sync' target='_blank'>Obsidian Fast Note Sync Plugin</a>\",\"ui.common.loading\":\"Loading...\",\"ui.common.downloading\":\"Downloading...\",\"ui.common.actions\":\"Actions\",\"ui.common.save\":\"Save\",\"ui.common.add\":\"Add\",\"ui.common.edit\":\"Edit\",\"ui.common.view\":\"View\",\"ui.common.viewDetail\":\"View Details\",\"ui.common.delete\":\"Delete\",\"ui.common.restore\":\"Restore\",\"ui.common.permanentDelete\":\"Permanent Delete\",\"ui.common.clear\":\"Clear\",\"ui.common.batchPermanentDelete\":\"Batch Permanent Delete\",\"ui.common.batchPermanentDeleteConfirm\":\"Are you sure you want to permanently delete the selected {{count}} items? This action cannot be undone!\",\"ui.common.search\":\"Search\",\"ui.common.refresh\":\"Refresh\",\"ui.common.retry\":\"Retry\",\"ui.common.refreshSuccess\":\"Refresh successful\",\"ui.common.close\":\"Close\",\"ui.common.cancel\":\"Cancel\",\"ui.common.confirm\":\"Confirm\",\"ui.common.success\":\"Success\",\"ui.common.error\":\"Error\",\"ui.common.warning\":\"Warning\",\"ui.common.info\":\"Info\",\"ui.common.total\":\"Total\",\"ui.common.isEnabled\":\"Enabled\",\"ui.common.isDisabled\":\"Disabled\",\"ui.common.switchLanguage\":\"Switch Language\",\"ui.common.comingSoon\":\"Coming Soon...\",\"ui.common.comingSoonDescription\":\"This feature is under development. Stay tuned...\",\"ui.common.helpAndSupport\":\"Help & Support\",\"ui.common.githubIssue\":\"Feedback\",\"ui.common.githubIssueDesc\":\"Submit Bug or Feature Request\",\"ui.common.telegramGroup\":\"Community\",\"ui.common.telegramGroupDesc\":\"Telegram community discussion and help\",\"ui.common.planned\":\"Planned\",\"ui.common.unknown\":\"Unknown\",\"ui.common.copied\":\"Copied\",\"ui.common.restoring\":\"Restoring...\",\"ui.common.rename\":\"Rename\",\"ui.common.toggleTheme\":\"Toggle Theme\",\"ui.common.previous\":\"Previous\",\"ui.common.next\":\"Next\",\"ui.common.page\":\"Page\",\"ui.common.of\":\"of\",\"ui.common.to\":\"to\",\"ui.common.perPage\":\"per page\",\"ui.common.copy\":\"Copy\",\"ui.common.createdAt\":\"Created At\",\"ui.common.updatedAt\":\"Updated At\",\"ui.common.noSearchResults\":\"No matching results found\",\"ui.common.selectAll\":\"Select All\",\"ui.common.items\":\"items\",\"ui.common.count\":\"count\",\"ui.common.saveSuccess\":\"Saved successfully\",\"ui.common.selectVault\":\"Select Vault\",\"ui.common.sourceCode\":\"Source Code\",\"ui.common.wideMode\":\"Wide Mode\",\"ui.common.narrowMode\":\"Normal Width\",\"ui.common.fold\":\"Collapse\",\"ui.common.noChange\":\"No changes made\",\"ui.common.na\":\"N/A\",\"ui.common.name\":\"Name\",\"ui.auth.login\":\"Login\",\"ui.auth.logout\":\"Logout\",\"ui.auth.register\":\"Register\",\"ui.auth.registerButton\":\"Register\",\"ui.auth.registerClosed\":\"User registration is closed\",\"ui.auth.credentials\":\"Username (or Email)\",\"ui.auth.username\":\"Username\",\"ui.auth.password\":\"Password\",\"ui.auth.remember\":\"Remember me\",\"ui.auth.confirmPassword\":\"Confirm Password\",\"ui.auth.email\":\"Email\",\"ui.auth.credentialsPlaceholder\":\"Enter username (or email)\",\"ui.auth.usernamePlaceholder\":\"Enter username\",\"ui.auth.passwordPlaceholder\":\"Enter password\",\"ui.auth.emailPlaceholder\":\"Enter email\",\"ui.auth.confirmPasswordPlaceholder\":\"Confirm password\",\"ui.auth.credentialsRequired\":\"Username (or email) cannot be empty\",\"ui.auth.passwordMinLength\":\"Password must be at least 6 characters\",\"ui.auth.usernameMinLength\":\"Username must be at least 3 characters\",\"ui.auth.emailInvalid\":\"Please enter a valid email address\",\"ui.auth.passwordMismatch\":\"Passwords do not match\",\"ui.auth.changePassword\":\"Change Password\",\"ui.auth.currentPassword\":\"Current Password\",\"ui.auth.newPassword\":\"New Password\",\"ui.auth.confirmNewPassword\":\"Confirm New Password\",\"ui.auth.passwordChangedSuccess\":\"Password changed successfully\",\"ui.auth.passwordChangeFailed\":\"Failed to change password\",\"ui.auth.sessionExpired\":\"User session expired, please log in again\",\"ui.auth.submitting\":\"Submitting...\",\"ui.auth.unknownUser\":\"Unknown User\",\"ui.auth.userUid\":\"User UID: {{uid}}\",\"ui.auth.loginRequestFailed\":\"Login request failed, please check network status\",\"ui.auth.registerRequestFailed\":\"Registration failed, please try again\",\"ui.user.password\":\"Access Password\",\"ui.nav.navigation\":\"Navigation\",\"ui.nav.menuDashboard\":\"Dashboard\",\"ui.nav.menuVaults\":\"Vaults\",\"ui.nav.menuNotes\":\"Note Management\",\"ui.nav.menuTrash\":\"Trash\",\"ui.nav.menuSync\":\"Backup & Sync\",\"ui.nav.menuSettings\":\"System Settings\",\"ui.nav.menuGit\":\"Git Automation\",\"ui.nav.menuFiles\":\"Attachment Management\",\"ui.nav.menuShares\":\"Share Management\",\"ui.nav.menuSettingsBrowser\":\"Vault Configuration Files\",\"ui.nav.menuSyncLogs\":\"Vault Update Logs\",\"ui.nav.mainNavigation\":\"Navigation\",\"ui.vault.vault\":\"Vault\",\"ui.vault.title\":\"Vault Management\",\"ui.vault.add\":\"Add Vault\",\"ui.vault.edit\":\"Edit Vault\",\"ui.vault.delete\":\"Delete Vault\",\"ui.vault.name\":\"Vault Name\",\"ui.vault.nameRequired\":\"Vault name cannot be empty\",\"ui.vault.confirmDelete\":\"Are you sure you want to delete this vault? This action cannot be undone!\",\"ui.vault.noVaults\":\"No vaults yet\",\"ui.vault.count\":\"Total {{count}} vaults\",\"ui.vault.searchPlaceholder\":\"Search vaults...\",\"ui.vault.note\":\"Note\",\"ui.vault.attachmentCount\":\"Attachments\",\"ui.vault.totalSize\":\"Total Size: {{size}}\",\"ui.vault.authTokenConfig\":\"Authorize Client\",\"ui.vault.copyConfig\":\"Copy Connection Config\",\"ui.vault.copyConfigSuccess\":\"Configuration copied to clipboard\",\"ui.vault.copyConfigError\":\"Copy failed, please select and copy manually\",\"ui.vault.oneClickImport\":\"One-click Authorize Obsidian\",\"ui.vault.pleaseCreateVault\":\"Please create a vault or authorize configuration to Obsidian to create automatically\",\"ui.vault.createVaultFirst\":\"Please create a vault first, then manage it\",\"ui.vault.goToVaultManagement\":\"Go to Vault Management\",\"ui.vault.setAsDefault\":\"Set as Default\",\"ui.note.note\":\"Note\",\"ui.note.notes\":\"Note List\",\"ui.note.newNote\":\"New Note\",\"ui.note.noNotes\":\"No notes yet\",\"ui.note.viewNote\":\"View Note\",\"ui.note.editNote\":\"Edit Note\",\"ui.note.search\":\"Search\",\"ui.note.searchPlaceholder\":\"Search notes...\",\"ui.note.noteTitlePlaceholder\":\"Note title (e.g., note.md)\",\"ui.note.noteContentPlaceholder\":\"Enter note content...\",\"ui.note.noteCount\":\"Note count\",\"ui.note.noteTitleRequired\":\"Title cannot be empty\",\"ui.note.results\":\"notes\",\"ui.note.saving\":\"Saving...\",\"ui.note.lastSavedAt\":\"Last saved\",\"ui.note.renameNote\":\"Rename note\",\"ui.note.renameNotePlaceholder\":\"Enter new note name (e.g., new-note.md)\",\"ui.note.renameSuccess\":\"Note renamed successfully\",\"ui.note.deleteNoteConfirm\":'Are you sure you want to delete the note \"{{title}}\"?',\"ui.note.permanentDeleteConfirm\":'Are you sure you want to permanently delete the note \"{{title}}\"? This action cannot be undone!',\"ui.note.restoreNoteConfirm\":'Are you sure you want to restore the note \"{{title}}\"?',\"ui.note.clearRecycleConfirm\":\"Are you sure you want to empty the note recycle bin? This action cannot be undone!\",\"ui.note.noVaultsForNotes\":\"No note vaults yet\",\"ui.note.searchPath\":\"Path\",\"ui.note.searchContentMode\":\"Content\",\"ui.note.sortByBy\":\"Sort by\",\"ui.note.sortByMtime\":\"Modification time\",\"ui.note.sortByCtime\":\"Creation time\",\"ui.note.sortByPath\":\"Path\",\"ui.note.sortAsc\":\"Ascending\",\"ui.note.sortDesc\":\"Descending\",\"ui.note.viewFlat\":\"Flat view\",\"ui.note.viewFolder\":\"Folder view\",\"ui.note.editorHr\":\"Horizontal rule\",\"ui.note.exportPdfPlanned\":\"PDF export feature in development...\",\"ui.note.undo\":\"Undo\",\"ui.note.redo\":\"Redo\",\"ui.note.cut\":\"Cut\",\"ui.note.copy\":\"Copy\",\"ui.note.paste\":\"Paste\",\"ui.note.selectAll\":\"Select all\",\"ui.note.fullscreen\":\"Fullscreen\",\"ui.note.exitFullscreen\":\"Exit fullscreen\",\"ui.note.contextMenu\":\"Context menu\",\"ui.note.loadingEditor\":\"Loading editor...\",\"ui.note.unsavedContentWithoutTitle\":\"Content not saved because title is empty.\",\"ui.note.wikiLinkNotFound\":'Note \"{{target}}\" not found',\"ui.history.title\":\"Note history\",\"ui.history.description\":\"View and restore historical versions of notes\",\"ui.history.version\":\"Version\",\"ui.history.versionLabel\":\"Version\",\"ui.history.time\":\"Update time\",\"ui.history.action\":\"Action\",\"ui.history.view\":\"View\",\"ui.history.count\":\"{{count}} history records in total\",\"ui.history.loading\":\"Loading...\",\"ui.history.noHistory\":\"No history records yet\",\"ui.history.clientSource\":\"Client\",\"ui.history.diffDetails\":\"Version v{{version}} diff details\",\"ui.history.showDiffOnly\":\"Show diff only\",\"ui.history.showOriginalContent\":\"Content before modification\",\"ui.history.diffLegendAdd\":\"Added\",\"ui.history.diffLegendDel\":\"Deleted\",\"ui.history.restore\":\"Restore\",\"ui.history.restoreToVersion\":\"Restore to this version\",\"ui.history.restoreVersionConfirmTitle\":\"Confirm restore\",\"ui.history.restoreVersionConfirmDesc\":\"Are you sure you want to restore the note to version v{{version}}? Current content will be overwritten but will be automatically saved as a new history version.\",\"ui.history.restoring\":\"Restoring...\",\"ui.file.file\":\"Attachment\",\"ui.file.files\":\"Attachment list\",\"ui.file.noFiles\":\"No attachments yet\",\"ui.file.searchFilePlaceholder\":\"Search attachments...\",\"ui.file.deleteFileConfirm\":'Are you sure you want to delete the attachment \"{{title}}\"?',\"ui.file.restoreFileConfirm\":'Are you sure you want to restore the attachment \"{{title}}\"?',\"ui.file.permanentDeleteConfirm\":'Are you sure you want to permanently delete the attachment \"{{title}}\"? This action cannot be undone!',\"ui.file.clearRecycleConfirm\":\"Are you sure you want to empty the attachment recycle bin? This action cannot be undone!\",\"ui.file.attachmentCount\":\"Attachments\",\"ui.file.results\":\"attachment items\",\"ui.file.searchPlaceholder\":\"Search attachments...\",\"ui.file.totalSize\":\"Total {{size}}\",\"ui.file.renameFile\":\"Rename Attachment\",\"ui.file.renameFilePlaceholder\":\"Enter new attachment name\",\"ui.file.renameSuccess\":\"Attachment renamed successfully\",\"ui.file.size\":\"Space Used\",\"ui.file.fileDetail\":\"Attachment Details\",\"ui.file.imagePreview\":\"Image Preview\",\"ui.file.audioPreview\":\"Audio Playback\",\"ui.file.videoPreview\":\"Video Playback\",\"ui.file.pdfPreview\":\"PDF Document\",\"ui.file.codePreview\":\"Script Code\",\"ui.file.unsupportedPreview\":\"This file type does not support direct preview yet\",\"ui.file.openInNewWindow\":\"Open in New Window\",\"ui.file.browserDownload\":\"Browser Download\",\"ui.file.batchRestore\":\"Batch Restore\",\"ui.file.batchPermanentDelete\":\"Batch Permanent Delete\",\"ui.file.selectedCount\":\"{{count}} items selected\",\"ui.file.batchRestoreConfirm\":\"Are you sure you want to restore the selected {{count}} items?\",\"ui.file.batchPermanentDeleteConfirm\":\"Are you sure you want to permanently delete the selected {{count}} items? This action cannot be undone!\",\"ui.file.noVaultsForFiles\":\"No vaults yet\",\"ui.settings.systemConfig\":\"Short Link Sharing\",\"ui.settings.securityConfig\":\"Security Token\",\"ui.settings.noteRelatedConfig\":\"Note Attachments\",\"ui.settings.fontConfig\":\"General\",\"ui.settings.saveSettings\":\"Save Settings\",\"ui.settings.saveSuccess\":\"Settings saved successfully\",\"ui.settings.saveFailed\":\"Failed to save settings\",\"ui.settings.fontSet\":\"WebGui Font Settings\",\"ui.settings.authTokenKey\":\"User Service Token Encryption Obfuscation String\",\"ui.settings.tokenExpiry\":\"User Service Token Expiry Time\",\"ui.settings.shareTokenKey\":\"Share Token Encryption Obfuscation String\",\"ui.settings.shareTokenExpiry\":\"Share Token Expiry Time\",\"ui.settings.registerIsEnable\":\"Open Registration\",\"ui.settings.fileChunkSize\":\"Attachment Upload Chunk Size\",\"ui.settings.softDeleteRetentionTime\":\"Recycle Bin (Soft Delete) Retention Time\",\"ui.settings.uploadSessionTimeout\":\"Attachment Upload Session Timeout\",\"ui.settings.historyKeepVersions\":\"Number of Note History Versions to Keep\",\"ui.settings.historySaveDelay\":\"Note History Save Delay\",\"ui.settings.historySaveDelayFormatError\":\"Invalid history save delay format\",\"ui.settings.adminUid\":\"System Settings Access Restriction (User UID)\",\"ui.settings.adminUidDesc\":\"Specify the UID with admin privileges. 0 means all logged-in users can manage. Default: 0\",\"ui.settings.onlyAdminAccess\":\"Only administrators can access this page\",\"ui.settings.fontSetDesc\":\"Set the WebGui interface font. Leave empty to use the system default font.\\nSupports <u>remote CSS font stylesheets</u> or <u>presets CSS stylesheet keywords</u> or <u>font network addresses</u> and <u>local fonts</u> (placed in the storage/user_static/ directory). Default: empty.\\n\\nExamples: \\n&nbsp;&nbsp;- Remote CSS font stylesheet: <b>https://fonts.googleapis.com/css2?family=Noto+Sans+SC&display=swap</b>\\n&nbsp;&nbsp;- Preset CSS stylesheet keyword: <b>local</b> or <b>remote</b>\\n&nbsp;&nbsp;- Local preset CSS font stylesheet: <b>/static/fonts/font.css</b>\\n&nbsp;&nbsp;- Font network address: <b>https://ik.imagekit.io/name/your-font.woff2</b>\\n&nbsp;&nbsp;- Local font: <b>/user_static/your-font.woff2</b>, requires placing the font file <u>your-font.woff2</u> in the storage/user_static/ directory\",\"ui.settings.authTokenKeyDesc\":\"This setting participates in the generation of all user service tokens. For server security after the first deployment, <u><b>it is strongly recommended to modify this item</b></u>. Once modified, existing tokens will immediately become invalid, and WebGui will require re-login.\",\"ui.settings.tokenExpiryDesc\":\"Expiry time for user service tokens. Supported formats: 7d (days), 24h (hours), 30m (minutes). Default: <b>365d</b>\",\"ui.settings.shareTokenKeyDesc\":\"Encryption obfuscation Key used to generate share tokens. Modifying this will invalidate existing share links. Default: <b>fns</b>\",\"ui.settings.shareTokenExpiryDesc\":\"Expiry time for share tokens. Supported formats: 7d (days), 24h (hours), 30m (minutes). Default: <b>30d</b>\",\"ui.settings.registerIsEnableDesc\":\"Whether to allow new users to create accounts on this service. Default: <b>Enabled</b>\",\"ui.settings.fileChunkSizeDesc\":\"Chunk size during upload (e.g., 1MB, 512KB). Recommended 512KB-2MB. Default: <b>512KB</b>\",\"ui.settings.softDeleteRetentionTimeDesc\":\"Retention duration after deleting notes and attachments (e.g., 30d, 24h). 0 means no automatic cleanup. Setting this too short may prevent offline devices from syncing deletion operations. Please set carefully. Default: 7d\",\"ui.settings.uploadSessionTimeoutDesc\":\"Validity period for file chunk upload sessions (e.g., 1h, 30m). Default: <b>1d</b>\",\"ui.settings.historyKeepVersionsDesc\":\"Number of historical versions to keep for each note. When exceeded, the oldest versions are automatically deleted. Minimum: 100. Default: <b>100</b>\",\"ui.settings.historySaveDelayDesc\":\"Delay time for saving history, used to prevent excessive history versions from frequent edits (e.g., 10s, 1m). Default: <b>10s</b>\",\"ui.settings.toastPosition\":\"Notification Display Position\",\"ui.settings.position.top-left\":\"Top Left\",\"ui.settings.position.top-center\":\"Top Center\",\"ui.settings.position.top-right\":\"Top Right\",\"ui.settings.position.bottom-left\":\"Bottom Left\",\"ui.settings.position.bottom-center\":\"Bottom Center\",\"ui.settings.position.bottom-right\":\"Bottom Right\",\"ui.settings.colorScheme\":\"Color Scheme\",\"ui.settings.colorSchemeSwitched\":\"Switched to {{scheme}} color scheme\",\"ui.settings.colorScheme.default\":\"Standard\",\"ui.settings.colorScheme.green\":\"Green\",\"ui.settings.colorScheme.blue\":\"Blue\",\"ui.settings.colorScheme.skyBlue\":\"Sky Blue\",\"ui.settings.colorScheme.purple\":\"Purple\",\"ui.settings.colorScheme.orange\":\"Orange\",\"ui.settings.colorScheme.rose\":\"Rose\",\"ui.settings.colorScheme.teal\":\"Teal\",\"ui.settings.themeAuto\":\"Auto (18:00-06:00 Dark)\",\"ui.settings.themeLight\":\"Light\",\"ui.settings.themeDark\":\"Dark\",\"ui.settings.cloudflaredTestRequired\":\"Please click the Tunnel Program Download button first\",\"ui.settings.downloadSuccess\":\"Download successful\",\"ui.settings.downloadFailed\":\"Download failed\",\"ui.settings.tunnelGatewayConfig\":\"Tunnel Gateway\",\"ui.settings.tunnelGatewayDesc\":\"<b>FNS</b> integrated intranet penetration service, allowing your local <b>FNS</b> service to be securely accessed directly via the public network without configuring cumbersome HTTPS and WebSocket proxies, enabling secure access services.\\nSupports access via Ngrok or Cloudflare tunnels.\",\"ui.settings.ngrokDesc\":'Use Ngrok intranet penetration tunnel to securely expose local services to the public network.\\nOfficial application entry: <a href=\"https://dashboard.ngrok.com/get-started/setup\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary hover:underline\">https://dashboard.ngrok.com/get-started/setup</a>\\n <b>Note</b>: Ngrok free accounts have various dimension limits, please refer to <a href=\"https://ngrok.com/docs/pricing-limits/free-plan-limits\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary hover:underline\">https://ngrok.com/docs/pricing-limits/free-plan-limits</a>',\"ui.settings.customDomain\":\"Custom Domain\",\"ui.settings.customDomainDesc\":\"Optional, requires a paid ngrok account\",\"ui.settings.saveNgrok\":\"Save Settings\",\"ui.settings.cloudflareDesc\":'Reverse tunnel built on Cloudflare Zero Trust network, providing higher security and stability.\\nOfficial application entry: <a href=\"https://one.dash.cloudflare.com\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary hover:underline\">https://one.dash.cloudflare.com</a>\\nBefore saving settings, you need to perform <b>Tunnel Program Download</b> first',\"ui.settings.enableLog\":\"Enable Logging\",\"ui.settings.cloudflareLogDesc\":\"Logs are recorded in storage/logs/cloudflared_tunnel.log\",\"ui.settings.saveCloudflare\":\"Save Settings\",\"ui.settings.downloadCloudflared\":\"Tunnel Program Download\",\"ui.settings.pullSource\":\"Version Check Source\",\"ui.settings.pullSourceDesc\":\"Select the source repository for version detection and updates.\",\"ui.settings.pullSource.auto\":\"Auto Detect\",\"ui.settings.pullSource.github\":\"github.com\",\"ui.settings.pullSource.cnb\":\"Tencent cnb.cool\",\"ui.settings.userDatabaseConfig\":\"Database Enhancement\",\"ui.settings.userDatabaseDesc\":\"Default <b>SQLite</b> (maintenance-free, supports concurrent read/single write), suitable for personal and small-scale deployments, meeting most needs of individual users.<br/><div class='mt-2 pt-2 border-t border-border/50 opacity-90'><div class='mb-2 text-amber-500 font-medium'>Recommended scenarios for switching to professional databases (PostgreSQL / MySQL):</div><ul class='list-disc list-inside space-y-1 mb-1'><li>Multiple users, multiple note libraries with extremely large scale</li><li>Huge number of notes or large attachment sizes</li><li>Frequently encountering concurrency-related errors</li></ul><div>Professional databases are more reliable for concurrency and large-scale data processing, but will increase hardware requirements and operational costs (requires operational capabilities).</div><div class='mt-2 pt-2 border-t border-border/50 text-xs text-rose-500 font-medium italic'>Note: When modifying the database, original data will not be migrated to the new database; you need to force resync the note library; settings must pass the connection test before changing the database type.</div></div>\",\"ui.settings.databaseType\":\"Database Type\",\"ui.settings.databaseType.sqlite\":\"SQLite (Embedded)\",\"ui.settings.databaseType.mysql\":\"MySQL\",\"ui.settings.databaseType.postgres\":\"PostgreSQL\",\"ui.settings.databaseHost\":\"Database Address (Host)\",\"ui.settings.databasePort\":\"Port (Port)\",\"ui.settings.databaseUser\":\"Username (User)\",\"ui.settings.databasePassword\":\"Password (Password)\",\"ui.settings.databaseName\":\"Database Name (Database)\",\"ui.settings.databaseMaxIdleConns\":\"Max Idle Connections (MaxIdle)\",\"ui.settings.databaseMaxOpenConns\":\"Max Open Connections (MaxOpen)\",\"ui.settings.databaseConnMaxLifetime\":\"Connection Max Lifetime (MaxLifetime)\",\"ui.settings.databaseConnMaxIdleTime\":\"Connection Max Idle Time (MaxIdleTime)\",\"ui.settings.databaseMaxWriteConcurrency\":\"Max Write Concurrency (MaxWriteConcurrency)\",\"ui.settings.databaseSchema\":\"Database Schema (Postgres)\",\"ui.settings.databaseSslMode\":\"SSL Mode\",\"ui.settings.mysqlPermissionWarning\":\"Note: When using MySQL, the provided account must have CREATE DATABASE permissions for user information isolation.\",\"ui.settings.postgresPermissionWarning\":\"Note: When using PostgreSQL, the provided account must have CREATE DATABASE permissions for user information isolation.\",\"ui.settings.testConnection\":\"Test Connection\",\"ui.settings.testSuccess\":\"Connection test successful\",\"ui.settings.testFailed\":\"Connection test failed\",\"ui.settings.testRequiredBeforeSave\":\"Please perform a connection test and ensure success before saving\",\"ui.settingsBrowser.title\":\"Vault Configuration Files\",\"ui.settingsBrowser.description\":\"Manage vault configuration information (e.g., vault config, plugins, themes, etc.)\",\"ui.settingsBrowser.add\":\"Add Configuration\",\"ui.settingsBrowser.edit\":\"Edit Configuration\",\"ui.settingsBrowser.key\":\"Path\",\"ui.settingsBrowser.value\":\"Size\",\"ui.settingsBrowser.content\":\"Content\",\"ui.settingsBrowser.keyRequired\":\"Path cannot be empty\",\"ui.settingsBrowser.confirmDelete\":'Are you sure you want to delete the configuration item \"{{key}}\"?',\"ui.settingsBrowser.rename\":\"Rename Path\",\"ui.settingsBrowser.newKey\":\"New Path\",\"ui.settingsBrowser.renameSuccess\":\"Configuration item renamed successfully\",\"ui.settingsBrowser.noSettings\":\"No configuration items yet\",\"ui.obsidian.authTokenConfig\":\"Authorization Configuration\",\"ui.obsidian.authTokenConfigTo\":\"One-click Authorization to Obsidian\",\"ui.obsidian.oneClickImport\":\"One-click Authorization to Obsidian\",\"ui.obsidian.copyConfigSuccess\":\"Authorization configuration copied, please return to the Obsidian plugin to paste the configuration\",\"ui.obsidian.copyConfigError\":\"Non-HTTPS page, clipboard function unavailable, please manually copy the authorization configuration\",\"ui.system.serviceInfo\":\"Service Information\",\"ui.system.versionInfo\":\"Version Information\",\"ui.system.repo\":\"Project Repository\",\"ui.system.githubRepo\":\"GitHub Repository\",\"ui.system.cnbMirror\":\"CNB Mirror\",\"ui.system.currentVersion\":\"Current Version\",\"ui.system.checkUpdate\":\"Check for Updates\",\"ui.system.checkNow\":\"Check Now\",\"ui.system.checking\":\"Checking...\",\"ui.system.newVersionAvailable\":\"New Version Available\",\"ui.system.alreadyLatest\":\"Already Latest Version\",\"ui.system.viewRelease\":\"View Release\",\"ui.system.upgradeNow\":\"Upgrade Now\",\"ui.system.upgrading\":\"Upgrading...\",\"ui.system.upgradeSuccess\":\"Upgrade triggered successfully, page will refresh shortly\",\"ui.system.upgradeFailed\":\"Upgrade trigger failed\",\"ui.system.upgradeRefreshTimeout\":\"Upgrade completed, but the service is not yet available. Please refresh the page manually.\",\"ui.system.viewChangelog\":\"View Changelog\",\"ui.system.getVersionError\":\"Failed to get version information\",\"ui.system.getWebGuiConfigError\":\"Failed to get WebGui configuration:\",\"ui.system.restartService\":\"Restart Service\",\"ui.system.restartServiceConfirm\":\"Are you sure you want to restart the service immediately? Connections will be disconnected during the restart.\",\"ui.system.manualGC\":\"Memory Reclamation\",\"ui.system.manualGCConfirm\":\"Are you sure you want to perform manual memory reclamation (GC) immediately?\",\"ui.system.manualGCSuccess\":\"Manual memory reclamation (GC) triggered successfully\",\"ui.system.serverSystemInfo\":\"Server Information\",\"ui.system.modelName\":\"Processor Model\",\"ui.system.hostInfo\":\"System Information\",\"ui.system.systemTime\":\"System Time\",\"ui.system.runtimeInfo\":\"Service Information\",\"ui.system.startTime\":\"Start Time\",\"ui.system.serviceUptime\":\"Uptime\",\"ui.system.physicalCores\":\"Cores (Logical/Physical)\",\"ui.system.cpuLoad\":\"Average Load (1/5/15)\",\"ui.system.totalMemory\":\"Total Memory\",\"ui.system.usedMemory\":\"Used Memory\",\"ui.system.memoryUsage\":\"Memory Usage\",\"ui.system.os\":\"Operating System\",\"ui.system.kernelVersion\":\"Kernel Version\",\"ui.system.uptime\":\"Uptime\",\"ui.system.goVersion\":\"Runtime Version\",\"ui.system.goroutines\":\"Number of Goroutines\",\"ui.system.heapMemory\":\"Heap Memory / Total Usage\",\"ui.system.numGc\":\"GC Count\",\"ui.system.createdAt\":\"Created At\",\"ui.system.updatedAt\":\"Updated At\",\"ui.system.websocketClients\":\"Online Clients\",\"ui.system.wsNickname\":\"Nickname\",\"ui.system.wsClientName\":\"Client\",\"ui.system.wsClientType\":\"Type\",\"ui.system.wsRemoteAddr\":\"Address\",\"ui.system.wsStartTime\":\"Connection Time\",\"ui.system.wsTraceId\":\"Trace ID\",\"ui.system.wsNoClients\":\"No online clients\",\"ui.storage.management\":\"Storage Configuration\",\"ui.storage.add\":\"Add Storage\",\"ui.storage.edit\":\"Edit Storage\",\"ui.storage.noStorage\":\"No storage configuration\",\"ui.storage.type\":\"Type\",\"ui.storage.storageType.oss\":\"Aliyun OSS\",\"ui.storage.storageType.s3\":\"AWS S3\",\"ui.storage.storageType.r2\":\"Cloudflare R2\",\"ui.storage.storageType.minio\":\"MinIO\",\"ui.storage.storageType.localfs\":\"Local Storage\",\"ui.storage.storageType.webdav\":\"WebDAV\",\"ui.storage.selectType\":\"Select Storage Type\",\"ui.storage.endpoint\":\"Endpoint\",\"ui.storage.region\":\"Region (S3 Region)\",\"ui.storage.accountId\":\"Account ID (R2 Account ID)\",\"ui.storage.bucketName\":\"Bucket\",\"ui.storage.accessKeyId\":\"Access Key ID\",\"ui.storage.accessKeySecret\":\"Access Key Secret\",\"ui.storage.webdavUrl\":\"WebDAV URL\",\"ui.storage.webdavUser\":\"Username\",\"ui.storage.webdavPassword\":\"Password\",\"ui.storage.customPath\":\"Custom Path\",\"ui.storage.accessUrlPrefix\":\"Access URL Prefix\",\"ui.storage.placeholder.endpoint.oss\":\"oss-cn-hangzhou.aliyuncs.com\",\"ui.storage.placeholder.endpoint.minio\":\"http://192.168.1.100:9000\",\"ui.storage.placeholder.region\":\"us-east-1\",\"ui.storage.placeholder.accountId\":\"your-account-id\",\"ui.storage.placeholder.bucketName\":\"my-bucket\",\"ui.storage.placeholder.accessKeyId\":\"\",\"ui.storage.placeholder.accessKeySecret\":\"\",\"ui.storage.placeholder.webdavUrl\":\"http://192.168.1.100:5244/dav\",\"ui.storage.placeholder.webdavUser\":\"admin\",\"ui.storage.placeholder.webdavPassword\":\"\",\"ui.storage.placeholder.customPath\":\"data/obsidian\",\"ui.storage.placeholder.accessUrlPrefix\":\"http://192.168.1.100:5244\",\"ui.storage.help.endpoint.oss\":\"Aliyun OSS Endpoint address, excluding the Bucket name\",\"ui.storage.help.endpoint.minio\":\"MinIO service address, must include protocol prefix and port\",\"ui.storage.help.region\":\"Region where the S3 bucket is located\",\"ui.storage.help.accountId\":\"Cloudflare Account ID, can be found in the right sidebar of the console\",\"ui.storage.help.webdavUrl\":\"Must include protocol prefix (http:// or https://) and full path\",\"ui.storage.help.webdavUser\":\"Login username for the WebDAV service\",\"ui.storage.help.webdavPassword\":\"Login password for the WebDAV service\",\"ui.storage.help.customPath\":\"Subdirectory path for file storage, no leading or trailing slashes required\",\"ui.storage.help.accessUrlPrefix\":\"Prefix address for generating file access links, for display purposes only\",\"ui.storage.confirmDelete\":\"Are you sure you want to delete this storage configuration?\",\"ui.storage.validate.title\":\"Test Connection\",\"ui.storage.validate.loading\":\"Testing...\",\"ui.backup.management\":\"Task Management\",\"ui.backup.add\":\"Add Task\",\"ui.backup.edit\":\"Edit Task\",\"ui.backup.noBackup\":\"No backup or sync tasks yet\",\"ui.backup.selectType\":\"Please select a backup type\",\"ui.backup.confirmDelete\":\"Are you sure you want to delete this backup task?\",\"ui.backup.vault\":\"Vault\",\"ui.backup.selectVault\":\"Select Vault\",\"ui.backup.type\":\"Backup Type\",\"ui.backup.backupType.full\":\"Full Backup\",\"ui.backup.backupType.incremental\":\"Incremental Backup\",\"ui.backup.backupType.sync\":\"Mirror Sync\",\"ui.backup.cronStrategy\":\"Schedule Strategy\",\"ui.backup.strategy.daily\":\"Daily Backup (00:00)\",\"ui.backup.strategy.weekly\":\"Weekly Backup (Monday 00:00)\",\"ui.backup.strategy.monthly\":\"Monthly Backup (1st 00:00)\",\"ui.backup.strategy.custom\":\"Custom Cron\",\"ui.backup.cronExpression\":\"Cron Expression\",\"ui.backup.retentionDays\":\"Retention Days\",\"ui.backup.fileCountUnit\":\"files\",\"ui.backup.retentionDays.sync\":\"History retention days (0: do not clean history, -1: do not retain history)\",\"ui.backup.retentionDays.backup\":\"Backup package retention days (0: do not clean backups, -1: do not retain historical backups)\",\"ui.backup.includeVaultName.label\":\"Include Vault Name\",\"ui.backup.includeVaultName.tooltip\":\"Off: {customPath}/notes/xxx.md\\nOn: {customPath}/{vaultName}/notes/xxx.md\",\"ui.backup.noAvailableStorage\":\"No available storage configuration\",\"ui.backup.addStorageTip\":\"Please add and enable a storage backend in Storage Management first\",\"ui.backup.storages\":\"Storage Selection\",\"ui.backup.validation.vaultRequired\":\"Please select a vault\",\"ui.backup.validation.typeRequired\":\"Please select a backup type\",\"ui.backup.validation.strategyRequired\":\"Please select a schedule strategy\",\"ui.backup.validation.storageRequired\":\"Please select at least one storage\",\"ui.backup.validation.retentionDaysMin\":\"Retention days must be ≥ -1\",\"ui.backup.validation.cronExpressionRequired\":\"Cron expression cannot be empty under custom strategy\",\"ui.backup.lastRunTime\":\"Last Run\",\"ui.backup.nextRunTime\":\"Next Run\",\"ui.backup.executeNow\":\"Execute Now\",\"ui.backup.executeSuccess\":\"Manual backup trigger successful\",\"ui.backup.status.0\":\"Waiting\",\"ui.backup.status.1\":\"In Progress\",\"ui.backup.status.2\":\"Success\",\"ui.backup.status.3\":\"Failed\",\"ui.backup.status.4\":\"Cancelled\",\"ui.backup.status.5\":\"No Updates\",\"ui.backup.history.title\":\"Task History\",\"ui.backup.history.startTime\":\"Task Execution Time\",\"ui.backup.history.storage\":\"Storage\",\"ui.backup.history.status\":\"Status\",\"ui.backup.history.backupStats\":\"Backup Statistics\",\"ui.backup.history.syncStats\":\"Sync Statistics\",\"ui.backup.history.backupFile\":\"Backup File\",\"ui.backup.history.message\":\"Message\",\"ui.backup.history.copyError\":\"Copy Error Message\",\"ui.backup.history.noData\":\"No task records yet\",\"ui.git.title\":\"Git Automation Management\",\"ui.git.config\":\"Git Repository Configuration\",\"ui.git.repoUrl\":\"Git Repository URL (SSH URLs not supported)\",\"ui.git.status\":\"Git Status Overview\",\"ui.git.history\":\"Automated Commit History\",\"ui.git.comingSoon\":\"Git automation features coming soon...\",\"ui.git.configDesc\":\"Configure your Git repository URL, branch, and authentication details.\",\"ui.git.statusDesc\":\"Real-time monitoring of the current repository's commit status and sync progress.\",\"ui.git.historyDesc\":\"View recent automated commit records.\",\"ui.git.retentionDays\":\"History Retention Days\",\"ui.git.retentionDaysDesc\":\"0: Do not clean history, -1: Do not retain history\",\"ui.git.addConfig\":\"Add Git Sync Configuration\",\"ui.git.editConfig\":\"Edit Git Sync Configuration\",\"ui.git.loading\":\"Loading configuration...\",\"ui.git.noConfig\":\"No Git sync configurations yet\",\"ui.git.addFirst\":\"Add your first repository now\",\"ui.git.lastCommit\":\"Last Commit\",\"ui.git.checkTime\":\"Last Check\",\"ui.git.neverRun\":\"Never executed\",\"ui.git.execute.title\":\"Trigger Sync Immediately\",\"ui.git.clean.title\":\"Clean Workspace\",\"ui.git.clean.confirm\":\"Are you sure you want to clean this Git workspace? This will delete the local clone and reinitialize it.\",\"ui.git.delete.confirm\":\"Are you sure you want to delete this Git configuration? This action cannot be undone.\",\"ui.git.form.branch\":\"Branch Name\",\"ui.git.form.delay\":\"Auto-sync Delay (seconds)\",\"ui.git.history.title\":\"Commit History\",\"ui.git.history.configId\":\"Config ID\",\"ui.git.history.startTime\":\"Commit Time\",\"ui.git.history.endTime\":\"End Time\",\"ui.git.history.status\":\"Status\",\"ui.git.history.message\":\"Message\",\"ui.git.history.noData\":\"No commit records yet\",\"ui.git.history.duration\":\"Commit Duration\",\"ui.git.history.vault\":\"Vault\",\"ui.git.status.0\":\"Idle\",\"ui.git.status.1\":\"Checking\",\"ui.git.status.2\":\"Success\",\"ui.git.status.3\":\"Failed\",\"ui.git.status.4\":\"Stopped\",\"ui.git.validate.title\":\"Check Connection\",\"ui.git.validate.loading\":\"Checking...\",\"ui.validation.git.vaultRequired\":\"Please select a vault\",\"ui.validation.git.repoUrlRequired\":\"Please enter the repository URL\",\"ui.validation.git.repoUrlInvalid\":\"Please enter a valid URL\",\"ui.validation.git.branchRequired\":\"Please enter the branch name\",\"ui.validation.git.delayMin\":\"Delay time cannot be negative\",\"ui.validation.git.retentionDaysMin\":\"Retention days must be ≥ -1\",\"ui.validation.storage.typeRequired\":\"Please select a storage type\",\"ui.validation.storage.accessUrlPrefixRequired\":\"Access URL prefix cannot be empty\",\"ui.validation.vault.nameRequired\":\"Vault name cannot be empty\",\"api.git.list.error\":\"Failed to fetch Git configuration list\",\"api.git.save.success\":\"Configuration saved successfully\",\"api.git.save.error\":\"Failed to save configuration\",\"api.git.delete.success\":\"Configuration deleted successfully\",\"api.git.delete.error\":\"Failed to delete configuration\",\"api.git.execute.success\":\"Sync task triggered\",\"api.git.execute.error\":\"Failed to trigger sync\",\"api.git.clean.success\":\"Workspace cleaned successfully\",\"api.git.clean.error\":\"Failed to clean workspace\",\"api.git.history.error\":\"Failed to fetch history records\",\"api.git.validate.success\":\"Git repository connection test successful\",\"api.git.validate.error\":\"Git repository connection test failed\",\"api.backup.configList.error\":\"Failed to fetch backup configuration list\",\"api.backup.delete.success\":\"Deletion successful\",\"api.backup.delete.error\":\"Failed to delete backup configuration\",\"api.backup.save.success\":\"Save successful\",\"api.backup.save.error\":\"Failed to save backup configuration\",\"api.backup.execute.success\":\"Manual trigger successful\",\"api.backup.execute.error\":\"Failed to trigger backup\",\"api.backup.history.error\":\"Failed to retrieve backup history\",\"api.system.restart.success\":\"Service restart triggered\",\"api.system.restart.error\":\"Failed to request service restart\",\"api.system.gc.success\":\"Garbage collection triggered\",\"api.system.gc.error\":\"Failed to request garbage collection\",\"api.storage.list.error\":\"Failed to retrieve storage configuration list\",\"api.storage.delete.success\":\"Deletion successful\",\"api.storage.delete.error\":\"Failed to delete storage configuration\",\"api.storage.save.error\":\"Failed to save storage configuration\",\"api.storage.types.error\":\"Failed to retrieve storage types\",\"api.storage.validate.success\":\"Storage connection test successful\",\"api.storage.validate.error\":\"Storage connection test failed\",\"error.storage.webdav.unauthorized\":\"WebDAV authentication failed, please check username and password\",\"error.storage.webdav.forbidden\":\"Insufficient WebDAV permissions, please check account write permissions\",\"error.storage.webdav.notFound\":\"WebDAV path does not exist, please check custom path configuration\",\"error.storage.webdav.methodNotAllowed\":\"WebDAV method not allowed, please confirm WebDAV is enabled on the server\",\"error.storage.webdav.unreachable\":\"WebDAV address is unreachable, please check the address in storage configuration\",\"error.storage.webdav.connectionRefused\":\"WebDAV connection refused, please confirm if the service is running\",\"error.storage.webdav.timeout\":\"WebDAV connection timed out, please check network or service status\",\"error.storage.webdav.generic\":\"WebDAV operation failed, please check storage configuration\",\"error.storage.s3.noSuchBucket\":\"S3 bucket does not exist, please check bucket name configuration\",\"error.storage.s3.accessDenied\":\"S3 access denied, please check Access Key permissions\",\"error.storage.s3.unreachable\":\"S3 endpoint is unreachable, please check Endpoint address\",\"error.storage.s3.timeout\":\"S3 connection timed out, please check network or endpoint configuration\",\"error.storage.oss.noSuchBucket\":\"OSS bucket does not exist, please check Bucket name\",\"error.storage.oss.accessDenied\":\"OSS access denied, please check AccessKey permissions\",\"error.storage.oss.unreachable\":\"OSS endpoint is unreachable, please check Endpoint address\",\"error.storage.local.noPermission\":\"No write permission for local storage\",\"error.storage.local.createDirFailed\":\"Failed to create local storage directory\",\"error.storage.local.permissionDenied\":\"Local storage permission denied\",\"error.backup.partialFailure\":\"Partial file sync failed, please check detailed error information\",\"error.backup.uploadFailed\":\"Backup file upload failed\",\"error.backup.openFileFailed\":\"Failed to open backup file\",\"error.backup.vaultNotExist\":\"Vault does not exist, please check configuration\",\"error.network.unreachable\":\"Target address is unreachable, please check network configuration\",\"error.network.connectionRefused\":\"Connection refused, please confirm if the target service is running\",\"error.network.timeout\":\"Connection timed out, please check network status\",\"ui.support.title\":\"Support this project\",\"ui.support.supportRequest\":\"If this project has helped you and you want it to continue development, please support us in the following ways. Thank you for your support of open source software!\",\"ui.support.listTitle\":\"Support List\",\"ui.support.noData\":\"No records yet\",\"ui.support.item\":\"Item\",\"ui.support.amount\":\"Amount\",\"ui.support.time\":\"Time\",\"ui.support.message\":\"Message\",\"ui.support.thanks\":\"Thank you everyone for supporting Fast Note Sync! The support list will be updated irregularly\",\"ui.support.sortDefault\":\"Default (Amount)\",\"ui.support.sortTime\":\"By Time\",\"ui.support.sort\":\"Sort\",\"ui.support.buyMeACoffee\":\"Buy Me a Coffee\",\"ui.support.wechatReward\":\"WeChat Reward Support\",\"ui.share.invalidLink\":\"Invalid share link. Missing ID or Token.\",\"ui.share.errorTitle\":\"Share Error\",\"ui.share.noteNotFound\":\"Shared note not found.\",\"ui.share.poweredByPrefix\":\"Powered by \",\"ui.share.poweredBySuffix\":\" \",\"ui.share.version\":\"Version\",\"ui.share.tabActive\":\"Sharing\",\"ui.share.noShares\":\"No share records yet\",\"ui.share.viewShare\":\"View Share Page\",\"ui.share.cancelShare\":\"Cancel Share\",\"ui.share.cancelConfirm\":\"Are you sure you want to cancel the share for this note? The share link will become invalid immediately after cancellation.\",\"ui.share.shareNotFound\":\"Share record not found\",\"ui.share.title\":\"Share Note\",\"ui.share.checking\":\"Checking share status...\",\"ui.share.create\":\"Enable Share\",\"ui.share.creating\":\"Enabling...\",\"ui.share.success\":\"Share successful\",\"ui.share.shortLinkCreate\":\"Generate Short Link\",\"ui.share.link\":\"Share Link\",\"ui.share.shortLink\":\"Short Link\",\"ui.share.copy\":\"Copy Link\",\"ui.share.copySuccess\":\"Link copied to clipboard\",\"ui.share.shortLinkCopy\":\"Copy Short Link\",\"ui.share.cancelSuccess\":\"Share cancelled\",\"ui.share.buttonCreating\":\"Enabling...\",\"ui.share.passwordRequired\":\"Password Required\",\"ui.share.passwordHint\":\"This share is password protected. Please enter the password to view the content.\",\"ui.share.passwordPlaceholder\":\"Enter password...\",\"ui.share.preview\":\"Content Preview\",\"ui.syncLog.title\":\"Vault Update Logs\",\"ui.syncLog.vault\":\"Vault\",\"ui.syncLog.type\":\"Type\",\"ui.syncLog.action\":\"Action\",\"ui.syncLog.path\":\"File Path\",\"ui.syncLog.size\":\"Size\",\"ui.syncLog.client\":\"Client\",\"ui.syncLog.status\":\"Status\",\"ui.syncLog.message\":\"Details\",\"ui.syncLog.time\":\"Record Time\",\"ui.syncLog.changedFields\":\"Changed Fields\",\"ui.syncLog.noLogs\":\"No Sync Records\",\"ui.syncLog.noLogsDescription\":\"No update records found\",\"ui.syncLog.description\":\"Trace all sync change records for notes, attachments, folders, and configuration files in vaults\",\"ui.syncLog.allVaults\":\"All Vaults\",\"ui.syncLog.allTypes\":\"All Types\",\"ui.syncLog.allActions\":\"All Actions\",\"ui.syncLog.resetFilters\":\"Reset Filters\",\"ui.syncLog.statusSuccess\":\"Success\",\"ui.syncLog.statusFailed\":\"Failed\",\"ui.syncLog.type.note\":\"Note\",\"ui.syncLog.type.file\":\"Attachment\",\"ui.syncLog.type.setting\":\"Configuration\",\"ui.syncLog.type.folder\":\"Folder\",\"ui.syncLog.action.create\":\"Create\",\"ui.syncLog.action.modify\":\"Modify\",\"ui.syncLog.action.soft_delete\":\"Soft Delete\",\"ui.syncLog.action.delete\":\"Permanent Delete\",\"ui.syncLog.action.rename\":\"Rename\",\"ui.syncLog.action.restore\":\"Restore\"};export{e as default};\n"
  },
  {
    "path": "frontend/assets/eye-DrvrOb4o.js",
    "content": "import{c as a}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const e=a(\"EyeOff\",[[\"path\",{d:\"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49\",key:\"ct8e1f\"}],[\"path\",{d:\"M14.084 14.158a3 3 0 0 1-4.242-4.242\",key:\"151rxh\"}],[\"path\",{d:\"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143\",key:\"13bj9a\"}],[\"path\",{d:\"m2 2 20 20\",key:\"1ooewy\"}]]),c=a(\"Eye\",[[\"path\",{d:\"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\",key:\"1nclc0\"}],[\"circle\",{cx:\"12\",cy:\"12\",r:\"3\",key:\"1v7zrd\"}]]);\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */export{c as E,e as a};\n"
  },
  {
    "path": "frontend/assets/file-manager-Bz0QGSbU.js",
    "content": "import{c as e,u as s,r as t,j as a,B as r,X as l,a0 as n,a7 as o,I as i,V as c,C as d,t as m}from\"./font-loader-CIrh3KnA.js\";import{A as h,m as u,E as x,P as p,u as f,C as g,f as j}from\"./main-BIi-kGYY.js\";import{u as b,a as v,R as w,F as N,C as y}from\"./canvas-viewer-Cxwbo1vR.js\";import{S as C,a as k,b as H,c as P,d as S}from\"./select-CJF_alSt.js\";import{C as z}from\"./checkbox-DhTHgmeh.js\";import{T as M}from\"./tooltip-Dr-qRlmI.js\";import{F,f as V}from\"./format-CdHm7RWL.js\";import{F as R,I as A}from\"./image-BFJJNQpe.js\";import{D}from\"./download-CKtDCbjj.js\";import{S as B}from\"./search-DdihTHF8.js\";import{R as E}from\"./refresh-cw-BxIJAPy3.js\";import{C as I}from\"./clock-C9LPHszx.js\";import{C as T}from\"./markdown-editor-CX5kQlgI.js\";import{A as L,D as K}from\"./database-eyf5nvY6.js\";import{T as $}from\"./trash-2-ad7PiUnC.js\";import{T as O}from\"./text-cursor-input-Bphfsfyn.js\";import\"./index-JfsWWBj_.js\";import\"./zap-CLLhzk_y.js\";import\"./pencil-DqQhr35g.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const W=e(\"Music\",[[\"path\",{d:\"M9 18V5l12-2v13\",key:\"1jmyc2\"}],[\"circle\",{cx:\"6\",cy:\"18\",r:\"3\",key:\"fqmcym\"}],[\"circle\",{cx:\"18\",cy:\"16\",r:\"3\",key:\"1hluhg\"}]]),_=e(\"Video\",[[\"path\",{d:\"m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5\",key:\"ftymec\"}],[\"rect\",{x:\"2\",y:\"6\",width:\"14\",height:\"12\",rx:\"2\",key:\"158x01\"}]]);\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */function G({file:e,url:o,onClose:i}){var c;const{t:d}=s(),m=(null==(c=e.path.split(\".\").pop())?void 0:c.toLowerCase())||\"\",f=[\"jpg\",\"jpeg\",\"png\",\"gif\",\"svg\",\"webp\",\"bmp\"].includes(m),g=[\"mp3\",\"wav\",\"flac\",\"ogg\",\"m4a\"].includes(m),j=[\"mp4\",\"webm\",\"mkv\",\"avi\",\"mov\"].includes(m),b=\"pdf\"===m,v=[\"js\",\"ts\",\"jsx\",\"tsx\",\"py\",\"sh\",\"bat\",\"go\",\"css\",\"html\",\"json\",\"c\",\"cpp\",\"rs\",\"php\"].includes(m),w=e.path.split(\"/\").pop()||e.path,N=t.useRef(null),[y,C]=t.useState(!0);t.useEffect(()=>{C(!0)},[o]),t.useEffect(()=>{const e=localStorage.getItem(\"preview-volume\");e&&N.current&&(N.current.volume=parseFloat(e))},[o]);const k=e=>{localStorage.setItem(\"preview-volume\",e.currentTarget.volume.toString())},H=()=>{C(!1)};return a.jsx(h,{children:a.jsxs(u.div,{initial:{opacity:0,y:50,scale:.9},animate:{opacity:1,y:0,scale:1},exit:{opacity:0,y:50,scale:.9},className:\"fixed bottom-6 right-6 z-100 w-[320px] sm:w-100 bg-card border border-border rounded-2xl shadow-2xl overflow-hidden flex flex-col max-h-[80vh]\",children:[a.jsxs(\"div\",{className:\"flex items-center justify-between p-3 border-b border-border bg-muted/50\",children:[a.jsxs(\"div\",{className:\"flex flex-col min-w-0\",children:[a.jsx(\"span\",{className:\"text-xs font-medium text-muted-foreground uppercase tracking-wider\",children:d(f?\"ui.file.imagePreview\":g?\"ui.file.audioPreview\":j?\"ui.file.videoPreview\":b?\"ui.file.pdfPreview\":v?\"ui.file.codePreview\":\"ui.file.detail\")}),a.jsx(\"h3\",{className:\"text-sm font-semibold truncate pr-2\",title:w,children:w})]}),a.jsxs(\"div\",{className:\"flex items-center gap-1 shrink-0\",children:[a.jsx(r,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 rounded-xl\",\"aria-label\":d(\"ui.file.openInNewWindow\"),onClick:()=>window.open(o,\"_blank\"),children:a.jsx(x,{className:\"h-4 w-4\"})}),a.jsx(r,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 rounded-xl hover:bg-destructive/10 hover:text-destructive\",\"aria-label\":d(\"ui.common.close\"),onClick:i,children:a.jsx(l,{className:\"h-4 w-4\"})})]})]}),a.jsxs(\"div\",{className:\"relative p-4 flex items-center justify-center bg-black/5 min-h-50 overflow-hidden text-center\",children:[a.jsx(h,{children:y&&a.jsx(u.div,{initial:{opacity:0},animate:{opacity:1},exit:{opacity:0},className:\"absolute inset-0 z-10 flex flex-col items-center justify-center bg-card/80 backdrop-blur-sm pointer-events-none\",children:a.jsx(n,{className:\"h-8 w-8 animate-spin text-primary mb-2\"})})}),f&&a.jsx(\"img\",{src:o,alt:w,className:\"max-w-full max-h-[70vh] object-contain rounded-lg shadow-sm transition-opacity duration-300 \"+(y?\"opacity-0\":\"opacity-100\"),onLoad:H,onError:H},o),g&&a.jsxs(\"div\",{className:\"w-full py-8\",children:[a.jsx(\"div\",{className:\"mb-4 flex justify-center\",children:a.jsx(\"div\",{className:\"w-16 h-16 rounded-full bg-primary/10 flex items-center justify-center text-primary animate-pulse\",children:a.jsx(\"svg\",{className:\"w-8 h-8\",fill:\"currentColor\",viewBox:\"0 0 24 24\",children:a.jsx(\"path\",{d:\"M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z\"})})})}),a.jsx(\"audio\",{ref:N,src:o,controls:!0,autoPlay:!0,className:\"w-full\",onVolumeChange:k,onLoadedMetadata:H,onCanPlay:H,onError:H})]},o),j&&a.jsx(\"video\",{ref:N,src:o,controls:!0,autoPlay:!0,className:\"max-w-full max-h-[70vh] object-contain rounded-lg shadow-sm transition-opacity duration-300 \"+(y?\"opacity-0\":\"opacity-100\"),onVolumeChange:k,onLoadedMetadata:H,onCanPlay:H,onError:H},o),!f&&!g&&!j&&a.jsxs(\"div\",{className:\"flex flex-col items-center gap-4 py-6\",children:[a.jsx(\"div\",{className:\"w-20 h-20 rounded-2xl bg-primary/5 flex items-center justify-center text-primary/60 border border-primary/10\",children:b?a.jsx(F,{className:\"w-10 h-10\"}):v?a.jsx(R,{className:\"w-10 h-10\"}):a.jsx(p,{className:\"w-10 h-10\"})}),a.jsxs(\"div\",{className:\"text-center\",children:[a.jsx(\"p\",{className:\"text-sm text-muted-foreground\",children:d(\"ui.file.unsupportedPreview\")}),a.jsx(r,{variant:\"link\",className:\"text-primary mt-1 h-auto p-0\",onClick:()=>window.open(o,\"_blank\"),children:d(\"ui.file.openInNewWindow\")})]})]})]}),a.jsx(\"div\",{className:\"p-3 border-t border-border bg-muted/30 flex justify-end\",children:a.jsxs(r,{variant:\"default\",size:\"sm\",className:\"rounded-xl gap-2 text-xs\",onClick:()=>window.open(o,\"_blank\"),children:[a.jsx(D,{className:\"h-3.5 w-3.5\"}),d(\"ui.file.browserDownload\")]})})]})})}function q(e){if(0===e)return\"0 B\";const s=Math.floor(Math.log(e)/Math.log(1024));return Math.round(e/Math.pow(1024,s)*100)/100+\" \"+[\"B\",\"KB\",\"MB\",\"GB\"][s]}function U({vault:e,vaults:n,onVaultChange:m,isRecycle:h=!1,page:u,setPage:x,pageSize:j,setPageSize:y,searchKeyword:D,setSearchKeyword:K,currentPath:U,setCurrentPath:J,currentPathHash:Q,setCurrentPathHash:X,pathHashMap:Y,setPathHashMap:Z,onCanvasOpen:ee}){const{t:se}=s(),{handleFileList:te,handleDeleteFile:ae,handleRestoreFile:re,getRawFileUrl:le,handleFolderFiles:ne,handleFolderList:oe,handlePermanentDeleteFile:ie,handleClearFileRecycle:ce,handleRenameFile:de}=b(),{openConfirmDialog:me}=o(),[he,ue]=t.useState([]),[xe,pe]=t.useState(!1),[fe,ge]=t.useState(0),[je,be]=t.useState(D),[ve,we]=t.useState(\"mtime\"),[Ne,ye]=t.useState(\"desc\"),[Ce,ke]=t.useState(new Set),[He,Pe]=t.useState(null),[Se,ze]=t.useState(\"folder\"),[Me,Fe]=t.useState([]),Ve=t.useRef(0),{trashType:Re,setModule:Ae}=f(),[De,Be]=t.useState(null),[Ee,Ie]=t.useState(\"\");t.useEffect(()=>{const e=setTimeout(()=>{be(D)},300);return()=>clearTimeout(e)},[D]);const Te=(s=u,t=j,a=je)=>{const r=++Ve.current;pe(!0),\"folder\"!==Se||h?te(e,s,t,h,a,ve,Ne,e=>{var s;r===Ve.current&&(ue((null==e?void 0:e.list)||[]),ge((null==(s=null==e?void 0:e.pager)?void 0:s.totalRows)||0),pe(!1))}):oe(e,U,Q,a=>{r===Ve.current&&(Fe(a||[]),ne(e,U,Q,s,t,ve,Ne,e=>{var s;r===Ve.current&&(ue((null==e?void 0:e.list)||[]),ge((null==(s=null==e?void 0:e.pager)?void 0:s.totalRows)||0),pe(!1))}))})};t.useEffect(()=>{Te(u,j,je),ke(new Set)},[e,u,j,je,h,ve,Ne,Se,U]),t.useEffect(()=>{je&&ze(\"flat\")},[je,U,Se,x]);const Le=e=>{e>=1&&e<=Math.ceil(fe/j)&&x(e)},Ke=e=>{if(!e.contentHash)return;const s=new Set(Ce);s.has(e.pathHash)?s.delete(e.pathHash):s.add(e.pathHash),ke(s)},$e=e=>{var s;const t=(null==(s=e.split(\".\").pop())?void 0:s.toLowerCase())||\"\";return[\"jpg\",\"jpeg\",\"png\",\"gif\",\"svg\",\"webp\",\"bmp\"].includes(t)?a.jsx(A,{className:\"h-5 w-5\"}):\"pdf\"===t?a.jsx(F,{className:\"h-5 w-5\"}):[\"mp3\",\"wav\",\"flac\",\"ogg\",\"m4a\"].includes(t)?a.jsx(W,{className:\"h-5 w-5\"}):[\"mp4\",\"webm\",\"mkv\",\"avi\",\"mov\"].includes(t)?a.jsx(_,{className:\"h-5 w-5\"}):[\"js\",\"ts\",\"jsx\",\"tsx\",\"py\",\"sh\",\"bat\",\"go\",\"css\",\"html\",\"json\",\"c\",\"cpp\",\"rs\",\"php\"].includes(t)?a.jsx(R,{className:\"h-5 w-5\"}):a.jsx(p,{className:\"h-5 w-5\"})},Oe=Math.ceil(fe/j);return a.jsxs(\"div\",{className:\"w-full flex flex-col space-y-4\",children:[a.jsxs(\"div\",{className:\"flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 py-1\",children:[a.jsx(\"div\",{className:\"flex items-center gap-3\",children:n&&m&&a.jsxs(C,{value:e,onValueChange:m,children:[a.jsx(k,{className:\"w-auto min-w-45 rounded-xl\",children:a.jsx(H,{placeholder:se(\"ui.common.selectVault\")})}),a.jsx(P,{className:\"rounded-xl\",children:n.map(e=>a.jsx(S,{value:e.vault,className:\"rounded-xl\",children:e.vault},e.id))})]})}),a.jsx(\"div\",{className:\"flex flex-col gap-2 w-full sm:w-auto\",children:a.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[a.jsxs(\"div\",{className:\"relative flex-1 sm:w-64\",children:[a.jsx(B,{className:\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none\"}),a.jsx(i,{type:\"text\",placeholder:se(\"ui.file.searchPlaceholder\"),className:\"pl-9 pr-8 rounded-xl\",value:D,onChange:e=>K(e.target.value)}),D&&a.jsx(\"button\",{className:\"absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground\",onClick:()=>K(\"\"),children:a.jsx(l,{className:\"h-4 w-4\"})})]}),a.jsx(r,{variant:\"outline\",size:\"icon\",\"aria-label\":se(\"ui.common.refresh\"),onClick:()=>Te(),disabled:xe,className:\"rounded-xl shrink-0\",children:a.jsx(E,{className:\"h-4 w-4 \"+(xe?\"animate-spin\":\"\")})})]})})]}),\"folder\"===Se&&!h&&U&&a.jsxs(\"div\",{className:\"flex items-center gap-2 px-1 text-sm text-muted-foreground overflow-x-auto whitespace-nowrap scrollbar-hide\",children:[a.jsx(\"button\",{className:\"hover:text-primary transition-colors flex items-center\",onClick:()=>{J(\"\"),X(\"\"),x(1)},children:e}),U.split(\"/\").filter(Boolean).map((e,s,t)=>a.jsxs(c.Fragment,{children:[a.jsx(d,{className:\"h-4 w-4 shrink-0\"}),a.jsx(\"button\",{className:\"transition-colors \"+(s===t.length-1?\"text-foreground font-medium pointer-events-none\":\"hover:text-primary\"),onClick:()=>{const e=t.slice(0,s+1).join(\"/\");J(e),X(Y[e]||\"\"),x(1)},children:e})]},`breadcrumb-${s}`))]}),!h&&a.jsxs(\"div\",{className:\"flex flex-wrap items-center gap-4 py-2 px-2 bg-muted/30 rounded-xl border border-border/50\",children:[a.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[a.jsxs(\"div\",{className:\"flex items-center h-8 rounded-lg border border-border overflow-hidden bg-background shadow-sm\",children:[a.jsx(\"button\",{className:\"px-4 h-full text-xs font-medium transition-colors \"+(\"folder\"===Se?\"bg-primary text-primary-foreground\":\"hover:bg-muted\"),onClick:()=>{K(\"\"),be(\"\"),ze(\"folder\")},children:se(\"ui.note.viewFolder\")}),a.jsx(\"button\",{className:\"px-4 h-full text-xs font-medium transition-colors border-l border-border \"+(\"flat\"===Se?\"bg-primary text-primary-foreground\":\"hover:bg-muted\"),onClick:()=>ze(\"flat\"),children:se(\"ui.note.viewFlat\")})]}),a.jsxs(\"span\",{className:\"text-sm font-medium text-muted-foreground mr-2\",children:[fe,\" \",se(\"ui.file.file\")]})]}),a.jsxs(\"div\",{className:\"flex items-center h-8 rounded-xl border border-border overflow-hidden bg-background shadow-sm ml-auto\",children:[a.jsxs(\"button\",{className:\"px-3 h-full text-xs flex items-center gap-1.5 transition-colors \"+(\"mtime\"===ve?\"bg-accent text-accent-foreground\":\"hover:bg-muted\"),onClick:()=>we(\"mtime\"),children:[a.jsx(I,{className:\"h-3.5 w-3.5\"}),se(\"ui.note.sortByMtime\")]}),a.jsxs(\"button\",{className:\"px-3 h-full text-xs flex items-center gap-1.5 transition-colors border-l border-border \"+(\"ctime\"===ve?\"bg-accent text-accent-foreground\":\"hover:bg-muted\"),onClick:()=>we(\"ctime\"),children:[a.jsx(T,{className:\"h-3.5 w-3.5\"}),se(\"ui.note.sortByCtime\")]}),a.jsxs(\"button\",{className:\"px-3 h-full text-xs flex items-center gap-1.5 transition-colors border-l border-border \"+(\"path\"===ve?\"bg-accent text-accent-foreground\":\"hover:bg-muted\"),onClick:()=>we(\"path\"),children:[a.jsx(F,{className:\"h-3.5 w-3.5\"}),se(\"ui.note.sortByPath\")]}),a.jsx(M,{content:se(\"desc\"===Ne?\"ui.note.sortDesc\":\"ui.note.sortAsc\"),side:\"top\",delay:200,children:a.jsx(\"button\",{className:\"px-2.5 h-full text-xs flex items-center transition-colors border-l border-border hover:bg-muted\",onClick:()=>ye(\"desc\"===Ne?\"asc\":\"desc\"),children:\"desc\"===Ne?a.jsx(L,{className:\"h-3.5 w-3.5\"}):a.jsx(v,{className:\"h-3.5 w-3.5\"})})})]})]}),h&&a.jsxs(\"div\",{className:\"flex flex-wrap items-center gap-4 py-2 px-2 bg-muted/30 rounded-xl border border-border/50\",children:[a.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[a.jsxs(\"div\",{className:\"flex items-center h-8 rounded-lg border border-border overflow-hidden bg-background shadow-sm\",children:[a.jsx(\"button\",{className:\"px-4 h-full text-xs font-medium transition-colors \"+(\"notes\"===Re?\"bg-primary text-primary-foreground\":\"hover:bg-muted\"),onClick:()=>Ae(\"trash\",\"notes\"),children:se(\"ui.note.note\")}),a.jsx(\"button\",{className:\"px-4 h-full text-xs font-medium transition-colors border-l border-border \"+(\"files\"===Re?\"bg-primary text-primary-foreground\":\"hover:bg-muted\"),onClick:()=>Ae(\"trash\",\"files\"),children:se(\"ui.file.file\")})]}),a.jsxs(\"span\",{className:\"text-sm font-medium text-muted-foreground mr-2\",children:[fe,\" \",se(\"ui.nav.menuTrash\"),se(\"ui.file.file\")]}),he.length>0&&a.jsxs(r,{variant:\"ghost\",size:\"sm\",onClick:()=>{me(se(\"ui.file.clearRecycleConfirm\"),\"confirm\",()=>{ce(e,()=>{Te()})})},className:\"h-8 rounded-lg text-destructive hover:bg-destructive/10 hover:text-destructive\",children:[a.jsx($,{className:\"h-3.5 w-3.5 mr-1.5\"}),se(\"ui.common.clear\")]})]}),he.length>0&&a.jsxs(\"div\",{className:\"flex items-center gap-3 pl-4 border-l border-border/60\",children:[a.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[a.jsx(z,{id:\"select-all\",checked:he.filter(e=>e.contentHash).length>0&&Ce.size===he.filter(e=>e.contentHash).length,onCheckedChange:()=>{const e=he.filter(e=>e.contentHash);Ce.size===e.length&&e.length>0?ke(new Set):ke(new Set(e.map(e=>e.pathHash)))},className:\"rounded-md\"}),a.jsx(\"label\",{htmlFor:\"select-all\",className:\"text-xs font-medium cursor-pointer text-muted-foreground hover:text-foreground transition-colors\",children:se(\"ui.common.selectAll\")})]}),Ce.size>0&&a.jsxs(\"div\",{className:\"flex items-center gap-3 animate-in fade-in slide-in-from-left-2 duration-200\",children:[a.jsx(\"span\",{className:\"text-xs text-primary font-semibold bg-primary/10 px-2 py-0.5 rounded-full\",children:se(\"ui.file.selectedCount\",{count:Ce.size})}),a.jsxs(r,{variant:\"outline\",size:\"sm\",onClick:()=>{if(0===Ce.size)return;const s=he.filter(e=>Ce.has(e.pathHash)&&e.contentHash);0!==s.length&&me(se(\"ui.file.batchRestoreConfirm\",{count:s.length}),\"confirm\",async()=>{pe(!0);const t=s.length;try{for(let a=0;a<s.length;a++)Pe({current:a+1,total:t}),await Promise.race([new Promise(t=>{re(e,s[a].path,s[a].pathHash,t)}),new Promise(e=>setTimeout(e,3e4))])}finally{Pe(null),ke(new Set),Te()}})},disabled:!he.some(e=>Ce.has(e.pathHash)&&e.contentHash),className:\"h-8 rounded-lg text-green-600 border-green-200 hover:bg-green-50 hover:text-green-700 hover:border-green-300 shadow-sm\",children:[a.jsx(w,{className:\"h-3.5 w-3.5 mr-1.5\"}),se(\"ui.file.batchRestore\")]}),a.jsxs(r,{variant:\"outline\",size:\"sm\",onClick:()=>{if(0===Ce.size)return;const s=he.filter(e=>Ce.has(e.pathHash));0!==s.length&&me(se(\"ui.common.batchPermanentDeleteConfirm\",{count:s.length}),\"confirm\",async()=>{pe(!0);const t=s.length;try{for(let a=0;a<s.length;a++)Pe({current:a+1,total:t}),await Promise.race([new Promise(t=>{ie(e,s[a].path,s[a].pathHash,t)}),new Promise(e=>setTimeout(e,3e4))])}finally{Pe(null),ke(new Set),Te()}})},className:\"h-8 rounded-lg text-destructive border-destructive/20 hover:bg-destructive/5 hover:text-destructive hover:border-destructive/40 shadow-sm\",children:[a.jsx($,{className:\"h-3.5 w-3.5 mr-1.5\"}),se(\"ui.common.batchPermanentDelete\")]})]})]}),a.jsxs(\"div\",{className:\"flex items-center h-8 rounded-xl border border-border overflow-hidden bg-background shadow-sm ml-auto\",children:[a.jsxs(\"button\",{className:\"px-3 h-full text-xs flex items-center gap-1.5 transition-colors \"+(\"mtime\"===ve?\"bg-accent text-accent-foreground\":\"hover:bg-muted\"),onClick:()=>we(\"mtime\"),children:[a.jsx(I,{className:\"h-3.5 w-3.5\"}),se(\"ui.note.sortByMtime\")]}),a.jsxs(\"button\",{className:\"px-3 h-full text-xs flex items-center gap-1.5 transition-colors border-l border-border \"+(\"ctime\"===ve?\"bg-accent text-accent-foreground\":\"hover:bg-muted\"),onClick:()=>we(\"ctime\"),children:[a.jsx(T,{className:\"h-3.5 w-3.5\"}),se(\"ui.note.sortByCtime\")]}),a.jsxs(\"button\",{className:\"px-3 h-full text-xs flex items-center gap-1.5 transition-colors border-l border-border \"+(\"path\"===ve?\"bg-accent text-accent-foreground\":\"hover:bg-muted\"),onClick:()=>we(\"path\"),children:[a.jsx(F,{className:\"h-3.5 w-3.5\"}),se(\"ui.note.sortByPath\")]}),a.jsx(M,{content:se(\"desc\"===Ne?\"ui.note.sortDesc\":\"ui.note.sortAsc\"),side:\"top\",delay:200,children:a.jsx(\"button\",{className:\"px-2.5 h-full text-xs flex items-center transition-colors border-l border-border hover:bg-muted\",onClick:()=>ye(\"desc\"===Ne?\"asc\":\"desc\"),children:\"desc\"===Ne?a.jsx(L,{className:\"h-3.5 w-3.5\"}):a.jsx(v,{className:\"h-3.5 w-3.5\"})})})]})]}),xe?a.jsxs(\"div\",{className:\"rounded-xl border border-border bg-card p-12 text-center text-muted-foreground\",children:[a.jsx(E,{className:\"h-6 w-6 animate-spin mx-auto mb-2\"}),He?`${He.current} / ${He.total}`:se(\"ui.common.loading\")]}):Array.isArray(he)&&0!==he.length||Array.isArray(Me)&&0!==Me.length&&\"flat\"!==Se?a.jsx(\"div\",{className:\"-mx-2 px-2\",children:a.jsxs(\"div\",{className:\"grid grid-cols-1 gap-3 py-1\",children:[\"folder\"===Se&&!h&&Array.isArray(Me)&&Me.map(e=>a.jsx(\"article\",{className:\"rounded-xl border border-border bg-card p-4 cursor-pointer transition-all duration-200 hover:shadow-md hover:border-primary/30\",onClick:()=>{Z({...Y,[e.path]:e.pathHash}),J(e.path),X(e.pathHash),x(1)},children:a.jsxs(\"div\",{className:\"flex items-center justify-between gap-4\",children:[a.jsxs(\"div\",{className:\"flex items-start gap-3 min-w-0 flex-1\",children:[a.jsx(\"span\",{className:\"flex h-10 w-10 items-center justify-center rounded-xl bg-blue-500/10 text-blue-500 shrink-0\",children:a.jsx(N,{className:\"h-5 w-5 fill-current opacity-70\"})}),a.jsxs(\"div\",{className:\"min-w-0 flex-1\",children:[a.jsx(\"h3\",{className:\"font-semibold text-card-foreground truncate\",children:e.path.split(\"/\").pop()}),a.jsxs(\"div\",{className:\"flex flex-wrap items-center gap-x-4 gap-y-1 mt-1.5 text-xs text-muted-foreground\",children:[a.jsx(M,{content:se(\"ui.common.createdAt\"),side:\"top\",delay:300,children:a.jsxs(\"span\",{className:\"hidden sm:flex items-center gap-1\",children:[a.jsx(T,{className:\"h-3.5 w-3.5\"}),V(new Date(e.ctime),\"yyyy-MM-dd HH:mm\")]})}),a.jsx(M,{content:se(\"ui.common.updatedAt\"),side:\"top\",delay:300,children:a.jsxs(\"span\",{className:\"flex items-center gap-1\",children:[a.jsx(I,{className:\"h-3.5 w-3.5\"}),V(new Date(e.mtime),\"yyyy-MM-dd HH:mm\")]})})]})]})]}),a.jsx(\"div\",{className:\"shrink-0\",children:a.jsx(d,{className:\"h-5 w-5 text-muted-foreground\"})})]})},`folder-${e.pathHash}`)),Array.isArray(he)&&he.map(s=>a.jsx(\"article\",{className:\"rounded-xl border border-border bg-card p-4 transition-all duration-200 hover:shadow-md hover:border-primary/30 cursor-pointer\",onClick:()=>(s=>{var t;if(s.path.toLowerCase().endsWith(\".canvas\")&&ee)return void ee({path:s.path,pathHash:s.pathHash});let a=le(e,s.path,null==(t=s.pathHash)?void 0:t.toString());h&&(a+=(a.includes(\"?\")?\"&\":\"?\")+\"isRecycle=1\"),Be(s),Ie(a)})(s),children:a.jsxs(\"div\",{className:\"flex items-center justify-between gap-4\",children:[a.jsxs(\"div\",{className:\"flex items-start gap-3 min-w-0 flex-1\",children:[h&&a.jsx(\"div\",{className:\"flex items-center self-center \"+(s.contentHash?\"\":\"opacity-30\"),onClick:e=>((e,s)=>{e.stopPropagation(),Ke(s)})(e,s),children:a.jsx(z,{checked:Ce.has(s.pathHash),onClick:e=>e.stopPropagation(),onCheckedChange:()=>{!xe&&s.contentHash&&Ke(s)},disabled:!s.contentHash,className:\"rounded-md\"})}),a.jsx(\"span\",{className:\"flex h-10 w-10 items-center justify-center rounded-xl bg-primary/10 text-primary shrink-0\",children:$e(s.path)}),a.jsxs(\"div\",{className:\"min-w-0 flex-1\",children:[a.jsx(\"h3\",{className:\"font-semibold text-card-foreground truncate\",children:\"folder\"!==Se||h?s.path:s.path.split(\"/\").pop()}),a.jsxs(\"div\",{className:\"flex flex-wrap items-center gap-x-4 gap-y-1 mt-1.5 text-xs text-muted-foreground\",children:[a.jsx(\"span\",{className:\"flex items-center gap-1\",children:q(s.size)}),a.jsx(M,{content:se(\"ui.common.createdAt\"),side:\"top\",delay:300,children:a.jsxs(\"span\",{className:\"hidden sm:flex items-center gap-1\",children:[a.jsx(T,{className:\"h-3.5 w-3.5\"}),V(new Date(s.ctime),\"yyyy-MM-dd HH:mm\")]})}),a.jsx(M,{content:se(\"ui.common.updatedAt\"),side:\"top\",delay:300,children:a.jsxs(\"span\",{className:\"flex items-center gap-1\",children:[a.jsx(I,{className:\"h-3.5 w-3.5\"}),V(new Date(s.mtime),\"yyyy-MM-dd HH:mm\")]})})]})]})]}),a.jsxs(\"div\",{className:\"flex items-center gap-1 shrink-0\",children:[!h&&a.jsxs(a.Fragment,{children:[a.jsx(M,{content:se(\"ui.common.rename\"),side:\"top\",delay:200,children:a.jsx(r,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 rounded-xl text-muted-foreground hover:text-blue-500\",onClick:t=>((s,t)=>{s.stopPropagation();const r=t.path.split(\"/\").pop()||\"\",l=r.lastIndexOf(\".\"),n=-1!==l?r.substring(l):\"\",o=-1!==l?r.substring(0,l):r;let c=o;me(se(\"ui.file.renameFile\"),\"confirm\",()=>{if(!c||c===o)return;const s=c.endsWith(n)?c:c+n,a=t.path.includes(\"/\")?t.path.substring(0,t.path.lastIndexOf(\"/\")+1):\"\";de({vault:e,oldPath:t.path,path:a+s,oldPathHash:t.pathHash},()=>{Te()})},a.jsx(\"div\",{className:\"pt-2\",children:a.jsx(i,{autoFocus:!0,defaultValue:o,placeholder:se(\"ui.file.renameFilePlaceholder\"),onChange:e=>{c=e.target.value}})}))})(t,s),children:a.jsx(O,{className:\"h-4 w-4\"})})}),a.jsx(M,{content:se(\"ui.common.delete\"),side:\"top\",delay:200,children:a.jsx(r,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 rounded-xl text-muted-foreground hover:text-destructive\",onClick:t=>((s,t)=>{s.stopPropagation(),me(se(\"ui.file.deleteFileConfirm\",{title:t.path}),\"confirm\",()=>{ae(e,t.path,t.pathHash,()=>{Te()})})})(t,s),children:a.jsx($,{className:\"h-4 w-4\"})})})]}),h&&a.jsxs(a.Fragment,{children:[a.jsx(M,{content:se(\"ui.common.restore\"),side:\"top\",delay:200,children:a.jsx(r,{variant:\"ghost\",size:\"icon\",disabled:!s.contentHash,className:\"h-8 w-8 rounded-xl text-muted-foreground hover:text-green-600\",onClick:t=>((s,t)=>{s.stopPropagation(),t.contentHash&&me(se(\"ui.file.restoreFileConfirm\",{title:t.path}),\"confirm\",()=>{re(e,t.path,t.pathHash,()=>{Te()})})})(t,s),children:a.jsx(w,{className:\"h-4 w-4\"})})}),a.jsx(M,{content:se(\"ui.common.permanentDelete\"),side:\"top\",delay:200,children:a.jsx(r,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 rounded-xl text-muted-foreground hover:text-destructive\",onClick:t=>((s,t)=>{s.stopPropagation(),me(se(\"ui.file.permanentDeleteConfirm\",{title:t.path}),\"confirm\",()=>{ie(e,t.path,t.pathHash,()=>{Te()})})})(t,s),children:a.jsx($,{className:\"h-4 w-4\"})})})]})]})]})},`file-${s.pathHash}`))]})}):a.jsx(\"div\",{className:\"rounded-xl border border-border bg-card p-12 text-center text-muted-foreground\",children:se(\"ui.file.noFiles\")}),he.length>0&&a.jsxs(\"div\",{className:\"flex flex-col sm:flex-row items-start sm:items-center sm:justify-between gap-4 pt-2 shrink-0\",children:[a.jsxs(\"div\",{className:\"flex items-center gap-2 text-sm text-muted-foreground\",children:[a.jsxs(\"span\",{children:[se(\"ui.common.of\"),\" \",fe,\" \",se(\"ui.file.results\")]}),a.jsxs(C,{value:j.toString(),onValueChange:e=>{const s=parseInt(e);y(s),x(1)},children:[a.jsx(k,{className:\"h-8 w-25 rounded-xl\",children:a.jsx(H,{placeholder:j.toString()})}),a.jsx(P,{className:\"rounded-xl\",children:[10,20,50,100].map(e=>a.jsxs(S,{value:e.toString(),className:\"rounded-xl\",children:[e,\" \",se(\"ui.common.perPage\")]},e))})]})]}),a.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[a.jsxs(r,{variant:\"outline\",size:\"sm\",onClick:()=>Le(u-1),disabled:1===u||xe,className:\"rounded-xl\",children:[a.jsx(g,{className:\"h-4 w-4\"}),se(\"ui.common.previous\")]}),a.jsxs(\"span\",{className:\"text-sm font-medium px-2\",children:[u,\" / \",Oe]}),a.jsxs(r,{variant:\"outline\",size:\"sm\",onClick:()=>Le(u+1),disabled:u===Oe||xe,className:\"rounded-xl\",children:[se(\"ui.common.next\"),a.jsx(d,{className:\"h-4 w-4\"})]})]})]}),De&&a.jsx(G,{file:De,url:Ee,onClose:()=>{Be(null),Ie(\"\")}},Ee)]})}function J({vault:e,onVaultChange:l,onNavigateToVaults:n,isRecycle:o=!1}){const{t:i}=s(),[c,d]=t.useState([]),h=t.useRef(!1),[u,x]=t.useState(1),[p,f]=t.useState(()=>{const e=localStorage.getItem(\"filePageSize\");return e?parseInt(e,10):10}),[g,b]=t.useState(\"\"),[v,w]=t.useState(\"\"),[N,C]=t.useState(\"\"),[k,H]=t.useState({}),[P,S]=t.useState(null);t.useEffect(()=>{localStorage.setItem(\"filePageSize\",p.toString())},[p]);const{handleVaultList:z}=j();t.useEffect(()=>{let e=!0;return(async()=>{try{await z(s=>{e&&d(s)})}catch(s){if(!e)return;m.error(s instanceof Error?s.message:String(s)),d([])}finally{e&&(h.current=!0)}})(),()=>{e=!1}},[z]),t.useEffect(()=>{x(1),w(\"\"),C(\"\"),H({}),S(null)},[e]);const M=t.useCallback(e=>{S(e)},[]),F=t.useCallback(()=>{S(null)},[]);return h.current&&0===c.length?a.jsxs(\"div\",{className:\"rounded-xl border border-border bg-card p-12 flex flex-col items-center justify-center\",children:[a.jsx(K,{className:\"h-16 w-16 text-muted-foreground/30 mb-4\"}),a.jsx(\"h3\",{className:\"text-lg font-semibold text-foreground mb-2\",children:i(\"ui.file.noVaults\")}),a.jsx(\"p\",{className:\"text-muted-foreground mb-6 text-center\",children:i(\"ui.file.createVaultFirst\")}),a.jsx(r,{onClick:()=>{n&&n()},className:\"rounded-xl\",children:i(\"ui.note.goToVaultManagement\")})]}):P?a.jsx(y,{vault:e,note:P,onBack:F,isRecycle:o}):a.jsx(U,{vault:e,vaults:c,onVaultChange:l,isRecycle:o,page:u,setPage:x,pageSize:p,setPageSize:f,searchKeyword:g,setSearchKeyword:b,currentPath:v,setCurrentPath:w,currentPathHash:N,setCurrentPathHash:C,pathHashMap:k,setPathHashMap:H,onCanvasOpen:M})}export{J as FileManager};\n"
  },
  {
    "path": "frontend/assets/file-type-DbD_pFnN.js",
    "content": "import{c as a}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const e=a(\"FileType\",[[\"path\",{d:\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\",key:\"1rqfz7\"}],[\"path\",{d:\"M14 2v4a2 2 0 0 0 2 2h4\",key:\"tnqrlb\"}],[\"path\",{d:\"M9 13v-1h6v1\",key:\"1bb014\"}],[\"path\",{d:\"M12 12v6\",key:\"3ahymv\"}],[\"path\",{d:\"M11 18h2\",key:\"12mj7e\"}]]);export{e as F};\n"
  },
  {
    "path": "frontend/assets/font-loader-B-ynJ_1p.css",
    "content": "/*! tailwindcss v4.1.18 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-blur:0;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y:0;--tw-exit-blur:0;--tw-exit-opacity:1;--tw-exit-rotate:0;--tw-exit-scale:1;--tw-exit-translate-x:0;--tw-exit-translate-y:0}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\",\"Noto Color Emoji\";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-900:oklch(39.6% .141 25.723);--color-red-950:oklch(25.8% .092 26.042);--color-orange-50:oklch(98% .016 73.684);--color-orange-100:oklch(95.4% .038 75.164);--color-orange-400:oklch(75% .183 55.934);--color-orange-500:oklch(70.5% .213 47.604);--color-orange-600:oklch(64.6% .222 41.116);--color-orange-900:oklch(40.8% .123 38.172);--color-orange-950:oklch(26.6% .079 36.259);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-700:oklch(55.5% .163 48.998);--color-yellow-50:oklch(98.7% .026 102.212);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-950:oklch(28.6% .066 53.813);--color-green-50:oklch(98.2% .018 155.826);--color-green-100:oklch(96.2% .044 156.743);--color-green-200:oklch(92.5% .084 155.995);--color-green-300:oklch(87.1% .15 154.449);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-700:oklch(52.7% .154 150.069);--color-green-900:oklch(39.3% .095 152.535);--color-green-950:oklch(26.6% .065 152.934);--color-emerald-300:oklch(84.5% .143 164.978);--color-emerald-500:oklch(69.6% .17 162.48);--color-teal-50:oklch(98.4% .014 180.72);--color-teal-400:oklch(77.7% .152 181.912);--color-teal-950:oklch(27.7% .046 192.524);--color-cyan-50:oklch(98.4% .019 200.873);--color-cyan-400:oklch(78.9% .154 211.53);--color-cyan-950:oklch(30.2% .056 229.695);--color-sky-300:oklch(82.8% .111 230.318);--color-sky-700:oklch(50% .134 242.749);--color-sky-900:oklch(39.1% .09 240.876);--color-blue-50:oklch(97% .014 254.604);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-200:oklch(88.2% .059 254.128);--color-blue-300:oklch(80.9% .105 251.813);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-blue-900:oklch(37.9% .146 265.522);--color-blue-950:oklch(28.2% .091 267.935);--color-indigo-500:oklch(58.5% .233 277.117);--color-violet-300:oklch(81.1% .111 293.571);--color-purple-50:oklch(97.7% .014 308.299);--color-purple-400:oklch(71.4% .203 305.504);--color-purple-500:oklch(62.7% .265 303.9);--color-purple-600:oklch(55.8% .288 302.321);--color-purple-700:oklch(49.6% .265 301.924);--color-purple-950:oklch(29.1% .149 302.717);--color-fuchsia-300:oklch(83.3% .145 321.434);--color-pink-500:oklch(65.6% .241 354.308);--color-rose-500:oklch(64.5% .246 16.439);--color-slate-50:oklch(98.4% .003 247.858);--color-slate-100:oklch(96.8% .007 247.896);--color-slate-200:oklch(92.9% .013 255.508);--color-slate-300:oklch(86.9% .022 252.894);--color-slate-400:oklch(70.4% .04 256.788);--color-slate-500:oklch(55.4% .046 257.417);--color-slate-700:oklch(37.2% .044 257.287);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-950:oklch(13% .028 261.692);--color-zinc-500:oklch(55.2% .016 285.938);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wider:.05em;--tracking-widest:.1em;--leading-tight:1.25;--leading-relaxed:1.625;--radius-2xl:1rem;--radius-3xl:1.5rem;--shadow-2xs:var(--shadow-2xs);--shadow-xs:var(--shadow-xs);--shadow-sm:var(--shadow-sm);--shadow-md:var(--shadow-md);--shadow-lg:var(--shadow-lg);--shadow-xl:var(--shadow-xl);--shadow-2xl:var(--shadow-2xl);--ease-out:cubic-bezier(0,0,.2,1);--animate-spin:spin 1s linear infinite;--animate-ping:ping 1s cubic-bezier(0,0,.2,1)infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--animate-bounce:bounce 1s infinite;--blur-sm:8px;--blur-md:12px;--blur-lg:16px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--shadow:var(--shadow)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\",\"Noto Color Emoji\");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*{border-color:var(--border);outline-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){*{outline-color:color-mix(in oklab,var(--ring)50%,transparent)}}body{background-color:var(--background);color:var(--foreground);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;position:relative}#root{position:relative}body:before,body:after{content:\"\";filter:blur(60px);opacity:.5;z-index:0;pointer-events:none;border-radius:9999px;position:fixed}html.dark body:before,.dark body:before,html.dark body:after,.dark body:after{opacity:.35}.custom-shadow{box-shadow:0 18px 40px #0f172a1a}html.dark .custom-shadow,.dark .custom-shadow{box-shadow:0 18px 40px #00000040}html{touch-action:manipulation;scroll-behavior:smooth;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%}body{padding-top:env(safe-area-inset-top);padding-bottom:env(safe-area-inset-bottom);padding-left:env(safe-area-inset-left);padding-right:env(safe-area-inset-right);overscroll-behavior:none}button,[role=button]{cursor:pointer}button,[role=button],input,select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-tap-highlight-color:transparent}button:not([role=checkbox]):not([data-state]),input:not([type=checkbox]):not([type=radio]),select,textarea{min-height:36px}button[data-size=icon-sm],button[data-size=icon-lg]{width:36px;min-width:36px;height:36px}@media(pointer:coarse){.min-touch-target{min-width:44px;min-height:44px}}input,textarea{font-size:16px}@media(max-width:768px){body:before{filter:blur(50px);opacity:.4;width:250px;height:250px;top:-8%;left:-8%}body:after{filter:blur(40px);opacity:.35;width:180px;height:180px;bottom:3%;right:-5%}}@media(max-width:480px){body:before{opacity:.3;width:180px;height:180px}body:after{opacity:.25;width:120px;height:120px}}.no-scrollbar::-webkit-scrollbar{display:none}.no-scrollbar{-ms-overflow-style:none;scrollbar-width:none}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing)*0)}.-top-1{top:calc(var(--spacing)*-1)}.top-0{top:calc(var(--spacing)*0)}.top-1{top:calc(var(--spacing)*1)}.top-1\\/2{top:50%}.top-2{top:calc(var(--spacing)*2)}.top-4{top:calc(var(--spacing)*4)}.top-\\[50\\%\\]{top:50%}.-right-1{right:calc(var(--spacing)*-1)}.-right-1\\.5{right:calc(var(--spacing)*-1.5)}.right-0{right:calc(var(--spacing)*0)}.right-1{right:calc(var(--spacing)*1)}.right-2{right:calc(var(--spacing)*2)}.right-3{right:calc(var(--spacing)*3)}.right-4{right:calc(var(--spacing)*4)}.right-6{right:calc(var(--spacing)*6)}.right-\\[-4px\\]{right:-4px}.bottom-0{bottom:calc(var(--spacing)*0)}.bottom-1{bottom:calc(var(--spacing)*1)}.bottom-2{bottom:calc(var(--spacing)*2)}.bottom-6{bottom:calc(var(--spacing)*6)}.left-0{left:calc(var(--spacing)*0)}.left-1\\/2{left:50%}.left-2{left:calc(var(--spacing)*2)}.left-3{left:calc(var(--spacing)*3)}.left-\\[-4px\\]{left:-4px}.left-\\[25px\\]{left:25px}.left-\\[50\\%\\]{left:50%}.z-0{z-index:0}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-50{z-index:50}.z-100{z-index:100}.z-\\[9999\\]{z-index:9999}.col-span-2{grid-column:span 2/span 2}.col-span-full{grid-column:1/-1}.col-start-2{grid-column-start:2}.row-span-2{grid-row:span 2/span 2}.row-start-1{grid-row-start:1}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.-mx-1{margin-inline:calc(var(--spacing)*-1)}.-mx-2{margin-inline:calc(var(--spacing)*-2)}.mx-1{margin-inline:calc(var(--spacing)*1)}.mx-1\\.5{margin-inline:calc(var(--spacing)*1.5)}.mx-2{margin-inline:calc(var(--spacing)*2)}.mx-auto{margin-inline:auto}.-my-0\\.5{margin-block:calc(var(--spacing)*-.5)}.my-1{margin-block:calc(var(--spacing)*1)}.my-1\\.5{margin-block:calc(var(--spacing)*1.5)}.my-3{margin-block:calc(var(--spacing)*3)}.my-4{margin-block:calc(var(--spacing)*4)}.my-6{margin-block:calc(var(--spacing)*6)}.mt-0{margin-top:calc(var(--spacing)*0)}.mt-0\\.5{margin-top:calc(var(--spacing)*.5)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-1\\.5{margin-top:calc(var(--spacing)*1.5)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-4{margin-top:calc(var(--spacing)*4)}.mt-6{margin-top:calc(var(--spacing)*6)}.mt-7{margin-top:calc(var(--spacing)*7)}.mt-8{margin-top:calc(var(--spacing)*8)}.mr-1{margin-right:calc(var(--spacing)*1)}.mr-1\\.5{margin-right:calc(var(--spacing)*1.5)}.mr-2{margin-right:calc(var(--spacing)*2)}.mr-3{margin-right:calc(var(--spacing)*3)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.ml-0\\.5{margin-left:calc(var(--spacing)*.5)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-2{margin-left:calc(var(--spacing)*2)}.ml-6{margin-left:calc(var(--spacing)*6)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.size-2{width:calc(var(--spacing)*2);height:calc(var(--spacing)*2)}.size-3\\/5{width:60%;height:60%}.size-4{width:calc(var(--spacing)*4);height:calc(var(--spacing)*4)}.size-5{width:calc(var(--spacing)*5);height:calc(var(--spacing)*5)}.size-8{width:calc(var(--spacing)*8);height:calc(var(--spacing)*8)}.size-9{width:calc(var(--spacing)*9);height:calc(var(--spacing)*9)}.size-10{width:calc(var(--spacing)*10);height:calc(var(--spacing)*10)}.h-1\\.5{height:calc(var(--spacing)*1.5)}.h-2{height:calc(var(--spacing)*2)}.h-2\\.5{height:calc(var(--spacing)*2.5)}.h-3{height:calc(var(--spacing)*3)}.h-3\\.5{height:calc(var(--spacing)*3.5)}.h-4{height:calc(var(--spacing)*4)}.h-4\\.5{height:calc(var(--spacing)*4.5)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-7{height:calc(var(--spacing)*7)}.h-8{height:calc(var(--spacing)*8)}.h-9{height:calc(var(--spacing)*9)}.h-10{height:calc(var(--spacing)*10)}.h-12{height:calc(var(--spacing)*12)}.h-14{height:calc(var(--spacing)*14)}.h-16{height:calc(var(--spacing)*16)}.h-20{height:calc(var(--spacing)*20)}.h-48{height:calc(var(--spacing)*48)}.h-64{height:calc(var(--spacing)*64)}.h-\\[1px\\]{height:1px}.h-\\[220px\\]{height:220px}.h-\\[var\\(--radix-select-trigger-height\\)\\]{height:var(--radix-select-trigger-height)}.h-auto{height:auto}.h-dvh{height:100dvh}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-40{max-height:calc(var(--spacing)*40)}.max-h-48{max-height:calc(var(--spacing)*48)}.max-h-60{max-height:calc(var(--spacing)*60)}.max-h-96{max-height:calc(var(--spacing)*96)}.max-h-\\[70vh\\]{max-height:70vh}.max-h-\\[80vh\\]{max-height:80vh}.max-h-\\[85vh\\]{max-height:85vh}.max-h-\\[300px\\]{max-height:300px}.max-h-\\[500px\\]{max-height:500px}.min-h-0{min-height:calc(var(--spacing)*0)}.min-h-30{min-height:calc(var(--spacing)*30)}.min-h-50{min-height:calc(var(--spacing)*50)}.min-h-\\[28px\\]{min-height:28px}.min-h-\\[80px\\]{min-height:80px}.min-h-\\[300px\\]{min-height:300px}.min-h-\\[450px\\]{min-height:450px}.min-h-full{min-height:100%}.min-h-screen{min-height:100vh}.w-1{width:calc(var(--spacing)*1)}.w-1\\.5{width:calc(var(--spacing)*1.5)}.w-2{width:calc(var(--spacing)*2)}.w-2\\.5{width:calc(var(--spacing)*2.5)}.w-3{width:calc(var(--spacing)*3)}.w-3\\.5{width:calc(var(--spacing)*3.5)}.w-4{width:calc(var(--spacing)*4)}.w-4\\.5{width:calc(var(--spacing)*4.5)}.w-5{width:calc(var(--spacing)*5)}.w-6{width:calc(var(--spacing)*6)}.w-7{width:calc(var(--spacing)*7)}.w-8{width:calc(var(--spacing)*8)}.w-9{width:calc(var(--spacing)*9)}.w-10{width:calc(var(--spacing)*10)}.w-12{width:calc(var(--spacing)*12)}.w-16{width:calc(var(--spacing)*16)}.w-20{width:calc(var(--spacing)*20)}.w-24{width:calc(var(--spacing)*24)}.w-25{width:calc(var(--spacing)*25)}.w-40{width:calc(var(--spacing)*40)}.w-48{width:calc(var(--spacing)*48)}.w-56{width:calc(var(--spacing)*56)}.w-\\[44px\\]{width:44px}.w-\\[80px\\]{width:80px}.w-\\[90vw\\]{width:90vw}.w-\\[95vw\\]{width:95vw}.w-\\[100px\\]{width:100px}.w-\\[110px\\]{width:110px}.w-\\[120px\\]{width:120px}.w-\\[150px\\]{width:150px}.w-\\[180px\\]{width:180px}.w-\\[320px\\]{width:320px}.w-\\[calc\\(100\\%-0\\.75rem\\)\\]{width:calc(100% - .75rem)}.w-\\[calc\\(100vw-2rem\\)\\]{width:calc(100vw - 2rem)}.w-auto{width:auto}.w-full{width:100%}.w-screen{width:100vw}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-5xl{max-width:var(--container-5xl)}.max-w-20{max-width:calc(var(--spacing)*20)}.max-w-37\\.5{max-width:calc(var(--spacing)*37.5)}.max-w-65{max-width:calc(var(--spacing)*65)}.max-w-75{max-width:calc(var(--spacing)*75)}.max-w-225{max-width:calc(var(--spacing)*225)}.max-w-\\[150px\\]{max-width:150px}.max-w-\\[200px\\]{max-width:200px}.max-w-\\[250px\\]{max-width:250px}.max-w-\\[300px\\]{max-width:300px}.max-w-\\[calc\\(100vw-3rem\\)\\]{max-width:calc(100vw - 3rem)}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-md{max-width:var(--container-md)}.max-w-none{max-width:none}.max-w-sm{max-width:var(--container-sm)}.min-w-0{min-width:calc(var(--spacing)*0)}.min-w-7\\.5{min-width:calc(var(--spacing)*7.5)}.min-w-20{min-width:calc(var(--spacing)*20)}.min-w-30{min-width:calc(var(--spacing)*30)}.min-w-32{min-width:calc(var(--spacing)*32)}.min-w-45{min-width:calc(var(--spacing)*45)}.min-w-\\[3\\.5rem\\]{min-width:3.5rem}.min-w-\\[8rem\\]{min-width:8rem}.min-w-\\[110px\\]{min-width:110px}.min-w-\\[120px\\]{min-width:120px}.min-w-\\[140px\\]{min-width:140px}.min-w-\\[180px\\]{min-width:180px}.min-w-\\[200px\\]{min-width:200px}.min-w-\\[250px\\]{min-width:250px}.min-w-\\[300px\\]{min-width:300px}.min-w-\\[var\\(--radix-select-trigger-width\\)\\]{min-width:var(--radix-select-trigger-width)}.flex-1{flex:1}.flex-none{flex:none}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.caption-bottom{caption-side:bottom}.border-collapse{border-collapse:collapse}.\\[transform-origin\\:center\\]{transform-origin:50%}.-translate-x-1\\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-\\[-50\\%\\]{--tw-translate-x:-50%;translate:var(--tw-translate-x)var(--tw-translate-y)}.-translate-y-1\\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-y-\\[-50\\%\\]{--tw-translate-y:-50%;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-y-\\[1px\\]{--tw-translate-y:1px;translate:var(--tw-translate-x)var(--tw-translate-y)}.scale-200{--tw-scale-x:200%;--tw-scale-y:200%;--tw-scale-z:200%;scale:var(--tw-scale-x)var(--tw-scale-y)}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.animate-bounce{animation:var(--animate-bounce)}.animate-in{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.animate-ping{animation:var(--animate-ping)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-default{cursor:default}.cursor-grab{cursor:grab}.cursor-help{cursor:help}.cursor-pointer{cursor:pointer}.cursor-text{cursor:text}.touch-none{touch-action:none}.resize{resize:both}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-\\[80px_1fr_80px\\]{grid-template-columns:80px 1fr 80px}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-nowrap{flex-wrap:nowrap}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.items-stretch{align-items:stretch}.justify-around{justify-content:space-around}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-0{gap:calc(var(--spacing)*0)}.gap-0\\.5{gap:calc(var(--spacing)*.5)}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-2\\.5{gap:calc(var(--spacing)*2.5)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}.gap-6{gap:calc(var(--spacing)*6)}:where(.space-y-0>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*0)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*0)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}.gap-x-2{column-gap:calc(var(--spacing)*2)}.gap-x-4{column-gap:calc(var(--spacing)*4)}.gap-x-6{column-gap:calc(var(--spacing)*6)}:where(.space-x-0\\.5>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*.5)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*.5)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*2)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-x-reverse)))}.gap-y-1{row-gap:calc(var(--spacing)*1)}.gap-y-2{row-gap:calc(var(--spacing)*2)}.gap-y-3{row-gap:calc(var(--spacing)*3)}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px*var(--tw-divide-y-reverse));border-bottom-width:calc(1px*calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-border\\/20>:not(:last-child)){border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){:where(.divide-border\\/20>:not(:last-child)){border-color:color-mix(in oklab,var(--border)20%,transparent)}}.self-center{align-self:center}.self-start{align-self:flex-start}.justify-self-end{justify-self:flex-end}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-3xl{border-radius:var(--radius-3xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-none{border-radius:0}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:calc(var(--radius) + 4px)}.rounded-t-2xl{border-top-left-radius:var(--radius-2xl);border-top-right-radius:var(--radius-2xl)}.rounded-r-lg{border-top-right-radius:var(--radius);border-bottom-right-radius:var(--radius)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-0{border-left-style:var(--tw-border-style);border-left-width:0}.border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-none{--tw-border-style:none;border-style:none}.border-background{border-color:var(--background)}.border-blue-200{border-color:var(--color-blue-200)}.border-blue-400{border-color:var(--color-blue-400)}.border-border,.border-border\\/10{border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.border-border\\/10{border-color:color-mix(in oklab,var(--border)10%,transparent)}}.border-border\\/30{border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.border-border\\/30{border-color:color-mix(in oklab,var(--border)30%,transparent)}}.border-border\\/40{border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.border-border\\/40{border-color:color-mix(in oklab,var(--border)40%,transparent)}}.border-border\\/50{border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.border-border\\/50{border-color:color-mix(in oklab,var(--border)50%,transparent)}}.border-border\\/60{border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.border-border\\/60{border-color:color-mix(in oklab,var(--border)60%,transparent)}}.border-border\\/70{border-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.border-border\\/70{border-color:color-mix(in oklab,var(--border)70%,transparent)}}.border-cyan-400{border-color:var(--color-cyan-400)}.border-destructive\\/10{border-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.border-destructive\\/10{border-color:color-mix(in oklab,var(--destructive)10%,transparent)}}.border-destructive\\/20{border-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.border-destructive\\/20{border-color:color-mix(in oklab,var(--destructive)20%,transparent)}}.border-emerald-500\\/20{border-color:#00bb7f33}@supports (color:color-mix(in lab,red,red)){.border-emerald-500\\/20{border-color:color-mix(in oklab,var(--color-emerald-500)20%,transparent)}}.border-gray-400{border-color:var(--color-gray-400)}.border-green-200{border-color:var(--color-green-200)}.border-green-200\\/50{border-color:#b9f8cf80}@supports (color:color-mix(in lab,red,red)){.border-green-200\\/50{border-color:color-mix(in oklab,var(--color-green-200)50%,transparent)}}.border-green-300{border-color:var(--color-green-300)}.border-green-400{border-color:var(--color-green-400)}.border-green-500\\/10{border-color:#00c7581a}@supports (color:color-mix(in lab,red,red)){.border-green-500\\/10{border-color:color-mix(in oklab,var(--color-green-500)10%,transparent)}}.border-input{border-color:var(--input)}.border-muted-foreground\\/20{border-color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.border-muted-foreground\\/20{border-color:color-mix(in oklab,var(--muted-foreground)20%,transparent)}}.border-orange-400{border-color:var(--color-orange-400)}.border-orange-500\\/10{border-color:#fe6e001a}@supports (color:color-mix(in lab,red,red)){.border-orange-500\\/10{border-color:color-mix(in oklab,var(--color-orange-500)10%,transparent)}}.border-primary,.border-primary\\/10{border-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.border-primary\\/10{border-color:color-mix(in oklab,var(--primary)10%,transparent)}}.border-primary\\/20{border-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.border-primary\\/20{border-color:color-mix(in oklab,var(--primary)20%,transparent)}}.border-primary\\/50{border-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.border-primary\\/50{border-color:color-mix(in oklab,var(--primary)50%,transparent)}}.border-purple-400{border-color:var(--color-purple-400)}.border-red-200\\/50{border-color:#ffcaca80}@supports (color:color-mix(in lab,red,red)){.border-red-200\\/50{border-color:color-mix(in oklab,var(--color-red-200)50%,transparent)}}.border-red-400{border-color:var(--color-red-400)}.border-rose-500\\/20{border-color:#ff235733}@supports (color:color-mix(in lab,red,red)){.border-rose-500\\/20{border-color:color-mix(in oklab,var(--color-rose-500)20%,transparent)}}.border-sidebar-border{border-color:var(--sidebar-border)}.border-slate-100{border-color:var(--color-slate-100)}.border-slate-200{border-color:var(--color-slate-200)}.border-teal-400{border-color:var(--color-teal-400)}.border-transparent{border-color:#0000}.border-white\\/30{border-color:#ffffff4d}@supports (color:color-mix(in lab,red,red)){.border-white\\/30{border-color:color-mix(in oklab,var(--color-white)30%,transparent)}}.border-yellow-400{border-color:var(--color-yellow-400)}.border-x-transparent{border-inline-color:#0000}.border-y-border{border-block-color:var(--border)}.border-y-transparent{border-block-color:#0000}.border-t-transparent{border-top-color:#0000}.border-t-white{border-top-color:var(--color-white)}.border-r-border,.border-r-border\\/30{border-right-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.border-r-border\\/30{border-right-color:color-mix(in oklab,var(--border)30%,transparent)}}.border-r-transparent{border-right-color:#0000}.border-l-blue-600{border-left-color:var(--color-blue-600)}.border-l-green-600{border-left-color:var(--color-green-600)}.border-l-muted{border-left-color:var(--muted)}.border-l-orange-500{border-left-color:var(--color-orange-500)}.border-l-transparent{border-left-color:#0000}.bg-\\[\\#1E88E5\\]{background-color:#1e88e5}.bg-\\[\\#7C4DFF\\]{background-color:#7c4dff}.bg-\\[\\#08b94e\\]{background-color:#08b94e}.bg-\\[\\#FF8A33\\]{background-color:#ff8a33}.bg-accent{background-color:var(--accent)}.bg-amber-500{background-color:var(--color-amber-500)}.bg-amber-500\\/10{background-color:#f99c001a}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\\/10{background-color:color-mix(in oklab,var(--color-amber-500)10%,transparent)}}.bg-background,.bg-background\\/50{background-color:var(--background)}@supports (color:color-mix(in lab,red,red)){.bg-background\\/50{background-color:color-mix(in oklab,var(--background)50%,transparent)}}.bg-background\\/80{background-color:var(--background)}@supports (color:color-mix(in lab,red,red)){.bg-background\\/80{background-color:color-mix(in oklab,var(--background)80%,transparent)}}.bg-black{background-color:var(--color-black)}.bg-black\\/5{background-color:#0000000d}@supports (color:color-mix(in lab,red,red)){.bg-black\\/5{background-color:color-mix(in oklab,var(--color-black)5%,transparent)}}.bg-black\\/60{background-color:#0009}@supports (color:color-mix(in lab,red,red)){.bg-black\\/60{background-color:color-mix(in oklab,var(--color-black)60%,transparent)}}.bg-black\\/80{background-color:#000c}@supports (color:color-mix(in lab,red,red)){.bg-black\\/80{background-color:color-mix(in oklab,var(--color-black)80%,transparent)}}.bg-blue-50{background-color:var(--color-blue-50)}.bg-blue-100{background-color:var(--color-blue-100)}.bg-blue-500{background-color:var(--color-blue-500)}.bg-blue-500\\/10{background-color:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\\/10{background-color:color-mix(in oklab,var(--color-blue-500)10%,transparent)}}.bg-border\\/50{background-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.bg-border\\/50{background-color:color-mix(in oklab,var(--border)50%,transparent)}}.bg-border\\/60{background-color:var(--border)}@supports (color:color-mix(in lab,red,red)){.bg-border\\/60{background-color:color-mix(in oklab,var(--border)60%,transparent)}}.bg-card,.bg-card\\/10{background-color:var(--card)}@supports (color:color-mix(in lab,red,red)){.bg-card\\/10{background-color:color-mix(in oklab,var(--card)10%,transparent)}}.bg-card\\/50{background-color:var(--card)}@supports (color:color-mix(in lab,red,red)){.bg-card\\/50{background-color:color-mix(in oklab,var(--card)50%,transparent)}}.bg-card\\/80{background-color:var(--card)}@supports (color:color-mix(in lab,red,red)){.bg-card\\/80{background-color:color-mix(in oklab,var(--card)80%,transparent)}}.bg-cyan-50{background-color:var(--color-cyan-50)}.bg-destructive,.bg-destructive\\/5{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.bg-destructive\\/5{background-color:color-mix(in oklab,var(--destructive)5%,transparent)}}.bg-destructive\\/10{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.bg-destructive\\/10{background-color:color-mix(in oklab,var(--destructive)10%,transparent)}}.bg-emerald-500{background-color:var(--color-emerald-500)}.bg-emerald-500\\/10{background-color:#00bb7f1a}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\\/10{background-color:color-mix(in oklab,var(--color-emerald-500)10%,transparent)}}.bg-gray-50{background-color:var(--color-gray-50)}.bg-green-50{background-color:var(--color-green-50)}.bg-green-100{background-color:var(--color-green-100)}.bg-green-500\\/5{background-color:#00c7580d}@supports (color:color-mix(in lab,red,red)){.bg-green-500\\/5{background-color:color-mix(in oklab,var(--color-green-500)5%,transparent)}}.bg-green-500\\/10{background-color:#00c7581a}@supports (color:color-mix(in lab,red,red)){.bg-green-500\\/10{background-color:color-mix(in oklab,var(--color-green-500)10%,transparent)}}.bg-muted,.bg-muted\\/20{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.bg-muted\\/20{background-color:color-mix(in oklab,var(--muted)20%,transparent)}}.bg-muted\\/30{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.bg-muted\\/30{background-color:color-mix(in oklab,var(--muted)30%,transparent)}}.bg-muted\\/40{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.bg-muted\\/40{background-color:color-mix(in oklab,var(--muted)40%,transparent)}}.bg-muted\\/50{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.bg-muted\\/50{background-color:color-mix(in oklab,var(--muted)50%,transparent)}}.bg-muted\\/60{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.bg-muted\\/60{background-color:color-mix(in oklab,var(--muted)60%,transparent)}}.bg-orange-50{background-color:var(--color-orange-50)}.bg-orange-100{background-color:var(--color-orange-100)}.bg-orange-500{background-color:var(--color-orange-500)}.bg-orange-500\\/5{background-color:#fe6e000d}@supports (color:color-mix(in lab,red,red)){.bg-orange-500\\/5{background-color:color-mix(in oklab,var(--color-orange-500)5%,transparent)}}.bg-orange-500\\/10{background-color:#fe6e001a}@supports (color:color-mix(in lab,red,red)){.bg-orange-500\\/10{background-color:color-mix(in oklab,var(--color-orange-500)10%,transparent)}}.bg-popover,.bg-popover\\/95{background-color:var(--popover)}@supports (color:color-mix(in lab,red,red)){.bg-popover\\/95{background-color:color-mix(in oklab,var(--popover)95%,transparent)}}.bg-primary,.bg-primary\\/5{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\\/5{background-color:color-mix(in oklab,var(--primary)5%,transparent)}}.bg-primary\\/10{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.bg-primary\\/10{background-color:color-mix(in oklab,var(--primary)10%,transparent)}}.bg-purple-50{background-color:var(--color-purple-50)}.bg-purple-500{background-color:var(--color-purple-500)}.bg-purple-500\\/10{background-color:#ac4bff1a}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\\/10{background-color:color-mix(in oklab,var(--color-purple-500)10%,transparent)}}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-red-400{background-color:var(--color-red-400)}.bg-red-500{background-color:var(--color-red-500)}.bg-ring{background-color:var(--ring)}.bg-rose-500{background-color:var(--color-rose-500)}.bg-rose-500\\/10{background-color:#ff23571a}@supports (color:color-mix(in lab,red,red)){.bg-rose-500\\/10{background-color:color-mix(in oklab,var(--color-rose-500)10%,transparent)}}.bg-secondary,.bg-secondary\\/20{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){.bg-secondary\\/20{background-color:color-mix(in oklab,var(--secondary)20%,transparent)}}.bg-secondary\\/50{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){.bg-secondary\\/50{background-color:color-mix(in oklab,var(--secondary)50%,transparent)}}.bg-sidebar{background-color:var(--sidebar)}.bg-sky-700{background-color:var(--color-sky-700)}.bg-slate-50{background-color:var(--color-slate-50)}.bg-slate-100{background-color:var(--color-slate-100)}.bg-teal-50{background-color:var(--color-teal-50)}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-white\\/80{background-color:#fffc}@supports (color:color-mix(in lab,red,red)){.bg-white\\/80{background-color:color-mix(in oklab,var(--color-white)80%,transparent)}}.bg-yellow-50{background-color:var(--color-yellow-50)}.bg-gradient-to-l{--tw-gradient-position:to left in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-r{--tw-gradient-position:to right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.from-muted\\/60{--tw-gradient-from:var(--muted)}@supports (color:color-mix(in lab,red,red)){.from-muted\\/60{--tw-gradient-from:color-mix(in oklab,var(--muted)60%,transparent)}}.from-muted\\/60{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-transparent{--tw-gradient-to:transparent;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.fill-current{fill:currentColor}.fill-red-500\\/10{fill:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.fill-red-500\\/10{fill:color-mix(in oklab,var(--color-red-500)10%,transparent)}}.stroke-\\[1\\.5\\]{stroke-width:1.5px}.object-contain{object-fit:contain}.p-0{padding:calc(var(--spacing)*0)}.p-1{padding:calc(var(--spacing)*1)}.p-1\\.5{padding:calc(var(--spacing)*1.5)}.p-2{padding:calc(var(--spacing)*2)}.p-2\\.5{padding:calc(var(--spacing)*2.5)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-5{padding:calc(var(--spacing)*5)}.p-6{padding:calc(var(--spacing)*6)}.p-8{padding:calc(var(--spacing)*8)}.p-12{padding:calc(var(--spacing)*12)}.p-24{padding:calc(var(--spacing)*24)}.px-0\\.5{padding-inline:calc(var(--spacing)*.5)}.px-1{padding-inline:calc(var(--spacing)*1)}.px-1\\.5{padding-inline:calc(var(--spacing)*1.5)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-2\\.5{padding-inline:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-5{padding-inline:calc(var(--spacing)*5)}.px-6{padding-inline:calc(var(--spacing)*6)}.px-8{padding-inline:calc(var(--spacing)*8)}.py-0{padding-block:calc(var(--spacing)*0)}.py-0\\.5{padding-block:calc(var(--spacing)*.5)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-2\\.5{padding-block:calc(var(--spacing)*2.5)}.py-3{padding-block:calc(var(--spacing)*3)}.py-4{padding-block:calc(var(--spacing)*4)}.py-6{padding-block:calc(var(--spacing)*6)}.py-8{padding-block:calc(var(--spacing)*8)}.py-10{padding-block:calc(var(--spacing)*10)}.py-12{padding-block:calc(var(--spacing)*12)}.py-20{padding-block:calc(var(--spacing)*20)}.pt-0{padding-top:calc(var(--spacing)*0)}.pt-0\\.5{padding-top:calc(var(--spacing)*.5)}.pt-1{padding-top:calc(var(--spacing)*1)}.pt-1\\.5{padding-top:calc(var(--spacing)*1.5)}.pt-2{padding-top:calc(var(--spacing)*2)}.pt-3{padding-top:calc(var(--spacing)*3)}.pt-4{padding-top:calc(var(--spacing)*4)}.pr-2{padding-right:calc(var(--spacing)*2)}.pr-3{padding-right:calc(var(--spacing)*3)}.pr-7{padding-right:calc(var(--spacing)*7)}.pr-8{padding-right:calc(var(--spacing)*8)}.pr-10{padding-right:calc(var(--spacing)*10)}.pr-14{padding-right:calc(var(--spacing)*14)}.pb-0\\.5{padding-bottom:calc(var(--spacing)*.5)}.pb-1{padding-bottom:calc(var(--spacing)*1)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.pb-18{padding-bottom:calc(var(--spacing)*18)}.pb-24{padding-bottom:calc(var(--spacing)*24)}.pb-\\[env\\(safe-area-inset-bottom\\)\\]{padding-bottom:env(safe-area-inset-bottom)}.pl-0{padding-left:calc(var(--spacing)*0)}.pl-1{padding-left:calc(var(--spacing)*1)}.pl-1\\.5{padding-left:calc(var(--spacing)*1.5)}.pl-2{padding-left:calc(var(--spacing)*2)}.pl-4{padding-left:calc(var(--spacing)*4)}.pl-6{padding-left:calc(var(--spacing)*6)}.pl-8{padding-left:calc(var(--spacing)*8)}.pl-9{padding-left:calc(var(--spacing)*9)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.align-middle{vertical-align:middle}.align-top{vertical-align:top}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\\[0\\.9em\\]{font-size:.9em}.text-\\[9px\\]{font-size:9px}.text-\\[10px\\]{font-size:10px}.text-\\[11px\\]{font-size:11px}.text-\\[12px\\]{font-size:12px}.text-\\[13px\\]{font-size:13px}.leading-7{--tw-leading:calc(var(--spacing)*7);line-height:calc(var(--spacing)*7)}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.text-nowrap{text-wrap:nowrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.text-ellipsis{text-overflow:ellipsis}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.whitespace-pre-wrap{white-space:pre-wrap}.text-\\[\\#3DDC84\\]{color:#3ddc84}.text-\\[\\#0078D4\\]{color:#0078d4}.text-\\[\\#4285F4\\]{color:#4285f4}.text-\\[\\#555555\\]{color:#555}.text-accent-foreground{color:var(--accent-foreground)}.text-amber-500{color:var(--color-amber-500)}.text-blue-400{color:var(--color-blue-400)}.text-blue-500{color:var(--color-blue-500)}.text-blue-500\\/70{color:#3080ffb3}@supports (color:color-mix(in lab,red,red)){.text-blue-500\\/70{color:color-mix(in oklab,var(--color-blue-500)70%,transparent)}}.text-blue-600{color:var(--color-blue-600)}.text-blue-700{color:var(--color-blue-700)}.text-card-foreground{color:var(--card-foreground)}.text-current{color:currentColor}.text-cyan-400{color:var(--color-cyan-400)}.text-destructive{color:var(--destructive)}.text-destructive-foreground{color:var(--destructive-foreground)}.text-destructive\\/70{color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.text-destructive\\/70{color:color-mix(in oklab,var(--destructive)70%,transparent)}}.text-destructive\\/80{color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.text-destructive\\/80{color:color-mix(in oklab,var(--destructive)80%,transparent)}}.text-destructive\\/90{color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.text-destructive\\/90{color:color-mix(in oklab,var(--destructive)90%,transparent)}}.text-emerald-500{color:var(--color-emerald-500)}.text-emerald-500\\/70{color:#00bb7fb3}@supports (color:color-mix(in lab,red,red)){.text-emerald-500\\/70{color:color-mix(in oklab,var(--color-emerald-500)70%,transparent)}}.text-foreground,.text-foreground\\/80{color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.text-foreground\\/80{color:color-mix(in oklab,var(--foreground)80%,transparent)}}.text-foreground\\/90{color:var(--foreground)}@supports (color:color-mix(in lab,red,red)){.text-foreground\\/90{color:color-mix(in oklab,var(--foreground)90%,transparent)}}.text-gray-400{color:var(--color-gray-400)}.text-green-400{color:var(--color-green-400)}.text-green-500{color:var(--color-green-500)}.text-green-600{color:var(--color-green-600)}.text-green-600\\/80{color:#00a544cc}@supports (color:color-mix(in lab,red,red)){.text-green-600\\/80{color:color-mix(in oklab,var(--color-green-600)80%,transparent)}}.text-green-700{color:var(--color-green-700)}.text-green-900{color:var(--color-green-900)}.text-indigo-500\\/70{color:#625fffb3}@supports (color:color-mix(in lab,red,red)){.text-indigo-500\\/70{color:color-mix(in oklab,var(--color-indigo-500)70%,transparent)}}.text-muted-foreground,.text-muted-foreground\\/30{color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\\/30{color:color-mix(in oklab,var(--muted-foreground)30%,transparent)}}.text-muted-foreground\\/40{color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\\/40{color:color-mix(in oklab,var(--muted-foreground)40%,transparent)}}.text-muted-foreground\\/50{color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\\/50{color:color-mix(in oklab,var(--muted-foreground)50%,transparent)}}.text-muted-foreground\\/60{color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\\/60{color:color-mix(in oklab,var(--muted-foreground)60%,transparent)}}.text-muted-foreground\\/70{color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\\/70{color:color-mix(in oklab,var(--muted-foreground)70%,transparent)}}.text-muted-foreground\\/80{color:var(--muted-foreground)}@supports (color:color-mix(in lab,red,red)){.text-muted-foreground\\/80{color:color-mix(in oklab,var(--muted-foreground)80%,transparent)}}.text-orange-400{color:var(--color-orange-400)}.text-orange-500{color:var(--color-orange-500)}.text-orange-500\\/70{color:#fe6e00b3}@supports (color:color-mix(in lab,red,red)){.text-orange-500\\/70{color:color-mix(in oklab,var(--color-orange-500)70%,transparent)}}.text-orange-600{color:var(--color-orange-600)}.text-orange-600\\/80{color:#f05100cc}@supports (color:color-mix(in lab,red,red)){.text-orange-600\\/80{color:color-mix(in oklab,var(--color-orange-600)80%,transparent)}}.text-pink-500\\/70{color:#f6339ab3}@supports (color:color-mix(in lab,red,red)){.text-pink-500\\/70{color:color-mix(in oklab,var(--color-pink-500)70%,transparent)}}.text-popover-foreground{color:var(--popover-foreground)}.text-primary{color:var(--primary)}.text-primary-foreground{color:var(--primary-foreground)}.text-primary\\/60{color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.text-primary\\/60{color:color-mix(in oklab,var(--primary)60%,transparent)}}.text-primary\\/70{color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.text-primary\\/70{color:color-mix(in oklab,var(--primary)70%,transparent)}}.text-purple-400{color:var(--color-purple-400)}.text-purple-500{color:var(--color-purple-500)}.text-purple-500\\/70{color:#ac4bffb3}@supports (color:color-mix(in lab,red,red)){.text-purple-500\\/70{color:color-mix(in oklab,var(--color-purple-500)70%,transparent)}}.text-red-400{color:var(--color-red-400)}.text-red-500{color:var(--color-red-500)}.text-red-900{color:var(--color-red-900)}.text-rose-500{color:var(--color-rose-500)}.text-secondary-foreground{color:var(--secondary-foreground)}.text-sidebar-foreground{color:var(--sidebar-foreground)}.text-slate-300{color:var(--color-slate-300)}.text-slate-400{color:var(--color-slate-400)}.text-slate-500{color:var(--color-slate-500)}.text-slate-700{color:var(--color-slate-700)}.text-teal-400{color:var(--color-teal-400)}.text-transparent{color:#0000}.text-white{color:var(--color-white)}.text-yellow-400{color:var(--color-yellow-400)}.text-yellow-500{color:var(--color-yellow-500)}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,)var(--tw-slashed-zero,)var(--tw-numeric-figure,)var(--tw-numeric-spacing,)var(--tw-numeric-fraction,)}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.underline{text-decoration-line:underline}.decoration-red-400{-webkit-text-decoration-color:var(--color-red-400);text-decoration-color:var(--color-red-400)}.underline-offset-4{text-underline-offset:4px}.accent-primary{accent-color:var(--primary)}.opacity-0{opacity:0}.opacity-10{opacity:.1}.opacity-20{opacity:.2}.opacity-30{opacity:.3}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.opacity-80{opacity:.8}.opacity-90{opacity:.9}.opacity-100{opacity:1}.shadow-2xl{--tw-shadow:var(--shadow-2xl);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-inner{--tw-shadow:inset 0 2px 4px 0 var(--tw-shadow-color,#0000000d);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:var(--shadow-lg);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-md{--tw-shadow:var(--shadow-md);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:var(--shadow-sm);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:var(--shadow-xl);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xs{--tw-shadow:var(--shadow-xs);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-primary\\/20{--tw-shadow-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.shadow-primary\\/20{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--primary)20%,transparent)var(--tw-shadow-alpha),transparent)}}.ring-background{--tw-ring-color:var(--background)}.ring-ring,.ring-ring\\/30{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){.ring-ring\\/30{--tw-ring-color:color-mix(in oklab,var(--ring)30%,transparent)}}.ring-offset-background{--tw-ring-offset-color:var(--background)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.backdrop-blur-lg{--tw-backdrop-blur:blur(var(--blur-lg));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.backdrop-blur-md{--tw-backdrop-blur:blur(var(--blur-md));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-100{--tw-duration:.1s;transition-duration:.1s}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.duration-500{--tw-duration:.5s;transition-duration:.5s}.duration-700{--tw-duration:.7s;transition-duration:.7s}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.fade-in-0{--tw-enter-opacity:0}.fill-mode-forwards{--tw-animation-fill-mode:forwards;animation-fill-mode:forwards}.outline-none{--tw-outline-style:none;outline-style:none}.select-all{-webkit-user-select:all;user-select:all}.select-none{-webkit-user-select:none;user-select:none}.select-text{-webkit-user-select:text;user-select:text}.zoom-in-95{--tw-enter-scale:.95}.\\[animation-direction\\:alternate\\]{animation-direction:alternate}.fade-in{--tw-enter-opacity:0}.running{animation-play-state:running}.slide-in-from-bottom-4{--tw-enter-translate-y:calc(4*var(--spacing))}.slide-in-from-left-2{--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.slide-in-from-top-2{--tw-enter-translate-y:calc(2*var(--spacing)*-1)}.slide-in-from-top-4{--tw-enter-translate-y:calc(4*var(--spacing)*-1)}.zoom-in{--tw-enter-scale:0}.group-focus-within\\:text-primary:is(:where(.group):focus-within *){color:var(--primary)}@media(hover:hover){.group-hover\\:scale-105:is(:where(.group):hover *){--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x)var(--tw-scale-y)}.group-hover\\:scale-110:is(:where(.group):hover *){--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x)var(--tw-scale-y)}.group-hover\\:border-primary\\/20:is(:where(.group):hover *){border-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.group-hover\\:border-primary\\/20:is(:where(.group):hover *){border-color:color-mix(in oklab,var(--primary)20%,transparent)}}.group-hover\\:bg-primary\\/10:is(:where(.group):hover *){background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.group-hover\\:bg-primary\\/10:is(:where(.group):hover *){background-color:color-mix(in oklab,var(--primary)10%,transparent)}}.group-hover\\:text-primary:is(:where(.group):hover *){color:var(--primary)}.group-hover\\:text-slate-400:is(:where(.group):hover *){color:var(--color-slate-400)}.group-hover\\:opacity-40:is(:where(.group):hover *){opacity:.4}.group-hover\\:opacity-100:is(:where(.group):hover *){opacity:1}.group-hover\\/stat\\:text-primary:is(:where(.group\\/stat):hover *){color:var(--primary)}}.peer-disabled\\:cursor-not-allowed:is(:where(.peer):disabled~*){cursor:not-allowed}.peer-disabled\\:opacity-70:is(:where(.peer):disabled~*){opacity:.7}.selection\\:bg-blue-100 ::selection{background-color:var(--color-blue-100)}.selection\\:bg-blue-100::selection{background-color:var(--color-blue-100)}.selection\\:text-blue-900 ::selection{color:var(--color-blue-900)}.selection\\:text-blue-900::selection{color:var(--color-blue-900)}.file\\:border-0::file-selector-button{border-style:var(--tw-border-style);border-width:0}.file\\:bg-transparent::file-selector-button{background-color:#0000}.file\\:text-sm::file-selector-button{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.file\\:font-medium::file-selector-button{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.file\\:text-foreground::file-selector-button{color:var(--foreground)}.placeholder\\:text-muted-foreground::placeholder{color:var(--muted-foreground)}.first\\:mt-0:first-child{margin-top:calc(var(--spacing)*0)}.last\\:mb-0:last-child{margin-bottom:calc(var(--spacing)*0)}.focus-within\\:ring-2:focus-within{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-within\\:ring-primary\\/20:focus-within{--tw-ring-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.focus-within\\:ring-primary\\/20:focus-within{--tw-ring-color:color-mix(in oklab,var(--primary)20%,transparent)}}@media(hover:hover){.hover\\:border-blue-300:hover{border-color:var(--color-blue-300)}.hover\\:border-destructive\\/40:hover{border-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\\:border-destructive\\/40:hover{border-color:color-mix(in oklab,var(--destructive)40%,transparent)}}.hover\\:border-destructive\\/50:hover{border-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\\:border-destructive\\/50:hover{border-color:color-mix(in oklab,var(--destructive)50%,transparent)}}.hover\\:border-green-300:hover{border-color:var(--color-green-300)}.hover\\:border-green-500\\/30:hover{border-color:#00c7584d}@supports (color:color-mix(in lab,red,red)){.hover\\:border-green-500\\/30:hover{border-color:color-mix(in oklab,var(--color-green-500)30%,transparent)}}.hover\\:border-orange-500\\/30:hover{border-color:#fe6e004d}@supports (color:color-mix(in lab,red,red)){.hover\\:border-orange-500\\/30:hover{border-color:color-mix(in oklab,var(--color-orange-500)30%,transparent)}}.hover\\:border-primary\\/20:hover{border-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\\:border-primary\\/20:hover{border-color:color-mix(in oklab,var(--primary)20%,transparent)}}.hover\\:border-primary\\/30:hover{border-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\\:border-primary\\/30:hover{border-color:color-mix(in oklab,var(--primary)30%,transparent)}}.hover\\:border-primary\\/50:hover{border-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\\:border-primary\\/50:hover{border-color:color-mix(in oklab,var(--primary)50%,transparent)}}.hover\\:bg-accent:hover,.hover\\:bg-accent\\/30:hover{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.hover\\:bg-accent\\/30:hover{background-color:color-mix(in oklab,var(--accent)30%,transparent)}}.hover\\:bg-accent\\/50:hover{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.hover\\:bg-accent\\/50:hover{background-color:color-mix(in oklab,var(--accent)50%,transparent)}}.hover\\:bg-accent\\/80:hover{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.hover\\:bg-accent\\/80:hover{background-color:color-mix(in oklab,var(--accent)80%,transparent)}}.hover\\:bg-blue-50:hover{background-color:var(--color-blue-50)}.hover\\:bg-destructive\\/5:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\\:bg-destructive\\/5:hover{background-color:color-mix(in oklab,var(--destructive)5%,transparent)}}.hover\\:bg-destructive\\/10:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\\:bg-destructive\\/10:hover{background-color:color-mix(in oklab,var(--destructive)10%,transparent)}}.hover\\:bg-destructive\\/80:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\\:bg-destructive\\/80:hover{background-color:color-mix(in oklab,var(--destructive)80%,transparent)}}.hover\\:bg-destructive\\/90:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.hover\\:bg-destructive\\/90:hover{background-color:color-mix(in oklab,var(--destructive)90%,transparent)}}.hover\\:bg-green-50:hover{background-color:var(--color-green-50)}.hover\\:bg-green-500\\/10:hover{background-color:#00c7581a}@supports (color:color-mix(in lab,red,red)){.hover\\:bg-green-500\\/10:hover{background-color:color-mix(in oklab,var(--color-green-500)10%,transparent)}}.hover\\:bg-muted:hover,.hover\\:bg-muted\\/30:hover{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.hover\\:bg-muted\\/30:hover{background-color:color-mix(in oklab,var(--muted)30%,transparent)}}.hover\\:bg-muted\\/50:hover{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.hover\\:bg-muted\\/50:hover{background-color:color-mix(in oklab,var(--muted)50%,transparent)}}.hover\\:bg-muted\\/80:hover{background-color:var(--muted)}@supports (color:color-mix(in lab,red,red)){.hover\\:bg-muted\\/80:hover{background-color:color-mix(in oklab,var(--muted)80%,transparent)}}.hover\\:bg-orange-500\\/10:hover{background-color:#fe6e001a}@supports (color:color-mix(in lab,red,red)){.hover\\:bg-orange-500\\/10:hover{background-color:color-mix(in oklab,var(--color-orange-500)10%,transparent)}}.hover\\:bg-primary\\/5:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\\:bg-primary\\/5:hover{background-color:color-mix(in oklab,var(--primary)5%,transparent)}}.hover\\:bg-primary\\/10:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\\:bg-primary\\/10:hover{background-color:color-mix(in oklab,var(--primary)10%,transparent)}}.hover\\:bg-primary\\/80:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\\:bg-primary\\/80:hover{background-color:color-mix(in oklab,var(--primary)80%,transparent)}}.hover\\:bg-primary\\/90:hover{background-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\\:bg-primary\\/90:hover{background-color:color-mix(in oklab,var(--primary)90%,transparent)}}.hover\\:bg-secondary\\/80:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab,red,red)){.hover\\:bg-secondary\\/80:hover{background-color:color-mix(in oklab,var(--secondary)80%,transparent)}}.hover\\:bg-sky-900:hover{background-color:var(--color-sky-900)}.hover\\:bg-transparent:hover{background-color:#0000}.hover\\:bg-white:hover{background-color:var(--color-white)}.hover\\:text-accent-foreground:hover{color:var(--accent-foreground)}.hover\\:text-blue-500:hover{color:var(--color-blue-500)}.hover\\:text-blue-600:hover{color:var(--color-blue-600)}.hover\\:text-destructive:hover{color:var(--destructive)}.hover\\:text-foreground:hover{color:var(--foreground)}.hover\\:text-green-600:hover{color:var(--color-green-600)}.hover\\:text-green-700:hover{color:var(--color-green-700)}.hover\\:text-muted-foreground:hover{color:var(--muted-foreground)}.hover\\:text-orange-500:hover{color:var(--color-orange-500)}.hover\\:text-primary:hover{color:var(--primary)}.hover\\:text-purple-600:hover{color:var(--color-purple-600)}.hover\\:text-white:hover{color:var(--color-white)}.hover\\:underline:hover{text-decoration-line:underline}.hover\\:opacity-80:hover{opacity:.8}.hover\\:opacity-100:hover{opacity:1}.hover\\:shadow-md:hover{--tw-shadow:var(--shadow-md);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\\:shadow-sm:hover{--tw-shadow:var(--shadow-sm);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\\:shadow-primary\\/30:hover{--tw-shadow-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.hover\\:shadow-primary\\/30:hover{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--primary)30%,transparent)var(--tw-shadow-alpha),transparent)}}.hover\\:ring-ring\\/50:hover{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){.hover\\:ring-ring\\/50:hover{--tw-ring-color:color-mix(in oklab,var(--ring)50%,transparent)}}}.focus\\:border-primary\\/50:focus{border-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.focus\\:border-primary\\/50:focus{border-color:color-mix(in oklab,var(--primary)50%,transparent)}}.focus\\:bg-accent:focus,.focus\\:bg-accent\\/80:focus{background-color:var(--accent)}@supports (color:color-mix(in lab,red,red)){.focus\\:bg-accent\\/80:focus{background-color:color-mix(in oklab,var(--accent)80%,transparent)}}.focus\\:bg-destructive\\/10:focus{background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.focus\\:bg-destructive\\/10:focus{background-color:color-mix(in oklab,var(--destructive)10%,transparent)}}.focus\\:text-accent-foreground:focus{color:var(--accent-foreground)}.focus\\:text-destructive:focus{color:var(--destructive)}.focus\\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\\:ring-primary\\/20:focus{--tw-ring-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.focus\\:ring-primary\\/20:focus{--tw-ring-color:color-mix(in oklab,var(--primary)20%,transparent)}}.focus\\:ring-primary\\/50:focus{--tw-ring-color:var(--primary)}@supports (color:color-mix(in lab,red,red)){.focus\\:ring-primary\\/50:focus{--tw-ring-color:color-mix(in oklab,var(--primary)50%,transparent)}}.focus\\:ring-ring:focus{--tw-ring-color:var(--ring)}.focus\\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus\\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\\:border-ring:focus-visible{border-color:var(--ring)}.focus-visible\\:ring-0:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(0px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\\:ring-\\[3px\\]:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(3px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\\:ring-primary:focus-visible{--tw-ring-color:var(--primary)}.focus-visible\\:ring-ring:focus-visible,.focus-visible\\:ring-ring\\/50:focus-visible{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab,red,red)){.focus-visible\\:ring-ring\\/50:focus-visible{--tw-ring-color:color-mix(in oklab,var(--ring)50%,transparent)}}.focus-visible\\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus-visible\\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.active\\:scale-95:active{--tw-scale-x:95%;--tw-scale-y:95%;--tw-scale-z:95%;scale:var(--tw-scale-x)var(--tw-scale-y)}.active\\:cursor-grabbing:active{cursor:grabbing}.active\\:bg-accent:active{background-color:var(--accent)}.disabled\\:pointer-events-none:disabled{pointer-events:none}.disabled\\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\\:opacity-40:disabled{opacity:.4}.disabled\\:opacity-50:disabled{opacity:.5}@media(hover:hover){.disabled\\:hover\\:bg-transparent:disabled:hover{background-color:#0000}}.has-\\[\\>svg\\]\\:px-2\\.5:has(>svg){padding-inline:calc(var(--spacing)*2.5)}.has-\\[\\>svg\\]\\:px-3:has(>svg){padding-inline:calc(var(--spacing)*3)}.has-\\[\\>svg\\]\\:px-4:has(>svg){padding-inline:calc(var(--spacing)*4)}.data-\\[disabled\\]\\:pointer-events-none[data-disabled]{pointer-events:none}.data-\\[disabled\\]\\:opacity-50[data-disabled]{opacity:.5}.data-\\[side\\=bottom\\]\\:translate-y-1[data-side=bottom]{--tw-translate-y:calc(var(--spacing)*1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\\[side\\=bottom\\]\\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y:calc(2*var(--spacing)*-1)}.data-\\[side\\=left\\]\\:-translate-x-1[data-side=left]{--tw-translate-x:calc(var(--spacing)*-1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\\[side\\=left\\]\\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x:calc(2*var(--spacing))}.data-\\[side\\=right\\]\\:translate-x-1[data-side=right]{--tw-translate-x:calc(var(--spacing)*1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\\[side\\=right\\]\\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.data-\\[side\\=top\\]\\:-translate-y-1[data-side=top]{--tw-translate-y:calc(var(--spacing)*-1);translate:var(--tw-translate-x)var(--tw-translate-y)}.data-\\[side\\=top\\]\\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y:calc(2*var(--spacing))}.data-\\[state\\=active\\]\\:bg-background[data-state=active]{background-color:var(--background)}.data-\\[state\\=active\\]\\:bg-card[data-state=active]{background-color:var(--card)}.data-\\[state\\=active\\]\\:text-foreground[data-state=active]{color:var(--foreground)}.data-\\[state\\=active\\]\\:text-primary[data-state=active]{color:var(--primary)}.data-\\[state\\=active\\]\\:shadow-sm[data-state=active]{--tw-shadow:var(--shadow-sm);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.data-\\[state\\=checked\\]\\:border-primary[data-state=checked]{border-color:var(--primary)}.data-\\[state\\=checked\\]\\:bg-primary[data-state=checked]{background-color:var(--primary)}.data-\\[state\\=checked\\]\\:text-primary-foreground[data-state=checked]{color:var(--primary-foreground)}.data-\\[state\\=closed\\]\\:animate-out[data-state=closed]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\\[state\\=closed\\]\\:fade-out-0[data-state=closed]{--tw-exit-opacity:0}.data-\\[state\\=closed\\]\\:zoom-out-95[data-state=closed]{--tw-exit-scale:.95}.data-\\[state\\=closed\\]\\:slide-out-to-left-1\\/2[data-state=closed]{--tw-exit-translate-x: -50% }.data-\\[state\\=closed\\]\\:slide-out-to-top-\\[48\\%\\][data-state=closed]{--tw-exit-translate-y: -48% }.data-\\[state\\=open\\]\\:animate-in[data-state=open]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-\\[state\\=open\\]\\:bg-accent[data-state=open]{background-color:var(--accent)}.data-\\[state\\=open\\]\\:text-muted-foreground[data-state=open]{color:var(--muted-foreground)}.data-\\[state\\=open\\]\\:fade-in-0[data-state=open]{--tw-enter-opacity:0}.data-\\[state\\=open\\]\\:zoom-in-95[data-state=open]{--tw-enter-scale:.95}.data-\\[state\\=open\\]\\:slide-in-from-left-1\\/2[data-state=open]{--tw-enter-translate-x: -50% }.data-\\[state\\=open\\]\\:slide-in-from-top-\\[48\\%\\][data-state=open]{--tw-enter-translate-y: -48% }.data-\\[state\\=selected\\]\\:bg-muted[data-state=selected]{background-color:var(--muted)}@media(min-width:40rem){.sm\\:mt-0{margin-top:calc(var(--spacing)*0)}.sm\\:mt-6{margin-top:calc(var(--spacing)*6)}.sm\\:mr-2{margin-right:calc(var(--spacing)*2)}.sm\\:mb-4{margin-bottom:calc(var(--spacing)*4)}.sm\\:mb-6{margin-bottom:calc(var(--spacing)*6)}.sm\\:block{display:block}.sm\\:flex{display:flex}.sm\\:hidden{display:none}.sm\\:inline{display:inline}.sm\\:inline-flex{display:inline-flex}.sm\\:h-3\\.5{height:calc(var(--spacing)*3.5)}.sm\\:h-4{height:calc(var(--spacing)*4)}.sm\\:h-5{height:calc(var(--spacing)*5)}.sm\\:h-7{height:calc(var(--spacing)*7)}.sm\\:h-8{height:calc(var(--spacing)*8)}.sm\\:h-10{height:calc(var(--spacing)*10)}.sm\\:h-auto{height:auto}.sm\\:max-h-64{max-height:calc(var(--spacing)*64)}.sm\\:max-h-\\[90vh\\]{max-height:90vh}.sm\\:w-3\\.5{width:calc(var(--spacing)*3.5)}.sm\\:w-4{width:calc(var(--spacing)*4)}.sm\\:w-5{width:calc(var(--spacing)*5)}.sm\\:w-7{width:calc(var(--spacing)*7)}.sm\\:w-8{width:calc(var(--spacing)*8)}.sm\\:w-10{width:calc(var(--spacing)*10)}.sm\\:w-64{width:calc(var(--spacing)*64)}.sm\\:w-100{width:calc(var(--spacing)*100)}.sm\\:w-auto{width:auto}.sm\\:max-w-\\[300px\\]{max-width:300px}.sm\\:max-w-md{max-width:var(--container-md)}.sm\\:max-w-none{max-width:none}.sm\\:scale-175{--tw-scale-x:175%;--tw-scale-y:175%;--tw-scale-z:175%;scale:var(--tw-scale-x)var(--tw-scale-y)}.sm\\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\\:flex-row{flex-direction:row}.sm\\:items-center{align-items:center}.sm\\:justify-between{justify-content:space-between}.sm\\:justify-end{justify-content:flex-end}.sm\\:justify-start{justify-content:flex-start}.sm\\:gap-0{gap:calc(var(--spacing)*0)}.sm\\:gap-2{gap:calc(var(--spacing)*2)}.sm\\:gap-3{gap:calc(var(--spacing)*3)}.sm\\:gap-4{gap:calc(var(--spacing)*4)}:where(.sm\\:space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}:where(.sm\\:space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*2)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-x-reverse)))}.sm\\:rounded-lg{border-radius:var(--radius)}.sm\\:rounded-xl{border-radius:calc(var(--radius) + 4px)}.sm\\:border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.sm\\:p-4{padding:calc(var(--spacing)*4)}.sm\\:p-6{padding:calc(var(--spacing)*6)}.sm\\:px-2{padding-inline:calc(var(--spacing)*2)}.sm\\:px-3{padding-inline:calc(var(--spacing)*3)}.sm\\:px-6{padding-inline:calc(var(--spacing)*6)}.sm\\:py-4{padding-block:calc(var(--spacing)*4)}.sm\\:pt-6{padding-top:calc(var(--spacing)*6)}.sm\\:pl-4{padding-left:calc(var(--spacing)*4)}.sm\\:text-left{text-align:left}.sm\\:text-right{text-align:right}.sm\\:text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.sm\\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.sm\\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.sm\\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.sm\\:text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}}@media(min-width:48rem){.md\\:relative{position:relative}.md\\:bottom-auto{bottom:auto}.md\\:left-auto{left:auto}.md\\:col-span-2{grid-column:span 2/span 2}.md\\:block{display:block}.md\\:flex{display:flex}.md\\:hidden{display:none}.md\\:inline-flex{display:inline-flex}.md\\:h-4{height:calc(var(--spacing)*4)}.md\\:h-screen{height:100vh}.md\\:max-h-\\[calc\\(100vh-6rem\\)\\]{max-height:calc(100vh - 6rem)}.md\\:w-4{width:calc(var(--spacing)*4)}.md\\:max-w-none{max-width:none}.md\\:translate-x-0{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.md\\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\\:flex-col{flex-direction:column}.md\\:gap-1{gap:calc(var(--spacing)*1)}.md\\:overflow-hidden{overflow:hidden}.md\\:overflow-y-auto{overflow-y:auto}.md\\:p-2{padding:calc(var(--spacing)*2)}.md\\:p-5{padding:calc(var(--spacing)*5)}.md\\:p-6{padding:calc(var(--spacing)*6)}.md\\:px-6{padding-inline:calc(var(--spacing)*6)}.md\\:pt-6{padding-top:calc(var(--spacing)*6)}.md\\:pb-4{padding-bottom:calc(var(--spacing)*4)}.md\\:pb-10{padding-bottom:calc(var(--spacing)*10)}.md\\:pl-4{padding-left:calc(var(--spacing)*4)}.md\\:text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}}@media(min-width:64rem){.lg\\:flex{display:flex}.lg\\:w-64{width:calc(var(--spacing)*64)}.lg\\:w-auto{width:auto}.lg\\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\\:flex-row{flex-direction:row}.lg\\:items-center{align-items:center}.lg\\:p-8{padding:calc(var(--spacing)*8)}}.dark\\:border-l-blue-500:is(.dark *,html.dark *){border-left-color:var(--color-blue-500)}.dark\\:border-l-green-500:is(.dark *,html.dark *){border-left-color:var(--color-green-500)}.dark\\:bg-blue-900\\/30:is(.dark *,html.dark *){background-color:#1c398e4d}@supports (color:color-mix(in lab,red,red)){.dark\\:bg-blue-900\\/30:is(.dark *,html.dark *){background-color:color-mix(in oklab,var(--color-blue-900)30%,transparent)}}.dark\\:bg-blue-950\\/30:is(.dark *,html.dark *){background-color:#1624564d}@supports (color:color-mix(in lab,red,red)){.dark\\:bg-blue-950\\/30:is(.dark *,html.dark *){background-color:color-mix(in oklab,var(--color-blue-950)30%,transparent)}}.dark\\:bg-cyan-950\\/30:is(.dark *,html.dark *){background-color:#0533454d}@supports (color:color-mix(in lab,red,red)){.dark\\:bg-cyan-950\\/30:is(.dark *,html.dark *){background-color:color-mix(in oklab,var(--color-cyan-950)30%,transparent)}}.dark\\:bg-destructive\\/20:is(.dark *,html.dark *){background-color:var(--destructive)}@supports (color:color-mix(in lab,red,red)){.dark\\:bg-destructive\\/20:is(.dark *,html.dark *){background-color:color-mix(in oklab,var(--destructive)20%,transparent)}}.dark\\:bg-gray-950\\/30:is(.dark *,html.dark *){background-color:#0307124d}@supports (color:color-mix(in lab,red,red)){.dark\\:bg-gray-950\\/30:is(.dark *,html.dark *){background-color:color-mix(in oklab,var(--color-gray-950)30%,transparent)}}.dark\\:bg-green-500\\/20:is(.dark *,html.dark *){background-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.dark\\:bg-green-500\\/20:is(.dark *,html.dark *){background-color:color-mix(in oklab,var(--color-green-500)20%,transparent)}}.dark\\:bg-green-900\\/30:is(.dark *,html.dark *){background-color:#0d542b4d}@supports (color:color-mix(in lab,red,red)){.dark\\:bg-green-900\\/30:is(.dark *,html.dark *){background-color:color-mix(in oklab,var(--color-green-900)30%,transparent)}}.dark\\:bg-green-950\\/30:is(.dark *,html.dark *){background-color:#032e154d}@supports (color:color-mix(in lab,red,red)){.dark\\:bg-green-950\\/30:is(.dark *,html.dark *){background-color:color-mix(in oklab,var(--color-green-950)30%,transparent)}}.dark\\:bg-orange-900\\/30:is(.dark *,html.dark *){background-color:#7e2a0c4d}@supports (color:color-mix(in lab,red,red)){.dark\\:bg-orange-900\\/30:is(.dark *,html.dark *){background-color:color-mix(in oklab,var(--color-orange-900)30%,transparent)}}.dark\\:bg-orange-950\\/30:is(.dark *,html.dark *){background-color:#4413064d}@supports (color:color-mix(in lab,red,red)){.dark\\:bg-orange-950\\/30:is(.dark *,html.dark *){background-color:color-mix(in oklab,var(--color-orange-950)30%,transparent)}}.dark\\:bg-purple-950\\/30:is(.dark *,html.dark *){background-color:#3c03664d}@supports (color:color-mix(in lab,red,red)){.dark\\:bg-purple-950\\/30:is(.dark *,html.dark *){background-color:color-mix(in oklab,var(--color-purple-950)30%,transparent)}}.dark\\:bg-red-950\\/30:is(.dark *,html.dark *){background-color:#4608094d}@supports (color:color-mix(in lab,red,red)){.dark\\:bg-red-950\\/30:is(.dark *,html.dark *){background-color:color-mix(in oklab,var(--color-red-950)30%,transparent)}}.dark\\:bg-teal-950\\/30:is(.dark *,html.dark *){background-color:#022f2e4d}@supports (color:color-mix(in lab,red,red)){.dark\\:bg-teal-950\\/30:is(.dark *,html.dark *){background-color:color-mix(in oklab,var(--color-teal-950)30%,transparent)}}.dark\\:bg-yellow-950\\/30:is(.dark *,html.dark *){background-color:#4320044d}@supports (color:color-mix(in lab,red,red)){.dark\\:bg-yellow-950\\/30:is(.dark *,html.dark *){background-color:color-mix(in oklab,var(--color-yellow-950)30%,transparent)}}.dark\\:text-blue-300:is(.dark *,html.dark *){color:var(--color-blue-300)}.dark\\:text-blue-400:is(.dark *,html.dark *){color:var(--color-blue-400)}.dark\\:text-green-400:is(.dark *,html.dark *){color:var(--color-green-400)}.dark\\:text-orange-400:is(.dark *,html.dark *){color:var(--color-orange-400)}.\\[\\&_\\.cm-editor\\]\\:h-full .cm-editor,.\\[\\&_\\.cm-gutters\\]\\:h-full .cm-gutters{height:100%}.\\[\\&_\\.cm-scroller\\]\\:overflow-auto .cm-scroller{overflow:auto}.\\[\\&_\\.hljs-attr\\]\\:text-amber-300 .hljs-attr{color:var(--color-amber-300)}.\\[\\&_\\.hljs-attr\\]\\:text-amber-700 .hljs-attr{color:var(--color-amber-700)}.\\[\\&_\\.hljs-built_in\\]\\:text-purple-700 .hljs-built in{color:var(--color-purple-700)}.\\[\\&_\\.hljs-built_in\\]\\:text-violet-300 .hljs-built in{color:var(--color-violet-300)}.\\[\\&_\\.hljs-comment\\]\\:text-zinc-500 .hljs-comment{color:var(--color-zinc-500)}.\\[\\&_\\.hljs-keyword\\]\\:text-blue-600 .hljs-keyword{color:var(--color-blue-600)}.\\[\\&_\\.hljs-keyword\\]\\:text-sky-300 .hljs-keyword{color:var(--color-sky-300)}.\\[\\&_\\.hljs-literal\\]\\:text-blue-600 .hljs-literal{color:var(--color-blue-600)}.\\[\\&_\\.hljs-literal\\]\\:text-sky-300 .hljs-literal{color:var(--color-sky-300)}.\\[\\&_\\.hljs-name\\]\\:text-emerald-300 .hljs-name{color:var(--color-emerald-300)}.\\[\\&_\\.hljs-name\\]\\:text-green-700 .hljs-name{color:var(--color-green-700)}.\\[\\&_\\.hljs-number\\]\\:text-fuchsia-300 .hljs-number{color:var(--color-fuchsia-300)}.\\[\\&_\\.hljs-number\\]\\:text-purple-700 .hljs-number{color:var(--color-purple-700)}.\\[\\&_\\.hljs-quote\\]\\:text-zinc-500 .hljs-quote{color:var(--color-zinc-500)}.\\[\\&_\\.hljs-section\\]\\:text-emerald-300 .hljs-section{color:var(--color-emerald-300)}.\\[\\&_\\.hljs-section\\]\\:text-green-700 .hljs-section{color:var(--color-green-700)}.\\[\\&_\\.hljs-selector-tag\\]\\:text-blue-600 .hljs-selector-tag{color:var(--color-blue-600)}.\\[\\&_\\.hljs-selector-tag\\]\\:text-sky-300 .hljs-selector-tag{color:var(--color-sky-300)}.\\[\\&_\\.hljs-string\\]\\:text-amber-300 .hljs-string{color:var(--color-amber-300)}.\\[\\&_\\.hljs-string\\]\\:text-amber-700 .hljs-string{color:var(--color-amber-700)}.\\[\\&_\\.hljs-template-tag\\]\\:text-amber-300 .hljs-template-tag{color:var(--color-amber-300)}.\\[\\&_\\.hljs-template-tag\\]\\:text-amber-700 .hljs-template-tag{color:var(--color-amber-700)}.\\[\\&_\\.hljs-title\\]\\:text-emerald-300 .hljs-title{color:var(--color-emerald-300)}.\\[\\&_\\.hljs-title\\]\\:text-green-700 .hljs-title{color:var(--color-green-700)}.\\[\\&_\\.hljs-type\\]\\:text-purple-700 .hljs-type{color:var(--color-purple-700)}.\\[\\&_\\.hljs-type\\]\\:text-violet-300 .hljs-type{color:var(--color-violet-300)}.\\[\\&_svg\\]\\:pointer-events-none svg{pointer-events:none}.\\[\\&_svg\\]\\:shrink-0 svg{flex-shrink:0}.\\[\\&_svg\\:not\\(\\[class\\*\\=\\'size-\\'\\]\\)\\]\\:size-4 svg:not([class*=size-]){width:calc(var(--spacing)*4);height:calc(var(--spacing)*4)}.\\[\\&_tr\\]\\:border-b tr{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.\\[\\&_tr\\:last-child\\]\\:border-0 tr:last-child{border-style:var(--tw-border-style);border-width:0}.\\[\\&\\:has\\(\\[role\\=checkbox\\]\\)\\]\\:pr-0:has([role=checkbox]){padding-right:calc(var(--spacing)*0)}.\\[\\&\\>span\\]\\:line-clamp-1>span{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.\\[\\&\\>tr\\]\\:last\\:border-b-0>tr:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}}@property --tw-animation-delay{syntax:\"*\";inherits:false;initial-value:0s}@property --tw-animation-direction{syntax:\"*\";inherits:false;initial-value:normal}@property --tw-animation-duration{syntax:\"*\";inherits:false}@property --tw-animation-fill-mode{syntax:\"*\";inherits:false;initial-value:none}@property --tw-animation-iteration-count{syntax:\"*\";inherits:false;initial-value:1}@property --tw-enter-blur{syntax:\"*\";inherits:false;initial-value:0}@property --tw-enter-opacity{syntax:\"*\";inherits:false;initial-value:1}@property --tw-enter-rotate{syntax:\"*\";inherits:false;initial-value:0}@property --tw-enter-scale{syntax:\"*\";inherits:false;initial-value:1}@property --tw-enter-translate-x{syntax:\"*\";inherits:false;initial-value:0}@property --tw-enter-translate-y{syntax:\"*\";inherits:false;initial-value:0}@property --tw-exit-blur{syntax:\"*\";inherits:false;initial-value:0}@property --tw-exit-opacity{syntax:\"*\";inherits:false;initial-value:1}@property --tw-exit-rotate{syntax:\"*\";inherits:false;initial-value:0}@property --tw-exit-scale{syntax:\"*\";inherits:false;initial-value:1}@property --tw-exit-translate-x{syntax:\"*\";inherits:false;initial-value:0}@property --tw-exit-translate-y{syntax:\"*\";inherits:false;initial-value:0}[data-sonner-toaster]{z-index:100000!important}:root,:root[data-color-scheme=default]{--background:#fff;--foreground:#34322d;--card:#fff;--card-foreground:#34322d;--popover:#fff;--popover-foreground:#34322d;--primary:#34322d;--primary-foreground:#fff;--secondary:#f3f3f3;--secondary-foreground:#34322d;--muted:#f3f3f3;--muted-foreground:#858481;--accent:#f3f3f3;--accent-foreground:#34322d;--destructive:oklch(57.87% .15 34.8321);--destructive-foreground:oklch(97.96% .0101 32.5158);--border:#0000001f;--input:#0000001f;--ring:#34322d;--chart-1:#34322d;--chart-2:#5c5a56;--chart-3:#858481;--chart-4:#adacaa;--chart-5:#d6d5d4;--sidebar:#fbfbfb;--sidebar-foreground:#34322d;--sidebar-primary:#34322d;--sidebar-primary-foreground:#fff;--sidebar-accent:#f3f3f3;--sidebar-accent-foreground:#34322d;--sidebar-border:#00000014;--sidebar-ring:#34322d;--glow-1:#34322d1a;--glow-2:#8584811a;--radius:.5rem;--shadow-x:0;--shadow-y:1px;--shadow-blur:3px;--shadow-spread:0px;--shadow-opacity:.1;--shadow-color:#000;--shadow-2xs:0 1px 3px 0px #0000000d;--shadow-xs:0 1px 3px 0px #0000000d;--shadow-sm:0 1px 3px 0px #0000001a,0 1px 2px -1px #0000001a;--shadow:0 1px 3px 0px #0000001a,0 1px 2px -1px #0000001a;--shadow-md:0 1px 3px 0px #0000001a,0 2px 4px -1px #0000001a;--shadow-lg:0 1px 3px 0px #0000001a,0 4px 6px -1px #0000001a;--shadow-xl:0 1px 3px 0px #0000001a,0 8px 10px -1px #0000001a;--shadow-2xl:0 1px 3px 0px #00000040;--tracking-normal:0em;--spacing:.25rem}:root[data-color-scheme=green]{--background:oklch(93.31% .0081 98.8844);--foreground:oklch(35.14% .025 84.4589);--card:oklch(98.91% .0017 325.59);--card-foreground:oklch(35.14% .025 84.4589);--popover:oklch(97.96% .0057 84.5661);--popover-foreground:oklch(35.14% .025 84.4589);--primary:oklch(20% 0 0);--primary-foreground:oklch(98% 0 0);--secondary:oklch(92.04% .0149 98.297);--secondary-foreground:oklch(40.06% .0305 86.8158);--muted:oklch(93.99% .0124 91.5213);--muted-foreground:oklch(51.92% .0198 84.5869);--accent:oklch(68% .1103 148.083);--accent-foreground:oklch(97.92% .0091 150.693);--destructive:oklch(57.87% .15 34.8321);--destructive-foreground:oklch(97.96% .0101 32.5158);--border:oklch(87.95% .0142 88.6926);--input:oklch(87.95% .0142 88.6926);--ring:oklch(20% 0 0);--chart-1:oklch(68.09% .1201 145.043);--chart-2:oklch(61.95% .11 147.856);--chart-3:oklch(56.05% .1006 149.757);--chart-4:oklch(50.06% .0903 151.802);--chart-5:oklch(43.88% .0792 155.555);--sidebar:oklch(98.91% .0017 325.59);--sidebar-foreground:oklch(35.14% .025 84.4589);--sidebar-primary:oklch(88.16% .0425 82.469);--sidebar-primary-foreground:oklch(0% 0 0);--sidebar-accent:oklch(96.89% .0079 73.7443);--sidebar-accent-foreground:oklch(97.92% .0091 150.693);--sidebar-border:oklch(87.95% .0142 88.6926);--sidebar-ring:oklch(62.05% .1199 144.861);--glow-1:oklch(50% 0 0);--glow-2:oklch(70% 0 0)}:root[data-color-scheme=blue]{--background:oklch(98.46% .0017 247.84);--foreground:oklch(20.8% .034 264.665);--card:oklch(100% 0 0);--card-foreground:oklch(20.8% .034 264.665);--popover:oklch(100% 0 0);--popover-foreground:oklch(20.8% .034 264.665);--primary:oklch(20.5% 0 0);--primary-foreground:oklch(98.5% 0 0);--secondary:oklch(93.2% .032 255.585);--secondary-foreground:oklch(20.8% .034 264.665);--muted:oklch(96% .002 247.84);--muted-foreground:oklch(55.2% .014 255.6);--accent:oklch(93.2% .032 255.585);--accent-foreground:oklch(48.8% .243 264.376);--destructive:oklch(57.87% .15 34.8321);--destructive-foreground:oklch(97.96% .0101 32.5158);--border:oklch(89.8% .005 255.6);--input:oklch(89.8% .005 255.6);--ring:oklch(54.6% .245 262.881);--chart-1:oklch(54.6% .245 262.881);--chart-2:oklch(59.6% .2 262.881);--chart-3:oklch(64.6% .15 262.881);--chart-4:oklch(69.6% .1 262.881);--chart-5:oklch(74.6% .05 262.881);--sidebar:oklch(98.46% .0017 247.84);--sidebar-foreground:oklch(20.8% .034 264.665);--sidebar-primary:oklch(54.6% .245 262.881);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(93.2% .032 255.585);--sidebar-accent-foreground:oklch(48.8% .243 264.376);--sidebar-border:oklch(89.8% .005 255.6);--sidebar-ring:oklch(54.6% .245 262.881);--glow-1:oklch(54.6% .245 262.881);--glow-2:oklch(60% .2 280)}:root[data-color-scheme=sky-blue]{--background:oklch(95% .012 220);--foreground:oklch(30% .03 230);--card:oklch(98.5% .008 220);--card-foreground:oklch(30% .03 230);--popover:oklch(98% .01 220);--popover-foreground:oklch(30% .03 230);--primary:oklch(72% .08 220);--primary-foreground:oklch(98% .01 220);--secondary:oklch(92% .02 220);--secondary-foreground:oklch(35% .035 230);--muted:oklch(94% .015 220);--muted-foreground:oklch(50% .025 230);--accent:oklch(75% .09 210);--accent-foreground:oklch(98% .01 220);--border:oklch(86% .02 220);--input:oklch(86% .02 220);--ring:oklch(72% .08 220);--chart-1:oklch(75% .09 220);--chart-2:oklch(68% .08 225);--chart-3:oklch(61% .07 230);--chart-4:oklch(54% .06 235);--chart-5:oklch(47% .05 240);--sidebar:oklch(98.5% .008 220);--sidebar-foreground:oklch(30% .03 230);--sidebar-primary:oklch(85% .05 220);--sidebar-primary-foreground:oklch(15% .02 230);--sidebar-accent:oklch(96% .012 220);--sidebar-accent-foreground:oklch(98% .01 220);--sidebar-border:oklch(86% .02 220);--sidebar-ring:oklch(72% .08 220);--glow-1:oklch(72% .08 220);--glow-2:oklch(75% .09 200)}:root[data-color-scheme=purple]{--background:oklch(95% .015 290);--foreground:oklch(30% .035 280);--card:oklch(98.5% .01 290);--card-foreground:oklch(30% .035 280);--popover:oklch(98% .012 290);--popover-foreground:oklch(30% .035 280);--primary:oklch(65% .15 280);--primary-foreground:oklch(98% .01 290);--secondary:oklch(92% .025 290);--secondary-foreground:oklch(35% .04 280);--muted:oklch(94% .018 290);--muted-foreground:oklch(50% .03 280);--accent:oklch(70% .14 275);--accent-foreground:oklch(98% .01 290);--border:oklch(86% .025 290);--input:oklch(86% .025 290);--ring:oklch(65% .15 280);--chart-1:oklch(70% .14 280);--chart-2:oklch(63% .13 285);--chart-3:oklch(56% .12 290);--chart-4:oklch(49% .11 295);--chart-5:oklch(42% .1 300);--sidebar:oklch(98.5% .01 290);--sidebar-foreground:oklch(30% .035 280);--sidebar-primary:oklch(85% .06 290);--sidebar-primary-foreground:oklch(15% .025 280);--sidebar-accent:oklch(96% .015 290);--sidebar-accent-foreground:oklch(98% .01 290);--sidebar-border:oklch(86% .025 290);--sidebar-ring:oklch(65% .15 280);--glow-1:oklch(65% .15 280);--glow-2:oklch(70% .12 300)}:root[data-color-scheme=orange]{--background:oklch(95% .015 60);--foreground:oklch(30% .035 50);--card:oklch(98.5% .01 60);--card-foreground:oklch(30% .035 50);--popover:oklch(98% .012 60);--popover-foreground:oklch(30% .035 50);--primary:oklch(70% .15 50);--primary-foreground:oklch(98% .01 60);--secondary:oklch(92% .025 60);--secondary-foreground:oklch(35% .04 50);--muted:oklch(94% .018 60);--muted-foreground:oklch(50% .03 50);--accent:oklch(75% .14 45);--accent-foreground:oklch(98% .01 60);--border:oklch(86% .025 60);--input:oklch(86% .025 60);--ring:oklch(70% .15 50);--chart-1:oklch(75% .14 50);--chart-2:oklch(68% .13 55);--chart-3:oklch(61% .12 60);--chart-4:oklch(54% .11 65);--chart-5:oklch(47% .1 70);--sidebar:oklch(98.5% .01 60);--sidebar-foreground:oklch(30% .035 50);--sidebar-primary:oklch(85% .06 60);--sidebar-primary-foreground:oklch(15% .025 50);--sidebar-accent:oklch(96% .015 60);--sidebar-accent-foreground:oklch(98% .01 60);--sidebar-border:oklch(86% .025 60);--sidebar-ring:oklch(70% .15 50);--glow-1:oklch(70% .15 50);--glow-2:oklch(75% .12 35)}:root[data-color-scheme=rose]{--background:oklch(95% .015 10);--foreground:oklch(30% .035 5);--card:oklch(98.5% .01 10);--card-foreground:oklch(30% .035 5);--popover:oklch(98% .012 10);--popover-foreground:oklch(30% .035 5);--primary:oklch(65% .15 10);--primary-foreground:oklch(98% .01 10);--secondary:oklch(92% .025 10);--secondary-foreground:oklch(35% .04 5);--muted:oklch(94% .018 10);--muted-foreground:oklch(50% .03 5);--accent:oklch(70% .14 5);--accent-foreground:oklch(98% .01 10);--border:oklch(86% .025 10);--input:oklch(86% .025 10);--ring:oklch(65% .15 10);--chart-1:oklch(70% .14 10);--chart-2:oklch(63% .13 15);--chart-3:oklch(56% .12 20);--chart-4:oklch(49% .11 25);--chart-5:oklch(42% .1 30);--sidebar:oklch(98.5% .01 10);--sidebar-foreground:oklch(30% .035 5);--sidebar-primary:oklch(85% .06 10);--sidebar-primary-foreground:oklch(15% .025 5);--sidebar-accent:oklch(96% .015 10);--sidebar-accent-foreground:oklch(98% .01 10);--sidebar-border:oklch(86% .025 10);--sidebar-ring:oklch(65% .15 10);--glow-1:oklch(65% .15 10);--glow-2:oklch(70% .12 350)}:root[data-color-scheme=teal]{--background:oklch(95% .015 175);--foreground:oklch(30% .035 180);--card:oklch(98.5% .01 175);--card-foreground:oklch(30% .035 180);--popover:oklch(98% .012 175);--popover-foreground:oklch(30% .035 180);--primary:oklch(70% .12 175);--primary-foreground:oklch(98% .01 175);--secondary:oklch(92% .025 175);--secondary-foreground:oklch(35% .04 180);--muted:oklch(94% .018 175);--muted-foreground:oklch(50% .03 180);--accent:oklch(75% .11 170);--accent-foreground:oklch(98% .01 175);--border:oklch(86% .025 175);--input:oklch(86% .025 175);--ring:oklch(70% .12 175);--chart-1:oklch(75% .11 175);--chart-2:oklch(68% .1 180);--chart-3:oklch(61% .09 185);--chart-4:oklch(54% .08 190);--chart-5:oklch(47% .07 195);--sidebar:oklch(98.5% .01 175);--sidebar-foreground:oklch(30% .035 180);--sidebar-primary:oklch(85% .06 175);--sidebar-primary-foreground:oklch(15% .025 180);--sidebar-accent:oklch(96% .015 175);--sidebar-accent-foreground:oklch(98% .01 175);--sidebar-border:oklch(86% .025 175);--sidebar-ring:oklch(70% .12 175);--glow-1:oklch(70% .12 175);--glow-2:oklch(75% .1 190)}html.dark[data-color-scheme=default],.dark[data-color-scheme=default]{--background:#1a1a1a;--foreground:#ededed;--card:#1a1a1a;--card-foreground:#ededed;--popover:#1a1a1a;--popover-foreground:#ededed;--primary:#ededed;--primary-foreground:#1a1a1a;--secondary:#2a2a2a;--secondary-foreground:#ededed;--muted:#2a2a2a;--muted-foreground:#a1a1a1;--accent:#2a2a2a;--accent-foreground:#ededed;--destructive:oklch(57.87% .15 34.8321);--destructive-foreground:oklch(94.96% .0101 32.5162);--border:#ffffff1f;--input:#ffffff1f;--ring:#ededed;--chart-1:#ededed;--chart-2:#c7c7c7;--chart-3:#a1a1a1;--chart-4:#7b7b7b;--chart-5:#555;--sidebar:#1f1f1f;--sidebar-foreground:#ededed;--sidebar-primary:#ededed;--sidebar-primary-foreground:#1a1a1a;--sidebar-accent:#2a2a2a;--sidebar-accent-foreground:#ededed;--sidebar-border:#ffffff14;--sidebar-ring:#ededed;--glow-1:#ededed1a;--glow-2:#a1a1a11a}html.dark,.dark,html.dark[data-color-scheme=green],.dark[data-color-scheme=green]{--background:oklch(28.09% .0264 153.404);--foreground:oklch(92% .0104 81.7947);--card:oklch(31.96% .0217 150.339);--card-foreground:oklch(92% .0104 81.7947);--popover:oklch(31.96% .0217 150.339);--popover-foreground:oklch(92% .0104 81.7947);--primary:oklch(69.9% .1292 144.873);--primary-foreground:oklch(22.14% .0312 149.621);--secondary:oklch(37.83% .02 147.987);--secondary-foreground:oklch(88.01% .0117 84.5809);--muted:oklch(34.03% .0192 151.125);--muted-foreground:oklch(68.01% .0151 88.7221);--accent:oklch(74.09% .1207 147.975);--accent-foreground:oklch(21.89% .0281 148.509);--destructive:oklch(57.87% .15 34.8321);--destructive-foreground:oklch(94.96% .0101 32.5162);--border:oklch(40.06% .021 145.17);--input:oklch(40.06% .021 145.17);--ring:oklch(69.9% .1292 144.873);--chart-1:oklch(76.03% .1309 144.994);--chart-2:oklch(70.08% .12 147.875);--chart-3:oklch(66.66% .1099 150);--chart-4:oklch(57.95% .0999 151.837);--chart-5:oklch(51.99% .0893 155.145);--sidebar:oklch(25.93% .0275 159.089);--sidebar-foreground:oklch(92% .0104 81.7947);--sidebar-primary:oklch(69.9% .1292 144.873);--sidebar-primary-foreground:oklch(22.14% .0312 149.621);--sidebar-accent:oklch(74.09% .1207 147.975);--sidebar-accent-foreground:oklch(21.89% .0281 148.509);--sidebar-border:oklch(40.06% .021 145.17);--sidebar-ring:oklch(69.9% .1292 144.873);--glow-1:oklch(40% .15 145);--glow-2:oklch(45% .12 160);--radius:.5rem;--shadow-x:0;--shadow-y:1px;--shadow-blur:3px;--shadow-spread:0px;--shadow-opacity:.1;--shadow-color:#000;--shadow-2xs:0 1px 3px 0px #0000000d;--shadow-xs:0 1px 3px 0px #0000000d;--shadow-sm:0 1px 3px 0px #0000001a,0 1px 2px -1px #0000001a;--shadow:0 1px 3px 0px #0000001a,0 1px 2px -1px #0000001a;--shadow-md:0 1px 3px 0px #0000001a,0 2px 4px -1px #0000001a;--shadow-lg:0 1px 3px 0px #0000001a,0 4px 6px -1px #0000001a;--shadow-xl:0 1px 3px 0px #0000001a,0 8px 10px -1px #0000001a;--shadow-2xl:0 1px 3px 0px #00000040}html.dark[data-color-scheme=blue],.dark[data-color-scheme=blue]{--background:oklch(18% .03 263);--foreground:oklch(92% .01 263);--card:oklch(20% .03 263);--card-foreground:oklch(92% .01 263);--popover:oklch(20% .03 263);--popover-foreground:oklch(92% .01 263);--primary:oklch(60% .2 263);--primary-foreground:oklch(18% .03 263);--secondary:oklch(25% .03 263);--secondary-foreground:oklch(90% .01 263);--muted:oklch(25% .03 263);--muted-foreground:oklch(70% .02 263);--accent:oklch(25% .03 263);--accent-foreground:oklch(90% .01 263);--destructive:oklch(57.87% .15 34.8321);--destructive-foreground:oklch(94.96% .0101 32.5162);--border:oklch(30% .03 263);--input:oklch(30% .03 263);--ring:oklch(60% .2 263);--chart-1:oklch(60% .2 263);--chart-2:oklch(55% .18 263);--chart-3:oklch(50% .16 263);--chart-4:oklch(45% .14 263);--chart-5:oklch(40% .12 263);--sidebar:oklch(18% .03 263);--sidebar-foreground:oklch(92% .01 263);--sidebar-primary:oklch(60% .2 263);--sidebar-primary-foreground:oklch(18% .03 263);--sidebar-accent:oklch(25% .03 263);--sidebar-accent-foreground:oklch(90% .01 263);--sidebar-border:oklch(30% .03 263);--sidebar-ring:oklch(60% .2 263);--glow-1:oklch(40% .2 263);--glow-2:oklch(45% .15 280)}html.dark[data-color-scheme=sky-blue],.dark[data-color-scheme=sky-blue]{--background:oklch(22% .03 220);--foreground:oklch(92% .015 220);--card:oklch(26% .025 220);--card-foreground:oklch(92% .015 220);--popover:oklch(26% .025 220);--popover-foreground:oklch(92% .015 220);--primary:oklch(72% .08 220);--primary-foreground:oklch(18% .02 220);--secondary:oklch(32% .03 220);--secondary-foreground:oklch(88% .015 220);--muted:oklch(28% .025 220);--muted-foreground:oklch(68% .02 220);--accent:oklch(75% .09 210);--accent-foreground:oklch(18% .02 220);--border:oklch(35% .03 220);--input:oklch(35% .03 220);--ring:oklch(72% .08 220);--chart-1:oklch(75% .09 220);--chart-2:oklch(68% .08 225);--chart-3:oklch(61% .07 230);--chart-4:oklch(54% .06 235);--chart-5:oklch(47% .05 240);--sidebar:oklch(20% .032 220);--sidebar-foreground:oklch(92% .015 220);--sidebar-primary:oklch(72% .08 220);--sidebar-primary-foreground:oklch(18% .02 220);--sidebar-accent:oklch(75% .09 210);--sidebar-accent-foreground:oklch(18% .02 220);--sidebar-border:oklch(35% .03 220);--sidebar-ring:oklch(72% .08 220);--glow-1:oklch(45% .1 220);--glow-2:oklch(50% .08 200)}html.dark[data-color-scheme=purple],.dark[data-color-scheme=purple]{--background:oklch(22% .035 280);--foreground:oklch(92% .018 290);--card:oklch(26% .03 280);--card-foreground:oklch(92% .018 290);--popover:oklch(26% .03 280);--popover-foreground:oklch(92% .018 290);--primary:oklch(65% .15 280);--primary-foreground:oklch(18% .025 280);--secondary:oklch(32% .035 280);--secondary-foreground:oklch(88% .018 290);--muted:oklch(28% .03 280);--muted-foreground:oklch(68% .025 290);--accent:oklch(70% .14 275);--accent-foreground:oklch(18% .025 280);--border:oklch(35% .035 280);--input:oklch(35% .035 280);--ring:oklch(65% .15 280);--chart-1:oklch(70% .14 280);--chart-2:oklch(63% .13 285);--chart-3:oklch(56% .12 290);--chart-4:oklch(49% .11 295);--chart-5:oklch(42% .1 300);--sidebar:oklch(20% .038 280);--sidebar-foreground:oklch(92% .018 290);--sidebar-primary:oklch(65% .15 280);--sidebar-primary-foreground:oklch(18% .025 280);--sidebar-accent:oklch(70% .14 275);--sidebar-accent-foreground:oklch(18% .025 280);--sidebar-border:oklch(35% .035 280);--sidebar-ring:oklch(65% .15 280);--glow-1:oklch(40% .18 280);--glow-2:oklch(45% .15 300)}html.dark[data-color-scheme=orange],.dark[data-color-scheme=orange]{--background:oklch(22% .035 50);--foreground:oklch(92% .018 60);--card:oklch(26% .03 50);--card-foreground:oklch(92% .018 60);--popover:oklch(26% .03 50);--popover-foreground:oklch(92% .018 60);--primary:oklch(70% .15 50);--primary-foreground:oklch(18% .025 50);--secondary:oklch(32% .035 50);--secondary-foreground:oklch(88% .018 60);--muted:oklch(28% .03 50);--muted-foreground:oklch(68% .025 60);--accent:oklch(75% .14 45);--accent-foreground:oklch(18% .025 50);--border:oklch(35% .035 50);--input:oklch(35% .035 50);--ring:oklch(70% .15 50);--chart-1:oklch(75% .14 50);--chart-2:oklch(68% .13 55);--chart-3:oklch(61% .12 60);--chart-4:oklch(54% .11 65);--chart-5:oklch(47% .1 70);--sidebar:oklch(20% .038 50);--sidebar-foreground:oklch(92% .018 60);--sidebar-primary:oklch(70% .15 50);--sidebar-primary-foreground:oklch(18% .025 50);--sidebar-accent:oklch(75% .14 45);--sidebar-accent-foreground:oklch(18% .025 50);--sidebar-border:oklch(35% .035 50);--sidebar-ring:oklch(70% .15 50);--glow-1:oklch(45% .18 50);--glow-2:oklch(50% .15 35)}html.dark[data-color-scheme=rose],.dark[data-color-scheme=rose]{--background:oklch(22% .035 5);--foreground:oklch(92% .018 10);--card:oklch(26% .03 5);--card-foreground:oklch(92% .018 10);--popover:oklch(26% .03 5);--popover-foreground:oklch(92% .018 10);--primary:oklch(65% .15 10);--primary-foreground:oklch(18% .025 5);--secondary:oklch(32% .035 5);--secondary-foreground:oklch(88% .018 10);--muted:oklch(28% .03 5);--muted-foreground:oklch(68% .025 10);--accent:oklch(70% .14 5);--accent-foreground:oklch(18% .025 5);--border:oklch(35% .035 5);--input:oklch(35% .035 5);--ring:oklch(65% .15 10);--chart-1:oklch(70% .14 10);--chart-2:oklch(63% .13 15);--chart-3:oklch(56% .12 20);--chart-4:oklch(49% .11 25);--chart-5:oklch(42% .1 30);--sidebar:oklch(20% .038 5);--sidebar-foreground:oklch(92% .018 10);--sidebar-primary:oklch(65% .15 10);--sidebar-primary-foreground:oklch(18% .025 5);--sidebar-accent:oklch(70% .14 5);--sidebar-accent-foreground:oklch(18% .025 5);--sidebar-border:oklch(35% .035 5);--sidebar-ring:oklch(65% .15 10);--glow-1:oklch(40% .18 10);--glow-2:oklch(45% .15 350)}html.dark[data-color-scheme=teal],.dark[data-color-scheme=teal]{--background:oklch(22% .035 175);--foreground:oklch(92% .018 175);--card:oklch(26% .03 175);--card-foreground:oklch(92% .018 175);--popover:oklch(26% .03 175);--popover-foreground:oklch(92% .018 175);--primary:oklch(70% .12 175);--primary-foreground:oklch(18% .025 175);--secondary:oklch(32% .035 175);--secondary-foreground:oklch(88% .018 175);--muted:oklch(28% .03 175);--muted-foreground:oklch(68% .025 175);--accent:oklch(75% .11 170);--accent-foreground:oklch(18% .025 175);--border:oklch(35% .035 175);--input:oklch(35% .035 175);--ring:oklch(70% .12 175);--chart-1:oklch(75% .11 175);--chart-2:oklch(68% .1 180);--chart-3:oklch(61% .09 185);--chart-4:oklch(54% .08 190);--chart-5:oklch(47% .07 195);--sidebar:oklch(20% .038 175);--sidebar-foreground:oklch(92% .018 175);--sidebar-primary:oklch(70% .12 175);--sidebar-primary-foreground:oklch(18% .025 175);--sidebar-accent:oklch(75% .11 170);--sidebar-accent-foreground:oklch(18% .025 175);--sidebar-border:oklch(35% .035 175);--sidebar-ring:oklch(70% .12 175);--glow-1:oklch(45% .15 175);--glow-2:oklch(50% .12 190)}@media(prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;transition-delay:0s!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}@property --tw-translate-x{syntax:\"*\";inherits:false;initial-value:0}@property --tw-translate-y{syntax:\"*\";inherits:false;initial-value:0}@property --tw-translate-z{syntax:\"*\";inherits:false;initial-value:0}@property --tw-scale-x{syntax:\"*\";inherits:false;initial-value:1}@property --tw-scale-y{syntax:\"*\";inherits:false;initial-value:1}@property --tw-scale-z{syntax:\"*\";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:\"*\";inherits:false}@property --tw-rotate-y{syntax:\"*\";inherits:false}@property --tw-rotate-z{syntax:\"*\";inherits:false}@property --tw-skew-x{syntax:\"*\";inherits:false}@property --tw-skew-y{syntax:\"*\";inherits:false}@property --tw-space-y-reverse{syntax:\"*\";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:\"*\";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:\"*\";inherits:false;initial-value:0}@property --tw-border-style{syntax:\"*\";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:\"*\";inherits:false}@property --tw-gradient-from{syntax:\"<color>\";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:\"<color>\";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:\"<color>\";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:\"*\";inherits:false}@property --tw-gradient-via-stops{syntax:\"*\";inherits:false}@property --tw-gradient-from-position{syntax:\"<length-percentage>\";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:\"<length-percentage>\";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:\"<length-percentage>\";inherits:false;initial-value:100%}@property --tw-leading{syntax:\"*\";inherits:false}@property --tw-font-weight{syntax:\"*\";inherits:false}@property --tw-tracking{syntax:\"*\";inherits:false}@property --tw-ordinal{syntax:\"*\";inherits:false}@property --tw-slashed-zero{syntax:\"*\";inherits:false}@property --tw-numeric-figure{syntax:\"*\";inherits:false}@property --tw-numeric-spacing{syntax:\"*\";inherits:false}@property --tw-numeric-fraction{syntax:\"*\";inherits:false}@property --tw-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:\"*\";inherits:false}@property --tw-shadow-alpha{syntax:\"<percentage>\";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:\"*\";inherits:false}@property --tw-inset-shadow-alpha{syntax:\"<percentage>\";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:\"*\";inherits:false}@property --tw-ring-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:\"*\";inherits:false}@property --tw-inset-ring-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:\"*\";inherits:false}@property --tw-ring-offset-width{syntax:\"<length>\";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:\"*\";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:\"*\";inherits:false;initial-value:solid}@property --tw-blur{syntax:\"*\";inherits:false}@property --tw-brightness{syntax:\"*\";inherits:false}@property --tw-contrast{syntax:\"*\";inherits:false}@property --tw-grayscale{syntax:\"*\";inherits:false}@property --tw-hue-rotate{syntax:\"*\";inherits:false}@property --tw-invert{syntax:\"*\";inherits:false}@property --tw-opacity{syntax:\"*\";inherits:false}@property --tw-saturate{syntax:\"*\";inherits:false}@property --tw-sepia{syntax:\"*\";inherits:false}@property --tw-drop-shadow{syntax:\"*\";inherits:false}@property --tw-drop-shadow-color{syntax:\"*\";inherits:false}@property --tw-drop-shadow-alpha{syntax:\"<percentage>\";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:\"*\";inherits:false}@property --tw-backdrop-blur{syntax:\"*\";inherits:false}@property --tw-backdrop-brightness{syntax:\"*\";inherits:false}@property --tw-backdrop-contrast{syntax:\"*\";inherits:false}@property --tw-backdrop-grayscale{syntax:\"*\";inherits:false}@property --tw-backdrop-hue-rotate{syntax:\"*\";inherits:false}@property --tw-backdrop-invert{syntax:\"*\";inherits:false}@property --tw-backdrop-opacity{syntax:\"*\";inherits:false}@property --tw-backdrop-saturate{syntax:\"*\";inherits:false}@property --tw-backdrop-sepia{syntax:\"*\";inherits:false}@property --tw-duration{syntax:\"*\";inherits:false}@property --tw-ease{syntax:\"*\";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes ping{75%,to{opacity:0;transform:scale(2)}}@keyframes pulse{50%{opacity:.5}}@keyframes bounce{0%,to{animation-timing-function:cubic-bezier(.8,0,1,1);transform:translateY(-25%)}50%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:none}}@keyframes enter{0%{opacity:var(--tw-enter-opacity,1);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0)scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1))rotate(var(--tw-enter-rotate,0));filter:blur(var(--tw-enter-blur,0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity,1);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0)scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1))rotate(var(--tw-exit-rotate,0));filter:blur(var(--tw-exit-blur,0))}}.auth-page-container{--default-bg: #ffffff;--default-text-primary: rgb(52, 50, 45);--default-text-secondary: rgb(133, 132, 129);--default-border: rgba(0, 0, 0, .12);--default-input-bg: #ffffff;--default-button-bg: rgb(137, 137, 136);--default-button-text: #ffffff;--default-accent: #000000;position:relative;min-height:100vh;min-height:100dvh;width:100%;display:flex;align-items:center;justify-content:center;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background-color:var(--default-bg);color:var(--default-text-primary);overflow:hidden;background-image:radial-gradient(circle,#d1d1d1 1px,transparent 1px);background-size:20px 20px;background-position:center}@media(pointer:coarse){.auth-page-container{overflow-x:hidden;overflow-y:auto}}.auth-background-layer{position:absolute;top:0;right:0;bottom:0;left:0;z-index:0}body:has(.auth-page-container):before,body:has(.auth-page-container):after{content:none!important;display:none!important}.auth-page-container.dark{--default-bg: #1a1a1a;--default-text-primary: #ededed;--default-text-secondary: #a1a1a1;background-image:radial-gradient(circle,#333 1px,transparent 1px)}.auth-card{position:relative;z-index:50;width:100%;max-width:360px;padding:0;margin:0 auto}.auth-logo-wrapper{display:flex;flex-direction:column;align-items:center;text-align:center}.auth-logo-box{width:64px;height:64px;display:flex;align-items:center;justify-content:center;border-radius:16px;background:#fff;border:1px solid rgba(0,0,0,.08);box-shadow:0 4px 12px #0000000d;margin-bottom:1.5rem;color:var(--default-text-primary)}.auth-page-container.dark .auth-logo-box{background:#2a2a2a;border-color:#ffffff1a;color:#fff;box-shadow:0 4px 12px #0003}.auth-title{font-size:32px;font-weight:600;color:var(--default-text-primary);margin-bottom:.5rem;text-align:center;letter-spacing:-.02em}.auth-subtitle{font-size:14px;color:var(--default-text-secondary);text-align:center;margin-bottom:2rem}.auth-tabs-container{margin-bottom:1.5rem;display:flex;justify-content:center}.auth-tabs{display:flex;background:transparent;padding:0;border:none;gap:1rem}.auth-tab{padding:.5rem 1rem;font-size:14px;font-weight:500;color:var(--default-text-secondary);background:transparent;border:none;cursor:pointer;transition:color .2s}.auth-tab.active{color:var(--default-text-primary);background:transparent;box-shadow:none;border-bottom:2px solid var(--default-text-primary);border-radius:0}.auth-input{width:100%!important;height:40px!important;padding:4px 12px!important;font-size:14px!important;border-radius:8px!important;border:none!important;box-shadow:0 0 0 1px #0000001f!important;background:#fffc!important;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:var(--default-text-primary)!important;margin-bottom:1rem!important;transition:box-shadow .2s ease!important}.auth-page-container.dark .auth-input{background:#2a2a2acc!important;box-shadow:0 0 0 1px #ffffff1f!important;color:#fff!important}.auth-input:focus{outline:none!important;box-shadow:0 0 0 2px #0003!important}.auth-input:-webkit-autofill,.auth-input:-webkit-autofill:hover,.auth-input:-webkit-autofill:focus,.auth-input:-webkit-autofill:active{-webkit-text-fill-color:#000000!important}.auth-page-container.dark .auth-input:-webkit-autofill,.auth-page-container.dark .auth-input:-webkit-autofill:hover,.auth-page-container.dark .auth-input:-webkit-autofill:focus,.auth-page-container.dark .auth-input:-webkit-autofill:active{-webkit-text-fill-color:#a1a1a1!important;-webkit-box-shadow:0 0 0 30px rgba(42,42,42,.8) inset!important}.auth-button-primary{width:100%;height:40px;display:flex;align-items:center;justify-content:center;gap:.5rem;background-color:var(--default-text-primary);color:#fff;font-size:14px;font-weight:500;border-radius:8px;border:none;cursor:pointer;transition:opacity .2s;margin-top:1rem}.auth-page-container.dark .auth-button-primary{background-color:#8a8a8a;color:#000}.auth-button-primary:hover{opacity:.9}.auth-button-primary:disabled{background-color:#898988;cursor:not-allowed}.auth-footer-wrapper{margin-top:auto;padding-top:2rem;padding-bottom:2rem;width:100%;display:flex;justify-content:center}.auth-brand-footer{text-align:center;font-size:12px;color:var(--default-text-secondary);opacity:.7;max-width:400px;line-height:1.5;white-space:pre-line}.auth-brand-footer a{text-decoration:none;color:inherit;transition:color .2s}.auth-brand-footer a:hover{color:var(--default-text-primary)}.auth-floating-actions{position:absolute;top:1.5rem;right:1.5rem;z-index:100;display:flex;gap:.5rem}.auth-floating-switcher{width:32px!important;height:32px!important;min-width:32px!important;min-height:32px!important;padding:0!important;border-radius:50%!important;border:1px solid rgba(0,0,0,.08)!important;background:#fff;box-shadow:0 2px 4px #0000000d;color:var(--default-text-secondary);display:flex!important;align-items:center;justify-content:center;cursor:pointer;transition:all .2s;aspect-ratio:1 / 1}.auth-floating-switcher:hover{background:#fcfcfc;border-color:#00000026;color:var(--default-text-primary);transform:translateY(-1px);box-shadow:0 4px 6px #00000014}.auth-page-container.dark .auth-floating-switcher{background:#2a2a2a;border-color:#ffffff1a;color:#a1a1a1}.auth-page-container.dark .auth-floating-switcher:hover{background:#333;color:#fff}\n"
  },
  {
    "path": "frontend/assets/font-loader-CIrh3KnA.js",
    "content": "function _mergeNamespaces(A,t){for(var e=0;e<t.length;e++){const a=t[e];if(\"string\"!=typeof a&&!Array.isArray(a))for(const t in a)if(\"default\"!==t&&!(t in A)){const e=Object.getOwnPropertyDescriptor(a,t);e&&Object.defineProperty(A,t,e.get?e:{enumerable:!0,get:()=>a[t]})}}return Object.freeze(Object.defineProperty(A,Symbol.toStringTag,{value:\"Module\"}))}function getDefaultExportFromCjs(A){return A&&A.__esModule&&Object.prototype.hasOwnProperty.call(A,\"default\")?A.default:A}!function(){const A=document.createElement(\"link\").relList;if(!(A&&A.supports&&A.supports(\"modulepreload\"))){for(const A of document.querySelectorAll('link[rel=\"modulepreload\"]'))t(A);new MutationObserver(A=>{for(const e of A)if(\"childList\"===e.type)for(const A of e.addedNodes)\"LINK\"===A.tagName&&\"modulepreload\"===A.rel&&t(A)}).observe(document,{childList:!0,subtree:!0})}function t(A){if(A.ep)return;A.ep=!0;const t=function(A){const t={};return A.integrity&&(t.integrity=A.integrity),A.referrerPolicy&&(t.referrerPolicy=A.referrerPolicy),\"use-credentials\"===A.crossOrigin?t.credentials=\"include\":\"anonymous\"===A.crossOrigin?t.credentials=\"omit\":t.credentials=\"same-origin\",t}(A);fetch(A.href,t)}}();var jsxRuntime={exports:{}},reactJsxRuntime_production={},hasRequiredReactJsxRuntime_production,hasRequiredJsxRuntime;function requireReactJsxRuntime_production(){if(hasRequiredReactJsxRuntime_production)return reactJsxRuntime_production;hasRequiredReactJsxRuntime_production=1;var A=Symbol.for(\"react.transitional.element\"),t=Symbol.for(\"react.fragment\");function e(t,e,a){var r=null;if(void 0!==a&&(r=\"\"+a),void 0!==e.key&&(r=\"\"+e.key),\"key\"in e)for(var n in a={},e)\"key\"!==n&&(a[n]=e[n]);else a=e;return e=a.ref,{$$typeof:A,type:t,key:r,ref:void 0!==e?e:null,props:a}}return reactJsxRuntime_production.Fragment=t,reactJsxRuntime_production.jsx=e,reactJsxRuntime_production.jsxs=e,reactJsxRuntime_production}function requireJsxRuntime(){return hasRequiredJsxRuntime||(hasRequiredJsxRuntime=1,jsxRuntime.exports=requireReactJsxRuntime_production()),jsxRuntime.exports}var jsxRuntimeExports=requireJsxRuntime();const scriptRel=\"modulepreload\",assetsURL=function(A){return\"/\"+A},seen={},__vitePreload=function(A,t,e){let a=Promise.resolve();if(t&&t.length>0){let A=function(A){return Promise.all(A.map(A=>Promise.resolve(A).then(A=>({status:\"fulfilled\",value:A}),A=>({status:\"rejected\",reason:A}))))};document.getElementsByTagName(\"link\");const e=document.querySelector(\"meta[property=csp-nonce]\"),r=(null==e?void 0:e.nonce)||(null==e?void 0:e.getAttribute(\"nonce\"));a=A(t.map(A=>{if((A=assetsURL(A))in seen)return;seen[A]=!0;const t=A.endsWith(\".css\"),e=t?'[rel=\"stylesheet\"]':\"\";if(document.querySelector(`link[href=\"${A}\"]${e}`))return;const a=document.createElement(\"link\");return a.rel=t?\"stylesheet\":scriptRel,t||(a.as=\"script\"),a.crossOrigin=\"\",a.href=A,r&&a.setAttribute(\"nonce\",r),document.head.appendChild(a),t?new Promise((t,e)=>{a.addEventListener(\"load\",t),a.addEventListener(\"error\",()=>e(new Error(`Unable to preload CSS for ${A}`)))}):void 0}))}function r(A){const t=new Event(\"vite:preloadError\",{cancelable:!0});if(t.payload=A,window.dispatchEvent(t),!t.defaultPrevented)throw A}return a.then(t=>{for(const A of t||[])\"rejected\"===A.status&&r(A.reason);return A().catch(r)})};var react={exports:{}},react_production={},hasRequiredReact_production,hasRequiredReact;function requireReact_production(){if(hasRequiredReact_production)return react_production;hasRequiredReact_production=1;var A=Symbol.for(\"react.transitional.element\"),t=Symbol.for(\"react.portal\"),e=Symbol.for(\"react.fragment\"),a=Symbol.for(\"react.strict_mode\"),r=Symbol.for(\"react.profiler\"),n=Symbol.for(\"react.consumer\"),l=Symbol.for(\"react.context\"),i=Symbol.for(\"react.forward_ref\"),o=Symbol.for(\"react.suspense\"),s=Symbol.for(\"react.memo\"),p=Symbol.for(\"react.lazy\"),c=Symbol.for(\"react.activity\"),u=Symbol.iterator;var d={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},h=Object.assign,S={};function f(A,t,e){this.props=A,this.context=t,this.refs=S,this.updater=e||d}function m(){}function L(A,t,e){this.props=A,this.context=t,this.refs=S,this.updater=e||d}f.prototype.isReactComponent={},f.prototype.setState=function(A,t){if(\"object\"!=typeof A&&\"function\"!=typeof A&&null!=A)throw Error(\"takes an object of state variables to update or a function which returns an object of state variables.\");this.updater.enqueueSetState(this,A,t,\"setState\")},f.prototype.forceUpdate=function(A){this.updater.enqueueForceUpdate(this,A,\"forceUpdate\")},m.prototype=f.prototype;var W=L.prototype=new m;W.constructor=L,h(W,f.prototype),W.isPureReactComponent=!0;var g=Array.isArray;function y(){}var v={H:null,A:null,T:null,S:null},b=Object.prototype.hasOwnProperty;function x(t,e,a){var r=a.ref;return{$$typeof:A,type:t,key:e,ref:void 0!==r?r:null,props:a}}function E(t){return\"object\"==typeof t&&null!==t&&t.$$typeof===A}var k=/\\/+/g;function w(A,t){return\"object\"==typeof A&&null!==A&&null!=A.key?(e=\"\"+A.key,a={\"=\":\"=0\",\":\":\"=2\"},\"$\"+e.replace(/[=:]/g,function(A){return a[A]})):t.toString(36);var e,a}function C(e,a,r,n,l){var i=typeof e;\"undefined\"!==i&&\"boolean\"!==i||(e=null);var o,s,c=!1;if(null===e)c=!0;else switch(i){case\"bigint\":case\"string\":case\"number\":c=!0;break;case\"object\":switch(e.$$typeof){case A:case t:c=!0;break;case p:return C((c=e._init)(e._payload),a,r,n,l)}}if(c)return l=l(e),c=\"\"===n?\".\"+w(e,0):n,g(l)?(r=\"\",null!=c&&(r=c.replace(k,\"$&/\")+\"/\"),C(l,a,r,\"\",function(A){return A})):null!=l&&(E(l)&&(o=l,s=r+(null==l.key||e&&e.key===l.key?\"\":(\"\"+l.key).replace(k,\"$&/\")+\"/\")+c,l=x(o.type,s,o.props)),a.push(l)),1;c=0;var d,h=\"\"===n?\".\":n+\":\";if(g(e))for(var S=0;S<e.length;S++)c+=C(n=e[S],a,r,i=h+w(n,S),l);else if(\"function\"==typeof(S=null===(d=e)||\"object\"!=typeof d?null:\"function\"==typeof(d=u&&d[u]||d[\"@@iterator\"])?d:null))for(e=S.call(e),S=0;!(n=e.next()).done;)c+=C(n=n.value,a,r,i=h+w(n,S++),l);else if(\"object\"===i){if(\"function\"==typeof e.then)return C(function(A){switch(A.status){case\"fulfilled\":return A.value;case\"rejected\":throw A.reason;default:switch(\"string\"==typeof A.status?A.then(y,y):(A.status=\"pending\",A.then(function(t){\"pending\"===A.status&&(A.status=\"fulfilled\",A.value=t)},function(t){\"pending\"===A.status&&(A.status=\"rejected\",A.reason=t)})),A.status){case\"fulfilled\":return A.value;case\"rejected\":throw A.reason}}throw A}(e),a,r,n,l);throw a=String(e),Error(\"Objects are not valid as a React child (found: \"+(\"[object Object]\"===a?\"object with keys {\"+Object.keys(e).join(\", \")+\"}\":a)+\"). If you meant to render a collection of children, use an array instead.\")}return c}function P(A,t,e){if(null==A)return A;var a=[],r=0;return C(A,a,\"\",\"\",function(A){return t.call(e,A,r++)}),a}function T(A){if(-1===A._status){var t=A._result;(t=t()).then(function(t){0!==A._status&&-1!==A._status||(A._status=1,A._result=t)},function(t){0!==A._status&&-1!==A._status||(A._status=2,A._result=t)}),-1===A._status&&(A._status=0,A._result=t)}if(1===A._status)return A._result.default;throw A._result}var _=\"function\"==typeof reportError?reportError:function(A){if(\"object\"==typeof window&&\"function\"==typeof window.ErrorEvent){var t=new window.ErrorEvent(\"error\",{bubbles:!0,cancelable:!0,message:\"object\"==typeof A&&null!==A&&\"string\"==typeof A.message?String(A.message):String(A),error:A});if(!window.dispatchEvent(t))return}else if(\"object\"==typeof process&&\"function\"==typeof process.emit)return void process.emit(\"uncaughtException\",A)},M={map:P,forEach:function(A,t,e){P(A,function(){t.apply(this,arguments)},e)},count:function(A){var t=0;return P(A,function(){t++}),t},toArray:function(A){return P(A,function(A){return A})||[]},only:function(A){if(!E(A))throw Error(\"React.Children.only expected to receive a single React element child.\");return A}};return react_production.Activity=c,react_production.Children=M,react_production.Component=f,react_production.Fragment=e,react_production.Profiler=r,react_production.PureComponent=L,react_production.StrictMode=a,react_production.Suspense=o,react_production.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=v,react_production.__COMPILER_RUNTIME={__proto__:null,c:function(A){return v.H.useMemoCache(A)}},react_production.cache=function(A){return function(){return A.apply(null,arguments)}},react_production.cacheSignal=function(){return null},react_production.cloneElement=function(A,t,e){if(null==A)throw Error(\"The argument must be a React element, but you passed \"+A+\".\");var a=h({},A.props),r=A.key;if(null!=t)for(n in void 0!==t.key&&(r=\"\"+t.key),t)!b.call(t,n)||\"key\"===n||\"__self\"===n||\"__source\"===n||\"ref\"===n&&void 0===t.ref||(a[n]=t[n]);var n=arguments.length-2;if(1===n)a.children=e;else if(1<n){for(var l=Array(n),i=0;i<n;i++)l[i]=arguments[i+2];a.children=l}return x(A.type,r,a)},react_production.createContext=function(A){return(A={$$typeof:l,_currentValue:A,_currentValue2:A,_threadCount:0,Provider:null,Consumer:null}).Provider=A,A.Consumer={$$typeof:n,_context:A},A},react_production.createElement=function(A,t,e){var a,r={},n=null;if(null!=t)for(a in void 0!==t.key&&(n=\"\"+t.key),t)b.call(t,a)&&\"key\"!==a&&\"__self\"!==a&&\"__source\"!==a&&(r[a]=t[a]);var l=arguments.length-2;if(1===l)r.children=e;else if(1<l){for(var i=Array(l),o=0;o<l;o++)i[o]=arguments[o+2];r.children=i}if(A&&A.defaultProps)for(a in l=A.defaultProps)void 0===r[a]&&(r[a]=l[a]);return x(A,n,r)},react_production.createRef=function(){return{current:null}},react_production.forwardRef=function(A){return{$$typeof:i,render:A}},react_production.isValidElement=E,react_production.lazy=function(A){return{$$typeof:p,_payload:{_status:-1,_result:A},_init:T}},react_production.memo=function(A,t){return{$$typeof:s,type:A,compare:void 0===t?null:t}},react_production.startTransition=function(A){var t=v.T,e={};v.T=e;try{var a=A(),r=v.S;null!==r&&r(e,a),\"object\"==typeof a&&null!==a&&\"function\"==typeof a.then&&a.then(y,_)}catch(n){_(n)}finally{null!==t&&null!==e.types&&(t.types=e.types),v.T=t}},react_production.unstable_useCacheRefresh=function(){return v.H.useCacheRefresh()},react_production.use=function(A){return v.H.use(A)},react_production.useActionState=function(A,t,e){return v.H.useActionState(A,t,e)},react_production.useCallback=function(A,t){return v.H.useCallback(A,t)},react_production.useContext=function(A){return v.H.useContext(A)},react_production.useDebugValue=function(){},react_production.useDeferredValue=function(A,t){return v.H.useDeferredValue(A,t)},react_production.useEffect=function(A,t){return v.H.useEffect(A,t)},react_production.useEffectEvent=function(A){return v.H.useEffectEvent(A)},react_production.useId=function(){return v.H.useId()},react_production.useImperativeHandle=function(A,t,e){return v.H.useImperativeHandle(A,t,e)},react_production.useInsertionEffect=function(A,t){return v.H.useInsertionEffect(A,t)},react_production.useLayoutEffect=function(A,t){return v.H.useLayoutEffect(A,t)},react_production.useMemo=function(A,t){return v.H.useMemo(A,t)},react_production.useOptimistic=function(A,t){return v.H.useOptimistic(A,t)},react_production.useReducer=function(A,t,e){return v.H.useReducer(A,t,e)},react_production.useRef=function(A){return v.H.useRef(A)},react_production.useState=function(A){return v.H.useState(A)},react_production.useSyncExternalStore=function(A,t,e){return v.H.useSyncExternalStore(A,t,e)},react_production.useTransition=function(){return v.H.useTransition()},react_production.version=\"19.2.3\",react_production}function requireReact(){return hasRequiredReact||(hasRequiredReact=1,react.exports=requireReact_production()),react.exports}var reactExports=requireReact();const React=getDefaultExportFromCjs(reactExports),React$1=_mergeNamespaces({__proto__:null,default:React},[reactExports]),warn=(A,t,e,a)=>{var r,n,l,i;const o=[e,{code:t,...a||{}}];if(null==(n=null==(r=null==A?void 0:A.services)?void 0:r.logger)?void 0:n.forward)return A.services.logger.forward(o,\"warn\",\"react-i18next::\",!0);isString$1(o[0])&&(o[0]=`react-i18next:: ${o[0]}`),(null==(i=null==(l=null==A?void 0:A.services)?void 0:l.logger)?void 0:i.warn)?A.services.logger.warn(...o):null==console||console.warn},alreadyWarned={},warnOnce=(A,t,e,a)=>{isString$1(e)&&alreadyWarned[e]||(isString$1(e)&&(alreadyWarned[e]=new Date),warn(A,t,e,a))},loadedClb=(A,t)=>()=>{if(A.isInitialized)t();else{const e=()=>{setTimeout(()=>{A.off(\"initialized\",e)},0),t()};A.on(\"initialized\",e)}},loadNamespaces=(A,t,e)=>{A.loadNamespaces(t,loadedClb(A,e))},loadLanguages=(A,t,e,a)=>{if(isString$1(e)&&(e=[e]),A.options.preload&&A.options.preload.indexOf(t)>-1)return loadNamespaces(A,e,a);e.forEach(t=>{A.options.ns.indexOf(t)<0&&A.options.ns.push(t)}),A.loadLanguages(t,loadedClb(A,a))},hasLoadedNamespace=(A,t,e={})=>t.languages&&t.languages.length?t.hasLoadedNamespace(A,{lng:e.lng,precheck:(t,a)=>{if(e.bindI18n&&e.bindI18n.indexOf(\"languageChanging\")>-1&&t.services.backendConnector.backend&&t.isLanguageChangingTo&&!a(t.isLanguageChangingTo,A))return!1}}):(warnOnce(t,\"NO_LANGUAGES\",\"i18n.languages were undefined or empty\",{languages:t.languages}),!0),isString$1=A=>\"string\"==typeof A,isObject=A=>\"object\"==typeof A&&null!==A,matchHtmlEntity=/&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34|nbsp|#160|copy|#169|reg|#174|hellip|#8230|#x2F|#47);/g,htmlEntities={\"&amp;\":\"&\",\"&#38;\":\"&\",\"&lt;\":\"<\",\"&#60;\":\"<\",\"&gt;\":\">\",\"&#62;\":\">\",\"&apos;\":\"'\",\"&#39;\":\"'\",\"&quot;\":'\"',\"&#34;\":'\"',\"&nbsp;\":\" \",\"&#160;\":\" \",\"&copy;\":\"©\",\"&#169;\":\"©\",\"&reg;\":\"®\",\"&#174;\":\"®\",\"&hellip;\":\"…\",\"&#8230;\":\"…\",\"&#x2F;\":\"/\",\"&#47;\":\"/\"},unescapeHtmlEntity=A=>htmlEntities[A],unescape=A=>A.replace(matchHtmlEntity,unescapeHtmlEntity);let defaultOptions$1={bindI18n:\"languageChanged\",bindI18nStore:\"\",transEmptyNodeValue:\"\",transSupportBasicHtmlNodes:!0,transWrapTextNodes:\"\",transKeepBasicHtmlNodesFor:[\"br\",\"strong\",\"i\",\"p\"],useSuspense:!0,unescape:unescape};const setDefaults=(A={})=>{defaultOptions$1={...defaultOptions$1,...A}},getDefaults=()=>defaultOptions$1;let i18nInstance;const setI18n=A=>{i18nInstance=A},getI18n=()=>i18nInstance,initReactI18next={type:\"3rdParty\",init(A){setDefaults(A.options.react),setI18n(A)}},I18nContext=reactExports.createContext();class ReportNamespaces{constructor(){this.usedNamespaces={}}addUsedNamespaces(A){A.forEach(A=>{this.usedNamespaces[A]||(this.usedNamespaces[A]=!0)})}getUsedNamespaces(){return Object.keys(this.usedNamespaces)}}const usePrevious=(A,t)=>{const e=reactExports.useRef();return reactExports.useEffect(()=>{e.current=A},[A,t]),e.current},alwaysNewT=(A,t,e,a)=>A.getFixedT(t,e,a),useMemoizedT=(A,t,e,a)=>reactExports.useCallback(alwaysNewT(A,t,e,a),[A,t,e,a]),useTranslation=(A,t={})=>{var e,a,r,n;const{i18n:l}=t,{i18n:i,defaultNS:o}=reactExports.useContext(I18nContext)||{},s=l||i||getI18n();if(s&&!s.reportNamespaces&&(s.reportNamespaces=new ReportNamespaces),!s){warnOnce(s,\"NO_I18NEXT_INSTANCE\",\"useTranslation: You will need to pass in an i18next instance by using initReactI18next\");const A=(A,t)=>isString$1(t)?t:isObject(t)&&isString$1(t.defaultValue)?t.defaultValue:Array.isArray(A)?A[A.length-1]:A,t=[A,{},!1];return t.t=A,t.i18n={},t.ready=!1,t}(null==(e=s.options.react)?void 0:e.wait)&&warnOnce(s,\"DEPRECATED_OPTION\",\"useTranslation: It seems you are still using the old wait option, you may migrate to the new useSuspense behaviour.\");const p={...getDefaults(),...s.options.react,...t},{useSuspense:c,keyPrefix:u}=p;let d=o||(null==(a=s.options)?void 0:a.defaultNS);d=isString$1(d)?[d]:d||[\"translation\"],null==(n=(r=s.reportNamespaces).addUsedNamespaces)||n.call(r,d);const h=(s.isInitialized||s.initializedStoreOnce)&&d.every(A=>hasLoadedNamespace(A,s,p)),S=useMemoizedT(s,t.lng||null,\"fallback\"===p.nsMode?d:d[0],u),f=()=>S,m=()=>alwaysNewT(s,t.lng||null,\"fallback\"===p.nsMode?d:d[0],u),[L,W]=reactExports.useState(f);let g=d.join();t.lng&&(g=`${t.lng}${g}`);const y=usePrevious(g),v=reactExports.useRef(!0);reactExports.useEffect(()=>{const{bindI18n:A,bindI18nStore:e}=p;v.current=!0,h||c||(t.lng?loadLanguages(s,t.lng,d,()=>{v.current&&W(m)}):loadNamespaces(s,d,()=>{v.current&&W(m)})),h&&y&&y!==g&&v.current&&W(m);const a=()=>{v.current&&W(m)};return A&&(null==s||s.on(A,a)),e&&(null==s||s.store.on(e,a)),()=>{v.current=!1,s&&A&&(null==A||A.split(\" \").forEach(A=>s.off(A,a))),e&&s&&e.split(\" \").forEach(A=>s.store.off(A,a))}},[s,g]),reactExports.useEffect(()=>{v.current&&h&&W(f)},[s,u,h]);const b=[L,s,h];if(b.t=L,b.i18n=s,b.ready=h,h)return b;if(!h&&!c)return b;throw new Promise(A=>{t.lng?loadLanguages(s,t.lng,d,()=>A()):loadNamespaces(s,d,()=>A())})},changeLang=async(A,t=\"lang\")=>{localStorage.setItem(t,A),await ensureResourceLoaded(A),await instance.changeLanguage(A)};function getBrowserLang(A){if(A)return localStorage.getItem(A)||navigator.language||\"en\";const t=\"undefined\"!=typeof window&&(window.location.pathname.toLowerCase().includes(\"share\")||window.location.search.toLowerCase().includes(\"share\")),e=t?\"share-lang\":\"lang\";let a=localStorage.getItem(e);!a&&t&&(a=localStorage.getItem(\"lang\")),a||(a=navigator.language);const r=null==a?void 0:a.toString();return r?r.includes(\",\")?r.split(\",\")[0]:r:\"en\"}const utils=Object.freeze(Object.defineProperty({__proto__:null,changeLang:changeLang,getBrowserLang:getBrowserLang},Symbol.toStringTag,{value:\"Module\"})),isString=A=>\"string\"==typeof A,defer=()=>{let A,t;const e=new Promise((e,a)=>{A=e,t=a});return e.resolve=A,e.reject=t,e},makeString=A=>null==A?\"\":\"\"+A,copy=(A,t,e)=>{A.forEach(A=>{t[A]&&(e[A]=t[A])})},lastOfPathSeparatorRegExp=/###/g,cleanKey=A=>A&&A.indexOf(\"###\")>-1?A.replace(lastOfPathSeparatorRegExp,\".\"):A,canNotTraverseDeeper=A=>!A||isString(A),getLastOfPath=(A,t,e)=>{const a=isString(t)?t.split(\".\"):t;let r=0;for(;r<a.length-1;){if(canNotTraverseDeeper(A))return{};const t=cleanKey(a[r]);!A[t]&&e&&(A[t]=new e),A=Object.prototype.hasOwnProperty.call(A,t)?A[t]:{},++r}return canNotTraverseDeeper(A)?{}:{obj:A,k:cleanKey(a[r])}},setPath=(A,t,e)=>{const{obj:a,k:r}=getLastOfPath(A,t,Object);if(void 0!==a||1===t.length)return void(a[r]=e);let n=t[t.length-1],l=t.slice(0,t.length-1),i=getLastOfPath(A,l,Object);for(;void 0===i.obj&&l.length;)n=`${l[l.length-1]}.${n}`,l=l.slice(0,l.length-1),i=getLastOfPath(A,l,Object),(null==i?void 0:i.obj)&&void 0!==i.obj[`${i.k}.${n}`]&&(i.obj=void 0);i.obj[`${i.k}.${n}`]=e},pushPath=(A,t,e,a)=>{const{obj:r,k:n}=getLastOfPath(A,t,Object);r[n]=r[n]||[],r[n].push(e)},getPath=(A,t)=>{const{obj:e,k:a}=getLastOfPath(A,t);if(e&&Object.prototype.hasOwnProperty.call(e,a))return e[a]},getPathWithDefaults=(A,t,e)=>{const a=getPath(A,e);return void 0!==a?a:getPath(t,e)},deepExtend=(A,t,e)=>{for(const a in t)\"__proto__\"!==a&&\"constructor\"!==a&&(a in A?isString(A[a])||A[a]instanceof String||isString(t[a])||t[a]instanceof String?e&&(A[a]=t[a]):deepExtend(A[a],t[a],e):A[a]=t[a]);return A},regexEscape=A=>A.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g,\"\\\\$&\");var _entityMap={\"&\":\"&amp;\",\"<\":\"&lt;\",\">\":\"&gt;\",'\"':\"&quot;\",\"'\":\"&#39;\",\"/\":\"&#x2F;\"};const escape=A=>isString(A)?A.replace(/[&<>\"'\\/]/g,A=>_entityMap[A]):A;class RegExpCache{constructor(A){this.capacity=A,this.regExpMap=new Map,this.regExpQueue=[]}getRegExp(A){const t=this.regExpMap.get(A);if(void 0!==t)return t;const e=new RegExp(A);return this.regExpQueue.length===this.capacity&&this.regExpMap.delete(this.regExpQueue.shift()),this.regExpMap.set(A,e),this.regExpQueue.push(A),e}}const chars=[\" \",\",\",\"?\",\"!\",\";\"],looksLikeObjectPathRegExpCache=new RegExpCache(20),looksLikeObjectPath=(A,t,e)=>{t=t||\"\",e=e||\"\";const a=chars.filter(A=>t.indexOf(A)<0&&e.indexOf(A)<0);if(0===a.length)return!0;const r=looksLikeObjectPathRegExpCache.getRegExp(`(${a.map(A=>\"?\"===A?\"\\\\?\":A).join(\"|\")})`);let n=!r.test(A);if(!n){const t=A.indexOf(e);t>0&&!r.test(A.substring(0,t))&&(n=!0)}return n},deepFind=function(A,t){let e=arguments.length>2&&void 0!==arguments[2]?arguments[2]:\".\";if(!A)return;if(A[t]){if(!Object.prototype.hasOwnProperty.call(A,t))return;return A[t]}const a=t.split(e);let r=A;for(let n=0;n<a.length;){if(!r||\"object\"!=typeof r)return;let A,t=\"\";for(let l=n;l<a.length;++l)if(l!==n&&(t+=e),t+=a[l],A=r[t],void 0!==A){if([\"string\",\"number\",\"boolean\"].indexOf(typeof A)>-1&&l<a.length-1)continue;n+=l-n+1;break}r=A}return r},getCleanedCode=A=>null==A?void 0:A.replace(\"_\",\"-\"),consoleLogger={type:\"logger\",log(A){this.output(\"log\",A)},warn(A){this.output(\"warn\",A)},error(A){this.output(\"error\",A)},output(A,t){var e,a;null==(a=null==(e=null==console?void 0:console[A])?void 0:e.apply)||a.call(e,console,t)}};class Logger{constructor(A){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};this.init(A,t)}init(A){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};this.prefix=t.prefix||\"i18next:\",this.logger=A||consoleLogger,this.options=t,this.debug=t.debug}log(){for(var A=arguments.length,t=new Array(A),e=0;e<A;e++)t[e]=arguments[e];return this.forward(t,\"log\",\"\",!0)}warn(){for(var A=arguments.length,t=new Array(A),e=0;e<A;e++)t[e]=arguments[e];return this.forward(t,\"warn\",\"\",!0)}error(){for(var A=arguments.length,t=new Array(A),e=0;e<A;e++)t[e]=arguments[e];return this.forward(t,\"error\",\"\")}deprecate(){for(var A=arguments.length,t=new Array(A),e=0;e<A;e++)t[e]=arguments[e];return this.forward(t,\"warn\",\"WARNING DEPRECATED: \",!0)}forward(A,t,e,a){return a&&!this.debug?null:(isString(A[0])&&(A[0]=`${e}${this.prefix} ${A[0]}`),this.logger[t](A))}create(A){return new Logger(this.logger,{prefix:`${this.prefix}:${A}:`,...this.options})}clone(A){return(A=A||this.options).prefix=A.prefix||this.prefix,new Logger(this.logger,A)}}var baseLogger=new Logger;class EventEmitter{constructor(){this.observers={}}on(A,t){return A.split(\" \").forEach(A=>{this.observers[A]||(this.observers[A]=new Map);const e=this.observers[A].get(t)||0;this.observers[A].set(t,e+1)}),this}off(A,t){this.observers[A]&&(t?this.observers[A].delete(t):delete this.observers[A])}emit(A){for(var t=arguments.length,e=new Array(t>1?t-1:0),a=1;a<t;a++)e[a-1]=arguments[a];if(this.observers[A]){Array.from(this.observers[A].entries()).forEach(A=>{let[t,a]=A;for(let r=0;r<a;r++)t(...e)})}if(this.observers[\"*\"]){Array.from(this.observers[\"*\"].entries()).forEach(t=>{let[a,r]=t;for(let n=0;n<r;n++)a.apply(a,[A,...e])})}}}class ResourceStore extends EventEmitter{constructor(A){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{ns:[\"translation\"],defaultNS:\"translation\"};super(),this.data=A||{},this.options=t,void 0===this.options.keySeparator&&(this.options.keySeparator=\".\"),void 0===this.options.ignoreJSONStructure&&(this.options.ignoreJSONStructure=!0)}addNamespaces(A){this.options.ns.indexOf(A)<0&&this.options.ns.push(A)}removeNamespaces(A){const t=this.options.ns.indexOf(A);t>-1&&this.options.ns.splice(t,1)}getResource(A,t,e){var a,r;let n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};const l=void 0!==n.keySeparator?n.keySeparator:this.options.keySeparator,i=void 0!==n.ignoreJSONStructure?n.ignoreJSONStructure:this.options.ignoreJSONStructure;let o;A.indexOf(\".\")>-1?o=A.split(\".\"):(o=[A,t],e&&(Array.isArray(e)?o.push(...e):isString(e)&&l?o.push(...e.split(l)):o.push(e)));const s=getPath(this.data,o);return!s&&!t&&!e&&A.indexOf(\".\")>-1&&(A=o[0],t=o[1],e=o.slice(2).join(\".\")),!s&&i&&isString(e)?deepFind(null==(r=null==(a=this.data)?void 0:a[A])?void 0:r[t],e,l):s}addResource(A,t,e,a){let r=arguments.length>4&&void 0!==arguments[4]?arguments[4]:{silent:!1};const n=void 0!==r.keySeparator?r.keySeparator:this.options.keySeparator;let l=[A,t];e&&(l=l.concat(n?e.split(n):e)),A.indexOf(\".\")>-1&&(l=A.split(\".\"),a=t,t=l[1]),this.addNamespaces(t),setPath(this.data,l,a),r.silent||this.emit(\"added\",A,t,e,a)}addResources(A,t,e){let a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{silent:!1};for(const r in e)(isString(e[r])||Array.isArray(e[r]))&&this.addResource(A,t,r,e[r],{silent:!0});a.silent||this.emit(\"added\",A,t,e)}addResourceBundle(A,t,e,a,r){let n=arguments.length>5&&void 0!==arguments[5]?arguments[5]:{silent:!1,skipCopy:!1},l=[A,t];A.indexOf(\".\")>-1&&(l=A.split(\".\"),a=e,e=t,t=l[1]),this.addNamespaces(t);let i=getPath(this.data,l)||{};n.skipCopy||(e=JSON.parse(JSON.stringify(e))),a?deepExtend(i,e,r):i={...i,...e},setPath(this.data,l,i),n.silent||this.emit(\"added\",A,t,e)}removeResourceBundle(A,t){this.hasResourceBundle(A,t)&&delete this.data[A][t],this.removeNamespaces(t),this.emit(\"removed\",A,t)}hasResourceBundle(A,t){return void 0!==this.getResource(A,t)}getResourceBundle(A,t){return t||(t=this.options.defaultNS),this.getResource(A,t)}getDataByLanguage(A){return this.data[A]}hasLanguageSomeTranslations(A){const t=this.getDataByLanguage(A);return!!(t&&Object.keys(t)||[]).find(A=>t[A]&&Object.keys(t[A]).length>0)}toJSON(){return this.data}}var postProcessor={processors:{},addPostProcessor(A){this.processors[A.name]=A},handle(A,t,e,a,r){return A.forEach(A=>{var n;t=(null==(n=this.processors[A])?void 0:n.process(t,e,a,r))??t}),t}};const checkedLoadedFor={},shouldHandleAsObject=A=>!isString(A)&&\"boolean\"!=typeof A&&\"number\"!=typeof A;class Translator extends EventEmitter{constructor(A){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};super(),copy([\"resourceStore\",\"languageUtils\",\"pluralResolver\",\"interpolator\",\"backendConnector\",\"i18nFormat\",\"utils\"],A,this),this.options=t,void 0===this.options.keySeparator&&(this.options.keySeparator=\".\"),this.logger=baseLogger.create(\"translator\")}changeLanguage(A){A&&(this.language=A)}exists(A){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{interpolation:{}};if(null==A)return!1;const e=this.resolve(A,t);return void 0!==(null==e?void 0:e.res)}extractFromKey(A,t){let e=void 0!==t.nsSeparator?t.nsSeparator:this.options.nsSeparator;void 0===e&&(e=\":\");const a=void 0!==t.keySeparator?t.keySeparator:this.options.keySeparator;let r=t.ns||this.options.defaultNS||[];const n=e&&A.indexOf(e)>-1,l=!(this.options.userDefinedKeySeparator||t.keySeparator||this.options.userDefinedNsSeparator||t.nsSeparator||looksLikeObjectPath(A,e,a));if(n&&!l){const t=A.match(this.interpolator.nestingRegexp);if(t&&t.length>0)return{key:A,namespaces:isString(r)?[r]:r};const n=A.split(e);(e!==a||e===a&&this.options.ns.indexOf(n[0])>-1)&&(r=n.shift()),A=n.join(a)}return{key:A,namespaces:isString(r)?[r]:r}}translate(A,t,e){if(\"object\"!=typeof t&&this.options.overloadTranslationOptionHandler&&(t=this.options.overloadTranslationOptionHandler(arguments)),\"object\"==typeof t&&(t={...t}),t||(t={}),null==A)return\"\";Array.isArray(A)||(A=[String(A)]);const a=void 0!==t.returnDetails?t.returnDetails:this.options.returnDetails,r=void 0!==t.keySeparator?t.keySeparator:this.options.keySeparator,{key:n,namespaces:l}=this.extractFromKey(A[A.length-1],t),i=l[l.length-1],o=t.lng||this.language,s=t.appendNamespaceToCIMode||this.options.appendNamespaceToCIMode;if(\"cimode\"===(null==o?void 0:o.toLowerCase())){if(s){const A=t.nsSeparator||this.options.nsSeparator;return a?{res:`${i}${A}${n}`,usedKey:n,exactUsedKey:n,usedLng:o,usedNS:i,usedParams:this.getUsedParamsDetails(t)}:`${i}${A}${n}`}return a?{res:n,usedKey:n,exactUsedKey:n,usedLng:o,usedNS:i,usedParams:this.getUsedParamsDetails(t)}:n}const p=this.resolve(A,t);let c=null==p?void 0:p.res;const u=(null==p?void 0:p.usedKey)||n,d=(null==p?void 0:p.exactUsedKey)||n,h=void 0!==t.joinArrays?t.joinArrays:this.options.joinArrays,S=!this.i18nFormat||this.i18nFormat.handleAsObject,f=void 0!==t.count&&!isString(t.count),m=Translator.hasDefaultValue(t),L=f?this.pluralResolver.getSuffix(o,t.count,t):\"\",W=t.ordinal&&f?this.pluralResolver.getSuffix(o,t.count,{ordinal:!1}):\"\",g=f&&!t.ordinal&&0===t.count,y=g&&t[`defaultValue${this.options.pluralSeparator}zero`]||t[`defaultValue${L}`]||t[`defaultValue${W}`]||t.defaultValue;let v=c;S&&!c&&m&&(v=y);const b=shouldHandleAsObject(v),x=Object.prototype.toString.apply(v);if(!(S&&v&&b&&[\"[object Number]\",\"[object Function]\",\"[object RegExp]\"].indexOf(x)<0)||isString(h)&&Array.isArray(v))if(S&&isString(h)&&Array.isArray(c))c=c.join(h),c&&(c=this.extendTranslation(c,A,t,e));else{let a=!1,l=!1;!this.isValidLookup(c)&&m&&(a=!0,c=y),this.isValidLookup(c)||(l=!0,c=n);const s=(t.missingKeyNoValueFallbackToKey||this.options.missingKeyNoValueFallbackToKey)&&l?void 0:c,u=m&&y!==c&&this.options.updateMissing;if(l||a||u){if(this.logger.log(u?\"updateKey\":\"missingKey\",o,i,n,u?y:c),r){const A=this.resolve(n,{...t,keySeparator:!1});A&&A.res&&this.logger.warn(\"Seems the loaded translations were in flat JSON format instead of nested. Either set keySeparator: false on init or make sure your translations are published in nested format.\")}let A=[];const e=this.languageUtils.getFallbackCodes(this.options.fallbackLng,t.lng||this.language);if(\"fallback\"===this.options.saveMissingTo&&e&&e[0])for(let t=0;t<e.length;t++)A.push(e[t]);else\"all\"===this.options.saveMissingTo?A=this.languageUtils.toResolveHierarchy(t.lng||this.language):A.push(t.lng||this.language);const a=(A,e,a)=>{var r;const n=m&&a!==c?a:s;this.options.missingKeyHandler?this.options.missingKeyHandler(A,i,e,n,u,t):(null==(r=this.backendConnector)?void 0:r.saveMissing)&&this.backendConnector.saveMissing(A,i,e,n,u,t),this.emit(\"missingKey\",A,i,e,c)};this.options.saveMissing&&(this.options.saveMissingPlurals&&f?A.forEach(A=>{const e=this.pluralResolver.getSuffixes(A,t);g&&t[`defaultValue${this.options.pluralSeparator}zero`]&&e.indexOf(`${this.options.pluralSeparator}zero`)<0&&e.push(`${this.options.pluralSeparator}zero`),e.forEach(e=>{a([A],n+e,t[`defaultValue${e}`]||y)})}):a(A,n,y))}c=this.extendTranslation(c,A,t,p,e),l&&c===n&&this.options.appendNamespaceToMissingKey&&(c=`${i}:${n}`),(l||a)&&this.options.parseMissingKeyHandler&&(c=this.options.parseMissingKeyHandler(this.options.appendNamespaceToMissingKey?`${i}:${n}`:n,a?c:void 0))}else{if(!t.returnObjects&&!this.options.returnObjects){this.options.returnedObjectHandler||this.logger.warn(\"accessing an object - but returnObjects options is not enabled!\");const A=this.options.returnedObjectHandler?this.options.returnedObjectHandler(u,v,{...t,ns:l}):`key '${n} (${this.language})' returned an object instead of string.`;return a?(p.res=A,p.usedParams=this.getUsedParamsDetails(t),p):A}if(r){const A=Array.isArray(v),e=A?[]:{},a=A?d:u;for(const n in v)if(Object.prototype.hasOwnProperty.call(v,n)){const A=`${a}${r}${n}`;e[n]=m&&!c?this.translate(A,{...t,defaultValue:shouldHandleAsObject(y)?y[n]:void 0,joinArrays:!1,ns:l}):this.translate(A,{...t,joinArrays:!1,ns:l}),e[n]===A&&(e[n]=v[n])}c=e}}return a?(p.res=c,p.usedParams=this.getUsedParamsDetails(t),p):c}extendTranslation(A,t,e,a,r){var n,l,i=this;if(null==(n=this.i18nFormat)?void 0:n.parse)A=this.i18nFormat.parse(A,{...this.options.interpolation.defaultVariables,...e},e.lng||this.language||a.usedLng,a.usedNS,a.usedKey,{resolved:a});else if(!e.skipInterpolation){e.interpolation&&this.interpolator.init({...e,interpolation:{...this.options.interpolation,...e.interpolation}});const n=isString(A)&&(void 0!==(null==(l=null==e?void 0:e.interpolation)?void 0:l.skipOnVariables)?e.interpolation.skipOnVariables:this.options.interpolation.skipOnVariables);let o;if(n){const t=A.match(this.interpolator.nestingRegexp);o=t&&t.length}let s=e.replace&&!isString(e.replace)?e.replace:e;if(this.options.interpolation.defaultVariables&&(s={...this.options.interpolation.defaultVariables,...s}),A=this.interpolator.interpolate(A,s,e.lng||this.language||a.usedLng,e),n){const t=A.match(this.interpolator.nestingRegexp);o<(t&&t.length)&&(e.nest=!1)}!e.lng&&a&&a.res&&(e.lng=this.language||a.usedLng),!1!==e.nest&&(A=this.interpolator.nest(A,function(){for(var A=arguments.length,a=new Array(A),n=0;n<A;n++)a[n]=arguments[n];return(null==r?void 0:r[0])!==a[0]||e.context?i.translate(...a,t):(i.logger.warn(`It seems you are nesting recursively key: ${a[0]} in key: ${t[0]}`),null)},e)),e.interpolation&&this.interpolator.reset()}const o=e.postProcess||this.options.postProcess,s=isString(o)?[o]:o;return null!=A&&(null==s?void 0:s.length)&&!1!==e.applyPostProcessor&&(A=postProcessor.handle(s,A,t,this.options&&this.options.postProcessPassResolved?{i18nResolved:{...a,usedParams:this.getUsedParamsDetails(e)},...e}:e,this)),A}resolve(A){let t,e,a,r,n,l=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return isString(A)&&(A=[A]),A.forEach(A=>{if(this.isValidLookup(t))return;const i=this.extractFromKey(A,l),o=i.key;e=o;let s=i.namespaces;this.options.fallbackNS&&(s=s.concat(this.options.fallbackNS));const p=void 0!==l.count&&!isString(l.count),c=p&&!l.ordinal&&0===l.count,u=void 0!==l.context&&(isString(l.context)||\"number\"==typeof l.context)&&\"\"!==l.context,d=l.lngs?l.lngs:this.languageUtils.toResolveHierarchy(l.lng||this.language,l.fallbackLng);s.forEach(A=>{var i,s;this.isValidLookup(t)||(n=A,checkedLoadedFor[`${d[0]}-${A}`]||!(null==(i=this.utils)?void 0:i.hasLoadedNamespace)||(null==(s=this.utils)?void 0:s.hasLoadedNamespace(n))||(checkedLoadedFor[`${d[0]}-${A}`]=!0,this.logger.warn(`key \"${e}\" for languages \"${d.join(\", \")}\" won't get resolved as namespace \"${n}\" was not yet loaded`,\"This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!\")),d.forEach(e=>{var n;if(this.isValidLookup(t))return;r=e;const i=[o];if(null==(n=this.i18nFormat)?void 0:n.addLookupKeys)this.i18nFormat.addLookupKeys(i,o,e,A,l);else{let A;p&&(A=this.pluralResolver.getSuffix(e,l.count,l));const t=`${this.options.pluralSeparator}zero`,a=`${this.options.pluralSeparator}ordinal${this.options.pluralSeparator}`;if(p&&(i.push(o+A),l.ordinal&&0===A.indexOf(a)&&i.push(o+A.replace(a,this.options.pluralSeparator)),c&&i.push(o+t)),u){const e=`${o}${this.options.contextSeparator}${l.context}`;i.push(e),p&&(i.push(e+A),l.ordinal&&0===A.indexOf(a)&&i.push(e+A.replace(a,this.options.pluralSeparator)),c&&i.push(e+t))}}let s;for(;s=i.pop();)this.isValidLookup(t)||(a=s,t=this.getResource(e,A,s,l))}))})}),{res:t,usedKey:e,exactUsedKey:a,usedLng:r,usedNS:n}}isValidLookup(A){return!(void 0===A||!this.options.returnNull&&null===A||!this.options.returnEmptyString&&\"\"===A)}getResource(A,t,e){var a;let r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};return(null==(a=this.i18nFormat)?void 0:a.getResource)?this.i18nFormat.getResource(A,t,e,r):this.resourceStore.getResource(A,t,e,r)}getUsedParamsDetails(){let A=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const t=[\"defaultValue\",\"ordinal\",\"context\",\"replace\",\"lng\",\"lngs\",\"fallbackLng\",\"ns\",\"keySeparator\",\"nsSeparator\",\"returnObjects\",\"returnDetails\",\"joinArrays\",\"postProcess\",\"interpolation\"],e=A.replace&&!isString(A.replace);let a=e?A.replace:A;if(e&&void 0!==A.count&&(a.count=A.count),this.options.interpolation.defaultVariables&&(a={...this.options.interpolation.defaultVariables,...a}),!e){a={...a};for(const A of t)delete a[A]}return a}static hasDefaultValue(A){const t=\"defaultValue\";for(const e in A)if(Object.prototype.hasOwnProperty.call(A,e)&&t===e.substring(0,12)&&void 0!==A[e])return!0;return!1}}class LanguageUtil{constructor(A){this.options=A,this.supportedLngs=this.options.supportedLngs||!1,this.logger=baseLogger.create(\"languageUtils\")}getScriptPartFromCode(A){if(!(A=getCleanedCode(A))||A.indexOf(\"-\")<0)return null;const t=A.split(\"-\");return 2===t.length?null:(t.pop(),\"x\"===t[t.length-1].toLowerCase()?null:this.formatLanguageCode(t.join(\"-\")))}getLanguagePartFromCode(A){if(!(A=getCleanedCode(A))||A.indexOf(\"-\")<0)return A;const t=A.split(\"-\");return this.formatLanguageCode(t[0])}formatLanguageCode(A){if(isString(A)&&A.indexOf(\"-\")>-1){let e;try{e=Intl.getCanonicalLocales(A)[0]}catch(t){}return e&&this.options.lowerCaseLng&&(e=e.toLowerCase()),e||(this.options.lowerCaseLng?A.toLowerCase():A)}return this.options.cleanCode||this.options.lowerCaseLng?A.toLowerCase():A}isSupportedCode(A){return(\"languageOnly\"===this.options.load||this.options.nonExplicitSupportedLngs)&&(A=this.getLanguagePartFromCode(A)),!this.supportedLngs||!this.supportedLngs.length||this.supportedLngs.indexOf(A)>-1}getBestMatchFromCodes(A){if(!A)return null;let t;return A.forEach(A=>{if(t)return;const e=this.formatLanguageCode(A);this.options.supportedLngs&&!this.isSupportedCode(e)||(t=e)}),!t&&this.options.supportedLngs&&A.forEach(A=>{if(t)return;const e=this.getLanguagePartFromCode(A);if(this.isSupportedCode(e))return t=e;t=this.options.supportedLngs.find(A=>A===e?A:A.indexOf(\"-\")<0&&e.indexOf(\"-\")<0?void 0:A.indexOf(\"-\")>0&&e.indexOf(\"-\")<0&&A.substring(0,A.indexOf(\"-\"))===e||0===A.indexOf(e)&&e.length>1?A:void 0)}),t||(t=this.getFallbackCodes(this.options.fallbackLng)[0]),t}getFallbackCodes(A,t){if(!A)return[];if(\"function\"==typeof A&&(A=A(t)),isString(A)&&(A=[A]),Array.isArray(A))return A;if(!t)return A.default||[];let e=A[t];return e||(e=A[this.getScriptPartFromCode(t)]),e||(e=A[this.formatLanguageCode(t)]),e||(e=A[this.getLanguagePartFromCode(t)]),e||(e=A.default),e||[]}toResolveHierarchy(A,t){const e=this.getFallbackCodes(t||this.options.fallbackLng||[],A),a=[],r=A=>{A&&(this.isSupportedCode(A)?a.push(A):this.logger.warn(`rejecting language code not found in supportedLngs: ${A}`))};return isString(A)&&(A.indexOf(\"-\")>-1||A.indexOf(\"_\")>-1)?(\"languageOnly\"!==this.options.load&&r(this.formatLanguageCode(A)),\"languageOnly\"!==this.options.load&&\"currentOnly\"!==this.options.load&&r(this.getScriptPartFromCode(A)),\"currentOnly\"!==this.options.load&&r(this.getLanguagePartFromCode(A))):isString(A)&&r(this.formatLanguageCode(A)),e.forEach(A=>{a.indexOf(A)<0&&r(this.formatLanguageCode(A))}),a}}const suffixesOrder={zero:0,one:1,two:2,few:3,many:4,other:5},dummyRule={select:A=>1===A?\"one\":\"other\",resolvedOptions:()=>({pluralCategories:[\"one\",\"other\"]})};class PluralResolver{constructor(A){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};this.languageUtils=A,this.options=t,this.logger=baseLogger.create(\"pluralResolver\"),this.pluralRulesCache={}}addRule(A,t){this.rules[A]=t}clearCache(){this.pluralRulesCache={}}getRule(A){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const e=getCleanedCode(\"dev\"===A?\"en\":A),a=t.ordinal?\"ordinal\":\"cardinal\",r=JSON.stringify({cleanedCode:e,type:a});if(r in this.pluralRulesCache)return this.pluralRulesCache[r];let n;try{n=new Intl.PluralRules(e,{type:a})}catch(l){if(!Intl)return this.logger.error(\"No Intl support, please use an Intl polyfill!\"),dummyRule;if(!A.match(/-|_/))return dummyRule;const e=this.languageUtils.getLanguagePartFromCode(A);n=this.getRule(e,t)}return this.pluralRulesCache[r]=n,n}needsPlural(A){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},e=this.getRule(A,t);return e||(e=this.getRule(\"dev\",t)),(null==e?void 0:e.resolvedOptions().pluralCategories.length)>1}getPluralFormsOfKey(A,t){let e=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return this.getSuffixes(A,e).map(A=>`${t}${A}`)}getSuffixes(A){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},e=this.getRule(A,t);return e||(e=this.getRule(\"dev\",t)),e?e.resolvedOptions().pluralCategories.sort((A,t)=>suffixesOrder[A]-suffixesOrder[t]).map(A=>`${this.options.prepend}${t.ordinal?`ordinal${this.options.prepend}`:\"\"}${A}`):[]}getSuffix(A,t){let e=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const a=this.getRule(A,e);return a?`${this.options.prepend}${e.ordinal?`ordinal${this.options.prepend}`:\"\"}${a.select(t)}`:(this.logger.warn(`no plural rule found for: ${A}`),this.getSuffix(\"dev\",t,e))}}const deepFindWithDefaults=function(A,t,e){let a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:\".\",r=!(arguments.length>4&&void 0!==arguments[4])||arguments[4],n=getPathWithDefaults(A,t,e);return!n&&r&&isString(e)&&(n=deepFind(A,e,a),void 0===n&&(n=deepFind(t,e,a))),n},regexSafe=A=>A.replace(/\\$/g,\"$$$$\");class Interpolator{constructor(){var A;let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.logger=baseLogger.create(\"interpolator\"),this.options=t,this.format=(null==(A=null==t?void 0:t.interpolation)?void 0:A.format)||(A=>A),this.init(t)}init(){let A=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};A.interpolation||(A.interpolation={escapeValue:!0});const{escape:t,escapeValue:e,useRawValueToEscape:a,prefix:r,prefixEscaped:n,suffix:l,suffixEscaped:i,formatSeparator:o,unescapeSuffix:s,unescapePrefix:p,nestingPrefix:c,nestingPrefixEscaped:u,nestingSuffix:d,nestingSuffixEscaped:h,nestingOptionsSeparator:S,maxReplaces:f,alwaysFormat:m}=A.interpolation;this.escape=void 0!==t?t:escape,this.escapeValue=void 0===e||e,this.useRawValueToEscape=void 0!==a&&a,this.prefix=r?regexEscape(r):n||\"{{\",this.suffix=l?regexEscape(l):i||\"}}\",this.formatSeparator=o||\",\",this.unescapePrefix=s?\"\":p||\"-\",this.unescapeSuffix=this.unescapePrefix?\"\":s||\"\",this.nestingPrefix=c?regexEscape(c):u||regexEscape(\"$t(\"),this.nestingSuffix=d?regexEscape(d):h||regexEscape(\")\"),this.nestingOptionsSeparator=S||\",\",this.maxReplaces=f||1e3,this.alwaysFormat=void 0!==m&&m,this.resetRegExp()}reset(){this.options&&this.init(this.options)}resetRegExp(){const A=(A,t)=>(null==A?void 0:A.source)===t?(A.lastIndex=0,A):new RegExp(t,\"g\");this.regexp=A(this.regexp,`${this.prefix}(.+?)${this.suffix}`),this.regexpUnescape=A(this.regexpUnescape,`${this.prefix}${this.unescapePrefix}(.+?)${this.unescapeSuffix}${this.suffix}`),this.nestingRegexp=A(this.nestingRegexp,`${this.nestingPrefix}(.+?)${this.nestingSuffix}`)}interpolate(A,t,e,a){var r;let n,l,i;const o=this.options&&this.options.interpolation&&this.options.interpolation.defaultVariables||{},s=A=>{if(A.indexOf(this.formatSeparator)<0){const r=deepFindWithDefaults(t,o,A,this.options.keySeparator,this.options.ignoreJSONStructure);return this.alwaysFormat?this.format(r,void 0,e,{...a,...t,interpolationkey:A}):r}const r=A.split(this.formatSeparator),n=r.shift().trim(),l=r.join(this.formatSeparator).trim();return this.format(deepFindWithDefaults(t,o,n,this.options.keySeparator,this.options.ignoreJSONStructure),l,e,{...a,...t,interpolationkey:n})};this.resetRegExp();const p=(null==a?void 0:a.missingInterpolationHandler)||this.options.missingInterpolationHandler,c=void 0!==(null==(r=null==a?void 0:a.interpolation)?void 0:r.skipOnVariables)?a.interpolation.skipOnVariables:this.options.interpolation.skipOnVariables;return[{regex:this.regexpUnescape,safeValue:A=>regexSafe(A)},{regex:this.regexp,safeValue:A=>this.escapeValue?regexSafe(this.escape(A)):regexSafe(A)}].forEach(t=>{for(i=0;n=t.regex.exec(A);){const e=n[1].trim();if(l=s(e),void 0===l)if(\"function\"==typeof p){const t=p(A,n,a);l=isString(t)?t:\"\"}else if(a&&Object.prototype.hasOwnProperty.call(a,e))l=\"\";else{if(c){l=n[0];continue}this.logger.warn(`missed to pass in variable ${e} for interpolating ${A}`),l=\"\"}else isString(l)||this.useRawValueToEscape||(l=makeString(l));const r=t.safeValue(l);if(A=A.replace(n[0],r),c?(t.regex.lastIndex+=l.length,t.regex.lastIndex-=n[0].length):t.regex.lastIndex=0,i++,i>=this.maxReplaces)break}}),A}nest(A,t){let e,a,r,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const l=(A,t)=>{const e=this.nestingOptionsSeparator;if(A.indexOf(e)<0)return A;const a=A.split(new RegExp(`${e}[ ]*{`));let n=`{${a[1]}`;A=a[0],n=this.interpolate(n,r);const l=n.match(/'/g),i=n.match(/\"/g);(((null==l?void 0:l.length)??0)%2==0&&!i||i.length%2!=0)&&(n=n.replace(/'/g,'\"'));try{r=JSON.parse(n),t&&(r={...t,...r})}catch(o){return this.logger.warn(`failed parsing options string in nesting for key ${A}`,o),`${A}${e}${n}`}return r.defaultValue&&r.defaultValue.indexOf(this.prefix)>-1&&delete r.defaultValue,A};for(;e=this.nestingRegexp.exec(A);){let i=[];r={...n},r=r.replace&&!isString(r.replace)?r.replace:r,r.applyPostProcessor=!1,delete r.defaultValue;let o=!1;if(-1!==e[0].indexOf(this.formatSeparator)&&!/{.*}/.test(e[1])){const A=e[1].split(this.formatSeparator).map(A=>A.trim());e[1]=A.shift(),i=A,o=!0}if(a=t(l.call(this,e[1].trim(),r),r),a&&e[0]===A&&!isString(a))return a;isString(a)||(a=makeString(a)),a||(this.logger.warn(`missed to resolve ${e[1]} for nesting ${A}`),a=\"\"),o&&(a=i.reduce((A,t)=>this.format(A,t,n.lng,{...n,interpolationkey:e[1].trim()}),a.trim())),A=A.replace(e[0],a),this.regexp.lastIndex=0}return A}}const parseFormatStr=A=>{let t=A.toLowerCase().trim();const e={};if(A.indexOf(\"(\")>-1){const a=A.split(\"(\");t=a[0].toLowerCase().trim();const r=a[1].substring(0,a[1].length-1);if(\"currency\"===t&&r.indexOf(\":\")<0)e.currency||(e.currency=r.trim());else if(\"relativetime\"===t&&r.indexOf(\":\")<0)e.range||(e.range=r.trim());else{r.split(\";\").forEach(A=>{if(A){const[t,...a]=A.split(\":\"),r=a.join(\":\").trim().replace(/^'+|'+$/g,\"\"),n=t.trim();e[n]||(e[n]=r),\"false\"===r&&(e[n]=!1),\"true\"===r&&(e[n]=!0),isNaN(r)||(e[n]=parseInt(r,10))}})}}return{formatName:t,formatOptions:e}},createCachedFormatter=A=>{const t={};return(e,a,r)=>{let n=r;r&&r.interpolationkey&&r.formatParams&&r.formatParams[r.interpolationkey]&&r[r.interpolationkey]&&(n={...n,[r.interpolationkey]:void 0});const l=a+JSON.stringify(n);let i=t[l];return i||(i=A(getCleanedCode(a),r),t[l]=i),i(e)}};class Formatter{constructor(){let A=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.logger=baseLogger.create(\"formatter\"),this.options=A,this.formats={number:createCachedFormatter((A,t)=>{const e=new Intl.NumberFormat(A,{...t});return A=>e.format(A)}),currency:createCachedFormatter((A,t)=>{const e=new Intl.NumberFormat(A,{...t,style:\"currency\"});return A=>e.format(A)}),datetime:createCachedFormatter((A,t)=>{const e=new Intl.DateTimeFormat(A,{...t});return A=>e.format(A)}),relativetime:createCachedFormatter((A,t)=>{const e=new Intl.RelativeTimeFormat(A,{...t});return A=>e.format(A,t.range||\"day\")}),list:createCachedFormatter((A,t)=>{const e=new Intl.ListFormat(A,{...t});return A=>e.format(A)})},this.init(A)}init(A){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{interpolation:{}};this.formatSeparator=t.interpolation.formatSeparator||\",\"}add(A,t){this.formats[A.toLowerCase().trim()]=t}addCached(A,t){this.formats[A.toLowerCase().trim()]=createCachedFormatter(t)}format(A,t,e){let a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};const r=t.split(this.formatSeparator);if(r.length>1&&r[0].indexOf(\"(\")>1&&r[0].indexOf(\")\")<0&&r.find(A=>A.indexOf(\")\")>-1)){const A=r.findIndex(A=>A.indexOf(\")\")>-1);r[0]=[r[0],...r.splice(1,A)].join(this.formatSeparator)}return r.reduce((A,t)=>{var r;const{formatName:n,formatOptions:l}=parseFormatStr(t);if(this.formats[n]){let t=A;try{const i=(null==(r=null==a?void 0:a.formatParams)?void 0:r[a.interpolationkey])||{},o=i.locale||i.lng||a.locale||a.lng||e;t=this.formats[n](A,o,{...l,...a,...i})}catch(i){this.logger.warn(i)}return t}return this.logger.warn(`there was no format function for ${n}`),A},A)}}const removePending=(A,t)=>{void 0!==A.pending[t]&&(delete A.pending[t],A.pendingCount--)};class Connector extends EventEmitter{constructor(A,t,e){var a,r;let n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};super(),this.backend=A,this.store=t,this.services=e,this.languageUtils=e.languageUtils,this.options=n,this.logger=baseLogger.create(\"backendConnector\"),this.waitingReads=[],this.maxParallelReads=n.maxParallelReads||10,this.readingCalls=0,this.maxRetries=n.maxRetries>=0?n.maxRetries:5,this.retryTimeout=n.retryTimeout>=1?n.retryTimeout:350,this.state={},this.queue=[],null==(r=null==(a=this.backend)?void 0:a.init)||r.call(a,e,n.backend,n)}queueLoad(A,t,e,a){const r={},n={},l={},i={};return A.forEach(A=>{let a=!0;t.forEach(t=>{const l=`${A}|${t}`;!e.reload&&this.store.hasResourceBundle(A,t)?this.state[l]=2:this.state[l]<0||(1===this.state[l]?void 0===n[l]&&(n[l]=!0):(this.state[l]=1,a=!1,void 0===n[l]&&(n[l]=!0),void 0===r[l]&&(r[l]=!0),void 0===i[t]&&(i[t]=!0)))}),a||(l[A]=!0)}),(Object.keys(r).length||Object.keys(n).length)&&this.queue.push({pending:n,pendingCount:Object.keys(n).length,loaded:{},errors:[],callback:a}),{toLoad:Object.keys(r),pending:Object.keys(n),toLoadLanguages:Object.keys(l),toLoadNamespaces:Object.keys(i)}}loaded(A,t,e){const a=A.split(\"|\"),r=a[0],n=a[1];t&&this.emit(\"failedLoading\",r,n,t),!t&&e&&this.store.addResourceBundle(r,n,e,void 0,void 0,{skipCopy:!0}),this.state[A]=t?-1:2,t&&e&&(this.state[A]=0);const l={};this.queue.forEach(e=>{pushPath(e.loaded,[r],n),removePending(e,A),t&&e.errors.push(t),0!==e.pendingCount||e.done||(Object.keys(e.loaded).forEach(A=>{l[A]||(l[A]={});const t=e.loaded[A];t.length&&t.forEach(t=>{void 0===l[A][t]&&(l[A][t]=!0)})}),e.done=!0,e.errors.length?e.callback(e.errors):e.callback())}),this.emit(\"loaded\",l),this.queue=this.queue.filter(A=>!A.done)}read(A,t,e){let a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,r=arguments.length>4&&void 0!==arguments[4]?arguments[4]:this.retryTimeout,n=arguments.length>5?arguments[5]:void 0;if(!A.length)return n(null,{});if(this.readingCalls>=this.maxParallelReads)return void this.waitingReads.push({lng:A,ns:t,fcName:e,tried:a,wait:r,callback:n});this.readingCalls++;const l=(l,i)=>{if(this.readingCalls--,this.waitingReads.length>0){const A=this.waitingReads.shift();this.read(A.lng,A.ns,A.fcName,A.tried,A.wait,A.callback)}l&&i&&a<this.maxRetries?setTimeout(()=>{this.read.call(this,A,t,e,a+1,2*r,n)},r):n(l,i)},i=this.backend[e].bind(this.backend);if(2!==i.length)return i(A,t,l);try{const e=i(A,t);e&&\"function\"==typeof e.then?e.then(A=>l(null,A)).catch(l):l(null,e)}catch(o){l(o)}}prepareLoading(A,t){let e=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},a=arguments.length>3?arguments[3]:void 0;if(!this.backend)return this.logger.warn(\"No backend was added via i18next.use. Will not load resources.\"),a&&a();isString(A)&&(A=this.languageUtils.toResolveHierarchy(A)),isString(t)&&(t=[t]);const r=this.queueLoad(A,t,e,a);if(!r.toLoad.length)return r.pending.length||a(),null;r.toLoad.forEach(A=>{this.loadOne(A)})}load(A,t,e){this.prepareLoading(A,t,{},e)}reload(A,t,e){this.prepareLoading(A,t,{reload:!0},e)}loadOne(A){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:\"\";const e=A.split(\"|\"),a=e[0],r=e[1];this.read(a,r,\"read\",void 0,void 0,(e,n)=>{e&&this.logger.warn(`${t}loading namespace ${r} for language ${a} failed`,e),!e&&n&&this.logger.log(`${t}loaded namespace ${r} for language ${a}`,n),this.loaded(A,e,n)})}saveMissing(A,t,e,a,r){var n,l,i,o,s;let p=arguments.length>5&&void 0!==arguments[5]?arguments[5]:{},c=arguments.length>6&&void 0!==arguments[6]?arguments[6]:()=>{};if(!(null==(l=null==(n=this.services)?void 0:n.utils)?void 0:l.hasLoadedNamespace)||(null==(o=null==(i=this.services)?void 0:i.utils)?void 0:o.hasLoadedNamespace(t))){if(null!=e&&\"\"!==e){if(null==(s=this.backend)?void 0:s.create){const n={...p,isUpdate:r},l=this.backend.create.bind(this.backend);if(l.length<6)try{let r;r=5===l.length?l(A,t,e,a,n):l(A,t,e,a),r&&\"function\"==typeof r.then?r.then(A=>c(null,A)).catch(c):c(null,r)}catch(u){c(u)}else l(A,t,e,a,c,n)}A&&A[0]&&this.store.addResource(A[0],t,e,a)}}else this.logger.warn(`did not save key \"${e}\" as the namespace \"${t}\" was not yet loaded`,\"This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!\")}}const get=()=>({debug:!1,initAsync:!0,ns:[\"translation\"],defaultNS:[\"translation\"],fallbackLng:[\"dev\"],fallbackNS:!1,supportedLngs:!1,nonExplicitSupportedLngs:!1,load:\"all\",preload:!1,simplifyPluralSuffix:!0,keySeparator:\".\",nsSeparator:\":\",pluralSeparator:\"_\",contextSeparator:\"_\",partialBundledLanguages:!1,saveMissing:!1,updateMissing:!1,saveMissingTo:\"fallback\",saveMissingPlurals:!0,missingKeyHandler:!1,missingInterpolationHandler:!1,postProcess:!1,postProcessPassResolved:!1,returnNull:!1,returnEmptyString:!0,returnObjects:!1,joinArrays:!1,returnedObjectHandler:!1,parseMissingKeyHandler:!1,appendNamespaceToMissingKey:!1,appendNamespaceToCIMode:!1,overloadTranslationOptionHandler:A=>{let t={};if(\"object\"==typeof A[1]&&(t=A[1]),isString(A[1])&&(t.defaultValue=A[1]),isString(A[2])&&(t.tDescription=A[2]),\"object\"==typeof A[2]||\"object\"==typeof A[3]){const e=A[3]||A[2];Object.keys(e).forEach(A=>{t[A]=e[A]})}return t},interpolation:{escapeValue:!0,format:A=>A,prefix:\"{{\",suffix:\"}}\",formatSeparator:\",\",unescapePrefix:\"-\",nestingPrefix:\"$t(\",nestingSuffix:\")\",nestingOptionsSeparator:\",\",maxReplaces:1e3,skipOnVariables:!0}}),transformOptions=A=>{var t,e;return isString(A.ns)&&(A.ns=[A.ns]),isString(A.fallbackLng)&&(A.fallbackLng=[A.fallbackLng]),isString(A.fallbackNS)&&(A.fallbackNS=[A.fallbackNS]),(null==(e=null==(t=A.supportedLngs)?void 0:t.indexOf)?void 0:e.call(t,\"cimode\"))<0&&(A.supportedLngs=A.supportedLngs.concat([\"cimode\"])),\"boolean\"==typeof A.initImmediate&&(A.initAsync=A.initImmediate),A},noop$1=()=>{},bindMemberFunctions=A=>{Object.getOwnPropertyNames(Object.getPrototypeOf(A)).forEach(t=>{\"function\"==typeof A[t]&&(A[t]=A[t].bind(A))})};class I18n extends EventEmitter{constructor(){let A=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1?arguments[1]:void 0;if(super(),this.options=transformOptions(A),this.services={},this.logger=baseLogger,this.modules={external:[]},bindMemberFunctions(this),t&&!this.isInitialized&&!A.isClone){if(!this.options.initAsync)return this.init(A,t),this;setTimeout(()=>{this.init(A,t)},0)}}init(){var A=this;let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=arguments.length>1?arguments[1]:void 0;this.isInitializing=!0,\"function\"==typeof t&&(e=t,t={}),null==t.defaultNS&&t.ns&&(isString(t.ns)?t.defaultNS=t.ns:t.ns.indexOf(\"translation\")<0&&(t.defaultNS=t.ns[0]));const a=get();this.options={...a,...this.options,...transformOptions(t)},this.options.interpolation={...a.interpolation,...this.options.interpolation},void 0!==t.keySeparator&&(this.options.userDefinedKeySeparator=t.keySeparator),void 0!==t.nsSeparator&&(this.options.userDefinedNsSeparator=t.nsSeparator);const r=A=>A?\"function\"==typeof A?new A:A:null;if(!this.options.isClone){let t;this.modules.logger?baseLogger.init(r(this.modules.logger),this.options):baseLogger.init(null,this.options),t=this.modules.formatter?this.modules.formatter:Formatter;const e=new LanguageUtil(this.options);this.store=new ResourceStore(this.options.resources,this.options);const n=this.services;n.logger=baseLogger,n.resourceStore=this.store,n.languageUtils=e,n.pluralResolver=new PluralResolver(e,{prepend:this.options.pluralSeparator,simplifyPluralSuffix:this.options.simplifyPluralSuffix}),!t||this.options.interpolation.format&&this.options.interpolation.format!==a.interpolation.format||(n.formatter=r(t),n.formatter.init(n,this.options),this.options.interpolation.format=n.formatter.format.bind(n.formatter)),n.interpolator=new Interpolator(this.options),n.utils={hasLoadedNamespace:this.hasLoadedNamespace.bind(this)},n.backendConnector=new Connector(r(this.modules.backend),n.resourceStore,n,this.options),n.backendConnector.on(\"*\",function(t){for(var e=arguments.length,a=new Array(e>1?e-1:0),r=1;r<e;r++)a[r-1]=arguments[r];A.emit(t,...a)}),this.modules.languageDetector&&(n.languageDetector=r(this.modules.languageDetector),n.languageDetector.init&&n.languageDetector.init(n,this.options.detection,this.options)),this.modules.i18nFormat&&(n.i18nFormat=r(this.modules.i18nFormat),n.i18nFormat.init&&n.i18nFormat.init(this)),this.translator=new Translator(this.services,this.options),this.translator.on(\"*\",function(t){for(var e=arguments.length,a=new Array(e>1?e-1:0),r=1;r<e;r++)a[r-1]=arguments[r];A.emit(t,...a)}),this.modules.external.forEach(A=>{A.init&&A.init(this)})}if(this.format=this.options.interpolation.format,e||(e=noop$1),this.options.fallbackLng&&!this.services.languageDetector&&!this.options.lng){const A=this.services.languageUtils.getFallbackCodes(this.options.fallbackLng);A.length>0&&\"dev\"!==A[0]&&(this.options.lng=A[0])}this.services.languageDetector||this.options.lng||this.logger.warn(\"init: no languageDetector is used and no lng is defined\");[\"getResource\",\"hasResourceBundle\",\"getResourceBundle\",\"getDataByLanguage\"].forEach(t=>{this[t]=function(){return A.store[t](...arguments)}});[\"addResource\",\"addResources\",\"addResourceBundle\",\"removeResourceBundle\"].forEach(t=>{this[t]=function(){return A.store[t](...arguments),A}});const n=defer(),l=()=>{const A=(A,t)=>{this.isInitializing=!1,this.isInitialized&&!this.initializedStoreOnce&&this.logger.warn(\"init: i18next is already initialized. You should call init just once!\"),this.isInitialized=!0,this.options.isClone||this.logger.log(\"initialized\",this.options),this.emit(\"initialized\",this.options),n.resolve(t),e(A,t)};if(this.languages&&!this.isInitialized)return A(null,this.t.bind(this));this.changeLanguage(this.options.lng,A)};return this.options.resources||!this.options.initAsync?l():setTimeout(l,0),n}loadResources(A){var t,e;let a=arguments.length>1&&void 0!==arguments[1]?arguments[1]:noop$1;const r=isString(A)?A:this.language;if(\"function\"==typeof A&&(a=A),!this.options.resources||this.options.partialBundledLanguages){if(\"cimode\"===(null==r?void 0:r.toLowerCase())&&(!this.options.preload||0===this.options.preload.length))return a();const A=[],n=t=>{if(!t)return;if(\"cimode\"===t)return;this.services.languageUtils.toResolveHierarchy(t).forEach(t=>{\"cimode\"!==t&&A.indexOf(t)<0&&A.push(t)})};if(r)n(r);else{this.services.languageUtils.getFallbackCodes(this.options.fallbackLng).forEach(A=>n(A))}null==(e=null==(t=this.options.preload)?void 0:t.forEach)||e.call(t,A=>n(A)),this.services.backendConnector.load(A,this.options.ns,A=>{A||this.resolvedLanguage||!this.language||this.setResolvedLanguage(this.language),a(A)})}else a(null)}reloadResources(A,t,e){const a=defer();return\"function\"==typeof A&&(e=A,A=void 0),\"function\"==typeof t&&(e=t,t=void 0),A||(A=this.languages),t||(t=this.options.ns),e||(e=noop$1),this.services.backendConnector.reload(A,t,A=>{a.resolve(),e(A)}),a}use(A){if(!A)throw new Error(\"You are passing an undefined module! Please check the object you are passing to i18next.use()\");if(!A.type)throw new Error(\"You are passing a wrong module! Please check the object you are passing to i18next.use()\");return\"backend\"===A.type&&(this.modules.backend=A),(\"logger\"===A.type||A.log&&A.warn&&A.error)&&(this.modules.logger=A),\"languageDetector\"===A.type&&(this.modules.languageDetector=A),\"i18nFormat\"===A.type&&(this.modules.i18nFormat=A),\"postProcessor\"===A.type&&postProcessor.addPostProcessor(A),\"formatter\"===A.type&&(this.modules.formatter=A),\"3rdParty\"===A.type&&this.modules.external.push(A),this}setResolvedLanguage(A){if(A&&this.languages&&!([\"cimode\",\"dev\"].indexOf(A)>-1))for(let t=0;t<this.languages.length;t++){const A=this.languages[t];if(!([\"cimode\",\"dev\"].indexOf(A)>-1)&&this.store.hasLanguageSomeTranslations(A)){this.resolvedLanguage=A;break}}}changeLanguage(A,t){var e=this;this.isLanguageChangingTo=A;const a=defer();this.emit(\"languageChanging\",A);const r=A=>{this.language=A,this.languages=this.services.languageUtils.toResolveHierarchy(A),this.resolvedLanguage=void 0,this.setResolvedLanguage(A)},n=(A,n)=>{n?(r(n),this.translator.changeLanguage(n),this.isLanguageChangingTo=void 0,this.emit(\"languageChanged\",n),this.logger.log(\"languageChanged\",n)):this.isLanguageChangingTo=void 0,a.resolve(function(){return e.t(...arguments)}),t&&t(A,function(){return e.t(...arguments)})},l=t=>{var e,a;A||t||!this.services.languageDetector||(t=[]);const l=isString(t)?t:this.services.languageUtils.getBestMatchFromCodes(t);l&&(this.language||r(l),this.translator.language||this.translator.changeLanguage(l),null==(a=null==(e=this.services.languageDetector)?void 0:e.cacheUserLanguage)||a.call(e,l)),this.loadResources(l,A=>{n(A,l)})};return A||!this.services.languageDetector||this.services.languageDetector.async?!A&&this.services.languageDetector&&this.services.languageDetector.async?0===this.services.languageDetector.detect.length?this.services.languageDetector.detect().then(l):this.services.languageDetector.detect(l):l(A):l(this.services.languageDetector.detect()),a}getFixedT(A,t,e){var a=this;const r=function(A,t){let n;if(\"object\"!=typeof t){for(var l=arguments.length,i=new Array(l>2?l-2:0),o=2;o<l;o++)i[o-2]=arguments[o];n=a.options.overloadTranslationOptionHandler([A,t].concat(i))}else n={...t};n.lng=n.lng||r.lng,n.lngs=n.lngs||r.lngs,n.ns=n.ns||r.ns,\"\"!==n.keyPrefix&&(n.keyPrefix=n.keyPrefix||e||r.keyPrefix);const s=a.options.keySeparator||\".\";let p;return p=n.keyPrefix&&Array.isArray(A)?A.map(A=>`${n.keyPrefix}${s}${A}`):n.keyPrefix?`${n.keyPrefix}${s}${A}`:A,a.t(p,n)};return isString(A)?r.lng=A:r.lngs=A,r.ns=t,r.keyPrefix=e,r}t(){for(var A,t=arguments.length,e=new Array(t),a=0;a<t;a++)e[a]=arguments[a];return null==(A=this.translator)?void 0:A.translate(...e)}exists(){for(var A,t=arguments.length,e=new Array(t),a=0;a<t;a++)e[a]=arguments[a];return null==(A=this.translator)?void 0:A.exists(...e)}setDefaultNamespace(A){this.options.defaultNS=A}hasLoadedNamespace(A){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!this.isInitialized)return this.logger.warn(\"hasLoadedNamespace: i18next was not initialized\",this.languages),!1;if(!this.languages||!this.languages.length)return this.logger.warn(\"hasLoadedNamespace: i18n.languages were undefined or empty\",this.languages),!1;const e=t.lng||this.resolvedLanguage||this.languages[0],a=!!this.options&&this.options.fallbackLng,r=this.languages[this.languages.length-1];if(\"cimode\"===e.toLowerCase())return!0;const n=(A,t)=>{const e=this.services.backendConnector.state[`${A}|${t}`];return-1===e||0===e||2===e};if(t.precheck){const A=t.precheck(this,n);if(void 0!==A)return A}return!!this.hasResourceBundle(e,A)||(!(this.services.backendConnector.backend&&(!this.options.resources||this.options.partialBundledLanguages))||!(!n(e,A)||a&&!n(r,A)))}loadNamespaces(A,t){const e=defer();return this.options.ns?(isString(A)&&(A=[A]),A.forEach(A=>{this.options.ns.indexOf(A)<0&&this.options.ns.push(A)}),this.loadResources(A=>{e.resolve(),t&&t(A)}),e):(t&&t(),Promise.resolve())}loadLanguages(A,t){const e=defer();isString(A)&&(A=[A]);const a=this.options.preload||[],r=A.filter(A=>a.indexOf(A)<0&&this.services.languageUtils.isSupportedCode(A));return r.length?(this.options.preload=a.concat(r),this.loadResources(A=>{e.resolve(),t&&t(A)}),e):(t&&t(),Promise.resolve())}dir(A){var t,e;if(A||(A=this.resolvedLanguage||((null==(t=this.languages)?void 0:t.length)>0?this.languages[0]:this.language)),!A)return\"rtl\";const a=(null==(e=this.services)?void 0:e.languageUtils)||new LanguageUtil(get());return[\"ar\",\"shu\",\"sqr\",\"ssh\",\"xaa\",\"yhd\",\"yud\",\"aao\",\"abh\",\"abv\",\"acm\",\"acq\",\"acw\",\"acx\",\"acy\",\"adf\",\"ads\",\"aeb\",\"aec\",\"afb\",\"ajp\",\"apc\",\"apd\",\"arb\",\"arq\",\"ars\",\"ary\",\"arz\",\"auz\",\"avl\",\"ayh\",\"ayl\",\"ayn\",\"ayp\",\"bbz\",\"pga\",\"he\",\"iw\",\"ps\",\"pbt\",\"pbu\",\"pst\",\"prp\",\"prd\",\"ug\",\"ur\",\"ydd\",\"yds\",\"yih\",\"ji\",\"yi\",\"hbo\",\"men\",\"xmn\",\"fa\",\"jpr\",\"peo\",\"pes\",\"prs\",\"dv\",\"sam\",\"ckb\"].indexOf(a.getLanguagePartFromCode(A))>-1||A.toLowerCase().indexOf(\"-arab\")>1?\"rtl\":\"ltr\"}static createInstance(){return new I18n(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},arguments.length>1?arguments[1]:void 0)}cloneInstance(){let A=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:noop$1;const e=A.forkResourceStore;e&&delete A.forkResourceStore;const a={...this.options,...A,isClone:!0},r=new I18n(a);void 0===A.debug&&void 0===A.prefix||(r.logger=r.logger.clone(A));if([\"store\",\"services\",\"language\"].forEach(A=>{r[A]=this[A]}),r.services={...this.services},r.services.utils={hasLoadedNamespace:r.hasLoadedNamespace.bind(r)},e){const A=Object.keys(this.store.data).reduce((A,t)=>(A[t]={...this.store.data[t]},Object.keys(A[t]).reduce((e,a)=>(e[a]={...A[t][a]},e),{})),{});r.store=new ResourceStore(A,a),r.services.resourceStore=r.store}return r.translator=new Translator(r.services,a),r.translator.on(\"*\",function(A){for(var t=arguments.length,e=new Array(t>1?t-1:0),a=1;a<t;a++)e[a-1]=arguments[a];r.emit(A,...e)}),r.init(a,t),r.translator.options=a,r.translator.backendConnector.services.utils={hasLoadedNamespace:r.hasLoadedNamespace.bind(r)},r}toJSON(){return{options:this.options,store:this.store,language:this.language,languages:this.languages,resolvedLanguage:this.resolvedLanguage}}}const instance=I18n.createInstance();instance.createInstance=I18n.createInstance,instance.createInstance,instance.dir,instance.init,instance.loadResources,instance.reloadResources,instance.use,instance.changeLanguage,instance.getFixedT,instance.t,instance.exists,instance.setDefaultNamespace,instance.hasLoadedNamespace,instance.loadNamespaces,instance.loadLanguages;const locales=Object.assign({\"./locales/en.ts\":()=>__vitePreload(()=>import(\"./en-vU35wTjd.js\"),[]),\"./locales/ja.ts\":()=>__vitePreload(()=>import(\"./ja-Q5acyAjl.js\"),[]),\"./locales/ko.ts\":()=>__vitePreload(()=>import(\"./ko-CMKMFQrR.js\"),[]),\"./locales/zh-CN.ts\":()=>__vitePreload(()=>import(\"./zh-CN-BZhLE8JW.js\"),[]),\"./locales/zh-TW.ts\":()=>__vitePreload(()=>import(\"./zh-TW-DGtNjFz9.js\"),[])}),ensureResourceLoaded=async A=>{if(instance.hasResourceBundle(A,\"translation\"))return;let t=null;const e=locales[`./locales/${A}.ts`];if(e)try{t=(await e()).default}catch(a){}if(!t){const e=A.toLowerCase(),r=Object.keys(locales).find(A=>A.toLowerCase()===`./locales/${e}.ts`);if(r){const A=locales[r];try{t=(await A()).default}catch(a){}}}if(!t&&A.includes(\"-\")){const e=A.split(\"-\")[0],r=locales[`./locales/${e}.ts`];if(r)try{t=(await r()).default}catch(a){}}if(!t&&\"zh-CN\"!==A)try{const A=locales[\"./locales/zh-CN.ts\"];if(A){t=(await A()).default}}catch(a){}t&&instance.addResourceBundle(A,\"translation\",t,!0,!0)};instance.use(initReactI18next).init({resources:{},lng:getBrowserLang(),fallbackLng:\"zh-CN\",interpolation:{escapeValue:!1},react:{bindI18n:\"languageChanged added\",useSuspense:!1}});const initialLang=getBrowserLang();ensureResourceLoaded(initialLang).then(()=>{instance.language!==initialLang&&instance.changeLanguage(initialLang)}),instance.on(\"languageChanged\",async A=>{await ensureResourceLoaded(A)});\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\nconst toKebabCase=A=>A.replace(/([a-z0-9])([A-Z])/g,\"$1-$2\").toLowerCase(),mergeClasses=(...A)=>A.filter((A,t,e)=>Boolean(A)&&\"\"!==A.trim()&&e.indexOf(A)===t).join(\" \").trim();\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\nvar defaultAttributes={xmlns:\"http://www.w3.org/2000/svg\",width:24,height:24,viewBox:\"0 0 24 24\",fill:\"none\",stroke:\"currentColor\",strokeWidth:2,strokeLinecap:\"round\",strokeLinejoin:\"round\"};\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const Icon=reactExports.forwardRef(({color:A=\"currentColor\",size:t=24,strokeWidth:e=2,absoluteStrokeWidth:a,className:r=\"\",children:n,iconNode:l,...i},o)=>reactExports.createElement(\"svg\",{ref:o,...defaultAttributes,width:t,height:t,stroke:A,strokeWidth:a?24*Number(e)/Number(t):e,className:mergeClasses(\"lucide\",r),...i},[...l.map(([A,t])=>reactExports.createElement(A,t)),...Array.isArray(n)?n:[n]])),createLucideIcon=(A,t)=>{const e=reactExports.forwardRef(({className:e,...a},r)=>reactExports.createElement(Icon,{ref:r,iconNode:t,className:mergeClasses(`lucide-${toKebabCase(A)}`,e),...a}));return e.displayName=`${A}`,e},Check=createLucideIcon(\"Check\",[[\"path\",{d:\"M20 6 9 17l-5-5\",key:\"1gmf2c\"}]]),ChevronRight=createLucideIcon(\"ChevronRight\",[[\"path\",{d:\"m9 18 6-6-6-6\",key:\"mthhwq\"}]]),CircleCheckBig=createLucideIcon(\"CircleCheckBig\",[[\"path\",{d:\"M21.801 10A10 10 0 1 1 17 3.335\",key:\"yps3ct\"}],[\"path\",{d:\"m9 11 3 3L22 4\",key:\"1pflzl\"}]]),CircleCheck=createLucideIcon(\"CircleCheck\",[[\"circle\",{cx:\"12\",cy:\"12\",r:\"10\",key:\"1mglay\"}],[\"path\",{d:\"m9 12 2 2 4-4\",key:\"dzmm74\"}]]),CircleHelp=createLucideIcon(\"CircleHelp\",[[\"circle\",{cx:\"12\",cy:\"12\",r:\"10\",key:\"1mglay\"}],[\"path\",{d:\"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3\",key:\"1u773s\"}],[\"path\",{d:\"M12 17h.01\",key:\"p32p05\"}]]),CircleX=createLucideIcon(\"CircleX\",[[\"circle\",{cx:\"12\",cy:\"12\",r:\"10\",key:\"1mglay\"}],[\"path\",{d:\"m15 9-6 6\",key:\"1uzhvr\"}],[\"path\",{d:\"m9 9 6 6\",key:\"z0biqf\"}]]),Circle=createLucideIcon(\"Circle\",[[\"circle\",{cx:\"12\",cy:\"12\",r:\"10\",key:\"1mglay\"}]]),Info=createLucideIcon(\"Info\",[[\"circle\",{cx:\"12\",cy:\"12\",r:\"10\",key:\"1mglay\"}],[\"path\",{d:\"M12 16v-4\",key:\"1dtifu\"}],[\"path\",{d:\"M12 8h.01\",key:\"e9boi3\"}]]),Languages=createLucideIcon(\"Languages\",[[\"path\",{d:\"m5 8 6 6\",key:\"1wu5hv\"}],[\"path\",{d:\"m4 14 6-6 2-3\",key:\"1k1g8d\"}],[\"path\",{d:\"M2 5h12\",key:\"or177f\"}],[\"path\",{d:\"M7 2h1\",key:\"1t2jsx\"}],[\"path\",{d:\"m22 22-5-10-5 10\",key:\"don7ne\"}],[\"path\",{d:\"M14 18h6\",key:\"1m8k6r\"}]]),LoaderCircle=createLucideIcon(\"LoaderCircle\",[[\"path\",{d:\"M21 12a9 9 0 1 1-6.219-8.56\",key:\"13zald\"}]]),Lock=createLucideIcon(\"Lock\",[[\"rect\",{width:\"18\",height:\"11\",x:\"3\",y:\"11\",rx:\"2\",ry:\"2\",key:\"1w4ew1\"}],[\"path\",{d:\"M7 11V7a5 5 0 0 1 10 0v4\",key:\"fwvmzm\"}]]),Moon=createLucideIcon(\"Moon\",[[\"path\",{d:\"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z\",key:\"a7tn18\"}]]),OctagonX=createLucideIcon(\"OctagonX\",[[\"path\",{d:\"m15 9-6 6\",key:\"1uzhvr\"}],[\"path\",{d:\"M2.586 16.726A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2h6.624a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586z\",key:\"2d38gg\"}],[\"path\",{d:\"m9 9 6 6\",key:\"z0biqf\"}]]),Palette=createLucideIcon(\"Palette\",[[\"circle\",{cx:\"13.5\",cy:\"6.5\",r:\".5\",fill:\"currentColor\",key:\"1okk4w\"}],[\"circle\",{cx:\"17.5\",cy:\"10.5\",r:\".5\",fill:\"currentColor\",key:\"f64h9f\"}],[\"circle\",{cx:\"8.5\",cy:\"7.5\",r:\".5\",fill:\"currentColor\",key:\"fotxhn\"}],[\"circle\",{cx:\"6.5\",cy:\"12.5\",r:\".5\",fill:\"currentColor\",key:\"qy21gx\"}],[\"path\",{d:\"M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z\",key:\"12rzf8\"}]]),SunMoon=createLucideIcon(\"SunMoon\",[[\"path\",{d:\"M12 8a2.83 2.83 0 0 0 4 4 4 4 0 1 1-4-4\",key:\"1fu5g2\"}],[\"path\",{d:\"M12 2v2\",key:\"tus03m\"}],[\"path\",{d:\"M12 20v2\",key:\"1lh1kg\"}],[\"path\",{d:\"m4.9 4.9 1.4 1.4\",key:\"b9915j\"}],[\"path\",{d:\"m17.7 17.7 1.4 1.4\",key:\"qc3ed3\"}],[\"path\",{d:\"M2 12h2\",key:\"1t8f8n\"}],[\"path\",{d:\"M20 12h2\",key:\"1q8mjw\"}],[\"path\",{d:\"m6.3 17.7-1.4 1.4\",key:\"5gca6\"}],[\"path\",{d:\"m19.1 4.9-1.4 1.4\",key:\"wpu9u6\"}]]),Sun=createLucideIcon(\"Sun\",[[\"circle\",{cx:\"12\",cy:\"12\",r:\"4\",key:\"4exip2\"}],[\"path\",{d:\"M12 2v2\",key:\"tus03m\"}],[\"path\",{d:\"M12 20v2\",key:\"1lh1kg\"}],[\"path\",{d:\"m4.93 4.93 1.41 1.41\",key:\"149t6j\"}],[\"path\",{d:\"m17.66 17.66 1.41 1.41\",key:\"ptbguv\"}],[\"path\",{d:\"M2 12h2\",key:\"1t8f8n\"}],[\"path\",{d:\"M20 12h2\",key:\"1q8mjw\"}],[\"path\",{d:\"m6.34 17.66-1.41 1.41\",key:\"1m8zz5\"}],[\"path\",{d:\"m19.07 4.93-1.41 1.41\",key:\"1shlcs\"}]]),TriangleAlert=createLucideIcon(\"TriangleAlert\",[[\"path\",{d:\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\",key:\"wmoenq\"}],[\"path\",{d:\"M12 9v4\",key:\"juzpu7\"}],[\"path\",{d:\"M12 17h.01\",key:\"p32p05\"}]]),X=createLucideIcon(\"X\",[[\"path\",{d:\"M18 6 6 18\",key:\"1bl5f8\"}],[\"path\",{d:\"m6 6 12 12\",key:\"d8bk6v\"}]]);\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */function createContext2(A,t){const e=reactExports.createContext(t),a=A=>{const{children:t,...a}=A,r=reactExports.useMemo(()=>a,Object.values(a));return jsxRuntimeExports.jsx(e.Provider,{value:r,children:t})};return a.displayName=A+\"Provider\",[a,function(a){const r=reactExports.useContext(e);if(r)return r;if(void 0!==t)return t;throw new Error(`\\`${a}\\` must be used within \\`${A}\\``)}]}function createContextScope(A,t=[]){let e=[];const a=()=>{const t=e.map(A=>reactExports.createContext(A));return function(e){const a=(null==e?void 0:e[A])||t;return reactExports.useMemo(()=>({[`__scope${A}`]:{...e,[A]:a}}),[e,a])}};return a.scopeName=A,[function(t,a){const r=reactExports.createContext(a),n=e.length;e=[...e,a];const l=t=>{var e;const{scope:a,children:l,...i}=t,o=(null==(e=null==a?void 0:a[A])?void 0:e[n])||r,s=reactExports.useMemo(()=>i,Object.values(i));return jsxRuntimeExports.jsx(o.Provider,{value:s,children:l})};return l.displayName=t+\"Provider\",[l,function(e,l){var i;const o=(null==(i=null==l?void 0:l[A])?void 0:i[n])||r,s=reactExports.useContext(o);if(s)return s;if(void 0!==a)return a;throw new Error(`\\`${e}\\` must be used within \\`${t}\\``)}]},composeContextScopes(a,...t)]}function composeContextScopes(...A){const t=A[0];if(1===A.length)return t;const e=()=>{const e=A.map(A=>({useScope:A(),scopeName:A.scopeName}));return function(A){const a=e.reduce((t,{useScope:e,scopeName:a})=>({...t,...e(A)[`__scope${a}`]}),{});return reactExports.useMemo(()=>({[`__scope${t.scopeName}`]:a}),[a])}};return e.scopeName=t.scopeName,e}function setRef(A,t){if(\"function\"==typeof A)return A(t);null!=A&&(A.current=t)}function composeRefs(...A){return t=>{let e=!1;const a=A.map(A=>{const a=setRef(A,t);return e||\"function\"!=typeof a||(e=!0),a});if(e)return()=>{for(let t=0;t<a.length;t++){const e=a[t];\"function\"==typeof e?e():setRef(A[t],null)}}}}function useComposedRefs(...A){return reactExports.useCallback(composeRefs(...A),A)}function composeEventHandlers(A,t,{checkForDefaultPrevented:e=!0}={}){return function(a){if(null==A||A(a),!1===e||!a.defaultPrevented)return null==t?void 0:t(a)}}var useLayoutEffect2=(null==globalThis?void 0:globalThis.document)?reactExports.useLayoutEffect:()=>{},useReactId=React$1[\" useId \".trim().toString()]||(()=>{}),count$1=0;function useId(A){const[t,e]=reactExports.useState(useReactId());return useLayoutEffect2(()=>{e(A=>A??String(count$1++))},[A]),t?`radix-${t}`:\"\"}var useInsertionEffect=React$1[\" useInsertionEffect \".trim().toString()]||useLayoutEffect2;function useControllableState({prop:A,defaultProp:t,onChange:e=()=>{},caller:a}){const[r,n,l]=useUncontrolledState({defaultProp:t,onChange:e}),i=void 0!==A,o=i?A:r;{const t=reactExports.useRef(void 0!==A);reactExports.useEffect(()=>{const A=t.current;if(A!==i){}t.current=i},[i,a])}return[o,reactExports.useCallback(t=>{var e;if(i){const a=isFunction(t)?t(A):t;a!==A&&(null==(e=l.current)||e.call(l,a))}else n(t)},[i,A,n,l])]}function useUncontrolledState({defaultProp:A,onChange:t}){const[e,a]=reactExports.useState(A),r=reactExports.useRef(e),n=reactExports.useRef(t);return useInsertionEffect(()=>{n.current=t},[t]),reactExports.useEffect(()=>{var A;r.current!==e&&(null==(A=n.current)||A.call(n,e),r.current=e)},[e,r]),[e,a,n]}function isFunction(A){return\"function\"==typeof A}var reactDom={exports:{}},reactDom_production={},hasRequiredReactDom_production,hasRequiredReactDom;function requireReactDom_production(){if(hasRequiredReactDom_production)return reactDom_production;hasRequiredReactDom_production=1;var A=requireReact();function t(A){var t=\"https://react.dev/errors/\"+A;if(1<arguments.length){t+=\"?args[]=\"+encodeURIComponent(arguments[1]);for(var e=2;e<arguments.length;e++)t+=\"&args[]=\"+encodeURIComponent(arguments[e])}return\"Minified React error #\"+A+\"; visit \"+t+\" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\"}function e(){}var a={d:{f:e,r:function(){throw Error(t(522))},D:e,C:e,L:e,m:e,X:e,S:e,M:e},p:0,findDOMNode:null},r=Symbol.for(\"react.portal\");var n=A.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;function l(A,t){return\"font\"===A?\"\":\"string\"==typeof t?\"use-credentials\"===t?t:\"\":void 0}return reactDom_production.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=a,reactDom_production.createPortal=function(A,e){var a=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null;if(!e||1!==e.nodeType&&9!==e.nodeType&&11!==e.nodeType)throw Error(t(299));return function(A,t,e){var a=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null;return{$$typeof:r,key:null==a?null:\"\"+a,children:A,containerInfo:t,implementation:e}}(A,e,null,a)},reactDom_production.flushSync=function(A){var t=n.T,e=a.p;try{if(n.T=null,a.p=2,A)return A()}finally{n.T=t,a.p=e,a.d.f()}},reactDom_production.preconnect=function(A,t){\"string\"==typeof A&&(t?t=\"string\"==typeof(t=t.crossOrigin)?\"use-credentials\"===t?t:\"\":void 0:t=null,a.d.C(A,t))},reactDom_production.prefetchDNS=function(A){\"string\"==typeof A&&a.d.D(A)},reactDom_production.preinit=function(A,t){if(\"string\"==typeof A&&t&&\"string\"==typeof t.as){var e=t.as,r=l(e,t.crossOrigin),n=\"string\"==typeof t.integrity?t.integrity:void 0,i=\"string\"==typeof t.fetchPriority?t.fetchPriority:void 0;\"style\"===e?a.d.S(A,\"string\"==typeof t.precedence?t.precedence:void 0,{crossOrigin:r,integrity:n,fetchPriority:i}):\"script\"===e&&a.d.X(A,{crossOrigin:r,integrity:n,fetchPriority:i,nonce:\"string\"==typeof t.nonce?t.nonce:void 0})}},reactDom_production.preinitModule=function(A,t){if(\"string\"==typeof A)if(\"object\"==typeof t&&null!==t){if(null==t.as||\"script\"===t.as){var e=l(t.as,t.crossOrigin);a.d.M(A,{crossOrigin:e,integrity:\"string\"==typeof t.integrity?t.integrity:void 0,nonce:\"string\"==typeof t.nonce?t.nonce:void 0})}}else null==t&&a.d.M(A)},reactDom_production.preload=function(A,t){if(\"string\"==typeof A&&\"object\"==typeof t&&null!==t&&\"string\"==typeof t.as){var e=t.as,r=l(e,t.crossOrigin);a.d.L(A,e,{crossOrigin:r,integrity:\"string\"==typeof t.integrity?t.integrity:void 0,nonce:\"string\"==typeof t.nonce?t.nonce:void 0,type:\"string\"==typeof t.type?t.type:void 0,fetchPriority:\"string\"==typeof t.fetchPriority?t.fetchPriority:void 0,referrerPolicy:\"string\"==typeof t.referrerPolicy?t.referrerPolicy:void 0,imageSrcSet:\"string\"==typeof t.imageSrcSet?t.imageSrcSet:void 0,imageSizes:\"string\"==typeof t.imageSizes?t.imageSizes:void 0,media:\"string\"==typeof t.media?t.media:void 0})}},reactDom_production.preloadModule=function(A,t){if(\"string\"==typeof A)if(t){var e=l(t.as,t.crossOrigin);a.d.m(A,{as:\"string\"==typeof t.as&&\"script\"!==t.as?t.as:void 0,crossOrigin:e,integrity:\"string\"==typeof t.integrity?t.integrity:void 0})}else a.d.m(A)},reactDom_production.requestFormReset=function(A){a.d.r(A)},reactDom_production.unstable_batchedUpdates=function(A,t){return A(t)},reactDom_production.useFormState=function(A,t,e){return n.H.useFormState(A,t,e)},reactDom_production.useFormStatus=function(){return n.H.useHostTransitionStatus()},reactDom_production.version=\"19.2.3\",reactDom_production}function requireReactDom(){if(hasRequiredReactDom)return reactDom.exports;return hasRequiredReactDom=1,function A(){if(\"undefined\"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&\"function\"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE)try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(A)}catch(t){}}(),reactDom.exports=requireReactDom_production(),reactDom.exports}var reactDomExports=requireReactDom();const ReactDOM$1=getDefaultExportFromCjs(reactDomExports);function createSlot$1(A){const t=createSlotClone$1(A),e=reactExports.forwardRef((A,e)=>{const{children:a,...r}=A,n=reactExports.Children.toArray(a),l=n.find(isSlottable$1);if(l){const A=l.props.children,a=n.map(t=>t===l?reactExports.Children.count(A)>1?reactExports.Children.only(null):reactExports.isValidElement(A)?A.props.children:null:t);return jsxRuntimeExports.jsx(t,{...r,ref:e,children:reactExports.isValidElement(A)?reactExports.cloneElement(A,void 0,a):null})}return jsxRuntimeExports.jsx(t,{...r,ref:e,children:a})});return e.displayName=`${A}.Slot`,e}function createSlotClone$1(A){const t=reactExports.forwardRef((A,t)=>{const{children:e,...a}=A;if(reactExports.isValidElement(e)){const A=getElementRef$2(e),r=mergeProps$1(a,e.props);return e.type!==reactExports.Fragment&&(r.ref=t?composeRefs(t,A):A),reactExports.cloneElement(e,r)}return reactExports.Children.count(e)>1?reactExports.Children.only(null):null});return t.displayName=`${A}.SlotClone`,t}var SLOTTABLE_IDENTIFIER$1=Symbol(\"radix.slottable\");function createSlottable(A){const t=({children:A})=>jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment,{children:A});return t.displayName=`${A}.Slottable`,t.__radixId=SLOTTABLE_IDENTIFIER$1,t}function isSlottable$1(A){return reactExports.isValidElement(A)&&\"function\"==typeof A.type&&\"__radixId\"in A.type&&A.type.__radixId===SLOTTABLE_IDENTIFIER$1}function mergeProps$1(A,t){const e={...t};for(const a in t){const r=A[a],n=t[a];/^on[A-Z]/.test(a)?r&&n?e[a]=(...A)=>{const t=n(...A);return r(...A),t}:r&&(e[a]=r):\"style\"===a?e[a]={...r,...n}:\"className\"===a&&(e[a]=[r,n].filter(Boolean).join(\" \"))}return{...A,...e}}function getElementRef$2(A){var t,e;let a=null==(t=Object.getOwnPropertyDescriptor(A.props,\"ref\"))?void 0:t.get,r=a&&\"isReactWarning\"in a&&a.isReactWarning;return r?A.ref:(a=null==(e=Object.getOwnPropertyDescriptor(A,\"ref\"))?void 0:e.get,r=a&&\"isReactWarning\"in a&&a.isReactWarning,r?A.props.ref:A.props.ref||A.ref)}var NODES=[\"a\",\"button\",\"div\",\"form\",\"h2\",\"h3\",\"img\",\"input\",\"label\",\"li\",\"nav\",\"ol\",\"p\",\"select\",\"span\",\"svg\",\"ul\"],Primitive=NODES.reduce((A,t)=>{const e=createSlot$1(`Primitive.${t}`),a=reactExports.forwardRef((A,a)=>{const{asChild:r,...n}=A,l=r?e:t;return\"undefined\"!=typeof window&&(window[Symbol.for(\"radix-ui\")]=!0),jsxRuntimeExports.jsx(l,{...n,ref:a})});return a.displayName=`Primitive.${t}`,{...A,[t]:a}},{});function dispatchDiscreteCustomEvent(A,t){A&&reactDomExports.flushSync(()=>A.dispatchEvent(t))}function useCallbackRef$1(A){const t=reactExports.useRef(A);return reactExports.useEffect(()=>{t.current=A}),reactExports.useMemo(()=>(...A)=>{var e;return null==(e=t.current)?void 0:e.call(t,...A)},[])}function useEscapeKeydown(A,t=(null==globalThis?void 0:globalThis.document)){const e=useCallbackRef$1(A);reactExports.useEffect(()=>{const A=A=>{\"Escape\"===A.key&&e(A)};return t.addEventListener(\"keydown\",A,{capture:!0}),()=>t.removeEventListener(\"keydown\",A,{capture:!0})},[e,t])}var DISMISSABLE_LAYER_NAME=\"DismissableLayer\",CONTEXT_UPDATE=\"dismissableLayer.update\",POINTER_DOWN_OUTSIDE=\"dismissableLayer.pointerDownOutside\",FOCUS_OUTSIDE=\"dismissableLayer.focusOutside\",originalBodyPointerEvents,DismissableLayerContext=reactExports.createContext({layers:new Set,layersWithOutsidePointerEventsDisabled:new Set,branches:new Set}),DismissableLayer=reactExports.forwardRef((A,t)=>{const{disableOutsidePointerEvents:e=!1,onEscapeKeyDown:a,onPointerDownOutside:r,onFocusOutside:n,onInteractOutside:l,onDismiss:i,...o}=A,s=reactExports.useContext(DismissableLayerContext),[p,c]=reactExports.useState(null),u=(null==p?void 0:p.ownerDocument)??(null==globalThis?void 0:globalThis.document),[,d]=reactExports.useState({}),h=useComposedRefs(t,A=>c(A)),S=Array.from(s.layers),[f]=[...s.layersWithOutsidePointerEventsDisabled].slice(-1),m=S.indexOf(f),L=p?S.indexOf(p):-1,W=s.layersWithOutsidePointerEventsDisabled.size>0,g=L>=m,y=usePointerDownOutside(A=>{const t=A.target,e=[...s.branches].some(A=>A.contains(t));g&&!e&&(null==r||r(A),null==l||l(A),A.defaultPrevented||null==i||i())},u),v=useFocusOutside(A=>{const t=A.target;[...s.branches].some(A=>A.contains(t))||(null==n||n(A),null==l||l(A),A.defaultPrevented||null==i||i())},u);return useEscapeKeydown(A=>{L===s.layers.size-1&&(null==a||a(A),!A.defaultPrevented&&i&&(A.preventDefault(),i()))},u),reactExports.useEffect(()=>{if(p)return e&&(0===s.layersWithOutsidePointerEventsDisabled.size&&(originalBodyPointerEvents=u.body.style.pointerEvents,u.body.style.pointerEvents=\"none\"),s.layersWithOutsidePointerEventsDisabled.add(p)),s.layers.add(p),dispatchUpdate(),()=>{e&&1===s.layersWithOutsidePointerEventsDisabled.size&&(u.body.style.pointerEvents=originalBodyPointerEvents)}},[p,u,e,s]),reactExports.useEffect(()=>()=>{p&&(s.layers.delete(p),s.layersWithOutsidePointerEventsDisabled.delete(p),dispatchUpdate())},[p,s]),reactExports.useEffect(()=>{const A=()=>d({});return document.addEventListener(CONTEXT_UPDATE,A),()=>document.removeEventListener(CONTEXT_UPDATE,A)},[]),jsxRuntimeExports.jsx(Primitive.div,{...o,ref:h,style:{pointerEvents:W?g?\"auto\":\"none\":void 0,...A.style},onFocusCapture:composeEventHandlers(A.onFocusCapture,v.onFocusCapture),onBlurCapture:composeEventHandlers(A.onBlurCapture,v.onBlurCapture),onPointerDownCapture:composeEventHandlers(A.onPointerDownCapture,y.onPointerDownCapture)})});DismissableLayer.displayName=DISMISSABLE_LAYER_NAME;var BRANCH_NAME=\"DismissableLayerBranch\",DismissableLayerBranch=reactExports.forwardRef((A,t)=>{const e=reactExports.useContext(DismissableLayerContext),a=reactExports.useRef(null),r=useComposedRefs(t,a);return reactExports.useEffect(()=>{const A=a.current;if(A)return e.branches.add(A),()=>{e.branches.delete(A)}},[e.branches]),jsxRuntimeExports.jsx(Primitive.div,{...A,ref:r})});function usePointerDownOutside(A,t=(null==globalThis?void 0:globalThis.document)){const e=useCallbackRef$1(A),a=reactExports.useRef(!1),r=reactExports.useRef(()=>{});return reactExports.useEffect(()=>{const A=A=>{if(A.target&&!a.current){let a=function(){handleAndDispatchCustomEvent(POINTER_DOWN_OUTSIDE,e,n,{discrete:!0})};const n={originalEvent:A};\"touch\"===A.pointerType?(t.removeEventListener(\"click\",r.current),r.current=a,t.addEventListener(\"click\",r.current,{once:!0})):a()}else t.removeEventListener(\"click\",r.current);a.current=!1},n=window.setTimeout(()=>{t.addEventListener(\"pointerdown\",A)},0);return()=>{window.clearTimeout(n),t.removeEventListener(\"pointerdown\",A),t.removeEventListener(\"click\",r.current)}},[t,e]),{onPointerDownCapture:()=>a.current=!0}}function useFocusOutside(A,t=(null==globalThis?void 0:globalThis.document)){const e=useCallbackRef$1(A),a=reactExports.useRef(!1);return reactExports.useEffect(()=>{const A=A=>{if(A.target&&!a.current){handleAndDispatchCustomEvent(FOCUS_OUTSIDE,e,{originalEvent:A},{discrete:!1})}};return t.addEventListener(\"focusin\",A),()=>t.removeEventListener(\"focusin\",A)},[t,e]),{onFocusCapture:()=>a.current=!0,onBlurCapture:()=>a.current=!1}}function dispatchUpdate(){const A=new CustomEvent(CONTEXT_UPDATE);document.dispatchEvent(A)}function handleAndDispatchCustomEvent(A,t,e,{discrete:a}){const r=e.originalEvent.target,n=new CustomEvent(A,{bubbles:!1,cancelable:!0,detail:e});t&&r.addEventListener(A,t,{once:!0}),a?dispatchDiscreteCustomEvent(r,n):r.dispatchEvent(n)}DismissableLayerBranch.displayName=BRANCH_NAME;var AUTOFOCUS_ON_MOUNT=\"focusScope.autoFocusOnMount\",AUTOFOCUS_ON_UNMOUNT=\"focusScope.autoFocusOnUnmount\",EVENT_OPTIONS$1={bubbles:!1,cancelable:!0},FOCUS_SCOPE_NAME=\"FocusScope\",FocusScope=reactExports.forwardRef((A,t)=>{const{loop:e=!1,trapped:a=!1,onMountAutoFocus:r,onUnmountAutoFocus:n,...l}=A,[i,o]=reactExports.useState(null),s=useCallbackRef$1(r),p=useCallbackRef$1(n),c=reactExports.useRef(null),u=useComposedRefs(t,A=>o(A)),d=reactExports.useRef({paused:!1,pause(){this.paused=!0},resume(){this.paused=!1}}).current;reactExports.useEffect(()=>{if(a){let A=function(A){if(d.paused||!i)return;const t=A.target;i.contains(t)?c.current=t:focus(c.current,{select:!0})},t=function(A){if(d.paused||!i)return;const t=A.relatedTarget;null!==t&&(i.contains(t)||focus(c.current,{select:!0}))},e=function(A){if(document.activeElement===document.body)for(const t of A)t.removedNodes.length>0&&focus(i)};document.addEventListener(\"focusin\",A),document.addEventListener(\"focusout\",t);const a=new MutationObserver(e);return i&&a.observe(i,{childList:!0,subtree:!0}),()=>{document.removeEventListener(\"focusin\",A),document.removeEventListener(\"focusout\",t),a.disconnect()}}},[a,i,d.paused]),reactExports.useEffect(()=>{if(i){focusScopesStack.add(d);const A=document.activeElement;if(!i.contains(A)){const t=new CustomEvent(AUTOFOCUS_ON_MOUNT,EVENT_OPTIONS$1);i.addEventListener(AUTOFOCUS_ON_MOUNT,s),i.dispatchEvent(t),t.defaultPrevented||(focusFirst$2(removeLinks(getTabbableCandidates(i)),{select:!0}),document.activeElement===A&&focus(i))}return()=>{i.removeEventListener(AUTOFOCUS_ON_MOUNT,s),setTimeout(()=>{const t=new CustomEvent(AUTOFOCUS_ON_UNMOUNT,EVENT_OPTIONS$1);i.addEventListener(AUTOFOCUS_ON_UNMOUNT,p),i.dispatchEvent(t),t.defaultPrevented||focus(A??document.body,{select:!0}),i.removeEventListener(AUTOFOCUS_ON_UNMOUNT,p),focusScopesStack.remove(d)},0)}}},[i,s,p,d]);const h=reactExports.useCallback(A=>{if(!e&&!a)return;if(d.paused)return;const t=\"Tab\"===A.key&&!A.altKey&&!A.ctrlKey&&!A.metaKey,r=document.activeElement;if(t&&r){const t=A.currentTarget,[a,n]=getTabbableEdges(t);a&&n?A.shiftKey||r!==n?A.shiftKey&&r===a&&(A.preventDefault(),e&&focus(n,{select:!0})):(A.preventDefault(),e&&focus(a,{select:!0})):r===t&&A.preventDefault()}},[e,a,d.paused]);return jsxRuntimeExports.jsx(Primitive.div,{tabIndex:-1,...l,ref:u,onKeyDown:h})});function focusFirst$2(A,{select:t=!1}={}){const e=document.activeElement;for(const a of A)if(focus(a,{select:t}),document.activeElement!==e)return}function getTabbableEdges(A){const t=getTabbableCandidates(A);return[findVisible(t,A),findVisible(t.reverse(),A)]}function getTabbableCandidates(A){const t=[],e=document.createTreeWalker(A,NodeFilter.SHOW_ELEMENT,{acceptNode:A=>{const t=\"INPUT\"===A.tagName&&\"hidden\"===A.type;return A.disabled||A.hidden||t?NodeFilter.FILTER_SKIP:A.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});for(;e.nextNode();)t.push(e.currentNode);return t}function findVisible(A,t){for(const e of A)if(!isHidden(e,{upTo:t}))return e}function isHidden(A,{upTo:t}){if(\"hidden\"===getComputedStyle(A).visibility)return!0;for(;A;){if(void 0!==t&&A===t)return!1;if(\"none\"===getComputedStyle(A).display)return!0;A=A.parentElement}return!1}function isSelectableInput(A){return A instanceof HTMLInputElement&&\"select\"in A}function focus(A,{select:t=!1}={}){if(A&&A.focus){const e=document.activeElement;A.focus({preventScroll:!0}),A!==e&&isSelectableInput(A)&&t&&A.select()}}FocusScope.displayName=FOCUS_SCOPE_NAME;var focusScopesStack=createFocusScopesStack();function createFocusScopesStack(){let A=[];return{add(t){const e=A[0];t!==e&&(null==e||e.pause()),A=arrayRemove(A,t),A.unshift(t)},remove(t){var e;A=arrayRemove(A,t),null==(e=A[0])||e.resume()}}}function arrayRemove(A,t){const e=[...A],a=e.indexOf(t);return-1!==a&&e.splice(a,1),e}function removeLinks(A){return A.filter(A=>\"A\"!==A.tagName)}var PORTAL_NAME$4=\"Portal\",Portal$2=reactExports.forwardRef((A,t)=>{var e;const{container:a,...r}=A,[n,l]=reactExports.useState(!1);useLayoutEffect2(()=>l(!0),[]);const i=a||n&&(null==(e=null==globalThis?void 0:globalThis.document)?void 0:e.body);return i?ReactDOM$1.createPortal(jsxRuntimeExports.jsx(Primitive.div,{...r,ref:t}),i):null});function useStateMachine(A,t){return reactExports.useReducer((A,e)=>t[A][e]??A,A)}Portal$2.displayName=PORTAL_NAME$4;var Presence=A=>{const{present:t,children:e}=A,a=usePresence(t),r=\"function\"==typeof e?e({present:a.isPresent}):reactExports.Children.only(e),n=useComposedRefs(a.ref,getElementRef$1(r));return\"function\"==typeof e||a.isPresent?reactExports.cloneElement(r,{ref:n}):null};function usePresence(A){const[t,e]=reactExports.useState(),a=reactExports.useRef(null),r=reactExports.useRef(A),n=reactExports.useRef(\"none\"),l=A?\"mounted\":\"unmounted\",[i,o]=useStateMachine(l,{mounted:{UNMOUNT:\"unmounted\",ANIMATION_OUT:\"unmountSuspended\"},unmountSuspended:{MOUNT:\"mounted\",ANIMATION_END:\"unmounted\"},unmounted:{MOUNT:\"mounted\"}});return reactExports.useEffect(()=>{const A=getAnimationName(a.current);n.current=\"mounted\"===i?A:\"none\"},[i]),useLayoutEffect2(()=>{const t=a.current,e=r.current;if(e!==A){const a=n.current,l=getAnimationName(t);if(A)o(\"MOUNT\");else if(\"none\"===l||\"none\"===(null==t?void 0:t.display))o(\"UNMOUNT\");else{o(e&&a!==l?\"ANIMATION_OUT\":\"UNMOUNT\")}r.current=A}},[A,o]),useLayoutEffect2(()=>{if(t){let A;const e=t.ownerDocument.defaultView??window,l=n=>{const l=getAnimationName(a.current).includes(CSS.escape(n.animationName));if(n.target===t&&l&&(o(\"ANIMATION_END\"),!r.current)){const a=t.style.animationFillMode;t.style.animationFillMode=\"forwards\",A=e.setTimeout(()=>{\"forwards\"===t.style.animationFillMode&&(t.style.animationFillMode=a)})}},i=A=>{A.target===t&&(n.current=getAnimationName(a.current))};return t.addEventListener(\"animationstart\",i),t.addEventListener(\"animationcancel\",l),t.addEventListener(\"animationend\",l),()=>{e.clearTimeout(A),t.removeEventListener(\"animationstart\",i),t.removeEventListener(\"animationcancel\",l),t.removeEventListener(\"animationend\",l)}}o(\"ANIMATION_END\")},[t,o]),{isPresent:[\"mounted\",\"unmountSuspended\"].includes(i),ref:reactExports.useCallback(A=>{a.current=A?getComputedStyle(A):null,e(A)},[])}}function getAnimationName(A){return(null==A?void 0:A.animationName)||\"none\"}function getElementRef$1(A){var t,e;let a=null==(t=Object.getOwnPropertyDescriptor(A.props,\"ref\"))?void 0:t.get,r=a&&\"isReactWarning\"in a&&a.isReactWarning;return r?A.ref:(a=null==(e=Object.getOwnPropertyDescriptor(A,\"ref\"))?void 0:e.get,r=a&&\"isReactWarning\"in a&&a.isReactWarning,r?A.props.ref:A.props.ref||A.ref)}Presence.displayName=\"Presence\";var count=0;function useFocusGuards(){reactExports.useEffect(()=>{const A=document.querySelectorAll(\"[data-radix-focus-guard]\");return document.body.insertAdjacentElement(\"afterbegin\",A[0]??createFocusGuard()),document.body.insertAdjacentElement(\"beforeend\",A[1]??createFocusGuard()),count++,()=>{1===count&&document.querySelectorAll(\"[data-radix-focus-guard]\").forEach(A=>A.remove()),count--}},[])}function createFocusGuard(){const A=document.createElement(\"span\");return A.setAttribute(\"data-radix-focus-guard\",\"\"),A.tabIndex=0,A.style.outline=\"none\",A.style.opacity=\"0\",A.style.position=\"fixed\",A.style.pointerEvents=\"none\",A}var __assign$1=function(){return __assign$1=Object.assign||function(A){for(var t,e=1,a=arguments.length;e<a;e++)for(var r in t=arguments[e])Object.prototype.hasOwnProperty.call(t,r)&&(A[r]=t[r]);return A},__assign$1.apply(this,arguments)};function __rest(A,t){var e={};for(var a in A)Object.prototype.hasOwnProperty.call(A,a)&&t.indexOf(a)<0&&(e[a]=A[a]);if(null!=A&&\"function\"==typeof Object.getOwnPropertySymbols){var r=0;for(a=Object.getOwnPropertySymbols(A);r<a.length;r++)t.indexOf(a[r])<0&&Object.prototype.propertyIsEnumerable.call(A,a[r])&&(e[a[r]]=A[a[r]])}return e}function __spreadArray(A,t,e){if(e||2===arguments.length)for(var a,r=0,n=t.length;r<n;r++)!a&&r in t||(a||(a=Array.prototype.slice.call(t,0,r)),a[r]=t[r]);return A.concat(a||Array.prototype.slice.call(t))}\"function\"==typeof SuppressedError&&SuppressedError;var zeroRightClassName=\"right-scroll-bar-position\",fullWidthClassName=\"width-before-scroll-bar\",noScrollbarsClassName=\"with-scroll-bars-hidden\",removedBarSizeVariable=\"--removed-body-scroll-bar-size\";function assignRef(A,t){return\"function\"==typeof A?A(t):A&&(A.current=t),A}function useCallbackRef(A,t){var e=reactExports.useState(function(){return{value:A,callback:t,facade:{get current(){return e.value},set current(A){var t=e.value;t!==A&&(e.value=A,e.callback(A,t))}}}})[0];return e.callback=t,e.facade}var useIsomorphicLayoutEffect=\"undefined\"!=typeof window?reactExports.useLayoutEffect:reactExports.useEffect,currentValues=new WeakMap;function useMergeRefs(A,t){var e=useCallbackRef(null,function(t){return A.forEach(function(A){return assignRef(A,t)})});return useIsomorphicLayoutEffect(function(){var t=currentValues.get(e);if(t){var a=new Set(t),r=new Set(A),n=e.current;a.forEach(function(A){r.has(A)||assignRef(A,null)}),r.forEach(function(A){a.has(A)||assignRef(A,n)})}currentValues.set(e,A)},[A]),e}function ItoI(A){return A}function innerCreateMedium(A,t){void 0===t&&(t=ItoI);var e=[],a=!1;return{read:function(){if(a)throw new Error(\"Sidecar: could not `read` from an `assigned` medium. `read` could be used only with `useMedium`.\");return e.length?e[e.length-1]:A},useMedium:function(A){var r=t(A,a);return e.push(r),function(){e=e.filter(function(A){return A!==r})}},assignSyncMedium:function(A){for(a=!0;e.length;){var t=e;e=[],t.forEach(A)}e={push:function(t){return A(t)},filter:function(){return e}}},assignMedium:function(A){a=!0;var t=[];if(e.length){var r=e;e=[],r.forEach(A),t=e}var n=function(){var e=t;t=[],e.forEach(A)},l=function(){return Promise.resolve().then(n)};l(),e={push:function(A){t.push(A),l()},filter:function(A){return t=t.filter(A),e}}}}}function createSidecarMedium(A){void 0===A&&(A={});var t=innerCreateMedium(null);return t.options=__assign$1({async:!0,ssr:!1},A),t}var SideCar$1=function(A){var t=A.sideCar,e=__rest(A,[\"sideCar\"]);if(!t)throw new Error(\"Sidecar: please provide `sideCar` property to import the right car\");var a=t.read();if(!a)throw new Error(\"Sidecar medium not found\");return reactExports.createElement(a,__assign$1({},e))};function exportSidecar(A,t){return A.useMedium(t),SideCar$1}SideCar$1.isSideCarExport=!0;var effectCar=createSidecarMedium(),nothing=function(){},RemoveScroll=reactExports.forwardRef(function(A,t){var e=reactExports.useRef(null),a=reactExports.useState({onScrollCapture:nothing,onWheelCapture:nothing,onTouchMoveCapture:nothing}),r=a[0],n=a[1],l=A.forwardProps,i=A.children,o=A.className,s=A.removeScrollBar,p=A.enabled,c=A.shards,u=A.sideCar,d=A.noRelative,h=A.noIsolation,S=A.inert,f=A.allowPinchZoom,m=A.as,L=void 0===m?\"div\":m,W=A.gapMode,g=__rest(A,[\"forwardProps\",\"children\",\"className\",\"removeScrollBar\",\"enabled\",\"shards\",\"sideCar\",\"noRelative\",\"noIsolation\",\"inert\",\"allowPinchZoom\",\"as\",\"gapMode\"]),y=u,v=useMergeRefs([e,t]),b=__assign$1(__assign$1({},g),r);return reactExports.createElement(reactExports.Fragment,null,p&&reactExports.createElement(y,{sideCar:effectCar,removeScrollBar:s,shards:c,noRelative:d,noIsolation:h,inert:S,setCallbacks:n,allowPinchZoom:!!f,lockRef:e,gapMode:W}),l?reactExports.cloneElement(reactExports.Children.only(i),__assign$1(__assign$1({},b),{ref:v})):reactExports.createElement(L,__assign$1({},b,{className:o,ref:v}),i))});RemoveScroll.defaultProps={enabled:!0,removeScrollBar:!0,inert:!1},RemoveScroll.classNames={fullWidth:fullWidthClassName,zeroRight:zeroRightClassName};var getNonce=function(){if(\"undefined\"!=typeof __webpack_nonce__)return __webpack_nonce__};function makeStyleTag(){if(!document)return null;var A=document.createElement(\"style\");A.type=\"text/css\";var t=getNonce();return t&&A.setAttribute(\"nonce\",t),A}function injectStyles(A,t){A.styleSheet?A.styleSheet.cssText=t:A.appendChild(document.createTextNode(t))}function insertStyleTag(A){(document.head||document.getElementsByTagName(\"head\")[0]).appendChild(A)}var stylesheetSingleton=function(){var A=0,t=null;return{add:function(e){0==A&&(t=makeStyleTag())&&(injectStyles(t,e),insertStyleTag(t)),A++},remove:function(){! --A&&t&&(t.parentNode&&t.parentNode.removeChild(t),t=null)}}},styleHookSingleton=function(){var A=stylesheetSingleton();return function(t,e){reactExports.useEffect(function(){return A.add(t),function(){A.remove()}},[t&&e])}},styleSingleton=function(){var A=styleHookSingleton();return function(t){var e=t.styles,a=t.dynamic;return A(e,a),null}},zeroGap={left:0,top:0,right:0,gap:0},parse=function(A){return parseInt(A||\"\",10)||0},getOffset=function(A){var t=window.getComputedStyle(document.body),e=t[\"padding\"===A?\"paddingLeft\":\"marginLeft\"],a=t[\"padding\"===A?\"paddingTop\":\"marginTop\"],r=t[\"padding\"===A?\"paddingRight\":\"marginRight\"];return[parse(e),parse(a),parse(r)]},getGapWidth=function(A){if(void 0===A&&(A=\"margin\"),\"undefined\"==typeof window)return zeroGap;var t=getOffset(A),e=document.documentElement.clientWidth,a=window.innerWidth;return{left:t[0],top:t[1],right:t[2],gap:Math.max(0,a-e+t[2]-t[0])}},Style=styleSingleton(),lockAttribute=\"data-scroll-locked\",getStyles=function(A,t,e,a){var r=A.left,n=A.top,l=A.right,i=A.gap;return void 0===e&&(e=\"margin\"),\"\\n  .\".concat(noScrollbarsClassName,\" {\\n   overflow: hidden \").concat(a,\";\\n   padding-right: \").concat(i,\"px \").concat(a,\";\\n  }\\n  body[\").concat(lockAttribute,\"] {\\n    overflow: hidden \").concat(a,\";\\n    overscroll-behavior: contain;\\n    \").concat([t&&\"position: relative \".concat(a,\";\"),\"margin\"===e&&\"\\n    padding-left: \".concat(r,\"px;\\n    padding-top: \").concat(n,\"px;\\n    padding-right: \").concat(l,\"px;\\n    margin-left:0;\\n    margin-top:0;\\n    margin-right: \").concat(i,\"px \").concat(a,\";\\n    \"),\"padding\"===e&&\"padding-right: \".concat(i,\"px \").concat(a,\";\")].filter(Boolean).join(\"\"),\"\\n  }\\n  \\n  .\").concat(zeroRightClassName,\" {\\n    right: \").concat(i,\"px \").concat(a,\";\\n  }\\n  \\n  .\").concat(fullWidthClassName,\" {\\n    margin-right: \").concat(i,\"px \").concat(a,\";\\n  }\\n  \\n  .\").concat(zeroRightClassName,\" .\").concat(zeroRightClassName,\" {\\n    right: 0 \").concat(a,\";\\n  }\\n  \\n  .\").concat(fullWidthClassName,\" .\").concat(fullWidthClassName,\" {\\n    margin-right: 0 \").concat(a,\";\\n  }\\n  \\n  body[\").concat(lockAttribute,\"] {\\n    \").concat(removedBarSizeVariable,\": \").concat(i,\"px;\\n  }\\n\")},getCurrentUseCounter=function(){var A=parseInt(document.body.getAttribute(lockAttribute)||\"0\",10);return isFinite(A)?A:0},useLockAttribute=function(){reactExports.useEffect(function(){return document.body.setAttribute(lockAttribute,(getCurrentUseCounter()+1).toString()),function(){var A=getCurrentUseCounter()-1;A<=0?document.body.removeAttribute(lockAttribute):document.body.setAttribute(lockAttribute,A.toString())}},[])},RemoveScrollBar=function(A){var t=A.noRelative,e=A.noImportant,a=A.gapMode,r=void 0===a?\"margin\":a;useLockAttribute();var n=reactExports.useMemo(function(){return getGapWidth(r)},[r]);return reactExports.createElement(Style,{styles:getStyles(n,!t,r,e?\"\":\"!important\")})},passiveSupported=!1;if(\"undefined\"!=typeof window)try{var options=Object.defineProperty({},\"passive\",{get:function(){return passiveSupported=!0,!0}});window.addEventListener(\"test\",options,options),window.removeEventListener(\"test\",options,options)}catch(err){passiveSupported=!1}var nonPassive=!!passiveSupported&&{passive:!1},alwaysContainsScroll=function(A){return\"TEXTAREA\"===A.tagName},elementCanBeScrolled=function(A,t){if(!(A instanceof Element))return!1;var e=window.getComputedStyle(A);return\"hidden\"!==e[t]&&!(e.overflowY===e.overflowX&&!alwaysContainsScroll(A)&&\"visible\"===e[t])},elementCouldBeVScrolled=function(A){return elementCanBeScrolled(A,\"overflowY\")},elementCouldBeHScrolled=function(A){return elementCanBeScrolled(A,\"overflowX\")},locationCouldBeScrolled=function(A,t){var e=t.ownerDocument,a=t;do{if(\"undefined\"!=typeof ShadowRoot&&a instanceof ShadowRoot&&(a=a.host),elementCouldBeScrolled(A,a)){var r=getScrollVariables(A,a);if(r[1]>r[2])return!0}a=a.parentNode}while(a&&a!==e.body);return!1},getVScrollVariables=function(A){return[A.scrollTop,A.scrollHeight,A.clientHeight]},getHScrollVariables=function(A){return[A.scrollLeft,A.scrollWidth,A.clientWidth]},elementCouldBeScrolled=function(A,t){return\"v\"===A?elementCouldBeVScrolled(t):elementCouldBeHScrolled(t)},getScrollVariables=function(A,t){return\"v\"===A?getVScrollVariables(t):getHScrollVariables(t)},getDirectionFactor=function(A,t){return\"h\"===A&&\"rtl\"===t?-1:1},handleScroll=function(A,t,e,a,r){var n=getDirectionFactor(A,window.getComputedStyle(t).direction),l=n*a,i=e.target,o=t.contains(i),s=!1,p=l>0,c=0,u=0;do{if(!i)break;var d=getScrollVariables(A,i),h=d[0],S=d[1]-d[2]-n*h;(h||S)&&elementCouldBeScrolled(A,i)&&(c+=S,u+=h);var f=i.parentNode;i=f&&f.nodeType===Node.DOCUMENT_FRAGMENT_NODE?f.host:f}while(!o&&i!==document.body||o&&(t.contains(i)||t===i));return(p&&Math.abs(c)<1||!p&&Math.abs(u)<1)&&(s=!0),s},getTouchXY=function(A){return\"changedTouches\"in A?[A.changedTouches[0].clientX,A.changedTouches[0].clientY]:[0,0]},getDeltaXY=function(A){return[A.deltaX,A.deltaY]},extractRef=function(A){return A&&\"current\"in A?A.current:A},deltaCompare=function(A,t){return A[0]===t[0]&&A[1]===t[1]},generateStyle=function(A){return\"\\n  .block-interactivity-\".concat(A,\" {pointer-events: none;}\\n  .allow-interactivity-\").concat(A,\" {pointer-events: all;}\\n\")},idCounter=0,lockStack=[];function RemoveScrollSideCar(A){var t=reactExports.useRef([]),e=reactExports.useRef([0,0]),a=reactExports.useRef(),r=reactExports.useState(idCounter++)[0],n=reactExports.useState(styleSingleton)[0],l=reactExports.useRef(A);reactExports.useEffect(function(){l.current=A},[A]),reactExports.useEffect(function(){if(A.inert){document.body.classList.add(\"block-interactivity-\".concat(r));var t=__spreadArray([A.lockRef.current],(A.shards||[]).map(extractRef),!0).filter(Boolean);return t.forEach(function(A){return A.classList.add(\"allow-interactivity-\".concat(r))}),function(){document.body.classList.remove(\"block-interactivity-\".concat(r)),t.forEach(function(A){return A.classList.remove(\"allow-interactivity-\".concat(r))})}}},[A.inert,A.lockRef.current,A.shards]);var i=reactExports.useCallback(function(A,t){if(\"touches\"in A&&2===A.touches.length||\"wheel\"===A.type&&A.ctrlKey)return!l.current.allowPinchZoom;var r,n=getTouchXY(A),i=e.current,o=\"deltaX\"in A?A.deltaX:i[0]-n[0],s=\"deltaY\"in A?A.deltaY:i[1]-n[1],p=A.target,c=Math.abs(o)>Math.abs(s)?\"h\":\"v\";if(\"touches\"in A&&\"h\"===c&&\"range\"===p.type)return!1;var u=window.getSelection(),d=u&&u.anchorNode;if(!!d&&(d===p||d.contains(p)))return!1;var h=locationCouldBeScrolled(c,p);if(!h)return!0;if(h?r=c:(r=\"v\"===c?\"h\":\"v\",h=locationCouldBeScrolled(c,p)),!h)return!1;if(!a.current&&\"changedTouches\"in A&&(o||s)&&(a.current=r),!r)return!0;var S=a.current||r;return handleScroll(S,t,A,\"h\"===S?o:s)},[]),o=reactExports.useCallback(function(A){var e=A;if(lockStack.length&&lockStack[lockStack.length-1]===n){var a=\"deltaY\"in e?getDeltaXY(e):getTouchXY(e),r=t.current.filter(function(A){return A.name===e.type&&(A.target===e.target||e.target===A.shadowParent)&&deltaCompare(A.delta,a)})[0];if(r&&r.should)e.cancelable&&e.preventDefault();else if(!r){var o=(l.current.shards||[]).map(extractRef).filter(Boolean).filter(function(A){return A.contains(e.target)});(o.length>0?i(e,o[0]):!l.current.noIsolation)&&e.cancelable&&e.preventDefault()}}},[]),s=reactExports.useCallback(function(A,e,a,r){var n={name:A,delta:e,target:a,should:r,shadowParent:getOutermostShadowParent(a)};t.current.push(n),setTimeout(function(){t.current=t.current.filter(function(A){return A!==n})},1)},[]),p=reactExports.useCallback(function(A){e.current=getTouchXY(A),a.current=void 0},[]),c=reactExports.useCallback(function(t){s(t.type,getDeltaXY(t),t.target,i(t,A.lockRef.current))},[]),u=reactExports.useCallback(function(t){s(t.type,getTouchXY(t),t.target,i(t,A.lockRef.current))},[]);reactExports.useEffect(function(){return lockStack.push(n),A.setCallbacks({onScrollCapture:c,onWheelCapture:c,onTouchMoveCapture:u}),document.addEventListener(\"wheel\",o,nonPassive),document.addEventListener(\"touchmove\",o,nonPassive),document.addEventListener(\"touchstart\",p,nonPassive),function(){lockStack=lockStack.filter(function(A){return A!==n}),document.removeEventListener(\"wheel\",o,nonPassive),document.removeEventListener(\"touchmove\",o,nonPassive),document.removeEventListener(\"touchstart\",p,nonPassive)}},[]);var d=A.removeScrollBar,h=A.inert;return reactExports.createElement(reactExports.Fragment,null,h?reactExports.createElement(n,{styles:generateStyle(r)}):null,d?reactExports.createElement(RemoveScrollBar,{noRelative:A.noRelative,gapMode:A.gapMode}):null)}function getOutermostShadowParent(A){for(var t=null;null!==A;)A instanceof ShadowRoot&&(t=A.host,A=A.host),A=A.parentNode;return t}const SideCar=exportSidecar(effectCar,RemoveScrollSideCar);var ReactRemoveScroll=reactExports.forwardRef(function(A,t){return reactExports.createElement(RemoveScroll,__assign$1({},A,{ref:t,sideCar:SideCar}))});ReactRemoveScroll.classNames=RemoveScroll.classNames;var getDefaultParent=function(A){return\"undefined\"==typeof document?null:(Array.isArray(A)?A[0]:A).ownerDocument.body},counterMap=new WeakMap,uncontrolledNodes=new WeakMap,markerMap={},lockCount=0,unwrapHost=function(A){return A&&(A.host||unwrapHost(A.parentNode))},correctTargets=function(A,t){return t.map(function(t){if(A.contains(t))return t;var e=unwrapHost(t);return e&&A.contains(e)?e:null}).filter(function(A){return Boolean(A)})},applyAttributeToOthers=function(A,t,e,a){var r=correctTargets(t,Array.isArray(A)?A:[A]);markerMap[e]||(markerMap[e]=new WeakMap);var n=markerMap[e],l=[],i=new Set,o=new Set(r),s=function(A){A&&!i.has(A)&&(i.add(A),s(A.parentNode))};r.forEach(s);var p=function(A){A&&!o.has(A)&&Array.prototype.forEach.call(A.children,function(A){if(i.has(A))p(A);else try{var t=A.getAttribute(a),r=null!==t&&\"false\"!==t,o=(counterMap.get(A)||0)+1,s=(n.get(A)||0)+1;counterMap.set(A,o),n.set(A,s),l.push(A),1===o&&r&&uncontrolledNodes.set(A,!0),1===s&&A.setAttribute(e,\"true\"),r||A.setAttribute(a,\"true\")}catch(c){}})};return p(t),i.clear(),lockCount++,function(){l.forEach(function(A){var t=counterMap.get(A)-1,r=n.get(A)-1;counterMap.set(A,t),n.set(A,r),t||(uncontrolledNodes.has(A)||A.removeAttribute(a),uncontrolledNodes.delete(A)),r||A.removeAttribute(e)}),--lockCount||(counterMap=new WeakMap,counterMap=new WeakMap,uncontrolledNodes=new WeakMap,markerMap={})}},hideOthers=function(A,t,e){void 0===e&&(e=\"data-aria-hidden\");var a=Array.from(Array.isArray(A)?A:[A]),r=getDefaultParent(A);return r?(a.push.apply(a,Array.from(r.querySelectorAll(\"[aria-live], script\"))),applyAttributeToOthers(a,r,e,\"aria-hidden\")):function(){return null}},DIALOG_NAME=\"Dialog\",[createDialogContext,createDialogScope]=createContextScope(DIALOG_NAME),[DialogProvider,useDialogContext]=createDialogContext(DIALOG_NAME),Dialog=A=>{const{__scopeDialog:t,children:e,open:a,defaultOpen:r,onOpenChange:n,modal:l=!0}=A,i=reactExports.useRef(null),o=reactExports.useRef(null),[s,p]=useControllableState({prop:a,defaultProp:r??!1,onChange:n,caller:DIALOG_NAME});return jsxRuntimeExports.jsx(DialogProvider,{scope:t,triggerRef:i,contentRef:o,contentId:useId(),titleId:useId(),descriptionId:useId(),open:s,onOpenChange:p,onOpenToggle:reactExports.useCallback(()=>p(A=>!A),[p]),modal:l,children:e})};Dialog.displayName=DIALOG_NAME;var TRIGGER_NAME$2=\"DialogTrigger\",DialogTrigger=reactExports.forwardRef((A,t)=>{const{__scopeDialog:e,...a}=A,r=useDialogContext(TRIGGER_NAME$2,e),n=useComposedRefs(t,r.triggerRef);return jsxRuntimeExports.jsx(Primitive.button,{type:\"button\",\"aria-haspopup\":\"dialog\",\"aria-expanded\":r.open,\"aria-controls\":r.contentId,\"data-state\":getState(r.open),...a,ref:n,onClick:composeEventHandlers(A.onClick,r.onOpenToggle)})});DialogTrigger.displayName=TRIGGER_NAME$2;var PORTAL_NAME$3=\"DialogPortal\",[PortalProvider$1,usePortalContext$1]=createDialogContext(PORTAL_NAME$3,{forceMount:void 0}),DialogPortal=A=>{const{__scopeDialog:t,forceMount:e,children:a,container:r}=A,n=useDialogContext(PORTAL_NAME$3,t);return jsxRuntimeExports.jsx(PortalProvider$1,{scope:t,forceMount:e,children:reactExports.Children.map(a,A=>jsxRuntimeExports.jsx(Presence,{present:e||n.open,children:jsxRuntimeExports.jsx(Portal$2,{asChild:!0,container:r,children:A})}))})};DialogPortal.displayName=PORTAL_NAME$3;var OVERLAY_NAME$1=\"DialogOverlay\",DialogOverlay=reactExports.forwardRef((A,t)=>{const e=usePortalContext$1(OVERLAY_NAME$1,A.__scopeDialog),{forceMount:a=e.forceMount,...r}=A,n=useDialogContext(OVERLAY_NAME$1,A.__scopeDialog);return n.modal?jsxRuntimeExports.jsx(Presence,{present:a||n.open,children:jsxRuntimeExports.jsx(DialogOverlayImpl,{...r,ref:t})}):null});DialogOverlay.displayName=OVERLAY_NAME$1;var Slot$2=createSlot$1(\"DialogOverlay.RemoveScroll\"),DialogOverlayImpl=reactExports.forwardRef((A,t)=>{const{__scopeDialog:e,...a}=A,r=useDialogContext(OVERLAY_NAME$1,e);return jsxRuntimeExports.jsx(ReactRemoveScroll,{as:Slot$2,allowPinchZoom:!0,shards:[r.contentRef],children:jsxRuntimeExports.jsx(Primitive.div,{\"data-state\":getState(r.open),...a,ref:t,style:{pointerEvents:\"auto\",...a.style}})})}),CONTENT_NAME$4=\"DialogContent\",DialogContent=reactExports.forwardRef((A,t)=>{const e=usePortalContext$1(CONTENT_NAME$4,A.__scopeDialog),{forceMount:a=e.forceMount,...r}=A,n=useDialogContext(CONTENT_NAME$4,A.__scopeDialog);return jsxRuntimeExports.jsx(Presence,{present:a||n.open,children:n.modal?jsxRuntimeExports.jsx(DialogContentModal,{...r,ref:t}):jsxRuntimeExports.jsx(DialogContentNonModal,{...r,ref:t})})});DialogContent.displayName=CONTENT_NAME$4;var DialogContentModal=reactExports.forwardRef((A,t)=>{const e=useDialogContext(CONTENT_NAME$4,A.__scopeDialog),a=reactExports.useRef(null),r=useComposedRefs(t,e.contentRef,a);return reactExports.useEffect(()=>{const A=a.current;if(A)return hideOthers(A)},[]),jsxRuntimeExports.jsx(DialogContentImpl,{...A,ref:r,trapFocus:e.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:composeEventHandlers(A.onCloseAutoFocus,A=>{var t;A.preventDefault(),null==(t=e.triggerRef.current)||t.focus()}),onPointerDownOutside:composeEventHandlers(A.onPointerDownOutside,A=>{const t=A.detail.originalEvent,e=0===t.button&&!0===t.ctrlKey;(2===t.button||e)&&A.preventDefault()}),onFocusOutside:composeEventHandlers(A.onFocusOutside,A=>A.preventDefault())})}),DialogContentNonModal=reactExports.forwardRef((A,t)=>{const e=useDialogContext(CONTENT_NAME$4,A.__scopeDialog),a=reactExports.useRef(!1),r=reactExports.useRef(!1);return jsxRuntimeExports.jsx(DialogContentImpl,{...A,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{var n,l;null==(n=A.onCloseAutoFocus)||n.call(A,t),t.defaultPrevented||(a.current||null==(l=e.triggerRef.current)||l.focus(),t.preventDefault()),a.current=!1,r.current=!1},onInteractOutside:t=>{var n,l;null==(n=A.onInteractOutside)||n.call(A,t),t.defaultPrevented||(a.current=!0,\"pointerdown\"===t.detail.originalEvent.type&&(r.current=!0));const i=t.target;(null==(l=e.triggerRef.current)?void 0:l.contains(i))&&t.preventDefault(),\"focusin\"===t.detail.originalEvent.type&&r.current&&t.preventDefault()}})}),DialogContentImpl=reactExports.forwardRef((A,t)=>{const{__scopeDialog:e,trapFocus:a,onOpenAutoFocus:r,onCloseAutoFocus:n,...l}=A,i=useDialogContext(CONTENT_NAME$4,e),o=reactExports.useRef(null),s=useComposedRefs(t,o);return useFocusGuards(),jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(FocusScope,{asChild:!0,loop:!0,trapped:a,onMountAutoFocus:r,onUnmountAutoFocus:n,children:jsxRuntimeExports.jsx(DismissableLayer,{role:\"dialog\",id:i.contentId,\"aria-describedby\":i.descriptionId,\"aria-labelledby\":i.titleId,\"data-state\":getState(i.open),...l,ref:s,onDismiss:()=>i.onOpenChange(!1)})}),jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(TitleWarning,{titleId:i.titleId}),jsxRuntimeExports.jsx(DescriptionWarning$1,{contentRef:o,descriptionId:i.descriptionId})]})]})}),TITLE_NAME$1=\"DialogTitle\",DialogTitle=reactExports.forwardRef((A,t)=>{const{__scopeDialog:e,...a}=A,r=useDialogContext(TITLE_NAME$1,e);return jsxRuntimeExports.jsx(Primitive.h2,{id:r.titleId,...a,ref:t})});DialogTitle.displayName=TITLE_NAME$1;var DESCRIPTION_NAME$1=\"DialogDescription\",DialogDescription=reactExports.forwardRef((A,t)=>{const{__scopeDialog:e,...a}=A,r=useDialogContext(DESCRIPTION_NAME$1,e);return jsxRuntimeExports.jsx(Primitive.p,{id:r.descriptionId,...a,ref:t})});DialogDescription.displayName=DESCRIPTION_NAME$1;var CLOSE_NAME=\"DialogClose\",DialogClose=reactExports.forwardRef((A,t)=>{const{__scopeDialog:e,...a}=A,r=useDialogContext(CLOSE_NAME,e);return jsxRuntimeExports.jsx(Primitive.button,{type:\"button\",...a,ref:t,onClick:composeEventHandlers(A.onClick,()=>r.onOpenChange(!1))})});function getState(A){return A?\"open\":\"closed\"}DialogClose.displayName=CLOSE_NAME;var TITLE_WARNING_NAME=\"DialogTitleWarning\",[WarningProvider,useWarningContext]=createContext2(TITLE_WARNING_NAME,{contentName:CONTENT_NAME$4,titleName:TITLE_NAME$1,docsSlug:\"dialog\"}),TitleWarning=({titleId:A})=>{const t=useWarningContext(TITLE_WARNING_NAME),e=`\\`${t.contentName}\\` requires a \\`${t.titleName}\\` for the component to be accessible for screen reader users.\\n\\nIf you want to hide the \\`${t.titleName}\\`, you can wrap it with our VisuallyHidden component.\\n\\nFor more information, see https://radix-ui.com/primitives/docs/components/${t.docsSlug}`;return reactExports.useEffect(()=>{if(A){document.getElementById(A)}},[e,A]),null},DESCRIPTION_WARNING_NAME=\"DialogDescriptionWarning\",DescriptionWarning$1=({contentRef:A,descriptionId:t})=>{const e=`Warning: Missing \\`Description\\` or \\`aria-describedby={undefined}\\` for {${useWarningContext(DESCRIPTION_WARNING_NAME).contentName}}.`;return reactExports.useEffect(()=>{var e;const a=null==(e=A.current)?void 0:e.getAttribute(\"aria-describedby\");if(t&&a){document.getElementById(t)}},[e,A,t]),null},Root$2=Dialog,Trigger$1=DialogTrigger,Portal$1=DialogPortal,Overlay=DialogOverlay,Content$1=DialogContent,Title=DialogTitle,Description=DialogDescription,Close=DialogClose,ROOT_NAME=\"AlertDialog\",[createAlertDialogContext]=createContextScope(ROOT_NAME,[createDialogScope]),useDialogScope=createDialogScope(),AlertDialog=A=>{const{__scopeAlertDialog:t,...e}=A,a=useDialogScope(t);return jsxRuntimeExports.jsx(Root$2,{...a,...e,modal:!0})};AlertDialog.displayName=ROOT_NAME;var TRIGGER_NAME$1=\"AlertDialogTrigger\",AlertDialogTrigger=reactExports.forwardRef((A,t)=>{const{__scopeAlertDialog:e,...a}=A,r=useDialogScope(e);return jsxRuntimeExports.jsx(Trigger$1,{...r,...a,ref:t})});AlertDialogTrigger.displayName=TRIGGER_NAME$1;var PORTAL_NAME$2=\"AlertDialogPortal\",AlertDialogPortal=A=>{const{__scopeAlertDialog:t,...e}=A,a=useDialogScope(t);return jsxRuntimeExports.jsx(Portal$1,{...a,...e})};AlertDialogPortal.displayName=PORTAL_NAME$2;var OVERLAY_NAME=\"AlertDialogOverlay\",AlertDialogOverlay=reactExports.forwardRef((A,t)=>{const{__scopeAlertDialog:e,...a}=A,r=useDialogScope(e);return jsxRuntimeExports.jsx(Overlay,{...r,...a,ref:t})});AlertDialogOverlay.displayName=OVERLAY_NAME;var CONTENT_NAME$3=\"AlertDialogContent\",[AlertDialogContentProvider,useAlertDialogContentContext]=createAlertDialogContext(CONTENT_NAME$3),Slottable=createSlottable(\"AlertDialogContent\"),AlertDialogContent=reactExports.forwardRef((A,t)=>{const{__scopeAlertDialog:e,children:a,...r}=A,n=useDialogScope(e),l=reactExports.useRef(null),i=useComposedRefs(t,l),o=reactExports.useRef(null);return jsxRuntimeExports.jsx(WarningProvider,{contentName:CONTENT_NAME$3,titleName:TITLE_NAME,docsSlug:\"alert-dialog\",children:jsxRuntimeExports.jsx(AlertDialogContentProvider,{scope:e,cancelRef:o,children:jsxRuntimeExports.jsxs(Content$1,{role:\"alertdialog\",...n,...r,ref:i,onOpenAutoFocus:composeEventHandlers(r.onOpenAutoFocus,A=>{var t;A.preventDefault(),null==(t=o.current)||t.focus({preventScroll:!0})}),onPointerDownOutside:A=>A.preventDefault(),onInteractOutside:A=>A.preventDefault(),children:[jsxRuntimeExports.jsx(Slottable,{children:a}),jsxRuntimeExports.jsx(DescriptionWarning,{contentRef:l})]})})})});AlertDialogContent.displayName=CONTENT_NAME$3;var TITLE_NAME=\"AlertDialogTitle\",AlertDialogTitle=reactExports.forwardRef((A,t)=>{const{__scopeAlertDialog:e,...a}=A,r=useDialogScope(e);return jsxRuntimeExports.jsx(Title,{...r,...a,ref:t})});AlertDialogTitle.displayName=TITLE_NAME;var DESCRIPTION_NAME=\"AlertDialogDescription\",AlertDialogDescription=reactExports.forwardRef((A,t)=>{const{__scopeAlertDialog:e,...a}=A,r=useDialogScope(e);return jsxRuntimeExports.jsx(Description,{...r,...a,ref:t})});AlertDialogDescription.displayName=DESCRIPTION_NAME;var ACTION_NAME=\"AlertDialogAction\",AlertDialogAction=reactExports.forwardRef((A,t)=>{const{__scopeAlertDialog:e,...a}=A,r=useDialogScope(e);return jsxRuntimeExports.jsx(Close,{...r,...a,ref:t})});AlertDialogAction.displayName=ACTION_NAME;var CANCEL_NAME=\"AlertDialogCancel\",AlertDialogCancel=reactExports.forwardRef((A,t)=>{const{__scopeAlertDialog:e,...a}=A,{cancelRef:r}=useAlertDialogContentContext(CANCEL_NAME,e),n=useDialogScope(e),l=useComposedRefs(t,r);return jsxRuntimeExports.jsx(Close,{...n,...a,ref:l})});AlertDialogCancel.displayName=CANCEL_NAME;var DescriptionWarning=({contentRef:A})=>{const t=`\\`${CONTENT_NAME$3}\\` requires a description for the component to be accessible for screen reader users.\\n\\nYou can add a description to the \\`${CONTENT_NAME$3}\\` by passing a \\`${DESCRIPTION_NAME}\\` component as a child, which also benefits sighted users by adding visible context to the dialog.\\n\\nAlternatively, you can use your own component as a description by assigning it an \\`id\\` and passing the same value to the \\`aria-describedby\\` prop in \\`${CONTENT_NAME$3}\\`. If the description is confusing or duplicative for sighted users, you can use the \\`@radix-ui/react-visually-hidden\\` primitive as a wrapper around your description component.\\n\\nFor more information, see https://radix-ui.com/primitives/docs/components/alert-dialog`;return reactExports.useEffect(()=>{var t;document.getElementById(null==(t=A.current)?void 0:t.getAttribute(\"aria-describedby\"))},[t,A]),null},Root2$2=AlertDialog,Portal2$1=AlertDialogPortal,Overlay2=AlertDialogOverlay,Content2$2=AlertDialogContent,Action=AlertDialogAction,Cancel=AlertDialogCancel,Title2=AlertDialogTitle,Description2=AlertDialogDescription;function r(A){var t,e,a=\"\";if(\"string\"==typeof A||\"number\"==typeof A)a+=A;else if(\"object\"==typeof A)if(Array.isArray(A)){var n=A.length;for(t=0;t<n;t++)A[t]&&(e=r(A[t]))&&(a&&(a+=\" \"),a+=e)}else for(e in A)A[e]&&(a&&(a+=\" \"),a+=e);return a}function clsx(){for(var A,t,e=0,a=\"\",n=arguments.length;e<n;e++)(A=arguments[e])&&(t=r(A))&&(a&&(a+=\" \"),a+=t);return a}const falsyToString=A=>\"boolean\"==typeof A?`${A}`:0===A?\"0\":A,cx=clsx,cva=(A,t)=>e=>{var a;if(null==(null==t?void 0:t.variants))return cx(A,null==e?void 0:e.class,null==e?void 0:e.className);const{variants:r,defaultVariants:n}=t,l=Object.keys(r).map(A=>{const t=null==e?void 0:e[A],a=null==n?void 0:n[A];if(null===t)return null;const l=falsyToString(t)||falsyToString(a);return r[A][l]}),i=e&&Object.entries(e).reduce((A,t)=>{let[e,a]=t;return void 0===a||(A[e]=a),A},{}),o=null==t||null===(a=t.compoundVariants)||void 0===a?void 0:a.reduce((A,t)=>{let{class:e,className:a,...r}=t;return Object.entries(r).every(A=>{let[t,e]=A;return Array.isArray(e)?e.includes({...n,...i}[t]):{...n,...i}[t]===e})?[...A,e,a]:A},[]);return cx(A,l,o,null==e?void 0:e.class,null==e?void 0:e.className)};var REACT_LAZY_TYPE=Symbol.for(\"react.lazy\"),use=React$1[\" use \".trim().toString()];function isPromiseLike(A){return\"object\"==typeof A&&null!==A&&\"then\"in A}function isLazyComponent(A){return null!=A&&\"object\"==typeof A&&\"$$typeof\"in A&&A.$$typeof===REACT_LAZY_TYPE&&\"_payload\"in A&&isPromiseLike(A._payload)}function createSlot(A){const t=createSlotClone(A),e=reactExports.forwardRef((A,e)=>{let{children:a,...r}=A;isLazyComponent(a)&&\"function\"==typeof use&&(a=use(a._payload));const n=reactExports.Children.toArray(a),l=n.find(isSlottable);if(l){const A=l.props.children,a=n.map(t=>t===l?reactExports.Children.count(A)>1?reactExports.Children.only(null):reactExports.isValidElement(A)?A.props.children:null:t);return jsxRuntimeExports.jsx(t,{...r,ref:e,children:reactExports.isValidElement(A)?reactExports.cloneElement(A,void 0,a):null})}return jsxRuntimeExports.jsx(t,{...r,ref:e,children:a})});return e.displayName=`${A}.Slot`,e}var Slot$1=createSlot(\"Slot\");function createSlotClone(A){const t=reactExports.forwardRef((A,t)=>{let{children:e,...a}=A;if(isLazyComponent(e)&&\"function\"==typeof use&&(e=use(e._payload)),reactExports.isValidElement(e)){const A=getElementRef(e),r=mergeProps(a,e.props);return e.type!==reactExports.Fragment&&(r.ref=t?composeRefs(t,A):A),reactExports.cloneElement(e,r)}return reactExports.Children.count(e)>1?reactExports.Children.only(null):null});return t.displayName=`${A}.SlotClone`,t}var SLOTTABLE_IDENTIFIER=Symbol(\"radix.slottable\");function isSlottable(A){return reactExports.isValidElement(A)&&\"function\"==typeof A.type&&\"__radixId\"in A.type&&A.type.__radixId===SLOTTABLE_IDENTIFIER}function mergeProps(A,t){const e={...t};for(const a in t){const r=A[a],n=t[a];/^on[A-Z]/.test(a)?r&&n?e[a]=(...A)=>{const t=n(...A);return r(...A),t}:r&&(e[a]=r):\"style\"===a?e[a]={...r,...n}:\"className\"===a&&(e[a]=[r,n].filter(Boolean).join(\" \"))}return{...A,...e}}function getElementRef(A){var t,e;let a=null==(t=Object.getOwnPropertyDescriptor(A.props,\"ref\"))?void 0:t.get,r=a&&\"isReactWarning\"in a&&a.isReactWarning;return r?A.ref:(a=null==(e=Object.getOwnPropertyDescriptor(A,\"ref\"))?void 0:e.get,r=a&&\"isReactWarning\"in a&&a.isReactWarning,r?A.props.ref:A.props.ref||A.ref)}const concatArrays=(A,t)=>{const e=new Array(A.length+t.length);for(let a=0;a<A.length;a++)e[a]=A[a];for(let a=0;a<t.length;a++)e[A.length+a]=t[a];return e},createClassValidatorObject=(A,t)=>({classGroupId:A,validator:t}),createClassPartObject=(A=new Map,t=null,e)=>({nextPart:A,validators:t,classGroupId:e}),CLASS_PART_SEPARATOR=\"-\",EMPTY_CONFLICTS=[],ARBITRARY_PROPERTY_PREFIX=\"arbitrary..\",createClassGroupUtils=A=>{const t=createClassMap(A),{conflictingClassGroups:e,conflictingClassGroupModifiers:a}=A;return{getClassGroupId:A=>{if(A.startsWith(\"[\")&&A.endsWith(\"]\"))return getGroupIdForArbitraryProperty(A);const e=A.split(CLASS_PART_SEPARATOR),a=\"\"===e[0]&&e.length>1?1:0;return getGroupRecursive(e,a,t)},getConflictingClassGroupIds:(A,t)=>{if(t){const t=a[A],r=e[A];return t?r?concatArrays(r,t):t:r||EMPTY_CONFLICTS}return e[A]||EMPTY_CONFLICTS}}},getGroupRecursive=(A,t,e)=>{if(0===A.length-t)return e.classGroupId;const a=A[t],r=e.nextPart.get(a);if(r){const e=getGroupRecursive(A,t+1,r);if(e)return e}const n=e.validators;if(null===n)return;const l=0===t?A.join(CLASS_PART_SEPARATOR):A.slice(t).join(CLASS_PART_SEPARATOR),i=n.length;for(let o=0;o<i;o++){const A=n[o];if(A.validator(l))return A.classGroupId}},getGroupIdForArbitraryProperty=A=>-1===A.slice(1,-1).indexOf(\":\")?void 0:(()=>{const t=A.slice(1,-1),e=t.indexOf(\":\"),a=t.slice(0,e);return a?ARBITRARY_PROPERTY_PREFIX+a:void 0})(),createClassMap=A=>{const{theme:t,classGroups:e}=A;return processClassGroups(e,t)},processClassGroups=(A,t)=>{const e=createClassPartObject();for(const a in A){const r=A[a];processClassesRecursively(r,e,a,t)}return e},processClassesRecursively=(A,t,e,a)=>{const r=A.length;for(let n=0;n<r;n++){const r=A[n];processClassDefinition(r,t,e,a)}},processClassDefinition=(A,t,e,a)=>{\"string\"!=typeof A?\"function\"!=typeof A?processObjectDefinition(A,t,e,a):processFunctionDefinition(A,t,e,a):processStringDefinition(A,t,e)},processStringDefinition=(A,t,e)=>{(\"\"===A?t:getPart(t,A)).classGroupId=e},processFunctionDefinition=(A,t,e,a)=>{isThemeGetter(A)?processClassesRecursively(A(a),t,e,a):(null===t.validators&&(t.validators=[]),t.validators.push(createClassValidatorObject(e,A)))},processObjectDefinition=(A,t,e,a)=>{const r=Object.entries(A),n=r.length;for(let l=0;l<n;l++){const[A,n]=r[l];processClassesRecursively(n,getPart(t,A),e,a)}},getPart=(A,t)=>{let e=A;const a=t.split(CLASS_PART_SEPARATOR),r=a.length;for(let n=0;n<r;n++){const A=a[n];let t=e.nextPart.get(A);t||(t=createClassPartObject(),e.nextPart.set(A,t)),e=t}return e},isThemeGetter=A=>\"isThemeGetter\"in A&&!0===A.isThemeGetter,createLruCache=A=>{if(A<1)return{get:()=>{},set:()=>{}};let t=0,e=Object.create(null),a=Object.create(null);const r=(r,n)=>{e[r]=n,t++,t>A&&(t=0,a=e,e=Object.create(null))};return{get(A){let t=e[A];return void 0!==t?t:void 0!==(t=a[A])?(r(A,t),t):void 0},set(A,t){A in e?e[A]=t:r(A,t)}}},IMPORTANT_MODIFIER=\"!\",MODIFIER_SEPARATOR=\":\",EMPTY_MODIFIERS=[],createResultObject=(A,t,e,a,r)=>({modifiers:A,hasImportantModifier:t,baseClassName:e,maybePostfixModifierPosition:a,isExternal:r}),createParseClassName=A=>{const{prefix:t,experimentalParseClassName:e}=A;let a=A=>{const t=[];let e,a=0,r=0,n=0;const l=A.length;for(let p=0;p<l;p++){const l=A[p];if(0===a&&0===r){if(l===MODIFIER_SEPARATOR){t.push(A.slice(n,p)),n=p+1;continue}if(\"/\"===l){e=p;continue}}\"[\"===l?a++:\"]\"===l?a--:\"(\"===l?r++:\")\"===l&&r--}const i=0===t.length?A:A.slice(n);let o=i,s=!1;i.endsWith(IMPORTANT_MODIFIER)?(o=i.slice(0,-1),s=!0):i.startsWith(IMPORTANT_MODIFIER)&&(o=i.slice(1),s=!0);return createResultObject(t,s,o,e&&e>n?e-n:void 0)};if(t){const A=t+MODIFIER_SEPARATOR,e=a;a=t=>t.startsWith(A)?e(t.slice(A.length)):createResultObject(EMPTY_MODIFIERS,!1,t,void 0,!0)}if(e){const A=a;a=t=>e({className:t,parseClassName:A})}return a},createSortModifiers=A=>{const t=new Map;return A.orderSensitiveModifiers.forEach((A,e)=>{t.set(A,1e6+e)}),A=>{const e=[];let a=[];for(let r=0;r<A.length;r++){const n=A[r],l=\"[\"===n[0],i=t.has(n);l||i?(a.length>0&&(a.sort(),e.push(...a),a=[]),e.push(n)):a.push(n)}return a.length>0&&(a.sort(),e.push(...a)),e}},createConfigUtils=A=>({cache:createLruCache(A.cacheSize),parseClassName:createParseClassName(A),sortModifiers:createSortModifiers(A),...createClassGroupUtils(A)}),SPLIT_CLASSES_REGEX=/\\s+/,mergeClassList=(A,t)=>{const{parseClassName:e,getClassGroupId:a,getConflictingClassGroupIds:r,sortModifiers:n}=t,l=[],i=A.trim().split(SPLIT_CLASSES_REGEX);let o=\"\";for(let s=i.length-1;s>=0;s-=1){const A=i[s],{isExternal:t,modifiers:p,hasImportantModifier:c,baseClassName:u,maybePostfixModifierPosition:d}=e(A);if(t){o=A+(o.length>0?\" \"+o:o);continue}let h=!!d,S=a(h?u.substring(0,d):u);if(!S){if(!h){o=A+(o.length>0?\" \"+o:o);continue}if(S=a(u),!S){o=A+(o.length>0?\" \"+o:o);continue}h=!1}const f=0===p.length?\"\":1===p.length?p[0]:n(p).join(\":\"),m=c?f+IMPORTANT_MODIFIER:f,L=m+S;if(l.indexOf(L)>-1)continue;l.push(L);const W=r(S,h);for(let e=0;e<W.length;++e){const A=W[e];l.push(m+A)}o=A+(o.length>0?\" \"+o:o)}return o},twJoin=(...A)=>{let t,e,a=0,r=\"\";for(;a<A.length;)(t=A[a++])&&(e=toValue(t))&&(r&&(r+=\" \"),r+=e);return r},toValue=A=>{if(\"string\"==typeof A)return A;let t,e=\"\";for(let a=0;a<A.length;a++)A[a]&&(t=toValue(A[a]))&&(e&&(e+=\" \"),e+=t);return e},createTailwindMerge=(A,...t)=>{let e,a,r,n;const l=A=>{const t=a(A);if(t)return t;const n=mergeClassList(A,e);return r(A,n),n};return n=i=>{const o=t.reduce((A,t)=>t(A),A());return e=createConfigUtils(o),a=e.cache.get,r=e.cache.set,n=l,l(i)},(...A)=>n(twJoin(...A))},fallbackThemeArr=[],fromTheme=A=>{const t=t=>t[A]||fallbackThemeArr;return t.isThemeGetter=!0,t},arbitraryValueRegex=/^\\[(?:(\\w[\\w-]*):)?(.+)\\]$/i,arbitraryVariableRegex=/^\\((?:(\\w[\\w-]*):)?(.+)\\)$/i,fractionRegex=/^\\d+\\/\\d+$/,tshirtUnitRegex=/^(\\d+(\\.\\d+)?)?(xs|sm|md|lg|xl)$/,lengthUnitRegex=/\\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\\b(calc|min|max|clamp)\\(.+\\)|^0$/,colorFunctionRegex=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\\(.+\\)$/,shadowRegex=/^(inset_)?-?((\\d+)?\\.?(\\d+)[a-z]+|0)_-?((\\d+)?\\.?(\\d+)[a-z]+|0)/,imageRegex=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\\(.+\\)$/,isFraction=A=>fractionRegex.test(A),isNumber=A=>!!A&&!Number.isNaN(Number(A)),isInteger=A=>!!A&&Number.isInteger(Number(A)),isPercent=A=>A.endsWith(\"%\")&&isNumber(A.slice(0,-1)),isTshirtSize=A=>tshirtUnitRegex.test(A),isAny=()=>!0,isLengthOnly=A=>lengthUnitRegex.test(A)&&!colorFunctionRegex.test(A),isNever=()=>!1,isShadow=A=>shadowRegex.test(A),isImage=A=>imageRegex.test(A),isAnyNonArbitrary=A=>!isArbitraryValue(A)&&!isArbitraryVariable(A),isArbitrarySize=A=>getIsArbitraryValue(A,isLabelSize,isNever),isArbitraryValue=A=>arbitraryValueRegex.test(A),isArbitraryLength=A=>getIsArbitraryValue(A,isLabelLength,isLengthOnly),isArbitraryNumber=A=>getIsArbitraryValue(A,isLabelNumber,isNumber),isArbitraryPosition=A=>getIsArbitraryValue(A,isLabelPosition,isNever),isArbitraryImage=A=>getIsArbitraryValue(A,isLabelImage,isImage),isArbitraryShadow=A=>getIsArbitraryValue(A,isLabelShadow,isShadow),isArbitraryVariable=A=>arbitraryVariableRegex.test(A),isArbitraryVariableLength=A=>getIsArbitraryVariable(A,isLabelLength),isArbitraryVariableFamilyName=A=>getIsArbitraryVariable(A,isLabelFamilyName),isArbitraryVariablePosition=A=>getIsArbitraryVariable(A,isLabelPosition),isArbitraryVariableSize=A=>getIsArbitraryVariable(A,isLabelSize),isArbitraryVariableImage=A=>getIsArbitraryVariable(A,isLabelImage),isArbitraryVariableShadow=A=>getIsArbitraryVariable(A,isLabelShadow,!0),getIsArbitraryValue=(A,t,e)=>{const a=arbitraryValueRegex.exec(A);return!!a&&(a[1]?t(a[1]):e(a[2]))},getIsArbitraryVariable=(A,t,e=!1)=>{const a=arbitraryVariableRegex.exec(A);return!!a&&(a[1]?t(a[1]):e)},isLabelPosition=A=>\"position\"===A||\"percentage\"===A,isLabelImage=A=>\"image\"===A||\"url\"===A,isLabelSize=A=>\"length\"===A||\"size\"===A||\"bg-size\"===A,isLabelLength=A=>\"length\"===A,isLabelNumber=A=>\"number\"===A,isLabelFamilyName=A=>\"family-name\"===A,isLabelShadow=A=>\"shadow\"===A,getDefaultConfig=()=>{const A=fromTheme(\"color\"),t=fromTheme(\"font\"),e=fromTheme(\"text\"),a=fromTheme(\"font-weight\"),r=fromTheme(\"tracking\"),n=fromTheme(\"leading\"),l=fromTheme(\"breakpoint\"),i=fromTheme(\"container\"),o=fromTheme(\"spacing\"),s=fromTheme(\"radius\"),p=fromTheme(\"shadow\"),c=fromTheme(\"inset-shadow\"),u=fromTheme(\"text-shadow\"),d=fromTheme(\"drop-shadow\"),h=fromTheme(\"blur\"),S=fromTheme(\"perspective\"),f=fromTheme(\"aspect\"),m=fromTheme(\"ease\"),L=fromTheme(\"animate\"),W=()=>[\"center\",\"top\",\"bottom\",\"left\",\"right\",\"top-left\",\"left-top\",\"top-right\",\"right-top\",\"bottom-right\",\"right-bottom\",\"bottom-left\",\"left-bottom\",isArbitraryVariable,isArbitraryValue],g=()=>[isArbitraryVariable,isArbitraryValue,o],y=()=>[isFraction,\"full\",\"auto\",...g()],v=()=>[isInteger,\"none\",\"subgrid\",isArbitraryVariable,isArbitraryValue],b=()=>[\"auto\",{span:[\"full\",isInteger,isArbitraryVariable,isArbitraryValue]},isInteger,isArbitraryVariable,isArbitraryValue],x=()=>[isInteger,\"auto\",isArbitraryVariable,isArbitraryValue],E=()=>[\"auto\",\"min\",\"max\",\"fr\",isArbitraryVariable,isArbitraryValue],k=()=>[\"auto\",...g()],w=()=>[isFraction,\"auto\",\"full\",\"dvw\",\"dvh\",\"lvw\",\"lvh\",\"svw\",\"svh\",\"min\",\"max\",\"fit\",...g()],C=()=>[A,isArbitraryVariable,isArbitraryValue],P=()=>[\"center\",\"top\",\"bottom\",\"left\",\"right\",\"top-left\",\"left-top\",\"top-right\",\"right-top\",\"bottom-right\",\"right-bottom\",\"bottom-left\",\"left-bottom\",isArbitraryVariablePosition,isArbitraryPosition,{position:[isArbitraryVariable,isArbitraryValue]}],T=()=>[\"auto\",\"cover\",\"contain\",isArbitraryVariableSize,isArbitrarySize,{size:[isArbitraryVariable,isArbitraryValue]}],_=()=>[isPercent,isArbitraryVariableLength,isArbitraryLength],M=()=>[\"\",\"none\",\"full\",s,isArbitraryVariable,isArbitraryValue],D=()=>[\"\",isNumber,isArbitraryVariableLength,isArbitraryLength],R=()=>[isNumber,isPercent,isArbitraryVariablePosition,isArbitraryPosition],N=()=>[\"\",\"none\",h,isArbitraryVariable,isArbitraryValue],I=()=>[\"none\",isNumber,isArbitraryVariable,isArbitraryValue],F=()=>[\"none\",isNumber,isArbitraryVariable,isArbitraryValue],O=()=>[isNumber,isArbitraryVariable,isArbitraryValue],V=()=>[isFraction,\"full\",...g()];return{cacheSize:500,theme:{animate:[\"spin\",\"ping\",\"pulse\",\"bounce\"],aspect:[\"video\"],blur:[isTshirtSize],breakpoint:[isTshirtSize],color:[isAny],container:[isTshirtSize],\"drop-shadow\":[isTshirtSize],ease:[\"in\",\"out\",\"in-out\"],font:[isAnyNonArbitrary],\"font-weight\":[\"thin\",\"extralight\",\"light\",\"normal\",\"medium\",\"semibold\",\"bold\",\"extrabold\",\"black\"],\"inset-shadow\":[isTshirtSize],leading:[\"none\",\"tight\",\"snug\",\"normal\",\"relaxed\",\"loose\"],perspective:[\"dramatic\",\"near\",\"normal\",\"midrange\",\"distant\",\"none\"],radius:[isTshirtSize],shadow:[isTshirtSize],spacing:[\"px\",isNumber],text:[isTshirtSize],\"text-shadow\":[isTshirtSize],tracking:[\"tighter\",\"tight\",\"normal\",\"wide\",\"wider\",\"widest\"]},classGroups:{aspect:[{aspect:[\"auto\",\"square\",isFraction,isArbitraryValue,isArbitraryVariable,f]}],container:[\"container\"],columns:[{columns:[isNumber,isArbitraryValue,isArbitraryVariable,i]}],\"break-after\":[{\"break-after\":[\"auto\",\"avoid\",\"all\",\"avoid-page\",\"page\",\"left\",\"right\",\"column\"]}],\"break-before\":[{\"break-before\":[\"auto\",\"avoid\",\"all\",\"avoid-page\",\"page\",\"left\",\"right\",\"column\"]}],\"break-inside\":[{\"break-inside\":[\"auto\",\"avoid\",\"avoid-page\",\"avoid-column\"]}],\"box-decoration\":[{\"box-decoration\":[\"slice\",\"clone\"]}],box:[{box:[\"border\",\"content\"]}],display:[\"block\",\"inline-block\",\"inline\",\"flex\",\"inline-flex\",\"table\",\"inline-table\",\"table-caption\",\"table-cell\",\"table-column\",\"table-column-group\",\"table-footer-group\",\"table-header-group\",\"table-row-group\",\"table-row\",\"flow-root\",\"grid\",\"inline-grid\",\"contents\",\"list-item\",\"hidden\"],sr:[\"sr-only\",\"not-sr-only\"],float:[{float:[\"right\",\"left\",\"none\",\"start\",\"end\"]}],clear:[{clear:[\"left\",\"right\",\"both\",\"none\",\"start\",\"end\"]}],isolation:[\"isolate\",\"isolation-auto\"],\"object-fit\":[{object:[\"contain\",\"cover\",\"fill\",\"none\",\"scale-down\"]}],\"object-position\":[{object:W()}],overflow:[{overflow:[\"auto\",\"hidden\",\"clip\",\"visible\",\"scroll\"]}],\"overflow-x\":[{\"overflow-x\":[\"auto\",\"hidden\",\"clip\",\"visible\",\"scroll\"]}],\"overflow-y\":[{\"overflow-y\":[\"auto\",\"hidden\",\"clip\",\"visible\",\"scroll\"]}],overscroll:[{overscroll:[\"auto\",\"contain\",\"none\"]}],\"overscroll-x\":[{\"overscroll-x\":[\"auto\",\"contain\",\"none\"]}],\"overscroll-y\":[{\"overscroll-y\":[\"auto\",\"contain\",\"none\"]}],position:[\"static\",\"fixed\",\"absolute\",\"relative\",\"sticky\"],inset:[{inset:y()}],\"inset-x\":[{\"inset-x\":y()}],\"inset-y\":[{\"inset-y\":y()}],start:[{start:y()}],end:[{end:y()}],top:[{top:y()}],right:[{right:y()}],bottom:[{bottom:y()}],left:[{left:y()}],visibility:[\"visible\",\"invisible\",\"collapse\"],z:[{z:[isInteger,\"auto\",isArbitraryVariable,isArbitraryValue]}],basis:[{basis:[isFraction,\"full\",\"auto\",i,...g()]}],\"flex-direction\":[{flex:[\"row\",\"row-reverse\",\"col\",\"col-reverse\"]}],\"flex-wrap\":[{flex:[\"nowrap\",\"wrap\",\"wrap-reverse\"]}],flex:[{flex:[isNumber,isFraction,\"auto\",\"initial\",\"none\",isArbitraryValue]}],grow:[{grow:[\"\",isNumber,isArbitraryVariable,isArbitraryValue]}],shrink:[{shrink:[\"\",isNumber,isArbitraryVariable,isArbitraryValue]}],order:[{order:[isInteger,\"first\",\"last\",\"none\",isArbitraryVariable,isArbitraryValue]}],\"grid-cols\":[{\"grid-cols\":v()}],\"col-start-end\":[{col:b()}],\"col-start\":[{\"col-start\":x()}],\"col-end\":[{\"col-end\":x()}],\"grid-rows\":[{\"grid-rows\":v()}],\"row-start-end\":[{row:b()}],\"row-start\":[{\"row-start\":x()}],\"row-end\":[{\"row-end\":x()}],\"grid-flow\":[{\"grid-flow\":[\"row\",\"col\",\"dense\",\"row-dense\",\"col-dense\"]}],\"auto-cols\":[{\"auto-cols\":E()}],\"auto-rows\":[{\"auto-rows\":E()}],gap:[{gap:g()}],\"gap-x\":[{\"gap-x\":g()}],\"gap-y\":[{\"gap-y\":g()}],\"justify-content\":[{justify:[\"start\",\"end\",\"center\",\"between\",\"around\",\"evenly\",\"stretch\",\"baseline\",\"center-safe\",\"end-safe\",\"normal\"]}],\"justify-items\":[{\"justify-items\":[\"start\",\"end\",\"center\",\"stretch\",\"center-safe\",\"end-safe\",\"normal\"]}],\"justify-self\":[{\"justify-self\":[\"auto\",\"start\",\"end\",\"center\",\"stretch\",\"center-safe\",\"end-safe\"]}],\"align-content\":[{content:[\"normal\",\"start\",\"end\",\"center\",\"between\",\"around\",\"evenly\",\"stretch\",\"baseline\",\"center-safe\",\"end-safe\"]}],\"align-items\":[{items:[\"start\",\"end\",\"center\",\"stretch\",\"center-safe\",\"end-safe\",{baseline:[\"\",\"last\"]}]}],\"align-self\":[{self:[\"auto\",\"start\",\"end\",\"center\",\"stretch\",\"center-safe\",\"end-safe\",{baseline:[\"\",\"last\"]}]}],\"place-content\":[{\"place-content\":[\"start\",\"end\",\"center\",\"between\",\"around\",\"evenly\",\"stretch\",\"baseline\",\"center-safe\",\"end-safe\"]}],\"place-items\":[{\"place-items\":[\"start\",\"end\",\"center\",\"stretch\",\"center-safe\",\"end-safe\",\"baseline\"]}],\"place-self\":[{\"place-self\":[\"auto\",\"start\",\"end\",\"center\",\"stretch\",\"center-safe\",\"end-safe\"]}],p:[{p:g()}],px:[{px:g()}],py:[{py:g()}],ps:[{ps:g()}],pe:[{pe:g()}],pt:[{pt:g()}],pr:[{pr:g()}],pb:[{pb:g()}],pl:[{pl:g()}],m:[{m:k()}],mx:[{mx:k()}],my:[{my:k()}],ms:[{ms:k()}],me:[{me:k()}],mt:[{mt:k()}],mr:[{mr:k()}],mb:[{mb:k()}],ml:[{ml:k()}],\"space-x\":[{\"space-x\":g()}],\"space-x-reverse\":[\"space-x-reverse\"],\"space-y\":[{\"space-y\":g()}],\"space-y-reverse\":[\"space-y-reverse\"],size:[{size:w()}],w:[{w:[i,\"screen\",...w()]}],\"min-w\":[{\"min-w\":[i,\"screen\",\"none\",...w()]}],\"max-w\":[{\"max-w\":[i,\"screen\",\"none\",\"prose\",{screen:[l]},...w()]}],h:[{h:[\"screen\",\"lh\",...w()]}],\"min-h\":[{\"min-h\":[\"screen\",\"lh\",\"none\",...w()]}],\"max-h\":[{\"max-h\":[\"screen\",\"lh\",...w()]}],\"font-size\":[{text:[\"base\",e,isArbitraryVariableLength,isArbitraryLength]}],\"font-smoothing\":[\"antialiased\",\"subpixel-antialiased\"],\"font-style\":[\"italic\",\"not-italic\"],\"font-weight\":[{font:[a,isArbitraryVariable,isArbitraryNumber]}],\"font-stretch\":[{\"font-stretch\":[\"ultra-condensed\",\"extra-condensed\",\"condensed\",\"semi-condensed\",\"normal\",\"semi-expanded\",\"expanded\",\"extra-expanded\",\"ultra-expanded\",isPercent,isArbitraryValue]}],\"font-family\":[{font:[isArbitraryVariableFamilyName,isArbitraryValue,t]}],\"fvn-normal\":[\"normal-nums\"],\"fvn-ordinal\":[\"ordinal\"],\"fvn-slashed-zero\":[\"slashed-zero\"],\"fvn-figure\":[\"lining-nums\",\"oldstyle-nums\"],\"fvn-spacing\":[\"proportional-nums\",\"tabular-nums\"],\"fvn-fraction\":[\"diagonal-fractions\",\"stacked-fractions\"],tracking:[{tracking:[r,isArbitraryVariable,isArbitraryValue]}],\"line-clamp\":[{\"line-clamp\":[isNumber,\"none\",isArbitraryVariable,isArbitraryNumber]}],leading:[{leading:[n,...g()]}],\"list-image\":[{\"list-image\":[\"none\",isArbitraryVariable,isArbitraryValue]}],\"list-style-position\":[{list:[\"inside\",\"outside\"]}],\"list-style-type\":[{list:[\"disc\",\"decimal\",\"none\",isArbitraryVariable,isArbitraryValue]}],\"text-alignment\":[{text:[\"left\",\"center\",\"right\",\"justify\",\"start\",\"end\"]}],\"placeholder-color\":[{placeholder:C()}],\"text-color\":[{text:C()}],\"text-decoration\":[\"underline\",\"overline\",\"line-through\",\"no-underline\"],\"text-decoration-style\":[{decoration:[\"solid\",\"dashed\",\"dotted\",\"double\",\"wavy\"]}],\"text-decoration-thickness\":[{decoration:[isNumber,\"from-font\",\"auto\",isArbitraryVariable,isArbitraryLength]}],\"text-decoration-color\":[{decoration:C()}],\"underline-offset\":[{\"underline-offset\":[isNumber,\"auto\",isArbitraryVariable,isArbitraryValue]}],\"text-transform\":[\"uppercase\",\"lowercase\",\"capitalize\",\"normal-case\"],\"text-overflow\":[\"truncate\",\"text-ellipsis\",\"text-clip\"],\"text-wrap\":[{text:[\"wrap\",\"nowrap\",\"balance\",\"pretty\"]}],indent:[{indent:g()}],\"vertical-align\":[{align:[\"baseline\",\"top\",\"middle\",\"bottom\",\"text-top\",\"text-bottom\",\"sub\",\"super\",isArbitraryVariable,isArbitraryValue]}],whitespace:[{whitespace:[\"normal\",\"nowrap\",\"pre\",\"pre-line\",\"pre-wrap\",\"break-spaces\"]}],break:[{break:[\"normal\",\"words\",\"all\",\"keep\"]}],wrap:[{wrap:[\"break-word\",\"anywhere\",\"normal\"]}],hyphens:[{hyphens:[\"none\",\"manual\",\"auto\"]}],content:[{content:[\"none\",isArbitraryVariable,isArbitraryValue]}],\"bg-attachment\":[{bg:[\"fixed\",\"local\",\"scroll\"]}],\"bg-clip\":[{\"bg-clip\":[\"border\",\"padding\",\"content\",\"text\"]}],\"bg-origin\":[{\"bg-origin\":[\"border\",\"padding\",\"content\"]}],\"bg-position\":[{bg:P()}],\"bg-repeat\":[{bg:[\"no-repeat\",{repeat:[\"\",\"x\",\"y\",\"space\",\"round\"]}]}],\"bg-size\":[{bg:T()}],\"bg-image\":[{bg:[\"none\",{linear:[{to:[\"t\",\"tr\",\"r\",\"br\",\"b\",\"bl\",\"l\",\"tl\"]},isInteger,isArbitraryVariable,isArbitraryValue],radial:[\"\",isArbitraryVariable,isArbitraryValue],conic:[isInteger,isArbitraryVariable,isArbitraryValue]},isArbitraryVariableImage,isArbitraryImage]}],\"bg-color\":[{bg:C()}],\"gradient-from-pos\":[{from:_()}],\"gradient-via-pos\":[{via:_()}],\"gradient-to-pos\":[{to:_()}],\"gradient-from\":[{from:C()}],\"gradient-via\":[{via:C()}],\"gradient-to\":[{to:C()}],rounded:[{rounded:M()}],\"rounded-s\":[{\"rounded-s\":M()}],\"rounded-e\":[{\"rounded-e\":M()}],\"rounded-t\":[{\"rounded-t\":M()}],\"rounded-r\":[{\"rounded-r\":M()}],\"rounded-b\":[{\"rounded-b\":M()}],\"rounded-l\":[{\"rounded-l\":M()}],\"rounded-ss\":[{\"rounded-ss\":M()}],\"rounded-se\":[{\"rounded-se\":M()}],\"rounded-ee\":[{\"rounded-ee\":M()}],\"rounded-es\":[{\"rounded-es\":M()}],\"rounded-tl\":[{\"rounded-tl\":M()}],\"rounded-tr\":[{\"rounded-tr\":M()}],\"rounded-br\":[{\"rounded-br\":M()}],\"rounded-bl\":[{\"rounded-bl\":M()}],\"border-w\":[{border:D()}],\"border-w-x\":[{\"border-x\":D()}],\"border-w-y\":[{\"border-y\":D()}],\"border-w-s\":[{\"border-s\":D()}],\"border-w-e\":[{\"border-e\":D()}],\"border-w-t\":[{\"border-t\":D()}],\"border-w-r\":[{\"border-r\":D()}],\"border-w-b\":[{\"border-b\":D()}],\"border-w-l\":[{\"border-l\":D()}],\"divide-x\":[{\"divide-x\":D()}],\"divide-x-reverse\":[\"divide-x-reverse\"],\"divide-y\":[{\"divide-y\":D()}],\"divide-y-reverse\":[\"divide-y-reverse\"],\"border-style\":[{border:[\"solid\",\"dashed\",\"dotted\",\"double\",\"hidden\",\"none\"]}],\"divide-style\":[{divide:[\"solid\",\"dashed\",\"dotted\",\"double\",\"hidden\",\"none\"]}],\"border-color\":[{border:C()}],\"border-color-x\":[{\"border-x\":C()}],\"border-color-y\":[{\"border-y\":C()}],\"border-color-s\":[{\"border-s\":C()}],\"border-color-e\":[{\"border-e\":C()}],\"border-color-t\":[{\"border-t\":C()}],\"border-color-r\":[{\"border-r\":C()}],\"border-color-b\":[{\"border-b\":C()}],\"border-color-l\":[{\"border-l\":C()}],\"divide-color\":[{divide:C()}],\"outline-style\":[{outline:[\"solid\",\"dashed\",\"dotted\",\"double\",\"none\",\"hidden\"]}],\"outline-offset\":[{\"outline-offset\":[isNumber,isArbitraryVariable,isArbitraryValue]}],\"outline-w\":[{outline:[\"\",isNumber,isArbitraryVariableLength,isArbitraryLength]}],\"outline-color\":[{outline:C()}],shadow:[{shadow:[\"\",\"none\",p,isArbitraryVariableShadow,isArbitraryShadow]}],\"shadow-color\":[{shadow:C()}],\"inset-shadow\":[{\"inset-shadow\":[\"none\",c,isArbitraryVariableShadow,isArbitraryShadow]}],\"inset-shadow-color\":[{\"inset-shadow\":C()}],\"ring-w\":[{ring:D()}],\"ring-w-inset\":[\"ring-inset\"],\"ring-color\":[{ring:C()}],\"ring-offset-w\":[{\"ring-offset\":[isNumber,isArbitraryLength]}],\"ring-offset-color\":[{\"ring-offset\":C()}],\"inset-ring-w\":[{\"inset-ring\":D()}],\"inset-ring-color\":[{\"inset-ring\":C()}],\"text-shadow\":[{\"text-shadow\":[\"none\",u,isArbitraryVariableShadow,isArbitraryShadow]}],\"text-shadow-color\":[{\"text-shadow\":C()}],opacity:[{opacity:[isNumber,isArbitraryVariable,isArbitraryValue]}],\"mix-blend\":[{\"mix-blend\":[\"normal\",\"multiply\",\"screen\",\"overlay\",\"darken\",\"lighten\",\"color-dodge\",\"color-burn\",\"hard-light\",\"soft-light\",\"difference\",\"exclusion\",\"hue\",\"saturation\",\"color\",\"luminosity\",\"plus-darker\",\"plus-lighter\"]}],\"bg-blend\":[{\"bg-blend\":[\"normal\",\"multiply\",\"screen\",\"overlay\",\"darken\",\"lighten\",\"color-dodge\",\"color-burn\",\"hard-light\",\"soft-light\",\"difference\",\"exclusion\",\"hue\",\"saturation\",\"color\",\"luminosity\"]}],\"mask-clip\":[{\"mask-clip\":[\"border\",\"padding\",\"content\",\"fill\",\"stroke\",\"view\"]},\"mask-no-clip\"],\"mask-composite\":[{mask:[\"add\",\"subtract\",\"intersect\",\"exclude\"]}],\"mask-image-linear-pos\":[{\"mask-linear\":[isNumber]}],\"mask-image-linear-from-pos\":[{\"mask-linear-from\":R()}],\"mask-image-linear-to-pos\":[{\"mask-linear-to\":R()}],\"mask-image-linear-from-color\":[{\"mask-linear-from\":C()}],\"mask-image-linear-to-color\":[{\"mask-linear-to\":C()}],\"mask-image-t-from-pos\":[{\"mask-t-from\":R()}],\"mask-image-t-to-pos\":[{\"mask-t-to\":R()}],\"mask-image-t-from-color\":[{\"mask-t-from\":C()}],\"mask-image-t-to-color\":[{\"mask-t-to\":C()}],\"mask-image-r-from-pos\":[{\"mask-r-from\":R()}],\"mask-image-r-to-pos\":[{\"mask-r-to\":R()}],\"mask-image-r-from-color\":[{\"mask-r-from\":C()}],\"mask-image-r-to-color\":[{\"mask-r-to\":C()}],\"mask-image-b-from-pos\":[{\"mask-b-from\":R()}],\"mask-image-b-to-pos\":[{\"mask-b-to\":R()}],\"mask-image-b-from-color\":[{\"mask-b-from\":C()}],\"mask-image-b-to-color\":[{\"mask-b-to\":C()}],\"mask-image-l-from-pos\":[{\"mask-l-from\":R()}],\"mask-image-l-to-pos\":[{\"mask-l-to\":R()}],\"mask-image-l-from-color\":[{\"mask-l-from\":C()}],\"mask-image-l-to-color\":[{\"mask-l-to\":C()}],\"mask-image-x-from-pos\":[{\"mask-x-from\":R()}],\"mask-image-x-to-pos\":[{\"mask-x-to\":R()}],\"mask-image-x-from-color\":[{\"mask-x-from\":C()}],\"mask-image-x-to-color\":[{\"mask-x-to\":C()}],\"mask-image-y-from-pos\":[{\"mask-y-from\":R()}],\"mask-image-y-to-pos\":[{\"mask-y-to\":R()}],\"mask-image-y-from-color\":[{\"mask-y-from\":C()}],\"mask-image-y-to-color\":[{\"mask-y-to\":C()}],\"mask-image-radial\":[{\"mask-radial\":[isArbitraryVariable,isArbitraryValue]}],\"mask-image-radial-from-pos\":[{\"mask-radial-from\":R()}],\"mask-image-radial-to-pos\":[{\"mask-radial-to\":R()}],\"mask-image-radial-from-color\":[{\"mask-radial-from\":C()}],\"mask-image-radial-to-color\":[{\"mask-radial-to\":C()}],\"mask-image-radial-shape\":[{\"mask-radial\":[\"circle\",\"ellipse\"]}],\"mask-image-radial-size\":[{\"mask-radial\":[{closest:[\"side\",\"corner\"],farthest:[\"side\",\"corner\"]}]}],\"mask-image-radial-pos\":[{\"mask-radial-at\":[\"center\",\"top\",\"bottom\",\"left\",\"right\",\"top-left\",\"left-top\",\"top-right\",\"right-top\",\"bottom-right\",\"right-bottom\",\"bottom-left\",\"left-bottom\"]}],\"mask-image-conic-pos\":[{\"mask-conic\":[isNumber]}],\"mask-image-conic-from-pos\":[{\"mask-conic-from\":R()}],\"mask-image-conic-to-pos\":[{\"mask-conic-to\":R()}],\"mask-image-conic-from-color\":[{\"mask-conic-from\":C()}],\"mask-image-conic-to-color\":[{\"mask-conic-to\":C()}],\"mask-mode\":[{mask:[\"alpha\",\"luminance\",\"match\"]}],\"mask-origin\":[{\"mask-origin\":[\"border\",\"padding\",\"content\",\"fill\",\"stroke\",\"view\"]}],\"mask-position\":[{mask:P()}],\"mask-repeat\":[{mask:[\"no-repeat\",{repeat:[\"\",\"x\",\"y\",\"space\",\"round\"]}]}],\"mask-size\":[{mask:T()}],\"mask-type\":[{\"mask-type\":[\"alpha\",\"luminance\"]}],\"mask-image\":[{mask:[\"none\",isArbitraryVariable,isArbitraryValue]}],filter:[{filter:[\"\",\"none\",isArbitraryVariable,isArbitraryValue]}],blur:[{blur:N()}],brightness:[{brightness:[isNumber,isArbitraryVariable,isArbitraryValue]}],contrast:[{contrast:[isNumber,isArbitraryVariable,isArbitraryValue]}],\"drop-shadow\":[{\"drop-shadow\":[\"\",\"none\",d,isArbitraryVariableShadow,isArbitraryShadow]}],\"drop-shadow-color\":[{\"drop-shadow\":C()}],grayscale:[{grayscale:[\"\",isNumber,isArbitraryVariable,isArbitraryValue]}],\"hue-rotate\":[{\"hue-rotate\":[isNumber,isArbitraryVariable,isArbitraryValue]}],invert:[{invert:[\"\",isNumber,isArbitraryVariable,isArbitraryValue]}],saturate:[{saturate:[isNumber,isArbitraryVariable,isArbitraryValue]}],sepia:[{sepia:[\"\",isNumber,isArbitraryVariable,isArbitraryValue]}],\"backdrop-filter\":[{\"backdrop-filter\":[\"\",\"none\",isArbitraryVariable,isArbitraryValue]}],\"backdrop-blur\":[{\"backdrop-blur\":N()}],\"backdrop-brightness\":[{\"backdrop-brightness\":[isNumber,isArbitraryVariable,isArbitraryValue]}],\"backdrop-contrast\":[{\"backdrop-contrast\":[isNumber,isArbitraryVariable,isArbitraryValue]}],\"backdrop-grayscale\":[{\"backdrop-grayscale\":[\"\",isNumber,isArbitraryVariable,isArbitraryValue]}],\"backdrop-hue-rotate\":[{\"backdrop-hue-rotate\":[isNumber,isArbitraryVariable,isArbitraryValue]}],\"backdrop-invert\":[{\"backdrop-invert\":[\"\",isNumber,isArbitraryVariable,isArbitraryValue]}],\"backdrop-opacity\":[{\"backdrop-opacity\":[isNumber,isArbitraryVariable,isArbitraryValue]}],\"backdrop-saturate\":[{\"backdrop-saturate\":[isNumber,isArbitraryVariable,isArbitraryValue]}],\"backdrop-sepia\":[{\"backdrop-sepia\":[\"\",isNumber,isArbitraryVariable,isArbitraryValue]}],\"border-collapse\":[{border:[\"collapse\",\"separate\"]}],\"border-spacing\":[{\"border-spacing\":g()}],\"border-spacing-x\":[{\"border-spacing-x\":g()}],\"border-spacing-y\":[{\"border-spacing-y\":g()}],\"table-layout\":[{table:[\"auto\",\"fixed\"]}],caption:[{caption:[\"top\",\"bottom\"]}],transition:[{transition:[\"\",\"all\",\"colors\",\"opacity\",\"shadow\",\"transform\",\"none\",isArbitraryVariable,isArbitraryValue]}],\"transition-behavior\":[{transition:[\"normal\",\"discrete\"]}],duration:[{duration:[isNumber,\"initial\",isArbitraryVariable,isArbitraryValue]}],ease:[{ease:[\"linear\",\"initial\",m,isArbitraryVariable,isArbitraryValue]}],delay:[{delay:[isNumber,isArbitraryVariable,isArbitraryValue]}],animate:[{animate:[\"none\",L,isArbitraryVariable,isArbitraryValue]}],backface:[{backface:[\"hidden\",\"visible\"]}],perspective:[{perspective:[S,isArbitraryVariable,isArbitraryValue]}],\"perspective-origin\":[{\"perspective-origin\":W()}],rotate:[{rotate:I()}],\"rotate-x\":[{\"rotate-x\":I()}],\"rotate-y\":[{\"rotate-y\":I()}],\"rotate-z\":[{\"rotate-z\":I()}],scale:[{scale:F()}],\"scale-x\":[{\"scale-x\":F()}],\"scale-y\":[{\"scale-y\":F()}],\"scale-z\":[{\"scale-z\":F()}],\"scale-3d\":[\"scale-3d\"],skew:[{skew:O()}],\"skew-x\":[{\"skew-x\":O()}],\"skew-y\":[{\"skew-y\":O()}],transform:[{transform:[isArbitraryVariable,isArbitraryValue,\"\",\"none\",\"gpu\",\"cpu\"]}],\"transform-origin\":[{origin:W()}],\"transform-style\":[{transform:[\"3d\",\"flat\"]}],translate:[{translate:V()}],\"translate-x\":[{\"translate-x\":V()}],\"translate-y\":[{\"translate-y\":V()}],\"translate-z\":[{\"translate-z\":V()}],\"translate-none\":[\"translate-none\"],accent:[{accent:C()}],appearance:[{appearance:[\"none\",\"auto\"]}],\"caret-color\":[{caret:C()}],\"color-scheme\":[{scheme:[\"normal\",\"dark\",\"light\",\"light-dark\",\"only-dark\",\"only-light\"]}],cursor:[{cursor:[\"auto\",\"default\",\"pointer\",\"wait\",\"text\",\"move\",\"help\",\"not-allowed\",\"none\",\"context-menu\",\"progress\",\"cell\",\"crosshair\",\"vertical-text\",\"alias\",\"copy\",\"no-drop\",\"grab\",\"grabbing\",\"all-scroll\",\"col-resize\",\"row-resize\",\"n-resize\",\"e-resize\",\"s-resize\",\"w-resize\",\"ne-resize\",\"nw-resize\",\"se-resize\",\"sw-resize\",\"ew-resize\",\"ns-resize\",\"nesw-resize\",\"nwse-resize\",\"zoom-in\",\"zoom-out\",isArbitraryVariable,isArbitraryValue]}],\"field-sizing\":[{\"field-sizing\":[\"fixed\",\"content\"]}],\"pointer-events\":[{\"pointer-events\":[\"auto\",\"none\"]}],resize:[{resize:[\"none\",\"\",\"y\",\"x\"]}],\"scroll-behavior\":[{scroll:[\"auto\",\"smooth\"]}],\"scroll-m\":[{\"scroll-m\":g()}],\"scroll-mx\":[{\"scroll-mx\":g()}],\"scroll-my\":[{\"scroll-my\":g()}],\"scroll-ms\":[{\"scroll-ms\":g()}],\"scroll-me\":[{\"scroll-me\":g()}],\"scroll-mt\":[{\"scroll-mt\":g()}],\"scroll-mr\":[{\"scroll-mr\":g()}],\"scroll-mb\":[{\"scroll-mb\":g()}],\"scroll-ml\":[{\"scroll-ml\":g()}],\"scroll-p\":[{\"scroll-p\":g()}],\"scroll-px\":[{\"scroll-px\":g()}],\"scroll-py\":[{\"scroll-py\":g()}],\"scroll-ps\":[{\"scroll-ps\":g()}],\"scroll-pe\":[{\"scroll-pe\":g()}],\"scroll-pt\":[{\"scroll-pt\":g()}],\"scroll-pr\":[{\"scroll-pr\":g()}],\"scroll-pb\":[{\"scroll-pb\":g()}],\"scroll-pl\":[{\"scroll-pl\":g()}],\"snap-align\":[{snap:[\"start\",\"end\",\"center\",\"align-none\"]}],\"snap-stop\":[{snap:[\"normal\",\"always\"]}],\"snap-type\":[{snap:[\"none\",\"x\",\"y\",\"both\"]}],\"snap-strictness\":[{snap:[\"mandatory\",\"proximity\"]}],touch:[{touch:[\"auto\",\"none\",\"manipulation\"]}],\"touch-x\":[{\"touch-pan\":[\"x\",\"left\",\"right\"]}],\"touch-y\":[{\"touch-pan\":[\"y\",\"up\",\"down\"]}],\"touch-pz\":[\"touch-pinch-zoom\"],select:[{select:[\"none\",\"text\",\"all\",\"auto\"]}],\"will-change\":[{\"will-change\":[\"auto\",\"scroll\",\"contents\",\"transform\",isArbitraryVariable,isArbitraryValue]}],fill:[{fill:[\"none\",...C()]}],\"stroke-w\":[{stroke:[isNumber,isArbitraryVariableLength,isArbitraryLength,isArbitraryNumber]}],stroke:[{stroke:[\"none\",...C()]}],\"forced-color-adjust\":[{\"forced-color-adjust\":[\"auto\",\"none\"]}]},conflictingClassGroups:{overflow:[\"overflow-x\",\"overflow-y\"],overscroll:[\"overscroll-x\",\"overscroll-y\"],inset:[\"inset-x\",\"inset-y\",\"start\",\"end\",\"top\",\"right\",\"bottom\",\"left\"],\"inset-x\":[\"right\",\"left\"],\"inset-y\":[\"top\",\"bottom\"],flex:[\"basis\",\"grow\",\"shrink\"],gap:[\"gap-x\",\"gap-y\"],p:[\"px\",\"py\",\"ps\",\"pe\",\"pt\",\"pr\",\"pb\",\"pl\"],px:[\"pr\",\"pl\"],py:[\"pt\",\"pb\"],m:[\"mx\",\"my\",\"ms\",\"me\",\"mt\",\"mr\",\"mb\",\"ml\"],mx:[\"mr\",\"ml\"],my:[\"mt\",\"mb\"],size:[\"w\",\"h\"],\"font-size\":[\"leading\"],\"fvn-normal\":[\"fvn-ordinal\",\"fvn-slashed-zero\",\"fvn-figure\",\"fvn-spacing\",\"fvn-fraction\"],\"fvn-ordinal\":[\"fvn-normal\"],\"fvn-slashed-zero\":[\"fvn-normal\"],\"fvn-figure\":[\"fvn-normal\"],\"fvn-spacing\":[\"fvn-normal\"],\"fvn-fraction\":[\"fvn-normal\"],\"line-clamp\":[\"display\",\"overflow\"],rounded:[\"rounded-s\",\"rounded-e\",\"rounded-t\",\"rounded-r\",\"rounded-b\",\"rounded-l\",\"rounded-ss\",\"rounded-se\",\"rounded-ee\",\"rounded-es\",\"rounded-tl\",\"rounded-tr\",\"rounded-br\",\"rounded-bl\"],\"rounded-s\":[\"rounded-ss\",\"rounded-es\"],\"rounded-e\":[\"rounded-se\",\"rounded-ee\"],\"rounded-t\":[\"rounded-tl\",\"rounded-tr\"],\"rounded-r\":[\"rounded-tr\",\"rounded-br\"],\"rounded-b\":[\"rounded-br\",\"rounded-bl\"],\"rounded-l\":[\"rounded-tl\",\"rounded-bl\"],\"border-spacing\":[\"border-spacing-x\",\"border-spacing-y\"],\"border-w\":[\"border-w-x\",\"border-w-y\",\"border-w-s\",\"border-w-e\",\"border-w-t\",\"border-w-r\",\"border-w-b\",\"border-w-l\"],\"border-w-x\":[\"border-w-r\",\"border-w-l\"],\"border-w-y\":[\"border-w-t\",\"border-w-b\"],\"border-color\":[\"border-color-x\",\"border-color-y\",\"border-color-s\",\"border-color-e\",\"border-color-t\",\"border-color-r\",\"border-color-b\",\"border-color-l\"],\"border-color-x\":[\"border-color-r\",\"border-color-l\"],\"border-color-y\":[\"border-color-t\",\"border-color-b\"],translate:[\"translate-x\",\"translate-y\",\"translate-none\"],\"translate-none\":[\"translate\",\"translate-x\",\"translate-y\",\"translate-z\"],\"scroll-m\":[\"scroll-mx\",\"scroll-my\",\"scroll-ms\",\"scroll-me\",\"scroll-mt\",\"scroll-mr\",\"scroll-mb\",\"scroll-ml\"],\"scroll-mx\":[\"scroll-mr\",\"scroll-ml\"],\"scroll-my\":[\"scroll-mt\",\"scroll-mb\"],\"scroll-p\":[\"scroll-px\",\"scroll-py\",\"scroll-ps\",\"scroll-pe\",\"scroll-pt\",\"scroll-pr\",\"scroll-pb\",\"scroll-pl\"],\"scroll-px\":[\"scroll-pr\",\"scroll-pl\"],\"scroll-py\":[\"scroll-pt\",\"scroll-pb\"],touch:[\"touch-x\",\"touch-y\",\"touch-pz\"],\"touch-x\":[\"touch\"],\"touch-y\":[\"touch\"],\"touch-pz\":[\"touch\"]},conflictingClassGroupModifiers:{\"font-size\":[\"leading\"]},orderSensitiveModifiers:[\"*\",\"**\",\"after\",\"backdrop\",\"before\",\"details-content\",\"file\",\"first-letter\",\"first-line\",\"marker\",\"placeholder\",\"selection\"]}},twMerge=createTailwindMerge(getDefaultConfig);function cn$1(...A){return twMerge(clsx(A))}const buttonVariants=cva(\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg text-sm font-medium transition-all outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0\",{variants:{variant:{default:\"bg-primary text-primary-foreground hover:bg-primary/90\",destructive:\"bg-destructive text-white hover:bg-destructive/90\",outline:\"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground\",secondary:\"bg-secondary text-secondary-foreground hover:bg-secondary/80\",ghost:\"hover:bg-accent hover:text-accent-foreground\",link:\"text-primary underline-offset-4 hover:underline\"},size:{default:\"h-10 px-4 py-2 has-[>svg]:px-3\",sm:\"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5\",lg:\"h-10 rounded-md px-6 has-[>svg]:px-4\",icon:\"size-9 min-touch-target\",\"icon-sm\":\"size-8 min-touch-target\",\"icon-lg\":\"size-10 min-touch-target\"}},defaultVariants:{variant:\"default\",size:\"default\"}}),Button=reactExports.forwardRef(({className:A,variant:t,size:e,asChild:a=!1,...r},n)=>{const l=a?Slot$1:\"button\";return jsxRuntimeExports.jsx(l,{className:cn$1(buttonVariants({variant:t,size:e,className:A})),\"data-size\":e,ref:n,...r})});Button.displayName=\"Button\";const typeConfig={success:{icon:CircleCheckBig,iconClass:\"text-green-500\",titleKey:\"ui.common.success\"},error:{icon:CircleX,iconClass:\"text-red-500\",titleKey:\"ui.common.error\"},warning:{icon:TriangleAlert,iconClass:\"text-yellow-500\",titleKey:\"ui.common.warning\"},info:{icon:Info,iconClass:\"text-blue-500\",titleKey:\"ui.common.info\"},confirm:{icon:CircleHelp,iconClass:\"text-amber-500\",titleKey:\"ui.common.confirm\"}},ConfirmDialog=({isOpen:A,onCancel:t,onConfirm:e,message:a,type:r=\"error\",children:n,className:l})=>{const{t:i}=useTranslation(),o=typeConfig[r in typeConfig?r:\"error\"],s=o.icon;reactExports.useEffect(()=>{const a=a=>{A&&\"Enter\"===a.key&&(a.preventDefault(),\"confirm\"===r&&e?e():t())};return window.addEventListener(\"keydown\",a),()=>window.removeEventListener(\"keydown\",a)},[A,r,e,t]);return jsxRuntimeExports.jsx(Root2$2,{open:A,onOpenChange:A=>!A&&t(),children:jsxRuntimeExports.jsxs(Portal2$1,{children:[jsxRuntimeExports.jsx(Overlay2,{className:cn$1(\"fixed inset-0 z-50 bg-black/60 backdrop-blur-sm\",\"data-[state=open]:animate-in data-[state=closed]:animate-out\",\"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\")}),jsxRuntimeExports.jsxs(Content2$2,{className:cn$1(\"fixed left-[50%] top-[50%] z-50 grid w-[calc(100vw-2rem)] translate-x-[-50%] translate-y-[-50%]\",\"gap-4 border bg-background p-4 sm:p-6 shadow-2xl duration-200 rounded-lg sm:rounded-xl\",\"data-[state=open]:animate-in data-[state=closed]:animate-out\",\"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\"data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%]\",\"data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]\",l||\"max-w-md\"),children:[jsxRuntimeExports.jsxs(\"div\",{className:\"flex flex-col space-y-2 text-center sm:text-left\",children:[jsxRuntimeExports.jsxs(Title2,{className:\"flex items-center justify-center sm:justify-start gap-2 text-base sm:text-lg font-semibold leading-none tracking-tight\",children:[jsxRuntimeExports.jsx(s,{className:cn$1(\"h-5 w-5\",o.iconClass)}),jsxRuntimeExports.jsx(\"span\",{className:o.iconClass,children:i(o.titleKey)})]}),jsxRuntimeExports.jsx(Description2,{className:\"text-sm text-muted-foreground leading-relaxed\",children:a})]}),n&&jsxRuntimeExports.jsx(\"div\",{className:\"py-2\",children:n}),jsxRuntimeExports.jsx(\"div\",{className:\"flex flex-col-reverse sm:flex-row sm:justify-end gap-2 sm:gap-0 sm:space-x-2\",children:\"confirm\"===r?jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(Cancel,{className:cn$1(buttonVariants({variant:\"outline\"}),\"w-full sm:w-auto rounded-xl\"),onClick:t,children:i(\"ui.common.cancel\")}),jsxRuntimeExports.jsx(Action,{className:cn$1(buttonVariants({variant:\"destructive\"}),\"w-full sm:w-auto rounded-xl\"),onClick:e,children:i(\"ui.common.confirm\")})]}):jsxRuntimeExports.jsx(Action,{className:cn$1(buttonVariants({variant:\"default\"}),\"w-full sm:w-auto rounded-xl\"),onClick:()=>{\"confirm\"!==r&&e&&e(),t()},children:i(\"ui.common.close\")})})]})]})})},ConfirmDialogContext=reactExports.createContext(void 0),ConfirmDialogProvider=({children:A})=>{const[t,e]=reactExports.useState(!1),[a,r]=reactExports.useState(\"\"),[n,l]=reactExports.useState(\"confirm\"),[i,o]=reactExports.useState(),[s,p]=reactExports.useState(),[c,u]=reactExports.useState(),d=()=>{e(!1)},h=React.useCallback((A,t,a,n,i)=>{r(A),l(t||\"confirm\"),o(()=>a),p(n),u(i),e(!0)},[]);return jsxRuntimeExports.jsxs(ConfirmDialogContext.Provider,{value:{isDialogOpen:t,message:a,type:n,openConfirmDialog:h,closeDialog:d},children:[A,jsxRuntimeExports.jsx(ConfirmDialog,{isOpen:t,onCancel:d,onConfirm:()=>{i&&i(),e(!1)},message:a,type:n,className:c,children:s})]})},useConfirmDialog=()=>{const A=reactExports.useContext(ConfirmDialogContext);if(!A)throw new Error(\"useConfirmDialog must be used within a ConfirmDialogProvider\");return A},ThemeContext=reactExports.createContext(void 0),STORAGE_KEY=\"theme\",LIGHT_THEME_COLOR=\"#eae9e3\",DARK_THEME_COLOR=\"#413a2c\";function getSystemTheme(){return\"undefined\"==typeof window?\"light\":window.matchMedia(\"(prefers-color-scheme: dark)\").matches?\"dark\":\"light\"}function getAutoTheme(){const A=(new Date).getHours();return A>=18||A<6?\"dark\":\"light\"}function updateMetaThemeColor(A){let t=document.querySelector('meta[name=\"theme-color\"]');t||(t=document.createElement(\"meta\"),t.setAttribute(\"name\",\"theme-color\"),document.head.appendChild(t)),t.setAttribute(\"content\",\"dark\"===A?DARK_THEME_COLOR:LIGHT_THEME_COLOR)}function applyTheme(A){const t=window.document.documentElement;t.classList.remove(\"light\",\"dark\"),t.classList.add(A),updateMetaThemeColor(A)}const ThemeProvider=({children:A,defaultTheme:t=\"system\",storageKey:e=STORAGE_KEY})=>{const[a,r]=reactExports.useState(()=>{if(\"undefined\"==typeof window)return t;try{const A=localStorage.getItem(e);if(\"light\"===A||\"dark\"===A||\"system\"===A||\"auto\"===A)return A}catch{}return t}),[n,l]=reactExports.useState(()=>\"system\"===a?getSystemTheme():\"auto\"===a?getAutoTheme():a);reactExports.useEffect(()=>{let A;A=\"system\"===a?getSystemTheme():\"auto\"===a?getAutoTheme():a,l(A),applyTheme(A)},[a]),reactExports.useEffect(()=>{if(\"auto\"!==a)return;const A=setInterval(()=>{const A=getAutoTheme();A!==n&&(l(A),applyTheme(A))},6e4);return()=>clearInterval(A)},[a,n]),reactExports.useEffect(()=>{try{localStorage.setItem(e,a)}catch{}},[a,e]),reactExports.useEffect(()=>{if(\"system\"!==a)return;const A=window.matchMedia(\"(prefers-color-scheme: dark)\"),t=A=>{const t=A.matches?\"dark\":\"light\";l(t),applyTheme(t)};return A.addEventListener(\"change\",t),()=>A.removeEventListener(\"change\",t)},[a]);return jsxRuntimeExports.jsx(ThemeContext.Provider,{value:{theme:a,setTheme:A=>{r(A)},resolvedTheme:n},children:A})},useTheme=()=>{const A=reactExports.useContext(ThemeContext);if(!A)throw new Error(\"useTheme must be used within a ThemeProvider\");return A};function createJSONStorage(A,t){let e;try{e=A()}catch(a){return}return{getItem:A=>{var t;const a=A=>null===A?null:JSON.parse(A,void 0),r=null!=(t=e.getItem(A))?t:null;return r instanceof Promise?r.then(a):a(r)},setItem:(A,t)=>e.setItem(A,JSON.stringify(t,void 0)),removeItem:A=>e.removeItem(A)}}const toThenable=A=>t=>{try{const e=A(t);return e instanceof Promise?e:{then:A=>toThenable(A)(e),catch(A){return this}}}catch(e){return{then(A){return this},catch:A=>toThenable(A)(e)}}},persistImpl=(A,t)=>(e,a,r)=>{let n={storage:createJSONStorage(()=>localStorage),partialize:A=>A,version:0,merge:(A,t)=>({...t,...A}),...t},l=!1;const i=new Set,o=new Set;let s=n.storage;if(!s)return A((...A)=>{e(...A)},a,r);const p=()=>{const A=n.partialize({...a()});return s.setItem(n.name,{state:A,version:n.version})},c=r.setState;r.setState=(A,t)=>(c(A,t),p());const u=A((...A)=>(e(...A),p()),a,r);let d;r.getInitialState=()=>u;const h=()=>{var A,t;if(!s)return;l=!1,i.forEach(A=>{var t;return A(null!=(t=a())?t:u)});const r=(null==(t=n.onRehydrateStorage)?void 0:t.call(n,null!=(A=a())?A:u))||void 0;return toThenable(s.getItem.bind(s))(n.name).then(A=>{if(A){if(\"number\"!=typeof A.version||A.version===n.version)return[!1,A.state];if(n.migrate){const t=n.migrate(A.state,A.version);return t instanceof Promise?t.then(A=>[!0,A]):[!0,t]}}return[!1,void 0]}).then(A=>{var t;const[r,l]=A;if(d=n.merge(l,null!=(t=a())?t:u),e(d,!0),r)return p()}).then(()=>{null==r||r(d,void 0),d=a(),l=!0,o.forEach(A=>A(d))}).catch(A=>{null==r||r(void 0,A)})};return r.persist={setOptions:A=>{n={...n,...A},A.storage&&(s=A.storage)},clearStorage:()=>{null==s||s.removeItem(n.name)},getOptions:()=>n,rehydrate:()=>h(),hasHydrated:()=>l,onHydrate:A=>(i.add(A),()=>{i.delete(A)}),onFinishHydration:A=>(o.add(A),()=>{o.delete(A)})},n.skipHydration||h(),d||u},persist=persistImpl,createStoreImpl=A=>{let t;const e=new Set,a=(A,a)=>{const r=\"function\"==typeof A?A(t):A;if(!Object.is(r,t)){const A=t;t=(null!=a?a:\"object\"!=typeof r||null===r)?r:Object.assign({},t,r),e.forEach(e=>e(t,A))}},r=()=>t,n={setState:a,getState:r,getInitialState:()=>l,subscribe:A=>(e.add(A),()=>e.delete(A))},l=t=A(a,r,n);return n},createStore=A=>A?createStoreImpl(A):createStoreImpl,identity=A=>A;function useStore(A,t=identity){const e=React.useSyncExternalStore(A.subscribe,React.useCallback(()=>t(A.getState()),[A,t]),React.useCallback(()=>t(A.getInitialState()),[A,t]));return React.useDebugValue(e),e}const createImpl=A=>{const t=createStore(A),e=A=>useStore(t,A);return Object.assign(e,t),e},create=A=>createImpl,COLOR_SCHEMES=[{value:\"default\",label:\"ui.settings.colorScheme.default\",color:\"#34322d\"},{value:\"green\",label:\"ui.settings.colorScheme.green\",color:\"#4ade80\"},{value:\"blue\",label:\"ui.settings.colorScheme.blue\",color:\"#2563EB\"},{value:\"sky-blue\",label:\"ui.settings.colorScheme.skyBlue\",color:\"#9fc2e2\"},{value:\"purple\",label:\"ui.settings.colorScheme.purple\",color:\"#a78bfa\"},{value:\"orange\",label:\"ui.settings.colorScheme.orange\",color:\"#fb923c\"},{value:\"rose\",label:\"ui.settings.colorScheme.rose\",color:\"#fb7185\"},{value:\"teal\",label:\"ui.settings.colorScheme.teal\",color:\"#2dd4bf\"}],createSettingsStore=A=>create()(persist(A=>({toastPosition:\"top-center\",setToastPosition:t=>A({toastPosition:t}),colorScheme:\"default\",setColorScheme:t=>{document.documentElement.setAttribute(\"data-color-scheme\",t),A({colorScheme:t})}}),{name:A,onRehydrateStorage:()=>A=>{(null==A?void 0:A.colorScheme)&&document.documentElement.setAttribute(\"data-color-scheme\",A.colorScheme)}})),useSettingsStore=createSettingsStore(\"app-settings\"),useShareSettingsStore=createSettingsStore(\"share-settings\");function __insertCSS(A){if(\"undefined\"==typeof document)return;let t=document.head||document.getElementsByTagName(\"head\")[0],e=document.createElement(\"style\");e.type=\"text/css\",t.appendChild(e),e.styleSheet?e.styleSheet.cssText=A:e.appendChild(document.createTextNode(A))}const getAsset=A=>{switch(A){case\"success\":return SuccessIcon;case\"info\":return InfoIcon;case\"warning\":return WarningIcon;case\"error\":return ErrorIcon;default:return null}},bars=Array(12).fill(0),Loader=({visible:A,className:t})=>React.createElement(\"div\",{className:[\"sonner-loading-wrapper\",t].filter(Boolean).join(\" \"),\"data-visible\":A},React.createElement(\"div\",{className:\"sonner-spinner\"},bars.map((A,t)=>React.createElement(\"div\",{className:\"sonner-loading-bar\",key:`spinner-bar-${t}`})))),SuccessIcon=React.createElement(\"svg\",{xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 20 20\",fill:\"currentColor\",height:\"20\",width:\"20\"},React.createElement(\"path\",{fillRule:\"evenodd\",d:\"M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z\",clipRule:\"evenodd\"})),WarningIcon=React.createElement(\"svg\",{xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 24 24\",fill:\"currentColor\",height:\"20\",width:\"20\"},React.createElement(\"path\",{fillRule:\"evenodd\",d:\"M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003zM12 8.25a.75.75 0 01.75.75v3.75a.75.75 0 01-1.5 0V9a.75.75 0 01.75-.75zm0 8.25a.75.75 0 100-1.5.75.75 0 000 1.5z\",clipRule:\"evenodd\"})),InfoIcon=React.createElement(\"svg\",{xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 20 20\",fill:\"currentColor\",height:\"20\",width:\"20\"},React.createElement(\"path\",{fillRule:\"evenodd\",d:\"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z\",clipRule:\"evenodd\"})),ErrorIcon=React.createElement(\"svg\",{xmlns:\"http://www.w3.org/2000/svg\",viewBox:\"0 0 20 20\",fill:\"currentColor\",height:\"20\",width:\"20\"},React.createElement(\"path\",{fillRule:\"evenodd\",d:\"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z\",clipRule:\"evenodd\"})),CloseIcon=React.createElement(\"svg\",{xmlns:\"http://www.w3.org/2000/svg\",width:\"12\",height:\"12\",viewBox:\"0 0 24 24\",fill:\"none\",stroke:\"currentColor\",strokeWidth:\"1.5\",strokeLinecap:\"round\",strokeLinejoin:\"round\"},React.createElement(\"line\",{x1:\"18\",y1:\"6\",x2:\"6\",y2:\"18\"}),React.createElement(\"line\",{x1:\"6\",y1:\"6\",x2:\"18\",y2:\"18\"})),useIsDocumentHidden=()=>{const[A,t]=React.useState(document.hidden);return React.useEffect(()=>{const A=()=>{t(document.hidden)};return document.addEventListener(\"visibilitychange\",A),()=>window.removeEventListener(\"visibilitychange\",A)},[]),A};let toastsCounter=1;class Observer{constructor(){this.subscribe=A=>(this.subscribers.push(A),()=>{const t=this.subscribers.indexOf(A);this.subscribers.splice(t,1)}),this.publish=A=>{this.subscribers.forEach(t=>t(A))},this.addToast=A=>{this.publish(A),this.toasts=[...this.toasts,A]},this.create=A=>{var t;const{message:e,...a}=A,r=\"number\"==typeof(null==A?void 0:A.id)||(null==(t=A.id)?void 0:t.length)>0?A.id:toastsCounter++,n=this.toasts.find(A=>A.id===r),l=void 0===A.dismissible||A.dismissible;return this.dismissedToasts.has(r)&&this.dismissedToasts.delete(r),n?this.toasts=this.toasts.map(t=>t.id===r?(this.publish({...t,...A,id:r,title:e}),{...t,...A,id:r,dismissible:l,title:e}):t):this.addToast({title:e,...a,dismissible:l,id:r}),r},this.dismiss=A=>(A?(this.dismissedToasts.add(A),requestAnimationFrame(()=>this.subscribers.forEach(t=>t({id:A,dismiss:!0})))):this.toasts.forEach(A=>{this.subscribers.forEach(t=>t({id:A.id,dismiss:!0}))}),A),this.message=(A,t)=>this.create({...t,message:A}),this.error=(A,t)=>this.create({...t,message:A,type:\"error\"}),this.success=(A,t)=>this.create({...t,type:\"success\",message:A}),this.info=(A,t)=>this.create({...t,type:\"info\",message:A}),this.warning=(A,t)=>this.create({...t,type:\"warning\",message:A}),this.loading=(A,t)=>this.create({...t,type:\"loading\",message:A}),this.promise=(A,t)=>{if(!t)return;let e;void 0!==t.loading&&(e=this.create({...t,promise:A,type:\"loading\",message:t.loading,description:\"function\"!=typeof t.description?t.description:void 0}));const a=Promise.resolve(A instanceof Function?A():A);let r,n=void 0!==e;const l=a.then(async A=>{r=[\"resolve\",A];if(React.isValidElement(A))n=!1,this.create({id:e,type:\"default\",message:A});else if(isHttpResponse(A)&&!A.ok){n=!1;const a=\"function\"==typeof t.error?await t.error(`HTTP error! status: ${A.status}`):t.error,r=\"function\"==typeof t.description?await t.description(`HTTP error! status: ${A.status}`):t.description,l=\"object\"==typeof a&&!React.isValidElement(a)?a:{message:a};this.create({id:e,type:\"error\",description:r,...l})}else if(A instanceof Error){n=!1;const a=\"function\"==typeof t.error?await t.error(A):t.error,r=\"function\"==typeof t.description?await t.description(A):t.description,l=\"object\"==typeof a&&!React.isValidElement(a)?a:{message:a};this.create({id:e,type:\"error\",description:r,...l})}else if(void 0!==t.success){n=!1;const a=\"function\"==typeof t.success?await t.success(A):t.success,r=\"function\"==typeof t.description?await t.description(A):t.description,l=\"object\"==typeof a&&!React.isValidElement(a)?a:{message:a};this.create({id:e,type:\"success\",description:r,...l})}}).catch(async A=>{if(r=[\"reject\",A],void 0!==t.error){n=!1;const a=\"function\"==typeof t.error?await t.error(A):t.error,r=\"function\"==typeof t.description?await t.description(A):t.description,l=\"object\"==typeof a&&!React.isValidElement(a)?a:{message:a};this.create({id:e,type:\"error\",description:r,...l})}}).finally(()=>{n&&(this.dismiss(e),e=void 0),null==t.finally||t.finally.call(t)}),i=()=>new Promise((A,t)=>l.then(()=>\"reject\"===r[0]?t(r[1]):A(r[1])).catch(t));return\"string\"!=typeof e&&\"number\"!=typeof e?{unwrap:i}:Object.assign(e,{unwrap:i})},this.custom=(A,t)=>{const e=(null==t?void 0:t.id)||toastsCounter++;return this.create({jsx:A(e),id:e,...t}),e},this.getActiveToasts=()=>this.toasts.filter(A=>!this.dismissedToasts.has(A.id)),this.subscribers=[],this.toasts=[],this.dismissedToasts=new Set}}const ToastState=new Observer,toastFunction=(A,t)=>{const e=(null==t?void 0:t.id)||toastsCounter++;return ToastState.addToast({title:A,...t,id:e}),e},isHttpResponse=A=>A&&\"object\"==typeof A&&\"ok\"in A&&\"boolean\"==typeof A.ok&&\"status\"in A&&\"number\"==typeof A.status,basicToast=toastFunction,getHistory=()=>ToastState.toasts,getToasts=()=>ToastState.getActiveToasts(),toast$1=Object.assign(basicToast,{success:ToastState.success,info:ToastState.info,warning:ToastState.warning,error:ToastState.error,custom:ToastState.custom,message:ToastState.message,promise:ToastState.promise,dismiss:ToastState.dismiss,loading:ToastState.loading},{getHistory:getHistory,getToasts:getToasts});function isAction(A){return void 0!==A.label}__insertCSS(\"[data-sonner-toaster][dir=ltr],html[dir=ltr]{--toast-icon-margin-start:-3px;--toast-icon-margin-end:4px;--toast-svg-margin-start:-1px;--toast-svg-margin-end:0px;--toast-button-margin-start:auto;--toast-button-margin-end:0;--toast-close-button-start:0;--toast-close-button-end:unset;--toast-close-button-transform:translate(-35%, -35%)}[data-sonner-toaster][dir=rtl],html[dir=rtl]{--toast-icon-margin-start:4px;--toast-icon-margin-end:-3px;--toast-svg-margin-start:0px;--toast-svg-margin-end:-1px;--toast-button-margin-start:0;--toast-button-margin-end:auto;--toast-close-button-start:unset;--toast-close-button-end:0;--toast-close-button-transform:translate(35%, -35%)}[data-sonner-toaster]{position:fixed;width:var(--width);font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;--gray1:hsl(0, 0%, 99%);--gray2:hsl(0, 0%, 97.3%);--gray3:hsl(0, 0%, 95.1%);--gray4:hsl(0, 0%, 93%);--gray5:hsl(0, 0%, 90.9%);--gray6:hsl(0, 0%, 88.7%);--gray7:hsl(0, 0%, 85.8%);--gray8:hsl(0, 0%, 78%);--gray9:hsl(0, 0%, 56.1%);--gray10:hsl(0, 0%, 52.3%);--gray11:hsl(0, 0%, 43.5%);--gray12:hsl(0, 0%, 9%);--border-radius:8px;box-sizing:border-box;padding:0;margin:0;list-style:none;outline:0;z-index:999999999;transition:transform .4s ease}@media (hover:none) and (pointer:coarse){[data-sonner-toaster][data-lifted=true]{transform:none}}[data-sonner-toaster][data-x-position=right]{right:var(--offset-right)}[data-sonner-toaster][data-x-position=left]{left:var(--offset-left)}[data-sonner-toaster][data-x-position=center]{left:50%;transform:translateX(-50%)}[data-sonner-toaster][data-y-position=top]{top:var(--offset-top)}[data-sonner-toaster][data-y-position=bottom]{bottom:var(--offset-bottom)}[data-sonner-toast]{--y:translateY(100%);--lift-amount:calc(var(--lift) * var(--gap));z-index:var(--z-index);position:absolute;opacity:0;transform:var(--y);touch-action:none;transition:transform .4s,opacity .4s,height .4s,box-shadow .2s;box-sizing:border-box;outline:0;overflow-wrap:anywhere}[data-sonner-toast][data-styled=true]{padding:16px;background:var(--normal-bg);border:1px solid var(--normal-border);color:var(--normal-text);border-radius:var(--border-radius);box-shadow:0 4px 12px rgba(0,0,0,.1);width:var(--width);font-size:13px;display:flex;align-items:center;gap:6px}[data-sonner-toast]:focus-visible{box-shadow:0 4px 12px rgba(0,0,0,.1),0 0 0 2px rgba(0,0,0,.2)}[data-sonner-toast][data-y-position=top]{top:0;--y:translateY(-100%);--lift:1;--lift-amount:calc(1 * var(--gap))}[data-sonner-toast][data-y-position=bottom]{bottom:0;--y:translateY(100%);--lift:-1;--lift-amount:calc(var(--lift) * var(--gap))}[data-sonner-toast][data-styled=true] [data-description]{font-weight:400;line-height:1.4;color:#3f3f3f}[data-rich-colors=true][data-sonner-toast][data-styled=true] [data-description]{color:inherit}[data-sonner-toaster][data-sonner-theme=dark] [data-description]{color:#e8e8e8}[data-sonner-toast][data-styled=true] [data-title]{font-weight:500;line-height:1.5;color:inherit}[data-sonner-toast][data-styled=true] [data-icon]{display:flex;height:16px;width:16px;position:relative;justify-content:flex-start;align-items:center;flex-shrink:0;margin-left:var(--toast-icon-margin-start);margin-right:var(--toast-icon-margin-end)}[data-sonner-toast][data-promise=true] [data-icon]>svg{opacity:0;transform:scale(.8);transform-origin:center;animation:sonner-fade-in .3s ease forwards}[data-sonner-toast][data-styled=true] [data-icon]>*{flex-shrink:0}[data-sonner-toast][data-styled=true] [data-icon] svg{margin-left:var(--toast-svg-margin-start);margin-right:var(--toast-svg-margin-end)}[data-sonner-toast][data-styled=true] [data-content]{display:flex;flex-direction:column;gap:2px}[data-sonner-toast][data-styled=true] [data-button]{border-radius:4px;padding-left:8px;padding-right:8px;height:24px;font-size:12px;color:var(--normal-bg);background:var(--normal-text);margin-left:var(--toast-button-margin-start);margin-right:var(--toast-button-margin-end);border:none;font-weight:500;cursor:pointer;outline:0;display:flex;align-items:center;flex-shrink:0;transition:opacity .4s,box-shadow .2s}[data-sonner-toast][data-styled=true] [data-button]:focus-visible{box-shadow:0 0 0 2px rgba(0,0,0,.4)}[data-sonner-toast][data-styled=true] [data-button]:first-of-type{margin-left:var(--toast-button-margin-start);margin-right:var(--toast-button-margin-end)}[data-sonner-toast][data-styled=true] [data-cancel]{color:var(--normal-text);background:rgba(0,0,0,.08)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-styled=true] [data-cancel]{background:rgba(255,255,255,.3)}[data-sonner-toast][data-styled=true] [data-close-button]{position:absolute;left:var(--toast-close-button-start);right:var(--toast-close-button-end);top:0;height:20px;width:20px;display:flex;justify-content:center;align-items:center;padding:0;color:var(--gray12);background:var(--normal-bg);border:1px solid var(--gray4);transform:var(--toast-close-button-transform);border-radius:50%;cursor:pointer;z-index:1;transition:opacity .1s,background .2s,border-color .2s}[data-sonner-toast][data-styled=true] [data-close-button]:focus-visible{box-shadow:0 4px 12px rgba(0,0,0,.1),0 0 0 2px rgba(0,0,0,.2)}[data-sonner-toast][data-styled=true] [data-disabled=true]{cursor:not-allowed}[data-sonner-toast][data-styled=true]:hover [data-close-button]:hover{background:var(--gray2);border-color:var(--gray5)}[data-sonner-toast][data-swiping=true]::before{content:'';position:absolute;left:-100%;right:-100%;height:100%;z-index:-1}[data-sonner-toast][data-y-position=top][data-swiping=true]::before{bottom:50%;transform:scaleY(3) translateY(50%)}[data-sonner-toast][data-y-position=bottom][data-swiping=true]::before{top:50%;transform:scaleY(3) translateY(-50%)}[data-sonner-toast][data-swiping=false][data-removed=true]::before{content:'';position:absolute;inset:0;transform:scaleY(2)}[data-sonner-toast][data-expanded=true]::after{content:'';position:absolute;left:0;height:calc(var(--gap) + 1px);bottom:100%;width:100%}[data-sonner-toast][data-mounted=true]{--y:translateY(0);opacity:1}[data-sonner-toast][data-expanded=false][data-front=false]{--scale:var(--toasts-before) * 0.05 + 1;--y:translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(-1 * var(--scale)));height:var(--front-toast-height)}[data-sonner-toast]>*{transition:opacity .4s}[data-sonner-toast][data-x-position=right]{right:0}[data-sonner-toast][data-x-position=left]{left:0}[data-sonner-toast][data-expanded=false][data-front=false][data-styled=true]>*{opacity:0}[data-sonner-toast][data-visible=false]{opacity:0;pointer-events:none}[data-sonner-toast][data-mounted=true][data-expanded=true]{--y:translateY(calc(var(--lift) * var(--offset)));height:var(--initial-height)}[data-sonner-toast][data-removed=true][data-front=true][data-swipe-out=false]{--y:translateY(calc(var(--lift) * -100%));opacity:0}[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=true]{--y:translateY(calc(var(--lift) * var(--offset) + var(--lift) * -100%));opacity:0}[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=false]{--y:translateY(40%);opacity:0;transition:transform .5s,opacity .2s}[data-sonner-toast][data-removed=true][data-front=false]::before{height:calc(var(--initial-height) + 20%)}[data-sonner-toast][data-swiping=true]{transform:var(--y) translateY(var(--swipe-amount-y,0)) translateX(var(--swipe-amount-x,0));transition:none}[data-sonner-toast][data-swiped=true]{user-select:none}[data-sonner-toast][data-swipe-out=true][data-y-position=bottom],[data-sonner-toast][data-swipe-out=true][data-y-position=top]{animation-duration:.2s;animation-timing-function:ease-out;animation-fill-mode:forwards}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=left]{animation-name:swipe-out-left}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=right]{animation-name:swipe-out-right}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=up]{animation-name:swipe-out-up}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=down]{animation-name:swipe-out-down}@keyframes swipe-out-left{from{transform:var(--y) translateX(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translateX(calc(var(--swipe-amount-x) - 100%));opacity:0}}@keyframes swipe-out-right{from{transform:var(--y) translateX(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translateX(calc(var(--swipe-amount-x) + 100%));opacity:0}}@keyframes swipe-out-up{from{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) - 100%));opacity:0}}@keyframes swipe-out-down{from{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) + 100%));opacity:0}}@media (max-width:600px){[data-sonner-toaster]{position:fixed;right:var(--mobile-offset-right);left:var(--mobile-offset-left);width:100%}[data-sonner-toaster][dir=rtl]{left:calc(var(--mobile-offset-left) * -1)}[data-sonner-toaster] [data-sonner-toast]{left:0;right:0;width:calc(100% - var(--mobile-offset-left) * 2)}[data-sonner-toaster][data-x-position=left]{left:var(--mobile-offset-left)}[data-sonner-toaster][data-y-position=bottom]{bottom:var(--mobile-offset-bottom)}[data-sonner-toaster][data-y-position=top]{top:var(--mobile-offset-top)}[data-sonner-toaster][data-x-position=center]{left:var(--mobile-offset-left);right:var(--mobile-offset-right);transform:none}}[data-sonner-toaster][data-sonner-theme=light]{--normal-bg:#fff;--normal-border:var(--gray4);--normal-text:var(--gray12);--success-bg:hsl(143, 85%, 96%);--success-border:hsl(145, 92%, 87%);--success-text:hsl(140, 100%, 27%);--info-bg:hsl(208, 100%, 97%);--info-border:hsl(221, 91%, 93%);--info-text:hsl(210, 92%, 45%);--warning-bg:hsl(49, 100%, 97%);--warning-border:hsl(49, 91%, 84%);--warning-text:hsl(31, 92%, 45%);--error-bg:hsl(359, 100%, 97%);--error-border:hsl(359, 100%, 94%);--error-text:hsl(360, 100%, 45%)}[data-sonner-toaster][data-sonner-theme=light] [data-sonner-toast][data-invert=true]{--normal-bg:#000;--normal-border:hsl(0, 0%, 20%);--normal-text:var(--gray1)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-invert=true]{--normal-bg:#fff;--normal-border:var(--gray3);--normal-text:var(--gray12)}[data-sonner-toaster][data-sonner-theme=dark]{--normal-bg:#000;--normal-bg-hover:hsl(0, 0%, 12%);--normal-border:hsl(0, 0%, 20%);--normal-border-hover:hsl(0, 0%, 25%);--normal-text:var(--gray1);--success-bg:hsl(150, 100%, 6%);--success-border:hsl(147, 100%, 12%);--success-text:hsl(150, 86%, 65%);--info-bg:hsl(215, 100%, 6%);--info-border:hsl(223, 43%, 17%);--info-text:hsl(216, 87%, 65%);--warning-bg:hsl(64, 100%, 6%);--warning-border:hsl(60, 100%, 9%);--warning-text:hsl(46, 87%, 65%);--error-bg:hsl(358, 76%, 10%);--error-border:hsl(357, 89%, 16%);--error-text:hsl(358, 100%, 81%)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button]{background:var(--normal-bg);border-color:var(--normal-border);color:var(--normal-text)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button]:hover{background:var(--normal-bg-hover);border-color:var(--normal-border-hover)}[data-rich-colors=true][data-sonner-toast][data-type=success]{background:var(--success-bg);border-color:var(--success-border);color:var(--success-text)}[data-rich-colors=true][data-sonner-toast][data-type=success] [data-close-button]{background:var(--success-bg);border-color:var(--success-border);color:var(--success-text)}[data-rich-colors=true][data-sonner-toast][data-type=info]{background:var(--info-bg);border-color:var(--info-border);color:var(--info-text)}[data-rich-colors=true][data-sonner-toast][data-type=info] [data-close-button]{background:var(--info-bg);border-color:var(--info-border);color:var(--info-text)}[data-rich-colors=true][data-sonner-toast][data-type=warning]{background:var(--warning-bg);border-color:var(--warning-border);color:var(--warning-text)}[data-rich-colors=true][data-sonner-toast][data-type=warning] [data-close-button]{background:var(--warning-bg);border-color:var(--warning-border);color:var(--warning-text)}[data-rich-colors=true][data-sonner-toast][data-type=error]{background:var(--error-bg);border-color:var(--error-border);color:var(--error-text)}[data-rich-colors=true][data-sonner-toast][data-type=error] [data-close-button]{background:var(--error-bg);border-color:var(--error-border);color:var(--error-text)}.sonner-loading-wrapper{--size:16px;height:var(--size);width:var(--size);position:absolute;inset:0;z-index:10}.sonner-loading-wrapper[data-visible=false]{transform-origin:center;animation:sonner-fade-out .2s ease forwards}.sonner-spinner{position:relative;top:50%;left:50%;height:var(--size);width:var(--size)}.sonner-loading-bar{animation:sonner-spin 1.2s linear infinite;background:var(--gray11);border-radius:6px;height:8%;left:-10%;position:absolute;top:-3.9%;width:24%}.sonner-loading-bar:first-child{animation-delay:-1.2s;transform:rotate(.0001deg) translate(146%)}.sonner-loading-bar:nth-child(2){animation-delay:-1.1s;transform:rotate(30deg) translate(146%)}.sonner-loading-bar:nth-child(3){animation-delay:-1s;transform:rotate(60deg) translate(146%)}.sonner-loading-bar:nth-child(4){animation-delay:-.9s;transform:rotate(90deg) translate(146%)}.sonner-loading-bar:nth-child(5){animation-delay:-.8s;transform:rotate(120deg) translate(146%)}.sonner-loading-bar:nth-child(6){animation-delay:-.7s;transform:rotate(150deg) translate(146%)}.sonner-loading-bar:nth-child(7){animation-delay:-.6s;transform:rotate(180deg) translate(146%)}.sonner-loading-bar:nth-child(8){animation-delay:-.5s;transform:rotate(210deg) translate(146%)}.sonner-loading-bar:nth-child(9){animation-delay:-.4s;transform:rotate(240deg) translate(146%)}.sonner-loading-bar:nth-child(10){animation-delay:-.3s;transform:rotate(270deg) translate(146%)}.sonner-loading-bar:nth-child(11){animation-delay:-.2s;transform:rotate(300deg) translate(146%)}.sonner-loading-bar:nth-child(12){animation-delay:-.1s;transform:rotate(330deg) translate(146%)}@keyframes sonner-fade-in{0%{opacity:0;transform:scale(.8)}100%{opacity:1;transform:scale(1)}}@keyframes sonner-fade-out{0%{opacity:1;transform:scale(1)}100%{opacity:0;transform:scale(.8)}}@keyframes sonner-spin{0%{opacity:1}100%{opacity:.15}}@media (prefers-reduced-motion){.sonner-loading-bar,[data-sonner-toast],[data-sonner-toast]>*{transition:none!important;animation:none!important}}.sonner-loader{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;transition:opacity .2s,transform .2s}.sonner-loader[data-visible=false]{opacity:0;transform:scale(.8) translate(-50%,-50%)}\");const VISIBLE_TOASTS_AMOUNT=3,VIEWPORT_OFFSET=\"24px\",MOBILE_VIEWPORT_OFFSET=\"16px\",TOAST_LIFETIME=4e3,TOAST_WIDTH=356,GAP=14,SWIPE_THRESHOLD=45,TIME_BEFORE_UNMOUNT=200;function cn(...A){return A.filter(Boolean).join(\" \")}function getDefaultSwipeDirections(A){const[t,e]=A.split(\"-\"),a=[];return t&&a.push(t),e&&a.push(e),a}const Toast=A=>{var t,e,a,r,n,l,i,o,s;const{invert:p,toast:c,unstyled:u,interacting:d,setHeights:h,visibleToasts:S,heights:f,index:m,toasts:L,expanded:W,removeToast:g,defaultRichColors:y,closeButton:v,style:b,cancelButtonStyle:x,actionButtonStyle:E,className:k=\"\",descriptionClassName:w=\"\",duration:C,position:P,gap:T,expandByDefault:_,classNames:M,icons:D,closeButtonAriaLabel:R=\"Close toast\"}=A,[N,I]=React.useState(null),[F,O]=React.useState(null),[V,B]=React.useState(!1),[z,j]=React.useState(!1),[$,H]=React.useState(!1),[G,q]=React.useState(!1),[U,X]=React.useState(!1),[J,Y]=React.useState(0),[K,Q]=React.useState(0),Z=React.useRef(c.duration||C||TOAST_LIFETIME),AA=React.useRef(null),tA=React.useRef(null),eA=0===m,aA=m+1<=S,rA=c.type,nA=!1!==c.dismissible,lA=c.className||\"\",iA=c.descriptionClassName||\"\",oA=React.useMemo(()=>f.findIndex(A=>A.toastId===c.id)||0,[f,c.id]),sA=React.useMemo(()=>{var A;return null!=(A=c.closeButton)?A:v},[c.closeButton,v]),pA=React.useMemo(()=>c.duration||C||TOAST_LIFETIME,[c.duration,C]),cA=React.useRef(0),uA=React.useRef(0),dA=React.useRef(0),hA=React.useRef(null),[SA,fA]=P.split(\"-\"),mA=React.useMemo(()=>f.reduce((A,t,e)=>e>=oA?A:A+t.height,0),[f,oA]),LA=useIsDocumentHidden(),WA=c.invert||p,gA=\"loading\"===rA;uA.current=React.useMemo(()=>oA*T+mA,[oA,mA]),React.useEffect(()=>{Z.current=pA},[pA]),React.useEffect(()=>{B(!0)},[]),React.useEffect(()=>{const A=tA.current;if(A){const t=A.getBoundingClientRect().height;return Q(t),h(A=>[{toastId:c.id,height:t,position:c.position},...A]),()=>h(A=>A.filter(A=>A.toastId!==c.id))}},[h,c.id]),React.useLayoutEffect(()=>{if(!V)return;const A=tA.current,t=A.style.height;A.style.height=\"auto\";const e=A.getBoundingClientRect().height;A.style.height=t,Q(e),h(A=>A.find(A=>A.toastId===c.id)?A.map(A=>A.toastId===c.id?{...A,height:e}:A):[{toastId:c.id,height:e,position:c.position},...A])},[V,c.title,c.description,h,c.id,c.jsx,c.action,c.cancel]);const yA=React.useCallback(()=>{j(!0),Y(uA.current),h(A=>A.filter(A=>A.toastId!==c.id)),setTimeout(()=>{g(c)},TIME_BEFORE_UNMOUNT)},[c,g,h,uA]);React.useEffect(()=>{if(c.promise&&\"loading\"===rA||c.duration===1/0||\"loading\"===c.type)return;let A;return W||d||LA?(()=>{if(dA.current<cA.current){const A=(new Date).getTime()-cA.current;Z.current=Z.current-A}dA.current=(new Date).getTime()})():Z.current!==1/0&&(cA.current=(new Date).getTime(),A=setTimeout(()=>{null==c.onAutoClose||c.onAutoClose.call(c,c),yA()},Z.current)),()=>clearTimeout(A)},[W,d,c,rA,LA,yA]),React.useEffect(()=>{c.delete&&(yA(),null==c.onDismiss||c.onDismiss.call(c,c))},[yA,c.delete]);const vA=c.icon||(null==D?void 0:D[rA])||getAsset(rA);var bA,xA,EA,kA;return React.createElement(\"li\",{tabIndex:0,ref:tA,className:cn(k,lA,null==M?void 0:M.toast,null==c||null==(t=c.classNames)?void 0:t.toast,null==M?void 0:M.default,null==M?void 0:M[rA],null==c||null==(e=c.classNames)?void 0:e[rA]),\"data-sonner-toast\":\"\",\"data-rich-colors\":null!=(bA=c.richColors)?bA:y,\"data-styled\":!Boolean(c.jsx||c.unstyled||u),\"data-mounted\":V,\"data-promise\":Boolean(c.promise),\"data-swiped\":U,\"data-removed\":z,\"data-visible\":aA,\"data-y-position\":SA,\"data-x-position\":fA,\"data-index\":m,\"data-front\":eA,\"data-swiping\":$,\"data-dismissible\":nA,\"data-type\":rA,\"data-invert\":WA,\"data-swipe-out\":G,\"data-swipe-direction\":F,\"data-expanded\":Boolean(W||_&&V),\"data-testid\":c.testId,style:{\"--index\":m,\"--toasts-before\":m,\"--z-index\":L.length-m,\"--offset\":`${z?J:uA.current}px`,\"--initial-height\":_?\"auto\":`${K}px`,...b,...c.style},onDragEnd:()=>{H(!1),I(null),hA.current=null},onPointerDown:A=>{2!==A.button&&!gA&&nA&&(AA.current=new Date,Y(uA.current),A.target.setPointerCapture(A.pointerId),\"BUTTON\"!==A.target.tagName&&(H(!0),hA.current={x:A.clientX,y:A.clientY}))},onPointerUp:()=>{var A,t,e;if(G||!nA)return;hA.current=null;const a=Number((null==(A=tA.current)?void 0:A.style.getPropertyValue(\"--swipe-amount-x\").replace(\"px\",\"\"))||0),r=Number((null==(t=tA.current)?void 0:t.style.getPropertyValue(\"--swipe-amount-y\").replace(\"px\",\"\"))||0),n=(new Date).getTime()-(null==(e=AA.current)?void 0:e.getTime()),l=\"x\"===N?a:r,i=Math.abs(l)/n;if(Math.abs(l)>=SWIPE_THRESHOLD||i>.11)return Y(uA.current),null==c.onDismiss||c.onDismiss.call(c,c),O(\"x\"===N?a>0?\"right\":\"left\":r>0?\"down\":\"up\"),yA(),void q(!0);var o,s;null==(o=tA.current)||o.style.setProperty(\"--swipe-amount-x\",\"0px\"),null==(s=tA.current)||s.style.setProperty(\"--swipe-amount-y\",\"0px\"),X(!1),H(!1),I(null)},onPointerMove:t=>{var e,a,r;if(!hA.current||!nA)return;if((null==(e=window.getSelection())?void 0:e.toString().length)>0)return;const n=t.clientY-hA.current.y,l=t.clientX-hA.current.x;var i;const o=null!=(i=A.swipeDirections)?i:getDefaultSwipeDirections(P);!N&&(Math.abs(l)>1||Math.abs(n)>1)&&I(Math.abs(l)>Math.abs(n)?\"x\":\"y\");let s={x:0,y:0};const p=A=>1/(1.5+Math.abs(A)/20);if(\"y\"===N){if(o.includes(\"top\")||o.includes(\"bottom\"))if(o.includes(\"top\")&&n<0||o.includes(\"bottom\")&&n>0)s.y=n;else{const A=n*p(n);s.y=Math.abs(A)<Math.abs(n)?A:n}}else if(\"x\"===N&&(o.includes(\"left\")||o.includes(\"right\")))if(o.includes(\"left\")&&l<0||o.includes(\"right\")&&l>0)s.x=l;else{const A=l*p(l);s.x=Math.abs(A)<Math.abs(l)?A:l}(Math.abs(s.x)>0||Math.abs(s.y)>0)&&X(!0),null==(a=tA.current)||a.style.setProperty(\"--swipe-amount-x\",`${s.x}px`),null==(r=tA.current)||r.style.setProperty(\"--swipe-amount-y\",`${s.y}px`)}},sA&&!c.jsx&&\"loading\"!==rA?React.createElement(\"button\",{\"aria-label\":R,\"data-disabled\":gA,\"data-close-button\":!0,onClick:gA||!nA?()=>{}:()=>{yA(),null==c.onDismiss||c.onDismiss.call(c,c)},className:cn(null==M?void 0:M.closeButton,null==c||null==(a=c.classNames)?void 0:a.closeButton)},null!=(xA=null==D?void 0:D.close)?xA:CloseIcon):null,(rA||c.icon||c.promise)&&null!==c.icon&&(null!==(null==D?void 0:D[rA])||c.icon)?React.createElement(\"div\",{\"data-icon\":\"\",className:cn(null==M?void 0:M.icon,null==c||null==(r=c.classNames)?void 0:r.icon)},c.promise||\"loading\"===c.type&&!c.icon?c.icon||((null==D?void 0:D.loading)?React.createElement(\"div\",{className:cn(null==M?void 0:M.loader,null==c||null==(kA=c.classNames)?void 0:kA.loader,\"sonner-loader\"),\"data-visible\":\"loading\"===rA},D.loading):React.createElement(Loader,{className:cn(null==M?void 0:M.loader,null==c||null==(EA=c.classNames)?void 0:EA.loader),visible:\"loading\"===rA})):null,\"loading\"!==c.type?vA:null):null,React.createElement(\"div\",{\"data-content\":\"\",className:cn(null==M?void 0:M.content,null==c||null==(n=c.classNames)?void 0:n.content)},React.createElement(\"div\",{\"data-title\":\"\",className:cn(null==M?void 0:M.title,null==c||null==(l=c.classNames)?void 0:l.title)},c.jsx?c.jsx:\"function\"==typeof c.title?c.title():c.title),c.description?React.createElement(\"div\",{\"data-description\":\"\",className:cn(w,iA,null==M?void 0:M.description,null==c||null==(i=c.classNames)?void 0:i.description)},\"function\"==typeof c.description?c.description():c.description):null),React.isValidElement(c.cancel)?c.cancel:c.cancel&&isAction(c.cancel)?React.createElement(\"button\",{\"data-button\":!0,\"data-cancel\":!0,style:c.cancelButtonStyle||x,onClick:A=>{isAction(c.cancel)&&nA&&(null==c.cancel.onClick||c.cancel.onClick.call(c.cancel,A),yA())},className:cn(null==M?void 0:M.cancelButton,null==c||null==(o=c.classNames)?void 0:o.cancelButton)},c.cancel.label):null,React.isValidElement(c.action)?c.action:c.action&&isAction(c.action)?React.createElement(\"button\",{\"data-button\":!0,\"data-action\":!0,style:c.actionButtonStyle||E,onClick:A=>{isAction(c.action)&&(null==c.action.onClick||c.action.onClick.call(c.action,A),A.defaultPrevented||yA())},className:cn(null==M?void 0:M.actionButton,null==c||null==(s=c.classNames)?void 0:s.actionButton)},c.action.label):null)};function getDocumentDirection(){if(\"undefined\"==typeof window)return\"ltr\";if(\"undefined\"==typeof document)return\"ltr\";const A=document.documentElement.getAttribute(\"dir\");return\"auto\"!==A&&A?A:window.getComputedStyle(document.documentElement).direction}function assignOffset(A,t){const e={};return[A,t].forEach((A,t)=>{const a=1===t,r=a?\"--mobile-offset\":\"--offset\",n=a?MOBILE_VIEWPORT_OFFSET:VIEWPORT_OFFSET;function l(A){[\"top\",\"right\",\"bottom\",\"left\"].forEach(t=>{e[`${r}-${t}`]=\"number\"==typeof A?`${A}px`:A})}\"number\"==typeof A||\"string\"==typeof A?l(A):\"object\"==typeof A?[\"top\",\"right\",\"bottom\",\"left\"].forEach(t=>{void 0===A[t]?e[`${r}-${t}`]=n:e[`${r}-${t}`]=\"number\"==typeof A[t]?`${A[t]}px`:A[t]}):l(n)}),e}const Toaster$1=React.forwardRef(function(A,t){const{id:e,invert:a,position:r=\"bottom-right\",hotkey:n=[\"altKey\",\"KeyT\"],expand:l,closeButton:i,className:o,offset:s,mobileOffset:p,theme:c=\"light\",richColors:u,duration:d,style:h,visibleToasts:S=VISIBLE_TOASTS_AMOUNT,toastOptions:f,dir:m=getDocumentDirection(),gap:L=GAP,icons:W,containerAriaLabel:g=\"Notifications\"}=A,[y,v]=React.useState([]),b=React.useMemo(()=>e?y.filter(A=>A.toasterId===e):y.filter(A=>!A.toasterId),[y,e]),x=React.useMemo(()=>Array.from(new Set([r].concat(b.filter(A=>A.position).map(A=>A.position)))),[b,r]),[E,k]=React.useState([]),[w,C]=React.useState(!1),[P,T]=React.useState(!1),[_,M]=React.useState(\"system\"!==c?c:\"undefined\"!=typeof window&&window.matchMedia&&window.matchMedia(\"(prefers-color-scheme: dark)\").matches?\"dark\":\"light\"),D=React.useRef(null),R=n.join(\"+\").replace(/Key/g,\"\").replace(/Digit/g,\"\"),N=React.useRef(null),I=React.useRef(!1),F=React.useCallback(A=>{v(t=>{var e;return(null==(e=t.find(t=>t.id===A.id))?void 0:e.delete)||ToastState.dismiss(A.id),t.filter(({id:t})=>t!==A.id)})},[]);return React.useEffect(()=>ToastState.subscribe(A=>{A.dismiss?requestAnimationFrame(()=>{v(t=>t.map(t=>t.id===A.id?{...t,delete:!0}:t))}):setTimeout(()=>{ReactDOM$1.flushSync(()=>{v(t=>{const e=t.findIndex(t=>t.id===A.id);return-1!==e?[...t.slice(0,e),{...t[e],...A},...t.slice(e+1)]:[A,...t]})})})}),[y]),React.useEffect(()=>{if(\"system\"!==c)return void M(c);if(\"system\"===c&&(window.matchMedia&&window.matchMedia(\"(prefers-color-scheme: dark)\").matches?M(\"dark\"):M(\"light\")),\"undefined\"==typeof window)return;const A=window.matchMedia(\"(prefers-color-scheme: dark)\");try{A.addEventListener(\"change\",({matches:A})=>{M(A?\"dark\":\"light\")})}catch(t){A.addListener(({matches:A})=>{try{M(A?\"dark\":\"light\")}catch(t){}})}},[c]),React.useEffect(()=>{y.length<=1&&C(!1)},[y]),React.useEffect(()=>{const A=A=>{var t;var e;n.every(t=>A[t]||A.code===t)&&(C(!0),null==(e=D.current)||e.focus());\"Escape\"!==A.code||document.activeElement!==D.current&&!(null==(t=D.current)?void 0:t.contains(document.activeElement))||C(!1)};return document.addEventListener(\"keydown\",A),()=>document.removeEventListener(\"keydown\",A)},[n]),React.useEffect(()=>{if(D.current)return()=>{N.current&&(N.current.focus({preventScroll:!0}),N.current=null,I.current=!1)}},[D.current]),React.createElement(\"section\",{ref:t,\"aria-label\":`${g} ${R}`,tabIndex:-1,\"aria-live\":\"polite\",\"aria-relevant\":\"additions text\",\"aria-atomic\":\"false\",suppressHydrationWarning:!0},x.map((t,e)=>{var r;const[n,c]=t.split(\"-\");return b.length?React.createElement(\"ol\",{key:t,dir:\"auto\"===m?getDocumentDirection():m,tabIndex:-1,ref:D,className:o,\"data-sonner-toaster\":!0,\"data-sonner-theme\":_,\"data-y-position\":n,\"data-x-position\":c,style:{\"--front-toast-height\":`${(null==(r=E[0])?void 0:r.height)||0}px`,\"--width\":`${TOAST_WIDTH}px`,\"--gap\":`${L}px`,...h,...assignOffset(s,p)},onBlur:A=>{I.current&&!A.currentTarget.contains(A.relatedTarget)&&(I.current=!1,N.current&&(N.current.focus({preventScroll:!0}),N.current=null))},onFocus:A=>{A.target instanceof HTMLElement&&\"false\"===A.target.dataset.dismissible||I.current||(I.current=!0,N.current=A.relatedTarget)},onMouseEnter:()=>C(!0),onMouseMove:()=>C(!0),onMouseLeave:()=>{P||C(!1)},onDragEnd:()=>C(!1),onPointerDown:A=>{A.target instanceof HTMLElement&&\"false\"===A.target.dataset.dismissible||T(!0)},onPointerUp:()=>T(!1)},b.filter(A=>!A.position&&0===e||A.position===t).map((e,r)=>{var n,o;return React.createElement(Toast,{key:e.id,icons:W,index:r,toast:e,defaultRichColors:u,duration:null!=(n=null==f?void 0:f.duration)?n:d,className:null==f?void 0:f.className,descriptionClassName:null==f?void 0:f.descriptionClassName,invert:a,visibleToasts:S,closeButton:null!=(o=null==f?void 0:f.closeButton)?o:i,interacting:P,position:t,style:null==f?void 0:f.style,unstyled:null==f?void 0:f.unstyled,classNames:null==f?void 0:f.classNames,cancelButtonStyle:null==f?void 0:f.cancelButtonStyle,actionButtonStyle:null==f?void 0:f.actionButtonStyle,closeButtonAriaLabel:null==f?void 0:f.closeButtonAriaLabel,removeToast:F,toasts:b.filter(A=>A.position==e.position),heights:E.filter(A=>A.position==e.position),setHeights:k,expandByDefault:l,gap:L,expanded:w,swipeDirections:A.swipeDirections})})):null}))}),Toaster=({...A})=>{const{theme:t}=useTheme(),e=useSettingsStore(A=>A.toastPosition);return jsxRuntimeExports.jsx(Toaster$1,{theme:t,className:\"toaster group\",position:e,richColors:!0,icons:{success:jsxRuntimeExports.jsx(CircleCheck,{className:\"size-4\"}),info:jsxRuntimeExports.jsx(Info,{className:\"size-4\"}),warning:jsxRuntimeExports.jsx(TriangleAlert,{className:\"size-4\"}),error:jsxRuntimeExports.jsx(OctagonX,{className:\"size-4\"}),loading:jsxRuntimeExports.jsx(LoaderCircle,{className:\"size-4 animate-spin\"})},style:{\"--normal-bg\":\"var(--popover)\",\"--normal-text\":\"var(--popover-foreground)\",\"--normal-border\":\"var(--border)\",\"--border-radius\":\"var(--radius)\",zIndex:1e5},...A})};var client={exports:{}},reactDomClient_production={},scheduler={exports:{}},scheduler_production={},hasRequiredScheduler_production,hasRequiredScheduler,hasRequiredReactDomClient_production,hasRequiredClient;function requireScheduler_production(){return hasRequiredScheduler_production||(hasRequiredScheduler_production=1,function(A){function t(A,t){var e=A.length;A.push(t);A:for(;0<e;){var a=e-1>>>1,n=A[a];if(!(0<r(n,t)))break A;A[a]=t,A[e]=n,e=a}}function e(A){return 0===A.length?null:A[0]}function a(A){if(0===A.length)return null;var t=A[0],e=A.pop();if(e!==t){A[0]=e;A:for(var a=0,n=A.length,l=n>>>1;a<l;){var i=2*(a+1)-1,o=A[i],s=i+1,p=A[s];if(0>r(o,e))s<n&&0>r(p,o)?(A[a]=p,A[s]=e,a=s):(A[a]=o,A[i]=e,a=i);else{if(!(s<n&&0>r(p,e)))break A;A[a]=p,A[s]=e,a=s}}}return t}function r(A,t){var e=A.sortIndex-t.sortIndex;return 0!==e?e:A.id-t.id}if(A.unstable_now=void 0,\"object\"==typeof performance&&\"function\"==typeof performance.now){var n=performance;A.unstable_now=function(){return n.now()}}else{var l=Date,i=l.now();A.unstable_now=function(){return l.now()-i}}var o=[],s=[],p=1,c=null,u=3,d=!1,h=!1,S=!1,f=!1,m=\"function\"==typeof setTimeout?setTimeout:null,L=\"function\"==typeof clearTimeout?clearTimeout:null,W=\"undefined\"!=typeof setImmediate?setImmediate:null;function g(A){for(var r=e(s);null!==r;){if(null===r.callback)a(s);else{if(!(r.startTime<=A))break;a(s),r.sortIndex=r.expirationTime,t(o,r)}r=e(s)}}function y(A){if(S=!1,g(A),!h)if(null!==e(o))h=!0,b||(b=!0,v());else{var t=e(s);null!==t&&_(y,t.startTime-A)}}var v,b=!1,x=-1,E=5,k=-1;function w(){return!!f||!(A.unstable_now()-k<E)}function C(){if(f=!1,b){var t=A.unstable_now();k=t;var r=!0;try{A:{h=!1,S&&(S=!1,L(x),x=-1),d=!0;var n=u;try{t:{for(g(t),c=e(o);null!==c&&!(c.expirationTime>t&&w());){var l=c.callback;if(\"function\"==typeof l){c.callback=null,u=c.priorityLevel;var i=l(c.expirationTime<=t);if(t=A.unstable_now(),\"function\"==typeof i){c.callback=i,g(t),r=!0;break t}c===e(o)&&a(o),g(t)}else a(o);c=e(o)}if(null!==c)r=!0;else{var p=e(s);null!==p&&_(y,p.startTime-t),r=!1}}break A}finally{c=null,u=n,d=!1}r=void 0}}finally{r?v():b=!1}}}if(\"function\"==typeof W)v=function(){W(C)};else if(\"undefined\"!=typeof MessageChannel){var P=new MessageChannel,T=P.port2;P.port1.onmessage=C,v=function(){T.postMessage(null)}}else v=function(){m(C,0)};function _(t,e){x=m(function(){t(A.unstable_now())},e)}A.unstable_IdlePriority=5,A.unstable_ImmediatePriority=1,A.unstable_LowPriority=4,A.unstable_NormalPriority=3,A.unstable_Profiling=null,A.unstable_UserBlockingPriority=2,A.unstable_cancelCallback=function(A){A.callback=null},A.unstable_forceFrameRate=function(A){0>A||125<A||(E=0<A?Math.floor(1e3/A):5)},A.unstable_getCurrentPriorityLevel=function(){return u},A.unstable_next=function(A){switch(u){case 1:case 2:case 3:var t=3;break;default:t=u}var e=u;u=t;try{return A()}finally{u=e}},A.unstable_requestPaint=function(){f=!0},A.unstable_runWithPriority=function(A,t){switch(A){case 1:case 2:case 3:case 4:case 5:break;default:A=3}var e=u;u=A;try{return t()}finally{u=e}},A.unstable_scheduleCallback=function(a,r,n){var l=A.unstable_now();switch(\"object\"==typeof n&&null!==n?n=\"number\"==typeof(n=n.delay)&&0<n?l+n:l:n=l,a){case 1:var i=-1;break;case 2:i=250;break;case 5:i=1073741823;break;case 4:i=1e4;break;default:i=5e3}return a={id:p++,callback:r,priorityLevel:a,startTime:n,expirationTime:i=n+i,sortIndex:-1},n>l?(a.sortIndex=n,t(s,a),null===e(o)&&a===e(s)&&(S?(L(x),x=-1):S=!0,_(y,n-l))):(a.sortIndex=i,t(o,a),h||d||(h=!0,b||(b=!0,v()))),a},A.unstable_shouldYield=w,A.unstable_wrapCallback=function(A){var t=u;return function(){var e=u;u=t;try{return A.apply(this,arguments)}finally{u=e}}}}(scheduler_production)),scheduler_production}function requireScheduler(){return hasRequiredScheduler||(hasRequiredScheduler=1,scheduler.exports=requireScheduler_production()),scheduler.exports}\n/**\n * @license React\n * react-dom-client.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */function requireReactDomClient_production(){if(hasRequiredReactDomClient_production)return reactDomClient_production;hasRequiredReactDomClient_production=1;var A=requireScheduler(),t=requireReact(),e=requireReactDom();function a(A){var t=\"https://react.dev/errors/\"+A;if(1<arguments.length){t+=\"?args[]=\"+encodeURIComponent(arguments[1]);for(var e=2;e<arguments.length;e++)t+=\"&args[]=\"+encodeURIComponent(arguments[e])}return\"Minified React error #\"+A+\"; visit \"+t+\" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\"}function r(A){return!(!A||1!==A.nodeType&&9!==A.nodeType&&11!==A.nodeType)}function n(A){var t=A,e=A;if(A.alternate)for(;t.return;)t=t.return;else{A=t;do{!!(4098&(t=A).flags)&&(e=t.return),A=t.return}while(A)}return 3===t.tag?e:null}function l(A){if(13===A.tag){var t=A.memoizedState;if(null===t&&(null!==(A=A.alternate)&&(t=A.memoizedState)),null!==t)return t.dehydrated}return null}function i(A){if(31===A.tag){var t=A.memoizedState;if(null===t&&(null!==(A=A.alternate)&&(t=A.memoizedState)),null!==t)return t.dehydrated}return null}function o(A){if(n(A)!==A)throw Error(a(188))}function s(A){var t=A.tag;if(5===t||26===t||27===t||6===t)return A;for(A=A.child;null!==A;){if(null!==(t=s(A)))return t;A=A.sibling}return null}var p=Object.assign,c=Symbol.for(\"react.element\"),u=Symbol.for(\"react.transitional.element\"),d=Symbol.for(\"react.portal\"),h=Symbol.for(\"react.fragment\"),S=Symbol.for(\"react.strict_mode\"),f=Symbol.for(\"react.profiler\"),m=Symbol.for(\"react.consumer\"),L=Symbol.for(\"react.context\"),W=Symbol.for(\"react.forward_ref\"),g=Symbol.for(\"react.suspense\"),y=Symbol.for(\"react.suspense_list\"),v=Symbol.for(\"react.memo\"),b=Symbol.for(\"react.lazy\"),x=Symbol.for(\"react.activity\"),E=Symbol.for(\"react.memo_cache_sentinel\"),k=Symbol.iterator;function w(A){return null===A||\"object\"!=typeof A?null:\"function\"==typeof(A=k&&A[k]||A[\"@@iterator\"])?A:null}var C=Symbol.for(\"react.client.reference\");function P(A){if(null==A)return null;if(\"function\"==typeof A)return A.$$typeof===C?null:A.displayName||A.name||null;if(\"string\"==typeof A)return A;switch(A){case h:return\"Fragment\";case f:return\"Profiler\";case S:return\"StrictMode\";case g:return\"Suspense\";case y:return\"SuspenseList\";case x:return\"Activity\"}if(\"object\"==typeof A)switch(A.$$typeof){case d:return\"Portal\";case L:return A.displayName||\"Context\";case m:return(A._context.displayName||\"Context\")+\".Consumer\";case W:var t=A.render;return(A=A.displayName)||(A=\"\"!==(A=t.displayName||t.name||\"\")?\"ForwardRef(\"+A+\")\":\"ForwardRef\"),A;case v:return null!==(t=A.displayName||null)?t:P(A.type)||\"Memo\";case b:t=A._payload,A=A._init;try{return P(A(t))}catch(e){}}return null}var T=Array.isArray,_=t.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,M=e.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,D={pending:!1,data:null,method:null,action:null},R=[],N=-1;function I(A){return{current:A}}function F(A){0>N||(A.current=R[N],R[N]=null,N--)}function O(A,t){N++,R[N]=A.current,A.current=t}var V,B,z=I(null),j=I(null),$=I(null),H=I(null);function G(A,t){switch(O($,t),O(j,A),O(z,null),t.nodeType){case 9:case 11:A=(A=t.documentElement)&&(A=A.namespaceURI)?hc(A):0;break;default:if(A=t.tagName,t=t.namespaceURI)A=Sc(t=hc(t),A);else switch(A){case\"svg\":A=1;break;case\"math\":A=2;break;default:A=0}}F(z),O(z,A)}function q(){F(z),F(j),F($)}function U(A){null!==A.memoizedState&&O(H,A);var t=z.current,e=Sc(t,A.type);t!==e&&(O(j,A),O(z,e))}function X(A){j.current===A&&(F(z),F(j)),H.current===A&&(F(H),iu._currentValue=D)}function J(A){if(void 0===V)try{throw Error()}catch(e){var t=e.stack.trim().match(/\\n( *(at )?)/);V=t&&t[1]||\"\",B=-1<e.stack.indexOf(\"\\n    at\")?\" (<anonymous>)\":-1<e.stack.indexOf(\"@\")?\"@unknown:0:0\":\"\"}return\"\\n\"+V+A+B}var Y=!1;function K(A,t){if(!A||Y)return\"\";Y=!0;var e=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var a={DetermineComponentFrameRoot:function(){try{if(t){var e=function(){throw Error()};if(Object.defineProperty(e.prototype,\"props\",{set:function(){throw Error()}}),\"object\"==typeof Reflect&&Reflect.construct){try{Reflect.construct(e,[])}catch(r){var a=r}Reflect.construct(A,[],e)}else{try{e.call()}catch(n){a=n}A.call(e.prototype)}}else{try{throw Error()}catch(l){a=l}(e=A())&&\"function\"==typeof e.catch&&e.catch(function(){})}}catch(i){if(i&&a&&\"string\"==typeof i.stack)return[i.stack,a.stack]}return[null,null]}};a.DetermineComponentFrameRoot.displayName=\"DetermineComponentFrameRoot\";var r=Object.getOwnPropertyDescriptor(a.DetermineComponentFrameRoot,\"name\");r&&r.configurable&&Object.defineProperty(a.DetermineComponentFrameRoot,\"name\",{value:\"DetermineComponentFrameRoot\"});var n=a.DetermineComponentFrameRoot(),l=n[0],i=n[1];if(l&&i){var o=l.split(\"\\n\"),s=i.split(\"\\n\");for(r=a=0;a<o.length&&!o[a].includes(\"DetermineComponentFrameRoot\");)a++;for(;r<s.length&&!s[r].includes(\"DetermineComponentFrameRoot\");)r++;if(a===o.length||r===s.length)for(a=o.length-1,r=s.length-1;1<=a&&0<=r&&o[a]!==s[r];)r--;for(;1<=a&&0<=r;a--,r--)if(o[a]!==s[r]){if(1!==a||1!==r)do{if(a--,0>--r||o[a]!==s[r]){var p=\"\\n\"+o[a].replace(\" at new \",\" at \");return A.displayName&&p.includes(\"<anonymous>\")&&(p=p.replace(\"<anonymous>\",A.displayName)),p}}while(1<=a&&0<=r);break}}}finally{Y=!1,Error.prepareStackTrace=e}return(e=A?A.displayName||A.name:\"\")?J(e):\"\"}function Q(A,t){switch(A.tag){case 26:case 27:case 5:return J(A.type);case 16:return J(\"Lazy\");case 13:return A.child!==t&&null!==t?J(\"Suspense Fallback\"):J(\"Suspense\");case 19:return J(\"SuspenseList\");case 0:case 15:return K(A.type,!1);case 11:return K(A.type.render,!1);case 1:return K(A.type,!0);case 31:return J(\"Activity\");default:return\"\"}}function Z(A){try{var t=\"\",e=null;do{t+=Q(A,e),e=A,A=A.return}while(A);return t}catch(a){return\"\\nError generating stack: \"+a.message+\"\\n\"+a.stack}}var AA=Object.prototype.hasOwnProperty,tA=A.unstable_scheduleCallback,eA=A.unstable_cancelCallback,aA=A.unstable_shouldYield,rA=A.unstable_requestPaint,nA=A.unstable_now,lA=A.unstable_getCurrentPriorityLevel,iA=A.unstable_ImmediatePriority,oA=A.unstable_UserBlockingPriority,sA=A.unstable_NormalPriority,pA=A.unstable_LowPriority,cA=A.unstable_IdlePriority,uA=A.log,dA=A.unstable_setDisableYieldValue,hA=null,SA=null;function fA(A){if(\"function\"==typeof uA&&dA(A),SA&&\"function\"==typeof SA.setStrictMode)try{SA.setStrictMode(hA,A)}catch(err){}}var mA=Math.clz32?Math.clz32:function(A){return 0===(A>>>=0)?32:31-(LA(A)/WA|0)|0},LA=Math.log,WA=Math.LN2;var gA=256,yA=262144,vA=4194304;function bA(A){var t=42&A;if(0!==t)return t;switch(A&-A){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return 261888&A;case 262144:case 524288:case 1048576:case 2097152:return 3932160&A;case 4194304:case 8388608:case 16777216:case 33554432:return 62914560&A;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return A}}function xA(A,t,e){var a=A.pendingLanes;if(0===a)return 0;var r=0,n=A.suspendedLanes,l=A.pingedLanes;A=A.warmLanes;var i=134217727&a;return 0!==i?0!==(a=i&~n)?r=bA(a):0!==(l&=i)?r=bA(l):e||0!==(e=i&~A)&&(r=bA(e)):0!==(i=a&~n)?r=bA(i):0!==l?r=bA(l):e||0!==(e=a&~A)&&(r=bA(e)),0===r?0:0!==t&&t!==r&&0===(t&n)&&((n=r&-r)>=(e=t&-t)||32===n&&4194048&e)?t:r}function EA(A,t){return 0===(A.pendingLanes&~(A.suspendedLanes&~A.pingedLanes)&t)}function kA(A,t){switch(A){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;default:return-1}}function wA(){var A=vA;return!(62914560&(vA<<=1))&&(vA=4194304),A}function CA(A){for(var t=[],e=0;31>e;e++)t.push(A);return t}function PA(A,t){A.pendingLanes|=t,268435456!==t&&(A.suspendedLanes=0,A.pingedLanes=0,A.warmLanes=0)}function TA(A,t,e){A.pendingLanes|=t,A.suspendedLanes&=~t;var a=31-mA(t);A.entangledLanes|=t,A.entanglements[a]=1073741824|A.entanglements[a]|261930&e}function _A(A,t){var e=A.entangledLanes|=t;for(A=A.entanglements;e;){var a=31-mA(e),r=1<<a;r&t|A[a]&t&&(A[a]|=t),e&=~r}}function MA(A,t){var e=t&-t;return 0!==((e=42&e?1:DA(e))&(A.suspendedLanes|t))?0:e}function DA(A){switch(A){case 2:A=1;break;case 8:A=4;break;case 32:A=16;break;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:A=128;break;case 268435456:A=134217728;break;default:A=0}return A}function RA(A){return 2<(A&=-A)?8<A?134217727&A?32:268435456:8:2}function NA(){var A=M.p;return 0!==A?A:void 0===(A=window.event)?32:bu(A.type)}function IA(A,t){var e=M.p;try{return M.p=A,t()}finally{M.p=e}}var FA=Math.random().toString(36).slice(2),OA=\"__reactFiber$\"+FA,VA=\"__reactProps$\"+FA,BA=\"__reactContainer$\"+FA,zA=\"__reactEvents$\"+FA,jA=\"__reactListeners$\"+FA,$A=\"__reactHandles$\"+FA,HA=\"__reactResources$\"+FA,GA=\"__reactMarker$\"+FA;function qA(A){delete A[OA],delete A[VA],delete A[zA],delete A[jA],delete A[$A]}function UA(A){var t=A[OA];if(t)return t;for(var e=A.parentNode;e;){if(t=e[BA]||e[OA]){if(e=t.alternate,null!==t.child||null!==e&&null!==e.child)for(A=Dc(A);null!==A;){if(e=A[OA])return e;A=Dc(A)}return t}e=(A=e).parentNode}return null}function XA(A){if(A=A[OA]||A[BA]){var t=A.tag;if(5===t||6===t||13===t||31===t||26===t||27===t||3===t)return A}return null}function JA(A){var t=A.tag;if(5===t||26===t||27===t||6===t)return A.stateNode;throw Error(a(33))}function YA(A){var t=A[HA];return t||(t=A[HA]={hoistableStyles:new Map,hoistableScripts:new Map}),t}function KA(A){A[GA]=!0}var QA=new Set,ZA={};function At(A,t){tt(A,t),tt(A+\"Capture\",t)}function tt(A,t){for(ZA[A]=t,A=0;A<t.length;A++)QA.add(t[A])}var et=RegExp(\"^[:A-Z_a-z\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD][:A-Z_a-z\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD\\\\-.0-9\\\\u00B7\\\\u0300-\\\\u036F\\\\u203F-\\\\u2040]*$\"),at={},rt={};function nt(A,t,e){if(r=t,AA.call(rt,r)||!AA.call(at,r)&&(et.test(r)?rt[r]=!0:(at[r]=!0,0)))if(null===e)A.removeAttribute(t);else{switch(typeof e){case\"undefined\":case\"function\":case\"symbol\":return void A.removeAttribute(t);case\"boolean\":var a=t.toLowerCase().slice(0,5);if(\"data-\"!==a&&\"aria-\"!==a)return void A.removeAttribute(t)}A.setAttribute(t,\"\"+e)}var r}function lt(A,t,e){if(null===e)A.removeAttribute(t);else{switch(typeof e){case\"undefined\":case\"function\":case\"symbol\":case\"boolean\":return void A.removeAttribute(t)}A.setAttribute(t,\"\"+e)}}function it(A,t,e,a){if(null===a)A.removeAttribute(e);else{switch(typeof a){case\"undefined\":case\"function\":case\"symbol\":case\"boolean\":return void A.removeAttribute(e)}A.setAttributeNS(t,e,\"\"+a)}}function ot(A){switch(typeof A){case\"bigint\":case\"boolean\":case\"number\":case\"string\":case\"undefined\":case\"object\":return A;default:return\"\"}}function st(A){var t=A.type;return(A=A.nodeName)&&\"input\"===A.toLowerCase()&&(\"checkbox\"===t||\"radio\"===t)}function pt(A){if(!A._valueTracker){var t=st(A)?\"checked\":\"value\";A._valueTracker=function(A,t,e){var a=Object.getOwnPropertyDescriptor(A.constructor.prototype,t);if(!A.hasOwnProperty(t)&&void 0!==a&&\"function\"==typeof a.get&&\"function\"==typeof a.set){var r=a.get,n=a.set;return Object.defineProperty(A,t,{configurable:!0,get:function(){return r.call(this)},set:function(A){e=\"\"+A,n.call(this,A)}}),Object.defineProperty(A,t,{enumerable:a.enumerable}),{getValue:function(){return e},setValue:function(A){e=\"\"+A},stopTracking:function(){A._valueTracker=null,delete A[t]}}}}(A,t,\"\"+A[t])}}function ct(A){if(!A)return!1;var t=A._valueTracker;if(!t)return!0;var e=t.getValue(),a=\"\";return A&&(a=st(A)?A.checked?\"true\":\"false\":A.value),(A=a)!==e&&(t.setValue(A),!0)}function ut(A){if(void 0===(A=A||(\"undefined\"!=typeof document?document:void 0)))return null;try{return A.activeElement||A.body}catch(t){return A.body}}var dt=/[\\n\"\\\\]/g;function ht(A){return A.replace(dt,function(A){return\"\\\\\"+A.charCodeAt(0).toString(16)+\" \"})}function St(A,t,e,a,r,n,l,i){A.name=\"\",null!=l&&\"function\"!=typeof l&&\"symbol\"!=typeof l&&\"boolean\"!=typeof l?A.type=l:A.removeAttribute(\"type\"),null!=t?\"number\"===l?(0===t&&\"\"===A.value||A.value!=t)&&(A.value=\"\"+ot(t)):A.value!==\"\"+ot(t)&&(A.value=\"\"+ot(t)):\"submit\"!==l&&\"reset\"!==l||A.removeAttribute(\"value\"),null!=t?mt(A,l,ot(t)):null!=e?mt(A,l,ot(e)):null!=a&&A.removeAttribute(\"value\"),null==r&&null!=n&&(A.defaultChecked=!!n),null!=r&&(A.checked=r&&\"function\"!=typeof r&&\"symbol\"!=typeof r),null!=i&&\"function\"!=typeof i&&\"symbol\"!=typeof i&&\"boolean\"!=typeof i?A.name=\"\"+ot(i):A.removeAttribute(\"name\")}function ft(A,t,e,a,r,n,l,i){if(null!=n&&\"function\"!=typeof n&&\"symbol\"!=typeof n&&\"boolean\"!=typeof n&&(A.type=n),null!=t||null!=e){if((\"submit\"===n||\"reset\"===n)&&null==t)return void pt(A);e=null!=e?\"\"+ot(e):\"\",t=null!=t?\"\"+ot(t):e,i||t===A.value||(A.value=t),A.defaultValue=t}a=\"function\"!=typeof(a=null!=a?a:r)&&\"symbol\"!=typeof a&&!!a,A.checked=i?A.checked:!!a,A.defaultChecked=!!a,null!=l&&\"function\"!=typeof l&&\"symbol\"!=typeof l&&\"boolean\"!=typeof l&&(A.name=l),pt(A)}function mt(A,t,e){\"number\"===t&&ut(A.ownerDocument)===A||A.defaultValue===\"\"+e||(A.defaultValue=\"\"+e)}function Lt(A,t,e,a){if(A=A.options,t){t={};for(var r=0;r<e.length;r++)t[\"$\"+e[r]]=!0;for(e=0;e<A.length;e++)r=t.hasOwnProperty(\"$\"+A[e].value),A[e].selected!==r&&(A[e].selected=r),r&&a&&(A[e].defaultSelected=!0)}else{for(e=\"\"+ot(e),t=null,r=0;r<A.length;r++){if(A[r].value===e)return A[r].selected=!0,void(a&&(A[r].defaultSelected=!0));null!==t||A[r].disabled||(t=A[r])}null!==t&&(t.selected=!0)}}function Wt(A,t,e){null==t||((t=\"\"+ot(t))!==A.value&&(A.value=t),null!=e)?A.defaultValue=null!=e?\"\"+ot(e):\"\":A.defaultValue!==t&&(A.defaultValue=t)}function gt(A,t,e,r){if(null==t){if(null!=r){if(null!=e)throw Error(a(92));if(T(r)){if(1<r.length)throw Error(a(93));r=r[0]}e=r}null==e&&(e=\"\"),t=e}e=ot(t),A.defaultValue=e,(r=A.textContent)===e&&\"\"!==r&&null!==r&&(A.value=r),pt(A)}function yt(A,t){if(t){var e=A.firstChild;if(e&&e===A.lastChild&&3===e.nodeType)return void(e.nodeValue=t)}A.textContent=t}var vt=new Set(\"animationIterationCount aspectRatio borderImageOutset borderImageSlice borderImageWidth boxFlex boxFlexGroup boxOrdinalGroup columnCount columns flex flexGrow flexPositive flexShrink flexNegative flexOrder gridArea gridRow gridRowEnd gridRowSpan gridRowStart gridColumn gridColumnEnd gridColumnSpan gridColumnStart fontWeight lineClamp lineHeight opacity order orphans scale tabSize widows zIndex zoom fillOpacity floodOpacity stopOpacity strokeDasharray strokeDashoffset strokeMiterlimit strokeOpacity strokeWidth MozAnimationIterationCount MozBoxFlex MozBoxFlexGroup MozLineClamp msAnimationIterationCount msFlex msZoom msFlexGrow msFlexNegative msFlexOrder msFlexPositive msFlexShrink msGridColumn msGridColumnSpan msGridRow msGridRowSpan WebkitAnimationIterationCount WebkitBoxFlex WebKitBoxFlexGroup WebkitBoxOrdinalGroup WebkitColumnCount WebkitColumns WebkitFlex WebkitFlexGrow WebkitFlexPositive WebkitFlexShrink WebkitLineClamp\".split(\" \"));function bt(A,t,e){var a=0===t.indexOf(\"--\");null==e||\"boolean\"==typeof e||\"\"===e?a?A.setProperty(t,\"\"):\"float\"===t?A.cssFloat=\"\":A[t]=\"\":a?A.setProperty(t,e):\"number\"!=typeof e||0===e||vt.has(t)?\"float\"===t?A.cssFloat=e:A[t]=(\"\"+e).trim():A[t]=e+\"px\"}function xt(A,t,e){if(null!=t&&\"object\"!=typeof t)throw Error(a(62));if(A=A.style,null!=e){for(var r in e)!e.hasOwnProperty(r)||null!=t&&t.hasOwnProperty(r)||(0===r.indexOf(\"--\")?A.setProperty(r,\"\"):\"float\"===r?A.cssFloat=\"\":A[r]=\"\");for(var n in t)r=t[n],t.hasOwnProperty(n)&&e[n]!==r&&bt(A,n,r)}else for(var l in t)t.hasOwnProperty(l)&&bt(A,l,t[l])}function Et(A){if(-1===A.indexOf(\"-\"))return!1;switch(A){case\"annotation-xml\":case\"color-profile\":case\"font-face\":case\"font-face-src\":case\"font-face-uri\":case\"font-face-format\":case\"font-face-name\":case\"missing-glyph\":return!1;default:return!0}}var kt=new Map([[\"acceptCharset\",\"accept-charset\"],[\"htmlFor\",\"for\"],[\"httpEquiv\",\"http-equiv\"],[\"crossOrigin\",\"crossorigin\"],[\"accentHeight\",\"accent-height\"],[\"alignmentBaseline\",\"alignment-baseline\"],[\"arabicForm\",\"arabic-form\"],[\"baselineShift\",\"baseline-shift\"],[\"capHeight\",\"cap-height\"],[\"clipPath\",\"clip-path\"],[\"clipRule\",\"clip-rule\"],[\"colorInterpolation\",\"color-interpolation\"],[\"colorInterpolationFilters\",\"color-interpolation-filters\"],[\"colorProfile\",\"color-profile\"],[\"colorRendering\",\"color-rendering\"],[\"dominantBaseline\",\"dominant-baseline\"],[\"enableBackground\",\"enable-background\"],[\"fillOpacity\",\"fill-opacity\"],[\"fillRule\",\"fill-rule\"],[\"floodColor\",\"flood-color\"],[\"floodOpacity\",\"flood-opacity\"],[\"fontFamily\",\"font-family\"],[\"fontSize\",\"font-size\"],[\"fontSizeAdjust\",\"font-size-adjust\"],[\"fontStretch\",\"font-stretch\"],[\"fontStyle\",\"font-style\"],[\"fontVariant\",\"font-variant\"],[\"fontWeight\",\"font-weight\"],[\"glyphName\",\"glyph-name\"],[\"glyphOrientationHorizontal\",\"glyph-orientation-horizontal\"],[\"glyphOrientationVertical\",\"glyph-orientation-vertical\"],[\"horizAdvX\",\"horiz-adv-x\"],[\"horizOriginX\",\"horiz-origin-x\"],[\"imageRendering\",\"image-rendering\"],[\"letterSpacing\",\"letter-spacing\"],[\"lightingColor\",\"lighting-color\"],[\"markerEnd\",\"marker-end\"],[\"markerMid\",\"marker-mid\"],[\"markerStart\",\"marker-start\"],[\"overlinePosition\",\"overline-position\"],[\"overlineThickness\",\"overline-thickness\"],[\"paintOrder\",\"paint-order\"],[\"panose-1\",\"panose-1\"],[\"pointerEvents\",\"pointer-events\"],[\"renderingIntent\",\"rendering-intent\"],[\"shapeRendering\",\"shape-rendering\"],[\"stopColor\",\"stop-color\"],[\"stopOpacity\",\"stop-opacity\"],[\"strikethroughPosition\",\"strikethrough-position\"],[\"strikethroughThickness\",\"strikethrough-thickness\"],[\"strokeDasharray\",\"stroke-dasharray\"],[\"strokeDashoffset\",\"stroke-dashoffset\"],[\"strokeLinecap\",\"stroke-linecap\"],[\"strokeLinejoin\",\"stroke-linejoin\"],[\"strokeMiterlimit\",\"stroke-miterlimit\"],[\"strokeOpacity\",\"stroke-opacity\"],[\"strokeWidth\",\"stroke-width\"],[\"textAnchor\",\"text-anchor\"],[\"textDecoration\",\"text-decoration\"],[\"textRendering\",\"text-rendering\"],[\"transformOrigin\",\"transform-origin\"],[\"underlinePosition\",\"underline-position\"],[\"underlineThickness\",\"underline-thickness\"],[\"unicodeBidi\",\"unicode-bidi\"],[\"unicodeRange\",\"unicode-range\"],[\"unitsPerEm\",\"units-per-em\"],[\"vAlphabetic\",\"v-alphabetic\"],[\"vHanging\",\"v-hanging\"],[\"vIdeographic\",\"v-ideographic\"],[\"vMathematical\",\"v-mathematical\"],[\"vectorEffect\",\"vector-effect\"],[\"vertAdvY\",\"vert-adv-y\"],[\"vertOriginX\",\"vert-origin-x\"],[\"vertOriginY\",\"vert-origin-y\"],[\"wordSpacing\",\"word-spacing\"],[\"writingMode\",\"writing-mode\"],[\"xmlnsXlink\",\"xmlns:xlink\"],[\"xHeight\",\"x-height\"]]),wt=/^[\\u0000-\\u001F ]*j[\\r\\n\\t]*a[\\r\\n\\t]*v[\\r\\n\\t]*a[\\r\\n\\t]*s[\\r\\n\\t]*c[\\r\\n\\t]*r[\\r\\n\\t]*i[\\r\\n\\t]*p[\\r\\n\\t]*t[\\r\\n\\t]*:/i;function Ct(A){return wt.test(\"\"+A)?\"javascript:throw new Error('React has blocked a javascript: URL as a security precaution.')\":A}function Pt(){}var Tt=null;function _t(A){return(A=A.target||A.srcElement||window).correspondingUseElement&&(A=A.correspondingUseElement),3===A.nodeType?A.parentNode:A}var Mt=null,Dt=null;function Rt(A){var t=XA(A);if(t&&(A=t.stateNode)){var e=A[VA]||null;A:switch(A=t.stateNode,t.type){case\"input\":if(St(A,e.value,e.defaultValue,e.defaultValue,e.checked,e.defaultChecked,e.type,e.name),t=e.name,\"radio\"===e.type&&null!=t){for(e=A;e.parentNode;)e=e.parentNode;for(e=e.querySelectorAll('input[name=\"'+ht(\"\"+t)+'\"][type=\"radio\"]'),t=0;t<e.length;t++){var r=e[t];if(r!==A&&r.form===A.form){var n=r[VA]||null;if(!n)throw Error(a(90));St(r,n.value,n.defaultValue,n.defaultValue,n.checked,n.defaultChecked,n.type,n.name)}}for(t=0;t<e.length;t++)(r=e[t]).form===A.form&&ct(r)}break A;case\"textarea\":Wt(A,e.value,e.defaultValue);break A;case\"select\":null!=(t=e.value)&&Lt(A,!!e.multiple,t,!1)}}}var Nt=!1;function It(A,t,e){if(Nt)return A(t,e);Nt=!0;try{return A(t)}finally{if(Nt=!1,(null!==Mt||null!==Dt)&&(Ys(),Mt&&(t=Mt,A=Dt,Dt=Mt=null,Rt(t),A)))for(t=0;t<A.length;t++)Rt(A[t])}}function Ft(A,t){var e=A.stateNode;if(null===e)return null;var r=e[VA]||null;if(null===r)return null;e=r[t];A:switch(t){case\"onClick\":case\"onClickCapture\":case\"onDoubleClick\":case\"onDoubleClickCapture\":case\"onMouseDown\":case\"onMouseDownCapture\":case\"onMouseMove\":case\"onMouseMoveCapture\":case\"onMouseUp\":case\"onMouseUpCapture\":case\"onMouseEnter\":(r=!r.disabled)||(r=!(\"button\"===(A=A.type)||\"input\"===A||\"select\"===A||\"textarea\"===A)),A=!r;break A;default:A=!1}if(A)return null;if(e&&\"function\"!=typeof e)throw Error(a(231,t,typeof e));return e}var Ot=!(\"undefined\"==typeof window||void 0===window.document||void 0===window.document.createElement),Vt=!1;if(Ot)try{var Bt={};Object.defineProperty(Bt,\"passive\",{get:function(){Vt=!0}}),window.addEventListener(\"test\",Bt,Bt),window.removeEventListener(\"test\",Bt,Bt)}catch(Xu){Vt=!1}var zt=null,jt=null,$t=null;function Ht(){if($t)return $t;var A,t,e=jt,a=e.length,r=\"value\"in zt?zt.value:zt.textContent,n=r.length;for(A=0;A<a&&e[A]===r[A];A++);var l=a-A;for(t=1;t<=l&&e[a-t]===r[n-t];t++);return $t=r.slice(A,1<t?1-t:void 0)}function Gt(A){var t=A.keyCode;return\"charCode\"in A?0===(A=A.charCode)&&13===t&&(A=13):A=t,10===A&&(A=13),32<=A||13===A?A:0}function qt(){return!0}function Ut(){return!1}function Xt(A){function t(t,e,a,r,n){for(var l in this._reactName=t,this._targetInst=a,this.type=e,this.nativeEvent=r,this.target=n,this.currentTarget=null,A)A.hasOwnProperty(l)&&(t=A[l],this[l]=t?t(r):r[l]);return this.isDefaultPrevented=(null!=r.defaultPrevented?r.defaultPrevented:!1===r.returnValue)?qt:Ut,this.isPropagationStopped=Ut,this}return p(t.prototype,{preventDefault:function(){this.defaultPrevented=!0;var A=this.nativeEvent;A&&(A.preventDefault?A.preventDefault():\"unknown\"!=typeof A.returnValue&&(A.returnValue=!1),this.isDefaultPrevented=qt)},stopPropagation:function(){var A=this.nativeEvent;A&&(A.stopPropagation?A.stopPropagation():\"unknown\"!=typeof A.cancelBubble&&(A.cancelBubble=!0),this.isPropagationStopped=qt)},persist:function(){},isPersistent:qt}),t}var Jt,Yt,Kt,Qt={eventPhase:0,bubbles:0,cancelable:0,timeStamp:function(A){return A.timeStamp||Date.now()},defaultPrevented:0,isTrusted:0},Zt=Xt(Qt),Ae=p({},Qt,{view:0,detail:0}),te=Xt(Ae),ee=p({},Ae,{screenX:0,screenY:0,clientX:0,clientY:0,pageX:0,pageY:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,getModifierState:de,button:0,buttons:0,relatedTarget:function(A){return void 0===A.relatedTarget?A.fromElement===A.srcElement?A.toElement:A.fromElement:A.relatedTarget},movementX:function(A){return\"movementX\"in A?A.movementX:(A!==Kt&&(Kt&&\"mousemove\"===A.type?(Jt=A.screenX-Kt.screenX,Yt=A.screenY-Kt.screenY):Yt=Jt=0,Kt=A),Jt)},movementY:function(A){return\"movementY\"in A?A.movementY:Yt}}),ae=Xt(ee),re=Xt(p({},ee,{dataTransfer:0})),ne=Xt(p({},Ae,{relatedTarget:0})),le=Xt(p({},Qt,{animationName:0,elapsedTime:0,pseudoElement:0})),ie=Xt(p({},Qt,{clipboardData:function(A){return\"clipboardData\"in A?A.clipboardData:window.clipboardData}})),oe=Xt(p({},Qt,{data:0})),se={Esc:\"Escape\",Spacebar:\" \",Left:\"ArrowLeft\",Up:\"ArrowUp\",Right:\"ArrowRight\",Down:\"ArrowDown\",Del:\"Delete\",Win:\"OS\",Menu:\"ContextMenu\",Apps:\"ContextMenu\",Scroll:\"ScrollLock\",MozPrintableKey:\"Unidentified\"},pe={8:\"Backspace\",9:\"Tab\",12:\"Clear\",13:\"Enter\",16:\"Shift\",17:\"Control\",18:\"Alt\",19:\"Pause\",20:\"CapsLock\",27:\"Escape\",32:\" \",33:\"PageUp\",34:\"PageDown\",35:\"End\",36:\"Home\",37:\"ArrowLeft\",38:\"ArrowUp\",39:\"ArrowRight\",40:\"ArrowDown\",45:\"Insert\",46:\"Delete\",112:\"F1\",113:\"F2\",114:\"F3\",115:\"F4\",116:\"F5\",117:\"F6\",118:\"F7\",119:\"F8\",120:\"F9\",121:\"F10\",122:\"F11\",123:\"F12\",144:\"NumLock\",145:\"ScrollLock\",224:\"Meta\"},ce={Alt:\"altKey\",Control:\"ctrlKey\",Meta:\"metaKey\",Shift:\"shiftKey\"};function ue(A){var t=this.nativeEvent;return t.getModifierState?t.getModifierState(A):!!(A=ce[A])&&!!t[A]}function de(){return ue}var he=Xt(p({},Ae,{key:function(A){if(A.key){var t=se[A.key]||A.key;if(\"Unidentified\"!==t)return t}return\"keypress\"===A.type?13===(A=Gt(A))?\"Enter\":String.fromCharCode(A):\"keydown\"===A.type||\"keyup\"===A.type?pe[A.keyCode]||\"Unidentified\":\"\"},code:0,location:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,repeat:0,locale:0,getModifierState:de,charCode:function(A){return\"keypress\"===A.type?Gt(A):0},keyCode:function(A){return\"keydown\"===A.type||\"keyup\"===A.type?A.keyCode:0},which:function(A){return\"keypress\"===A.type?Gt(A):\"keydown\"===A.type||\"keyup\"===A.type?A.keyCode:0}})),Se=Xt(p({},ee,{pointerId:0,width:0,height:0,pressure:0,tangentialPressure:0,tiltX:0,tiltY:0,twist:0,pointerType:0,isPrimary:0})),fe=Xt(p({},Ae,{touches:0,targetTouches:0,changedTouches:0,altKey:0,metaKey:0,ctrlKey:0,shiftKey:0,getModifierState:de})),me=Xt(p({},Qt,{propertyName:0,elapsedTime:0,pseudoElement:0})),Le=Xt(p({},ee,{deltaX:function(A){return\"deltaX\"in A?A.deltaX:\"wheelDeltaX\"in A?-A.wheelDeltaX:0},deltaY:function(A){return\"deltaY\"in A?A.deltaY:\"wheelDeltaY\"in A?-A.wheelDeltaY:\"wheelDelta\"in A?-A.wheelDelta:0},deltaZ:0,deltaMode:0})),We=Xt(p({},Qt,{newState:0,oldState:0})),ge=[9,13,27,32],ye=Ot&&\"CompositionEvent\"in window,ve=null;Ot&&\"documentMode\"in document&&(ve=document.documentMode);var be=Ot&&\"TextEvent\"in window&&!ve,xe=Ot&&(!ye||ve&&8<ve&&11>=ve),Ee=String.fromCharCode(32),ke=!1;function we(A,t){switch(A){case\"keyup\":return-1!==ge.indexOf(t.keyCode);case\"keydown\":return 229!==t.keyCode;case\"keypress\":case\"mousedown\":case\"focusout\":return!0;default:return!1}}function Ce(A){return\"object\"==typeof(A=A.detail)&&\"data\"in A?A.data:null}var Pe=!1;var Te={color:!0,date:!0,datetime:!0,\"datetime-local\":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};function _e(A){var t=A&&A.nodeName&&A.nodeName.toLowerCase();return\"input\"===t?!!Te[A.type]:\"textarea\"===t}function Me(A,t,e,a){Mt?Dt?Dt.push(a):Dt=[a]:Mt=a,0<(t=Ac(t,\"onChange\")).length&&(e=new Zt(\"onChange\",\"change\",null,e,a),A.push({event:e,listeners:t}))}var De=null,Re=null;function Ne(A){qp(A,0)}function Ie(A){if(ct(JA(A)))return A}function Fe(A,t){if(\"change\"===A)return t}var Oe=!1;if(Ot){var Ve;if(Ot){var Be=\"oninput\"in document;if(!Be){var ze=document.createElement(\"div\");ze.setAttribute(\"oninput\",\"return;\"),Be=\"function\"==typeof ze.oninput}Ve=Be}else Ve=!1;Oe=Ve&&(!document.documentMode||9<document.documentMode)}function je(){De&&(De.detachEvent(\"onpropertychange\",$e),Re=De=null)}function $e(A){if(\"value\"===A.propertyName&&Ie(Re)){var t=[];Me(t,Re,A,_t(A)),It(Ne,t)}}function He(A,t,e){\"focusin\"===A?(je(),Re=e,(De=t).attachEvent(\"onpropertychange\",$e)):\"focusout\"===A&&je()}function Ge(A){if(\"selectionchange\"===A||\"keyup\"===A||\"keydown\"===A)return Ie(Re)}function qe(A,t){if(\"click\"===A)return Ie(t)}function Ue(A,t){if(\"input\"===A||\"change\"===A)return Ie(t)}var Xe=\"function\"==typeof Object.is?Object.is:function(A,t){return A===t&&(0!==A||1/A==1/t)||A!=A&&t!=t};function Je(A,t){if(Xe(A,t))return!0;if(\"object\"!=typeof A||null===A||\"object\"!=typeof t||null===t)return!1;var e=Object.keys(A),a=Object.keys(t);if(e.length!==a.length)return!1;for(a=0;a<e.length;a++){var r=e[a];if(!AA.call(t,r)||!Xe(A[r],t[r]))return!1}return!0}function Ye(A){for(;A&&A.firstChild;)A=A.firstChild;return A}function Ke(A,t){var e,a=Ye(A);for(A=0;a;){if(3===a.nodeType){if(e=A+a.textContent.length,A<=t&&e>=t)return{node:a,offset:t-A};A=e}A:{for(;a;){if(a.nextSibling){a=a.nextSibling;break A}a=a.parentNode}a=void 0}a=Ye(a)}}function Qe(A,t){return!(!A||!t)&&(A===t||(!A||3!==A.nodeType)&&(t&&3===t.nodeType?Qe(A,t.parentNode):\"contains\"in A?A.contains(t):!!A.compareDocumentPosition&&!!(16&A.compareDocumentPosition(t))))}function Ze(A){for(var t=ut((A=null!=A&&null!=A.ownerDocument&&null!=A.ownerDocument.defaultView?A.ownerDocument.defaultView:window).document);t instanceof A.HTMLIFrameElement;){try{var e=\"string\"==typeof t.contentWindow.location.href}catch(err){e=!1}if(!e)break;t=ut((A=t.contentWindow).document)}return t}function Aa(A){var t=A&&A.nodeName&&A.nodeName.toLowerCase();return t&&(\"input\"===t&&(\"text\"===A.type||\"search\"===A.type||\"tel\"===A.type||\"url\"===A.type||\"password\"===A.type)||\"textarea\"===t||\"true\"===A.contentEditable)}var ta=Ot&&\"documentMode\"in document&&11>=document.documentMode,ea=null,aa=null,ra=null,na=!1;function la(A,t,e){var a=e.window===e?e.document:9===e.nodeType?e:e.ownerDocument;na||null==ea||ea!==ut(a)||(\"selectionStart\"in(a=ea)&&Aa(a)?a={start:a.selectionStart,end:a.selectionEnd}:a={anchorNode:(a=(a.ownerDocument&&a.ownerDocument.defaultView||window).getSelection()).anchorNode,anchorOffset:a.anchorOffset,focusNode:a.focusNode,focusOffset:a.focusOffset},ra&&Je(ra,a)||(ra=a,0<(a=Ac(aa,\"onSelect\")).length&&(t=new Zt(\"onSelect\",\"select\",null,t,e),A.push({event:t,listeners:a}),t.target=ea)))}function ia(A,t){var e={};return e[A.toLowerCase()]=t.toLowerCase(),e[\"Webkit\"+A]=\"webkit\"+t,e[\"Moz\"+A]=\"moz\"+t,e}var oa={animationend:ia(\"Animation\",\"AnimationEnd\"),animationiteration:ia(\"Animation\",\"AnimationIteration\"),animationstart:ia(\"Animation\",\"AnimationStart\"),transitionrun:ia(\"Transition\",\"TransitionRun\"),transitionstart:ia(\"Transition\",\"TransitionStart\"),transitioncancel:ia(\"Transition\",\"TransitionCancel\"),transitionend:ia(\"Transition\",\"TransitionEnd\")},sa={},pa={};function ca(A){if(sa[A])return sa[A];if(!oa[A])return A;var t,e=oa[A];for(t in e)if(e.hasOwnProperty(t)&&t in pa)return sa[A]=e[t];return A}Ot&&(pa=document.createElement(\"div\").style,\"AnimationEvent\"in window||(delete oa.animationend.animation,delete oa.animationiteration.animation,delete oa.animationstart.animation),\"TransitionEvent\"in window||delete oa.transitionend.transition);var ua=ca(\"animationend\"),da=ca(\"animationiteration\"),ha=ca(\"animationstart\"),Sa=ca(\"transitionrun\"),fa=ca(\"transitionstart\"),ma=ca(\"transitioncancel\"),La=ca(\"transitionend\"),Wa=new Map,ga=\"abort auxClick beforeToggle cancel canPlay canPlayThrough click close contextMenu copy cut drag dragEnd dragEnter dragExit dragLeave dragOver dragStart drop durationChange emptied encrypted ended error gotPointerCapture input invalid keyDown keyPress keyUp load loadedData loadedMetadata loadStart lostPointerCapture mouseDown mouseMove mouseOut mouseOver mouseUp paste pause play playing pointerCancel pointerDown pointerMove pointerOut pointerOver pointerUp progress rateChange reset resize seeked seeking stalled submit suspend timeUpdate touchCancel touchEnd touchStart volumeChange scroll toggle touchMove waiting wheel\".split(\" \");function ya(A,t){Wa.set(A,t),At(t,[A])}ga.push(\"scrollEnd\");var va=\"function\"==typeof reportError?reportError:function(A){if(\"object\"==typeof window&&\"function\"==typeof window.ErrorEvent){var t=new window.ErrorEvent(\"error\",{bubbles:!0,cancelable:!0,message:\"object\"==typeof A&&null!==A&&\"string\"==typeof A.message?String(A.message):String(A),error:A});if(!window.dispatchEvent(t))return}else if(\"object\"==typeof process&&\"function\"==typeof process.emit)return void process.emit(\"uncaughtException\",A)},ba=[],xa=0,Ea=0;function ka(){for(var A=xa,t=Ea=xa=0;t<A;){var e=ba[t];ba[t++]=null;var a=ba[t];ba[t++]=null;var r=ba[t];ba[t++]=null;var n=ba[t];if(ba[t++]=null,null!==a&&null!==r){var l=a.pending;null===l?r.next=r:(r.next=l.next,l.next=r),a.pending=r}0!==n&&Ta(e,r,n)}}function wa(A,t,e,a){ba[xa++]=A,ba[xa++]=t,ba[xa++]=e,ba[xa++]=a,Ea|=a,A.lanes|=a,null!==(A=A.alternate)&&(A.lanes|=a)}function Ca(A,t,e,a){return wa(A,t,e,a),_a(A)}function Pa(A,t){return wa(A,null,null,t),_a(A)}function Ta(A,t,e){A.lanes|=e;var a=A.alternate;null!==a&&(a.lanes|=e);for(var r=!1,n=A.return;null!==n;)n.childLanes|=e,null!==(a=n.alternate)&&(a.childLanes|=e),22===n.tag&&(null===(A=n.stateNode)||1&A._visibility||(r=!0)),A=n,n=n.return;return 3===A.tag?(n=A.stateNode,r&&null!==t&&(r=31-mA(e),null===(a=(A=n.hiddenUpdates)[r])?A[r]=[t]:a.push(t),t.lane=536870912|e),n):null}function _a(A){if(50<zs)throw zs=0,js=null,Error(a(185));for(var t=A.return;null!==t;)t=(A=t).return;return 3===A.tag?A.stateNode:null}var Ma={};function Da(A,t,e,a){this.tag=A,this.key=e,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.refCleanup=this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=a,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Ra(A,t,e,a){return new Da(A,t,e,a)}function Na(A){return!(!(A=A.prototype)||!A.isReactComponent)}function Ia(A,t){var e=A.alternate;return null===e?((e=Ra(A.tag,t,A.key,A.mode)).elementType=A.elementType,e.type=A.type,e.stateNode=A.stateNode,e.alternate=A,A.alternate=e):(e.pendingProps=t,e.type=A.type,e.flags=0,e.subtreeFlags=0,e.deletions=null),e.flags=65011712&A.flags,e.childLanes=A.childLanes,e.lanes=A.lanes,e.child=A.child,e.memoizedProps=A.memoizedProps,e.memoizedState=A.memoizedState,e.updateQueue=A.updateQueue,t=A.dependencies,e.dependencies=null===t?null:{lanes:t.lanes,firstContext:t.firstContext},e.sibling=A.sibling,e.index=A.index,e.ref=A.ref,e.refCleanup=A.refCleanup,e}function Fa(A,t){A.flags&=65011714;var e=A.alternate;return null===e?(A.childLanes=0,A.lanes=t,A.child=null,A.subtreeFlags=0,A.memoizedProps=null,A.memoizedState=null,A.updateQueue=null,A.dependencies=null,A.stateNode=null):(A.childLanes=e.childLanes,A.lanes=e.lanes,A.child=e.child,A.subtreeFlags=0,A.deletions=null,A.memoizedProps=e.memoizedProps,A.memoizedState=e.memoizedState,A.updateQueue=e.updateQueue,A.type=e.type,t=e.dependencies,A.dependencies=null===t?null:{lanes:t.lanes,firstContext:t.firstContext}),A}function Oa(A,t,e,r,n,l){var i=0;if(r=A,\"function\"==typeof A)Na(A)&&(i=1);else if(\"string\"==typeof A)i=function(A,t,e){if(1===e||null!=t.itemProp)return!1;switch(A){case\"meta\":case\"title\":return!0;case\"style\":if(\"string\"!=typeof t.precedence||\"string\"!=typeof t.href||\"\"===t.href)break;return!0;case\"link\":if(\"string\"!=typeof t.rel||\"string\"!=typeof t.href||\"\"===t.href||t.onLoad||t.onError)break;return\"stylesheet\"!==t.rel||(A=t.disabled,\"string\"==typeof t.precedence&&null==A);case\"script\":if(t.async&&\"function\"!=typeof t.async&&\"symbol\"!=typeof t.async&&!t.onLoad&&!t.onError&&t.src&&\"string\"==typeof t.src)return!0}return!1}(A,e,z.current)?26:\"html\"===A||\"head\"===A||\"body\"===A?27:5;else A:switch(A){case x:return(A=Ra(31,e,t,n)).elementType=x,A.lanes=l,A;case h:return Va(e.children,n,l,t);case S:i=8,n|=24;break;case f:return(A=Ra(12,e,t,2|n)).elementType=f,A.lanes=l,A;case g:return(A=Ra(13,e,t,n)).elementType=g,A.lanes=l,A;case y:return(A=Ra(19,e,t,n)).elementType=y,A.lanes=l,A;default:if(\"object\"==typeof A&&null!==A)switch(A.$$typeof){case L:i=10;break A;case m:i=9;break A;case W:i=11;break A;case v:i=14;break A;case b:i=16,r=null;break A}i=29,e=Error(a(130,null===A?\"null\":typeof A,\"\")),r=null}return(t=Ra(i,e,t,n)).elementType=A,t.type=r,t.lanes=l,t}function Va(A,t,e,a){return(A=Ra(7,A,a,t)).lanes=e,A}function Ba(A,t,e){return(A=Ra(6,A,null,t)).lanes=e,A}function za(A){var t=Ra(18,null,null,0);return t.stateNode=A,t}function ja(A,t,e){return(t=Ra(4,null!==A.children?A.children:[],A.key,t)).lanes=e,t.stateNode={containerInfo:A.containerInfo,pendingChildren:null,implementation:A.implementation},t}var $a=new WeakMap;function Ha(A,t){if(\"object\"==typeof A&&null!==A){var e=$a.get(A);return void 0!==e?e:(t={value:A,source:t,stack:Z(t)},$a.set(A,t),t)}return{value:A,source:t,stack:Z(t)}}var Ga=[],qa=0,Ua=null,Xa=0,Ja=[],Ya=0,Ka=null,Qa=1,Za=\"\";function Ar(A,t){Ga[qa++]=Xa,Ga[qa++]=Ua,Ua=A,Xa=t}function tr(A,t,e){Ja[Ya++]=Qa,Ja[Ya++]=Za,Ja[Ya++]=Ka,Ka=A;var a=Qa;A=Za;var r=32-mA(a)-1;a&=~(1<<r),e+=1;var n=32-mA(t)+r;if(30<n){var l=r-r%5;n=(a&(1<<l)-1).toString(32),a>>=l,r-=l,Qa=1<<32-mA(t)+r|e<<r|a,Za=n+A}else Qa=1<<n|e<<r|a,Za=A}function er(A){null!==A.return&&(Ar(A,1),tr(A,1,0))}function ar(A){for(;A===Ua;)Ua=Ga[--qa],Ga[qa]=null,Xa=Ga[--qa],Ga[qa]=null;for(;A===Ka;)Ka=Ja[--Ya],Ja[Ya]=null,Za=Ja[--Ya],Ja[Ya]=null,Qa=Ja[--Ya],Ja[Ya]=null}function rr(A,t){Ja[Ya++]=Qa,Ja[Ya++]=Za,Ja[Ya++]=Ka,Qa=t.id,Za=t.overflow,Ka=A}var nr=null,lr=null,ir=!1,or=null,sr=!1,pr=Error(a(519));function cr(A){throw mr(Ha(Error(a(418,1<arguments.length&&void 0!==arguments[1]&&arguments[1]?\"text\":\"HTML\",\"\")),A)),pr}function ur(A){var t=A.stateNode,e=A.type,a=A.memoizedProps;switch(t[OA]=A,t[VA]=a,e){case\"dialog\":Up(\"cancel\",t),Up(\"close\",t);break;case\"iframe\":case\"object\":case\"embed\":Up(\"load\",t);break;case\"video\":case\"audio\":for(e=0;e<Hp.length;e++)Up(Hp[e],t);break;case\"source\":Up(\"error\",t);break;case\"img\":case\"image\":case\"link\":Up(\"error\",t),Up(\"load\",t);break;case\"details\":Up(\"toggle\",t);break;case\"input\":Up(\"invalid\",t),ft(t,a.value,a.defaultValue,a.checked,a.defaultChecked,a.type,a.name,!0);break;case\"select\":Up(\"invalid\",t);break;case\"textarea\":Up(\"invalid\",t),gt(t,a.value,a.defaultValue,a.children)}\"string\"!=typeof(e=a.children)&&\"number\"!=typeof e&&\"bigint\"!=typeof e||t.textContent===\"\"+e||!0===a.suppressHydrationWarning||lc(t.textContent,e)?(null!=a.popover&&(Up(\"beforetoggle\",t),Up(\"toggle\",t)),null!=a.onScroll&&Up(\"scroll\",t),null!=a.onScrollEnd&&Up(\"scrollend\",t),null!=a.onClick&&(t.onclick=Pt),t=!0):t=!1,t||cr(A,!0)}function dr(A){for(nr=A.return;nr;)switch(nr.tag){case 5:case 31:case 13:return void(sr=!1);case 27:case 3:return void(sr=!0);default:nr=nr.return}}function hr(A){if(A!==nr)return!1;if(!ir)return dr(A),ir=!0,!1;var t,e=A.tag;if((t=3!==e&&27!==e)&&((t=5===e)&&(t=!(\"form\"!==(t=A.type)&&\"button\"!==t)||fc(A.type,A.memoizedProps)),t=!t),t&&lr&&cr(A),dr(A),13===e){if(!(A=null!==(A=A.memoizedState)?A.dehydrated:null))throw Error(a(317));lr=Mc(A)}else if(31===e){if(!(A=null!==(A=A.memoizedState)?A.dehydrated:null))throw Error(a(317));lr=Mc(A)}else 27===e?(e=lr,bc(A.type)?(A=_c,_c=null,lr=A):lr=e):lr=nr?Tc(A.stateNode.nextSibling):null;return!0}function Sr(){lr=nr=null,ir=!1}function fr(){var A=or;return null!==A&&(null===ws?ws=A:ws.push.apply(ws,A),or=null),A}function mr(A){null===or?or=[A]:or.push(A)}var Lr=I(null),Wr=null,gr=null;function yr(A,t,e){O(Lr,t._currentValue),t._currentValue=e}function vr(A){A._currentValue=Lr.current,F(Lr)}function br(A,t,e){for(;null!==A;){var a=A.alternate;if((A.childLanes&t)!==t?(A.childLanes|=t,null!==a&&(a.childLanes|=t)):null!==a&&(a.childLanes&t)!==t&&(a.childLanes|=t),A===e)break;A=A.return}}function xr(A,t,e,r){var n=A.child;for(null!==n&&(n.return=A);null!==n;){var l=n.dependencies;if(null!==l){var i=n.child;l=l.firstContext;A:for(;null!==l;){var o=l;l=n;for(var s=0;s<t.length;s++)if(o.context===t[s]){l.lanes|=e,null!==(o=l.alternate)&&(o.lanes|=e),br(l.return,e,A),r||(i=null);break A}l=o.next}}else if(18===n.tag){if(null===(i=n.return))throw Error(a(341));i.lanes|=e,null!==(l=i.alternate)&&(l.lanes|=e),br(i,e,A),i=null}else i=n.child;if(null!==i)i.return=n;else for(i=n;null!==i;){if(i===A){i=null;break}if(null!==(n=i.sibling)){n.return=i.return,i=n;break}i=i.return}n=i}}function Er(A,t,e,r){A=null;for(var n=t,l=!1;null!==n;){if(!l)if(524288&n.flags)l=!0;else if(262144&n.flags)break;if(10===n.tag){var i=n.alternate;if(null===i)throw Error(a(387));if(null!==(i=i.memoizedProps)){var o=n.type;Xe(n.pendingProps.value,i.value)||(null!==A?A.push(o):A=[o])}}else if(n===H.current){if(null===(i=n.alternate))throw Error(a(387));i.memoizedState.memoizedState!==n.memoizedState.memoizedState&&(null!==A?A.push(iu):A=[iu])}n=n.return}null!==A&&xr(t,A,e,r),t.flags|=262144}function kr(A){for(A=A.firstContext;null!==A;){if(!Xe(A.context._currentValue,A.memoizedValue))return!0;A=A.next}return!1}function wr(A){Wr=A,gr=null,null!==(A=A.dependencies)&&(A.firstContext=null)}function Cr(A){return Tr(Wr,A)}function Pr(A,t){return null===Wr&&wr(A),Tr(A,t)}function Tr(A,t){var e=t._currentValue;if(t={context:t,memoizedValue:e,next:null},null===gr){if(null===A)throw Error(a(308));gr=t,A.dependencies={lanes:0,firstContext:t},A.flags|=524288}else gr=gr.next=t;return e}var _r=\"undefined\"!=typeof AbortController?AbortController:function(){var A=[],t=this.signal={aborted:!1,addEventListener:function(t,e){A.push(e)}};this.abort=function(){t.aborted=!0,A.forEach(function(A){return A()})}},Mr=A.unstable_scheduleCallback,Dr=A.unstable_NormalPriority,Rr={$$typeof:L,Consumer:null,Provider:null,_currentValue:null,_currentValue2:null,_threadCount:0};function Nr(){return{controller:new _r,data:new Map,refCount:0}}function Ir(A){A.refCount--,0===A.refCount&&Mr(Dr,function(){A.controller.abort()})}var Fr=null,Or=0,Vr=0,Br=null;function zr(){if(0===--Or&&null!==Fr){null!==Br&&(Br.status=\"fulfilled\");var A=Fr;Fr=null,Vr=0,Br=null;for(var t=0;t<A.length;t++)(0,A[t])()}}var jr=_.S;_.S=function(A,t){Ts=nA(),\"object\"==typeof t&&null!==t&&\"function\"==typeof t.then&&function(A,t){if(null===Fr){var e=Fr=[];Or=0,Vr=Vp(),Br={status:\"pending\",value:void 0,then:function(A){e.push(A)}}}Or++,t.then(zr,zr)}(0,t),null!==jr&&jr(A,t)};var $r=I(null);function Hr(){var A=$r.current;return null!==A?A:cs.pooledCache}function Gr(A,t){O($r,null===t?$r.current:t.pool)}function qr(){var A=Hr();return null===A?null:{parent:Rr._currentValue,pool:A}}var Ur=Error(a(460)),Xr=Error(a(474)),Jr=Error(a(542)),Yr={then:function(){}};function Kr(A){return\"fulfilled\"===(A=A.status)||\"rejected\"===A}function Qr(A,t,e){switch(void 0===(e=A[e])?A.push(t):e!==t&&(t.then(Pt,Pt),t=e),t.status){case\"fulfilled\":return t.value;case\"rejected\":throw en(A=t.reason),A;default:if(\"string\"==typeof t.status)t.then(Pt,Pt);else{if(null!==(A=cs)&&100<A.shellSuspendCounter)throw Error(a(482));(A=t).status=\"pending\",A.then(function(A){if(\"pending\"===t.status){var e=t;e.status=\"fulfilled\",e.value=A}},function(A){if(\"pending\"===t.status){var e=t;e.status=\"rejected\",e.reason=A}})}switch(t.status){case\"fulfilled\":return t.value;case\"rejected\":throw en(A=t.reason),A}throw An=t,Ur}}function Zr(A){try{return(0,A._init)(A._payload)}catch(t){if(null!==t&&\"object\"==typeof t&&\"function\"==typeof t.then)throw An=t,Ur;throw t}}var An=null;function tn(){if(null===An)throw Error(a(459));var A=An;return An=null,A}function en(A){if(A===Ur||A===Jr)throw Error(a(483))}var an=null,rn=0;function nn(A){var t=rn;return rn+=1,null===an&&(an=[]),Qr(an,A,t)}function ln(A,t){t=t.props.ref,A.ref=void 0!==t?t:null}function on(A,t){if(t.$$typeof===c)throw Error(a(525));throw A=Object.prototype.toString.call(t),Error(a(31,\"[object Object]\"===A?\"object with keys {\"+Object.keys(t).join(\", \")+\"}\":A))}function sn(A){function t(t,e){if(A){var a=t.deletions;null===a?(t.deletions=[e],t.flags|=16):a.push(e)}}function e(e,a){if(!A)return null;for(;null!==a;)t(e,a),a=a.sibling;return null}function r(A){for(var t=new Map;null!==A;)null!==A.key?t.set(A.key,A):t.set(A.index,A),A=A.sibling;return t}function n(A,t){return(A=Ia(A,t)).index=0,A.sibling=null,A}function l(t,e,a){return t.index=a,A?null!==(a=t.alternate)?(a=a.index)<e?(t.flags|=67108866,e):a:(t.flags|=67108866,e):(t.flags|=1048576,e)}function i(t){return A&&null===t.alternate&&(t.flags|=67108866),t}function o(A,t,e,a){return null===t||6!==t.tag?((t=Ba(e,A.mode,a)).return=A,t):((t=n(t,e)).return=A,t)}function s(A,t,e,a){var r=e.type;return r===h?c(A,t,e.props.children,a,e.key):null!==t&&(t.elementType===r||\"object\"==typeof r&&null!==r&&r.$$typeof===b&&Zr(r)===t.type)?(ln(t=n(t,e.props),e),t.return=A,t):(ln(t=Oa(e.type,e.key,e.props,null,A.mode,a),e),t.return=A,t)}function p(A,t,e,a){return null===t||4!==t.tag||t.stateNode.containerInfo!==e.containerInfo||t.stateNode.implementation!==e.implementation?((t=ja(e,A.mode,a)).return=A,t):((t=n(t,e.children||[])).return=A,t)}function c(A,t,e,a,r){return null===t||7!==t.tag?((t=Va(e,A.mode,a,r)).return=A,t):((t=n(t,e)).return=A,t)}function S(A,t,e){if(\"string\"==typeof t&&\"\"!==t||\"number\"==typeof t||\"bigint\"==typeof t)return(t=Ba(\"\"+t,A.mode,e)).return=A,t;if(\"object\"==typeof t&&null!==t){switch(t.$$typeof){case u:return ln(e=Oa(t.type,t.key,t.props,null,A.mode,e),t),e.return=A,e;case d:return(t=ja(t,A.mode,e)).return=A,t;case b:return S(A,t=Zr(t),e)}if(T(t)||w(t))return(t=Va(t,A.mode,e,null)).return=A,t;if(\"function\"==typeof t.then)return S(A,nn(t),e);if(t.$$typeof===L)return S(A,Pr(A,t),e);on(A,t)}return null}function f(A,t,e,a){var r=null!==t?t.key:null;if(\"string\"==typeof e&&\"\"!==e||\"number\"==typeof e||\"bigint\"==typeof e)return null!==r?null:o(A,t,\"\"+e,a);if(\"object\"==typeof e&&null!==e){switch(e.$$typeof){case u:return e.key===r?s(A,t,e,a):null;case d:return e.key===r?p(A,t,e,a):null;case b:return f(A,t,e=Zr(e),a)}if(T(e)||w(e))return null!==r?null:c(A,t,e,a,null);if(\"function\"==typeof e.then)return f(A,t,nn(e),a);if(e.$$typeof===L)return f(A,t,Pr(A,e),a);on(A,e)}return null}function m(A,t,e,a,r){if(\"string\"==typeof a&&\"\"!==a||\"number\"==typeof a||\"bigint\"==typeof a)return o(t,A=A.get(e)||null,\"\"+a,r);if(\"object\"==typeof a&&null!==a){switch(a.$$typeof){case u:return s(t,A=A.get(null===a.key?e:a.key)||null,a,r);case d:return p(t,A=A.get(null===a.key?e:a.key)||null,a,r);case b:return m(A,t,e,a=Zr(a),r)}if(T(a)||w(a))return c(t,A=A.get(e)||null,a,r,null);if(\"function\"==typeof a.then)return m(A,t,e,nn(a),r);if(a.$$typeof===L)return m(A,t,e,Pr(t,a),r);on(t,a)}return null}function W(o,s,p,c){if(\"object\"==typeof p&&null!==p&&p.type===h&&null===p.key&&(p=p.props.children),\"object\"==typeof p&&null!==p){switch(p.$$typeof){case u:A:{for(var g=p.key;null!==s;){if(s.key===g){if((g=p.type)===h){if(7===s.tag){e(o,s.sibling),(c=n(s,p.props.children)).return=o,o=c;break A}}else if(s.elementType===g||\"object\"==typeof g&&null!==g&&g.$$typeof===b&&Zr(g)===s.type){e(o,s.sibling),ln(c=n(s,p.props),p),c.return=o,o=c;break A}e(o,s);break}t(o,s),s=s.sibling}p.type===h?((c=Va(p.props.children,o.mode,c,p.key)).return=o,o=c):(ln(c=Oa(p.type,p.key,p.props,null,o.mode,c),p),c.return=o,o=c)}return i(o);case d:A:{for(g=p.key;null!==s;){if(s.key===g){if(4===s.tag&&s.stateNode.containerInfo===p.containerInfo&&s.stateNode.implementation===p.implementation){e(o,s.sibling),(c=n(s,p.children||[])).return=o,o=c;break A}e(o,s);break}t(o,s),s=s.sibling}(c=ja(p,o.mode,c)).return=o,o=c}return i(o);case b:return W(o,s,p=Zr(p),c)}if(T(p))return function(a,n,i,o){for(var s=null,p=null,c=n,u=n=0,d=null;null!==c&&u<i.length;u++){c.index>u?(d=c,c=null):d=c.sibling;var h=f(a,c,i[u],o);if(null===h){null===c&&(c=d);break}A&&c&&null===h.alternate&&t(a,c),n=l(h,n,u),null===p?s=h:p.sibling=h,p=h,c=d}if(u===i.length)return e(a,c),ir&&Ar(a,u),s;if(null===c){for(;u<i.length;u++)null!==(c=S(a,i[u],o))&&(n=l(c,n,u),null===p?s=c:p.sibling=c,p=c);return ir&&Ar(a,u),s}for(c=r(c);u<i.length;u++)null!==(d=m(c,a,u,i[u],o))&&(A&&null!==d.alternate&&c.delete(null===d.key?u:d.key),n=l(d,n,u),null===p?s=d:p.sibling=d,p=d);return A&&c.forEach(function(A){return t(a,A)}),ir&&Ar(a,u),s}(o,s,p,c);if(w(p)){if(\"function\"!=typeof(g=w(p)))throw Error(a(150));return function(n,i,o,s){if(null==o)throw Error(a(151));for(var p=null,c=null,u=i,d=i=0,h=null,L=o.next();null!==u&&!L.done;d++,L=o.next()){u.index>d?(h=u,u=null):h=u.sibling;var W=f(n,u,L.value,s);if(null===W){null===u&&(u=h);break}A&&u&&null===W.alternate&&t(n,u),i=l(W,i,d),null===c?p=W:c.sibling=W,c=W,u=h}if(L.done)return e(n,u),ir&&Ar(n,d),p;if(null===u){for(;!L.done;d++,L=o.next())null!==(L=S(n,L.value,s))&&(i=l(L,i,d),null===c?p=L:c.sibling=L,c=L);return ir&&Ar(n,d),p}for(u=r(u);!L.done;d++,L=o.next())null!==(L=m(u,n,d,L.value,s))&&(A&&null!==L.alternate&&u.delete(null===L.key?d:L.key),i=l(L,i,d),null===c?p=L:c.sibling=L,c=L);return A&&u.forEach(function(A){return t(n,A)}),ir&&Ar(n,d),p}(o,s,p=g.call(p),c)}if(\"function\"==typeof p.then)return W(o,s,nn(p),c);if(p.$$typeof===L)return W(o,s,Pr(o,p),c);on(o,p)}return\"string\"==typeof p&&\"\"!==p||\"number\"==typeof p||\"bigint\"==typeof p?(p=\"\"+p,null!==s&&6===s.tag?(e(o,s.sibling),(c=n(s,p)).return=o,o=c):(e(o,s),(c=Ba(p,o.mode,c)).return=o,o=c),i(o)):e(o,s)}return function(A,t,e,a){try{rn=0;var r=W(A,t,e,a);return an=null,r}catch(l){if(l===Ur||l===Jr)throw l;var n=Ra(29,l,null,A.mode);return n.lanes=a,n.return=A,n}}}var pn=sn(!0),cn=sn(!1),un=!1;function dn(A){A.updateQueue={baseState:A.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function hn(A,t){A=A.updateQueue,t.updateQueue===A&&(t.updateQueue={baseState:A.baseState,firstBaseUpdate:A.firstBaseUpdate,lastBaseUpdate:A.lastBaseUpdate,shared:A.shared,callbacks:null})}function Sn(A){return{lane:A,tag:0,payload:null,callback:null,next:null}}function fn(A,t,e){var a=A.updateQueue;if(null===a)return null;if(a=a.shared,2&ps){var r=a.pending;return null===r?t.next=t:(t.next=r.next,r.next=t),a.pending=t,t=_a(A),Ta(A,null,e),t}return wa(A,a,t,e),_a(A)}function mn(A,t,e){if(null!==(t=t.updateQueue)&&(t=t.shared,4194048&e)){var a=t.lanes;e|=a&=A.pendingLanes,t.lanes=e,_A(A,e)}}function Ln(A,t){var e=A.updateQueue,a=A.alternate;if(null!==a&&e===(a=a.updateQueue)){var r=null,n=null;if(null!==(e=e.firstBaseUpdate)){do{var l={lane:e.lane,tag:e.tag,payload:e.payload,callback:null,next:null};null===n?r=n=l:n=n.next=l,e=e.next}while(null!==e);null===n?r=n=t:n=n.next=t}else r=n=t;return e={baseState:a.baseState,firstBaseUpdate:r,lastBaseUpdate:n,shared:a.shared,callbacks:a.callbacks},void(A.updateQueue=e)}null===(A=e.lastBaseUpdate)?e.firstBaseUpdate=t:A.next=t,e.lastBaseUpdate=t}var Wn=!1;function gn(){if(Wn){if(null!==Br)throw Br}}function yn(A,t,e,a){Wn=!1;var r=A.updateQueue;un=!1;var n=r.firstBaseUpdate,l=r.lastBaseUpdate,i=r.shared.pending;if(null!==i){r.shared.pending=null;var o=i,s=o.next;o.next=null,null===l?n=s:l.next=s,l=o;var c=A.alternate;null!==c&&((i=(c=c.updateQueue).lastBaseUpdate)!==l&&(null===i?c.firstBaseUpdate=s:i.next=s,c.lastBaseUpdate=o))}if(null!==n){var u=r.baseState;for(l=0,c=s=o=null,i=n;;){var d=-536870913&i.lane,h=d!==i.lane;if(h?(ds&d)===d:(a&d)===d){0!==d&&d===Vr&&(Wn=!0),null!==c&&(c=c.next={lane:0,tag:i.tag,payload:i.payload,callback:null,next:null});A:{var S=A,f=i;d=t;var m=e;switch(f.tag){case 1:if(\"function\"==typeof(S=f.payload)){u=S.call(m,u,d);break A}u=S;break A;case 3:S.flags=-65537&S.flags|128;case 0:if(null==(d=\"function\"==typeof(S=f.payload)?S.call(m,u,d):S))break A;u=p({},u,d);break A;case 2:un=!0}}null!==(d=i.callback)&&(A.flags|=64,h&&(A.flags|=8192),null===(h=r.callbacks)?r.callbacks=[d]:h.push(d))}else h={lane:d,tag:i.tag,payload:i.payload,callback:i.callback,next:null},null===c?(s=c=h,o=u):c=c.next=h,l|=d;if(null===(i=i.next)){if(null===(i=r.shared.pending))break;i=(h=i).next,h.next=null,r.lastBaseUpdate=h,r.shared.pending=null}}null===c&&(o=u),r.baseState=o,r.firstBaseUpdate=s,r.lastBaseUpdate=c,null===n&&(r.shared.lanes=0),ys|=l,A.lanes=l,A.memoizedState=u}}function vn(A,t){if(\"function\"!=typeof A)throw Error(a(191,A));A.call(t)}function bn(A,t){var e=A.callbacks;if(null!==e)for(A.callbacks=null,A=0;A<e.length;A++)vn(e[A],t)}var xn=I(null),En=I(0);function kn(A,t){O(En,A=Ws),O(xn,t),Ws=A|t.baseLanes}function wn(){O(En,Ws),O(xn,xn.current)}function Cn(){Ws=En.current,F(xn),F(En)}var Pn=I(null),Tn=null;function _n(A){var t=A.alternate;O(In,1&In.current),O(Pn,A),null===Tn&&(null===t||null!==xn.current||null!==t.memoizedState)&&(Tn=A)}function Mn(A){O(In,In.current),O(Pn,A),null===Tn&&(Tn=A)}function Dn(A){22===A.tag?(O(In,In.current),O(Pn,A),null===Tn&&(Tn=A)):Rn()}function Rn(){O(In,In.current),O(Pn,Pn.current)}function Nn(A){F(Pn),Tn===A&&(Tn=null),F(In)}var In=I(0);function Fn(A){for(var t=A;null!==t;){if(13===t.tag){var e=t.memoizedState;if(null!==e&&(null===(e=e.dehydrated)||Cc(e)||Pc(e)))return t}else if(19!==t.tag||\"forwards\"!==t.memoizedProps.revealOrder&&\"backwards\"!==t.memoizedProps.revealOrder&&\"unstable_legacy-backwards\"!==t.memoizedProps.revealOrder&&\"together\"!==t.memoizedProps.revealOrder){if(null!==t.child){t.child.return=t,t=t.child;continue}}else if(128&t.flags)return t;if(t===A)break;for(;null===t.sibling;){if(null===t.return||t.return===A)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}var On=0,Vn=null,Bn=null,zn=null,jn=!1,$n=!1,Hn=!1,Gn=0,qn=0,Un=null,Xn=0;function Jn(){throw Error(a(321))}function Yn(A,t){if(null===t)return!1;for(var e=0;e<t.length&&e<A.length;e++)if(!Xe(A[e],t[e]))return!1;return!0}function Kn(A,t,e,a,r,n){return On=n,Vn=t,t.memoizedState=null,t.updateQueue=null,t.lanes=0,_.H=null===A||null===A.memoizedState?ui:di,Hn=!1,n=e(a,r),Hn=!1,$n&&(n=Zn(t,e,a,r)),Qn(A),n}function Qn(A){_.H=ci;var t=null!==Bn&&null!==Bn.next;if(On=0,zn=Bn=Vn=null,jn=!1,qn=0,Un=null,t)throw Error(a(300));null===A||Pi||null!==(A=A.dependencies)&&kr(A)&&(Pi=!0)}function Zn(A,t,e,r){Vn=A;var n=0;do{if($n&&(Un=null),qn=0,$n=!1,25<=n)throw Error(a(301));if(n+=1,zn=Bn=null,null!=A.updateQueue){var l=A.updateQueue;l.lastEffect=null,l.events=null,l.stores=null,null!=l.memoCache&&(l.memoCache.index=0)}_.H=hi,l=t(e,r)}while($n);return l}function Al(){var A=_.H,t=A.useState()[0];return t=\"function\"==typeof t.then?ll(t):t,A=A.useState()[0],(null!==Bn?Bn.memoizedState:null)!==A&&(Vn.flags|=1024),t}function tl(){var A=0!==Gn;return Gn=0,A}function el(A,t,e){t.updateQueue=A.updateQueue,t.flags&=-2053,A.lanes&=~e}function al(A){if(jn){for(A=A.memoizedState;null!==A;){var t=A.queue;null!==t&&(t.pending=null),A=A.next}jn=!1}On=0,zn=Bn=Vn=null,$n=!1,qn=Gn=0,Un=null}function rl(){var A={memoizedState:null,baseState:null,baseQueue:null,queue:null,next:null};return null===zn?Vn.memoizedState=zn=A:zn=zn.next=A,zn}function nl(){if(null===Bn){var A=Vn.alternate;A=null!==A?A.memoizedState:null}else A=Bn.next;var t=null===zn?Vn.memoizedState:zn.next;if(null!==t)zn=t,Bn=A;else{if(null===A){if(null===Vn.alternate)throw Error(a(467));throw Error(a(310))}A={memoizedState:(Bn=A).memoizedState,baseState:Bn.baseState,baseQueue:Bn.baseQueue,queue:Bn.queue,next:null},null===zn?Vn.memoizedState=zn=A:zn=zn.next=A}return zn}function ll(A){var t=qn;return qn+=1,null===Un&&(Un=[]),A=Qr(Un,A,t),t=Vn,null===(null===zn?t.memoizedState:zn.next)&&(t=t.alternate,_.H=null===t||null===t.memoizedState?ui:di),A}function il(A){if(null!==A&&\"object\"==typeof A){if(\"function\"==typeof A.then)return ll(A);if(A.$$typeof===L)return Cr(A)}throw Error(a(438,String(A)))}function ol(A){var t=null,e=Vn.updateQueue;if(null!==e&&(t=e.memoCache),null==t){var a=Vn.alternate;null!==a&&(null!==(a=a.updateQueue)&&(null!=(a=a.memoCache)&&(t={data:a.data.map(function(A){return A.slice()}),index:0})))}if(null==t&&(t={data:[],index:0}),null===e&&(e={lastEffect:null,events:null,stores:null,memoCache:null},Vn.updateQueue=e),e.memoCache=t,void 0===(e=t.data[t.index]))for(e=t.data[t.index]=Array(A),a=0;a<A;a++)e[a]=E;return t.index++,e}function sl(A,t){return\"function\"==typeof t?t(A):t}function pl(A){return cl(nl(),Bn,A)}function cl(A,t,e){var r=A.queue;if(null===r)throw Error(a(311));r.lastRenderedReducer=e;var n=A.baseQueue,l=r.pending;if(null!==l){if(null!==n){var i=n.next;n.next=l.next,l.next=i}t.baseQueue=n=l,r.pending=null}if(l=A.baseState,null===n)A.memoizedState=l;else{var o=i=null,s=null,p=t=n.next,c=!1;do{var u=-536870913&p.lane;if(u!==p.lane?(ds&u)===u:(On&u)===u){var d=p.revertLane;if(0===d)null!==s&&(s=s.next={lane:0,revertLane:0,gesture:null,action:p.action,hasEagerState:p.hasEagerState,eagerState:p.eagerState,next:null}),u===Vr&&(c=!0);else{if((On&d)===d){p=p.next,d===Vr&&(c=!0);continue}u={lane:0,revertLane:p.revertLane,gesture:null,action:p.action,hasEagerState:p.hasEagerState,eagerState:p.eagerState,next:null},null===s?(o=s=u,i=l):s=s.next=u,Vn.lanes|=d,ys|=d}u=p.action,Hn&&e(l,u),l=p.hasEagerState?p.eagerState:e(l,u)}else d={lane:u,revertLane:p.revertLane,gesture:p.gesture,action:p.action,hasEagerState:p.hasEagerState,eagerState:p.eagerState,next:null},null===s?(o=s=d,i=l):s=s.next=d,Vn.lanes|=u,ys|=u;p=p.next}while(null!==p&&p!==t);if(null===s?i=l:s.next=o,!Xe(l,A.memoizedState)&&(Pi=!0,c&&null!==(e=Br)))throw e;A.memoizedState=l,A.baseState=i,A.baseQueue=s,r.lastRenderedState=l}return null===n&&(r.lanes=0),[A.memoizedState,r.dispatch]}function ul(A){var t=nl(),e=t.queue;if(null===e)throw Error(a(311));e.lastRenderedReducer=A;var r=e.dispatch,n=e.pending,l=t.memoizedState;if(null!==n){e.pending=null;var i=n=n.next;do{l=A(l,i.action),i=i.next}while(i!==n);Xe(l,t.memoizedState)||(Pi=!0),t.memoizedState=l,null===t.baseQueue&&(t.baseState=l),e.lastRenderedState=l}return[l,r]}function dl(A,t,e){var r=Vn,n=nl(),l=ir;if(l){if(void 0===e)throw Error(a(407));e=e()}else e=t();var i=!Xe((Bn||n).memoizedState,e);if(i&&(n.memoizedState=e,Pi=!0),n=n.queue,Ol(fl.bind(null,r,n,A),[A]),n.getSnapshot!==t||i||null!==zn&&1&zn.memoizedState.tag){if(r.flags|=2048,Dl(9,{destroy:void 0},Sl.bind(null,r,n,e,t),null),null===cs)throw Error(a(349));l||127&On||hl(r,t,e)}return e}function hl(A,t,e){A.flags|=16384,A={getSnapshot:t,value:e},null===(t=Vn.updateQueue)?(t={lastEffect:null,events:null,stores:null,memoCache:null},Vn.updateQueue=t,t.stores=[A]):null===(e=t.stores)?t.stores=[A]:e.push(A)}function Sl(A,t,e,a){t.value=e,t.getSnapshot=a,ml(t)&&Ll(A)}function fl(A,t,e){return e(function(){ml(t)&&Ll(A)})}function ml(A){var t=A.getSnapshot;A=A.value;try{var e=t();return!Xe(A,e)}catch(a){return!0}}function Ll(A){var t=Pa(A,2);null!==t&&Gs(t,A,2)}function Wl(A){var t=rl();if(\"function\"==typeof A){var e=A;if(A=e(),Hn){fA(!0);try{e()}finally{fA(!1)}}}return t.memoizedState=t.baseState=A,t.queue={pending:null,lanes:0,dispatch:null,lastRenderedReducer:sl,lastRenderedState:A},t}function gl(A,t,e,a){return A.baseState=e,cl(A,Bn,\"function\"==typeof a?a:sl)}function yl(A,t,e,r,n){if(oi(A))throw Error(a(485));if(null!==(A=t.action)){var l={payload:n,action:A,next:null,isTransition:!0,status:\"pending\",value:null,reason:null,listeners:[],then:function(A){l.listeners.push(A)}};null!==_.T?e(!0):l.isTransition=!1,r(l),null===(e=t.pending)?(l.next=t.pending=l,vl(t,l)):(l.next=e.next,t.pending=e.next=l)}}function vl(A,t){var e=t.action,a=t.payload,r=A.state;if(t.isTransition){var n=_.T,l={};_.T=l;try{var i=e(r,a),o=_.S;null!==o&&o(l,i),bl(A,t,i)}catch(s){El(A,t,s)}finally{null!==n&&null!==l.types&&(n.types=l.types),_.T=n}}else try{bl(A,t,n=e(r,a))}catch(p){El(A,t,p)}}function bl(A,t,e){null!==e&&\"object\"==typeof e&&\"function\"==typeof e.then?e.then(function(e){xl(A,t,e)},function(e){return El(A,t,e)}):xl(A,t,e)}function xl(A,t,e){t.status=\"fulfilled\",t.value=e,kl(t),A.state=e,null!==(t=A.pending)&&((e=t.next)===t?A.pending=null:(e=e.next,t.next=e,vl(A,e)))}function El(A,t,e){var a=A.pending;if(A.pending=null,null!==a){a=a.next;do{t.status=\"rejected\",t.reason=e,kl(t),t=t.next}while(t!==a)}A.action=null}function kl(A){A=A.listeners;for(var t=0;t<A.length;t++)(0,A[t])()}function wl(A,t){return t}function Cl(A,t){if(ir){var e=cs.formState;if(null!==e){A:{var a=Vn;if(ir){if(lr){t:{for(var r=lr,n=sr;8!==r.nodeType;){if(!n){r=null;break t}if(null===(r=Tc(r.nextSibling))){r=null;break t}}r=\"F!\"===(n=r.data)||\"F\"===n?r:null}if(r){lr=Tc(r.nextSibling),a=\"F!\"===r.data;break A}}cr(a)}a=!1}a&&(t=e[0])}}return(e=rl()).memoizedState=e.baseState=t,a={pending:null,lanes:0,dispatch:null,lastRenderedReducer:wl,lastRenderedState:t},e.queue=a,e=ni.bind(null,Vn,a),a.dispatch=e,a=Wl(!1),n=ii.bind(null,Vn,!1,a.queue),r={state:t,dispatch:null,action:A,pending:null},(a=rl()).queue=r,e=yl.bind(null,Vn,r,n,e),r.dispatch=e,a.memoizedState=A,[t,e,!1]}function Pl(A){return Tl(nl(),Bn,A)}function Tl(A,t,e){if(t=cl(A,t,wl)[0],A=pl(sl)[0],\"object\"==typeof t&&null!==t&&\"function\"==typeof t.then)try{var a=ll(t)}catch(l){if(l===Ur)throw Jr;throw l}else a=t;var r=(t=nl()).queue,n=r.dispatch;return e!==t.memoizedState&&(Vn.flags|=2048,Dl(9,{destroy:void 0},_l.bind(null,r,e),null)),[a,n,A]}function _l(A,t){A.action=t}function Ml(A){var t=nl(),e=Bn;if(null!==e)return Tl(t,e,A);nl(),t=t.memoizedState;var a=(e=nl()).queue.dispatch;return e.memoizedState=A,[t,a,!1]}function Dl(A,t,e,a){return A={tag:A,create:e,deps:a,inst:t,next:null},null===(t=Vn.updateQueue)&&(t={lastEffect:null,events:null,stores:null,memoCache:null},Vn.updateQueue=t),null===(e=t.lastEffect)?t.lastEffect=A.next=A:(a=e.next,e.next=A,A.next=a,t.lastEffect=A),A}function Rl(){return nl().memoizedState}function Nl(A,t,e,a){var r=rl();Vn.flags|=A,r.memoizedState=Dl(1|t,{destroy:void 0},e,void 0===a?null:a)}function Il(A,t,e,a){var r=nl();a=void 0===a?null:a;var n=r.memoizedState.inst;null!==Bn&&null!==a&&Yn(a,Bn.memoizedState.deps)?r.memoizedState=Dl(t,n,e,a):(Vn.flags|=A,r.memoizedState=Dl(1|t,n,e,a))}function Fl(A,t){Nl(8390656,8,A,t)}function Ol(A,t){Il(2048,8,A,t)}function Vl(A){var t=nl().memoizedState;return function(A){Vn.flags|=4;var t=Vn.updateQueue;if(null===t)t={lastEffect:null,events:null,stores:null,memoCache:null},Vn.updateQueue=t,t.events=[A];else{var e=t.events;null===e?t.events=[A]:e.push(A)}}({ref:t,nextImpl:A}),function(){if(2&ps)throw Error(a(440));return t.impl.apply(void 0,arguments)}}function Bl(A,t){return Il(4,2,A,t)}function zl(A,t){return Il(4,4,A,t)}function jl(A,t){if(\"function\"==typeof t){A=A();var e=t(A);return function(){\"function\"==typeof e?e():t(null)}}if(null!=t)return A=A(),t.current=A,function(){t.current=null}}function $l(A,t,e){e=null!=e?e.concat([A]):null,Il(4,4,jl.bind(null,t,A),e)}function Hl(){}function Gl(A,t){var e=nl();t=void 0===t?null:t;var a=e.memoizedState;return null!==t&&Yn(t,a[1])?a[0]:(e.memoizedState=[A,t],A)}function ql(A,t){var e=nl();t=void 0===t?null:t;var a=e.memoizedState;if(null!==t&&Yn(t,a[1]))return a[0];if(a=A(),Hn){fA(!0);try{A()}finally{fA(!1)}}return e.memoizedState=[a,t],a}function Ul(A,t,e){return void 0===e||1073741824&On&&!(261930&ds)?A.memoizedState=t:(A.memoizedState=e,A=Hs(),Vn.lanes|=A,ys|=A,e)}function Xl(A,t,e,a){return Xe(e,t)?e:null!==xn.current?(A=Ul(A,e,a),Xe(A,t)||(Pi=!0),A):42&On&&(!(1073741824&On)||261930&ds)?(A=Hs(),Vn.lanes|=A,ys|=A,t):(Pi=!0,A.memoizedState=e)}function Jl(A,t,e,a,r){var n=M.p;M.p=0!==n&&8>n?n:8;var l,i,o,s=_.T,p={};_.T=p,ii(A,!1,t,e);try{var c=r(),u=_.S;if(null!==u&&u(p,c),null!==c&&\"object\"==typeof c&&\"function\"==typeof c.then)li(A,t,(l=a,i=[],o={status:\"pending\",value:null,reason:null,then:function(A){i.push(A)}},c.then(function(){o.status=\"fulfilled\",o.value=l;for(var A=0;A<i.length;A++)(0,i[A])(l)},function(A){for(o.status=\"rejected\",o.reason=A,A=0;A<i.length;A++)(0,i[A])(void 0)}),o),$s());else li(A,t,a,$s())}catch(d){li(A,t,{then:function(){},status:\"rejected\",reason:d},$s())}finally{M.p=n,null!==s&&null!==p.types&&(s.types=p.types),_.T=s}}function Yl(){}function Kl(A,t,e,r){if(5!==A.tag)throw Error(a(476));var n=Ql(A).queue;Jl(A,n,t,D,null===e?Yl:function(){return Zl(A),e(r)})}function Ql(A){var t=A.memoizedState;if(null!==t)return t;var e={};return(t={memoizedState:D,baseState:D,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:sl,lastRenderedState:D},next:null}).next={memoizedState:e,baseState:e,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:sl,lastRenderedState:e},next:null},A.memoizedState=t,null!==(A=A.alternate)&&(A.memoizedState=t),t}function Zl(A){var t=Ql(A);null===t.next&&(t=A.alternate.memoizedState),li(A,t.next.queue,{},$s())}function Ai(){return Cr(iu)}function ti(){return nl().memoizedState}function ei(){return nl().memoizedState}function ai(A){for(var t=A.return;null!==t;){switch(t.tag){case 24:case 3:var e=$s(),a=fn(t,A=Sn(e),e);return null!==a&&(Gs(a,t,e),mn(a,t,e)),t={cache:Nr()},void(A.payload=t)}t=t.return}}function ri(A,t,e){var a=$s();e={lane:a,revertLane:0,gesture:null,action:e,hasEagerState:!1,eagerState:null,next:null},oi(A)?si(t,e):null!==(e=Ca(A,t,e,a))&&(Gs(e,A,a),pi(e,t,a))}function ni(A,t,e){li(A,t,e,$s())}function li(A,t,e,a){var r={lane:a,revertLane:0,gesture:null,action:e,hasEagerState:!1,eagerState:null,next:null};if(oi(A))si(t,r);else{var n=A.alternate;if(0===A.lanes&&(null===n||0===n.lanes)&&null!==(n=t.lastRenderedReducer))try{var l=t.lastRenderedState,i=n(l,e);if(r.hasEagerState=!0,r.eagerState=i,Xe(i,l))return wa(A,t,r,0),null===cs&&ka(),!1}catch(o){}if(null!==(e=Ca(A,t,r,a)))return Gs(e,A,a),pi(e,t,a),!0}return!1}function ii(A,t,e,r){if(r={lane:2,revertLane:Vp(),gesture:null,action:r,hasEagerState:!1,eagerState:null,next:null},oi(A)){if(t)throw Error(a(479))}else null!==(t=Ca(A,e,r,2))&&Gs(t,A,2)}function oi(A){var t=A.alternate;return A===Vn||null!==t&&t===Vn}function si(A,t){$n=jn=!0;var e=A.pending;null===e?t.next=t:(t.next=e.next,e.next=t),A.pending=t}function pi(A,t,e){if(4194048&e){var a=t.lanes;e|=a&=A.pendingLanes,t.lanes=e,_A(A,e)}}var ci={readContext:Cr,use:il,useCallback:Jn,useContext:Jn,useEffect:Jn,useImperativeHandle:Jn,useLayoutEffect:Jn,useInsertionEffect:Jn,useMemo:Jn,useReducer:Jn,useRef:Jn,useState:Jn,useDebugValue:Jn,useDeferredValue:Jn,useTransition:Jn,useSyncExternalStore:Jn,useId:Jn,useHostTransitionStatus:Jn,useFormState:Jn,useActionState:Jn,useOptimistic:Jn,useMemoCache:Jn,useCacheRefresh:Jn};ci.useEffectEvent=Jn;var ui={readContext:Cr,use:il,useCallback:function(A,t){return rl().memoizedState=[A,void 0===t?null:t],A},useContext:Cr,useEffect:Fl,useImperativeHandle:function(A,t,e){e=null!=e?e.concat([A]):null,Nl(4194308,4,jl.bind(null,t,A),e)},useLayoutEffect:function(A,t){return Nl(4194308,4,A,t)},useInsertionEffect:function(A,t){Nl(4,2,A,t)},useMemo:function(A,t){var e=rl();t=void 0===t?null:t;var a=A();if(Hn){fA(!0);try{A()}finally{fA(!1)}}return e.memoizedState=[a,t],a},useReducer:function(A,t,e){var a=rl();if(void 0!==e){var r=e(t);if(Hn){fA(!0);try{e(t)}finally{fA(!1)}}}else r=t;return a.memoizedState=a.baseState=r,A={pending:null,lanes:0,dispatch:null,lastRenderedReducer:A,lastRenderedState:r},a.queue=A,A=A.dispatch=ri.bind(null,Vn,A),[a.memoizedState,A]},useRef:function(A){return A={current:A},rl().memoizedState=A},useState:function(A){var t=(A=Wl(A)).queue,e=ni.bind(null,Vn,t);return t.dispatch=e,[A.memoizedState,e]},useDebugValue:Hl,useDeferredValue:function(A,t){return Ul(rl(),A,t)},useTransition:function(){var A=Wl(!1);return A=Jl.bind(null,Vn,A.queue,!0,!1),rl().memoizedState=A,[!1,A]},useSyncExternalStore:function(A,t,e){var r=Vn,n=rl();if(ir){if(void 0===e)throw Error(a(407));e=e()}else{if(e=t(),null===cs)throw Error(a(349));127&ds||hl(r,t,e)}n.memoizedState=e;var l={value:e,getSnapshot:t};return n.queue=l,Fl(fl.bind(null,r,l,A),[A]),r.flags|=2048,Dl(9,{destroy:void 0},Sl.bind(null,r,l,e,t),null),e},useId:function(){var A=rl(),t=cs.identifierPrefix;if(ir){var e=Za;t=\"_\"+t+\"R_\"+(e=(Qa&~(1<<32-mA(Qa)-1)).toString(32)+e),0<(e=Gn++)&&(t+=\"H\"+e.toString(32)),t+=\"_\"}else t=\"_\"+t+\"r_\"+(e=Xn++).toString(32)+\"_\";return A.memoizedState=t},useHostTransitionStatus:Ai,useFormState:Cl,useActionState:Cl,useOptimistic:function(A){var t=rl();t.memoizedState=t.baseState=A;var e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:null,lastRenderedState:null};return t.queue=e,t=ii.bind(null,Vn,!0,e),e.dispatch=t,[A,t]},useMemoCache:ol,useCacheRefresh:function(){return rl().memoizedState=ai.bind(null,Vn)},useEffectEvent:function(A){var t=rl(),e={impl:A};return t.memoizedState=e,function(){if(2&ps)throw Error(a(440));return e.impl.apply(void 0,arguments)}}},di={readContext:Cr,use:il,useCallback:Gl,useContext:Cr,useEffect:Ol,useImperativeHandle:$l,useInsertionEffect:Bl,useLayoutEffect:zl,useMemo:ql,useReducer:pl,useRef:Rl,useState:function(){return pl(sl)},useDebugValue:Hl,useDeferredValue:function(A,t){return Xl(nl(),Bn.memoizedState,A,t)},useTransition:function(){var A=pl(sl)[0],t=nl().memoizedState;return[\"boolean\"==typeof A?A:ll(A),t]},useSyncExternalStore:dl,useId:ti,useHostTransitionStatus:Ai,useFormState:Pl,useActionState:Pl,useOptimistic:function(A,t){return gl(nl(),0,A,t)},useMemoCache:ol,useCacheRefresh:ei};di.useEffectEvent=Vl;var hi={readContext:Cr,use:il,useCallback:Gl,useContext:Cr,useEffect:Ol,useImperativeHandle:$l,useInsertionEffect:Bl,useLayoutEffect:zl,useMemo:ql,useReducer:ul,useRef:Rl,useState:function(){return ul(sl)},useDebugValue:Hl,useDeferredValue:function(A,t){var e=nl();return null===Bn?Ul(e,A,t):Xl(e,Bn.memoizedState,A,t)},useTransition:function(){var A=ul(sl)[0],t=nl().memoizedState;return[\"boolean\"==typeof A?A:ll(A),t]},useSyncExternalStore:dl,useId:ti,useHostTransitionStatus:Ai,useFormState:Ml,useActionState:Ml,useOptimistic:function(A,t){var e=nl();return null!==Bn?gl(e,0,A,t):(e.baseState=A,[A,e.queue.dispatch])},useMemoCache:ol,useCacheRefresh:ei};function Si(A,t,e,a){e=null==(e=e(a,t=A.memoizedState))?t:p({},t,e),A.memoizedState=e,0===A.lanes&&(A.updateQueue.baseState=e)}hi.useEffectEvent=Vl;var fi={enqueueSetState:function(A,t,e){A=A._reactInternals;var a=$s(),r=Sn(a);r.payload=t,null!=e&&(r.callback=e),null!==(t=fn(A,r,a))&&(Gs(t,A,a),mn(t,A,a))},enqueueReplaceState:function(A,t,e){A=A._reactInternals;var a=$s(),r=Sn(a);r.tag=1,r.payload=t,null!=e&&(r.callback=e),null!==(t=fn(A,r,a))&&(Gs(t,A,a),mn(t,A,a))},enqueueForceUpdate:function(A,t){A=A._reactInternals;var e=$s(),a=Sn(e);a.tag=2,null!=t&&(a.callback=t),null!==(t=fn(A,a,e))&&(Gs(t,A,e),mn(t,A,e))}};function mi(A,t,e,a,r,n,l){return\"function\"==typeof(A=A.stateNode).shouldComponentUpdate?A.shouldComponentUpdate(a,n,l):!t.prototype||!t.prototype.isPureReactComponent||(!Je(e,a)||!Je(r,n))}function Li(A,t,e,a){A=t.state,\"function\"==typeof t.componentWillReceiveProps&&t.componentWillReceiveProps(e,a),\"function\"==typeof t.UNSAFE_componentWillReceiveProps&&t.UNSAFE_componentWillReceiveProps(e,a),t.state!==A&&fi.enqueueReplaceState(t,t.state,null)}function Wi(A,t){var e=t;if(\"ref\"in t)for(var a in e={},t)\"ref\"!==a&&(e[a]=t[a]);if(A=A.defaultProps)for(var r in e===t&&(e=p({},e)),A)void 0===e[r]&&(e[r]=A[r]);return e}function gi(A){va(A)}function yi(A){}function vi(A){va(A)}function bi(A,t){try{(0,A.onUncaughtError)(t.value,{componentStack:t.stack})}catch(e){setTimeout(function(){throw e})}}function xi(A,t,e){try{(0,A.onCaughtError)(e.value,{componentStack:e.stack,errorBoundary:1===t.tag?t.stateNode:null})}catch(a){setTimeout(function(){throw a})}}function Ei(A,t,e){return(e=Sn(e)).tag=3,e.payload={element:null},e.callback=function(){bi(A,t)},e}function ki(A){return(A=Sn(A)).tag=3,A}function wi(A,t,e,a){var r=e.type.getDerivedStateFromError;if(\"function\"==typeof r){var n=a.value;A.payload=function(){return r(n)},A.callback=function(){xi(t,e,a)}}var l=e.stateNode;null!==l&&\"function\"==typeof l.componentDidCatch&&(A.callback=function(){xi(t,e,a),\"function\"!=typeof r&&(null===Ds?Ds=new Set([this]):Ds.add(this));var A=a.stack;this.componentDidCatch(a.value,{componentStack:null!==A?A:\"\"})})}var Ci=Error(a(461)),Pi=!1;function Ti(A,t,e,a){t.child=null===A?cn(t,null,e,a):pn(t,A.child,e,a)}function _i(A,t,e,a,r){e=e.render;var n=t.ref;if(\"ref\"in a){var l={};for(var i in a)\"ref\"!==i&&(l[i]=a[i])}else l=a;return wr(t),a=Kn(A,t,e,l,n,r),i=tl(),null===A||Pi?(ir&&i&&er(t),t.flags|=1,Ti(A,t,a,r),t.child):(el(A,t,r),Ao(A,t,r))}function Mi(A,t,e,a,r){if(null===A){var n=e.type;return\"function\"!=typeof n||Na(n)||void 0!==n.defaultProps||null!==e.compare?((A=Oa(e.type,null,a,t,t.mode,r)).ref=t.ref,A.return=t,t.child=A):(t.tag=15,t.type=n,Di(A,t,n,a,r))}if(n=A.child,!to(A,r)){var l=n.memoizedProps;if((e=null!==(e=e.compare)?e:Je)(l,a)&&A.ref===t.ref)return Ao(A,t,r)}return t.flags|=1,(A=Ia(n,a)).ref=t.ref,A.return=t,t.child=A}function Di(A,t,e,a,r){if(null!==A){var n=A.memoizedProps;if(Je(n,a)&&A.ref===t.ref){if(Pi=!1,t.pendingProps=a=n,!to(A,r))return t.lanes=A.lanes,Ao(A,t,r);131072&A.flags&&(Pi=!0)}}return Bi(A,t,e,a,r)}function Ri(A,t,e,a){var r=a.children,n=null!==A?A.memoizedState:null;if(null===A&&null===t.stateNode&&(t.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),\"hidden\"===a.mode){if(128&t.flags){if(n=null!==n?n.baseLanes|e:e,null!==A){for(a=t.child=A.child,r=0;null!==a;)r=r|a.lanes|a.childLanes,a=a.sibling;a=r&~n}else a=0,t.child=null;return Ii(A,t,n,e,a)}if(!(536870912&e))return a=t.lanes=536870912,Ii(A,t,null!==n?n.baseLanes|e:e,e,a);t.memoizedState={baseLanes:0,cachePool:null},null!==A&&Gr(0,null!==n?n.cachePool:null),null!==n?kn(t,n):wn(),Dn(t)}else null!==n?(Gr(0,n.cachePool),kn(t,n),Rn(),t.memoizedState=null):(null!==A&&Gr(0,null),wn(),Rn());return Ti(A,t,r,e),t.child}function Ni(A,t){return null!==A&&22===A.tag||null!==t.stateNode||(t.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),t.sibling}function Ii(A,t,e,a,r){var n=Hr();return n=null===n?null:{parent:Rr._currentValue,pool:n},t.memoizedState={baseLanes:e,cachePool:n},null!==A&&Gr(0,null),wn(),Dn(t),null!==A&&Er(A,t,a,!0),t.childLanes=r,null}function Fi(A,t){return(t=Ji({mode:t.mode,children:t.children},A.mode)).ref=A.ref,A.child=t,t.return=A,t}function Oi(A,t,e){return pn(t,A.child,null,e),(A=Fi(t,t.pendingProps)).flags|=2,Nn(t),t.memoizedState=null,A}function Vi(A,t){var e=t.ref;if(null===e)null!==A&&null!==A.ref&&(t.flags|=4194816);else{if(\"function\"!=typeof e&&\"object\"!=typeof e)throw Error(a(284));null!==A&&A.ref===e||(t.flags|=4194816)}}function Bi(A,t,e,a,r){return wr(t),e=Kn(A,t,e,a,void 0,r),a=tl(),null===A||Pi?(ir&&a&&er(t),t.flags|=1,Ti(A,t,e,r),t.child):(el(A,t,r),Ao(A,t,r))}function zi(A,t,e,a,r,n){return wr(t),t.updateQueue=null,e=Zn(t,a,e,r),Qn(A),a=tl(),null===A||Pi?(ir&&a&&er(t),t.flags|=1,Ti(A,t,e,n),t.child):(el(A,t,n),Ao(A,t,n))}function ji(A,t,e,a,r){if(wr(t),null===t.stateNode){var n=Ma,l=e.contextType;\"object\"==typeof l&&null!==l&&(n=Cr(l)),n=new e(a,n),t.memoizedState=null!==n.state&&void 0!==n.state?n.state:null,n.updater=fi,t.stateNode=n,n._reactInternals=t,(n=t.stateNode).props=a,n.state=t.memoizedState,n.refs={},dn(t),l=e.contextType,n.context=\"object\"==typeof l&&null!==l?Cr(l):Ma,n.state=t.memoizedState,\"function\"==typeof(l=e.getDerivedStateFromProps)&&(Si(t,e,l,a),n.state=t.memoizedState),\"function\"==typeof e.getDerivedStateFromProps||\"function\"==typeof n.getSnapshotBeforeUpdate||\"function\"!=typeof n.UNSAFE_componentWillMount&&\"function\"!=typeof n.componentWillMount||(l=n.state,\"function\"==typeof n.componentWillMount&&n.componentWillMount(),\"function\"==typeof n.UNSAFE_componentWillMount&&n.UNSAFE_componentWillMount(),l!==n.state&&fi.enqueueReplaceState(n,n.state,null),yn(t,a,n,r),gn(),n.state=t.memoizedState),\"function\"==typeof n.componentDidMount&&(t.flags|=4194308),a=!0}else if(null===A){n=t.stateNode;var i=t.memoizedProps,o=Wi(e,i);n.props=o;var s=n.context,p=e.contextType;l=Ma,\"object\"==typeof p&&null!==p&&(l=Cr(p));var c=e.getDerivedStateFromProps;p=\"function\"==typeof c||\"function\"==typeof n.getSnapshotBeforeUpdate,i=t.pendingProps!==i,p||\"function\"!=typeof n.UNSAFE_componentWillReceiveProps&&\"function\"!=typeof n.componentWillReceiveProps||(i||s!==l)&&Li(t,n,a,l),un=!1;var u=t.memoizedState;n.state=u,yn(t,a,n,r),gn(),s=t.memoizedState,i||u!==s||un?(\"function\"==typeof c&&(Si(t,e,c,a),s=t.memoizedState),(o=un||mi(t,e,o,a,u,s,l))?(p||\"function\"!=typeof n.UNSAFE_componentWillMount&&\"function\"!=typeof n.componentWillMount||(\"function\"==typeof n.componentWillMount&&n.componentWillMount(),\"function\"==typeof n.UNSAFE_componentWillMount&&n.UNSAFE_componentWillMount()),\"function\"==typeof n.componentDidMount&&(t.flags|=4194308)):(\"function\"==typeof n.componentDidMount&&(t.flags|=4194308),t.memoizedProps=a,t.memoizedState=s),n.props=a,n.state=s,n.context=l,a=o):(\"function\"==typeof n.componentDidMount&&(t.flags|=4194308),a=!1)}else{n=t.stateNode,hn(A,t),p=Wi(e,l=t.memoizedProps),n.props=p,c=t.pendingProps,u=n.context,s=e.contextType,o=Ma,\"object\"==typeof s&&null!==s&&(o=Cr(s)),(s=\"function\"==typeof(i=e.getDerivedStateFromProps)||\"function\"==typeof n.getSnapshotBeforeUpdate)||\"function\"!=typeof n.UNSAFE_componentWillReceiveProps&&\"function\"!=typeof n.componentWillReceiveProps||(l!==c||u!==o)&&Li(t,n,a,o),un=!1,u=t.memoizedState,n.state=u,yn(t,a,n,r),gn();var d=t.memoizedState;l!==c||u!==d||un||null!==A&&null!==A.dependencies&&kr(A.dependencies)?(\"function\"==typeof i&&(Si(t,e,i,a),d=t.memoizedState),(p=un||mi(t,e,p,a,u,d,o)||null!==A&&null!==A.dependencies&&kr(A.dependencies))?(s||\"function\"!=typeof n.UNSAFE_componentWillUpdate&&\"function\"!=typeof n.componentWillUpdate||(\"function\"==typeof n.componentWillUpdate&&n.componentWillUpdate(a,d,o),\"function\"==typeof n.UNSAFE_componentWillUpdate&&n.UNSAFE_componentWillUpdate(a,d,o)),\"function\"==typeof n.componentDidUpdate&&(t.flags|=4),\"function\"==typeof n.getSnapshotBeforeUpdate&&(t.flags|=1024)):(\"function\"!=typeof n.componentDidUpdate||l===A.memoizedProps&&u===A.memoizedState||(t.flags|=4),\"function\"!=typeof n.getSnapshotBeforeUpdate||l===A.memoizedProps&&u===A.memoizedState||(t.flags|=1024),t.memoizedProps=a,t.memoizedState=d),n.props=a,n.state=d,n.context=o,a=p):(\"function\"!=typeof n.componentDidUpdate||l===A.memoizedProps&&u===A.memoizedState||(t.flags|=4),\"function\"!=typeof n.getSnapshotBeforeUpdate||l===A.memoizedProps&&u===A.memoizedState||(t.flags|=1024),a=!1)}return n=a,Vi(A,t),a=!!(128&t.flags),n||a?(n=t.stateNode,e=a&&\"function\"!=typeof e.getDerivedStateFromError?null:n.render(),t.flags|=1,null!==A&&a?(t.child=pn(t,A.child,null,r),t.child=pn(t,null,e,r)):Ti(A,t,e,r),t.memoizedState=n.state,A=t.child):A=Ao(A,t,r),A}function $i(A,t,e,a){return Sr(),t.flags|=256,Ti(A,t,e,a),t.child}var Hi={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function Gi(A){return{baseLanes:A,cachePool:qr()}}function qi(A,t,e){return A=null!==A?A.childLanes&~e:0,t&&(A|=xs),A}function Ui(A,t,e){var r,n=t.pendingProps,l=!1,i=!!(128&t.flags);if((r=i)||(r=(null===A||null!==A.memoizedState)&&!!(2&In.current)),r&&(l=!0,t.flags&=-129),r=!!(32&t.flags),t.flags&=-33,null===A){if(ir){if(l?_n(t):Rn(),(A=lr)?null!==(A=null!==(A=wc(A,sr))&&\"&\"!==A.data?A:null)&&(t.memoizedState={dehydrated:A,treeContext:null!==Ka?{id:Qa,overflow:Za}:null,retryLane:536870912,hydrationErrors:null},(e=za(A)).return=t,t.child=e,nr=t,lr=null):A=null,null===A)throw cr(t);return Pc(A)?t.lanes=32:t.lanes=536870912,null}var o=n.children;return n=n.fallback,l?(Rn(),o=Ji({mode:\"hidden\",children:o},l=t.mode),n=Va(n,l,e,null),o.return=t,n.return=t,o.sibling=n,t.child=o,(n=t.child).memoizedState=Gi(e),n.childLanes=qi(A,r,e),t.memoizedState=Hi,Ni(null,n)):(_n(t),Xi(t,o))}var s=A.memoizedState;if(null!==s&&null!==(o=s.dehydrated)){if(i)256&t.flags?(_n(t),t.flags&=-257,t=Yi(A,t,e)):null!==t.memoizedState?(Rn(),t.child=A.child,t.flags|=128,t=null):(Rn(),o=n.fallback,l=t.mode,n=Ji({mode:\"visible\",children:n.children},l),(o=Va(o,l,e,null)).flags|=2,n.return=t,o.return=t,n.sibling=o,t.child=n,pn(t,A.child,null,e),(n=t.child).memoizedState=Gi(e),n.childLanes=qi(A,r,e),t.memoizedState=Hi,t=Ni(null,n));else if(_n(t),Pc(o)){if(r=o.nextSibling&&o.nextSibling.dataset)var p=r.dgst;r=p,(n=Error(a(419))).stack=\"\",n.digest=r,mr({value:n,source:null,stack:null}),t=Yi(A,t,e)}else if(Pi||Er(A,t,e,!1),r=0!==(e&A.childLanes),Pi||r){if(null!==(r=cs)&&(0!==(n=MA(r,e))&&n!==s.retryLane))throw s.retryLane=n,Pa(A,n),Gs(r,A,n),Ci;Cc(o)||ap(),t=Yi(A,t,e)}else Cc(o)?(t.flags|=192,t.child=A.child,t=null):(A=s.treeContext,lr=Tc(o.nextSibling),nr=t,ir=!0,or=null,sr=!1,null!==A&&rr(t,A),(t=Xi(t,n.children)).flags|=4096);return t}return l?(Rn(),o=n.fallback,l=t.mode,p=(s=A.child).sibling,(n=Ia(s,{mode:\"hidden\",children:n.children})).subtreeFlags=65011712&s.subtreeFlags,null!==p?o=Ia(p,o):(o=Va(o,l,e,null)).flags|=2,o.return=t,n.return=t,n.sibling=o,t.child=n,Ni(null,n),n=t.child,null===(o=A.child.memoizedState)?o=Gi(e):(null!==(l=o.cachePool)?(s=Rr._currentValue,l=l.parent!==s?{parent:s,pool:s}:l):l=qr(),o={baseLanes:o.baseLanes|e,cachePool:l}),n.memoizedState=o,n.childLanes=qi(A,r,e),t.memoizedState=Hi,Ni(A.child,n)):(_n(t),A=(e=A.child).sibling,(e=Ia(e,{mode:\"visible\",children:n.children})).return=t,e.sibling=null,null!==A&&(null===(r=t.deletions)?(t.deletions=[A],t.flags|=16):r.push(A)),t.child=e,t.memoizedState=null,e)}function Xi(A,t){return(t=Ji({mode:\"visible\",children:t},A.mode)).return=A,A.child=t}function Ji(A,t){return(A=Ra(22,A,null,t)).lanes=0,A}function Yi(A,t,e){return pn(t,A.child,null,e),(A=Xi(t,t.pendingProps.children)).flags|=2,t.memoizedState=null,A}function Ki(A,t,e){A.lanes|=t;var a=A.alternate;null!==a&&(a.lanes|=t),br(A.return,t,e)}function Qi(A,t,e,a,r,n){var l=A.memoizedState;null===l?A.memoizedState={isBackwards:t,rendering:null,renderingStartTime:0,last:a,tail:e,tailMode:r,treeForkCount:n}:(l.isBackwards=t,l.rendering=null,l.renderingStartTime=0,l.last=a,l.tail=e,l.tailMode=r,l.treeForkCount=n)}function Zi(A,t,e){var a=t.pendingProps,r=a.revealOrder,n=a.tail;a=a.children;var l=In.current,i=!!(2&l);if(i?(l=1&l|2,t.flags|=128):l&=1,O(In,l),Ti(A,t,a,e),a=ir?Xa:0,!i&&null!==A&&128&A.flags)A:for(A=t.child;null!==A;){if(13===A.tag)null!==A.memoizedState&&Ki(A,e,t);else if(19===A.tag)Ki(A,e,t);else if(null!==A.child){A.child.return=A,A=A.child;continue}if(A===t)break A;for(;null===A.sibling;){if(null===A.return||A.return===t)break A;A=A.return}A.sibling.return=A.return,A=A.sibling}switch(r){case\"forwards\":for(e=t.child,r=null;null!==e;)null!==(A=e.alternate)&&null===Fn(A)&&(r=e),e=e.sibling;null===(e=r)?(r=t.child,t.child=null):(r=e.sibling,e.sibling=null),Qi(t,!1,r,e,n,a);break;case\"backwards\":case\"unstable_legacy-backwards\":for(e=null,r=t.child,t.child=null;null!==r;){if(null!==(A=r.alternate)&&null===Fn(A)){t.child=r;break}A=r.sibling,r.sibling=e,e=r,r=A}Qi(t,!0,e,null,n,a);break;case\"together\":Qi(t,!1,null,null,void 0,a);break;default:t.memoizedState=null}return t.child}function Ao(A,t,e){if(null!==A&&(t.dependencies=A.dependencies),ys|=t.lanes,0===(e&t.childLanes)){if(null===A)return null;if(Er(A,t,e,!1),0===(e&t.childLanes))return null}if(null!==A&&t.child!==A.child)throw Error(a(153));if(null!==t.child){for(e=Ia(A=t.child,A.pendingProps),t.child=e,e.return=t;null!==A.sibling;)A=A.sibling,(e=e.sibling=Ia(A,A.pendingProps)).return=t;e.sibling=null}return t.child}function to(A,t){return 0!==(A.lanes&t)||!(null===(A=A.dependencies)||!kr(A))}function eo(A,t,e){if(null!==A)if(A.memoizedProps!==t.pendingProps)Pi=!0;else{if(!(to(A,e)||128&t.flags))return Pi=!1,function(A,t,e){switch(t.tag){case 3:G(t,t.stateNode.containerInfo),yr(0,Rr,A.memoizedState.cache),Sr();break;case 27:case 5:U(t);break;case 4:G(t,t.stateNode.containerInfo);break;case 10:yr(0,t.type,t.memoizedProps.value);break;case 31:if(null!==t.memoizedState)return t.flags|=128,Mn(t),null;break;case 13:var a=t.memoizedState;if(null!==a)return null!==a.dehydrated?(_n(t),t.flags|=128,null):0!==(e&t.child.childLanes)?Ui(A,t,e):(_n(t),null!==(A=Ao(A,t,e))?A.sibling:null);_n(t);break;case 19:var r=!!(128&A.flags);if((a=0!==(e&t.childLanes))||(Er(A,t,e,!1),a=0!==(e&t.childLanes)),r){if(a)return Zi(A,t,e);t.flags|=128}if(null!==(r=t.memoizedState)&&(r.rendering=null,r.tail=null,r.lastEffect=null),O(In,In.current),a)break;return null;case 22:return t.lanes=0,Ri(A,t,e,t.pendingProps);case 24:yr(0,Rr,A.memoizedState.cache)}return Ao(A,t,e)}(A,t,e);Pi=!!(131072&A.flags)}else Pi=!1,ir&&1048576&t.flags&&tr(t,Xa,t.index);switch(t.lanes=0,t.tag){case 16:A:{var r=t.pendingProps;if(A=Zr(t.elementType),t.type=A,\"function\"!=typeof A){if(null!=A){var n=A.$$typeof;if(n===W){t.tag=11,t=_i(null,t,A,r,e);break A}if(n===v){t.tag=14,t=Mi(null,t,A,r,e);break A}}throw t=P(A)||A,Error(a(306,t,\"\"))}Na(A)?(r=Wi(A,r),t.tag=1,t=ji(null,t,A,r,e)):(t.tag=0,t=Bi(null,t,A,r,e))}return t;case 0:return Bi(A,t,t.type,t.pendingProps,e);case 1:return ji(A,t,r=t.type,n=Wi(r,t.pendingProps),e);case 3:A:{if(G(t,t.stateNode.containerInfo),null===A)throw Error(a(387));r=t.pendingProps;var l=t.memoizedState;n=l.element,hn(A,t),yn(t,r,null,e);var i=t.memoizedState;if(r=i.cache,yr(0,Rr,r),r!==l.cache&&xr(t,[Rr],e,!0),gn(),r=i.element,l.isDehydrated){if(l={element:r,isDehydrated:!1,cache:i.cache},t.updateQueue.baseState=l,t.memoizedState=l,256&t.flags){t=$i(A,t,r,e);break A}if(r!==n){mr(n=Ha(Error(a(424)),t)),t=$i(A,t,r,e);break A}if(9===(A=t.stateNode.containerInfo).nodeType)A=A.body;else A=\"HTML\"===A.nodeName?A.ownerDocument.body:A;for(lr=Tc(A.firstChild),nr=t,ir=!0,or=null,sr=!0,e=cn(t,null,r,e),t.child=e;e;)e.flags=-3&e.flags|4096,e=e.sibling}else{if(Sr(),r===n){t=Ao(A,t,e);break A}Ti(A,t,r,e)}t=t.child}return t;case 26:return Vi(A,t),null===A?(e=jc(t.type,null,t.pendingProps,null))?t.memoizedState=e:ir||(e=t.type,A=t.pendingProps,(r=dc($.current).createElement(e))[OA]=t,r[VA]=A,sc(r,e,A),KA(r),t.stateNode=r):t.memoizedState=jc(t.type,A.memoizedProps,t.pendingProps,A.memoizedState),null;case 27:return U(t),null===A&&ir&&(r=t.stateNode=Rc(t.type,t.pendingProps,$.current),nr=t,sr=!0,n=lr,bc(t.type)?(_c=n,lr=Tc(r.firstChild)):lr=n),Ti(A,t,t.pendingProps.children,e),Vi(A,t),null===A&&(t.flags|=4194304),t.child;case 5:return null===A&&ir&&((n=r=lr)&&(null!==(r=function(A,t,e,a){for(;1===A.nodeType;){var r=e;if(A.nodeName.toLowerCase()!==t.toLowerCase()){if(!a&&(\"INPUT\"!==A.nodeName||\"hidden\"!==A.type))break}else if(a){if(!A[GA])switch(t){case\"meta\":if(!A.hasAttribute(\"itemprop\"))break;return A;case\"link\":if(\"stylesheet\"===(n=A.getAttribute(\"rel\"))&&A.hasAttribute(\"data-precedence\"))break;if(n!==r.rel||A.getAttribute(\"href\")!==(null==r.href||\"\"===r.href?null:r.href)||A.getAttribute(\"crossorigin\")!==(null==r.crossOrigin?null:r.crossOrigin)||A.getAttribute(\"title\")!==(null==r.title?null:r.title))break;return A;case\"style\":if(A.hasAttribute(\"data-precedence\"))break;return A;case\"script\":if(((n=A.getAttribute(\"src\"))!==(null==r.src?null:r.src)||A.getAttribute(\"type\")!==(null==r.type?null:r.type)||A.getAttribute(\"crossorigin\")!==(null==r.crossOrigin?null:r.crossOrigin))&&n&&A.hasAttribute(\"async\")&&!A.hasAttribute(\"itemprop\"))break;return A;default:return A}}else{if(\"input\"!==t||\"hidden\"!==A.type)return A;var n=null==r.name?null:\"\"+r.name;if(\"hidden\"===r.type&&A.getAttribute(\"name\")===n)return A}if(null===(A=Tc(A.nextSibling)))break}return null}(r,t.type,t.pendingProps,sr))?(t.stateNode=r,nr=t,lr=Tc(r.firstChild),sr=!1,n=!0):n=!1),n||cr(t)),U(t),n=t.type,l=t.pendingProps,i=null!==A?A.memoizedProps:null,r=l.children,fc(n,l)?r=null:null!==i&&fc(n,i)&&(t.flags|=32),null!==t.memoizedState&&(n=Kn(A,t,Al,null,null,e),iu._currentValue=n),Vi(A,t),Ti(A,t,r,e),t.child;case 6:return null===A&&ir&&((A=e=lr)&&(null!==(e=function(A,t,e){if(\"\"===t)return null;for(;3!==A.nodeType;){if((1!==A.nodeType||\"INPUT\"!==A.nodeName||\"hidden\"!==A.type)&&!e)return null;if(null===(A=Tc(A.nextSibling)))return null}return A}(e,t.pendingProps,sr))?(t.stateNode=e,nr=t,lr=null,A=!0):A=!1),A||cr(t)),null;case 13:return Ui(A,t,e);case 4:return G(t,t.stateNode.containerInfo),r=t.pendingProps,null===A?t.child=pn(t,null,r,e):Ti(A,t,r,e),t.child;case 11:return _i(A,t,t.type,t.pendingProps,e);case 7:return Ti(A,t,t.pendingProps,e),t.child;case 8:case 12:return Ti(A,t,t.pendingProps.children,e),t.child;case 10:return r=t.pendingProps,yr(0,t.type,r.value),Ti(A,t,r.children,e),t.child;case 9:return n=t.type._context,r=t.pendingProps.children,wr(t),r=r(n=Cr(n)),t.flags|=1,Ti(A,t,r,e),t.child;case 14:return Mi(A,t,t.type,t.pendingProps,e);case 15:return Di(A,t,t.type,t.pendingProps,e);case 19:return Zi(A,t,e);case 31:return function(A,t,e){var r=t.pendingProps,n=!!(128&t.flags);if(t.flags&=-129,null===A){if(ir){if(\"hidden\"===r.mode)return A=Fi(t,r),t.lanes=536870912,Ni(null,A);if(Mn(t),(A=lr)?null!==(A=null!==(A=wc(A,sr))&&\"&\"===A.data?A:null)&&(t.memoizedState={dehydrated:A,treeContext:null!==Ka?{id:Qa,overflow:Za}:null,retryLane:536870912,hydrationErrors:null},(e=za(A)).return=t,t.child=e,nr=t,lr=null):A=null,null===A)throw cr(t);return t.lanes=536870912,null}return Fi(t,r)}var l=A.memoizedState;if(null!==l){var i=l.dehydrated;if(Mn(t),n)if(256&t.flags)t.flags&=-257,t=Oi(A,t,e);else{if(null===t.memoizedState)throw Error(a(558));t.child=A.child,t.flags|=128,t=null}else if(Pi||Er(A,t,e,!1),n=0!==(e&A.childLanes),Pi||n){if(null!==(r=cs)&&0!==(i=MA(r,e))&&i!==l.retryLane)throw l.retryLane=i,Pa(A,i),Gs(r,A,i),Ci;ap(),t=Oi(A,t,e)}else A=l.treeContext,lr=Tc(i.nextSibling),nr=t,ir=!0,or=null,sr=!1,null!==A&&rr(t,A),(t=Fi(t,r)).flags|=4096;return t}return(A=Ia(A.child,{mode:r.mode,children:r.children})).ref=t.ref,t.child=A,A.return=t,A}(A,t,e);case 22:return Ri(A,t,e,t.pendingProps);case 24:return wr(t),r=Cr(Rr),null===A?(null===(n=Hr())&&(n=cs,l=Nr(),n.pooledCache=l,l.refCount++,null!==l&&(n.pooledCacheLanes|=e),n=l),t.memoizedState={parent:r,cache:n},dn(t),yr(0,Rr,n)):(0!==(A.lanes&e)&&(hn(A,t),yn(t,null,null,e),gn()),n=A.memoizedState,l=t.memoizedState,n.parent!==r?(n={parent:r,cache:r},t.memoizedState=n,0===t.lanes&&(t.memoizedState=t.updateQueue.baseState=n),yr(0,Rr,r)):(r=l.cache,yr(0,Rr,r),r!==n.cache&&xr(t,[Rr],e,!0))),Ti(A,t,t.pendingProps.children,e),t.child;case 29:throw t.pendingProps}throw Error(a(156,t.tag))}function ao(A){A.flags|=4}function ro(A,t,e,a,r){if((t=!!(32&A.mode))&&(t=!1),t){if(A.flags|=16777216,(335544128&r)===r)if(A.stateNode.complete)A.flags|=8192;else{if(!Ap())throw An=Yr,Xr;A.flags|=8192}}else A.flags&=-16777217}function no(A,t){if(\"stylesheet\"!==t.type||4&t.state.loading)A.flags&=-16777217;else if(A.flags|=16777216,!tu(t)){if(!Ap())throw An=Yr,Xr;A.flags|=8192}}function lo(A,t){null!==t&&(A.flags|=4),16384&A.flags&&(t=22!==A.tag?wA():536870912,A.lanes|=t,Es|=t)}function io(A,t){if(!ir)switch(A.tailMode){case\"hidden\":t=A.tail;for(var e=null;null!==t;)null!==t.alternate&&(e=t),t=t.sibling;null===e?A.tail=null:e.sibling=null;break;case\"collapsed\":e=A.tail;for(var a=null;null!==e;)null!==e.alternate&&(a=e),e=e.sibling;null===a?t||null===A.tail?A.tail=null:A.tail.sibling=null:a.sibling=null}}function oo(A){var t=null!==A.alternate&&A.alternate.child===A.child,e=0,a=0;if(t)for(var r=A.child;null!==r;)e|=r.lanes|r.childLanes,a|=65011712&r.subtreeFlags,a|=65011712&r.flags,r.return=A,r=r.sibling;else for(r=A.child;null!==r;)e|=r.lanes|r.childLanes,a|=r.subtreeFlags,a|=r.flags,r.return=A,r=r.sibling;return A.subtreeFlags|=a,A.childLanes=e,t}function so(A,t,e){var r=t.pendingProps;switch(ar(t),t.tag){case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:case 1:return oo(t),null;case 3:return e=t.stateNode,r=null,null!==A&&(r=A.memoizedState.cache),t.memoizedState.cache!==r&&(t.flags|=2048),vr(Rr),q(),e.pendingContext&&(e.context=e.pendingContext,e.pendingContext=null),null!==A&&null!==A.child||(hr(t)?ao(t):null===A||A.memoizedState.isDehydrated&&!(256&t.flags)||(t.flags|=1024,fr())),oo(t),null;case 26:var n=t.type,l=t.memoizedState;return null===A?(ao(t),null!==l?(oo(t),no(t,l)):(oo(t),ro(t,n,0,0,e))):l?l!==A.memoizedState?(ao(t),oo(t),no(t,l)):(oo(t),t.flags&=-16777217):((A=A.memoizedProps)!==r&&ao(t),oo(t),ro(t,n,0,0,e)),null;case 27:if(X(t),e=$.current,n=t.type,null!==A&&null!=t.stateNode)A.memoizedProps!==r&&ao(t);else{if(!r){if(null===t.stateNode)throw Error(a(166));return oo(t),null}A=z.current,hr(t)?ur(t):(A=Rc(n,r,e),t.stateNode=A,ao(t))}return oo(t),null;case 5:if(X(t),n=t.type,null!==A&&null!=t.stateNode)A.memoizedProps!==r&&ao(t);else{if(!r){if(null===t.stateNode)throw Error(a(166));return oo(t),null}if(l=z.current,hr(t))ur(t);else{var i=dc($.current);switch(l){case 1:l=i.createElementNS(\"http://www.w3.org/2000/svg\",n);break;case 2:l=i.createElementNS(\"http://www.w3.org/1998/Math/MathML\",n);break;default:switch(n){case\"svg\":l=i.createElementNS(\"http://www.w3.org/2000/svg\",n);break;case\"math\":l=i.createElementNS(\"http://www.w3.org/1998/Math/MathML\",n);break;case\"script\":(l=i.createElement(\"div\")).innerHTML=\"<script><\\/script>\",l=l.removeChild(l.firstChild);break;case\"select\":l=\"string\"==typeof r.is?i.createElement(\"select\",{is:r.is}):i.createElement(\"select\"),r.multiple?l.multiple=!0:r.size&&(l.size=r.size);break;default:l=\"string\"==typeof r.is?i.createElement(n,{is:r.is}):i.createElement(n)}}l[OA]=t,l[VA]=r;A:for(i=t.child;null!==i;){if(5===i.tag||6===i.tag)l.appendChild(i.stateNode);else if(4!==i.tag&&27!==i.tag&&null!==i.child){i.child.return=i,i=i.child;continue}if(i===t)break A;for(;null===i.sibling;){if(null===i.return||i.return===t)break A;i=i.return}i.sibling.return=i.return,i=i.sibling}t.stateNode=l;A:switch(sc(l,n,r),n){case\"button\":case\"input\":case\"select\":case\"textarea\":r=!!r.autoFocus;break A;case\"img\":r=!0;break A;default:r=!1}r&&ao(t)}}return oo(t),ro(t,t.type,null===A||A.memoizedProps,t.pendingProps,e),null;case 6:if(A&&null!=t.stateNode)A.memoizedProps!==r&&ao(t);else{if(\"string\"!=typeof r&&null===t.stateNode)throw Error(a(166));if(A=$.current,hr(t)){if(A=t.stateNode,e=t.memoizedProps,r=null,null!==(n=nr))switch(n.tag){case 27:case 5:r=n.memoizedProps}A[OA]=t,(A=!!(A.nodeValue===e||null!==r&&!0===r.suppressHydrationWarning||lc(A.nodeValue,e)))||cr(t,!0)}else(A=dc(A).createTextNode(r))[OA]=t,t.stateNode=A}return oo(t),null;case 31:if(e=t.memoizedState,null===A||null!==A.memoizedState){if(r=hr(t),null!==e){if(null===A){if(!r)throw Error(a(318));if(!(A=null!==(A=t.memoizedState)?A.dehydrated:null))throw Error(a(557));A[OA]=t}else Sr(),!(128&t.flags)&&(t.memoizedState=null),t.flags|=4;oo(t),A=!1}else e=fr(),null!==A&&null!==A.memoizedState&&(A.memoizedState.hydrationErrors=e),A=!0;if(!A)return 256&t.flags?(Nn(t),t):(Nn(t),null);if(128&t.flags)throw Error(a(558))}return oo(t),null;case 13:if(r=t.memoizedState,null===A||null!==A.memoizedState&&null!==A.memoizedState.dehydrated){if(n=hr(t),null!==r&&null!==r.dehydrated){if(null===A){if(!n)throw Error(a(318));if(!(n=null!==(n=t.memoizedState)?n.dehydrated:null))throw Error(a(317));n[OA]=t}else Sr(),!(128&t.flags)&&(t.memoizedState=null),t.flags|=4;oo(t),n=!1}else n=fr(),null!==A&&null!==A.memoizedState&&(A.memoizedState.hydrationErrors=n),n=!0;if(!n)return 256&t.flags?(Nn(t),t):(Nn(t),null)}return Nn(t),128&t.flags?(t.lanes=e,t):(e=null!==r,A=null!==A&&null!==A.memoizedState,e&&(n=null,null!==(r=t.child).alternate&&null!==r.alternate.memoizedState&&null!==r.alternate.memoizedState.cachePool&&(n=r.alternate.memoizedState.cachePool.pool),l=null,null!==r.memoizedState&&null!==r.memoizedState.cachePool&&(l=r.memoizedState.cachePool.pool),l!==n&&(r.flags|=2048)),e!==A&&e&&(t.child.flags|=8192),lo(t,t.updateQueue),oo(t),null);case 4:return q(),null===A&&Yp(t.stateNode.containerInfo),oo(t),null;case 10:return vr(t.type),oo(t),null;case 19:if(F(In),null===(r=t.memoizedState))return oo(t),null;if(n=!!(128&t.flags),null===(l=r.rendering))if(n)io(r,!1);else{if(0!==gs||null!==A&&128&A.flags)for(A=t.child;null!==A;){if(null!==(l=Fn(A))){for(t.flags|=128,io(r,!1),A=l.updateQueue,t.updateQueue=A,lo(t,A),t.subtreeFlags=0,A=e,e=t.child;null!==e;)Fa(e,A),e=e.sibling;return O(In,1&In.current|2),ir&&Ar(t,r.treeForkCount),t.child}A=A.sibling}null!==r.tail&&nA()>_s&&(t.flags|=128,n=!0,io(r,!1),t.lanes=4194304)}else{if(!n)if(null!==(A=Fn(l))){if(t.flags|=128,n=!0,A=A.updateQueue,t.updateQueue=A,lo(t,A),io(r,!0),null===r.tail&&\"hidden\"===r.tailMode&&!l.alternate&&!ir)return oo(t),null}else 2*nA()-r.renderingStartTime>_s&&536870912!==e&&(t.flags|=128,n=!0,io(r,!1),t.lanes=4194304);r.isBackwards?(l.sibling=t.child,t.child=l):(null!==(A=r.last)?A.sibling=l:t.child=l,r.last=l)}return null!==r.tail?(A=r.tail,r.rendering=A,r.tail=A.sibling,r.renderingStartTime=nA(),A.sibling=null,e=In.current,O(In,n?1&e|2:1&e),ir&&Ar(t,r.treeForkCount),A):(oo(t),null);case 22:case 23:return Nn(t),Cn(),r=null!==t.memoizedState,null!==A?null!==A.memoizedState!==r&&(t.flags|=8192):r&&(t.flags|=8192),r?!!(536870912&e)&&!(128&t.flags)&&(oo(t),6&t.subtreeFlags&&(t.flags|=8192)):oo(t),null!==(e=t.updateQueue)&&lo(t,e.retryQueue),e=null,null!==A&&null!==A.memoizedState&&null!==A.memoizedState.cachePool&&(e=A.memoizedState.cachePool.pool),r=null,null!==t.memoizedState&&null!==t.memoizedState.cachePool&&(r=t.memoizedState.cachePool.pool),r!==e&&(t.flags|=2048),null!==A&&F($r),null;case 24:return e=null,null!==A&&(e=A.memoizedState.cache),t.memoizedState.cache!==e&&(t.flags|=2048),vr(Rr),oo(t),null;case 25:case 30:return null}throw Error(a(156,t.tag))}function po(A,t){switch(ar(t),t.tag){case 1:return 65536&(A=t.flags)?(t.flags=-65537&A|128,t):null;case 3:return vr(Rr),q(),65536&(A=t.flags)&&!(128&A)?(t.flags=-65537&A|128,t):null;case 26:case 27:case 5:return X(t),null;case 31:if(null!==t.memoizedState){if(Nn(t),null===t.alternate)throw Error(a(340));Sr()}return 65536&(A=t.flags)?(t.flags=-65537&A|128,t):null;case 13:if(Nn(t),null!==(A=t.memoizedState)&&null!==A.dehydrated){if(null===t.alternate)throw Error(a(340));Sr()}return 65536&(A=t.flags)?(t.flags=-65537&A|128,t):null;case 19:return F(In),null;case 4:return q(),null;case 10:return vr(t.type),null;case 22:case 23:return Nn(t),Cn(),null!==A&&F($r),65536&(A=t.flags)?(t.flags=-65537&A|128,t):null;case 24:return vr(Rr),null;default:return null}}function co(A,t){switch(ar(t),t.tag){case 3:vr(Rr),q();break;case 26:case 27:case 5:X(t);break;case 4:q();break;case 31:null!==t.memoizedState&&Nn(t);break;case 13:Nn(t);break;case 19:F(In);break;case 10:vr(t.type);break;case 22:case 23:Nn(t),Cn(),null!==A&&F($r);break;case 24:vr(Rr)}}function uo(A,t){try{var e=t.updateQueue,a=null!==e?e.lastEffect:null;if(null!==a){var r=a.next;e=r;do{if((e.tag&A)===A){a=void 0;var n=e.create,l=e.inst;a=n(),l.destroy=a}e=e.next}while(e!==r)}}catch(i){gp(t,t.return,i)}}function ho(A,t,e){try{var a=t.updateQueue,r=null!==a?a.lastEffect:null;if(null!==r){var n=r.next;a=n;do{if((a.tag&A)===A){var l=a.inst,i=l.destroy;if(void 0!==i){l.destroy=void 0,r=t;var o=e,s=i;try{s()}catch(p){gp(r,o,p)}}}a=a.next}while(a!==n)}}catch(p){gp(t,t.return,p)}}function So(A){var t=A.updateQueue;if(null!==t){var e=A.stateNode;try{bn(t,e)}catch(a){gp(A,A.return,a)}}}function fo(A,t,e){e.props=Wi(A.type,A.memoizedProps),e.state=A.memoizedState;try{e.componentWillUnmount()}catch(a){gp(A,t,a)}}function mo(A,t){try{var e=A.ref;if(null!==e){switch(A.tag){case 26:case 27:case 5:var a=A.stateNode;break;default:a=A.stateNode}\"function\"==typeof e?A.refCleanup=e(a):e.current=a}}catch(r){gp(A,t,r)}}function Lo(A,t){var e=A.ref,a=A.refCleanup;if(null!==e)if(\"function\"==typeof a)try{a()}catch(r){gp(A,t,r)}finally{A.refCleanup=null,null!=(A=A.alternate)&&(A.refCleanup=null)}else if(\"function\"==typeof e)try{e(null)}catch(n){gp(A,t,n)}else e.current=null}function Wo(A){var t=A.type,e=A.memoizedProps,a=A.stateNode;try{A:switch(t){case\"button\":case\"input\":case\"select\":case\"textarea\":e.autoFocus&&a.focus();break A;case\"img\":e.src?a.src=e.src:e.srcSet&&(a.srcset=e.srcSet)}}catch(r){gp(A,A.return,r)}}function go(A,t,e){try{var r=A.stateNode;!function(A,t,e,r){switch(t){case\"div\":case\"span\":case\"svg\":case\"path\":case\"a\":case\"g\":case\"p\":case\"li\":break;case\"input\":var n=null,l=null,i=null,o=null,s=null,p=null,c=null;for(h in e){var u=e[h];if(e.hasOwnProperty(h)&&null!=u)switch(h){case\"checked\":case\"value\":break;case\"defaultValue\":s=u;default:r.hasOwnProperty(h)||ic(A,t,h,null,r,u)}}for(var d in r){var h=r[d];if(u=e[d],r.hasOwnProperty(d)&&(null!=h||null!=u))switch(d){case\"type\":l=h;break;case\"name\":n=h;break;case\"checked\":p=h;break;case\"defaultChecked\":c=h;break;case\"value\":i=h;break;case\"defaultValue\":o=h;break;case\"children\":case\"dangerouslySetInnerHTML\":if(null!=h)throw Error(a(137,t));break;default:h!==u&&ic(A,t,d,h,r,u)}}return void St(A,i,o,s,p,c,l,n);case\"select\":for(l in h=i=o=d=null,e)if(s=e[l],e.hasOwnProperty(l)&&null!=s)switch(l){case\"value\":break;case\"multiple\":h=s;default:r.hasOwnProperty(l)||ic(A,t,l,null,r,s)}for(n in r)if(l=r[n],s=e[n],r.hasOwnProperty(n)&&(null!=l||null!=s))switch(n){case\"value\":d=l;break;case\"defaultValue\":o=l;break;case\"multiple\":i=l;default:l!==s&&ic(A,t,n,l,r,s)}return t=o,e=i,r=h,void(null!=d?Lt(A,!!e,d,!1):!!r!=!!e&&(null!=t?Lt(A,!!e,t,!0):Lt(A,!!e,e?[]:\"\",!1)));case\"textarea\":for(o in h=d=null,e)if(n=e[o],e.hasOwnProperty(o)&&null!=n&&!r.hasOwnProperty(o))switch(o){case\"value\":case\"children\":break;default:ic(A,t,o,null,r,n)}for(i in r)if(n=r[i],l=e[i],r.hasOwnProperty(i)&&(null!=n||null!=l))switch(i){case\"value\":d=n;break;case\"defaultValue\":h=n;break;case\"children\":break;case\"dangerouslySetInnerHTML\":if(null!=n)throw Error(a(91));break;default:n!==l&&ic(A,t,i,n,r,l)}return void Wt(A,d,h);case\"option\":for(var S in e)if(d=e[S],e.hasOwnProperty(S)&&null!=d&&!r.hasOwnProperty(S))if(\"selected\"===S)A.selected=!1;else ic(A,t,S,null,r,d);for(s in r)if(d=r[s],h=e[s],r.hasOwnProperty(s)&&d!==h&&(null!=d||null!=h))if(\"selected\"===s)A.selected=d&&\"function\"!=typeof d&&\"symbol\"!=typeof d;else ic(A,t,s,d,r,h);return;case\"img\":case\"link\":case\"area\":case\"base\":case\"br\":case\"col\":case\"embed\":case\"hr\":case\"keygen\":case\"meta\":case\"param\":case\"source\":case\"track\":case\"wbr\":case\"menuitem\":for(var f in e)d=e[f],e.hasOwnProperty(f)&&null!=d&&!r.hasOwnProperty(f)&&ic(A,t,f,null,r,d);for(p in r)if(d=r[p],h=e[p],r.hasOwnProperty(p)&&d!==h&&(null!=d||null!=h))switch(p){case\"children\":case\"dangerouslySetInnerHTML\":if(null!=d)throw Error(a(137,t));break;default:ic(A,t,p,d,r,h)}return;default:if(Et(t)){for(var m in e)d=e[m],e.hasOwnProperty(m)&&void 0!==d&&!r.hasOwnProperty(m)&&oc(A,t,m,void 0,r,d);for(c in r)d=r[c],h=e[c],!r.hasOwnProperty(c)||d===h||void 0===d&&void 0===h||oc(A,t,c,d,r,h);return}}for(var L in e)d=e[L],e.hasOwnProperty(L)&&null!=d&&!r.hasOwnProperty(L)&&ic(A,t,L,null,r,d);for(u in r)d=r[u],h=e[u],!r.hasOwnProperty(u)||d===h||null==d&&null==h||ic(A,t,u,d,r,h)}(r,A.type,e,t),r[VA]=t}catch(n){gp(A,A.return,n)}}function yo(A){return 5===A.tag||3===A.tag||26===A.tag||27===A.tag&&bc(A.type)||4===A.tag}function vo(A){A:for(;;){for(;null===A.sibling;){if(null===A.return||yo(A.return))return null;A=A.return}for(A.sibling.return=A.return,A=A.sibling;5!==A.tag&&6!==A.tag&&18!==A.tag;){if(27===A.tag&&bc(A.type))continue A;if(2&A.flags)continue A;if(null===A.child||4===A.tag)continue A;A.child.return=A,A=A.child}if(!(2&A.flags))return A.stateNode}}function bo(A,t,e){var a=A.tag;if(5===a||6===a)A=A.stateNode,t?(9===e.nodeType?e.body:\"HTML\"===e.nodeName?e.ownerDocument.body:e).insertBefore(A,t):((t=9===e.nodeType?e.body:\"HTML\"===e.nodeName?e.ownerDocument.body:e).appendChild(A),null!=(e=e._reactRootContainer)||null!==t.onclick||(t.onclick=Pt));else if(4!==a&&(27===a&&bc(A.type)&&(e=A.stateNode,t=null),null!==(A=A.child)))for(bo(A,t,e),A=A.sibling;null!==A;)bo(A,t,e),A=A.sibling}function xo(A,t,e){var a=A.tag;if(5===a||6===a)A=A.stateNode,t?e.insertBefore(A,t):e.appendChild(A);else if(4!==a&&(27===a&&bc(A.type)&&(e=A.stateNode),null!==(A=A.child)))for(xo(A,t,e),A=A.sibling;null!==A;)xo(A,t,e),A=A.sibling}function Eo(A){var t=A.stateNode,e=A.memoizedProps;try{for(var a=A.type,r=t.attributes;r.length;)t.removeAttributeNode(r[0]);sc(t,a,e),t[OA]=A,t[VA]=e}catch(n){gp(A,A.return,n)}}var ko=!1,wo=!1,Co=!1,Po=\"function\"==typeof WeakSet?WeakSet:Set,To=null;function _o(A,t,e){var a=e.flags;switch(e.tag){case 0:case 11:case 15:Go(A,e),4&a&&uo(5,e);break;case 1:if(Go(A,e),4&a)if(A=e.stateNode,null===t)try{A.componentDidMount()}catch(l){gp(e,e.return,l)}else{var r=Wi(e.type,t.memoizedProps);t=t.memoizedState;try{A.componentDidUpdate(r,t,A.__reactInternalSnapshotBeforeUpdate)}catch(i){gp(e,e.return,i)}}64&a&&So(e),512&a&&mo(e,e.return);break;case 3:if(Go(A,e),64&a&&null!==(A=e.updateQueue)){if(t=null,null!==e.child)switch(e.child.tag){case 27:case 5:case 1:t=e.child.stateNode}try{bn(A,t)}catch(l){gp(e,e.return,l)}}break;case 27:null===t&&4&a&&Eo(e);case 26:case 5:Go(A,e),null===t&&4&a&&Wo(e),512&a&&mo(e,e.return);break;case 12:Go(A,e);break;case 31:Go(A,e),4&a&&Fo(A,e);break;case 13:Go(A,e),4&a&&Oo(A,e),64&a&&(null!==(A=e.memoizedState)&&(null!==(A=A.dehydrated)&&function(A,t){var e=A.ownerDocument;if(\"$~\"===A.data)A._reactRetry=t;else if(\"$?\"!==A.data||\"loading\"!==e.readyState)t();else{var a=function(){t(),e.removeEventListener(\"DOMContentLoaded\",a)};e.addEventListener(\"DOMContentLoaded\",a),A._reactRetry=a}}(A,e=xp.bind(null,e))));break;case 22:if(!(a=null!==e.memoizedState||ko)){t=null!==t&&null!==t.memoizedState||wo,r=ko;var n=wo;ko=a,(wo=t)&&!n?Uo(A,e,!!(8772&e.subtreeFlags)):Go(A,e),ko=r,wo=n}break;case 30:break;default:Go(A,e)}}function Mo(A){var t=A.alternate;null!==t&&(A.alternate=null,Mo(t)),A.child=null,A.deletions=null,A.sibling=null,5===A.tag&&(null!==(t=A.stateNode)&&qA(t)),A.stateNode=null,A.return=null,A.dependencies=null,A.memoizedProps=null,A.memoizedState=null,A.pendingProps=null,A.stateNode=null,A.updateQueue=null}var Do=null,Ro=!1;function No(A,t,e){for(e=e.child;null!==e;)Io(A,t,e),e=e.sibling}function Io(A,t,e){if(SA&&\"function\"==typeof SA.onCommitFiberUnmount)try{SA.onCommitFiberUnmount(hA,e)}catch(err){}switch(e.tag){case 26:wo||Lo(e,t),No(A,t,e),e.memoizedState?e.memoizedState.count--:e.stateNode&&(e=e.stateNode).parentNode.removeChild(e);break;case 27:wo||Lo(e,t);var a=Do,r=Ro;bc(e.type)&&(Do=e.stateNode,Ro=!1),No(A,t,e),Nc(e.stateNode),Do=a,Ro=r;break;case 5:wo||Lo(e,t);case 6:if(a=Do,r=Ro,Do=null,No(A,t,e),Ro=r,null!==(Do=a))if(Ro)try{(9===Do.nodeType?Do.body:\"HTML\"===Do.nodeName?Do.ownerDocument.body:Do).removeChild(e.stateNode)}catch(n){gp(e,t,n)}else try{Do.removeChild(e.stateNode)}catch(n){gp(e,t,n)}break;case 18:null!==Do&&(Ro?(xc(9===(A=Do).nodeType?A.body:\"HTML\"===A.nodeName?A.ownerDocument.body:A,e.stateNode),zu(A)):xc(Do,e.stateNode));break;case 4:a=Do,r=Ro,Do=e.stateNode.containerInfo,Ro=!0,No(A,t,e),Do=a,Ro=r;break;case 0:case 11:case 14:case 15:ho(2,e,t),wo||ho(4,e,t),No(A,t,e);break;case 1:wo||(Lo(e,t),\"function\"==typeof(a=e.stateNode).componentWillUnmount&&fo(e,t,a)),No(A,t,e);break;case 21:No(A,t,e);break;case 22:wo=(a=wo)||null!==e.memoizedState,No(A,t,e),wo=a;break;default:No(A,t,e)}}function Fo(A,t){if(null===t.memoizedState&&(null!==(A=t.alternate)&&null!==(A=A.memoizedState))){A=A.dehydrated;try{zu(A)}catch(e){gp(t,t.return,e)}}}function Oo(A,t){if(null===t.memoizedState&&(null!==(A=t.alternate)&&(null!==(A=A.memoizedState)&&null!==(A=A.dehydrated))))try{zu(A)}catch(e){gp(t,t.return,e)}}function Vo(A,t){var e=function(A){switch(A.tag){case 31:case 13:case 19:var t=A.stateNode;return null===t&&(t=A.stateNode=new Po),t;case 22:return null===(t=(A=A.stateNode)._retryCache)&&(t=A._retryCache=new Po),t;default:throw Error(a(435,A.tag))}}(A);t.forEach(function(t){if(!e.has(t)){e.add(t);var a=Ep.bind(null,A,t);t.then(a,a)}})}function Bo(A,t){var e=t.deletions;if(null!==e)for(var r=0;r<e.length;r++){var n=e[r],l=A,i=t,o=i;A:for(;null!==o;){switch(o.tag){case 27:if(bc(o.type)){Do=o.stateNode,Ro=!1;break A}break;case 5:Do=o.stateNode,Ro=!1;break A;case 3:case 4:Do=o.stateNode.containerInfo,Ro=!0;break A}o=o.return}if(null===Do)throw Error(a(160));Io(l,i,n),Do=null,Ro=!1,null!==(l=n.alternate)&&(l.return=null),n.return=null}if(13886&t.subtreeFlags)for(t=t.child;null!==t;)jo(t,A),t=t.sibling}var zo=null;function jo(A,t){var e=A.alternate,r=A.flags;switch(A.tag){case 0:case 11:case 14:case 15:Bo(t,A),$o(A),4&r&&(ho(3,A,A.return),uo(3,A),ho(5,A,A.return));break;case 1:Bo(t,A),$o(A),512&r&&(wo||null===e||Lo(e,e.return)),64&r&&ko&&(null!==(A=A.updateQueue)&&(null!==(r=A.callbacks)&&(e=A.shared.hiddenCallbacks,A.shared.hiddenCallbacks=null===e?r:e.concat(r))));break;case 26:var n=zo;if(Bo(t,A),$o(A),512&r&&(wo||null===e||Lo(e,e.return)),4&r){var l=null!==e?e.memoizedState:null;if(r=A.memoizedState,null===e)if(null===r)if(null===A.stateNode){A:{r=A.type,e=A.memoizedProps,n=n.ownerDocument||n;t:switch(r){case\"title\":(!(l=n.getElementsByTagName(\"title\")[0])||l[GA]||l[OA]||\"http://www.w3.org/2000/svg\"===l.namespaceURI||l.hasAttribute(\"itemprop\"))&&(l=n.createElement(r),n.head.insertBefore(l,n.querySelector(\"head > title\"))),sc(l,r,e),l[OA]=A,KA(l),r=l;break A;case\"link\":var i=Zc(\"link\",\"href\",n).get(r+(e.href||\"\"));if(i)for(var o=0;o<i.length;o++)if((l=i[o]).getAttribute(\"href\")===(null==e.href||\"\"===e.href?null:e.href)&&l.getAttribute(\"rel\")===(null==e.rel?null:e.rel)&&l.getAttribute(\"title\")===(null==e.title?null:e.title)&&l.getAttribute(\"crossorigin\")===(null==e.crossOrigin?null:e.crossOrigin)){i.splice(o,1);break t}sc(l=n.createElement(r),r,e),n.head.appendChild(l);break;case\"meta\":if(i=Zc(\"meta\",\"content\",n).get(r+(e.content||\"\")))for(o=0;o<i.length;o++)if((l=i[o]).getAttribute(\"content\")===(null==e.content?null:\"\"+e.content)&&l.getAttribute(\"name\")===(null==e.name?null:e.name)&&l.getAttribute(\"property\")===(null==e.property?null:e.property)&&l.getAttribute(\"http-equiv\")===(null==e.httpEquiv?null:e.httpEquiv)&&l.getAttribute(\"charset\")===(null==e.charSet?null:e.charSet)){i.splice(o,1);break t}sc(l=n.createElement(r),r,e),n.head.appendChild(l);break;default:throw Error(a(468,r))}l[OA]=A,KA(l),r=l}A.stateNode=r}else Au(n,A.type,A.stateNode);else A.stateNode=Xc(n,r,A.memoizedProps);else l!==r?(null===l?null!==e.stateNode&&(e=e.stateNode).parentNode.removeChild(e):l.count--,null===r?Au(n,A.type,A.stateNode):Xc(n,r,A.memoizedProps)):null===r&&null!==A.stateNode&&go(A,A.memoizedProps,e.memoizedProps)}break;case 27:Bo(t,A),$o(A),512&r&&(wo||null===e||Lo(e,e.return)),null!==e&&4&r&&go(A,A.memoizedProps,e.memoizedProps);break;case 5:if(Bo(t,A),$o(A),512&r&&(wo||null===e||Lo(e,e.return)),32&A.flags){n=A.stateNode;try{yt(n,\"\")}catch(S){gp(A,A.return,S)}}4&r&&null!=A.stateNode&&go(A,n=A.memoizedProps,null!==e?e.memoizedProps:n),1024&r&&(Co=!0);break;case 6:if(Bo(t,A),$o(A),4&r){if(null===A.stateNode)throw Error(a(162));r=A.memoizedProps,e=A.stateNode;try{e.nodeValue=r}catch(S){gp(A,A.return,S)}}break;case 3:if(Qc=null,n=zo,zo=Oc(t.containerInfo),Bo(t,A),zo=n,$o(A),4&r&&null!==e&&e.memoizedState.isDehydrated)try{zu(t.containerInfo)}catch(S){gp(A,A.return,S)}Co&&(Co=!1,Ho(A));break;case 4:r=zo,zo=Oc(A.stateNode.containerInfo),Bo(t,A),$o(A),zo=r;break;case 12:default:Bo(t,A),$o(A);break;case 31:case 19:Bo(t,A),$o(A),4&r&&(null!==(r=A.updateQueue)&&(A.updateQueue=null,Vo(A,r)));break;case 13:Bo(t,A),$o(A),8192&A.child.flags&&null!==A.memoizedState!=(null!==e&&null!==e.memoizedState)&&(Ps=nA()),4&r&&(null!==(r=A.updateQueue)&&(A.updateQueue=null,Vo(A,r)));break;case 22:n=null!==A.memoizedState;var s=null!==e&&null!==e.memoizedState,p=ko,c=wo;if(ko=p||n,wo=c||s,Bo(t,A),wo=c,ko=p,$o(A),8192&r)A:for(t=A.stateNode,t._visibility=n?-2&t._visibility:1|t._visibility,n&&(null===e||s||ko||wo||qo(A)),e=null,t=A;;){if(5===t.tag||26===t.tag){if(null===e){s=e=t;try{if(l=s.stateNode,n)\"function\"==typeof(i=l.style).setProperty?i.setProperty(\"display\",\"none\",\"important\"):i.display=\"none\";else{o=s.stateNode;var u=s.memoizedProps.style,d=null!=u&&u.hasOwnProperty(\"display\")?u.display:null;o.style.display=null==d||\"boolean\"==typeof d?\"\":(\"\"+d).trim()}}catch(S){gp(s,s.return,S)}}}else if(6===t.tag){if(null===e){s=t;try{s.stateNode.nodeValue=n?\"\":s.memoizedProps}catch(S){gp(s,s.return,S)}}}else if(18===t.tag){if(null===e){s=t;try{var h=s.stateNode;n?Ec(h,!0):Ec(s.stateNode,!1)}catch(S){gp(s,s.return,S)}}}else if((22!==t.tag&&23!==t.tag||null===t.memoizedState||t===A)&&null!==t.child){t.child.return=t,t=t.child;continue}if(t===A)break A;for(;null===t.sibling;){if(null===t.return||t.return===A)break A;e===t&&(e=null),t=t.return}e===t&&(e=null),t.sibling.return=t.return,t=t.sibling}4&r&&(null!==(r=A.updateQueue)&&(null!==(e=r.retryQueue)&&(r.retryQueue=null,Vo(A,e))));case 30:case 21:}}function $o(A){var t=A.flags;if(2&t){try{for(var e,r=A.return;null!==r;){if(yo(r)){e=r;break}r=r.return}if(null==e)throw Error(a(160));switch(e.tag){case 27:var n=e.stateNode;xo(A,vo(A),n);break;case 5:var l=e.stateNode;32&e.flags&&(yt(l,\"\"),e.flags&=-33),xo(A,vo(A),l);break;case 3:case 4:var i=e.stateNode.containerInfo;bo(A,vo(A),i);break;default:throw Error(a(161))}}catch(o){gp(A,A.return,o)}A.flags&=-3}4096&t&&(A.flags&=-4097)}function Ho(A){if(1024&A.subtreeFlags)for(A=A.child;null!==A;){var t=A;Ho(t),5===t.tag&&1024&t.flags&&t.stateNode.reset(),A=A.sibling}}function Go(A,t){if(8772&t.subtreeFlags)for(t=t.child;null!==t;)_o(A,t.alternate,t),t=t.sibling}function qo(A){for(A=A.child;null!==A;){var t=A;switch(t.tag){case 0:case 11:case 14:case 15:ho(4,t,t.return),qo(t);break;case 1:Lo(t,t.return);var e=t.stateNode;\"function\"==typeof e.componentWillUnmount&&fo(t,t.return,e),qo(t);break;case 27:Nc(t.stateNode);case 26:case 5:Lo(t,t.return),qo(t);break;case 22:null===t.memoizedState&&qo(t);break;default:qo(t)}A=A.sibling}}function Uo(A,t,e){for(e=e&&!!(8772&t.subtreeFlags),t=t.child;null!==t;){var a=t.alternate,r=A,n=t,l=n.flags;switch(n.tag){case 0:case 11:case 15:Uo(r,n,e),uo(4,n);break;case 1:if(Uo(r,n,e),\"function\"==typeof(r=(a=n).stateNode).componentDidMount)try{r.componentDidMount()}catch(s){gp(a,a.return,s)}if(null!==(r=(a=n).updateQueue)){var i=a.stateNode;try{var o=r.shared.hiddenCallbacks;if(null!==o)for(r.shared.hiddenCallbacks=null,r=0;r<o.length;r++)vn(o[r],i)}catch(s){gp(a,a.return,s)}}e&&64&l&&So(n),mo(n,n.return);break;case 27:Eo(n);case 26:case 5:Uo(r,n,e),e&&null===a&&4&l&&Wo(n),mo(n,n.return);break;case 12:Uo(r,n,e);break;case 31:Uo(r,n,e),e&&4&l&&Fo(r,n);break;case 13:Uo(r,n,e),e&&4&l&&Oo(r,n);break;case 22:null===n.memoizedState&&Uo(r,n,e),mo(n,n.return);break;case 30:break;default:Uo(r,n,e)}t=t.sibling}}function Xo(A,t){var e=null;null!==A&&null!==A.memoizedState&&null!==A.memoizedState.cachePool&&(e=A.memoizedState.cachePool.pool),A=null,null!==t.memoizedState&&null!==t.memoizedState.cachePool&&(A=t.memoizedState.cachePool.pool),A!==e&&(null!=A&&A.refCount++,null!=e&&Ir(e))}function Jo(A,t){A=null,null!==t.alternate&&(A=t.alternate.memoizedState.cache),(t=t.memoizedState.cache)!==A&&(t.refCount++,null!=A&&Ir(A))}function Yo(A,t,e,a){if(10256&t.subtreeFlags)for(t=t.child;null!==t;)Ko(A,t,e,a),t=t.sibling}function Ko(A,t,e,a){var r=t.flags;switch(t.tag){case 0:case 11:case 15:Yo(A,t,e,a),2048&r&&uo(9,t);break;case 1:case 31:case 13:default:Yo(A,t,e,a);break;case 3:Yo(A,t,e,a),2048&r&&(A=null,null!==t.alternate&&(A=t.alternate.memoizedState.cache),(t=t.memoizedState.cache)!==A&&(t.refCount++,null!=A&&Ir(A)));break;case 12:if(2048&r){Yo(A,t,e,a),A=t.stateNode;try{var n=t.memoizedProps,l=n.id,i=n.onPostCommit;\"function\"==typeof i&&i(l,null===t.alternate?\"mount\":\"update\",A.passiveEffectDuration,-0)}catch(o){gp(t,t.return,o)}}else Yo(A,t,e,a);break;case 23:break;case 22:n=t.stateNode,l=t.alternate,null!==t.memoizedState?2&n._visibility?Yo(A,t,e,a):Zo(A,t):2&n._visibility?Yo(A,t,e,a):(n._visibility|=2,Qo(A,t,e,a,!!(10256&t.subtreeFlags)||!1)),2048&r&&Xo(l,t);break;case 24:Yo(A,t,e,a),2048&r&&Jo(t.alternate,t)}}function Qo(A,t,e,a,r){for(r=r&&(!!(10256&t.subtreeFlags)||!1),t=t.child;null!==t;){var n=A,l=t,i=e,o=a,s=l.flags;switch(l.tag){case 0:case 11:case 15:Qo(n,l,i,o,r),uo(8,l);break;case 23:break;case 22:var p=l.stateNode;null!==l.memoizedState?2&p._visibility?Qo(n,l,i,o,r):Zo(n,l):(p._visibility|=2,Qo(n,l,i,o,r)),r&&2048&s&&Xo(l.alternate,l);break;case 24:Qo(n,l,i,o,r),r&&2048&s&&Jo(l.alternate,l);break;default:Qo(n,l,i,o,r)}t=t.sibling}}function Zo(A,t){if(10256&t.subtreeFlags)for(t=t.child;null!==t;){var e=A,a=t,r=a.flags;switch(a.tag){case 22:Zo(e,a),2048&r&&Xo(a.alternate,a);break;case 24:Zo(e,a),2048&r&&Jo(a.alternate,a);break;default:Zo(e,a)}t=t.sibling}}var As=8192;function ts(A,t,e){if(A.subtreeFlags&As)for(A=A.child;null!==A;)es(A,t,e),A=A.sibling}function es(A,t,e){switch(A.tag){case 26:ts(A,t,e),A.flags&As&&null!==A.memoizedState&&function(A,t,e,a){if(!(\"stylesheet\"!==e.type||\"string\"==typeof a.media&&!1===matchMedia(a.media).matches||4&e.state.loading)){if(null===e.instance){var r=$c(a.href),n=t.querySelector(Hc(r));if(n)return null!==(t=n._p)&&\"object\"==typeof t&&\"function\"==typeof t.then&&(A.count++,A=au.bind(A),t.then(A,A)),e.state.loading|=4,e.instance=n,void KA(n);n=t.ownerDocument||t,a=Gc(a),(r=Ic.get(r))&&Yc(a,r),KA(n=n.createElement(\"link\"));var l=n;l._p=new Promise(function(A,t){l.onload=A,l.onerror=t}),sc(n,\"link\",a),e.instance=n}null===A.stylesheets&&(A.stylesheets=new Map),A.stylesheets.set(e,t),(t=e.state.preload)&&!(3&e.state.loading)&&(A.count++,e=au.bind(A),t.addEventListener(\"load\",e),t.addEventListener(\"error\",e))}}(e,zo,A.memoizedState,A.memoizedProps);break;case 5:default:ts(A,t,e);break;case 3:case 4:var a=zo;zo=Oc(A.stateNode.containerInfo),ts(A,t,e),zo=a;break;case 22:null===A.memoizedState&&(null!==(a=A.alternate)&&null!==a.memoizedState?(a=As,As=16777216,ts(A,t,e),As=a):ts(A,t,e))}}function as(A){var t=A.alternate;if(null!==t&&null!==(A=t.child)){t.child=null;do{t=A.sibling,A.sibling=null,A=t}while(null!==A)}}function rs(A){var t=A.deletions;if(16&A.flags){if(null!==t)for(var e=0;e<t.length;e++){var a=t[e];To=a,is(a,A)}as(A)}if(10256&A.subtreeFlags)for(A=A.child;null!==A;)ns(A),A=A.sibling}function ns(A){switch(A.tag){case 0:case 11:case 15:rs(A),2048&A.flags&&ho(9,A,A.return);break;case 3:case 12:default:rs(A);break;case 22:var t=A.stateNode;null!==A.memoizedState&&2&t._visibility&&(null===A.return||13!==A.return.tag)?(t._visibility&=-3,ls(A)):rs(A)}}function ls(A){var t=A.deletions;if(16&A.flags){if(null!==t)for(var e=0;e<t.length;e++){var a=t[e];To=a,is(a,A)}as(A)}for(A=A.child;null!==A;){switch((t=A).tag){case 0:case 11:case 15:ho(8,t,t.return),ls(t);break;case 22:2&(e=t.stateNode)._visibility&&(e._visibility&=-3,ls(t));break;default:ls(t)}A=A.sibling}}function is(A,t){for(;null!==To;){var e=To;switch(e.tag){case 0:case 11:case 15:ho(8,e,t);break;case 23:case 22:if(null!==e.memoizedState&&null!==e.memoizedState.cachePool){var a=e.memoizedState.cachePool.pool;null!=a&&a.refCount++}break;case 24:Ir(e.memoizedState.cache)}if(null!==(a=e.child))a.return=e,To=a;else A:for(e=A;null!==To;){var r=(a=To).sibling,n=a.return;if(Mo(a),a===e){To=null;break A}if(null!==r){r.return=n,To=r;break A}To=n}}}var os={getCacheForType:function(A){var t=Cr(Rr),e=t.data.get(A);return void 0===e&&(e=A(),t.data.set(A,e)),e},cacheSignal:function(){return Cr(Rr).controller.signal}},ss=\"function\"==typeof WeakMap?WeakMap:Map,ps=0,cs=null,us=null,ds=0,hs=0,Ss=null,fs=!1,ms=!1,Ls=!1,Ws=0,gs=0,ys=0,vs=0,bs=0,xs=0,Es=0,ks=null,ws=null,Cs=!1,Ps=0,Ts=0,_s=1/0,Ms=null,Ds=null,Rs=0,Ns=null,Is=null,Fs=0,Os=0,Vs=null,Bs=null,zs=0,js=null;function $s(){return 2&ps&&0!==ds?ds&-ds:null!==_.T?Vp():NA()}function Hs(){if(0===xs)if(536870912&ds&&!ir)xs=536870912;else{var A=yA;!(3932160&(yA<<=1))&&(yA=262144),xs=A}return null!==(A=Pn.current)&&(A.flags|=32),xs}function Gs(A,t,e){(A!==cs||2!==hs&&9!==hs)&&null===A.cancelPendingCommit||(Qs(A,0),Js(A,ds,xs,!1)),PA(A,e),2&ps&&A===cs||(A===cs&&(!(2&ps)&&(vs|=e),4===gs&&Js(A,ds,xs,!1)),Mp(A))}function qs(A,t,e){if(6&ps)throw Error(a(327));for(var r=!e&&!(127&t)&&0===(t&A.expiredLanes)||EA(A,t),n=r?function(A,t){var e=ps;ps|=2;var r=tp(),n=ep();cs!==A||ds!==t?(Ms=null,_s=nA()+500,Qs(A,t)):ms=EA(A,t);A:for(;;)try{if(0!==hs&&null!==us){t=us;var l=Ss;t:switch(hs){case 1:hs=0,Ss=null,sp(A,t,l,1);break;case 2:case 9:if(Kr(l)){hs=0,Ss=null,op(t);break}t=function(){2!==hs&&9!==hs||cs!==A||(hs=7),Mp(A)},l.then(t,t);break A;case 3:hs=7;break A;case 4:hs=5;break A;case 7:Kr(l)?(hs=0,Ss=null,op(t)):(hs=0,Ss=null,sp(A,t,l,7));break;case 5:var i=null;switch(us.tag){case 26:i=us.memoizedState;case 5:case 27:var o=us;if(i?tu(i):o.stateNode.complete){hs=0,Ss=null;var s=o.sibling;if(null!==s)us=s;else{var p=o.return;null!==p?(us=p,pp(p)):us=null}break t}}hs=0,Ss=null,sp(A,t,l,5);break;case 6:hs=0,Ss=null,sp(A,t,l,6);break;case 8:Ks(),gs=6;break A;default:throw Error(a(462))}}lp();break}catch(c){Zs(A,c)}return gr=Wr=null,_.H=r,_.A=n,ps=e,null!==us?0:(cs=null,ds=0,ka(),gs)}(A,t):rp(A,t,!0),l=r;;){if(0===n){ms&&!r&&Js(A,t,0,!1);break}if(e=A.current.alternate,!l||Xs(e)){if(2===n){if(l=t,A.errorRecoveryDisabledLanes&l)var i=0;else i=0!==(i=-536870913&A.pendingLanes)?i:536870912&i?536870912:0;if(0!==i){t=i;A:{var o=A;n=ks;var s=o.current.memoizedState.isDehydrated;if(s&&(Qs(o,i).flags|=256),2!==(i=rp(o,i,!1))){if(Ls&&!s){o.errorRecoveryDisabledLanes|=l,vs|=l,n=4;break A}l=ws,ws=n,null!==l&&(null===ws?ws=l:ws.push.apply(ws,l))}n=i}if(l=!1,2!==n)continue}}if(1===n){Qs(A,0),Js(A,t,0,!0);break}A:{switch(r=A,l=n){case 0:case 1:throw Error(a(345));case 4:if((4194048&t)!==t)break;case 6:Js(r,t,xs,!fs);break A;case 2:ws=null;break;case 3:case 5:break;default:throw Error(a(329))}if((62914560&t)===t&&10<(n=Ps+300-nA())){if(Js(r,t,xs,!fs),0!==xA(r,0,!0))break A;Fs=t,r.timeoutHandle=Lc(Us.bind(null,r,e,ws,Ms,Cs,t,xs,vs,Es,fs,l,\"Throttled\",-0,0),n)}else Us(r,e,ws,Ms,Cs,t,xs,vs,Es,fs,l,null,-0,0)}break}n=rp(A,t,!1),l=!1}Mp(A)}function Us(A,t,e,a,r,n,l,i,o,s,p,c,u,d){if(A.timeoutHandle=-1,8192&(c=t.subtreeFlags)||!(16785408&~c)){es(t,n,c={stylesheets:null,count:0,imgCount:0,imgBytes:0,suspenseyImages:[],waitingForImages:!0,waitingForViewTransition:!1,unsuspend:Pt});var h=(62914560&n)===n?Ps-nA():(4194048&n)===n?Ts-nA():0;if(null!==(h=function(A,t){return A.stylesheets&&0===A.count&&nu(A,A.stylesheets),0<A.count||0<A.imgCount?function(e){var a=setTimeout(function(){if(A.stylesheets&&nu(A,A.stylesheets),A.unsuspend){var t=A.unsuspend;A.unsuspend=null,t()}},6e4+t);0<A.imgBytes&&0===eu&&(eu=62500*function(){if(\"function\"==typeof performance.getEntriesByType){for(var A=0,t=0,e=performance.getEntriesByType(\"resource\"),a=0;a<e.length;a++){var r=e[a],n=r.transferSize,l=r.initiatorType,i=r.duration;if(n&&i&&pc(l)){for(l=0,i=r.responseEnd,a+=1;a<e.length;a++){var o=e[a],s=o.startTime;if(s>i)break;var p=o.transferSize,c=o.initiatorType;p&&pc(c)&&(l+=p*((o=o.responseEnd)<i?1:(i-s)/(o-s)))}if(--a,t+=8*(n+l)/(r.duration/1e3),10<++A)break}}if(0<A)return t/A/1e6}return navigator.connection&&\"number\"==typeof(A=navigator.connection.downlink)?A:5}());var r=setTimeout(function(){if(A.waitingForImages=!1,0===A.count&&(A.stylesheets&&nu(A,A.stylesheets),A.unsuspend)){var t=A.unsuspend;A.unsuspend=null,t()}},(A.imgBytes>eu?50:800)+t);return A.unsuspend=e,function(){A.unsuspend=null,clearTimeout(a),clearTimeout(r)}}:null}(c,h)))return Fs=n,A.cancelPendingCommit=h(up.bind(null,A,t,n,e,a,r,l,i,o,p,c,null,u,d)),void Js(A,n,l,!s)}up(A,t,n,e,a,r,l,i,o)}function Xs(A){for(var t=A;;){var e=t.tag;if((0===e||11===e||15===e)&&16384&t.flags&&(null!==(e=t.updateQueue)&&null!==(e=e.stores)))for(var a=0;a<e.length;a++){var r=e[a],n=r.getSnapshot;r=r.value;try{if(!Xe(n(),r))return!1}catch(l){return!1}}if(e=t.child,16384&t.subtreeFlags&&null!==e)e.return=t,t=e;else{if(t===A)break;for(;null===t.sibling;){if(null===t.return||t.return===A)return!0;t=t.return}t.sibling.return=t.return,t=t.sibling}}return!0}function Js(A,t,e,a){t&=~bs,t&=~vs,A.suspendedLanes|=t,A.pingedLanes&=~t,a&&(A.warmLanes|=t),a=A.expirationTimes;for(var r=t;0<r;){var n=31-mA(r),l=1<<n;a[n]=-1,r&=~l}0!==e&&TA(A,e,t)}function Ys(){return!!(6&ps)||(Dp(0),!1)}function Ks(){if(null!==us){if(0===hs)var A=us.return;else gr=Wr=null,al(A=us),an=null,rn=0,A=us;for(;null!==A;)co(A.alternate,A),A=A.return;us=null}}function Qs(A,t){var e=A.timeoutHandle;-1!==e&&(A.timeoutHandle=-1,Wc(e)),null!==(e=A.cancelPendingCommit)&&(A.cancelPendingCommit=null,e()),Fs=0,Ks(),cs=A,us=e=Ia(A.current,null),ds=t,hs=0,Ss=null,fs=!1,ms=EA(A,t),Ls=!1,Es=xs=bs=vs=ys=gs=0,ws=ks=null,Cs=!1,8&t&&(t|=32&t);var a=A.entangledLanes;if(0!==a)for(A=A.entanglements,a&=t;0<a;){var r=31-mA(a),n=1<<r;t|=A[r],a&=~n}return Ws=t,ka(),e}function Zs(A,t){Vn=null,_.H=ci,t===Ur||t===Jr?(t=tn(),hs=3):t===Xr?(t=tn(),hs=4):hs=t===Ci?8:null!==t&&\"object\"==typeof t&&\"function\"==typeof t.then?6:1,Ss=t,null===us&&(gs=1,bi(A,Ha(t,A.current)))}function Ap(){var A=Pn.current;return null===A||((4194048&ds)===ds?null===Tn:!!((62914560&ds)===ds||536870912&ds)&&A===Tn)}function tp(){var A=_.H;return _.H=ci,null===A?ci:A}function ep(){var A=_.A;return _.A=os,A}function ap(){gs=4,fs||(4194048&ds)!==ds&&null!==Pn.current||(ms=!0),!(134217727&ys)&&!(134217727&vs)||null===cs||Js(cs,ds,xs,!1)}function rp(A,t,e){var a=ps;ps|=2;var r=tp(),n=ep();cs===A&&ds===t||(Ms=null,Qs(A,t)),t=!1;var l=gs;A:for(;;)try{if(0!==hs&&null!==us){var i=us,o=Ss;switch(hs){case 8:Ks(),l=6;break A;case 3:case 2:case 9:case 6:null===Pn.current&&(t=!0);var s=hs;if(hs=0,Ss=null,sp(A,i,o,s),e&&ms){l=0;break A}break;default:s=hs,hs=0,Ss=null,sp(A,i,o,s)}}np(),l=gs;break}catch(p){Zs(A,p)}return t&&A.shellSuspendCounter++,gr=Wr=null,ps=a,_.H=r,_.A=n,null===us&&(cs=null,ds=0,ka()),l}function np(){for(;null!==us;)ip(us)}function lp(){for(;null!==us&&!aA();)ip(us)}function ip(A){var t=eo(A.alternate,A,Ws);A.memoizedProps=A.pendingProps,null===t?pp(A):us=t}function op(A){var t=A,e=t.alternate;switch(t.tag){case 15:case 0:t=zi(e,t,t.pendingProps,t.type,void 0,ds);break;case 11:t=zi(e,t,t.pendingProps,t.type.render,t.ref,ds);break;case 5:al(t);default:co(e,t),t=eo(e,t=us=Fa(t,Ws),Ws)}A.memoizedProps=A.pendingProps,null===t?pp(A):us=t}function sp(A,t,e,r){gr=Wr=null,al(t),an=null,rn=0;var n=t.return;try{if(function(A,t,e,r,n){if(e.flags|=32768,null!==r&&\"object\"==typeof r&&\"function\"==typeof r.then){if(null!==(t=e.alternate)&&Er(t,e,n,!0),null!==(e=Pn.current)){switch(e.tag){case 31:case 13:return null===Tn?ap():null===e.alternate&&0===gs&&(gs=3),e.flags&=-257,e.flags|=65536,e.lanes=n,r===Yr?e.flags|=16384:(null===(t=e.updateQueue)?e.updateQueue=new Set([r]):t.add(r),yp(A,r,n)),!1;case 22:return e.flags|=65536,r===Yr?e.flags|=16384:(null===(t=e.updateQueue)?(t={transitions:null,markerInstances:null,retryQueue:new Set([r])},e.updateQueue=t):null===(e=t.retryQueue)?t.retryQueue=new Set([r]):e.add(r),yp(A,r,n)),!1}throw Error(a(435,e.tag))}return yp(A,r,n),ap(),!1}if(ir)return null!==(t=Pn.current)?(!(65536&t.flags)&&(t.flags|=256),t.flags|=65536,t.lanes=n,r!==pr&&mr(Ha(A=Error(a(422),{cause:r}),e))):(r!==pr&&mr(Ha(t=Error(a(423),{cause:r}),e)),(A=A.current.alternate).flags|=65536,n&=-n,A.lanes|=n,r=Ha(r,e),Ln(A,n=Ei(A.stateNode,r,n)),4!==gs&&(gs=2)),!1;var l=Error(a(520),{cause:r});if(l=Ha(l,e),null===ks?ks=[l]:ks.push(l),4!==gs&&(gs=2),null===t)return!0;r=Ha(r,e),e=t;do{switch(e.tag){case 3:return e.flags|=65536,A=n&-n,e.lanes|=A,Ln(e,A=Ei(e.stateNode,r,A)),!1;case 1:if(t=e.type,l=e.stateNode,!(128&e.flags||\"function\"!=typeof t.getDerivedStateFromError&&(null===l||\"function\"!=typeof l.componentDidCatch||null!==Ds&&Ds.has(l))))return e.flags|=65536,n&=-n,e.lanes|=n,wi(n=ki(n),A,e,r),Ln(e,n),!1}e=e.return}while(null!==e);return!1}(A,n,t,e,ds))return gs=1,bi(A,Ha(e,A.current)),void(us=null)}catch(l){if(null!==n)throw us=n,l;return gs=1,bi(A,Ha(e,A.current)),void(us=null)}32768&t.flags?(ir||1===r?A=!0:ms||536870912&ds?A=!1:(fs=A=!0,(2===r||9===r||3===r||6===r)&&(null!==(r=Pn.current)&&13===r.tag&&(r.flags|=16384))),cp(t,A)):pp(t)}function pp(A){var t=A;do{if(32768&t.flags)return void cp(t,fs);A=t.return;var e=so(t.alternate,t,Ws);if(null!==e)return void(us=e);if(null!==(t=t.sibling))return void(us=t);us=t=A}while(null!==t);0===gs&&(gs=5)}function cp(A,t){do{var e=po(A.alternate,A);if(null!==e)return e.flags&=32767,void(us=e);if(null!==(e=A.return)&&(e.flags|=32768,e.subtreeFlags=0,e.deletions=null),!t&&null!==(A=A.sibling))return void(us=A);us=A=e}while(null!==A);gs=6,us=null}function up(A,t,e,r,n,l,i,o,s){A.cancelPendingCommit=null;do{mp()}while(0!==Rs);if(6&ps)throw Error(a(327));if(null!==t){if(t===A.current)throw Error(a(177));if(l=t.lanes|t.childLanes,function(A,t,e,a,r,n){var l=A.pendingLanes;A.pendingLanes=e,A.suspendedLanes=0,A.pingedLanes=0,A.warmLanes=0,A.expiredLanes&=e,A.entangledLanes&=e,A.errorRecoveryDisabledLanes&=e,A.shellSuspendCounter=0;var i=A.entanglements,o=A.expirationTimes,s=A.hiddenUpdates;for(e=l&~e;0<e;){var p=31-mA(e),c=1<<p;i[p]=0,o[p]=-1;var u=s[p];if(null!==u)for(s[p]=null,p=0;p<u.length;p++){var d=u[p];null!==d&&(d.lane&=-536870913)}e&=~c}0!==a&&TA(A,a,0),0!==n&&0===r&&0!==A.tag&&(A.suspendedLanes|=n&~(l&~t))}(A,e,l|=Ea,i,o,s),A===cs&&(us=cs=null,ds=0),Is=t,Ns=A,Fs=e,Os=l,Vs=n,Bs=r,10256&t.subtreeFlags||10256&t.flags?(A.callbackNode=null,A.callbackPriority=0,tA(sA,function(){return Lp(),null})):(A.callbackNode=null,A.callbackPriority=0),r=!!(13878&t.flags),13878&t.subtreeFlags||r){r=_.T,_.T=null,n=M.p,M.p=2,i=ps,ps|=4;try{!function(A,t){if(A=A.containerInfo,cc=fu,Aa(A=Ze(A))){if(\"selectionStart\"in A)var e={start:A.selectionStart,end:A.selectionEnd};else A:{var r=(e=(e=A.ownerDocument)&&e.defaultView||window).getSelection&&e.getSelection();if(r&&0!==r.rangeCount){e=r.anchorNode;var n=r.anchorOffset,l=r.focusNode;r=r.focusOffset;try{e.nodeType,l.nodeType}catch(f){e=null;break A}var i=0,o=-1,s=-1,p=0,c=0,u=A,d=null;t:for(;;){for(var h;u!==e||0!==n&&3!==u.nodeType||(o=i+n),u!==l||0!==r&&3!==u.nodeType||(s=i+r),3===u.nodeType&&(i+=u.nodeValue.length),null!==(h=u.firstChild);)d=u,u=h;for(;;){if(u===A)break t;if(d===e&&++p===n&&(o=i),d===l&&++c===r&&(s=i),null!==(h=u.nextSibling))break;d=(u=d).parentNode}u=h}e=-1===o||-1===s?null:{start:o,end:s}}else e=null}e=e||{start:0,end:0}}else e=null;for(uc={focusedElem:A,selectionRange:e},fu=!1,To=t;null!==To;)if(A=(t=To).child,1028&t.subtreeFlags&&null!==A)A.return=t,To=A;else for(;null!==To;){switch(l=(t=To).alternate,A=t.flags,t.tag){case 0:if(4&A&&null!==(A=null!==(A=t.updateQueue)?A.events:null))for(e=0;e<A.length;e++)(n=A[e]).ref.impl=n.nextImpl;break;case 11:case 15:case 5:case 26:case 27:case 6:case 4:case 17:break;case 1:if(1024&A&&null!==l){A=void 0,e=t,n=l.memoizedProps,l=l.memoizedState,r=e.stateNode;try{var S=Wi(e.type,n);A=r.getSnapshotBeforeUpdate(S,l),r.__reactInternalSnapshotBeforeUpdate=A}catch(m){gp(e,e.return,m)}}break;case 3:if(1024&A)if(9===(e=(A=t.stateNode.containerInfo).nodeType))kc(A);else if(1===e)switch(A.nodeName){case\"HEAD\":case\"HTML\":case\"BODY\":kc(A);break;default:A.textContent=\"\"}break;default:if(1024&A)throw Error(a(163))}if(null!==(A=t.sibling)){A.return=t.return,To=A;break}To=t.return}}(A,t)}finally{ps=i,M.p=n,_.T=r}}Rs=1,dp(),hp(),Sp()}}function dp(){if(1===Rs){Rs=0;var A=Ns,t=Is,e=!!(13878&t.flags);if(13878&t.subtreeFlags||e){e=_.T,_.T=null;var a=M.p;M.p=2;var r=ps;ps|=4;try{jo(t,A);var n=uc,l=Ze(A.containerInfo),i=n.focusedElem,o=n.selectionRange;if(l!==i&&i&&i.ownerDocument&&Qe(i.ownerDocument.documentElement,i)){if(null!==o&&Aa(i)){var s=o.start,p=o.end;if(void 0===p&&(p=s),\"selectionStart\"in i)i.selectionStart=s,i.selectionEnd=Math.min(p,i.value.length);else{var c=i.ownerDocument||document,u=c&&c.defaultView||window;if(u.getSelection){var d=u.getSelection(),h=i.textContent.length,S=Math.min(o.start,h),f=void 0===o.end?S:Math.min(o.end,h);!d.extend&&S>f&&(l=f,f=S,S=l);var m=Ke(i,S),L=Ke(i,f);if(m&&L&&(1!==d.rangeCount||d.anchorNode!==m.node||d.anchorOffset!==m.offset||d.focusNode!==L.node||d.focusOffset!==L.offset)){var W=c.createRange();W.setStart(m.node,m.offset),d.removeAllRanges(),S>f?(d.addRange(W),d.extend(L.node,L.offset)):(W.setEnd(L.node,L.offset),d.addRange(W))}}}}for(c=[],d=i;d=d.parentNode;)1===d.nodeType&&c.push({element:d,left:d.scrollLeft,top:d.scrollTop});for(\"function\"==typeof i.focus&&i.focus(),i=0;i<c.length;i++){var g=c[i];g.element.scrollLeft=g.left,g.element.scrollTop=g.top}}fu=!!cc,uc=cc=null}finally{ps=r,M.p=a,_.T=e}}A.current=t,Rs=2}}function hp(){if(2===Rs){Rs=0;var A=Ns,t=Is,e=!!(8772&t.flags);if(8772&t.subtreeFlags||e){e=_.T,_.T=null;var a=M.p;M.p=2;var r=ps;ps|=4;try{_o(A,t.alternate,t)}finally{ps=r,M.p=a,_.T=e}}Rs=3}}function Sp(){if(4===Rs||3===Rs){Rs=0,rA();var A=Ns,t=Is,e=Fs,a=Bs;10256&t.subtreeFlags||10256&t.flags?Rs=5:(Rs=0,Is=Ns=null,fp(A,A.pendingLanes));var r=A.pendingLanes;if(0===r&&(Ds=null),RA(e),t=t.stateNode,SA&&\"function\"==typeof SA.onCommitFiberRoot)try{SA.onCommitFiberRoot(hA,t,void 0,!(128&~t.current.flags))}catch(err){}if(null!==a){t=_.T,r=M.p,M.p=2,_.T=null;try{for(var n=A.onRecoverableError,l=0;l<a.length;l++){var i=a[l];n(i.value,{componentStack:i.stack})}}finally{_.T=t,M.p=r}}3&Fs&&mp(),Mp(A),r=A.pendingLanes,261930&e&&42&r?A===js?zs++:(zs=0,js=A):zs=0,Dp(0)}}function fp(A,t){0===(A.pooledCacheLanes&=t)&&(null!=(t=A.pooledCache)&&(A.pooledCache=null,Ir(t)))}function mp(){return dp(),hp(),Sp(),Lp()}function Lp(){if(5!==Rs)return!1;var A=Ns,t=Os;Os=0;var e=RA(Fs),r=_.T,n=M.p;try{M.p=32>e?32:e,_.T=null,e=Vs,Vs=null;var l=Ns,i=Fs;if(Rs=0,Is=Ns=null,Fs=0,6&ps)throw Error(a(331));var o=ps;if(ps|=4,ns(l.current),Ko(l,l.current,i,e),ps=o,Dp(0,!1),SA&&\"function\"==typeof SA.onPostCommitFiberRoot)try{SA.onPostCommitFiberRoot(hA,l)}catch(err){}return!0}finally{M.p=n,_.T=r,fp(A,t)}}function Wp(A,t,e){t=Ha(e,t),null!==(A=fn(A,t=Ei(A.stateNode,t,2),2))&&(PA(A,2),Mp(A))}function gp(A,t,e){if(3===A.tag)Wp(A,A,e);else for(;null!==t;){if(3===t.tag){Wp(t,A,e);break}if(1===t.tag){var a=t.stateNode;if(\"function\"==typeof t.type.getDerivedStateFromError||\"function\"==typeof a.componentDidCatch&&(null===Ds||!Ds.has(a))){A=Ha(e,A),null!==(a=fn(t,e=ki(2),2))&&(wi(e,a,t,A),PA(a,2),Mp(a));break}}t=t.return}}function yp(A,t,e){var a=A.pingCache;if(null===a){a=A.pingCache=new ss;var r=new Set;a.set(t,r)}else void 0===(r=a.get(t))&&(r=new Set,a.set(t,r));r.has(e)||(Ls=!0,r.add(e),A=vp.bind(null,A,t,e),t.then(A,A))}function vp(A,t,e){var a=A.pingCache;null!==a&&a.delete(t),A.pingedLanes|=A.suspendedLanes&e,A.warmLanes&=~e,cs===A&&(ds&e)===e&&(4===gs||3===gs&&(62914560&ds)===ds&&300>nA()-Ps?!(2&ps)&&Qs(A,0):bs|=e,Es===ds&&(Es=0)),Mp(A)}function bp(A,t){0===t&&(t=wA()),null!==(A=Pa(A,t))&&(PA(A,t),Mp(A))}function xp(A){var t=A.memoizedState,e=0;null!==t&&(e=t.retryLane),bp(A,e)}function Ep(A,t){var e=0;switch(A.tag){case 31:case 13:var r=A.stateNode,n=A.memoizedState;null!==n&&(e=n.retryLane);break;case 19:r=A.stateNode;break;case 22:r=A.stateNode._retryCache;break;default:throw Error(a(314))}null!==r&&r.delete(t),bp(A,e)}var kp=null,wp=null,Cp=!1,Pp=!1,Tp=!1,_p=0;function Mp(A){A!==wp&&null===A.next&&(null===wp?kp=wp=A:wp=wp.next=A),Pp=!0,Cp||(Cp=!0,yc(function(){6&ps?tA(iA,Rp):Np()}))}function Dp(A,t){if(!Tp&&Pp){Tp=!0;do{for(var e=!1,a=kp;null!==a;){if(0!==A){var r=a.pendingLanes;if(0===r)var n=0;else{var l=a.suspendedLanes,i=a.pingedLanes;n=(1<<31-mA(42|A)+1)-1,n=201326741&(n&=r&~(l&~i))?201326741&n|1:n?2|n:0}0!==n&&(e=!0,Op(a,n))}else n=ds,!(3&(n=xA(a,a===cs?n:0,null!==a.cancelPendingCommit||-1!==a.timeoutHandle)))||EA(a,n)||(e=!0,Op(a,n));a=a.next}}while(e);Tp=!1}}function Rp(){Np()}function Np(){Pp=Cp=!1;var A=0;0!==_p&&function(){var A=window.event;if(A&&\"popstate\"===A.type)return A!==mc&&(mc=A,!0);return mc=null,!1}()&&(A=_p);for(var t=nA(),e=null,a=kp;null!==a;){var r=a.next,n=Ip(a,t);0===n?(a.next=null,null===e?kp=r:e.next=r,null===r&&(wp=e)):(e=a,(0!==A||3&n)&&(Pp=!0)),a=r}0!==Rs&&5!==Rs||Dp(A),0!==_p&&(_p=0)}function Ip(A,t){for(var e=A.suspendedLanes,a=A.pingedLanes,r=A.expirationTimes,n=-62914561&A.pendingLanes;0<n;){var l=31-mA(n),i=1<<l,o=r[l];-1===o?0!==(i&e)&&0===(i&a)||(r[l]=kA(i,t)):o<=t&&(A.expiredLanes|=i),n&=~i}if(e=ds,e=xA(A,A===(t=cs)?e:0,null!==A.cancelPendingCommit||-1!==A.timeoutHandle),a=A.callbackNode,0===e||A===t&&(2===hs||9===hs)||null!==A.cancelPendingCommit)return null!==a&&null!==a&&eA(a),A.callbackNode=null,A.callbackPriority=0;if(!(3&e)||EA(A,e)){if((t=e&-e)===A.callbackPriority)return t;switch(null!==a&&eA(a),RA(e)){case 2:case 8:e=oA;break;case 32:default:e=sA;break;case 268435456:e=cA}return a=Fp.bind(null,A),e=tA(e,a),A.callbackPriority=t,A.callbackNode=e,t}return null!==a&&null!==a&&eA(a),A.callbackPriority=2,A.callbackNode=null,2}function Fp(A,t){if(0!==Rs&&5!==Rs)return A.callbackNode=null,A.callbackPriority=0,null;var e=A.callbackNode;if(mp()&&A.callbackNode!==e)return null;var a=ds;return 0===(a=xA(A,A===cs?a:0,null!==A.cancelPendingCommit||-1!==A.timeoutHandle))?null:(qs(A,a,t),Ip(A,nA()),null!=A.callbackNode&&A.callbackNode===e?Fp.bind(null,A):null)}function Op(A,t){if(mp())return null;qs(A,t,!0)}function Vp(){if(0===_p){var A=Vr;0===A&&(A=gA,!(261888&(gA<<=1))&&(gA=256)),_p=A}return _p}function Bp(A){return null==A||\"symbol\"==typeof A||\"boolean\"==typeof A?null:\"function\"==typeof A?A:Ct(\"\"+A)}function zp(A,t){var e=t.ownerDocument.createElement(\"input\");return e.name=t.name,e.value=t.value,A.id&&e.setAttribute(\"form\",A.id),t.parentNode.insertBefore(e,t),A=new FormData(A),e.parentNode.removeChild(e),A}for(var jp=0;jp<ga.length;jp++){var $p=ga[jp];ya($p.toLowerCase(),\"on\"+($p[0].toUpperCase()+$p.slice(1)))}ya(ua,\"onAnimationEnd\"),ya(da,\"onAnimationIteration\"),ya(ha,\"onAnimationStart\"),ya(\"dblclick\",\"onDoubleClick\"),ya(\"focusin\",\"onFocus\"),ya(\"focusout\",\"onBlur\"),ya(Sa,\"onTransitionRun\"),ya(fa,\"onTransitionStart\"),ya(ma,\"onTransitionCancel\"),ya(La,\"onTransitionEnd\"),tt(\"onMouseEnter\",[\"mouseout\",\"mouseover\"]),tt(\"onMouseLeave\",[\"mouseout\",\"mouseover\"]),tt(\"onPointerEnter\",[\"pointerout\",\"pointerover\"]),tt(\"onPointerLeave\",[\"pointerout\",\"pointerover\"]),At(\"onChange\",\"change click focusin focusout input keydown keyup selectionchange\".split(\" \")),At(\"onSelect\",\"focusout contextmenu dragend focusin keydown keyup mousedown mouseup selectionchange\".split(\" \")),At(\"onBeforeInput\",[\"compositionend\",\"keypress\",\"textInput\",\"paste\"]),At(\"onCompositionEnd\",\"compositionend focusout keydown keypress keyup mousedown\".split(\" \")),At(\"onCompositionStart\",\"compositionstart focusout keydown keypress keyup mousedown\".split(\" \")),At(\"onCompositionUpdate\",\"compositionupdate focusout keydown keypress keyup mousedown\".split(\" \"));var Hp=\"abort canplay canplaythrough durationchange emptied encrypted ended error loadeddata loadedmetadata loadstart pause play playing progress ratechange resize seeked seeking stalled suspend timeupdate volumechange waiting\".split(\" \"),Gp=new Set(\"beforetoggle cancel close invalid load scroll scrollend toggle\".split(\" \").concat(Hp));function qp(A,t){t=!!(4&t);for(var e=0;e<A.length;e++){var a=A[e],r=a.event;a=a.listeners;A:{var n=void 0;if(t)for(var l=a.length-1;0<=l;l--){var i=a[l],o=i.instance,s=i.currentTarget;if(i=i.listener,o!==n&&r.isPropagationStopped())break A;n=i,r.currentTarget=s;try{n(r)}catch(p){va(p)}r.currentTarget=null,n=o}else for(l=0;l<a.length;l++){if(o=(i=a[l]).instance,s=i.currentTarget,i=i.listener,o!==n&&r.isPropagationStopped())break A;n=i,r.currentTarget=s;try{n(r)}catch(p){va(p)}r.currentTarget=null,n=o}}}}function Up(A,t){var e=t[zA];void 0===e&&(e=t[zA]=new Set);var a=A+\"__bubble\";e.has(a)||(Kp(t,A,2,!1),e.add(a))}function Xp(A,t,e){var a=0;t&&(a|=4),Kp(e,A,a,t)}var Jp=\"_reactListening\"+Math.random().toString(36).slice(2);function Yp(A){if(!A[Jp]){A[Jp]=!0,QA.forEach(function(t){\"selectionchange\"!==t&&(Gp.has(t)||Xp(t,!1,A),Xp(t,!0,A))});var t=9===A.nodeType?A:A.ownerDocument;null===t||t[Jp]||(t[Jp]=!0,Xp(\"selectionchange\",!1,t))}}function Kp(A,t,e,a){switch(bu(t)){case 2:var r=mu;break;case 8:r=Lu;break;default:r=Wu}e=r.bind(null,t,e,A),r=void 0,!Vt||\"touchstart\"!==t&&\"touchmove\"!==t&&\"wheel\"!==t||(r=!0),a?void 0!==r?A.addEventListener(t,e,{capture:!0,passive:r}):A.addEventListener(t,e,!0):void 0!==r?A.addEventListener(t,e,{passive:r}):A.addEventListener(t,e,!1)}function Qp(A,t,e,a,r){var l=a;if(!(1&t||2&t||null===a))A:for(;;){if(null===a)return;var i=a.tag;if(3===i||4===i){var o=a.stateNode.containerInfo;if(o===r)break;if(4===i)for(i=a.return;null!==i;){var s=i.tag;if((3===s||4===s)&&i.stateNode.containerInfo===r)return;i=i.return}for(;null!==o;){if(null===(i=UA(o)))return;if(5===(s=i.tag)||6===s||26===s||27===s){a=l=i;continue A}o=o.parentNode}}a=a.return}It(function(){var a=l,r=_t(e),i=[];A:{var o=Wa.get(A);if(void 0!==o){var s=Zt,p=A;switch(A){case\"keypress\":if(0===Gt(e))break A;case\"keydown\":case\"keyup\":s=he;break;case\"focusin\":p=\"focus\",s=ne;break;case\"focusout\":p=\"blur\",s=ne;break;case\"beforeblur\":case\"afterblur\":s=ne;break;case\"click\":if(2===e.button)break A;case\"auxclick\":case\"dblclick\":case\"mousedown\":case\"mousemove\":case\"mouseup\":case\"mouseout\":case\"mouseover\":case\"contextmenu\":s=ae;break;case\"drag\":case\"dragend\":case\"dragenter\":case\"dragexit\":case\"dragleave\":case\"dragover\":case\"dragstart\":case\"drop\":s=re;break;case\"touchcancel\":case\"touchend\":case\"touchmove\":case\"touchstart\":s=fe;break;case ua:case da:case ha:s=le;break;case La:s=me;break;case\"scroll\":case\"scrollend\":s=te;break;case\"wheel\":s=Le;break;case\"copy\":case\"cut\":case\"paste\":s=ie;break;case\"gotpointercapture\":case\"lostpointercapture\":case\"pointercancel\":case\"pointerdown\":case\"pointermove\":case\"pointerout\":case\"pointerover\":case\"pointerup\":s=Se;break;case\"toggle\":case\"beforetoggle\":s=We}var c=!!(4&t),u=!c&&(\"scroll\"===A||\"scrollend\"===A),d=c?null!==o?o+\"Capture\":null:o;c=[];for(var h,S=a;null!==S;){var f=S;if(h=f.stateNode,5!==(f=f.tag)&&26!==f&&27!==f||null===h||null===d||null!=(f=Ft(S,d))&&c.push(Zp(S,f,h)),u)break;S=S.return}0<c.length&&(o=new s(o,p,null,e,r),i.push({event:o,listeners:c}))}}if(!(7&t)){if(s=\"mouseout\"===A||\"pointerout\"===A,(!(o=\"mouseover\"===A||\"pointerover\"===A)||e===Tt||!(p=e.relatedTarget||e.fromElement)||!UA(p)&&!p[BA])&&(s||o)&&(o=r.window===r?r:(o=r.ownerDocument)?o.defaultView||o.parentWindow:window,s?(s=a,null!==(p=(p=e.relatedTarget||e.toElement)?UA(p):null)&&(u=n(p),c=p.tag,p!==u||5!==c&&27!==c&&6!==c)&&(p=null)):(s=null,p=a),s!==p)){if(c=ae,f=\"onMouseLeave\",d=\"onMouseEnter\",S=\"mouse\",\"pointerout\"!==A&&\"pointerover\"!==A||(c=Se,f=\"onPointerLeave\",d=\"onPointerEnter\",S=\"pointer\"),u=null==s?o:JA(s),h=null==p?o:JA(p),(o=new c(f,S+\"leave\",s,e,r)).target=u,o.relatedTarget=h,f=null,UA(r)===a&&((c=new c(d,S+\"enter\",p,e,r)).target=h,c.relatedTarget=u,f=c),u=f,s&&p)A:{for(c=tc,S=p,h=0,f=d=s;f;f=c(f))h++;f=0;for(var m=S;m;m=c(m))f++;for(;0<h-f;)d=c(d),h--;for(;0<f-h;)S=c(S),f--;for(;h--;){if(d===S||null!==S&&d===S.alternate){c=d;break A}d=c(d),S=c(S)}c=null}else c=null;null!==s&&ec(i,o,s,c,!1),null!==p&&null!==u&&ec(i,u,p,c,!0)}if(\"select\"===(s=(o=a?JA(a):window).nodeName&&o.nodeName.toLowerCase())||\"input\"===s&&\"file\"===o.type)var L=Fe;else if(_e(o))if(Oe)L=Ue;else{L=Ge;var W=He}else!(s=o.nodeName)||\"input\"!==s.toLowerCase()||\"checkbox\"!==o.type&&\"radio\"!==o.type?a&&Et(a.elementType)&&(L=Fe):L=qe;switch(L&&(L=L(A,a))?Me(i,L,e,r):(W&&W(A,o,a),\"focusout\"===A&&a&&\"number\"===o.type&&null!=a.memoizedProps.value&&mt(o,\"number\",o.value)),W=a?JA(a):window,A){case\"focusin\":(_e(W)||\"true\"===W.contentEditable)&&(ea=W,aa=a,ra=null);break;case\"focusout\":ra=aa=ea=null;break;case\"mousedown\":na=!0;break;case\"contextmenu\":case\"mouseup\":case\"dragend\":na=!1,la(i,e,r);break;case\"selectionchange\":if(ta)break;case\"keydown\":case\"keyup\":la(i,e,r)}var g;if(ye)A:{switch(A){case\"compositionstart\":var y=\"onCompositionStart\";break A;case\"compositionend\":y=\"onCompositionEnd\";break A;case\"compositionupdate\":y=\"onCompositionUpdate\";break A}y=void 0}else Pe?we(A,e)&&(y=\"onCompositionEnd\"):\"keydown\"===A&&229===e.keyCode&&(y=\"onCompositionStart\");y&&(xe&&\"ko\"!==e.locale&&(Pe||\"onCompositionStart\"!==y?\"onCompositionEnd\"===y&&Pe&&(g=Ht()):(jt=\"value\"in(zt=r)?zt.value:zt.textContent,Pe=!0)),0<(W=Ac(a,y)).length&&(y=new oe(y,A,null,e,r),i.push({event:y,listeners:W}),g?y.data=g:null!==(g=Ce(e))&&(y.data=g))),(g=be?function(A,t){switch(A){case\"compositionend\":return Ce(t);case\"keypress\":return 32!==t.which?null:(ke=!0,Ee);case\"textInput\":return(A=t.data)===Ee&&ke?null:A;default:return null}}(A,e):function(A,t){if(Pe)return\"compositionend\"===A||!ye&&we(A,t)?(A=Ht(),$t=jt=zt=null,Pe=!1,A):null;switch(A){case\"paste\":default:return null;case\"keypress\":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1<t.char.length)return t.char;if(t.which)return String.fromCharCode(t.which)}return null;case\"compositionend\":return xe&&\"ko\"!==t.locale?null:t.data}}(A,e))&&(0<(y=Ac(a,\"onBeforeInput\")).length&&(W=new oe(\"onBeforeInput\",\"beforeinput\",null,e,r),i.push({event:W,listeners:y}),W.data=g)),function(A,t,e,a,r){if(\"submit\"===t&&e&&e.stateNode===r){var n=Bp((r[VA]||null).action),l=a.submitter;l&&null!==(t=(t=l[VA]||null)?Bp(t.formAction):l.getAttribute(\"formAction\"))&&(n=t,l=null);var i=new Zt(\"action\",\"action\",null,a,r);A.push({event:i,listeners:[{instance:null,listener:function(){if(a.defaultPrevented){if(0!==_p){var A=l?zp(r,l):new FormData(r);Kl(e,{pending:!0,data:A,method:r.method,action:n},null,A)}}else\"function\"==typeof n&&(i.preventDefault(),A=l?zp(r,l):new FormData(r),Kl(e,{pending:!0,data:A,method:r.method,action:n},n,A))},currentTarget:r}]})}}(i,A,a,e,r)}qp(i,t)})}function Zp(A,t,e){return{instance:A,listener:t,currentTarget:e}}function Ac(A,t){for(var e=t+\"Capture\",a=[];null!==A;){var r=A,n=r.stateNode;if(5!==(r=r.tag)&&26!==r&&27!==r||null===n||(null!=(r=Ft(A,e))&&a.unshift(Zp(A,r,n)),null!=(r=Ft(A,t))&&a.push(Zp(A,r,n))),3===A.tag)return a;A=A.return}return[]}function tc(A){if(null===A)return null;do{A=A.return}while(A&&5!==A.tag&&27!==A.tag);return A||null}function ec(A,t,e,a,r){for(var n=t._reactName,l=[];null!==e&&e!==a;){var i=e,o=i.alternate,s=i.stateNode;if(i=i.tag,null!==o&&o===a)break;5!==i&&26!==i&&27!==i||null===s||(o=s,r?null!=(s=Ft(e,n))&&l.unshift(Zp(e,s,o)):r||null!=(s=Ft(e,n))&&l.push(Zp(e,s,o))),e=e.return}0!==l.length&&A.push({event:t,listeners:l})}var ac=/\\r\\n?/g,rc=/\\u0000|\\uFFFD/g;function nc(A){return(\"string\"==typeof A?A:\"\"+A).replace(ac,\"\\n\").replace(rc,\"\")}function lc(A,t){return t=nc(t),nc(A)===t}function ic(A,t,e,r,n,l){switch(e){case\"children\":\"string\"==typeof r?\"body\"===t||\"textarea\"===t&&\"\"===r||yt(A,r):(\"number\"==typeof r||\"bigint\"==typeof r)&&\"body\"!==t&&yt(A,\"\"+r);break;case\"className\":lt(A,\"class\",r);break;case\"tabIndex\":lt(A,\"tabindex\",r);break;case\"dir\":case\"role\":case\"viewBox\":case\"width\":case\"height\":lt(A,e,r);break;case\"style\":xt(A,r,l);break;case\"data\":if(\"object\"!==t){lt(A,\"data\",r);break}case\"src\":case\"href\":if(\"\"===r&&(\"a\"!==t||\"href\"!==e)){A.removeAttribute(e);break}if(null==r||\"function\"==typeof r||\"symbol\"==typeof r||\"boolean\"==typeof r){A.removeAttribute(e);break}r=Ct(\"\"+r),A.setAttribute(e,r);break;case\"action\":case\"formAction\":if(\"function\"==typeof r){A.setAttribute(e,\"javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')\");break}if(\"function\"==typeof l&&(\"formAction\"===e?(\"input\"!==t&&ic(A,t,\"name\",n.name,n,null),ic(A,t,\"formEncType\",n.formEncType,n,null),ic(A,t,\"formMethod\",n.formMethod,n,null),ic(A,t,\"formTarget\",n.formTarget,n,null)):(ic(A,t,\"encType\",n.encType,n,null),ic(A,t,\"method\",n.method,n,null),ic(A,t,\"target\",n.target,n,null))),null==r||\"symbol\"==typeof r||\"boolean\"==typeof r){A.removeAttribute(e);break}r=Ct(\"\"+r),A.setAttribute(e,r);break;case\"onClick\":null!=r&&(A.onclick=Pt);break;case\"onScroll\":null!=r&&Up(\"scroll\",A);break;case\"onScrollEnd\":null!=r&&Up(\"scrollend\",A);break;case\"dangerouslySetInnerHTML\":if(null!=r){if(\"object\"!=typeof r||!(\"__html\"in r))throw Error(a(61));if(null!=(e=r.__html)){if(null!=n.children)throw Error(a(60));A.innerHTML=e}}break;case\"multiple\":A.multiple=r&&\"function\"!=typeof r&&\"symbol\"!=typeof r;break;case\"muted\":A.muted=r&&\"function\"!=typeof r&&\"symbol\"!=typeof r;break;case\"suppressContentEditableWarning\":case\"suppressHydrationWarning\":case\"defaultValue\":case\"defaultChecked\":case\"innerHTML\":case\"ref\":case\"autoFocus\":break;case\"xlinkHref\":if(null==r||\"function\"==typeof r||\"boolean\"==typeof r||\"symbol\"==typeof r){A.removeAttribute(\"xlink:href\");break}e=Ct(\"\"+r),A.setAttributeNS(\"http://www.w3.org/1999/xlink\",\"xlink:href\",e);break;case\"contentEditable\":case\"spellCheck\":case\"draggable\":case\"value\":case\"autoReverse\":case\"externalResourcesRequired\":case\"focusable\":case\"preserveAlpha\":null!=r&&\"function\"!=typeof r&&\"symbol\"!=typeof r?A.setAttribute(e,\"\"+r):A.removeAttribute(e);break;case\"inert\":case\"allowFullScreen\":case\"async\":case\"autoPlay\":case\"controls\":case\"default\":case\"defer\":case\"disabled\":case\"disablePictureInPicture\":case\"disableRemotePlayback\":case\"formNoValidate\":case\"hidden\":case\"loop\":case\"noModule\":case\"noValidate\":case\"open\":case\"playsInline\":case\"readOnly\":case\"required\":case\"reversed\":case\"scoped\":case\"seamless\":case\"itemScope\":r&&\"function\"!=typeof r&&\"symbol\"!=typeof r?A.setAttribute(e,\"\"):A.removeAttribute(e);break;case\"capture\":case\"download\":!0===r?A.setAttribute(e,\"\"):!1!==r&&null!=r&&\"function\"!=typeof r&&\"symbol\"!=typeof r?A.setAttribute(e,r):A.removeAttribute(e);break;case\"cols\":case\"rows\":case\"size\":case\"span\":null!=r&&\"function\"!=typeof r&&\"symbol\"!=typeof r&&!isNaN(r)&&1<=r?A.setAttribute(e,r):A.removeAttribute(e);break;case\"rowSpan\":case\"start\":null==r||\"function\"==typeof r||\"symbol\"==typeof r||isNaN(r)?A.removeAttribute(e):A.setAttribute(e,r);break;case\"popover\":Up(\"beforetoggle\",A),Up(\"toggle\",A),nt(A,\"popover\",r);break;case\"xlinkActuate\":it(A,\"http://www.w3.org/1999/xlink\",\"xlink:actuate\",r);break;case\"xlinkArcrole\":it(A,\"http://www.w3.org/1999/xlink\",\"xlink:arcrole\",r);break;case\"xlinkRole\":it(A,\"http://www.w3.org/1999/xlink\",\"xlink:role\",r);break;case\"xlinkShow\":it(A,\"http://www.w3.org/1999/xlink\",\"xlink:show\",r);break;case\"xlinkTitle\":it(A,\"http://www.w3.org/1999/xlink\",\"xlink:title\",r);break;case\"xlinkType\":it(A,\"http://www.w3.org/1999/xlink\",\"xlink:type\",r);break;case\"xmlBase\":it(A,\"http://www.w3.org/XML/1998/namespace\",\"xml:base\",r);break;case\"xmlLang\":it(A,\"http://www.w3.org/XML/1998/namespace\",\"xml:lang\",r);break;case\"xmlSpace\":it(A,\"http://www.w3.org/XML/1998/namespace\",\"xml:space\",r);break;case\"is\":nt(A,\"is\",r);break;case\"innerText\":case\"textContent\":break;default:(!(2<e.length)||\"o\"!==e[0]&&\"O\"!==e[0]||\"n\"!==e[1]&&\"N\"!==e[1])&&nt(A,e=kt.get(e)||e,r)}}function oc(A,t,e,r,n,l){switch(e){case\"style\":xt(A,r,l);break;case\"dangerouslySetInnerHTML\":if(null!=r){if(\"object\"!=typeof r||!(\"__html\"in r))throw Error(a(61));if(null!=(e=r.__html)){if(null!=n.children)throw Error(a(60));A.innerHTML=e}}break;case\"children\":\"string\"==typeof r?yt(A,r):(\"number\"==typeof r||\"bigint\"==typeof r)&&yt(A,\"\"+r);break;case\"onScroll\":null!=r&&Up(\"scroll\",A);break;case\"onScrollEnd\":null!=r&&Up(\"scrollend\",A);break;case\"onClick\":null!=r&&(A.onclick=Pt);break;case\"suppressContentEditableWarning\":case\"suppressHydrationWarning\":case\"innerHTML\":case\"ref\":case\"innerText\":case\"textContent\":break;default:ZA.hasOwnProperty(e)||(\"o\"!==e[0]||\"n\"!==e[1]||(n=e.endsWith(\"Capture\"),t=e.slice(2,n?e.length-7:void 0),\"function\"==typeof(l=null!=(l=A[VA]||null)?l[e]:null)&&A.removeEventListener(t,l,n),\"function\"!=typeof r)?e in A?A[e]=r:!0===r?A.setAttribute(e,\"\"):nt(A,e,r):(\"function\"!=typeof l&&null!==l&&(e in A?A[e]=null:A.hasAttribute(e)&&A.removeAttribute(e)),A.addEventListener(t,r,n)))}}function sc(A,t,e){switch(t){case\"div\":case\"span\":case\"svg\":case\"path\":case\"a\":case\"g\":case\"p\":case\"li\":break;case\"img\":Up(\"error\",A),Up(\"load\",A);var r,n=!1,l=!1;for(r in e)if(e.hasOwnProperty(r)){var i=e[r];if(null!=i)switch(r){case\"src\":n=!0;break;case\"srcSet\":l=!0;break;case\"children\":case\"dangerouslySetInnerHTML\":throw Error(a(137,t));default:ic(A,t,r,i,e,null)}}return l&&ic(A,t,\"srcSet\",e.srcSet,e,null),void(n&&ic(A,t,\"src\",e.src,e,null));case\"input\":Up(\"invalid\",A);var o=r=i=l=null,s=null,p=null;for(n in e)if(e.hasOwnProperty(n)){var c=e[n];if(null!=c)switch(n){case\"name\":l=c;break;case\"type\":i=c;break;case\"checked\":s=c;break;case\"defaultChecked\":p=c;break;case\"value\":r=c;break;case\"defaultValue\":o=c;break;case\"children\":case\"dangerouslySetInnerHTML\":if(null!=c)throw Error(a(137,t));break;default:ic(A,t,n,c,e,null)}}return void ft(A,r,o,s,p,i,l,!1);case\"select\":for(l in Up(\"invalid\",A),n=i=r=null,e)if(e.hasOwnProperty(l)&&null!=(o=e[l]))switch(l){case\"value\":r=o;break;case\"defaultValue\":i=o;break;case\"multiple\":n=o;default:ic(A,t,l,o,e,null)}return t=r,e=i,A.multiple=!!n,void(null!=t?Lt(A,!!n,t,!1):null!=e&&Lt(A,!!n,e,!0));case\"textarea\":for(i in Up(\"invalid\",A),r=l=n=null,e)if(e.hasOwnProperty(i)&&null!=(o=e[i]))switch(i){case\"value\":n=o;break;case\"defaultValue\":l=o;break;case\"children\":r=o;break;case\"dangerouslySetInnerHTML\":if(null!=o)throw Error(a(91));break;default:ic(A,t,i,o,e,null)}return void gt(A,n,l,r);case\"option\":for(s in e)if(e.hasOwnProperty(s)&&null!=(n=e[s]))if(\"selected\"===s)A.selected=n&&\"function\"!=typeof n&&\"symbol\"!=typeof n;else ic(A,t,s,n,e,null);return;case\"dialog\":Up(\"beforetoggle\",A),Up(\"toggle\",A),Up(\"cancel\",A),Up(\"close\",A);break;case\"iframe\":case\"object\":Up(\"load\",A);break;case\"video\":case\"audio\":for(n=0;n<Hp.length;n++)Up(Hp[n],A);break;case\"image\":Up(\"error\",A),Up(\"load\",A);break;case\"details\":Up(\"toggle\",A);break;case\"embed\":case\"source\":case\"link\":Up(\"error\",A),Up(\"load\",A);case\"area\":case\"base\":case\"br\":case\"col\":case\"hr\":case\"keygen\":case\"meta\":case\"param\":case\"track\":case\"wbr\":case\"menuitem\":for(p in e)if(e.hasOwnProperty(p)&&null!=(n=e[p]))switch(p){case\"children\":case\"dangerouslySetInnerHTML\":throw Error(a(137,t));default:ic(A,t,p,n,e,null)}return;default:if(Et(t)){for(c in e)e.hasOwnProperty(c)&&(void 0!==(n=e[c])&&oc(A,t,c,n,e,void 0));return}}for(o in e)e.hasOwnProperty(o)&&(null!=(n=e[o])&&ic(A,t,o,n,e,null))}function pc(A){switch(A){case\"css\":case\"script\":case\"font\":case\"img\":case\"image\":case\"input\":case\"link\":return!0;default:return!1}}var cc=null,uc=null;function dc(A){return 9===A.nodeType?A:A.ownerDocument}function hc(A){switch(A){case\"http://www.w3.org/2000/svg\":return 1;case\"http://www.w3.org/1998/Math/MathML\":return 2;default:return 0}}function Sc(A,t){if(0===A)switch(t){case\"svg\":return 1;case\"math\":return 2;default:return 0}return 1===A&&\"foreignObject\"===t?0:A}function fc(A,t){return\"textarea\"===A||\"noscript\"===A||\"string\"==typeof t.children||\"number\"==typeof t.children||\"bigint\"==typeof t.children||\"object\"==typeof t.dangerouslySetInnerHTML&&null!==t.dangerouslySetInnerHTML&&null!=t.dangerouslySetInnerHTML.__html}var mc=null;var Lc=\"function\"==typeof setTimeout?setTimeout:void 0,Wc=\"function\"==typeof clearTimeout?clearTimeout:void 0,gc=\"function\"==typeof Promise?Promise:void 0,yc=\"function\"==typeof queueMicrotask?queueMicrotask:void 0!==gc?function(A){return gc.resolve(null).then(A).catch(vc)}:Lc;function vc(A){setTimeout(function(){throw A})}function bc(A){return\"head\"===A}function xc(A,t){var e=t,a=0;do{var r=e.nextSibling;if(A.removeChild(e),r&&8===r.nodeType)if(\"/$\"===(e=r.data)||\"/&\"===e){if(0===a)return A.removeChild(r),void zu(t);a--}else if(\"$\"===e||\"$?\"===e||\"$~\"===e||\"$!\"===e||\"&\"===e)a++;else if(\"html\"===e)Nc(A.ownerDocument.documentElement);else if(\"head\"===e){Nc(e=A.ownerDocument.head);for(var n=e.firstChild;n;){var l=n.nextSibling,i=n.nodeName;n[GA]||\"SCRIPT\"===i||\"STYLE\"===i||\"LINK\"===i&&\"stylesheet\"===n.rel.toLowerCase()||e.removeChild(n),n=l}}else\"body\"===e&&Nc(A.ownerDocument.body);e=r}while(e);zu(t)}function Ec(A,t){var e=A;A=0;do{var a=e.nextSibling;if(1===e.nodeType?t?(e._stashedDisplay=e.style.display,e.style.display=\"none\"):(e.style.display=e._stashedDisplay||\"\",\"\"===e.getAttribute(\"style\")&&e.removeAttribute(\"style\")):3===e.nodeType&&(t?(e._stashedText=e.nodeValue,e.nodeValue=\"\"):e.nodeValue=e._stashedText||\"\"),a&&8===a.nodeType)if(\"/$\"===(e=a.data)){if(0===A)break;A--}else\"$\"!==e&&\"$?\"!==e&&\"$~\"!==e&&\"$!\"!==e||A++;e=a}while(e)}function kc(A){var t=A.firstChild;for(t&&10===t.nodeType&&(t=t.nextSibling);t;){var e=t;switch(t=t.nextSibling,e.nodeName){case\"HTML\":case\"HEAD\":case\"BODY\":kc(e),qA(e);continue;case\"SCRIPT\":case\"STYLE\":continue;case\"LINK\":if(\"stylesheet\"===e.rel.toLowerCase())continue}A.removeChild(e)}}function wc(A,t){for(;8!==A.nodeType;){if((1!==A.nodeType||\"INPUT\"!==A.nodeName||\"hidden\"!==A.type)&&!t)return null;if(null===(A=Tc(A.nextSibling)))return null}return A}function Cc(A){return\"$?\"===A.data||\"$~\"===A.data}function Pc(A){return\"$!\"===A.data||\"$?\"===A.data&&\"loading\"!==A.ownerDocument.readyState}function Tc(A){for(;null!=A;A=A.nextSibling){var t=A.nodeType;if(1===t||3===t)break;if(8===t){if(\"$\"===(t=A.data)||\"$!\"===t||\"$?\"===t||\"$~\"===t||\"&\"===t||\"F!\"===t||\"F\"===t)break;if(\"/$\"===t||\"/&\"===t)return null}}return A}var _c=null;function Mc(A){A=A.nextSibling;for(var t=0;A;){if(8===A.nodeType){var e=A.data;if(\"/$\"===e||\"/&\"===e){if(0===t)return Tc(A.nextSibling);t--}else\"$\"!==e&&\"$!\"!==e&&\"$?\"!==e&&\"$~\"!==e&&\"&\"!==e||t++}A=A.nextSibling}return null}function Dc(A){A=A.previousSibling;for(var t=0;A;){if(8===A.nodeType){var e=A.data;if(\"$\"===e||\"$!\"===e||\"$?\"===e||\"$~\"===e||\"&\"===e){if(0===t)return A;t--}else\"/$\"!==e&&\"/&\"!==e||t++}A=A.previousSibling}return null}function Rc(A,t,e){switch(t=dc(e),A){case\"html\":if(!(A=t.documentElement))throw Error(a(452));return A;case\"head\":if(!(A=t.head))throw Error(a(453));return A;case\"body\":if(!(A=t.body))throw Error(a(454));return A;default:throw Error(a(451))}}function Nc(A){for(var t=A.attributes;t.length;)A.removeAttributeNode(t[0]);qA(A)}var Ic=new Map,Fc=new Set;function Oc(A){return\"function\"==typeof A.getRootNode?A.getRootNode():9===A.nodeType?A:A.ownerDocument}var Vc=M.d;M.d={f:function(){var A=Vc.f(),t=Ys();return A||t},r:function(A){var t=XA(A);null!==t&&5===t.tag&&\"form\"===t.type?Zl(t):Vc.r(A)},D:function(A){Vc.D(A),zc(\"dns-prefetch\",A,null)},C:function(A,t){Vc.C(A,t),zc(\"preconnect\",A,t)},L:function(A,t,e){Vc.L(A,t,e);var a=Bc;if(a&&A&&t){var r='link[rel=\"preload\"][as=\"'+ht(t)+'\"]';\"image\"===t&&e&&e.imageSrcSet?(r+='[imagesrcset=\"'+ht(e.imageSrcSet)+'\"]',\"string\"==typeof e.imageSizes&&(r+='[imagesizes=\"'+ht(e.imageSizes)+'\"]')):r+='[href=\"'+ht(A)+'\"]';var n=r;switch(t){case\"style\":n=$c(A);break;case\"script\":n=qc(A)}Ic.has(n)||(A=p({rel:\"preload\",href:\"image\"===t&&e&&e.imageSrcSet?void 0:A,as:t},e),Ic.set(n,A),null!==a.querySelector(r)||\"style\"===t&&a.querySelector(Hc(n))||\"script\"===t&&a.querySelector(Uc(n))||(sc(t=a.createElement(\"link\"),\"link\",A),KA(t),a.head.appendChild(t)))}},m:function(A,t){Vc.m(A,t);var e=Bc;if(e&&A){var a=t&&\"string\"==typeof t.as?t.as:\"script\",r='link[rel=\"modulepreload\"][as=\"'+ht(a)+'\"][href=\"'+ht(A)+'\"]',n=r;switch(a){case\"audioworklet\":case\"paintworklet\":case\"serviceworker\":case\"sharedworker\":case\"worker\":case\"script\":n=qc(A)}if(!Ic.has(n)&&(A=p({rel:\"modulepreload\",href:A},t),Ic.set(n,A),null===e.querySelector(r))){switch(a){case\"audioworklet\":case\"paintworklet\":case\"serviceworker\":case\"sharedworker\":case\"worker\":case\"script\":if(e.querySelector(Uc(n)))return}sc(a=e.createElement(\"link\"),\"link\",A),KA(a),e.head.appendChild(a)}}},X:function(A,t){Vc.X(A,t);var e=Bc;if(e&&A){var a=YA(e).hoistableScripts,r=qc(A),n=a.get(r);n||((n=e.querySelector(Uc(r)))||(A=p({src:A,async:!0},t),(t=Ic.get(r))&&Kc(A,t),KA(n=e.createElement(\"script\")),sc(n,\"link\",A),e.head.appendChild(n)),n={type:\"script\",instance:n,count:1,state:null},a.set(r,n))}},S:function(A,t,e){Vc.S(A,t,e);var a=Bc;if(a&&A){var r=YA(a).hoistableStyles,n=$c(A);t=t||\"default\";var l=r.get(n);if(!l){var i={loading:0,preload:null};if(l=a.querySelector(Hc(n)))i.loading=5;else{A=p({rel:\"stylesheet\",href:A,\"data-precedence\":t},e),(e=Ic.get(n))&&Yc(A,e);var o=l=a.createElement(\"link\");KA(o),sc(o,\"link\",A),o._p=new Promise(function(A,t){o.onload=A,o.onerror=t}),o.addEventListener(\"load\",function(){i.loading|=1}),o.addEventListener(\"error\",function(){i.loading|=2}),i.loading|=4,Jc(l,t,a)}l={type:\"stylesheet\",instance:l,count:1,state:i},r.set(n,l)}}},M:function(A,t){Vc.M(A,t);var e=Bc;if(e&&A){var a=YA(e).hoistableScripts,r=qc(A),n=a.get(r);n||((n=e.querySelector(Uc(r)))||(A=p({src:A,async:!0,type:\"module\"},t),(t=Ic.get(r))&&Kc(A,t),KA(n=e.createElement(\"script\")),sc(n,\"link\",A),e.head.appendChild(n)),n={type:\"script\",instance:n,count:1,state:null},a.set(r,n))}}};var Bc=\"undefined\"==typeof document?null:document;function zc(A,t,e){var a=Bc;if(a&&\"string\"==typeof t&&t){var r=ht(t);r='link[rel=\"'+A+'\"][href=\"'+r+'\"]',\"string\"==typeof e&&(r+='[crossorigin=\"'+e+'\"]'),Fc.has(r)||(Fc.add(r),A={rel:A,crossOrigin:e,href:t},null===a.querySelector(r)&&(sc(t=a.createElement(\"link\"),\"link\",A),KA(t),a.head.appendChild(t)))}}function jc(A,t,e,r){var n,l,i,o,s=(s=$.current)?Oc(s):null;if(!s)throw Error(a(446));switch(A){case\"meta\":case\"title\":return null;case\"style\":return\"string\"==typeof e.precedence&&\"string\"==typeof e.href?(t=$c(e.href),(r=(e=YA(s).hoistableStyles).get(t))||(r={type:\"style\",instance:null,count:0,state:null},e.set(t,r)),r):{type:\"void\",instance:null,count:0,state:null};case\"link\":if(\"stylesheet\"===e.rel&&\"string\"==typeof e.href&&\"string\"==typeof e.precedence){A=$c(e.href);var p=YA(s).hoistableStyles,c=p.get(A);if(c||(s=s.ownerDocument||s,c={type:\"stylesheet\",instance:null,count:0,state:{loading:0,preload:null}},p.set(A,c),(p=s.querySelector(Hc(A)))&&!p._p&&(c.instance=p,c.state.loading=5),Ic.has(A)||(e={rel:\"preload\",as:\"style\",href:e.href,crossOrigin:e.crossOrigin,integrity:e.integrity,media:e.media,hrefLang:e.hrefLang,referrerPolicy:e.referrerPolicy},Ic.set(A,e),p||(n=s,l=A,i=e,o=c.state,n.querySelector('link[rel=\"preload\"][as=\"style\"]['+l+\"]\")?o.loading=1:(l=n.createElement(\"link\"),o.preload=l,l.addEventListener(\"load\",function(){return o.loading|=1}),l.addEventListener(\"error\",function(){return o.loading|=2}),sc(l,\"link\",i),KA(l),n.head.appendChild(l))))),t&&null===r)throw Error(a(528,\"\"));return c}if(t&&null!==r)throw Error(a(529,\"\"));return null;case\"script\":return t=e.async,\"string\"==typeof(e=e.src)&&t&&\"function\"!=typeof t&&\"symbol\"!=typeof t?(t=qc(e),(r=(e=YA(s).hoistableScripts).get(t))||(r={type:\"script\",instance:null,count:0,state:null},e.set(t,r)),r):{type:\"void\",instance:null,count:0,state:null};default:throw Error(a(444,A))}}function $c(A){return'href=\"'+ht(A)+'\"'}function Hc(A){return'link[rel=\"stylesheet\"]['+A+\"]\"}function Gc(A){return p({},A,{\"data-precedence\":A.precedence,precedence:null})}function qc(A){return'[src=\"'+ht(A)+'\"]'}function Uc(A){return\"script[async]\"+A}function Xc(A,t,e){if(t.count++,null===t.instance)switch(t.type){case\"style\":var r=A.querySelector('style[data-href~=\"'+ht(e.href)+'\"]');if(r)return t.instance=r,KA(r),r;var n=p({},e,{\"data-href\":e.href,\"data-precedence\":e.precedence,href:null,precedence:null});return KA(r=(A.ownerDocument||A).createElement(\"style\")),sc(r,\"style\",n),Jc(r,e.precedence,A),t.instance=r;case\"stylesheet\":n=$c(e.href);var l=A.querySelector(Hc(n));if(l)return t.state.loading|=4,t.instance=l,KA(l),l;r=Gc(e),(n=Ic.get(n))&&Yc(r,n),KA(l=(A.ownerDocument||A).createElement(\"link\"));var i=l;return i._p=new Promise(function(A,t){i.onload=A,i.onerror=t}),sc(l,\"link\",r),t.state.loading|=4,Jc(l,e.precedence,A),t.instance=l;case\"script\":return l=qc(e.src),(n=A.querySelector(Uc(l)))?(t.instance=n,KA(n),n):(r=e,(n=Ic.get(l))&&Kc(r=p({},e),n),KA(n=(A=A.ownerDocument||A).createElement(\"script\")),sc(n,\"link\",r),A.head.appendChild(n),t.instance=n);case\"void\":return null;default:throw Error(a(443,t.type))}else\"stylesheet\"===t.type&&!(4&t.state.loading)&&(r=t.instance,t.state.loading|=4,Jc(r,e.precedence,A));return t.instance}function Jc(A,t,e){for(var a=e.querySelectorAll('link[rel=\"stylesheet\"][data-precedence],style[data-precedence]'),r=a.length?a[a.length-1]:null,n=r,l=0;l<a.length;l++){var i=a[l];if(i.dataset.precedence===t)n=i;else if(n!==r)break}n?n.parentNode.insertBefore(A,n.nextSibling):(t=9===e.nodeType?e.head:e).insertBefore(A,t.firstChild)}function Yc(A,t){null==A.crossOrigin&&(A.crossOrigin=t.crossOrigin),null==A.referrerPolicy&&(A.referrerPolicy=t.referrerPolicy),null==A.title&&(A.title=t.title)}function Kc(A,t){null==A.crossOrigin&&(A.crossOrigin=t.crossOrigin),null==A.referrerPolicy&&(A.referrerPolicy=t.referrerPolicy),null==A.integrity&&(A.integrity=t.integrity)}var Qc=null;function Zc(A,t,e){if(null===Qc){var a=new Map,r=Qc=new Map;r.set(e,a)}else(a=(r=Qc).get(e))||(a=new Map,r.set(e,a));if(a.has(A))return a;for(a.set(A,null),e=e.getElementsByTagName(A),r=0;r<e.length;r++){var n=e[r];if(!(n[GA]||n[OA]||\"link\"===A&&\"stylesheet\"===n.getAttribute(\"rel\"))&&\"http://www.w3.org/2000/svg\"!==n.namespaceURI){var l=n.getAttribute(t)||\"\";l=A+l;var i=a.get(l);i?i.push(n):a.set(l,[n])}}return a}function Au(A,t,e){(A=A.ownerDocument||A).head.insertBefore(e,\"title\"===t?A.querySelector(\"head > title\"):null)}function tu(A){return!!(\"stylesheet\"!==A.type||3&A.state.loading)}var eu=0;function au(){if(this.count--,0===this.count&&(0===this.imgCount||!this.waitingForImages))if(this.stylesheets)nu(this,this.stylesheets);else if(this.unsuspend){var A=this.unsuspend;this.unsuspend=null,A()}}var ru=null;function nu(A,t){A.stylesheets=null,null!==A.unsuspend&&(A.count++,ru=new Map,t.forEach(lu,A),ru=null,au.call(A))}function lu(A,t){if(!(4&t.state.loading)){var e=ru.get(A);if(e)var a=e.get(null);else{e=new Map,ru.set(A,e);for(var r=A.querySelectorAll(\"link[data-precedence],style[data-precedence]\"),n=0;n<r.length;n++){var l=r[n];\"LINK\"!==l.nodeName&&\"not all\"===l.getAttribute(\"media\")||(e.set(l.dataset.precedence,l),a=l)}a&&e.set(null,a)}l=(r=t.instance).getAttribute(\"data-precedence\"),(n=e.get(l)||a)===a&&e.set(null,r),e.set(l,r),this.count++,a=au.bind(this),r.addEventListener(\"load\",a),r.addEventListener(\"error\",a),n?n.parentNode.insertBefore(r,n.nextSibling):(A=9===A.nodeType?A.head:A).insertBefore(r,A.firstChild),t.state.loading|=4}}var iu={$$typeof:L,Provider:null,Consumer:null,_currentValue:D,_currentValue2:D,_threadCount:0};function ou(A,t,e,a,r,n,l,i,o){this.tag=1,this.containerInfo=A,this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.next=this.pendingContext=this.context=this.cancelPendingCommit=null,this.callbackPriority=0,this.expirationTimes=CA(-1),this.entangledLanes=this.shellSuspendCounter=this.errorRecoveryDisabledLanes=this.expiredLanes=this.warmLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=CA(0),this.hiddenUpdates=CA(null),this.identifierPrefix=a,this.onUncaughtError=r,this.onCaughtError=n,this.onRecoverableError=l,this.pooledCache=null,this.pooledCacheLanes=0,this.formState=o,this.incompleteTransitions=new Map}function su(A,t,e,a,r,n,l,i,o,s,p,c){return A=new ou(A,t,e,l,o,s,p,c,i),t=1,!0===n&&(t|=24),n=Ra(3,null,null,t),A.current=n,n.stateNode=A,(t=Nr()).refCount++,A.pooledCache=t,t.refCount++,n.memoizedState={element:a,isDehydrated:e,cache:t},dn(n),A}function pu(A){return A?A=Ma:Ma}function cu(A,t,e,a,r,n){r=pu(r),null===a.context?a.context=r:a.pendingContext=r,(a=Sn(t)).payload={element:e},null!==(n=void 0===n?null:n)&&(a.callback=n),null!==(e=fn(A,a,t))&&(Gs(e,0,t),mn(e,A,t))}function uu(A,t){if(null!==(A=A.memoizedState)&&null!==A.dehydrated){var e=A.retryLane;A.retryLane=0!==e&&e<t?e:t}}function du(A,t){uu(A,t),(A=A.alternate)&&uu(A,t)}function hu(A){if(13===A.tag||31===A.tag){var t=Pa(A,67108864);null!==t&&Gs(t,0,67108864),du(A,67108864)}}function Su(A){if(13===A.tag||31===A.tag){var t=$s(),e=Pa(A,t=DA(t));null!==e&&Gs(e,0,t),du(A,t)}}var fu=!0;function mu(A,t,e,a){var r=_.T;_.T=null;var n=M.p;try{M.p=2,Wu(A,t,e,a)}finally{M.p=n,_.T=r}}function Lu(A,t,e,a){var r=_.T;_.T=null;var n=M.p;try{M.p=8,Wu(A,t,e,a)}finally{M.p=n,_.T=r}}function Wu(A,t,e,a){if(fu){var r=gu(a);if(null===r)Qp(A,t,a,yu,e),Mu(A,a);else if(function(A,t,e,a,r){switch(t){case\"focusin\":return Eu=Du(Eu,A,t,e,a,r),!0;case\"dragenter\":return ku=Du(ku,A,t,e,a,r),!0;case\"mouseover\":return wu=Du(wu,A,t,e,a,r),!0;case\"pointerover\":var n=r.pointerId;return Cu.set(n,Du(Cu.get(n)||null,A,t,e,a,r)),!0;case\"gotpointercapture\":return n=r.pointerId,Pu.set(n,Du(Pu.get(n)||null,A,t,e,a,r)),!0}return!1}(r,A,t,e,a))a.stopPropagation();else if(Mu(A,a),4&t&&-1<_u.indexOf(A)){for(;null!==r;){var n=XA(r);if(null!==n)switch(n.tag){case 3:if((n=n.stateNode).current.memoizedState.isDehydrated){var l=bA(n.pendingLanes);if(0!==l){var i=n;for(i.pendingLanes|=2,i.entangledLanes|=2;l;){var o=1<<31-mA(l);i.entanglements[1]|=o,l&=~o}Mp(n),!(6&ps)&&(_s=nA()+500,Dp(0))}}break;case 31:case 13:null!==(i=Pa(n,2))&&Gs(i,0,2),Ys(),du(n,2)}if(null===(n=gu(a))&&Qp(A,t,a,yu,e),n===r)break;r=n}null!==r&&a.stopPropagation()}else Qp(A,t,a,null,e)}}function gu(A){return vu(A=_t(A))}var yu=null;function vu(A){if(yu=null,null!==(A=UA(A))){var t=n(A);if(null===t)A=null;else{var e=t.tag;if(13===e){if(null!==(A=l(t)))return A;A=null}else if(31===e){if(null!==(A=i(t)))return A;A=null}else if(3===e){if(t.stateNode.current.memoizedState.isDehydrated)return 3===t.tag?t.stateNode.containerInfo:null;A=null}else t!==A&&(A=null)}}return yu=A,null}function bu(A){switch(A){case\"beforetoggle\":case\"cancel\":case\"click\":case\"close\":case\"contextmenu\":case\"copy\":case\"cut\":case\"auxclick\":case\"dblclick\":case\"dragend\":case\"dragstart\":case\"drop\":case\"focusin\":case\"focusout\":case\"input\":case\"invalid\":case\"keydown\":case\"keypress\":case\"keyup\":case\"mousedown\":case\"mouseup\":case\"paste\":case\"pause\":case\"play\":case\"pointercancel\":case\"pointerdown\":case\"pointerup\":case\"ratechange\":case\"reset\":case\"resize\":case\"seeked\":case\"submit\":case\"toggle\":case\"touchcancel\":case\"touchend\":case\"touchstart\":case\"volumechange\":case\"change\":case\"selectionchange\":case\"textInput\":case\"compositionstart\":case\"compositionend\":case\"compositionupdate\":case\"beforeblur\":case\"afterblur\":case\"beforeinput\":case\"blur\":case\"fullscreenchange\":case\"focus\":case\"hashchange\":case\"popstate\":case\"select\":case\"selectstart\":return 2;case\"drag\":case\"dragenter\":case\"dragexit\":case\"dragleave\":case\"dragover\":case\"mousemove\":case\"mouseout\":case\"mouseover\":case\"pointermove\":case\"pointerout\":case\"pointerover\":case\"scroll\":case\"touchmove\":case\"wheel\":case\"mouseenter\":case\"mouseleave\":case\"pointerenter\":case\"pointerleave\":return 8;case\"message\":switch(lA()){case iA:return 2;case oA:return 8;case sA:case pA:return 32;case cA:return 268435456;default:return 32}default:return 32}}var xu=!1,Eu=null,ku=null,wu=null,Cu=new Map,Pu=new Map,Tu=[],_u=\"mousedown mouseup touchcancel touchend touchstart auxclick dblclick pointercancel pointerdown pointerup dragend dragstart drop compositionend compositionstart keydown keypress keyup input textInput copy cut paste click change contextmenu reset\".split(\" \");function Mu(A,t){switch(A){case\"focusin\":case\"focusout\":Eu=null;break;case\"dragenter\":case\"dragleave\":ku=null;break;case\"mouseover\":case\"mouseout\":wu=null;break;case\"pointerover\":case\"pointerout\":Cu.delete(t.pointerId);break;case\"gotpointercapture\":case\"lostpointercapture\":Pu.delete(t.pointerId)}}function Du(A,t,e,a,r,n){return null===A||A.nativeEvent!==n?(A={blockedOn:t,domEventName:e,eventSystemFlags:a,nativeEvent:n,targetContainers:[r]},null!==t&&(null!==(t=XA(t))&&hu(t)),A):(A.eventSystemFlags|=a,t=A.targetContainers,null!==r&&-1===t.indexOf(r)&&t.push(r),A)}function Ru(A){var t=UA(A.target);if(null!==t){var e=n(t);if(null!==e)if(13===(t=e.tag)){if(null!==(t=l(e)))return A.blockedOn=t,void IA(A.priority,function(){Su(e)})}else if(31===t){if(null!==(t=i(e)))return A.blockedOn=t,void IA(A.priority,function(){Su(e)})}else if(3===t&&e.stateNode.current.memoizedState.isDehydrated)return void(A.blockedOn=3===e.tag?e.stateNode.containerInfo:null)}A.blockedOn=null}function Nu(A){if(null!==A.blockedOn)return!1;for(var t=A.targetContainers;0<t.length;){var e=gu(A.nativeEvent);if(null!==e)return null!==(t=XA(e))&&hu(t),A.blockedOn=e,!1;var a=new(e=A.nativeEvent).constructor(e.type,e);Tt=a,e.target.dispatchEvent(a),Tt=null,t.shift()}return!0}function Iu(A,t,e){Nu(A)&&e.delete(t)}function Fu(){xu=!1,null!==Eu&&Nu(Eu)&&(Eu=null),null!==ku&&Nu(ku)&&(ku=null),null!==wu&&Nu(wu)&&(wu=null),Cu.forEach(Iu),Pu.forEach(Iu)}function Ou(t,e){t.blockedOn===e&&(t.blockedOn=null,xu||(xu=!0,A.unstable_scheduleCallback(A.unstable_NormalPriority,Fu)))}var Vu=null;function Bu(t){Vu!==t&&(Vu=t,A.unstable_scheduleCallback(A.unstable_NormalPriority,function(){Vu===t&&(Vu=null);for(var A=0;A<t.length;A+=3){var e=t[A],a=t[A+1],r=t[A+2];if(\"function\"!=typeof a){if(null===vu(a||e))continue;break}var n=XA(e);null!==n&&(t.splice(A,3),A-=3,Kl(n,{pending:!0,data:r,method:e.method,action:a},a,r))}}))}function zu(A){function t(t){return Ou(t,A)}null!==Eu&&Ou(Eu,A),null!==ku&&Ou(ku,A),null!==wu&&Ou(wu,A),Cu.forEach(t),Pu.forEach(t);for(var e=0;e<Tu.length;e++){var a=Tu[e];a.blockedOn===A&&(a.blockedOn=null)}for(;0<Tu.length&&null===(e=Tu[0]).blockedOn;)Ru(e),null===e.blockedOn&&Tu.shift();if(null!=(e=(A.ownerDocument||A).$$reactFormReplay))for(a=0;a<e.length;a+=3){var r=e[a],n=e[a+1],l=r[VA]||null;if(\"function\"==typeof n)l||Bu(e);else if(l){var i=null;if(n&&n.hasAttribute(\"formAction\")){if(r=n,l=n[VA]||null)i=l.formAction;else if(null!==vu(r))continue}else i=l.action;\"function\"==typeof i?e[a+1]=i:(e.splice(a,3),a-=3),Bu(e)}}}function ju(){function A(A){A.canIntercept&&\"react-transition\"===A.info&&A.intercept({handler:function(){return new Promise(function(A){return r=A})},focusReset:\"manual\",scroll:\"manual\"})}function t(){null!==r&&(r(),r=null),a||setTimeout(e,20)}function e(){if(!a&&!navigation.transition){var A=navigation.currentEntry;A&&null!=A.url&&navigation.navigate(A.url,{state:A.getState(),info:\"react-transition\",history:\"replace\"})}}if(\"object\"==typeof navigation){var a=!1,r=null;return navigation.addEventListener(\"navigate\",A),navigation.addEventListener(\"navigatesuccess\",t),navigation.addEventListener(\"navigateerror\",t),setTimeout(e,100),function(){a=!0,navigation.removeEventListener(\"navigate\",A),navigation.removeEventListener(\"navigatesuccess\",t),navigation.removeEventListener(\"navigateerror\",t),null!==r&&(r(),r=null)}}}function $u(A){this._internalRoot=A}function Hu(A){this._internalRoot=A}Hu.prototype.render=$u.prototype.render=function(A){var t=this._internalRoot;if(null===t)throw Error(a(409));cu(t.current,$s(),A,t,null,null)},Hu.prototype.unmount=$u.prototype.unmount=function(){var A=this._internalRoot;if(null!==A){this._internalRoot=null;var t=A.containerInfo;cu(A.current,2,null,A,null,null),Ys(),t[BA]=null}},Hu.prototype.unstable_scheduleHydration=function(A){if(A){var t=NA();A={blockedOn:null,target:A,priority:t};for(var e=0;e<Tu.length&&0!==t&&t<Tu[e].priority;e++);Tu.splice(e,0,A),0===e&&Ru(A)}};var Gu=t.version;if(\"19.2.3\"!==Gu)throw Error(a(527,Gu,\"19.2.3\"));M.findDOMNode=function(A){var t=A._reactInternals;if(void 0===t){if(\"function\"==typeof A.render)throw Error(a(188));throw A=Object.keys(A).join(\",\"),Error(a(268,A))}return A=function(A){var t=A.alternate;if(!t){if(null===(t=n(A)))throw Error(a(188));return t!==A?null:A}for(var e=A,r=t;;){var l=e.return;if(null===l)break;var i=l.alternate;if(null===i){if(null!==(r=l.return)){e=r;continue}break}if(l.child===i.child){for(i=l.child;i;){if(i===e)return o(l),A;if(i===r)return o(l),t;i=i.sibling}throw Error(a(188))}if(e.return!==r.return)e=l,r=i;else{for(var s=!1,p=l.child;p;){if(p===e){s=!0,e=l,r=i;break}if(p===r){s=!0,r=l,e=i;break}p=p.sibling}if(!s){for(p=i.child;p;){if(p===e){s=!0,e=i,r=l;break}if(p===r){s=!0,r=i,e=l;break}p=p.sibling}if(!s)throw Error(a(189))}}if(e.alternate!==r)throw Error(a(190))}if(3!==e.tag)throw Error(a(188));return e.stateNode.current===e?A:t}(t),A=null===(A=null!==A?s(A):null)?null:A.stateNode};var qu={bundleType:0,version:\"19.2.3\",rendererPackageName:\"react-dom\",currentDispatcherRef:_,reconcilerVersion:\"19.2.3\"};if(\"undefined\"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__){var Uu=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(!Uu.isDisabled&&Uu.supportsFiber)try{hA=Uu.inject(qu),SA=Uu}catch(err){}}return reactDomClient_production.createRoot=function(A,t){if(!r(A))throw Error(a(299));var e=!1,n=\"\",l=gi,i=yi,o=vi;return null!=t&&(!0===t.unstable_strictMode&&(e=!0),void 0!==t.identifierPrefix&&(n=t.identifierPrefix),void 0!==t.onUncaughtError&&(l=t.onUncaughtError),void 0!==t.onCaughtError&&(i=t.onCaughtError),void 0!==t.onRecoverableError&&(o=t.onRecoverableError)),t=su(A,1,!1,null,0,e,n,null,l,i,o,ju),A[BA]=t.current,Yp(A),new $u(t)},reactDomClient_production.hydrateRoot=function(A,t,e){if(!r(A))throw Error(a(299));var n=!1,l=\"\",i=gi,o=yi,s=vi,p=null;return null!=e&&(!0===e.unstable_strictMode&&(n=!0),void 0!==e.identifierPrefix&&(l=e.identifierPrefix),void 0!==e.onUncaughtError&&(i=e.onUncaughtError),void 0!==e.onCaughtError&&(o=e.onCaughtError),void 0!==e.onRecoverableError&&(s=e.onRecoverableError),void 0!==e.formState&&(p=e.formState)),(t=su(A,1,!0,t,0,n,l,p,i,o,s,ju)).context=pu(null),e=t.current,(l=Sn(n=DA(n=$s()))).callback=null,fn(e,l,n),e=n,t.current.lanes=e,PA(t,e),Mp(t),A[BA]=t.current,Yp(A),new Hu(t)},reactDomClient_production.version=\"19.2.3\",reactDomClient_production}function requireClient(){if(hasRequiredClient)return client.exports;return hasRequiredClient=1,function A(){if(\"undefined\"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&\"function\"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE)try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(A)}catch(err){}}(),client.exports=requireReactDomClient_production(),client.exports}var clientExports=requireClient();const ReactDOM=getDefaultExportFromCjs(clientExports);function addCacheBuster(A){const t=A.includes(\"?\")?\"&\":\"?\";return`${A}${t}_=${Date.now()}_${Math.random().toString(36).substring(2,15)}&lang=${getBrowserLang()}`}const icons={success:jsxRuntimeExports.jsx(CircleCheck,{className:\"size-5 text-primary\"}),error:jsxRuntimeExports.jsx(CircleX,{className:\"size-5 text-destructive\"}),warning:jsxRuntimeExports.jsx(TriangleAlert,{className:\"size-5 text-destructive/70\"}),info:jsxRuntimeExports.jsx(Info,{className:\"size-5 text-accent-foreground\"}),loading:jsxRuntimeExports.jsx(LoaderCircle,{className:\"size-5 text-muted-foreground animate-spin\"})},createAccessibleMessage=A=>jsxRuntimeExports.jsx(\"span\",{role:\"alert\",\"aria-live\":\"assertive\",children:A}),toast={success:(A,t)=>{toast$1(createAccessibleMessage(A),{icon:icons.success,...t})},error:(A,t)=>{toast$1(createAccessibleMessage(A),{icon:icons.error,...t})},warning:(A,t)=>{toast$1(createAccessibleMessage(A),{icon:icons.warning,...t})},info:(A,t)=>{toast$1(createAccessibleMessage(A),{icon:icons.info,...t})},loading:(A,t)=>toast$1(createAccessibleMessage(A),{icon:icons.loading,duration:1/0,...t}),dismiss:toast$1.dismiss};function buildApiHeaders({token:A,includeContentType:t=!0,includeDomain:e=!0,includeLang:a=!0,extraHeaders:r={}}={}){const n={\"X-Client\":\"WebGui\",\"X-Client-Name\":encodeURIComponent(\"Web\"),\"X-Client-Version\":\"1.0.0\"};return t&&(n[\"Content-Type\"]=\"application/json\"),A&&(n.Authorization=`Bearer ${A}`),e&&(n.Domain=window.location.origin),a&&(n.Lang=getBrowserLang()),{...n,...r}}let API_URL=localStorage.getItem(\"API_URL\");const env={API_URL:API_URL||window.location.origin,debug:!0};\n/*! *****************************************************************************\nCopyright (c) Microsoft Corporation.\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n***************************************************************************** */var extendStatics=function(A,t){return(extendStatics=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(A,t){A.__proto__=t}||function(A,t){for(var e in t)Object.prototype.hasOwnProperty.call(t,e)&&(A[e]=t[e])})(A,t)};function __extends(A,t){if(\"function\"!=typeof t&&null!==t)throw new TypeError(\"Class extends value \"+String(t)+\" is not a constructor or null\");function e(){this.constructor=A}extendStatics(A,t),A.prototype=null===t?Object.create(t):(e.prototype=t.prototype,new e)}var __assign=function(){return(__assign=Object.assign||function(A){for(var t,e=1,a=arguments.length;e<a;e++)for(var r in t=arguments[e])Object.prototype.hasOwnProperty.call(t,r)&&(A[r]=t[r]);return A}).apply(this,arguments)};function __awaiter(A,t,e,a){return new(e||(e=Promise))(function(t,r){function n(A){try{i(a.next(A))}catch(t){r(t)}}function l(A){try{i(a.throw(A))}catch(t){r(t)}}function i(A){var a;A.done?t(A.value):(a=A.value,a instanceof e?a:new e(function(A){A(a)})).then(n,l)}i((a=a.apply(A,[])).next())})}function __generator(A,t){var e,a,r,n,l={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return n={next:i(0),throw:i(1),return:i(2)},\"function\"==typeof Symbol&&(n[Symbol.iterator]=function(){return this}),n;function i(n){return function(i){return function(n){if(e)throw new TypeError(\"Generator is already executing.\");for(;l;)try{if(e=1,a&&(r=2&n[0]?a.return:n[0]?a.throw||((r=a.return)&&r.call(a),0):a.next)&&!(r=r.call(a,n[1])).done)return r;switch(a=0,r&&(n=[2&n[0],r.value]),n[0]){case 0:case 1:r=n;break;case 4:return l.label++,{value:n[1],done:!1};case 5:l.label++,a=n[1],n=[0];continue;case 7:n=l.ops.pop(),l.trys.pop();continue;default:if(!((r=(r=l.trys).length>0&&r[r.length-1])||6!==n[0]&&2!==n[0])){l=0;continue}if(3===n[0]&&(!r||n[1]>r[0]&&n[1]<r[3])){l.label=n[1];break}if(6===n[0]&&l.label<r[1]){l.label=r[1],r=n;break}if(r&&l.label<r[2]){l.label=r[2],l.ops.push(n);break}r[2]&&l.ops.pop(),l.trys.pop();continue}n=t.call(A,l)}catch(i){n=[6,i],a=0}finally{e=r=0}if(5&n[0])throw n[1];return{value:n[0]?n[1]:void 0,done:!0}}([n,i])}}}function createCommonjsModule(A,t){return A(t={exports:{}},t.exports),t.exports}var lottie=createCommonjsModule(function(module,exports$1){var e;\"undefined\"!=typeof navigator&&(e=function(){var svgNS=\"http://www.w3.org/2000/svg\",locationHref=\"\",_useWebWorker=!1,initialDefaultFrame=-999999,setWebWorker=function(A){_useWebWorker=!!A},getWebWorker=function(){return _useWebWorker},setLocationHref=function(A){locationHref=A},getLocationHref=function(){return locationHref};function createTag(A){return document.createElement(A)}function extendPrototype(A,t){var e,a,r=A.length;for(e=0;e<r;e+=1)for(var n in a=A[e].prototype)Object.prototype.hasOwnProperty.call(a,n)&&(t.prototype[n]=a[n])}function getDescriptor(A,t){return Object.getOwnPropertyDescriptor(A,t)}function createProxyFunction(A){function t(){}return t.prototype=A,t}var audioControllerFactory=function(){function A(A){this.audios=[],this.audioFactory=A,this._volume=1,this._isMuted=!1}return A.prototype={addAudio:function(A){this.audios.push(A)},pause:function(){var A,t=this.audios.length;for(A=0;A<t;A+=1)this.audios[A].pause()},resume:function(){var A,t=this.audios.length;for(A=0;A<t;A+=1)this.audios[A].resume()},setRate:function(A){var t,e=this.audios.length;for(t=0;t<e;t+=1)this.audios[t].setRate(A)},createAudio:function(A){return this.audioFactory?this.audioFactory(A):window.Howl?new window.Howl({src:[A]}):{isPlaying:!1,play:function(){this.isPlaying=!0},seek:function(){this.isPlaying=!1},playing:function(){},rate:function(){},setVolume:function(){}}},setAudioFactory:function(A){this.audioFactory=A},setVolume:function(A){this._volume=A,this._updateVolume()},mute:function(){this._isMuted=!0,this._updateVolume()},unmute:function(){this._isMuted=!1,this._updateVolume()},getVolume:function(){return this._volume},_updateVolume:function(){var A,t=this.audios.length;for(A=0;A<t;A+=1)this.audios[A].volume(this._volume*(this._isMuted?0:1))}},function(){return new A}}(),createTypedArray=function(){function A(A,t){var e,a=0,r=[];switch(A){case\"int16\":case\"uint8c\":e=1;break;default:e=1.1}for(a=0;a<t;a+=1)r.push(e);return r}return\"function\"==typeof Uint8ClampedArray&&\"function\"==typeof Float32Array?function(t,e){return\"float32\"===t?new Float32Array(e):\"int16\"===t?new Int16Array(e):\"uint8c\"===t?new Uint8ClampedArray(e):A(t,e)}:A}();function createSizedArray(A){return Array.apply(null,{length:A})}function _typeof$6(A){return(_typeof$6=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(A){return typeof A}:function(A){return A&&\"function\"==typeof Symbol&&A.constructor===Symbol&&A!==Symbol.prototype?\"symbol\":typeof A})(A)}var subframeEnabled=!0,expressionsPlugin=null,expressionsInterfaces=null,idPrefix$1=\"\",isSafari=/^((?!chrome|android).)*safari/i.test(navigator.userAgent),bmPow=Math.pow,bmSqrt=Math.sqrt,bmFloor=Math.floor,bmMax=Math.max,bmMin=Math.min,BMMath={};!function(){var A,t=[\"abs\",\"acos\",\"acosh\",\"asin\",\"asinh\",\"atan\",\"atanh\",\"atan2\",\"ceil\",\"cbrt\",\"expm1\",\"clz32\",\"cos\",\"cosh\",\"exp\",\"floor\",\"fround\",\"hypot\",\"imul\",\"log\",\"log1p\",\"log2\",\"log10\",\"max\",\"min\",\"pow\",\"random\",\"round\",\"sign\",\"sin\",\"sinh\",\"sqrt\",\"tan\",\"tanh\",\"trunc\",\"E\",\"LN10\",\"LN2\",\"LOG10E\",\"LOG2E\",\"PI\",\"SQRT1_2\",\"SQRT2\"],e=t.length;for(A=0;A<e;A+=1)BMMath[t[A]]=Math[t[A]]}(),BMMath.random=Math.random,BMMath.abs=function(A){if(\"object\"===_typeof$6(A)&&A.length){var t,e=createSizedArray(A.length),a=A.length;for(t=0;t<a;t+=1)e[t]=Math.abs(A[t]);return e}return Math.abs(A)};var defaultCurveSegments=150,degToRads=Math.PI/180,roundCorner=.5519;function styleDiv(A){A.style.position=\"absolute\",A.style.top=0,A.style.left=0,A.style.display=\"block\",A.style.transformOrigin=\"0 0\",A.style.webkitTransformOrigin=\"0 0\",A.style.backfaceVisibility=\"visible\",A.style.webkitBackfaceVisibility=\"visible\",A.style.transformStyle=\"preserve-3d\",A.style.webkitTransformStyle=\"preserve-3d\",A.style.mozTransformStyle=\"preserve-3d\"}function BMEnterFrameEvent(A,t,e,a){this.type=A,this.currentTime=t,this.totalTime=e,this.direction=a<0?-1:1}function BMCompleteEvent(A,t){this.type=A,this.direction=t<0?-1:1}function BMCompleteLoopEvent(A,t,e,a){this.type=A,this.currentLoop=e,this.totalLoops=t,this.direction=a<0?-1:1}function BMSegmentStartEvent(A,t,e){this.type=A,this.firstFrame=t,this.totalFrames=e}function BMDestroyEvent(A,t){this.type=A,this.target=t}function BMRenderFrameErrorEvent(A,t){this.type=\"renderFrameError\",this.nativeError=A,this.currentTime=t}function BMConfigErrorEvent(A){this.type=\"configError\",this.nativeError=A}var createElementID=(_count=0,function(){return idPrefix$1+\"__lottie_element_\"+(_count+=1)}),_count;function HSVtoRGB(A,t,e){var a,r,n,l,i,o,s,p;switch(o=e*(1-t),s=e*(1-(i=6*A-(l=Math.floor(6*A)))*t),p=e*(1-(1-i)*t),l%6){case 0:a=e,r=p,n=o;break;case 1:a=s,r=e,n=o;break;case 2:a=o,r=e,n=p;break;case 3:a=o,r=s,n=e;break;case 4:a=p,r=o,n=e;break;case 5:a=e,r=o,n=s}return[a,r,n]}function RGBtoHSV(A,t,e){var a,r=Math.max(A,t,e),n=Math.min(A,t,e),l=r-n,i=0===r?0:l/r,o=r/255;switch(r){case n:a=0;break;case A:a=t-e+l*(t<e?6:0),a/=6*l;break;case t:a=e-A+2*l,a/=6*l;break;case e:a=A-t+4*l,a/=6*l}return[a,i,o]}function addSaturationToRGB(A,t){var e=RGBtoHSV(255*A[0],255*A[1],255*A[2]);return e[1]+=t,e[1]>1?e[1]=1:e[1]<=0&&(e[1]=0),HSVtoRGB(e[0],e[1],e[2])}function addBrightnessToRGB(A,t){var e=RGBtoHSV(255*A[0],255*A[1],255*A[2]);return e[2]+=t,e[2]>1?e[2]=1:e[2]<0&&(e[2]=0),HSVtoRGB(e[0],e[1],e[2])}function addHueToRGB(A,t){var e=RGBtoHSV(255*A[0],255*A[1],255*A[2]);return e[0]+=t/360,e[0]>1?e[0]-=1:e[0]<0&&(e[0]+=1),HSVtoRGB(e[0],e[1],e[2])}var rgbToHex=function(){var A,t,e=[];for(A=0;A<256;A+=1)t=A.toString(16),e[A]=1===t.length?\"0\"+t:t;return function(A,t,a){return A<0&&(A=0),t<0&&(t=0),a<0&&(a=0),\"#\"+e[A]+e[t]+e[a]}}(),setSubframeEnabled=function(A){subframeEnabled=!!A},getSubframeEnabled=function(){return subframeEnabled},setExpressionsPlugin=function(A){expressionsPlugin=A},getExpressionsPlugin=function(){return expressionsPlugin},setExpressionInterfaces=function(A){expressionsInterfaces=A},getExpressionInterfaces=function(){return expressionsInterfaces},setDefaultCurveSegments=function(A){defaultCurveSegments=A},getDefaultCurveSegments=function(){return defaultCurveSegments},setIdPrefix=function(A){idPrefix$1=A};function createNS(A){return document.createElementNS(svgNS,A)}function _typeof$5(A){return(_typeof$5=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(A){return typeof A}:function(A){return A&&\"function\"==typeof Symbol&&A.constructor===Symbol&&A!==Symbol.prototype?\"symbol\":typeof A})(A)}var dataManager=function(){var A,t,e=1,a=[],r={onmessage:function(){},postMessage:function(t){A({data:t})}},n={postMessage:function(A){r.onmessage({data:A})}};function l(){t||((t=function(t){if(window.Worker&&window.Blob&&getWebWorker()){var e=new Blob([\"var _workerSelf = self; self.onmessage = \",t.toString()],{type:\"text/javascript\"}),a=URL.createObjectURL(e);return new Worker(a)}return A=t,r}(function(A){if(n.dataManager||(n.dataManager=function(){function A(r,n){var l,i,o,s,p,u,d=r.length;for(i=0;i<d;i+=1)if(\"ks\"in(l=r[i])&&!l.completed){if(l.completed=!0,l.hasMask){var h=l.masksProperties;for(s=h.length,o=0;o<s;o+=1)if(h[o].pt.k.i)a(h[o].pt.k);else for(u=h[o].pt.k.length,p=0;p<u;p+=1)h[o].pt.k[p].s&&a(h[o].pt.k[p].s[0]),h[o].pt.k[p].e&&a(h[o].pt.k[p].e[0])}0===l.ty?(l.layers=t(l.refId,n),A(l.layers,n)):4===l.ty?e(l.shapes):5===l.ty&&c(l)}}function t(A,t){var e=function(A,t){for(var e=0,a=t.length;e<a;){if(t[e].id===A)return t[e];e+=1}return null}(A,t);return e?e.layers.__used?JSON.parse(JSON.stringify(e.layers)):(e.layers.__used=!0,e.layers):null}function e(A){var t,r,n;for(t=A.length-1;t>=0;t-=1)if(\"sh\"===A[t].ty)if(A[t].ks.k.i)a(A[t].ks.k);else for(n=A[t].ks.k.length,r=0;r<n;r+=1)A[t].ks.k[r].s&&a(A[t].ks.k[r].s[0]),A[t].ks.k[r].e&&a(A[t].ks.k[r].e[0]);else\"gr\"===A[t].ty&&e(A[t].it)}function a(A){var t,e=A.i.length;for(t=0;t<e;t+=1)A.i[t][0]+=A.v[t][0],A.i[t][1]+=A.v[t][1],A.o[t][0]+=A.v[t][0],A.o[t][1]+=A.v[t][1]}function r(A,t){var e=t?t.split(\".\"):[100,100,100];return A[0]>e[0]||!(e[0]>A[0])&&(A[1]>e[1]||!(e[1]>A[1])&&(A[2]>e[2]||!(e[2]>A[2])&&null))}var n,l=function(){var A=[4,4,14];function t(A){var t,e,a,r=A.length;for(t=0;t<r;t+=1)5===A[t].ty&&(a=(e=A[t]).t.d,e.t.d={k:[{s:a,t:0}]})}return function(e){if(r(A,e.v)&&(t(e.layers),e.assets)){var a,n=e.assets.length;for(a=0;a<n;a+=1)e.assets[a].layers&&t(e.assets[a].layers)}}}(),i=(n=[4,7,99],function(A){if(A.chars&&!r(n,A.v)){var t,a=A.chars.length;for(t=0;t<a;t+=1){var l=A.chars[t];l.data&&l.data.shapes&&(e(l.data.shapes),l.data.ip=0,l.data.op=99999,l.data.st=0,l.data.sr=1,l.data.ks={p:{k:[0,0],a:0},s:{k:[100,100],a:0},a:{k:[0,0],a:0},r:{k:0,a:0},o:{k:100,a:0}},A.chars[t].t||(l.data.shapes.push({ty:\"no\"}),l.data.shapes[0].it.push({p:{k:[0,0],a:0},s:{k:[100,100],a:0},a:{k:[0,0],a:0},r:{k:0,a:0},o:{k:100,a:0},sk:{k:0,a:0},sa:{k:0,a:0},ty:\"tr\"})))}}}),o=function(){var A=[5,7,15];function t(A){var t,e,a=A.length;for(t=0;t<a;t+=1)5===A[t].ty&&(e=void 0,\"number\"==typeof(e=A[t].t.p).a&&(e.a={a:0,k:e.a}),\"number\"==typeof e.p&&(e.p={a:0,k:e.p}),\"number\"==typeof e.r&&(e.r={a:0,k:e.r}))}return function(e){if(r(A,e.v)&&(t(e.layers),e.assets)){var a,n=e.assets.length;for(a=0;a<n;a+=1)e.assets[a].layers&&t(e.assets[a].layers)}}}(),s=function(){var A=[4,1,9];function t(A){var e,a,r,n=A.length;for(e=0;e<n;e+=1)if(\"gr\"===A[e].ty)t(A[e].it);else if(\"fl\"===A[e].ty||\"st\"===A[e].ty)if(A[e].c.k&&A[e].c.k[0].i)for(r=A[e].c.k.length,a=0;a<r;a+=1)A[e].c.k[a].s&&(A[e].c.k[a].s[0]/=255,A[e].c.k[a].s[1]/=255,A[e].c.k[a].s[2]/=255,A[e].c.k[a].s[3]/=255),A[e].c.k[a].e&&(A[e].c.k[a].e[0]/=255,A[e].c.k[a].e[1]/=255,A[e].c.k[a].e[2]/=255,A[e].c.k[a].e[3]/=255);else A[e].c.k[0]/=255,A[e].c.k[1]/=255,A[e].c.k[2]/=255,A[e].c.k[3]/=255}function e(A){var e,a=A.length;for(e=0;e<a;e+=1)4===A[e].ty&&t(A[e].shapes)}return function(t){if(r(A,t.v)&&(e(t.layers),t.assets)){var a,n=t.assets.length;for(a=0;a<n;a+=1)t.assets[a].layers&&e(t.assets[a].layers)}}}(),p=function(){var A=[4,4,18];function t(A){var e,a,r;for(e=A.length-1;e>=0;e-=1)if(\"sh\"===A[e].ty)if(A[e].ks.k.i)A[e].ks.k.c=A[e].closed;else for(r=A[e].ks.k.length,a=0;a<r;a+=1)A[e].ks.k[a].s&&(A[e].ks.k[a].s[0].c=A[e].closed),A[e].ks.k[a].e&&(A[e].ks.k[a].e[0].c=A[e].closed);else\"gr\"===A[e].ty&&t(A[e].it)}function e(A){var e,a,r,n,l,i,o=A.length;for(a=0;a<o;a+=1){if((e=A[a]).hasMask){var s=e.masksProperties;for(n=s.length,r=0;r<n;r+=1)if(s[r].pt.k.i)s[r].pt.k.c=s[r].cl;else for(i=s[r].pt.k.length,l=0;l<i;l+=1)s[r].pt.k[l].s&&(s[r].pt.k[l].s[0].c=s[r].cl),s[r].pt.k[l].e&&(s[r].pt.k[l].e[0].c=s[r].cl)}4===e.ty&&t(e.shapes)}}return function(t){if(r(A,t.v)&&(e(t.layers),t.assets)){var a,n=t.assets.length;for(a=0;a<n;a+=1)t.assets[a].layers&&e(t.assets[a].layers)}}}();function c(A){0===A.t.a.length&&A.t.p}var u={completeData:function(e){e.__complete||(s(e),l(e),i(e),o(e),p(e),A(e.layers,e.assets),function(e,a){if(e){var r=0,n=e.length;for(r=0;r<n;r+=1)1===e[r].t&&(e[r].data.layers=t(e[r].data.refId,a),A(e[r].data.layers,a))}}(e.chars,e.assets),e.__complete=!0)}};return u.checkColors=s,u.checkChars=i,u.checkPathProperties=o,u.checkShapes=p,u.completeLayers=A,u}()),n.assetLoader||(n.assetLoader=function(){function A(A){var t=A.getResponseHeader(\"content-type\");return t&&\"json\"===A.responseType&&-1!==t.indexOf(\"json\")||A.response&&\"object\"===_typeof$5(A.response)?A.response:A.response&&\"string\"==typeof A.response?JSON.parse(A.response):A.responseText?JSON.parse(A.responseText):null}return{load:function(t,e,a,r){var n,l=new XMLHttpRequest;try{l.responseType=\"json\"}catch(i){}l.onreadystatechange=function(){if(4===l.readyState)if(200===l.status)n=A(l),a(n);else try{n=A(l),a(n)}catch(i){r&&r(i)}};try{l.open([\"G\",\"E\",\"T\"].join(\"\"),t,!0)}catch(i){l.open([\"G\",\"E\",\"T\"].join(\"\"),e+\"/\"+t,!0)}l.send()}}}()),\"loadAnimation\"===A.data.type)n.assetLoader.load(A.data.path,A.data.fullPath,function(t){n.dataManager.completeData(t),n.postMessage({id:A.data.id,payload:t,status:\"success\"})},function(){n.postMessage({id:A.data.id,status:\"error\"})});else if(\"complete\"===A.data.type){var t=A.data.animation;n.dataManager.completeData(t),n.postMessage({id:A.data.id,payload:t,status:\"success\"})}else\"loadData\"===A.data.type&&n.assetLoader.load(A.data.path,A.data.fullPath,function(t){n.postMessage({id:A.data.id,payload:t,status:\"success\"})},function(){n.postMessage({id:A.data.id,status:\"error\"})})})).onmessage=function(A){var t=A.data,e=t.id,r=a[e];a[e]=null,\"success\"===t.status?r.onComplete(t.payload):r.onError&&r.onError()})}function i(A,t){var r=\"processId_\"+(e+=1);return a[r]={onComplete:A,onError:t},r}return{loadAnimation:function(A,e,a){l();var r=i(e,a);t.postMessage({type:\"loadAnimation\",path:A,fullPath:window.location.origin+window.location.pathname,id:r})},loadData:function(A,e,a){l();var r=i(e,a);t.postMessage({type:\"loadData\",path:A,fullPath:window.location.origin+window.location.pathname,id:r})},completeAnimation:function(A,e,a){l();var r=i(e,a);t.postMessage({type:\"complete\",animation:A,id:r})}}}(),ImagePreloader=function(){var A=function(){var A=createTag(\"canvas\");A.width=1,A.height=1;var t=A.getContext(\"2d\");return t.fillStyle=\"rgba(0,0,0,0)\",t.fillRect(0,0,1,1),A}();function t(){this.loadedAssets+=1,this.loadedAssets===this.totalImages&&this.loadedFootagesCount===this.totalFootages&&this.imagesLoadedCb&&this.imagesLoadedCb(null)}function e(){this.loadedFootagesCount+=1,this.loadedAssets===this.totalImages&&this.loadedFootagesCount===this.totalFootages&&this.imagesLoadedCb&&this.imagesLoadedCb(null)}function a(A,t,e){var a=\"\";if(A.e)a=A.p;else if(t){var r=A.p;-1!==r.indexOf(\"images/\")&&(r=r.split(\"/\")[1]),a=t+r}else a=e,a+=A.u?A.u:\"\",a+=A.p;return a}function r(A){var t=0,e=setInterval(function(){(A.getBBox().width||t>500)&&(this._imageLoaded(),clearInterval(e)),t+=1}.bind(this),50)}function n(A){var t={assetData:A},e=a(A,this.assetsPath,this.path);return dataManager.loadData(e,function(A){t.img=A,this._footageLoaded()}.bind(this),function(){t.img={},this._footageLoaded()}.bind(this)),t}function l(){this._imageLoaded=t.bind(this),this._footageLoaded=e.bind(this),this.testImageLoaded=r.bind(this),this.createFootageData=n.bind(this),this.assetsPath=\"\",this.path=\"\",this.totalImages=0,this.totalFootages=0,this.loadedAssets=0,this.loadedFootagesCount=0,this.imagesLoadedCb=null,this.images=[]}return l.prototype={loadAssets:function(A,t){var e;this.imagesLoadedCb=t;var a=A.length;for(e=0;e<a;e+=1)A[e].layers||(A[e].t&&\"seq\"!==A[e].t?3===A[e].t&&(this.totalFootages+=1,this.images.push(this.createFootageData(A[e]))):(this.totalImages+=1,this.images.push(this._createImageData(A[e]))))},setAssetsPath:function(A){this.assetsPath=A||\"\"},setPath:function(A){this.path=A||\"\"},loadedImages:function(){return this.totalImages===this.loadedAssets},loadedFootages:function(){return this.totalFootages===this.loadedFootagesCount},destroy:function(){this.imagesLoadedCb=null,this.images.length=0},getAsset:function(A){for(var t=0,e=this.images.length;t<e;){if(this.images[t].assetData===A)return this.images[t].img;t+=1}return null},createImgData:function(t){var e=a(t,this.assetsPath,this.path),r=createTag(\"img\");r.crossOrigin=\"anonymous\",r.addEventListener(\"load\",this._imageLoaded,!1),r.addEventListener(\"error\",function(){n.img=A,this._imageLoaded()}.bind(this),!1),r.src=e;var n={img:r,assetData:t};return n},createImageData:function(t){var e=a(t,this.assetsPath,this.path),r=createNS(\"image\");isSafari?this.testImageLoaded(r):r.addEventListener(\"load\",this._imageLoaded,!1),r.addEventListener(\"error\",function(){n.img=A,this._imageLoaded()}.bind(this),!1),r.setAttributeNS(\"http://www.w3.org/1999/xlink\",\"href\",e),this._elementHelper.append?this._elementHelper.append(r):this._elementHelper.appendChild(r);var n={img:r,assetData:t};return n},imageLoaded:t,footageLoaded:e,setCacheType:function(A,t){\"svg\"===A?(this._elementHelper=t,this._createImageData=this.createImageData.bind(this)):this._createImageData=this.createImgData.bind(this)}},l}();function BaseEvent(){}BaseEvent.prototype={triggerEvent:function(A,t){if(this._cbs[A])for(var e=this._cbs[A],a=0;a<e.length;a+=1)e[a](t)},addEventListener:function(A,t){return this._cbs[A]||(this._cbs[A]=[]),this._cbs[A].push(t),function(){this.removeEventListener(A,t)}.bind(this)},removeEventListener:function(A,t){if(t){if(this._cbs[A]){for(var e=0,a=this._cbs[A].length;e<a;)this._cbs[A][e]===t&&(this._cbs[A].splice(e,1),e-=1,a-=1),e+=1;this._cbs[A].length||(this._cbs[A]=null)}}else this._cbs[A]=null}};var markerParser=function(){function A(A){for(var t,e=A.split(\"\\r\\n\"),a={},r=0,n=0;n<e.length;n+=1)2===(t=e[n].split(\":\")).length&&(a[t[0]]=t[1].trim(),r+=1);if(0===r)throw new Error;return a}return function(t){for(var e=[],a=0;a<t.length;a+=1){var r=t[a],n={time:r.tm,duration:r.dr};try{n.payload=JSON.parse(t[a].cm)}catch(l){try{n.payload=A(t[a].cm)}catch(i){n.payload={name:t[a].cm}}}e.push(n)}return e}}(),ProjectInterface=function(){function A(A){this.compositions.push(A)}return function(){function t(A){for(var t=0,e=this.compositions.length;t<e;){if(this.compositions[t].data&&this.compositions[t].data.nm===A)return this.compositions[t].prepareFrame&&this.compositions[t].data.xt&&this.compositions[t].prepareFrame(this.currentFrame),this.compositions[t].compInterface;t+=1}return null}return t.compositions=[],t.currentFrame=0,t.registerComposition=A,t}}(),renderers={},registerRenderer=function(A,t){renderers[A]=t};function getRenderer(A){return renderers[A]}function getRegisteredRenderer(){if(renderers.canvas)return\"canvas\";for(var A in renderers)if(renderers[A])return A;return\"\"}function _typeof$4(A){return(_typeof$4=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(A){return typeof A}:function(A){return A&&\"function\"==typeof Symbol&&A.constructor===Symbol&&A!==Symbol.prototype?\"symbol\":typeof A})(A)}var AnimationItem=function(){this._cbs=[],this.name=\"\",this.path=\"\",this.isLoaded=!1,this.currentFrame=0,this.currentRawFrame=0,this.firstFrame=0,this.totalFrames=0,this.frameRate=0,this.frameMult=0,this.playSpeed=1,this.playDirection=1,this.playCount=0,this.animationData={},this.assets=[],this.isPaused=!0,this.autoplay=!1,this.loop=!0,this.renderer=null,this.animationID=createElementID(),this.assetsPath=\"\",this.timeCompleted=0,this.segmentPos=0,this.isSubframeEnabled=getSubframeEnabled(),this.segments=[],this._idle=!0,this._completedLoop=!1,this.projectInterface=ProjectInterface(),this.imagePreloader=new ImagePreloader,this.audioController=audioControllerFactory(),this.markers=[],this.configAnimation=this.configAnimation.bind(this),this.onSetupError=this.onSetupError.bind(this),this.onSegmentComplete=this.onSegmentComplete.bind(this),this.drawnFrameEvent=new BMEnterFrameEvent(\"drawnFrame\",0,0,0),this.expressionsPlugin=getExpressionsPlugin()};extendPrototype([BaseEvent],AnimationItem),AnimationItem.prototype.setParams=function(A){(A.wrapper||A.container)&&(this.wrapper=A.wrapper||A.container);var t=\"svg\";A.animType?t=A.animType:A.renderer&&(t=A.renderer);var e=getRenderer(t);this.renderer=new e(this,A.rendererSettings),this.imagePreloader.setCacheType(t,this.renderer.globalData.defs),this.renderer.setProjectInterface(this.projectInterface),this.animType=t,\"\"===A.loop||null===A.loop||void 0===A.loop||!0===A.loop?this.loop=!0:!1===A.loop?this.loop=!1:this.loop=parseInt(A.loop,10),this.autoplay=!(\"autoplay\"in A)||A.autoplay,this.name=A.name?A.name:\"\",this.autoloadSegments=!Object.prototype.hasOwnProperty.call(A,\"autoloadSegments\")||A.autoloadSegments,this.assetsPath=A.assetsPath,this.initialSegment=A.initialSegment,A.audioFactory&&this.audioController.setAudioFactory(A.audioFactory),A.animationData?this.setupAnimation(A.animationData):A.path&&(-1!==A.path.lastIndexOf(\"\\\\\")?this.path=A.path.substr(0,A.path.lastIndexOf(\"\\\\\")+1):this.path=A.path.substr(0,A.path.lastIndexOf(\"/\")+1),this.fileName=A.path.substr(A.path.lastIndexOf(\"/\")+1),this.fileName=this.fileName.substr(0,this.fileName.lastIndexOf(\".json\")),dataManager.loadAnimation(A.path,this.configAnimation,this.onSetupError))},AnimationItem.prototype.onSetupError=function(){this.trigger(\"data_failed\")},AnimationItem.prototype.setupAnimation=function(A){dataManager.completeAnimation(A,this.configAnimation)},AnimationItem.prototype.setData=function(A,t){t&&\"object\"!==_typeof$4(t)&&(t=JSON.parse(t));var e={wrapper:A,animationData:t},a=A.attributes;e.path=a.getNamedItem(\"data-animation-path\")?a.getNamedItem(\"data-animation-path\").value:a.getNamedItem(\"data-bm-path\")?a.getNamedItem(\"data-bm-path\").value:a.getNamedItem(\"bm-path\")?a.getNamedItem(\"bm-path\").value:\"\",e.animType=a.getNamedItem(\"data-anim-type\")?a.getNamedItem(\"data-anim-type\").value:a.getNamedItem(\"data-bm-type\")?a.getNamedItem(\"data-bm-type\").value:a.getNamedItem(\"bm-type\")?a.getNamedItem(\"bm-type\").value:a.getNamedItem(\"data-bm-renderer\")?a.getNamedItem(\"data-bm-renderer\").value:a.getNamedItem(\"bm-renderer\")?a.getNamedItem(\"bm-renderer\").value:getRegisteredRenderer()||\"canvas\";var r=a.getNamedItem(\"data-anim-loop\")?a.getNamedItem(\"data-anim-loop\").value:a.getNamedItem(\"data-bm-loop\")?a.getNamedItem(\"data-bm-loop\").value:a.getNamedItem(\"bm-loop\")?a.getNamedItem(\"bm-loop\").value:\"\";\"false\"===r?e.loop=!1:\"true\"===r?e.loop=!0:\"\"!==r&&(e.loop=parseInt(r,10));var n=a.getNamedItem(\"data-anim-autoplay\")?a.getNamedItem(\"data-anim-autoplay\").value:a.getNamedItem(\"data-bm-autoplay\")?a.getNamedItem(\"data-bm-autoplay\").value:!a.getNamedItem(\"bm-autoplay\")||a.getNamedItem(\"bm-autoplay\").value;e.autoplay=\"false\"!==n,e.name=a.getNamedItem(\"data-name\")?a.getNamedItem(\"data-name\").value:a.getNamedItem(\"data-bm-name\")?a.getNamedItem(\"data-bm-name\").value:a.getNamedItem(\"bm-name\")?a.getNamedItem(\"bm-name\").value:\"\",\"false\"===(a.getNamedItem(\"data-anim-prerender\")?a.getNamedItem(\"data-anim-prerender\").value:a.getNamedItem(\"data-bm-prerender\")?a.getNamedItem(\"data-bm-prerender\").value:a.getNamedItem(\"bm-prerender\")?a.getNamedItem(\"bm-prerender\").value:\"\")&&(e.prerender=!1),e.path?this.setParams(e):this.trigger(\"destroy\")},AnimationItem.prototype.includeLayers=function(A){A.op>this.animationData.op&&(this.animationData.op=A.op,this.totalFrames=Math.floor(A.op-this.animationData.ip));var t,e,a=this.animationData.layers,r=a.length,n=A.layers,l=n.length;for(e=0;e<l;e+=1)for(t=0;t<r;){if(a[t].id===n[e].id){a[t]=n[e];break}t+=1}if((A.chars||A.fonts)&&(this.renderer.globalData.fontManager.addChars(A.chars),this.renderer.globalData.fontManager.addFonts(A.fonts,this.renderer.globalData.defs)),A.assets)for(r=A.assets.length,t=0;t<r;t+=1)this.animationData.assets.push(A.assets[t]);this.animationData.__complete=!1,dataManager.completeAnimation(this.animationData,this.onSegmentComplete)},AnimationItem.prototype.onSegmentComplete=function(A){this.animationData=A;var t=getExpressionsPlugin();t&&t.initExpressions(this),this.loadNextSegment()},AnimationItem.prototype.loadNextSegment=function(){var A=this.animationData.segments;if(!A||0===A.length||!this.autoloadSegments)return this.trigger(\"data_ready\"),void(this.timeCompleted=this.totalFrames);var t=A.shift();this.timeCompleted=t.time*this.frameRate;var e=this.path+this.fileName+\"_\"+this.segmentPos+\".json\";this.segmentPos+=1,dataManager.loadData(e,this.includeLayers.bind(this),function(){this.trigger(\"data_failed\")}.bind(this))},AnimationItem.prototype.loadSegments=function(){this.animationData.segments||(this.timeCompleted=this.totalFrames),this.loadNextSegment()},AnimationItem.prototype.imagesLoaded=function(){this.trigger(\"loaded_images\"),this.checkLoaded()},AnimationItem.prototype.preloadImages=function(){this.imagePreloader.setAssetsPath(this.assetsPath),this.imagePreloader.setPath(this.path),this.imagePreloader.loadAssets(this.animationData.assets,this.imagesLoaded.bind(this))},AnimationItem.prototype.configAnimation=function(A){if(this.renderer)try{this.animationData=A,this.initialSegment?(this.totalFrames=Math.floor(this.initialSegment[1]-this.initialSegment[0]),this.firstFrame=Math.round(this.initialSegment[0])):(this.totalFrames=Math.floor(this.animationData.op-this.animationData.ip),this.firstFrame=Math.round(this.animationData.ip)),this.renderer.configAnimation(A),A.assets||(A.assets=[]),this.assets=this.animationData.assets,this.frameRate=this.animationData.fr,this.frameMult=this.animationData.fr/1e3,this.renderer.searchExtraCompositions(A.assets),this.markers=markerParser(A.markers||[]),this.trigger(\"config_ready\"),this.preloadImages(),this.loadSegments(),this.updaFrameModifier(),this.waitForFontsLoaded(),this.isPaused&&this.audioController.pause()}catch(t){this.triggerConfigError(t)}},AnimationItem.prototype.waitForFontsLoaded=function(){this.renderer&&(this.renderer.globalData.fontManager.isLoaded?this.checkLoaded():setTimeout(this.waitForFontsLoaded.bind(this),20))},AnimationItem.prototype.checkLoaded=function(){if(!this.isLoaded&&this.renderer.globalData.fontManager.isLoaded&&(this.imagePreloader.loadedImages()||\"canvas\"!==this.renderer.rendererType)&&this.imagePreloader.loadedFootages()){this.isLoaded=!0;var A=getExpressionsPlugin();A&&A.initExpressions(this),this.renderer.initItems(),setTimeout(function(){this.trigger(\"DOMLoaded\")}.bind(this),0),this.gotoFrame(),this.autoplay&&this.play()}},AnimationItem.prototype.resize=function(A,t){var e=\"number\"==typeof A?A:void 0,a=\"number\"==typeof t?t:void 0;this.renderer.updateContainerSize(e,a)},AnimationItem.prototype.setSubframe=function(A){this.isSubframeEnabled=!!A},AnimationItem.prototype.gotoFrame=function(){this.currentFrame=this.isSubframeEnabled?this.currentRawFrame:~~this.currentRawFrame,this.timeCompleted!==this.totalFrames&&this.currentFrame>this.timeCompleted&&(this.currentFrame=this.timeCompleted),this.trigger(\"enterFrame\"),this.renderFrame(),this.trigger(\"drawnFrame\")},AnimationItem.prototype.renderFrame=function(){if(!1!==this.isLoaded&&this.renderer)try{this.expressionsPlugin&&this.expressionsPlugin.resetFrame(),this.renderer.renderFrame(this.currentFrame+this.firstFrame)}catch(t){this.triggerRenderFrameError(t)}},AnimationItem.prototype.play=function(A){A&&this.name!==A||!0===this.isPaused&&(this.isPaused=!1,this.trigger(\"_play\"),this.audioController.resume(),this._idle&&(this._idle=!1,this.trigger(\"_active\")))},AnimationItem.prototype.pause=function(A){A&&this.name!==A||!1===this.isPaused&&(this.isPaused=!0,this.trigger(\"_pause\"),this._idle=!0,this.trigger(\"_idle\"),this.audioController.pause())},AnimationItem.prototype.togglePause=function(A){A&&this.name!==A||(!0===this.isPaused?this.play():this.pause())},AnimationItem.prototype.stop=function(A){A&&this.name!==A||(this.pause(),this.playCount=0,this._completedLoop=!1,this.setCurrentRawFrameValue(0))},AnimationItem.prototype.getMarkerData=function(A){for(var t,e=0;e<this.markers.length;e+=1)if((t=this.markers[e]).payload&&t.payload.name===A)return t;return null},AnimationItem.prototype.goToAndStop=function(A,t,e){if(!e||this.name===e){var a=Number(A);if(isNaN(a)){var r=this.getMarkerData(A);r&&this.goToAndStop(r.time,!0)}else t?this.setCurrentRawFrameValue(A):this.setCurrentRawFrameValue(A*this.frameModifier);this.pause()}},AnimationItem.prototype.goToAndPlay=function(A,t,e){if(!e||this.name===e){var a=Number(A);if(isNaN(a)){var r=this.getMarkerData(A);r&&(r.duration?this.playSegments([r.time,r.time+r.duration],!0):this.goToAndStop(r.time,!0))}else this.goToAndStop(a,t,e);this.play()}},AnimationItem.prototype.advanceTime=function(A){if(!0!==this.isPaused&&!1!==this.isLoaded){var t=this.currentRawFrame+A*this.frameModifier,e=!1;t>=this.totalFrames-1&&this.frameModifier>0?this.loop&&this.playCount!==this.loop?t>=this.totalFrames?(this.playCount+=1,this.checkSegments(t%this.totalFrames)||(this.setCurrentRawFrameValue(t%this.totalFrames),this._completedLoop=!0,this.trigger(\"loopComplete\"))):this.setCurrentRawFrameValue(t):this.checkSegments(t>this.totalFrames?t%this.totalFrames:0)||(e=!0,t=this.totalFrames-1):t<0?this.checkSegments(t%this.totalFrames)||(!this.loop||this.playCount--<=0&&!0!==this.loop?(e=!0,t=0):(this.setCurrentRawFrameValue(this.totalFrames+t%this.totalFrames),this._completedLoop?this.trigger(\"loopComplete\"):this._completedLoop=!0)):this.setCurrentRawFrameValue(t),e&&(this.setCurrentRawFrameValue(t),this.pause(),this.trigger(\"complete\"))}},AnimationItem.prototype.adjustSegment=function(A,t){this.playCount=0,A[1]<A[0]?(this.frameModifier>0&&(this.playSpeed<0?this.setSpeed(-this.playSpeed):this.setDirection(-1)),this.totalFrames=A[0]-A[1],this.timeCompleted=this.totalFrames,this.firstFrame=A[1],this.setCurrentRawFrameValue(this.totalFrames-.001-t)):A[1]>A[0]&&(this.frameModifier<0&&(this.playSpeed<0?this.setSpeed(-this.playSpeed):this.setDirection(1)),this.totalFrames=A[1]-A[0],this.timeCompleted=this.totalFrames,this.firstFrame=A[0],this.setCurrentRawFrameValue(.001+t)),this.trigger(\"segmentStart\")},AnimationItem.prototype.setSegment=function(A,t){var e=-1;this.isPaused&&(this.currentRawFrame+this.firstFrame<A?e=A:this.currentRawFrame+this.firstFrame>t&&(e=t-A)),this.firstFrame=A,this.totalFrames=t-A,this.timeCompleted=this.totalFrames,-1!==e&&this.goToAndStop(e,!0)},AnimationItem.prototype.playSegments=function(A,t){if(t&&(this.segments.length=0),\"object\"===_typeof$4(A[0])){var e,a=A.length;for(e=0;e<a;e+=1)this.segments.push(A[e])}else this.segments.push(A);this.segments.length&&t&&this.adjustSegment(this.segments.shift(),0),this.isPaused&&this.play()},AnimationItem.prototype.resetSegments=function(A){this.segments.length=0,this.segments.push([this.animationData.ip,this.animationData.op]),A&&this.checkSegments(0)},AnimationItem.prototype.checkSegments=function(A){return!!this.segments.length&&(this.adjustSegment(this.segments.shift(),A),!0)},AnimationItem.prototype.destroy=function(A){A&&this.name!==A||!this.renderer||(this.renderer.destroy(),this.imagePreloader.destroy(),this.trigger(\"destroy\"),this._cbs=null,this.onEnterFrame=null,this.onLoopComplete=null,this.onComplete=null,this.onSegmentStart=null,this.onDestroy=null,this.renderer=null,this.expressionsPlugin=null,this.imagePreloader=null,this.projectInterface=null)},AnimationItem.prototype.setCurrentRawFrameValue=function(A){this.currentRawFrame=A,this.gotoFrame()},AnimationItem.prototype.setSpeed=function(A){this.playSpeed=A,this.updaFrameModifier()},AnimationItem.prototype.setDirection=function(A){this.playDirection=A<0?-1:1,this.updaFrameModifier()},AnimationItem.prototype.setLoop=function(A){this.loop=A},AnimationItem.prototype.setVolume=function(A,t){t&&this.name!==t||this.audioController.setVolume(A)},AnimationItem.prototype.getVolume=function(){return this.audioController.getVolume()},AnimationItem.prototype.mute=function(A){A&&this.name!==A||this.audioController.mute()},AnimationItem.prototype.unmute=function(A){A&&this.name!==A||this.audioController.unmute()},AnimationItem.prototype.updaFrameModifier=function(){this.frameModifier=this.frameMult*this.playSpeed*this.playDirection,this.audioController.setRate(this.playSpeed*this.playDirection)},AnimationItem.prototype.getPath=function(){return this.path},AnimationItem.prototype.getAssetsPath=function(A){var t=\"\";if(A.e)t=A.p;else if(this.assetsPath){var e=A.p;-1!==e.indexOf(\"images/\")&&(e=e.split(\"/\")[1]),t=this.assetsPath+e}else t=this.path,t+=A.u?A.u:\"\",t+=A.p;return t},AnimationItem.prototype.getAssetData=function(A){for(var t=0,e=this.assets.length;t<e;){if(A===this.assets[t].id)return this.assets[t];t+=1}return null},AnimationItem.prototype.hide=function(){this.renderer.hide()},AnimationItem.prototype.show=function(){this.renderer.show()},AnimationItem.prototype.getDuration=function(A){return A?this.totalFrames:this.totalFrames/this.frameRate},AnimationItem.prototype.updateDocumentData=function(A,t,e){try{this.renderer.getElementByPath(A).updateDocumentData(t,e)}catch(a){}},AnimationItem.prototype.trigger=function(A){if(this._cbs&&this._cbs[A])switch(A){case\"enterFrame\":this.triggerEvent(A,new BMEnterFrameEvent(A,this.currentFrame,this.totalFrames,this.frameModifier));break;case\"drawnFrame\":this.drawnFrameEvent.currentTime=this.currentFrame,this.drawnFrameEvent.totalTime=this.totalFrames,this.drawnFrameEvent.direction=this.frameModifier,this.triggerEvent(A,this.drawnFrameEvent);break;case\"loopComplete\":this.triggerEvent(A,new BMCompleteLoopEvent(A,this.loop,this.playCount,this.frameMult));break;case\"complete\":this.triggerEvent(A,new BMCompleteEvent(A,this.frameMult));break;case\"segmentStart\":this.triggerEvent(A,new BMSegmentStartEvent(A,this.firstFrame,this.totalFrames));break;case\"destroy\":this.triggerEvent(A,new BMDestroyEvent(A,this));break;default:this.triggerEvent(A)}\"enterFrame\"===A&&this.onEnterFrame&&this.onEnterFrame.call(this,new BMEnterFrameEvent(A,this.currentFrame,this.totalFrames,this.frameMult)),\"loopComplete\"===A&&this.onLoopComplete&&this.onLoopComplete.call(this,new BMCompleteLoopEvent(A,this.loop,this.playCount,this.frameMult)),\"complete\"===A&&this.onComplete&&this.onComplete.call(this,new BMCompleteEvent(A,this.frameMult)),\"segmentStart\"===A&&this.onSegmentStart&&this.onSegmentStart.call(this,new BMSegmentStartEvent(A,this.firstFrame,this.totalFrames)),\"destroy\"===A&&this.onDestroy&&this.onDestroy.call(this,new BMDestroyEvent(A,this))},AnimationItem.prototype.triggerRenderFrameError=function(A){var t=new BMRenderFrameErrorEvent(A,this.currentFrame);this.triggerEvent(\"error\",t),this.onError&&this.onError.call(this,t)},AnimationItem.prototype.triggerConfigError=function(A){var t=new BMConfigErrorEvent(A,this.currentFrame);this.triggerEvent(\"error\",t),this.onError&&this.onError.call(this,t)};var animationManager=function(){var A={},t=[],e=0,a=0,r=0,n=!0,l=!1;function i(A){for(var e=0,r=A.target;e<a;)t[e].animation===r&&(t.splice(e,1),e-=1,a-=1,r.isPaused||p()),e+=1}function o(A,e){if(!A)return null;for(var r=0;r<a;){if(t[r].elem===A&&null!==t[r].elem)return t[r].animation;r+=1}var n=new AnimationItem;return c(n,A),n.setData(A,e),n}function s(){r+=1,h()}function p(){r-=1}function c(A,e){A.addEventListener(\"destroy\",i),A.addEventListener(\"_active\",s),A.addEventListener(\"_idle\",p),t.push({elem:e,animation:A}),a+=1}function u(A){var i,o=A-e;for(i=0;i<a;i+=1)t[i].animation.advanceTime(o);e=A,r&&!l?window.requestAnimationFrame(u):n=!0}function d(A){e=A,window.requestAnimationFrame(u)}function h(){!l&&r&&n&&(window.requestAnimationFrame(d),n=!1)}return A.registerAnimation=o,A.loadAnimation=function(A){var t=new AnimationItem;return c(t,null),t.setParams(A),t},A.setSpeed=function(A,e){var r;for(r=0;r<a;r+=1)t[r].animation.setSpeed(A,e)},A.setDirection=function(A,e){var r;for(r=0;r<a;r+=1)t[r].animation.setDirection(A,e)},A.play=function(A){var e;for(e=0;e<a;e+=1)t[e].animation.play(A)},A.pause=function(A){var e;for(e=0;e<a;e+=1)t[e].animation.pause(A)},A.stop=function(A){var e;for(e=0;e<a;e+=1)t[e].animation.stop(A)},A.togglePause=function(A){var e;for(e=0;e<a;e+=1)t[e].animation.togglePause(A)},A.searchAnimations=function(A,t,e){var a,r=[].concat([].slice.call(document.getElementsByClassName(\"lottie\")),[].slice.call(document.getElementsByClassName(\"bodymovin\"))),n=r.length;for(a=0;a<n;a+=1)e&&r[a].setAttribute(\"data-bm-type\",e),o(r[a],A);if(t&&0===n){e||(e=\"svg\");var l=document.getElementsByTagName(\"body\")[0];l.innerText=\"\";var i=createTag(\"div\");i.style.width=\"100%\",i.style.height=\"100%\",i.setAttribute(\"data-bm-type\",e),l.appendChild(i),o(i,A)}},A.resize=function(){var A;for(A=0;A<a;A+=1)t[A].animation.resize()},A.goToAndStop=function(A,e,r){var n;for(n=0;n<a;n+=1)t[n].animation.goToAndStop(A,e,r)},A.destroy=function(A){var e;for(e=a-1;e>=0;e-=1)t[e].animation.destroy(A)},A.freeze=function(){l=!0},A.unfreeze=function(){l=!1,h()},A.setVolume=function(A,e){var r;for(r=0;r<a;r+=1)t[r].animation.setVolume(A,e)},A.mute=function(A){var e;for(e=0;e<a;e+=1)t[e].animation.mute(A)},A.unmute=function(A){var e;for(e=0;e<a;e+=1)t[e].animation.unmute(A)},A.getRegisteredAnimations=function(){var A,e=t.length,a=[];for(A=0;A<e;A+=1)a.push(t[A].animation);return a},A}(),BezierFactory=function(){var A={getBezierEasing:function(A,e,a,r,n){var l=n||(\"bez_\"+A+\"_\"+e+\"_\"+a+\"_\"+r).replace(/\\./g,\"p\");if(t[l])return t[l];var i=new o([A,e,a,r]);return t[l]=i,i}},t={},e=\"function\"==typeof Float32Array;function a(A,t){return 1-3*t+3*A}function r(A,t){return 3*t-6*A}function n(A){return 3*A}function l(A,t,e){return((a(t,e)*A+r(t,e))*A+n(t))*A}function i(A,t,e){return 3*a(t,e)*A*A+2*r(t,e)*A+n(t)}function o(A){this._p=A,this._mSampleValues=e?new Float32Array(11):new Array(11),this._precomputed=!1,this.get=this.get.bind(this)}return o.prototype={get:function(A){var t=this._p[0],e=this._p[1],a=this._p[2],r=this._p[3];return this._precomputed||this._precompute(),t===e&&a===r?A:0===A?0:1===A?1:l(this._getTForX(A),e,r)},_precompute:function(){var A=this._p[0],t=this._p[1],e=this._p[2],a=this._p[3];this._precomputed=!0,A===t&&e===a||this._calcSampleValues()},_calcSampleValues:function(){for(var A=this._p[0],t=this._p[2],e=0;e<11;++e)this._mSampleValues[e]=l(.1*e,A,t)},_getTForX:function(A){for(var t=this._p[0],e=this._p[2],a=this._mSampleValues,r=0,n=1;10!==n&&a[n]<=A;++n)r+=.1;var o=r+(A-a[--n])/(a[n+1]-a[n])*.1,s=i(o,t,e);return s>=.001?function(A,t,e,a){for(var r=0;r<4;++r){var n=i(t,e,a);if(0===n)return t;t-=(l(t,e,a)-A)/n}return t}(A,o,t,e):0===s?o:function(A,t,e,a,r){var n,i,o=0;do{(n=l(i=t+(e-t)/2,a,r)-A)>0?e=i:t=i}while(Math.abs(n)>1e-7&&++o<10);return i}(A,r,r+.1,t,e)}},A}(),pooling={double:function(A){return A.concat(createSizedArray(A.length))}},poolFactory=function(A,t,e){var a=0,r=A,n=createSizedArray(r);return{newElement:function(){return a?n[a-=1]:t()},release:function(A){a===r&&(n=pooling.double(n),r*=2),e&&e(A),n[a]=A,a+=1}}},bezierLengthPool=poolFactory(8,function(){return{addedLength:0,percents:createTypedArray(\"float32\",getDefaultCurveSegments()),lengths:createTypedArray(\"float32\",getDefaultCurveSegments())}}),segmentsLengthPool=poolFactory(8,function(){return{lengths:[],totalLength:0}},function(A){var t,e=A.lengths.length;for(t=0;t<e;t+=1)bezierLengthPool.release(A.lengths[t]);A.lengths.length=0});function bezFunction(){var A=Math;function t(A,t,e,a,r,n){var l=A*a+t*r+e*n-r*a-n*A-e*t;return l>-.001&&l<.001}var e=function(A,t,e,a){var r,n,l,i,o,s,p=getDefaultCurveSegments(),c=0,u=[],d=[],h=bezierLengthPool.newElement();for(l=e.length,r=0;r<p;r+=1){for(o=r/(p-1),s=0,n=0;n<l;n+=1)i=bmPow(1-o,3)*A[n]+3*bmPow(1-o,2)*o*e[n]+3*(1-o)*bmPow(o,2)*a[n]+bmPow(o,3)*t[n],u[n]=i,null!==d[n]&&(s+=bmPow(u[n]-d[n],2)),d[n]=u[n];s&&(c+=s=bmSqrt(s)),h.percents[r]=o,h.lengths[r]=c}return h.addedLength=c,h};function a(A){this.segmentLength=0,this.points=new Array(A)}function r(A,t){this.partialLength=A,this.point=t}var n,l=(n={},function(A,e,l,i){var o=(A[0]+\"_\"+A[1]+\"_\"+e[0]+\"_\"+e[1]+\"_\"+l[0]+\"_\"+l[1]+\"_\"+i[0]+\"_\"+i[1]).replace(/\\./g,\"p\");if(!n[o]){var s,p,c,u,d,h,S,f=getDefaultCurveSegments(),m=0,L=null;2===A.length&&(A[0]!==e[0]||A[1]!==e[1])&&t(A[0],A[1],e[0],e[1],A[0]+l[0],A[1]+l[1])&&t(A[0],A[1],e[0],e[1],e[0]+i[0],e[1]+i[1])&&(f=2);var W=new a(f);for(c=l.length,s=0;s<f;s+=1){for(S=createSizedArray(c),d=s/(f-1),h=0,p=0;p<c;p+=1)u=bmPow(1-d,3)*A[p]+3*bmPow(1-d,2)*d*(A[p]+l[p])+3*(1-d)*bmPow(d,2)*(e[p]+i[p])+bmPow(d,3)*e[p],S[p]=u,null!==L&&(h+=bmPow(S[p]-L[p],2));m+=h=bmSqrt(h),W.points[s]=new r(h,S),L=S}W.segmentLength=m,n[o]=W}return n[o]});function i(A,t){var e=t.percents,a=t.lengths,r=e.length,n=bmFloor((r-1)*A),l=A*t.addedLength,i=0;if(n===r-1||0===n||l===a[n])return e[n];for(var o=a[n]>l?-1:1,s=!0;s;)if(a[n]<=l&&a[n+1]>l?(i=(l-a[n])/(a[n+1]-a[n]),s=!1):n+=o,n<0||n>=r-1){if(n===r-1)return e[n];s=!1}return e[n]+(e[n+1]-e[n])*i}var o=createTypedArray(\"float32\",8);return{getSegmentsLength:function(A){var t,a=segmentsLengthPool.newElement(),r=A.c,n=A.v,l=A.o,i=A.i,o=A._length,s=a.lengths,p=0;for(t=0;t<o-1;t+=1)s[t]=e(n[t],n[t+1],l[t],i[t+1]),p+=s[t].addedLength;return r&&o&&(s[t]=e(n[t],n[0],l[t],i[0]),p+=s[t].addedLength),a.totalLength=p,a},getNewSegment:function(t,e,a,r,n,l,s){n<0?n=0:n>1&&(n=1);var p,c=i(n,s),u=i(l=l>1?1:l,s),d=t.length,h=1-c,S=1-u,f=h*h*h,m=c*h*h*3,L=c*c*h*3,W=c*c*c,g=h*h*S,y=c*h*S+h*c*S+h*h*u,v=c*c*S+h*c*u+c*h*u,b=c*c*u,x=h*S*S,E=c*S*S+h*u*S+h*S*u,k=c*u*S+h*u*u+c*S*u,w=c*u*u,C=S*S*S,P=u*S*S+S*u*S+S*S*u,T=u*u*S+S*u*u+u*S*u,_=u*u*u;for(p=0;p<d;p+=1)o[4*p]=A.round(1e3*(f*t[p]+m*a[p]+L*r[p]+W*e[p]))/1e3,o[4*p+1]=A.round(1e3*(g*t[p]+y*a[p]+v*r[p]+b*e[p]))/1e3,o[4*p+2]=A.round(1e3*(x*t[p]+E*a[p]+k*r[p]+w*e[p]))/1e3,o[4*p+3]=A.round(1e3*(C*t[p]+P*a[p]+T*r[p]+_*e[p]))/1e3;return o},getPointInSegment:function(t,e,a,r,n,l){var o=i(n,l),s=1-o;return[A.round(1e3*(s*s*s*t[0]+(o*s*s+s*o*s+s*s*o)*a[0]+(o*o*s+s*o*o+o*s*o)*r[0]+o*o*o*e[0]))/1e3,A.round(1e3*(s*s*s*t[1]+(o*s*s+s*o*s+s*s*o)*a[1]+(o*o*s+s*o*o+o*s*o)*r[1]+o*o*o*e[1]))/1e3]},buildBezierData:l,pointOnLine2D:t,pointOnLine3D:function(e,a,r,n,l,i,o,s,p){if(0===r&&0===i&&0===p)return t(e,a,n,l,o,s);var c,u=A.sqrt(A.pow(n-e,2)+A.pow(l-a,2)+A.pow(i-r,2)),d=A.sqrt(A.pow(o-e,2)+A.pow(s-a,2)+A.pow(p-r,2)),h=A.sqrt(A.pow(o-n,2)+A.pow(s-l,2)+A.pow(p-i,2));return(c=u>d?u>h?u-d-h:h-d-u:h>d?h-d-u:d-u-h)>-1e-4&&c<1e-4}}}var bez=bezFunction(),initFrame=initialDefaultFrame,mathAbs=Math.abs;function interpolateValue(A,t){var e,a=this.offsetTime;\"multidimensional\"===this.propType&&(e=createTypedArray(\"float32\",this.pv.length));for(var r,n,l,i,o,s,p,c,u,d=t.lastIndex,h=d,S=this.keyframes.length-1,f=!0;f;){if(r=this.keyframes[h],n=this.keyframes[h+1],h===S-1&&A>=n.t-a){r.h&&(r=n),d=0;break}if(n.t-a>A){d=h;break}h<S-1?h+=1:(d=0,f=!1)}l=this.keyframesMetadata[h]||{};var m,L=n.t-a,W=r.t-a;if(r.to){l.bezierData||(l.bezierData=bez.buildBezierData(r.s,n.s||r.e,r.to,r.ti));var g=l.bezierData;if(A>=L||A<W){var y=A>=L?g.points.length-1:0;for(o=g.points[y].point.length,i=0;i<o;i+=1)e[i]=g.points[y].point[i]}else{l.__fnct?u=l.__fnct:(u=BezierFactory.getBezierEasing(r.o.x,r.o.y,r.i.x,r.i.y,r.n).get,l.__fnct=u),s=u((A-W)/(L-W));var v,b=g.segmentLength*s,x=t.lastFrame<A&&t._lastKeyframeIndex===h?t._lastAddedLength:0;for(c=t.lastFrame<A&&t._lastKeyframeIndex===h?t._lastPoint:0,f=!0,p=g.points.length;f;){if(x+=g.points[c].partialLength,0===b||0===s||c===g.points.length-1){for(o=g.points[c].point.length,i=0;i<o;i+=1)e[i]=g.points[c].point[i];break}if(b>=x&&b<x+g.points[c+1].partialLength){for(v=(b-x)/g.points[c+1].partialLength,o=g.points[c].point.length,i=0;i<o;i+=1)e[i]=g.points[c].point[i]+(g.points[c+1].point[i]-g.points[c].point[i])*v;break}c<p-1?c+=1:f=!1}t._lastPoint=c,t._lastAddedLength=x-g.points[c].partialLength,t._lastKeyframeIndex=h}}else{var E,k,w,C,P;if(S=r.s.length,m=n.s||r.e,this.sh&&1!==r.h)A>=L?(e[0]=m[0],e[1]=m[1],e[2]=m[2]):A<=W?(e[0]=r.s[0],e[1]=r.s[1],e[2]=r.s[2]):quaternionToEuler(e,slerp(createQuaternion(r.s),createQuaternion(m),(A-W)/(L-W)));else for(h=0;h<S;h+=1)1!==r.h&&(A>=L?s=1:A<W?s=0:(r.o.x.constructor===Array?(l.__fnct||(l.__fnct=[]),l.__fnct[h]?u=l.__fnct[h]:(E=void 0===r.o.x[h]?r.o.x[0]:r.o.x[h],k=void 0===r.o.y[h]?r.o.y[0]:r.o.y[h],w=void 0===r.i.x[h]?r.i.x[0]:r.i.x[h],C=void 0===r.i.y[h]?r.i.y[0]:r.i.y[h],u=BezierFactory.getBezierEasing(E,k,w,C).get,l.__fnct[h]=u)):l.__fnct?u=l.__fnct:(E=r.o.x,k=r.o.y,w=r.i.x,C=r.i.y,u=BezierFactory.getBezierEasing(E,k,w,C).get,r.keyframeMetadata=u),s=u((A-W)/(L-W)))),m=n.s||r.e,P=1===r.h?r.s[h]:r.s[h]+(m[h]-r.s[h])*s,\"multidimensional\"===this.propType?e[h]=P:e=P}return t.lastIndex=d,e}function slerp(A,t,e){var a,r,n,l,i,o=[],s=A[0],p=A[1],c=A[2],u=A[3],d=t[0],h=t[1],S=t[2],f=t[3];return(r=s*d+p*h+c*S+u*f)<0&&(r=-r,d=-d,h=-h,S=-S,f=-f),1-r>1e-6?(a=Math.acos(r),n=Math.sin(a),l=Math.sin((1-e)*a)/n,i=Math.sin(e*a)/n):(l=1-e,i=e),o[0]=l*s+i*d,o[1]=l*p+i*h,o[2]=l*c+i*S,o[3]=l*u+i*f,o}function quaternionToEuler(A,t){var e=t[0],a=t[1],r=t[2],n=t[3],l=Math.atan2(2*a*n-2*e*r,1-2*a*a-2*r*r),i=Math.asin(2*e*a+2*r*n),o=Math.atan2(2*e*n-2*a*r,1-2*e*e-2*r*r);A[0]=l/degToRads,A[1]=i/degToRads,A[2]=o/degToRads}function createQuaternion(A){var t=A[0]*degToRads,e=A[1]*degToRads,a=A[2]*degToRads,r=Math.cos(t/2),n=Math.cos(e/2),l=Math.cos(a/2),i=Math.sin(t/2),o=Math.sin(e/2),s=Math.sin(a/2);return[i*o*l+r*n*s,i*n*l+r*o*s,r*o*l-i*n*s,r*n*l-i*o*s]}function getValueAtCurrentTime(){var A=this.comp.renderedFrame-this.offsetTime,t=this.keyframes[0].t-this.offsetTime,e=this.keyframes[this.keyframes.length-1].t-this.offsetTime;if(!(A===this._caching.lastFrame||this._caching.lastFrame!==initFrame&&(this._caching.lastFrame>=e&&A>=e||this._caching.lastFrame<t&&A<t))){this._caching.lastFrame>=A&&(this._caching._lastKeyframeIndex=-1,this._caching.lastIndex=0);var a=this.interpolateValue(A,this._caching);this.pv=a}return this._caching.lastFrame=A,this.pv}function setVValue(A){var t;if(\"unidimensional\"===this.propType)t=A*this.mult,mathAbs(this.v-t)>1e-5&&(this.v=t,this._mdf=!0);else for(var e=0,a=this.v.length;e<a;)t=A[e]*this.mult,mathAbs(this.v[e]-t)>1e-5&&(this.v[e]=t,this._mdf=!0),e+=1}function processEffectsSequence(){if(this.elem.globalData.frameId!==this.frameId&&this.effectsSequence.length)if(this.lock)this.setVValue(this.pv);else{var A;this.lock=!0,this._mdf=this._isFirstFrame;var t=this.effectsSequence.length,e=this.kf?this.pv:this.data.k;for(A=0;A<t;A+=1)e=this.effectsSequence[A](e);this.setVValue(e),this._isFirstFrame=!1,this.lock=!1,this.frameId=this.elem.globalData.frameId}}function addEffect(A){this.effectsSequence.push(A),this.container.addDynamicProperty(this)}function ValueProperty(A,t,e,a){this.propType=\"unidimensional\",this.mult=e||1,this.data=t,this.v=e?t.k*e:t.k,this.pv=t.k,this._mdf=!1,this.elem=A,this.container=a,this.comp=A.comp,this.k=!1,this.kf=!1,this.vel=0,this.effectsSequence=[],this._isFirstFrame=!0,this.getValue=processEffectsSequence,this.setVValue=setVValue,this.addEffect=addEffect}function MultiDimensionalProperty(A,t,e,a){var r;this.propType=\"multidimensional\",this.mult=e||1,this.data=t,this._mdf=!1,this.elem=A,this.container=a,this.comp=A.comp,this.k=!1,this.kf=!1,this.frameId=-1;var n=t.k.length;for(this.v=createTypedArray(\"float32\",n),this.pv=createTypedArray(\"float32\",n),this.vel=createTypedArray(\"float32\",n),r=0;r<n;r+=1)this.v[r]=t.k[r]*this.mult,this.pv[r]=t.k[r];this._isFirstFrame=!0,this.effectsSequence=[],this.getValue=processEffectsSequence,this.setVValue=setVValue,this.addEffect=addEffect}function KeyframedValueProperty(A,t,e,a){this.propType=\"unidimensional\",this.keyframes=t.k,this.keyframesMetadata=[],this.offsetTime=A.data.st,this.frameId=-1,this._caching={lastFrame:initFrame,lastIndex:0,value:0,_lastKeyframeIndex:-1},this.k=!0,this.kf=!0,this.data=t,this.mult=e||1,this.elem=A,this.container=a,this.comp=A.comp,this.v=initFrame,this.pv=initFrame,this._isFirstFrame=!0,this.getValue=processEffectsSequence,this.setVValue=setVValue,this.interpolateValue=interpolateValue,this.effectsSequence=[getValueAtCurrentTime.bind(this)],this.addEffect=addEffect}function KeyframedMultidimensionalProperty(A,t,e,a){var r;this.propType=\"multidimensional\";var n,l,i,o,s=t.k.length;for(r=0;r<s-1;r+=1)t.k[r].to&&t.k[r].s&&t.k[r+1]&&t.k[r+1].s&&(n=t.k[r].s,l=t.k[r+1].s,i=t.k[r].to,o=t.k[r].ti,(2===n.length&&(n[0]!==l[0]||n[1]!==l[1])&&bez.pointOnLine2D(n[0],n[1],l[0],l[1],n[0]+i[0],n[1]+i[1])&&bez.pointOnLine2D(n[0],n[1],l[0],l[1],l[0]+o[0],l[1]+o[1])||3===n.length&&(n[0]!==l[0]||n[1]!==l[1]||n[2]!==l[2])&&bez.pointOnLine3D(n[0],n[1],n[2],l[0],l[1],l[2],n[0]+i[0],n[1]+i[1],n[2]+i[2])&&bez.pointOnLine3D(n[0],n[1],n[2],l[0],l[1],l[2],l[0]+o[0],l[1]+o[1],l[2]+o[2]))&&(t.k[r].to=null,t.k[r].ti=null),n[0]===l[0]&&n[1]===l[1]&&0===i[0]&&0===i[1]&&0===o[0]&&0===o[1]&&(2===n.length||n[2]===l[2]&&0===i[2]&&0===o[2])&&(t.k[r].to=null,t.k[r].ti=null));this.effectsSequence=[getValueAtCurrentTime.bind(this)],this.data=t,this.keyframes=t.k,this.keyframesMetadata=[],this.offsetTime=A.data.st,this.k=!0,this.kf=!0,this._isFirstFrame=!0,this.mult=e||1,this.elem=A,this.container=a,this.comp=A.comp,this.getValue=processEffectsSequence,this.setVValue=setVValue,this.interpolateValue=interpolateValue,this.frameId=-1;var p=t.k[0].s.length;for(this.v=createTypedArray(\"float32\",p),this.pv=createTypedArray(\"float32\",p),r=0;r<p;r+=1)this.v[r]=initFrame,this.pv[r]=initFrame;this._caching={lastFrame:initFrame,lastIndex:0,value:createTypedArray(\"float32\",p)},this.addEffect=addEffect}var PropertyFactory={getProp:function(A,t,e,a,r){var n;if(t.sid&&(t=A.globalData.slotManager.getProp(t)),t.k.length)if(\"number\"==typeof t.k[0])n=new MultiDimensionalProperty(A,t,a,r);else switch(e){case 0:n=new KeyframedValueProperty(A,t,a,r);break;case 1:n=new KeyframedMultidimensionalProperty(A,t,a,r)}else n=new ValueProperty(A,t,a,r);return n.effectsSequence.length&&r.addDynamicProperty(n),n}};function DynamicPropertyContainer(){}DynamicPropertyContainer.prototype={addDynamicProperty:function(A){-1===this.dynamicProperties.indexOf(A)&&(this.dynamicProperties.push(A),this.container.addDynamicProperty(this),this._isAnimated=!0)},iterateDynamicProperties:function(){var A;this._mdf=!1;var t=this.dynamicProperties.length;for(A=0;A<t;A+=1)this.dynamicProperties[A].getValue(),this.dynamicProperties[A]._mdf&&(this._mdf=!0)},initDynamicPropertyContainer:function(A){this.container=A,this.dynamicProperties=[],this._mdf=!1,this._isAnimated=!1}};var pointPool=poolFactory(8,function(){return createTypedArray(\"float32\",2)});function ShapePath(){this.c=!1,this._length=0,this._maxLength=8,this.v=createSizedArray(this._maxLength),this.o=createSizedArray(this._maxLength),this.i=createSizedArray(this._maxLength)}ShapePath.prototype.setPathData=function(A,t){this.c=A,this.setLength(t);for(var e=0;e<t;)this.v[e]=pointPool.newElement(),this.o[e]=pointPool.newElement(),this.i[e]=pointPool.newElement(),e+=1},ShapePath.prototype.setLength=function(A){for(;this._maxLength<A;)this.doubleArrayLength();this._length=A},ShapePath.prototype.doubleArrayLength=function(){this.v=this.v.concat(createSizedArray(this._maxLength)),this.i=this.i.concat(createSizedArray(this._maxLength)),this.o=this.o.concat(createSizedArray(this._maxLength)),this._maxLength*=2},ShapePath.prototype.setXYAt=function(A,t,e,a,r){var n;switch(this._length=Math.max(this._length,a+1),this._length>=this._maxLength&&this.doubleArrayLength(),e){case\"v\":n=this.v;break;case\"i\":n=this.i;break;case\"o\":n=this.o;break;default:n=[]}(!n[a]||n[a]&&!r)&&(n[a]=pointPool.newElement()),n[a][0]=A,n[a][1]=t},ShapePath.prototype.setTripleAt=function(A,t,e,a,r,n,l,i){this.setXYAt(A,t,\"v\",l,i),this.setXYAt(e,a,\"o\",l,i),this.setXYAt(r,n,\"i\",l,i)},ShapePath.prototype.reverse=function(){var A=new ShapePath;A.setPathData(this.c,this._length);var t=this.v,e=this.o,a=this.i,r=0;this.c&&(A.setTripleAt(t[0][0],t[0][1],a[0][0],a[0][1],e[0][0],e[0][1],0,!1),r=1);var n,l=this._length-1,i=this._length;for(n=r;n<i;n+=1)A.setTripleAt(t[l][0],t[l][1],a[l][0],a[l][1],e[l][0],e[l][1],n,!1),l-=1;return A},ShapePath.prototype.length=function(){return this._length};var shapePool=(factory=poolFactory(4,function(){return new ShapePath},function(A){var t,e=A._length;for(t=0;t<e;t+=1)pointPool.release(A.v[t]),pointPool.release(A.i[t]),pointPool.release(A.o[t]),A.v[t]=null,A.i[t]=null,A.o[t]=null;A._length=0,A.c=!1}),factory.clone=function(A){var t,e=factory.newElement(),a=void 0===A._length?A.v.length:A._length;for(e.setLength(a),e.c=A.c,t=0;t<a;t+=1)e.setTripleAt(A.v[t][0],A.v[t][1],A.o[t][0],A.o[t][1],A.i[t][0],A.i[t][1],t);return e},factory),factory;function ShapeCollection(){this._length=0,this._maxLength=4,this.shapes=createSizedArray(this._maxLength)}ShapeCollection.prototype.addShape=function(A){this._length===this._maxLength&&(this.shapes=this.shapes.concat(createSizedArray(this._maxLength)),this._maxLength*=2),this.shapes[this._length]=A,this._length+=1},ShapeCollection.prototype.releaseShapes=function(){var A;for(A=0;A<this._length;A+=1)shapePool.release(this.shapes[A]);this._length=0};var shapeCollectionPool=(ob={newShapeCollection:function(){return _length?pool[_length-=1]:new ShapeCollection},release:function(A){var t,e=A._length;for(t=0;t<e;t+=1)shapePool.release(A.shapes[t]);A._length=0,_length===_maxLength&&(pool=pooling.double(pool),_maxLength*=2),pool[_length]=A,_length+=1}},_length=0,_maxLength=4,pool=createSizedArray(_maxLength),ob),ob,_length,_maxLength,pool,ShapePropertyFactory=function(){function A(A,t,e){var a,r,n,l,i,o,s,p,c,u=e.lastIndex,d=this.keyframes;if(A<d[0].t-this.offsetTime)a=d[0].s[0],n=!0,u=0;else if(A>=d[d.length-1].t-this.offsetTime)a=d[d.length-1].s?d[d.length-1].s[0]:d[d.length-2].e[0],n=!0;else{for(var h,S,f,m=u,L=d.length-1,W=!0;W&&(h=d[m],!((S=d[m+1]).t-this.offsetTime>A));)m<L-1?m+=1:W=!1;if(f=this.keyframesMetadata[m]||{},u=m,!(n=1===h.h)){if(A>=S.t-this.offsetTime)p=1;else if(A<h.t-this.offsetTime)p=0;else{var g;f.__fnct?g=f.__fnct:(g=BezierFactory.getBezierEasing(h.o.x,h.o.y,h.i.x,h.i.y).get,f.__fnct=g),p=g((A-(h.t-this.offsetTime))/(S.t-this.offsetTime-(h.t-this.offsetTime)))}r=S.s?S.s[0]:h.e[0]}a=h.s[0]}for(o=t._length,s=a.i[0].length,e.lastIndex=u,l=0;l<o;l+=1)for(i=0;i<s;i+=1)c=n?a.i[l][i]:a.i[l][i]+(r.i[l][i]-a.i[l][i])*p,t.i[l][i]=c,c=n?a.o[l][i]:a.o[l][i]+(r.o[l][i]-a.o[l][i])*p,t.o[l][i]=c,c=n?a.v[l][i]:a.v[l][i]+(r.v[l][i]-a.v[l][i])*p,t.v[l][i]=c}function t(){var A=this.comp.renderedFrame-this.offsetTime,t=this.keyframes[0].t-this.offsetTime,e=this.keyframes[this.keyframes.length-1].t-this.offsetTime,a=this._caching.lastFrame;return-999999!==a&&(a<t&&A<t||a>e&&A>e)||(this._caching.lastIndex=a<A?this._caching.lastIndex:0,this.interpolateShape(A,this.pv,this._caching)),this._caching.lastFrame=A,this.pv}function e(){this.paths=this.localShapeCollection}function a(A){(function(A,t){if(A._length!==t._length||A.c!==t.c)return!1;var e,a=A._length;for(e=0;e<a;e+=1)if(A.v[e][0]!==t.v[e][0]||A.v[e][1]!==t.v[e][1]||A.o[e][0]!==t.o[e][0]||A.o[e][1]!==t.o[e][1]||A.i[e][0]!==t.i[e][0]||A.i[e][1]!==t.i[e][1])return!1;return!0})(this.v,A)||(this.v=shapePool.clone(A),this.localShapeCollection.releaseShapes(),this.localShapeCollection.addShape(this.v),this._mdf=!0,this.paths=this.localShapeCollection)}function r(){if(this.elem.globalData.frameId!==this.frameId)if(this.effectsSequence.length)if(this.lock)this.setVValue(this.pv);else{var A,t;this.lock=!0,this._mdf=!1,A=this.kf?this.pv:this.data.ks?this.data.ks.k:this.data.pt.k;var e=this.effectsSequence.length;for(t=0;t<e;t+=1)A=this.effectsSequence[t](A);this.setVValue(A),this.lock=!1,this.frameId=this.elem.globalData.frameId}else this._mdf=!1}function n(A,t,a){this.propType=\"shape\",this.comp=A.comp,this.container=A,this.elem=A,this.data=t,this.k=!1,this.kf=!1,this._mdf=!1;var r=3===a?t.pt.k:t.ks.k;this.v=shapePool.clone(r),this.pv=shapePool.clone(this.v),this.localShapeCollection=shapeCollectionPool.newShapeCollection(),this.paths=this.localShapeCollection,this.paths.addShape(this.v),this.reset=e,this.effectsSequence=[]}function l(A){this.effectsSequence.push(A),this.container.addDynamicProperty(this)}function i(A,a,r){this.propType=\"shape\",this.comp=A.comp,this.elem=A,this.container=A,this.offsetTime=A.data.st,this.keyframes=3===r?a.pt.k:a.ks.k,this.keyframesMetadata=[],this.k=!0,this.kf=!0;var n=this.keyframes[0].s[0].i.length;this.v=shapePool.newElement(),this.v.setPathData(this.keyframes[0].s[0].c,n),this.pv=shapePool.clone(this.v),this.localShapeCollection=shapeCollectionPool.newShapeCollection(),this.paths=this.localShapeCollection,this.paths.addShape(this.v),this.lastFrame=-999999,this.reset=e,this._caching={lastFrame:-999999,lastIndex:0},this.effectsSequence=[t.bind(this)]}n.prototype.interpolateShape=A,n.prototype.getValue=r,n.prototype.setVValue=a,n.prototype.addEffect=l,i.prototype.getValue=r,i.prototype.interpolateShape=A,i.prototype.setVValue=a,i.prototype.addEffect=l;var o=function(){var A=roundCorner;function t(A,t){this.v=shapePool.newElement(),this.v.setPathData(!0,4),this.localShapeCollection=shapeCollectionPool.newShapeCollection(),this.paths=this.localShapeCollection,this.localShapeCollection.addShape(this.v),this.d=t.d,this.elem=A,this.comp=A.comp,this.frameId=-1,this.initDynamicPropertyContainer(A),this.p=PropertyFactory.getProp(A,t.p,1,0,this),this.s=PropertyFactory.getProp(A,t.s,1,0,this),this.dynamicProperties.length?this.k=!0:(this.k=!1,this.convertEllToPath())}return t.prototype={reset:e,getValue:function(){this.elem.globalData.frameId!==this.frameId&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties(),this._mdf&&this.convertEllToPath())},convertEllToPath:function(){var t=this.p.v[0],e=this.p.v[1],a=this.s.v[0]/2,r=this.s.v[1]/2,n=3!==this.d,l=this.v;l.v[0][0]=t,l.v[0][1]=e-r,l.v[1][0]=n?t+a:t-a,l.v[1][1]=e,l.v[2][0]=t,l.v[2][1]=e+r,l.v[3][0]=n?t-a:t+a,l.v[3][1]=e,l.i[0][0]=n?t-a*A:t+a*A,l.i[0][1]=e-r,l.i[1][0]=n?t+a:t-a,l.i[1][1]=e-r*A,l.i[2][0]=n?t+a*A:t-a*A,l.i[2][1]=e+r,l.i[3][0]=n?t-a:t+a,l.i[3][1]=e+r*A,l.o[0][0]=n?t+a*A:t-a*A,l.o[0][1]=e-r,l.o[1][0]=n?t+a:t-a,l.o[1][1]=e+r*A,l.o[2][0]=n?t-a*A:t+a*A,l.o[2][1]=e+r,l.o[3][0]=n?t-a:t+a,l.o[3][1]=e-r*A}},extendPrototype([DynamicPropertyContainer],t),t}(),s=function(){function A(A,t){this.v=shapePool.newElement(),this.v.setPathData(!0,0),this.elem=A,this.comp=A.comp,this.data=t,this.frameId=-1,this.d=t.d,this.initDynamicPropertyContainer(A),1===t.sy?(this.ir=PropertyFactory.getProp(A,t.ir,0,0,this),this.is=PropertyFactory.getProp(A,t.is,0,.01,this),this.convertToPath=this.convertStarToPath):this.convertToPath=this.convertPolygonToPath,this.pt=PropertyFactory.getProp(A,t.pt,0,0,this),this.p=PropertyFactory.getProp(A,t.p,1,0,this),this.r=PropertyFactory.getProp(A,t.r,0,degToRads,this),this.or=PropertyFactory.getProp(A,t.or,0,0,this),this.os=PropertyFactory.getProp(A,t.os,0,.01,this),this.localShapeCollection=shapeCollectionPool.newShapeCollection(),this.localShapeCollection.addShape(this.v),this.paths=this.localShapeCollection,this.dynamicProperties.length?this.k=!0:(this.k=!1,this.convertToPath())}return A.prototype={reset:e,getValue:function(){this.elem.globalData.frameId!==this.frameId&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties(),this._mdf&&this.convertToPath())},convertStarToPath:function(){var A,t,e,a,r=2*Math.floor(this.pt.v),n=2*Math.PI/r,l=!0,i=this.or.v,o=this.ir.v,s=this.os.v,p=this.is.v,c=2*Math.PI*i/(2*r),u=2*Math.PI*o/(2*r),d=-Math.PI/2;d+=this.r.v;var h=3===this.data.d?-1:1;for(this.v._length=0,A=0;A<r;A+=1){e=l?s:p,a=l?c:u;var S=(t=l?i:o)*Math.cos(d),f=t*Math.sin(d),m=0===S&&0===f?0:f/Math.sqrt(S*S+f*f),L=0===S&&0===f?0:-S/Math.sqrt(S*S+f*f);S+=+this.p.v[0],f+=+this.p.v[1],this.v.setTripleAt(S,f,S-m*a*e*h,f-L*a*e*h,S+m*a*e*h,f+L*a*e*h,A,!0),l=!l,d+=n*h}},convertPolygonToPath:function(){var A,t=Math.floor(this.pt.v),e=2*Math.PI/t,a=this.or.v,r=this.os.v,n=2*Math.PI*a/(4*t),l=.5*-Math.PI,i=3===this.data.d?-1:1;for(l+=this.r.v,this.v._length=0,A=0;A<t;A+=1){var o=a*Math.cos(l),s=a*Math.sin(l),p=0===o&&0===s?0:s/Math.sqrt(o*o+s*s),c=0===o&&0===s?0:-o/Math.sqrt(o*o+s*s);o+=+this.p.v[0],s+=+this.p.v[1],this.v.setTripleAt(o,s,o-p*n*r*i,s-c*n*r*i,o+p*n*r*i,s+c*n*r*i,A,!0),l+=e*i}this.paths.length=0,this.paths[0]=this.v}},extendPrototype([DynamicPropertyContainer],A),A}(),p=function(){function A(A,t){this.v=shapePool.newElement(),this.v.c=!0,this.localShapeCollection=shapeCollectionPool.newShapeCollection(),this.localShapeCollection.addShape(this.v),this.paths=this.localShapeCollection,this.elem=A,this.comp=A.comp,this.frameId=-1,this.d=t.d,this.initDynamicPropertyContainer(A),this.p=PropertyFactory.getProp(A,t.p,1,0,this),this.s=PropertyFactory.getProp(A,t.s,1,0,this),this.r=PropertyFactory.getProp(A,t.r,0,0,this),this.dynamicProperties.length?this.k=!0:(this.k=!1,this.convertRectToPath())}return A.prototype={convertRectToPath:function(){var A=this.p.v[0],t=this.p.v[1],e=this.s.v[0]/2,a=this.s.v[1]/2,r=bmMin(e,a,this.r.v),n=r*(1-roundCorner);this.v._length=0,2===this.d||1===this.d?(this.v.setTripleAt(A+e,t-a+r,A+e,t-a+r,A+e,t-a+n,0,!0),this.v.setTripleAt(A+e,t+a-r,A+e,t+a-n,A+e,t+a-r,1,!0),0!==r?(this.v.setTripleAt(A+e-r,t+a,A+e-r,t+a,A+e-n,t+a,2,!0),this.v.setTripleAt(A-e+r,t+a,A-e+n,t+a,A-e+r,t+a,3,!0),this.v.setTripleAt(A-e,t+a-r,A-e,t+a-r,A-e,t+a-n,4,!0),this.v.setTripleAt(A-e,t-a+r,A-e,t-a+n,A-e,t-a+r,5,!0),this.v.setTripleAt(A-e+r,t-a,A-e+r,t-a,A-e+n,t-a,6,!0),this.v.setTripleAt(A+e-r,t-a,A+e-n,t-a,A+e-r,t-a,7,!0)):(this.v.setTripleAt(A-e,t+a,A-e+n,t+a,A-e,t+a,2),this.v.setTripleAt(A-e,t-a,A-e,t-a+n,A-e,t-a,3))):(this.v.setTripleAt(A+e,t-a+r,A+e,t-a+n,A+e,t-a+r,0,!0),0!==r?(this.v.setTripleAt(A+e-r,t-a,A+e-r,t-a,A+e-n,t-a,1,!0),this.v.setTripleAt(A-e+r,t-a,A-e+n,t-a,A-e+r,t-a,2,!0),this.v.setTripleAt(A-e,t-a+r,A-e,t-a+r,A-e,t-a+n,3,!0),this.v.setTripleAt(A-e,t+a-r,A-e,t+a-n,A-e,t+a-r,4,!0),this.v.setTripleAt(A-e+r,t+a,A-e+r,t+a,A-e+n,t+a,5,!0),this.v.setTripleAt(A+e-r,t+a,A+e-n,t+a,A+e-r,t+a,6,!0),this.v.setTripleAt(A+e,t+a-r,A+e,t+a-r,A+e,t+a-n,7,!0)):(this.v.setTripleAt(A-e,t-a,A-e+n,t-a,A-e,t-a,1,!0),this.v.setTripleAt(A-e,t+a,A-e,t+a-n,A-e,t+a,2,!0),this.v.setTripleAt(A+e,t+a,A+e-n,t+a,A+e,t+a,3,!0)))},getValue:function(){this.elem.globalData.frameId!==this.frameId&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties(),this._mdf&&this.convertRectToPath())},reset:e},extendPrototype([DynamicPropertyContainer],A),A}();return{getShapeProp:function(A,t,e){var a;return 3===e||4===e?a=(3===e?t.pt:t.ks).k.length?new i(A,t,e):new n(A,t,e):5===e?a=new p(A,t):6===e?a=new o(A,t):7===e&&(a=new s(A,t)),a.k&&A.addDynamicProperty(a),a},getConstructorFunction:function(){return n},getKeyframedConstructorFunction:function(){return i}}}(),Matrix=function(){var A=Math.cos,t=Math.sin,e=Math.tan,a=Math.round;function r(){return this.props[0]=1,this.props[1]=0,this.props[2]=0,this.props[3]=0,this.props[4]=0,this.props[5]=1,this.props[6]=0,this.props[7]=0,this.props[8]=0,this.props[9]=0,this.props[10]=1,this.props[11]=0,this.props[12]=0,this.props[13]=0,this.props[14]=0,this.props[15]=1,this}function n(e){if(0===e)return this;var a=A(e),r=t(e);return this._t(a,-r,0,0,r,a,0,0,0,0,1,0,0,0,0,1)}function l(e){if(0===e)return this;var a=A(e),r=t(e);return this._t(1,0,0,0,0,a,-r,0,0,r,a,0,0,0,0,1)}function i(e){if(0===e)return this;var a=A(e),r=t(e);return this._t(a,0,r,0,0,1,0,0,-r,0,a,0,0,0,0,1)}function o(e){if(0===e)return this;var a=A(e),r=t(e);return this._t(a,-r,0,0,r,a,0,0,0,0,1,0,0,0,0,1)}function s(A,t){return this._t(1,t,A,1,0,0)}function p(A,t){return this.shear(e(A),e(t))}function c(a,r){var n=A(r),l=t(r);return this._t(n,l,0,0,-l,n,0,0,0,0,1,0,0,0,0,1)._t(1,0,0,0,e(a),1,0,0,0,0,1,0,0,0,0,1)._t(n,-l,0,0,l,n,0,0,0,0,1,0,0,0,0,1)}function u(A,t,e){return e||0===e||(e=1),1===A&&1===t&&1===e?this:this._t(A,0,0,0,0,t,0,0,0,0,e,0,0,0,0,1)}function d(A,t,e,a,r,n,l,i,o,s,p,c,u,d,h,S){return this.props[0]=A,this.props[1]=t,this.props[2]=e,this.props[3]=a,this.props[4]=r,this.props[5]=n,this.props[6]=l,this.props[7]=i,this.props[8]=o,this.props[9]=s,this.props[10]=p,this.props[11]=c,this.props[12]=u,this.props[13]=d,this.props[14]=h,this.props[15]=S,this}function h(A,t,e){return e=e||0,0!==A||0!==t||0!==e?this._t(1,0,0,0,0,1,0,0,0,0,1,0,A,t,e,1):this}function S(A,t,e,a,r,n,l,i,o,s,p,c,u,d,h,S){var f=this.props;if(1===A&&0===t&&0===e&&0===a&&0===r&&1===n&&0===l&&0===i&&0===o&&0===s&&1===p&&0===c)return f[12]=f[12]*A+f[15]*u,f[13]=f[13]*n+f[15]*d,f[14]=f[14]*p+f[15]*h,f[15]*=S,this._identityCalculated=!1,this;var m=f[0],L=f[1],W=f[2],g=f[3],y=f[4],v=f[5],b=f[6],x=f[7],E=f[8],k=f[9],w=f[10],C=f[11],P=f[12],T=f[13],_=f[14],M=f[15];return f[0]=m*A+L*r+W*o+g*u,f[1]=m*t+L*n+W*s+g*d,f[2]=m*e+L*l+W*p+g*h,f[3]=m*a+L*i+W*c+g*S,f[4]=y*A+v*r+b*o+x*u,f[5]=y*t+v*n+b*s+x*d,f[6]=y*e+v*l+b*p+x*h,f[7]=y*a+v*i+b*c+x*S,f[8]=E*A+k*r+w*o+C*u,f[9]=E*t+k*n+w*s+C*d,f[10]=E*e+k*l+w*p+C*h,f[11]=E*a+k*i+w*c+C*S,f[12]=P*A+T*r+_*o+M*u,f[13]=P*t+T*n+_*s+M*d,f[14]=P*e+T*l+_*p+M*h,f[15]=P*a+T*i+_*c+M*S,this._identityCalculated=!1,this}function f(A){var t=A.props;return this.transform(t[0],t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8],t[9],t[10],t[11],t[12],t[13],t[14],t[15])}function m(){return this._identityCalculated||(this._identity=!(1!==this.props[0]||0!==this.props[1]||0!==this.props[2]||0!==this.props[3]||0!==this.props[4]||1!==this.props[5]||0!==this.props[6]||0!==this.props[7]||0!==this.props[8]||0!==this.props[9]||1!==this.props[10]||0!==this.props[11]||0!==this.props[12]||0!==this.props[13]||0!==this.props[14]||1!==this.props[15]),this._identityCalculated=!0),this._identity}function L(A){for(var t=0;t<16;){if(A.props[t]!==this.props[t])return!1;t+=1}return!0}function W(A){var t;for(t=0;t<16;t+=1)A.props[t]=this.props[t];return A}function g(A){var t;for(t=0;t<16;t+=1)this.props[t]=A[t]}function y(A,t,e){return{x:A*this.props[0]+t*this.props[4]+e*this.props[8]+this.props[12],y:A*this.props[1]+t*this.props[5]+e*this.props[9]+this.props[13],z:A*this.props[2]+t*this.props[6]+e*this.props[10]+this.props[14]}}function v(A,t,e){return A*this.props[0]+t*this.props[4]+e*this.props[8]+this.props[12]}function b(A,t,e){return A*this.props[1]+t*this.props[5]+e*this.props[9]+this.props[13]}function x(A,t,e){return A*this.props[2]+t*this.props[6]+e*this.props[10]+this.props[14]}function E(){var A=this.props[0]*this.props[5]-this.props[1]*this.props[4],t=this.props[5]/A,e=-this.props[1]/A,a=-this.props[4]/A,r=this.props[0]/A,n=(this.props[4]*this.props[13]-this.props[5]*this.props[12])/A,l=-(this.props[0]*this.props[13]-this.props[1]*this.props[12])/A,i=new Matrix;return i.props[0]=t,i.props[1]=e,i.props[4]=a,i.props[5]=r,i.props[12]=n,i.props[13]=l,i}function k(A){return this.getInverseMatrix().applyToPointArray(A[0],A[1],A[2]||0)}function w(A){var t,e=A.length,a=[];for(t=0;t<e;t+=1)a[t]=k(A[t]);return a}function C(A,t,e){var a=createTypedArray(\"float32\",6);if(this.isIdentity())a[0]=A[0],a[1]=A[1],a[2]=t[0],a[3]=t[1],a[4]=e[0],a[5]=e[1];else{var r=this.props[0],n=this.props[1],l=this.props[4],i=this.props[5],o=this.props[12],s=this.props[13];a[0]=A[0]*r+A[1]*l+o,a[1]=A[0]*n+A[1]*i+s,a[2]=t[0]*r+t[1]*l+o,a[3]=t[0]*n+t[1]*i+s,a[4]=e[0]*r+e[1]*l+o,a[5]=e[0]*n+e[1]*i+s}return a}function P(A,t,e){return this.isIdentity()?[A,t,e]:[A*this.props[0]+t*this.props[4]+e*this.props[8]+this.props[12],A*this.props[1]+t*this.props[5]+e*this.props[9]+this.props[13],A*this.props[2]+t*this.props[6]+e*this.props[10]+this.props[14]]}function T(A,t){if(this.isIdentity())return A+\",\"+t;var e=this.props;return Math.round(100*(A*e[0]+t*e[4]+e[12]))/100+\",\"+Math.round(100*(A*e[1]+t*e[5]+e[13]))/100}function _(){for(var A=0,t=this.props,e=\"matrix3d(\";A<16;)e+=a(1e4*t[A])/1e4,e+=15===A?\")\":\",\",A+=1;return e}function M(A){return A<1e-6&&A>0||A>-1e-6&&A<0?a(1e4*A)/1e4:A}function D(){var A=this.props;return\"matrix(\"+M(A[0])+\",\"+M(A[1])+\",\"+M(A[4])+\",\"+M(A[5])+\",\"+M(A[12])+\",\"+M(A[13])+\")\"}return function(){this.reset=r,this.rotate=n,this.rotateX=l,this.rotateY=i,this.rotateZ=o,this.skew=p,this.skewFromAxis=c,this.shear=s,this.scale=u,this.setTransform=d,this.translate=h,this.transform=S,this.multiply=f,this.applyToPoint=y,this.applyToX=v,this.applyToY=b,this.applyToZ=x,this.applyToPointArray=P,this.applyToTriplePoints=C,this.applyToPointStringified=T,this.toCSS=_,this.to2dCSS=D,this.clone=W,this.cloneFromProps=g,this.equals=L,this.inversePoints=w,this.inversePoint=k,this.getInverseMatrix=E,this._t=this.transform,this.isIdentity=m,this._identity=!0,this._identityCalculated=!1,this.props=createTypedArray(\"float32\",16),this.reset()}}();function _typeof$3(A){return(_typeof$3=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(A){return typeof A}:function(A){return A&&\"function\"==typeof Symbol&&A.constructor===Symbol&&A!==Symbol.prototype?\"symbol\":typeof A})(A)}var lottie={};function setLocation(A){setLocationHref(A)}function searchAnimations(){animationManager.searchAnimations()}function setSubframeRendering(A){setSubframeEnabled(A)}function setPrefix(A){setIdPrefix(A)}function loadAnimation(A){return animationManager.loadAnimation(A)}function setQuality(A){if(\"string\"==typeof A)switch(A){case\"high\":setDefaultCurveSegments(200);break;default:case\"medium\":setDefaultCurveSegments(50);break;case\"low\":setDefaultCurveSegments(10)}else!isNaN(A)&&A>1&&setDefaultCurveSegments(A)}function inBrowser(){return\"undefined\"!=typeof navigator}function installPlugin(A,t){\"expressions\"===A&&setExpressionsPlugin(t)}function getFactory(A){switch(A){case\"propertyFactory\":return PropertyFactory;case\"shapePropertyFactory\":return ShapePropertyFactory;case\"matrix\":return Matrix;default:return null}}function checkReady(){\"complete\"===document.readyState&&(clearInterval(readyStateCheckInterval),searchAnimations())}function getQueryVariable(A){for(var t=queryString.split(\"&\"),e=0;e<t.length;e+=1){var a=t[e].split(\"=\");if(decodeURIComponent(a[0])==A)return decodeURIComponent(a[1])}return null}lottie.play=animationManager.play,lottie.pause=animationManager.pause,lottie.setLocationHref=setLocation,lottie.togglePause=animationManager.togglePause,lottie.setSpeed=animationManager.setSpeed,lottie.setDirection=animationManager.setDirection,lottie.stop=animationManager.stop,lottie.searchAnimations=searchAnimations,lottie.registerAnimation=animationManager.registerAnimation,lottie.loadAnimation=loadAnimation,lottie.setSubframeRendering=setSubframeRendering,lottie.resize=animationManager.resize,lottie.goToAndStop=animationManager.goToAndStop,lottie.destroy=animationManager.destroy,lottie.setQuality=setQuality,lottie.inBrowser=inBrowser,lottie.installPlugin=installPlugin,lottie.freeze=animationManager.freeze,lottie.unfreeze=animationManager.unfreeze,lottie.setVolume=animationManager.setVolume,lottie.mute=animationManager.mute,lottie.unmute=animationManager.unmute,lottie.getRegisteredAnimations=animationManager.getRegisteredAnimations,lottie.useWebWorker=setWebWorker,lottie.setIDPrefix=setPrefix,lottie.__getFactory=getFactory,lottie.version=\"5.12.2\";var queryString=\"\",scripts=document.getElementsByTagName(\"script\"),index=scripts.length-1,myScript=scripts[index]||{src:\"\"};queryString=myScript.src?myScript.src.replace(/^[^\\?]+\\??/,\"\"):\"\",getQueryVariable(\"renderer\");var readyStateCheckInterval=setInterval(checkReady,100);try{\"object\"!==_typeof$3(exports$1)&&(window.bodymovin=lottie)}catch(t){}var ShapeModifiers=(t={},e={},t.registerModifier=function(A,t){e[A]||(e[A]=t)},t.getModifier=function(A,t,a){return new e[A](t,a)},t),t,e;function ShapeModifier(){}function TrimModifier(){}function PuckerAndBloatModifier(){}ShapeModifier.prototype.initModifierProperties=function(){},ShapeModifier.prototype.addShapeToModifier=function(){},ShapeModifier.prototype.addShape=function(A){if(!this.closed){A.sh.container.addDynamicProperty(A.sh);var t={shape:A.sh,data:A,localShapeCollection:shapeCollectionPool.newShapeCollection()};this.shapes.push(t),this.addShapeToModifier(t),this._isAnimated&&A.setAsAnimated()}},ShapeModifier.prototype.init=function(A,t){this.shapes=[],this.elem=A,this.initDynamicPropertyContainer(A),this.initModifierProperties(A,t),this.frameId=initialDefaultFrame,this.closed=!1,this.k=!1,this.dynamicProperties.length?this.k=!0:this.getValue(!0)},ShapeModifier.prototype.processKeys=function(){this.elem.globalData.frameId!==this.frameId&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties())},extendPrototype([DynamicPropertyContainer],ShapeModifier),extendPrototype([ShapeModifier],TrimModifier),TrimModifier.prototype.initModifierProperties=function(A,t){this.s=PropertyFactory.getProp(A,t.s,0,.01,this),this.e=PropertyFactory.getProp(A,t.e,0,.01,this),this.o=PropertyFactory.getProp(A,t.o,0,0,this),this.sValue=0,this.eValue=0,this.getValue=this.processKeys,this.m=t.m,this._isAnimated=!!this.s.effectsSequence.length||!!this.e.effectsSequence.length||!!this.o.effectsSequence.length},TrimModifier.prototype.addShapeToModifier=function(A){A.pathsData=[]},TrimModifier.prototype.calculateShapeEdges=function(A,t,e,a,r){var n=[];t<=1?n.push({s:A,e:t}):A>=1?n.push({s:A-1,e:t-1}):(n.push({s:A,e:1}),n.push({s:0,e:t-1}));var l,i,o=[],s=n.length;for(l=0;l<s;l+=1){var p,c;(i=n[l]).e*r<a||i.s*r>a+e||(p=i.s*r<=a?0:(i.s*r-a)/e,c=i.e*r>=a+e?1:(i.e*r-a)/e,o.push([p,c]))}return o.length||o.push([0,0]),o},TrimModifier.prototype.releasePathsData=function(A){var t,e=A.length;for(t=0;t<e;t+=1)segmentsLengthPool.release(A[t]);return A.length=0,A},TrimModifier.prototype.processShapes=function(A){var t,e,a,r;if(this._mdf||A){var n=this.o.v%360/360;if(n<0&&(n+=1),(t=this.s.v>1?1+n:this.s.v<0?0+n:this.s.v+n)>(e=this.e.v>1?1+n:this.e.v<0?0+n:this.e.v+n)){var l=t;t=e,e=l}t=1e-4*Math.round(1e4*t),e=1e-4*Math.round(1e4*e),this.sValue=t,this.eValue=e}else t=this.sValue,e=this.eValue;var i,o,s,p,c,u=this.shapes.length,d=0;if(e===t)for(r=0;r<u;r+=1)this.shapes[r].localShapeCollection.releaseShapes(),this.shapes[r].shape._mdf=!0,this.shapes[r].shape.paths=this.shapes[r].localShapeCollection,this._mdf&&(this.shapes[r].pathsData.length=0);else if(1===e&&0===t||0===e&&1===t){if(this._mdf)for(r=0;r<u;r+=1)this.shapes[r].pathsData.length=0,this.shapes[r].shape._mdf=!0}else{var h,S,f=[];for(r=0;r<u;r+=1)if((h=this.shapes[r]).shape._mdf||this._mdf||A||2===this.m){if(o=(a=h.shape.paths)._length,c=0,!h.shape._mdf&&h.pathsData.length)c=h.totalShapeLength;else{for(s=this.releasePathsData(h.pathsData),i=0;i<o;i+=1)p=bez.getSegmentsLength(a.shapes[i]),s.push(p),c+=p.totalLength;h.totalShapeLength=c,h.pathsData=s}d+=c,h.shape._mdf=!0}else h.shape.paths=h.localShapeCollection;var m,L=t,W=e,g=0;for(r=u-1;r>=0;r-=1)if((h=this.shapes[r]).shape._mdf){for((S=h.localShapeCollection).releaseShapes(),2===this.m&&u>1?(m=this.calculateShapeEdges(t,e,h.totalShapeLength,g,d),g+=h.totalShapeLength):m=[[L,W]],o=m.length,i=0;i<o;i+=1){L=m[i][0],W=m[i][1],f.length=0,W<=1?f.push({s:h.totalShapeLength*L,e:h.totalShapeLength*W}):L>=1?f.push({s:h.totalShapeLength*(L-1),e:h.totalShapeLength*(W-1)}):(f.push({s:h.totalShapeLength*L,e:h.totalShapeLength}),f.push({s:0,e:h.totalShapeLength*(W-1)}));var y=this.addShapes(h,f[0]);if(f[0].s!==f[0].e){if(f.length>1)if(h.shape.paths.shapes[h.shape.paths._length-1].c){var v=y.pop();this.addPaths(y,S),y=this.addShapes(h,f[1],v)}else this.addPaths(y,S),y=this.addShapes(h,f[1]);this.addPaths(y,S)}}h.shape.paths=S}}},TrimModifier.prototype.addPaths=function(A,t){var e,a=A.length;for(e=0;e<a;e+=1)t.addShape(A[e])},TrimModifier.prototype.addSegment=function(A,t,e,a,r,n,l){r.setXYAt(t[0],t[1],\"o\",n),r.setXYAt(e[0],e[1],\"i\",n+1),l&&r.setXYAt(A[0],A[1],\"v\",n),r.setXYAt(a[0],a[1],\"v\",n+1)},TrimModifier.prototype.addSegmentFromArray=function(A,t,e,a){t.setXYAt(A[1],A[5],\"o\",e),t.setXYAt(A[2],A[6],\"i\",e+1),a&&t.setXYAt(A[0],A[4],\"v\",e),t.setXYAt(A[3],A[7],\"v\",e+1)},TrimModifier.prototype.addShapes=function(A,t,e){var a,r,n,l,i,o,s,p,c=A.pathsData,u=A.shape.paths.shapes,d=A.shape.paths._length,h=0,S=[],f=!0;for(e?(i=e._length,p=e._length):(e=shapePool.newElement(),i=0,p=0),S.push(e),a=0;a<d;a+=1){for(o=c[a].lengths,e.c=u[a].c,n=u[a].c?o.length:o.length+1,r=1;r<n;r+=1)if(h+(l=o[r-1]).addedLength<t.s)h+=l.addedLength,e.c=!1;else{if(h>t.e){e.c=!1;break}t.s<=h&&t.e>=h+l.addedLength?(this.addSegment(u[a].v[r-1],u[a].o[r-1],u[a].i[r],u[a].v[r],e,i,f),f=!1):(s=bez.getNewSegment(u[a].v[r-1],u[a].v[r],u[a].o[r-1],u[a].i[r],(t.s-h)/l.addedLength,(t.e-h)/l.addedLength,o[r-1]),this.addSegmentFromArray(s,e,i,f),f=!1,e.c=!1),h+=l.addedLength,i+=1}if(u[a].c&&o.length){if(l=o[r-1],h<=t.e){var m=o[r-1].addedLength;t.s<=h&&t.e>=h+m?(this.addSegment(u[a].v[r-1],u[a].o[r-1],u[a].i[0],u[a].v[0],e,i,f),f=!1):(s=bez.getNewSegment(u[a].v[r-1],u[a].v[0],u[a].o[r-1],u[a].i[0],(t.s-h)/m,(t.e-h)/m,o[r-1]),this.addSegmentFromArray(s,e,i,f),f=!1,e.c=!1)}else e.c=!1;h+=l.addedLength,i+=1}if(e._length&&(e.setXYAt(e.v[p][0],e.v[p][1],\"i\",p),e.setXYAt(e.v[e._length-1][0],e.v[e._length-1][1],\"o\",e._length-1)),h>t.e)break;a<d-1&&(e=shapePool.newElement(),f=!0,S.push(e),i=0)}return S},extendPrototype([ShapeModifier],PuckerAndBloatModifier),PuckerAndBloatModifier.prototype.initModifierProperties=function(A,t){this.getValue=this.processKeys,this.amount=PropertyFactory.getProp(A,t.a,0,null,this),this._isAnimated=!!this.amount.effectsSequence.length},PuckerAndBloatModifier.prototype.processPath=function(A,t){var e=t/100,a=[0,0],r=A._length,n=0;for(n=0;n<r;n+=1)a[0]+=A.v[n][0],a[1]+=A.v[n][1];a[0]/=r,a[1]/=r;var l,i,o,s,p,c,u=shapePool.newElement();for(u.c=A.c,n=0;n<r;n+=1)l=A.v[n][0]+(a[0]-A.v[n][0])*e,i=A.v[n][1]+(a[1]-A.v[n][1])*e,o=A.o[n][0]+(a[0]-A.o[n][0])*-e,s=A.o[n][1]+(a[1]-A.o[n][1])*-e,p=A.i[n][0]+(a[0]-A.i[n][0])*-e,c=A.i[n][1]+(a[1]-A.i[n][1])*-e,u.setTripleAt(l,i,o,s,p,c,n);return u},PuckerAndBloatModifier.prototype.processShapes=function(A){var t,e,a,r,n,l,i=this.shapes.length,o=this.amount.v;if(0!==o)for(e=0;e<i;e+=1){if(l=(n=this.shapes[e]).localShapeCollection,n.shape._mdf||this._mdf||A)for(l.releaseShapes(),n.shape._mdf=!0,t=n.shape.paths.shapes,r=n.shape.paths._length,a=0;a<r;a+=1)l.addShape(this.processPath(t[a],o));n.shape.paths=n.localShapeCollection}this.dynamicProperties.length||(this._mdf=!1)};var TransformPropertyFactory=function(){var A=[0,0];function t(A,t,e){if(this.elem=A,this.frameId=-1,this.propType=\"transform\",this.data=t,this.v=new Matrix,this.pre=new Matrix,this.appliedTransformations=0,this.initDynamicPropertyContainer(e||A),t.p&&t.p.s?(this.px=PropertyFactory.getProp(A,t.p.x,0,0,this),this.py=PropertyFactory.getProp(A,t.p.y,0,0,this),t.p.z&&(this.pz=PropertyFactory.getProp(A,t.p.z,0,0,this))):this.p=PropertyFactory.getProp(A,t.p||{k:[0,0,0]},1,0,this),t.rx){if(this.rx=PropertyFactory.getProp(A,t.rx,0,degToRads,this),this.ry=PropertyFactory.getProp(A,t.ry,0,degToRads,this),this.rz=PropertyFactory.getProp(A,t.rz,0,degToRads,this),t.or.k[0].ti){var a,r=t.or.k.length;for(a=0;a<r;a+=1)t.or.k[a].to=null,t.or.k[a].ti=null}this.or=PropertyFactory.getProp(A,t.or,1,degToRads,this),this.or.sh=!0}else this.r=PropertyFactory.getProp(A,t.r||{k:0},0,degToRads,this);t.sk&&(this.sk=PropertyFactory.getProp(A,t.sk,0,degToRads,this),this.sa=PropertyFactory.getProp(A,t.sa,0,degToRads,this)),this.a=PropertyFactory.getProp(A,t.a||{k:[0,0,0]},1,0,this),this.s=PropertyFactory.getProp(A,t.s||{k:[100,100,100]},1,.01,this),t.o?this.o=PropertyFactory.getProp(A,t.o,0,.01,A):this.o={_mdf:!1,v:1},this._isDirty=!0,this.dynamicProperties.length||this.getValue(!0)}return t.prototype={applyToMatrix:function(A){var t=this._mdf;this.iterateDynamicProperties(),this._mdf=this._mdf||t,this.a&&A.translate(-this.a.v[0],-this.a.v[1],this.a.v[2]),this.s&&A.scale(this.s.v[0],this.s.v[1],this.s.v[2]),this.sk&&A.skewFromAxis(-this.sk.v,this.sa.v),this.r?A.rotate(-this.r.v):A.rotateZ(-this.rz.v).rotateY(this.ry.v).rotateX(this.rx.v).rotateZ(-this.or.v[2]).rotateY(this.or.v[1]).rotateX(this.or.v[0]),this.data.p.s?this.data.p.z?A.translate(this.px.v,this.py.v,-this.pz.v):A.translate(this.px.v,this.py.v,0):A.translate(this.p.v[0],this.p.v[1],-this.p.v[2])},getValue:function(t){if(this.elem.globalData.frameId!==this.frameId){if(this._isDirty&&(this.precalculateMatrix(),this._isDirty=!1),this.iterateDynamicProperties(),this._mdf||t){var e;if(this.v.cloneFromProps(this.pre.props),this.appliedTransformations<1&&this.v.translate(-this.a.v[0],-this.a.v[1],this.a.v[2]),this.appliedTransformations<2&&this.v.scale(this.s.v[0],this.s.v[1],this.s.v[2]),this.sk&&this.appliedTransformations<3&&this.v.skewFromAxis(-this.sk.v,this.sa.v),this.r&&this.appliedTransformations<4?this.v.rotate(-this.r.v):!this.r&&this.appliedTransformations<4&&this.v.rotateZ(-this.rz.v).rotateY(this.ry.v).rotateX(this.rx.v).rotateZ(-this.or.v[2]).rotateY(this.or.v[1]).rotateX(this.or.v[0]),this.autoOriented){var a,r;if(e=this.elem.globalData.frameRate,this.p&&this.p.keyframes&&this.p.getValueAtTime)this.p._caching.lastFrame+this.p.offsetTime<=this.p.keyframes[0].t?(a=this.p.getValueAtTime((this.p.keyframes[0].t+.01)/e,0),r=this.p.getValueAtTime(this.p.keyframes[0].t/e,0)):this.p._caching.lastFrame+this.p.offsetTime>=this.p.keyframes[this.p.keyframes.length-1].t?(a=this.p.getValueAtTime(this.p.keyframes[this.p.keyframes.length-1].t/e,0),r=this.p.getValueAtTime((this.p.keyframes[this.p.keyframes.length-1].t-.05)/e,0)):(a=this.p.pv,r=this.p.getValueAtTime((this.p._caching.lastFrame+this.p.offsetTime-.01)/e,this.p.offsetTime));else if(this.px&&this.px.keyframes&&this.py.keyframes&&this.px.getValueAtTime&&this.py.getValueAtTime){a=[],r=[];var n=this.px,l=this.py;n._caching.lastFrame+n.offsetTime<=n.keyframes[0].t?(a[0]=n.getValueAtTime((n.keyframes[0].t+.01)/e,0),a[1]=l.getValueAtTime((l.keyframes[0].t+.01)/e,0),r[0]=n.getValueAtTime(n.keyframes[0].t/e,0),r[1]=l.getValueAtTime(l.keyframes[0].t/e,0)):n._caching.lastFrame+n.offsetTime>=n.keyframes[n.keyframes.length-1].t?(a[0]=n.getValueAtTime(n.keyframes[n.keyframes.length-1].t/e,0),a[1]=l.getValueAtTime(l.keyframes[l.keyframes.length-1].t/e,0),r[0]=n.getValueAtTime((n.keyframes[n.keyframes.length-1].t-.01)/e,0),r[1]=l.getValueAtTime((l.keyframes[l.keyframes.length-1].t-.01)/e,0)):(a=[n.pv,l.pv],r[0]=n.getValueAtTime((n._caching.lastFrame+n.offsetTime-.01)/e,n.offsetTime),r[1]=l.getValueAtTime((l._caching.lastFrame+l.offsetTime-.01)/e,l.offsetTime))}else a=r=A;this.v.rotate(-Math.atan2(a[1]-r[1],a[0]-r[0]))}this.data.p&&this.data.p.s?this.data.p.z?this.v.translate(this.px.v,this.py.v,-this.pz.v):this.v.translate(this.px.v,this.py.v,0):this.v.translate(this.p.v[0],this.p.v[1],-this.p.v[2])}this.frameId=this.elem.globalData.frameId}},precalculateMatrix:function(){if(this.appliedTransformations=0,this.pre.reset(),!this.a.effectsSequence.length&&(this.pre.translate(-this.a.v[0],-this.a.v[1],this.a.v[2]),this.appliedTransformations=1,!this.s.effectsSequence.length)){if(this.pre.scale(this.s.v[0],this.s.v[1],this.s.v[2]),this.appliedTransformations=2,this.sk){if(this.sk.effectsSequence.length||this.sa.effectsSequence.length)return;this.pre.skewFromAxis(-this.sk.v,this.sa.v),this.appliedTransformations=3}this.r?this.r.effectsSequence.length||(this.pre.rotate(-this.r.v),this.appliedTransformations=4):this.rz.effectsSequence.length||this.ry.effectsSequence.length||this.rx.effectsSequence.length||this.or.effectsSequence.length||(this.pre.rotateZ(-this.rz.v).rotateY(this.ry.v).rotateX(this.rx.v).rotateZ(-this.or.v[2]).rotateY(this.or.v[1]).rotateX(this.or.v[0]),this.appliedTransformations=4)}},autoOrient:function(){}},extendPrototype([DynamicPropertyContainer],t),t.prototype.addDynamicProperty=function(A){this._addDynamicProperty(A),this.elem.addDynamicProperty(A),this._isDirty=!0},t.prototype._addDynamicProperty=DynamicPropertyContainer.prototype.addDynamicProperty,{getTransformProperty:function(A,e,a){return new t(A,e,a)}}}();function RepeaterModifier(){}function RoundCornersModifier(){}function floatEqual(A,t){return 1e5*Math.abs(A-t)<=Math.min(Math.abs(A),Math.abs(t))}function floatZero(A){return Math.abs(A)<=1e-5}function lerp(A,t,e){return A*(1-e)+t*e}function lerpPoint(A,t,e){return[lerp(A[0],t[0],e),lerp(A[1],t[1],e)]}function quadRoots(A,t,e){if(0===A)return[];var a=t*t-4*A*e;if(a<0)return[];var r=-t/(2*A);if(0===a)return[r];var n=Math.sqrt(a)/(2*A);return[r-n,r+n]}function polynomialCoefficients(A,t,e,a){return[3*t-A-3*e+a,3*A-6*t+3*e,-3*A+3*t,A]}function singlePoint(A){return new PolynomialBezier(A,A,A,A,!1)}function PolynomialBezier(A,t,e,a,r){r&&pointEqual(A,t)&&(t=lerpPoint(A,a,1/3)),r&&pointEqual(e,a)&&(e=lerpPoint(A,a,2/3));var n=polynomialCoefficients(A[0],t[0],e[0],a[0]),l=polynomialCoefficients(A[1],t[1],e[1],a[1]);this.a=[n[0],l[0]],this.b=[n[1],l[1]],this.c=[n[2],l[2]],this.d=[n[3],l[3]],this.points=[A,t,e,a]}function extrema(A,t){var e=A.points[0][t],a=A.points[A.points.length-1][t];if(e>a){var r=a;a=e,e=r}for(var n=quadRoots(3*A.a[t],2*A.b[t],A.c[t]),l=0;l<n.length;l+=1)if(n[l]>0&&n[l]<1){var i=A.point(n[l])[t];i<e?e=i:i>a&&(a=i)}return{min:e,max:a}}function intersectData(A,t,e){var a=A.boundingBox();return{cx:a.cx,cy:a.cy,width:a.width,height:a.height,bez:A,t:(t+e)/2,t1:t,t2:e}}function splitData(A){var t=A.bez.split(.5);return[intersectData(t[0],A.t1,A.t),intersectData(t[1],A.t,A.t2)]}function boxIntersect(A,t){return 2*Math.abs(A.cx-t.cx)<A.width+t.width&&2*Math.abs(A.cy-t.cy)<A.height+t.height}function intersectsImpl(A,t,e,a,r,n){if(boxIntersect(A,t))if(e>=n||A.width<=a&&A.height<=a&&t.width<=a&&t.height<=a)r.push([A.t,t.t]);else{var l=splitData(A),i=splitData(t);intersectsImpl(l[0],i[0],e+1,a,r,n),intersectsImpl(l[0],i[1],e+1,a,r,n),intersectsImpl(l[1],i[0],e+1,a,r,n),intersectsImpl(l[1],i[1],e+1,a,r,n)}}function crossProduct(A,t){return[A[1]*t[2]-A[2]*t[1],A[2]*t[0]-A[0]*t[2],A[0]*t[1]-A[1]*t[0]]}function lineIntersection(A,t,e,a){var r=[A[0],A[1],1],n=[t[0],t[1],1],l=[e[0],e[1],1],i=[a[0],a[1],1],o=crossProduct(crossProduct(r,n),crossProduct(l,i));return floatZero(o[2])?null:[o[0]/o[2],o[1]/o[2]]}function polarOffset(A,t,e){return[A[0]+Math.cos(t)*e,A[1]-Math.sin(t)*e]}function pointDistance(A,t){return Math.hypot(A[0]-t[0],A[1]-t[1])}function pointEqual(A,t){return floatEqual(A[0],t[0])&&floatEqual(A[1],t[1])}function ZigZagModifier(){}function setPoint(A,t,e,a,r,n,l){var i=e-Math.PI/2,o=e+Math.PI/2,s=t[0]+Math.cos(e)*a*r,p=t[1]-Math.sin(e)*a*r;A.setTripleAt(s,p,s+Math.cos(i)*n,p-Math.sin(i)*n,s+Math.cos(o)*l,p-Math.sin(o)*l,A.length())}function getPerpendicularVector(A,t){var e=[t[0]-A[0],t[1]-A[1]],a=.5*-Math.PI;return[Math.cos(a)*e[0]-Math.sin(a)*e[1],Math.sin(a)*e[0]+Math.cos(a)*e[1]]}function getProjectingAngle(A,t){var e=0===t?A.length()-1:t-1,a=(t+1)%A.length(),r=getPerpendicularVector(A.v[e],A.v[a]);return Math.atan2(0,1)-Math.atan2(r[1],r[0])}function zigZagCorner(A,t,e,a,r,n,l){var i=getProjectingAngle(t,e),o=t.v[e%t._length],s=t.v[0===e?t._length-1:e-1],p=t.v[(e+1)%t._length],c=2===n?Math.sqrt(Math.pow(o[0]-s[0],2)+Math.pow(o[1]-s[1],2)):0,u=2===n?Math.sqrt(Math.pow(o[0]-p[0],2)+Math.pow(o[1]-p[1],2)):0;setPoint(A,t.v[e%t._length],i,l,a,u/(2*(r+1)),c/(2*(r+1)))}function zigZagSegment(A,t,e,a,r,n){for(var l=0;l<a;l+=1){var i=(l+1)/(a+1),o=2===r?Math.sqrt(Math.pow(t.points[3][0]-t.points[0][0],2)+Math.pow(t.points[3][1]-t.points[0][1],2)):0,s=t.normalAngle(i);setPoint(A,t.point(i),s,n,e,o/(2*(a+1)),o/(2*(a+1))),n=-n}return n}function linearOffset(A,t,e){var a=Math.atan2(t[0]-A[0],t[1]-A[1]);return[polarOffset(A,a,e),polarOffset(t,a,e)]}function offsetSegment(A,t){var e,a,r,n,l,i,o;e=(o=linearOffset(A.points[0],A.points[1],t))[0],a=o[1],r=(o=linearOffset(A.points[1],A.points[2],t))[0],n=o[1],l=(o=linearOffset(A.points[2],A.points[3],t))[0],i=o[1];var s=lineIntersection(e,a,r,n);null===s&&(s=a);var p=lineIntersection(l,i,r,n);return null===p&&(p=l),new PolynomialBezier(e,s,p,i)}function joinLines(A,t,e,a,r){var n=t.points[3],l=e.points[0];if(3===a)return n;if(pointEqual(n,l))return n;if(2===a){var i=-t.tangentAngle(1),o=-e.tangentAngle(0)+Math.PI,s=lineIntersection(n,polarOffset(n,i+Math.PI/2,100),l,polarOffset(l,i+Math.PI/2,100)),p=s?pointDistance(s,n):pointDistance(n,l)/2,c=polarOffset(n,i,2*p*roundCorner);return A.setXYAt(c[0],c[1],\"o\",A.length()-1),c=polarOffset(l,o,2*p*roundCorner),A.setTripleAt(l[0],l[1],l[0],l[1],c[0],c[1],A.length()),l}var u=lineIntersection(pointEqual(n,t.points[2])?t.points[0]:t.points[2],n,l,pointEqual(l,e.points[1])?e.points[3]:e.points[1]);return u&&pointDistance(u,n)<r?(A.setTripleAt(u[0],u[1],u[0],u[1],u[0],u[1],A.length()),u):n}function getIntersection(A,t){var e=A.intersections(t);return e.length&&floatEqual(e[0][0],1)&&e.shift(),e.length?e[0]:null}function pruneSegmentIntersection(A,t){var e=A.slice(),a=t.slice(),r=getIntersection(A[A.length-1],t[0]);return r&&(e[A.length-1]=A[A.length-1].split(r[0])[0],a[0]=t[0].split(r[1])[1]),A.length>1&&t.length>1&&(r=getIntersection(A[0],t[t.length-1]))?[[A[0].split(r[0])[0]],[t[t.length-1].split(r[1])[1]]]:[e,a]}function pruneIntersections(A){for(var t,e=1;e<A.length;e+=1)t=pruneSegmentIntersection(A[e-1],A[e]),A[e-1]=t[0],A[e]=t[1];return A.length>1&&(t=pruneSegmentIntersection(A[A.length-1],A[0]),A[A.length-1]=t[0],A[0]=t[1]),A}function offsetSegmentSplit(A,t){var e,a,r,n,l=A.inflectionPoints();if(0===l.length)return[offsetSegment(A,t)];if(1===l.length||floatEqual(l[1],1))return e=(r=A.split(l[0]))[0],a=r[1],[offsetSegment(e,t),offsetSegment(a,t)];e=(r=A.split(l[0]))[0];var i=(l[1]-l[0])/(1-l[0]);return n=(r=r[1].split(i))[0],a=r[1],[offsetSegment(e,t),offsetSegment(n,t),offsetSegment(a,t)]}function OffsetPathModifier(){}function getFontProperties(A){for(var t=A.fStyle?A.fStyle.split(\" \"):[],e=\"normal\",a=\"normal\",r=t.length,n=0;n<r;n+=1)switch(t[n].toLowerCase()){case\"italic\":a=\"italic\";break;case\"bold\":e=\"700\";break;case\"black\":e=\"900\";break;case\"medium\":e=\"500\";break;case\"regular\":case\"normal\":e=\"400\";break;case\"light\":case\"thin\":e=\"200\"}return{style:a,weight:A.fWeight||e}}extendPrototype([ShapeModifier],RepeaterModifier),RepeaterModifier.prototype.initModifierProperties=function(A,t){this.getValue=this.processKeys,this.c=PropertyFactory.getProp(A,t.c,0,null,this),this.o=PropertyFactory.getProp(A,t.o,0,null,this),this.tr=TransformPropertyFactory.getTransformProperty(A,t.tr,this),this.so=PropertyFactory.getProp(A,t.tr.so,0,.01,this),this.eo=PropertyFactory.getProp(A,t.tr.eo,0,.01,this),this.data=t,this.dynamicProperties.length||this.getValue(!0),this._isAnimated=!!this.dynamicProperties.length,this.pMatrix=new Matrix,this.rMatrix=new Matrix,this.sMatrix=new Matrix,this.tMatrix=new Matrix,this.matrix=new Matrix},RepeaterModifier.prototype.applyTransforms=function(A,t,e,a,r,n){var l=n?-1:1,i=a.s.v[0]+(1-a.s.v[0])*(1-r),o=a.s.v[1]+(1-a.s.v[1])*(1-r);A.translate(a.p.v[0]*l*r,a.p.v[1]*l*r,a.p.v[2]),t.translate(-a.a.v[0],-a.a.v[1],a.a.v[2]),t.rotate(-a.r.v*l*r),t.translate(a.a.v[0],a.a.v[1],a.a.v[2]),e.translate(-a.a.v[0],-a.a.v[1],a.a.v[2]),e.scale(n?1/i:i,n?1/o:o),e.translate(a.a.v[0],a.a.v[1],a.a.v[2])},RepeaterModifier.prototype.init=function(A,t,e,a){for(this.elem=A,this.arr=t,this.pos=e,this.elemsData=a,this._currentCopies=0,this._elements=[],this._groups=[],this.frameId=-1,this.initDynamicPropertyContainer(A),this.initModifierProperties(A,t[e]);e>0;)e-=1,this._elements.unshift(t[e]);this.dynamicProperties.length?this.k=!0:this.getValue(!0)},RepeaterModifier.prototype.resetElements=function(A){var t,e=A.length;for(t=0;t<e;t+=1)A[t]._processed=!1,\"gr\"===A[t].ty&&this.resetElements(A[t].it)},RepeaterModifier.prototype.cloneElements=function(A){var t=JSON.parse(JSON.stringify(A));return this.resetElements(t),t},RepeaterModifier.prototype.changeGroupRender=function(A,t){var e,a=A.length;for(e=0;e<a;e+=1)A[e]._render=t,\"gr\"===A[e].ty&&this.changeGroupRender(A[e].it,t)},RepeaterModifier.prototype.processShapes=function(A){var t,e,a,r,n,l=!1;if(this._mdf||A){var i,o=Math.ceil(this.c.v);if(this._groups.length<o){for(;this._groups.length<o;){var s={it:this.cloneElements(this._elements),ty:\"gr\"};s.it.push({a:{a:0,ix:1,k:[0,0]},nm:\"Transform\",o:{a:0,ix:7,k:100},p:{a:0,ix:2,k:[0,0]},r:{a:1,ix:6,k:[{s:0,e:0,t:0},{s:0,e:0,t:1}]},s:{a:0,ix:3,k:[100,100]},sa:{a:0,ix:5,k:0},sk:{a:0,ix:4,k:0},ty:\"tr\"}),this.arr.splice(0,0,s),this._groups.splice(0,0,s),this._currentCopies+=1}this.elem.reloadShapes(),l=!0}for(n=0,a=0;a<=this._groups.length-1;a+=1){if(i=n<o,this._groups[a]._render=i,this.changeGroupRender(this._groups[a].it,i),!i){var p=this.elemsData[a].it,c=p[p.length-1];0!==c.transform.op.v?(c.transform.op._mdf=!0,c.transform.op.v=0):c.transform.op._mdf=!1}n+=1}this._currentCopies=o;var u=this.o.v,d=u%1,h=u>0?Math.floor(u):Math.ceil(u),S=this.pMatrix.props,f=this.rMatrix.props,m=this.sMatrix.props;this.pMatrix.reset(),this.rMatrix.reset(),this.sMatrix.reset(),this.tMatrix.reset(),this.matrix.reset();var L,W,g=0;if(u>0){for(;g<h;)this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,1,!1),g+=1;d&&(this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,d,!1),g+=d)}else if(u<0){for(;g>h;)this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,1,!0),g-=1;d&&(this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,-d,!0),g-=d)}for(a=1===this.data.m?0:this._currentCopies-1,r=1===this.data.m?1:-1,n=this._currentCopies;n;){if(W=(e=(t=this.elemsData[a].it)[t.length-1].transform.mProps.v.props).length,t[t.length-1].transform.mProps._mdf=!0,t[t.length-1].transform.op._mdf=!0,t[t.length-1].transform.op.v=1===this._currentCopies?this.so.v:this.so.v+(this.eo.v-this.so.v)*(a/(this._currentCopies-1)),0!==g){for((0!==a&&1===r||a!==this._currentCopies-1&&-1===r)&&this.applyTransforms(this.pMatrix,this.rMatrix,this.sMatrix,this.tr,1,!1),this.matrix.transform(f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7],f[8],f[9],f[10],f[11],f[12],f[13],f[14],f[15]),this.matrix.transform(m[0],m[1],m[2],m[3],m[4],m[5],m[6],m[7],m[8],m[9],m[10],m[11],m[12],m[13],m[14],m[15]),this.matrix.transform(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[8],S[9],S[10],S[11],S[12],S[13],S[14],S[15]),L=0;L<W;L+=1)e[L]=this.matrix.props[L];this.matrix.reset()}else for(this.matrix.reset(),L=0;L<W;L+=1)e[L]=this.matrix.props[L];g+=1,n-=1,a+=r}}else for(n=this._currentCopies,a=0,r=1;n;)e=(t=this.elemsData[a].it)[t.length-1].transform.mProps.v.props,t[t.length-1].transform.mProps._mdf=!1,t[t.length-1].transform.op._mdf=!1,n-=1,a+=r;return l},RepeaterModifier.prototype.addShape=function(){},extendPrototype([ShapeModifier],RoundCornersModifier),RoundCornersModifier.prototype.initModifierProperties=function(A,t){this.getValue=this.processKeys,this.rd=PropertyFactory.getProp(A,t.r,0,null,this),this._isAnimated=!!this.rd.effectsSequence.length},RoundCornersModifier.prototype.processPath=function(A,t){var e,a=shapePool.newElement();a.c=A.c;var r,n,l,i,o,s,p,c,u,d,h,S,f=A._length,m=0;for(e=0;e<f;e+=1)r=A.v[e],l=A.o[e],n=A.i[e],r[0]===l[0]&&r[1]===l[1]&&r[0]===n[0]&&r[1]===n[1]?0!==e&&e!==f-1||A.c?(i=0===e?A.v[f-1]:A.v[e-1],s=(o=Math.sqrt(Math.pow(r[0]-i[0],2)+Math.pow(r[1]-i[1],2)))?Math.min(o/2,t)/o:0,p=h=r[0]+(i[0]-r[0])*s,c=S=r[1]-(r[1]-i[1])*s,u=p-(p-r[0])*roundCorner,d=c-(c-r[1])*roundCorner,a.setTripleAt(p,c,u,d,h,S,m),m+=1,i=e===f-1?A.v[0]:A.v[e+1],s=(o=Math.sqrt(Math.pow(r[0]-i[0],2)+Math.pow(r[1]-i[1],2)))?Math.min(o/2,t)/o:0,p=u=r[0]+(i[0]-r[0])*s,c=d=r[1]+(i[1]-r[1])*s,h=p-(p-r[0])*roundCorner,S=c-(c-r[1])*roundCorner,a.setTripleAt(p,c,u,d,h,S,m),m+=1):(a.setTripleAt(r[0],r[1],l[0],l[1],n[0],n[1],m),m+=1):(a.setTripleAt(A.v[e][0],A.v[e][1],A.o[e][0],A.o[e][1],A.i[e][0],A.i[e][1],m),m+=1);return a},RoundCornersModifier.prototype.processShapes=function(A){var t,e,a,r,n,l,i=this.shapes.length,o=this.rd.v;if(0!==o)for(e=0;e<i;e+=1){if(l=(n=this.shapes[e]).localShapeCollection,n.shape._mdf||this._mdf||A)for(l.releaseShapes(),n.shape._mdf=!0,t=n.shape.paths.shapes,r=n.shape.paths._length,a=0;a<r;a+=1)l.addShape(this.processPath(t[a],o));n.shape.paths=n.localShapeCollection}this.dynamicProperties.length||(this._mdf=!1)},PolynomialBezier.prototype.point=function(A){return[((this.a[0]*A+this.b[0])*A+this.c[0])*A+this.d[0],((this.a[1]*A+this.b[1])*A+this.c[1])*A+this.d[1]]},PolynomialBezier.prototype.derivative=function(A){return[(3*A*this.a[0]+2*this.b[0])*A+this.c[0],(3*A*this.a[1]+2*this.b[1])*A+this.c[1]]},PolynomialBezier.prototype.tangentAngle=function(A){var t=this.derivative(A);return Math.atan2(t[1],t[0])},PolynomialBezier.prototype.normalAngle=function(A){var t=this.derivative(A);return Math.atan2(t[0],t[1])},PolynomialBezier.prototype.inflectionPoints=function(){var A=this.a[1]*this.b[0]-this.a[0]*this.b[1];if(floatZero(A))return[];var t=-.5*(this.a[1]*this.c[0]-this.a[0]*this.c[1])/A,e=t*t-1/3*(this.b[1]*this.c[0]-this.b[0]*this.c[1])/A;if(e<0)return[];var a=Math.sqrt(e);return floatZero(a)?a>0&&a<1?[t]:[]:[t-a,t+a].filter(function(A){return A>0&&A<1})},PolynomialBezier.prototype.split=function(A){if(A<=0)return[singlePoint(this.points[0]),this];if(A>=1)return[this,singlePoint(this.points[this.points.length-1])];var t=lerpPoint(this.points[0],this.points[1],A),e=lerpPoint(this.points[1],this.points[2],A),a=lerpPoint(this.points[2],this.points[3],A),r=lerpPoint(t,e,A),n=lerpPoint(e,a,A),l=lerpPoint(r,n,A);return[new PolynomialBezier(this.points[0],t,r,l,!0),new PolynomialBezier(l,n,a,this.points[3],!0)]},PolynomialBezier.prototype.bounds=function(){return{x:extrema(this,0),y:extrema(this,1)}},PolynomialBezier.prototype.boundingBox=function(){var A=this.bounds();return{left:A.x.min,right:A.x.max,top:A.y.min,bottom:A.y.max,width:A.x.max-A.x.min,height:A.y.max-A.y.min,cx:(A.x.max+A.x.min)/2,cy:(A.y.max+A.y.min)/2}},PolynomialBezier.prototype.intersections=function(A,t,e){void 0===t&&(t=2),void 0===e&&(e=7);var a=[];return intersectsImpl(intersectData(this,0,1),intersectData(A,0,1),0,t,a,e),a},PolynomialBezier.shapeSegment=function(A,t){var e=(t+1)%A.length();return new PolynomialBezier(A.v[t],A.o[t],A.i[e],A.v[e],!0)},PolynomialBezier.shapeSegmentInverted=function(A,t){var e=(t+1)%A.length();return new PolynomialBezier(A.v[e],A.i[e],A.o[t],A.v[t],!0)},extendPrototype([ShapeModifier],ZigZagModifier),ZigZagModifier.prototype.initModifierProperties=function(A,t){this.getValue=this.processKeys,this.amplitude=PropertyFactory.getProp(A,t.s,0,null,this),this.frequency=PropertyFactory.getProp(A,t.r,0,null,this),this.pointsType=PropertyFactory.getProp(A,t.pt,0,null,this),this._isAnimated=0!==this.amplitude.effectsSequence.length||0!==this.frequency.effectsSequence.length||0!==this.pointsType.effectsSequence.length},ZigZagModifier.prototype.processPath=function(A,t,e,a){var r=A._length,n=shapePool.newElement();if(n.c=A.c,A.c||(r-=1),0===r)return n;var l=-1,i=PolynomialBezier.shapeSegment(A,0);zigZagCorner(n,A,0,t,e,a,l);for(var o=0;o<r;o+=1)l=zigZagSegment(n,i,t,e,a,-l),i=o!==r-1||A.c?PolynomialBezier.shapeSegment(A,(o+1)%r):null,zigZagCorner(n,A,o+1,t,e,a,l);return n},ZigZagModifier.prototype.processShapes=function(A){var t,e,a,r,n,l,i=this.shapes.length,o=this.amplitude.v,s=Math.max(0,Math.round(this.frequency.v)),p=this.pointsType.v;if(0!==o)for(e=0;e<i;e+=1){if(l=(n=this.shapes[e]).localShapeCollection,n.shape._mdf||this._mdf||A)for(l.releaseShapes(),n.shape._mdf=!0,t=n.shape.paths.shapes,r=n.shape.paths._length,a=0;a<r;a+=1)l.addShape(this.processPath(t[a],o,s,p));n.shape.paths=n.localShapeCollection}this.dynamicProperties.length||(this._mdf=!1)},extendPrototype([ShapeModifier],OffsetPathModifier),OffsetPathModifier.prototype.initModifierProperties=function(A,t){this.getValue=this.processKeys,this.amount=PropertyFactory.getProp(A,t.a,0,null,this),this.miterLimit=PropertyFactory.getProp(A,t.ml,0,null,this),this.lineJoin=t.lj,this._isAnimated=0!==this.amount.effectsSequence.length},OffsetPathModifier.prototype.processPath=function(A,t,e,a){var r=shapePool.newElement();r.c=A.c;var n,l,i,o=A.length();A.c||(o-=1);var s=[];for(n=0;n<o;n+=1)i=PolynomialBezier.shapeSegment(A,n),s.push(offsetSegmentSplit(i,t));if(!A.c)for(n=o-1;n>=0;n-=1)i=PolynomialBezier.shapeSegmentInverted(A,n),s.push(offsetSegmentSplit(i,t));s=pruneIntersections(s);var p=null,c=null;for(n=0;n<s.length;n+=1){var u=s[n];for(c&&(p=joinLines(r,c,u[0],e,a)),c=u[u.length-1],l=0;l<u.length;l+=1)i=u[l],p&&pointEqual(i.points[0],p)?r.setXYAt(i.points[1][0],i.points[1][1],\"o\",r.length()-1):r.setTripleAt(i.points[0][0],i.points[0][1],i.points[1][0],i.points[1][1],i.points[0][0],i.points[0][1],r.length()),r.setTripleAt(i.points[3][0],i.points[3][1],i.points[3][0],i.points[3][1],i.points[2][0],i.points[2][1],r.length()),p=i.points[3]}return s.length&&joinLines(r,c,s[0][0],e,a),r},OffsetPathModifier.prototype.processShapes=function(A){var t,e,a,r,n,l,i=this.shapes.length,o=this.amount.v,s=this.miterLimit.v,p=this.lineJoin;if(0!==o)for(e=0;e<i;e+=1){if(l=(n=this.shapes[e]).localShapeCollection,n.shape._mdf||this._mdf||A)for(l.releaseShapes(),n.shape._mdf=!0,t=n.shape.paths.shapes,r=n.shape.paths._length,a=0;a<r;a+=1)l.addShape(this.processPath(t[a],o,p,s));n.shape.paths=n.localShapeCollection}this.dynamicProperties.length||(this._mdf=!1)};var FontManager=function(){var A={w:0,size:0,shapes:[],data:{shapes:[]}},t=[];t=t.concat([2304,2305,2306,2307,2362,2363,2364,2364,2366,2367,2368,2369,2370,2371,2372,2373,2374,2375,2376,2377,2378,2379,2380,2381,2382,2383,2387,2388,2389,2390,2391,2402,2403]);var e=[\"d83cdffb\",\"d83cdffc\",\"d83cdffd\",\"d83cdffe\",\"d83cdfff\"];function a(A,t){var e=createTag(\"span\");e.setAttribute(\"aria-hidden\",!0),e.style.fontFamily=t;var a=createTag(\"span\");a.innerText=\"giItT1WQy@!-/#\",e.style.position=\"absolute\",e.style.left=\"-10000px\",e.style.top=\"-10000px\",e.style.fontSize=\"300px\",e.style.fontVariant=\"normal\",e.style.fontStyle=\"normal\",e.style.fontWeight=\"normal\",e.style.letterSpacing=\"0\",e.appendChild(a),document.body.appendChild(e);var r=a.offsetWidth;return a.style.fontFamily=function(A){var t,e=A.split(\",\"),a=e.length,r=[];for(t=0;t<a;t+=1)\"sans-serif\"!==e[t]&&\"monospace\"!==e[t]&&r.push(e[t]);return r.join(\",\")}(A)+\", \"+t,{node:a,w:r,parent:e}}function r(A,t){var e,a=document.body&&t?\"svg\":\"canvas\",r=getFontProperties(A);if(\"svg\"===a){var n=createNS(\"text\");n.style.fontSize=\"100px\",n.setAttribute(\"font-family\",A.fFamily),n.setAttribute(\"font-style\",r.style),n.setAttribute(\"font-weight\",r.weight),n.textContent=\"1\",A.fClass?(n.style.fontFamily=\"inherit\",n.setAttribute(\"class\",A.fClass)):n.style.fontFamily=A.fFamily,t.appendChild(n),e=n}else{var l=new OffscreenCanvas(500,500).getContext(\"2d\");l.font=r.style+\" \"+r.weight+\" 100px \"+A.fFamily,e=l}return{measureText:function(A){return\"svg\"===a?(e.textContent=A,e.getComputedTextLength()):e.measureText(A).width}}}function n(A){var t=0,e=A.charCodeAt(0);if(e>=55296&&e<=56319){var a=A.charCodeAt(1);a>=56320&&a<=57343&&(t=1024*(e-55296)+a-56320+65536)}return t}function l(A){var t=n(A);return t>=127462&&t<=127487}var i=function(){this.fonts=[],this.chars=null,this.typekitLoaded=0,this.isLoaded=!1,this._warned=!1,this.initTime=Date.now(),this.setIsLoadedBinded=this.setIsLoaded.bind(this),this.checkLoadedFontsBinded=this.checkLoadedFonts.bind(this)};return i.isModifier=function(A,t){var a=A.toString(16)+t.toString(16);return-1!==e.indexOf(a)},i.isZeroWidthJoiner=function(A){return 8205===A},i.isFlagEmoji=function(A){return l(A.substr(0,2))&&l(A.substr(2,2))},i.isRegionalCode=l,i.isCombinedCharacter=function(A){return-1!==t.indexOf(A)},i.isRegionalFlag=function(A,t){var e=n(A.substr(t,2));if(127988!==e)return!1;var a=0;for(t+=2;a<5;){if((e=n(A.substr(t,2)))<917601||e>917626)return!1;a+=1,t+=2}return 917631===n(A.substr(t,2))},i.isVariationSelector=function(A){return 65039===A},i.BLACK_FLAG_CODE_POINT=127988,i.prototype={addChars:function(A){if(A){var t;this.chars||(this.chars=[]);var e,a,r=A.length,n=this.chars.length;for(t=0;t<r;t+=1){for(e=0,a=!1;e<n;)this.chars[e].style===A[t].style&&this.chars[e].fFamily===A[t].fFamily&&this.chars[e].ch===A[t].ch&&(a=!0),e+=1;a||(this.chars.push(A[t]),n+=1)}}},addFonts:function(A,t){if(A){if(this.chars)return this.isLoaded=!0,void(this.fonts=A.list);if(!document.body)return this.isLoaded=!0,A.list.forEach(function(A){A.helper=r(A),A.cache={}}),void(this.fonts=A.list);var e,n=A.list,l=n.length,i=l;for(e=0;e<l;e+=1){var o,s,p=!0;if(n[e].loaded=!1,n[e].monoCase=a(n[e].fFamily,\"monospace\"),n[e].sansCase=a(n[e].fFamily,\"sans-serif\"),n[e].fPath){if(\"p\"===n[e].fOrigin||3===n[e].origin){if((o=document.querySelectorAll('style[f-forigin=\"p\"][f-family=\"'+n[e].fFamily+'\"], style[f-origin=\"3\"][f-family=\"'+n[e].fFamily+'\"]')).length>0&&(p=!1),p){var c=createTag(\"style\");c.setAttribute(\"f-forigin\",n[e].fOrigin),c.setAttribute(\"f-origin\",n[e].origin),c.setAttribute(\"f-family\",n[e].fFamily),c.type=\"text/css\",c.innerText=\"@font-face {font-family: \"+n[e].fFamily+\"; font-style: normal; src: url('\"+n[e].fPath+\"');}\",t.appendChild(c)}}else if(\"g\"===n[e].fOrigin||1===n[e].origin){for(o=document.querySelectorAll('link[f-forigin=\"g\"], link[f-origin=\"1\"]'),s=0;s<o.length;s+=1)-1!==o[s].href.indexOf(n[e].fPath)&&(p=!1);if(p){var u=createTag(\"link\");u.setAttribute(\"f-forigin\",n[e].fOrigin),u.setAttribute(\"f-origin\",n[e].origin),u.type=\"text/css\",u.rel=\"stylesheet\",u.href=n[e].fPath,document.body.appendChild(u)}}else if(\"t\"===n[e].fOrigin||2===n[e].origin){for(o=document.querySelectorAll('script[f-forigin=\"t\"], script[f-origin=\"2\"]'),s=0;s<o.length;s+=1)n[e].fPath===o[s].src&&(p=!1);if(p){var d=createTag(\"link\");d.setAttribute(\"f-forigin\",n[e].fOrigin),d.setAttribute(\"f-origin\",n[e].origin),d.setAttribute(\"rel\",\"stylesheet\"),d.setAttribute(\"href\",n[e].fPath),t.appendChild(d)}}}else n[e].loaded=!0,i-=1;n[e].helper=r(n[e],t),n[e].cache={},this.fonts.push(n[e])}0===i?this.isLoaded=!0:setTimeout(this.checkLoadedFonts.bind(this),100)}else this.isLoaded=!0},getCharData:function(t,e,a){for(var r=0,n=this.chars.length;r<n;){if(this.chars[r].ch===t&&this.chars[r].style===e&&this.chars[r].fFamily===a)return this.chars[r];r+=1}return(\"string\"==typeof t&&13!==t.charCodeAt(0)||!t)&&console&&console.warn&&!this._warned&&(this._warned=!0),A},getFontByName:function(A){for(var t=0,e=this.fonts.length;t<e;){if(this.fonts[t].fName===A)return this.fonts[t];t+=1}return this.fonts[0]},measureText:function(A,t,e){var a=this.getFontByName(t),r=A;if(!a.cache[r]){var n=a.helper;if(\" \"===A){var l=n.measureText(\"|\"+A+\"|\"),i=n.measureText(\"||\");a.cache[r]=(l-i)/100}else a.cache[r]=n.measureText(A)/100}return a.cache[r]*e},checkLoadedFonts:function(){var A,t,e,a=this.fonts.length,r=a;for(A=0;A<a;A+=1)this.fonts[A].loaded?r-=1:\"n\"===this.fonts[A].fOrigin||0===this.fonts[A].origin?this.fonts[A].loaded=!0:(t=this.fonts[A].monoCase.node,e=this.fonts[A].monoCase.w,t.offsetWidth!==e?(r-=1,this.fonts[A].loaded=!0):(t=this.fonts[A].sansCase.node,e=this.fonts[A].sansCase.w,t.offsetWidth!==e&&(r-=1,this.fonts[A].loaded=!0)),this.fonts[A].loaded&&(this.fonts[A].sansCase.parent.parentNode.removeChild(this.fonts[A].sansCase.parent),this.fonts[A].monoCase.parent.parentNode.removeChild(this.fonts[A].monoCase.parent)));0!==r&&Date.now()-this.initTime<5e3?setTimeout(this.checkLoadedFontsBinded,20):setTimeout(this.setIsLoadedBinded,10)},setIsLoaded:function(){this.isLoaded=!0}},i}();function SlotManager(A){this.animationData=A}function slotFactory(A){return new SlotManager(A)}function RenderableElement(){}SlotManager.prototype.getProp=function(A){return this.animationData.slots&&this.animationData.slots[A.sid]?Object.assign(A,this.animationData.slots[A.sid].p):A},RenderableElement.prototype={initRenderable:function(){this.isInRange=!1,this.hidden=!1,this.isTransparent=!1,this.renderableComponents=[]},addRenderableComponent:function(A){-1===this.renderableComponents.indexOf(A)&&this.renderableComponents.push(A)},removeRenderableComponent:function(A){-1!==this.renderableComponents.indexOf(A)&&this.renderableComponents.splice(this.renderableComponents.indexOf(A),1)},prepareRenderableFrame:function(A){this.checkLayerLimits(A)},checkTransparency:function(){this.finalTransform.mProp.o.v<=0?!this.isTransparent&&this.globalData.renderConfig.hideOnTransparent&&(this.isTransparent=!0,this.hide()):this.isTransparent&&(this.isTransparent=!1,this.show())},checkLayerLimits:function(A){this.data.ip-this.data.st<=A&&this.data.op-this.data.st>A?!0!==this.isInRange&&(this.globalData._mdf=!0,this._mdf=!0,this.isInRange=!0,this.show()):!1!==this.isInRange&&(this.globalData._mdf=!0,this.isInRange=!1,this.hide())},renderRenderable:function(){var A,t=this.renderableComponents.length;for(A=0;A<t;A+=1)this.renderableComponents[A].renderFrame(this._isFirstFrame)},sourceRectAtTime:function(){return{top:0,left:0,width:100,height:100}},getLayerSize:function(){return 5===this.data.ty?{w:this.data.textData.width,h:this.data.textData.height}:{w:this.data.width,h:this.data.height}}};var getBlendMode=(blendModeEnums={0:\"source-over\",1:\"multiply\",2:\"screen\",3:\"overlay\",4:\"darken\",5:\"lighten\",6:\"color-dodge\",7:\"color-burn\",8:\"hard-light\",9:\"soft-light\",10:\"difference\",11:\"exclusion\",12:\"hue\",13:\"saturation\",14:\"color\",15:\"luminosity\"},function(A){return blendModeEnums[A]||\"\"}),blendModeEnums;function SliderEffect(A,t,e){this.p=PropertyFactory.getProp(t,A.v,0,0,e)}function AngleEffect(A,t,e){this.p=PropertyFactory.getProp(t,A.v,0,0,e)}function ColorEffect(A,t,e){this.p=PropertyFactory.getProp(t,A.v,1,0,e)}function PointEffect(A,t,e){this.p=PropertyFactory.getProp(t,A.v,1,0,e)}function LayerIndexEffect(A,t,e){this.p=PropertyFactory.getProp(t,A.v,0,0,e)}function MaskIndexEffect(A,t,e){this.p=PropertyFactory.getProp(t,A.v,0,0,e)}function CheckboxEffect(A,t,e){this.p=PropertyFactory.getProp(t,A.v,0,0,e)}function NoValueEffect(){this.p={}}function EffectsManager(A,t){var e,a=A.ef||[];this.effectElements=[];var r,n=a.length;for(e=0;e<n;e+=1)r=new GroupEffect(a[e],t),this.effectElements.push(r)}function GroupEffect(A,t){this.init(A,t)}function BaseElement(){}function FrameElement(){}function FootageElement(A,t,e){this.initFrame(),this.initRenderable(),this.assetData=t.getAssetData(A.refId),this.footageData=t.imageLoader.getAsset(this.assetData),this.initBaseData(A,t,e)}function AudioElement(A,t,e){this.initFrame(),this.initRenderable(),this.assetData=t.getAssetData(A.refId),this.initBaseData(A,t,e),this._isPlaying=!1,this._canPlay=!1;var a=this.globalData.getAssetsPath(this.assetData);this.audio=this.globalData.audioController.createAudio(a),this._currentTime=0,this.globalData.audioController.addAudio(this),this._volumeMultiplier=1,this._volume=1,this._previousVolume=null,this.tm=A.tm?PropertyFactory.getProp(this,A.tm,0,t.frameRate,this):{_placeholder:!0},this.lv=PropertyFactory.getProp(this,A.au&&A.au.lv?A.au.lv:{k:[100]},1,.01,this)}function BaseRenderer(){}extendPrototype([DynamicPropertyContainer],GroupEffect),GroupEffect.prototype.getValue=GroupEffect.prototype.iterateDynamicProperties,GroupEffect.prototype.init=function(A,t){var e;this.data=A,this.effectElements=[],this.initDynamicPropertyContainer(t);var a,r=this.data.ef.length,n=this.data.ef;for(e=0;e<r;e+=1){switch(a=null,n[e].ty){case 0:a=new SliderEffect(n[e],t,this);break;case 1:a=new AngleEffect(n[e],t,this);break;case 2:a=new ColorEffect(n[e],t,this);break;case 3:a=new PointEffect(n[e],t,this);break;case 4:case 7:a=new CheckboxEffect(n[e],t,this);break;case 10:a=new LayerIndexEffect(n[e],t,this);break;case 11:a=new MaskIndexEffect(n[e],t,this);break;case 5:a=new EffectsManager(n[e],t);break;default:a=new NoValueEffect(n[e])}a&&this.effectElements.push(a)}},BaseElement.prototype={checkMasks:function(){if(!this.data.hasMask)return!1;for(var A=0,t=this.data.masksProperties.length;A<t;){if(\"n\"!==this.data.masksProperties[A].mode&&!1!==this.data.masksProperties[A].cl)return!0;A+=1}return!1},initExpressions:function(){var A=getExpressionInterfaces();if(A){var t=A(\"layer\"),e=A(\"effects\"),a=A(\"shape\"),r=A(\"text\"),n=A(\"comp\");this.layerInterface=t(this),this.data.hasMask&&this.maskManager&&this.layerInterface.registerMaskInterface(this.maskManager);var l=e.createEffectsInterface(this,this.layerInterface);this.layerInterface.registerEffectsInterface(l),0===this.data.ty||this.data.xt?this.compInterface=n(this):4===this.data.ty?(this.layerInterface.shapeInterface=a(this.shapesData,this.itemsData,this.layerInterface),this.layerInterface.content=this.layerInterface.shapeInterface):5===this.data.ty&&(this.layerInterface.textInterface=r(this),this.layerInterface.text=this.layerInterface.textInterface)}},setBlendMode:function(){var A=getBlendMode(this.data.bm);(this.baseElement||this.layerElement).style[\"mix-blend-mode\"]=A},initBaseData:function(A,t,e){this.globalData=t,this.comp=e,this.data=A,this.layerId=createElementID(),this.data.sr||(this.data.sr=1),this.effectsManager=new EffectsManager(this.data,this,this.dynamicProperties)},getType:function(){return this.type},sourceRectAtTime:function(){}},FrameElement.prototype={initFrame:function(){this._isFirstFrame=!1,this.dynamicProperties=[],this._mdf=!1},prepareProperties:function(A,t){var e,a=this.dynamicProperties.length;for(e=0;e<a;e+=1)(t||this._isParent&&\"transform\"===this.dynamicProperties[e].propType)&&(this.dynamicProperties[e].getValue(),this.dynamicProperties[e]._mdf&&(this.globalData._mdf=!0,this._mdf=!0))},addDynamicProperty:function(A){-1===this.dynamicProperties.indexOf(A)&&this.dynamicProperties.push(A)}},FootageElement.prototype.prepareFrame=function(){},extendPrototype([RenderableElement,BaseElement,FrameElement],FootageElement),FootageElement.prototype.getBaseElement=function(){return null},FootageElement.prototype.renderFrame=function(){},FootageElement.prototype.destroy=function(){},FootageElement.prototype.initExpressions=function(){var A=getExpressionInterfaces();if(A){var t=A(\"footage\");this.layerInterface=t(this)}},FootageElement.prototype.getFootageData=function(){return this.footageData},AudioElement.prototype.prepareFrame=function(A){if(this.prepareRenderableFrame(A,!0),this.prepareProperties(A,!0),this.tm._placeholder)this._currentTime=A/this.data.sr;else{var t=this.tm.v;this._currentTime=t}this._volume=this.lv.v[0];var e=this._volume*this._volumeMultiplier;this._previousVolume!==e&&(this._previousVolume=e,this.audio.volume(e))},extendPrototype([RenderableElement,BaseElement,FrameElement],AudioElement),AudioElement.prototype.renderFrame=function(){this.isInRange&&this._canPlay&&(this._isPlaying?(!this.audio.playing()||Math.abs(this._currentTime/this.globalData.frameRate-this.audio.seek())>.1)&&this.audio.seek(this._currentTime/this.globalData.frameRate):(this.audio.play(),this.audio.seek(this._currentTime/this.globalData.frameRate),this._isPlaying=!0))},AudioElement.prototype.show=function(){},AudioElement.prototype.hide=function(){this.audio.pause(),this._isPlaying=!1},AudioElement.prototype.pause=function(){this.audio.pause(),this._isPlaying=!1,this._canPlay=!1},AudioElement.prototype.resume=function(){this._canPlay=!0},AudioElement.prototype.setRate=function(A){this.audio.rate(A)},AudioElement.prototype.volume=function(A){this._volumeMultiplier=A,this._previousVolume=A*this._volume,this.audio.volume(this._previousVolume)},AudioElement.prototype.getBaseElement=function(){return null},AudioElement.prototype.destroy=function(){},AudioElement.prototype.sourceRectAtTime=function(){},AudioElement.prototype.initExpressions=function(){},BaseRenderer.prototype.checkLayers=function(A){var t,e,a=this.layers.length;for(this.completeLayers=!0,t=a-1;t>=0;t-=1)this.elements[t]||(e=this.layers[t]).ip-e.st<=A-this.layers[t].st&&e.op-e.st>A-this.layers[t].st&&this.buildItem(t),this.completeLayers=!!this.elements[t]&&this.completeLayers;this.checkPendingElements()},BaseRenderer.prototype.createItem=function(A){switch(A.ty){case 2:return this.createImage(A);case 0:return this.createComp(A);case 1:return this.createSolid(A);case 3:default:return this.createNull(A);case 4:return this.createShape(A);case 5:return this.createText(A);case 6:return this.createAudio(A);case 13:return this.createCamera(A);case 15:return this.createFootage(A)}},BaseRenderer.prototype.createCamera=function(){throw new Error(\"You're using a 3d camera. Try the html renderer.\")},BaseRenderer.prototype.createAudio=function(A){return new AudioElement(A,this.globalData,this)},BaseRenderer.prototype.createFootage=function(A){return new FootageElement(A,this.globalData,this)},BaseRenderer.prototype.buildAllItems=function(){var A,t=this.layers.length;for(A=0;A<t;A+=1)this.buildItem(A);this.checkPendingElements()},BaseRenderer.prototype.includeLayers=function(A){var t;this.completeLayers=!1;var e,a=A.length,r=this.layers.length;for(t=0;t<a;t+=1)for(e=0;e<r;){if(this.layers[e].id===A[t].id){this.layers[e]=A[t];break}e+=1}},BaseRenderer.prototype.setProjectInterface=function(A){this.globalData.projectInterface=A},BaseRenderer.prototype.initItems=function(){this.globalData.progressiveLoad||this.buildAllItems()},BaseRenderer.prototype.buildElementParenting=function(A,t,e){for(var a=this.elements,r=this.layers,n=0,l=r.length;n<l;)r[n].ind==t&&(a[n]&&!0!==a[n]?(e.push(a[n]),a[n].setAsParent(),void 0!==r[n].parent?this.buildElementParenting(A,r[n].parent,e):A.setHierarchy(e)):(this.buildItem(n),this.addPendingElement(A))),n+=1},BaseRenderer.prototype.addPendingElement=function(A){this.pendingElements.push(A)},BaseRenderer.prototype.searchExtraCompositions=function(A){var t,e=A.length;for(t=0;t<e;t+=1)if(A[t].xt){var a=this.createComp(A[t]);a.initExpressions(),this.globalData.projectInterface.registerComposition(a)}},BaseRenderer.prototype.getElementById=function(A){var t,e=this.elements.length;for(t=0;t<e;t+=1)if(this.elements[t].data.ind===A)return this.elements[t];return null},BaseRenderer.prototype.getElementByPath=function(A){var t,e=A.shift();if(\"number\"==typeof e)t=this.elements[e];else{var a,r=this.elements.length;for(a=0;a<r;a+=1)if(this.elements[a].data.nm===e){t=this.elements[a];break}}return 0===A.length?t:t.getElementByPath(A)},BaseRenderer.prototype.setupGlobalData=function(A,t){this.globalData.fontManager=new FontManager,this.globalData.slotManager=slotFactory(A),this.globalData.fontManager.addChars(A.chars),this.globalData.fontManager.addFonts(A.fonts,t),this.globalData.getAssetData=this.animationItem.getAssetData.bind(this.animationItem),this.globalData.getAssetsPath=this.animationItem.getAssetsPath.bind(this.animationItem),this.globalData.imageLoader=this.animationItem.imagePreloader,this.globalData.audioController=this.animationItem.audioController,this.globalData.frameId=0,this.globalData.frameRate=A.fr,this.globalData.nm=A.nm,this.globalData.compSize={w:A.w,h:A.h}};var effectTypes={TRANSFORM_EFFECT:\"transformEFfect\"};function TransformElement(){}function MaskElement(A,t,e){this.data=A,this.element=t,this.globalData=e,this.storedData=[],this.masksProperties=this.data.masksProperties||[],this.maskElement=null;var a,r,n=this.globalData.defs,l=this.masksProperties?this.masksProperties.length:0;this.viewData=createSizedArray(l),this.solidPath=\"\";var i,o,s,p,c,u,d=this.masksProperties,h=0,S=[],f=createElementID(),m=\"clipPath\",L=\"clip-path\";for(a=0;a<l;a+=1)if((\"a\"!==d[a].mode&&\"n\"!==d[a].mode||d[a].inv||100!==d[a].o.k||d[a].o.x)&&(m=\"mask\",L=\"mask\"),\"s\"!==d[a].mode&&\"i\"!==d[a].mode||0!==h?s=null:((s=createNS(\"rect\")).setAttribute(\"fill\",\"#ffffff\"),s.setAttribute(\"width\",this.element.comp.data.w||0),s.setAttribute(\"height\",this.element.comp.data.h||0),S.push(s)),r=createNS(\"path\"),\"n\"===d[a].mode)this.viewData[a]={op:PropertyFactory.getProp(this.element,d[a].o,0,.01,this.element),prop:ShapePropertyFactory.getShapeProp(this.element,d[a],3),elem:r,lastPath:\"\"},n.appendChild(r);else{var W;if(h+=1,r.setAttribute(\"fill\",\"s\"===d[a].mode?\"#000000\":\"#ffffff\"),r.setAttribute(\"clip-rule\",\"nonzero\"),0!==d[a].x.k?(m=\"mask\",L=\"mask\",u=PropertyFactory.getProp(this.element,d[a].x,0,null,this.element),W=createElementID(),(p=createNS(\"filter\")).setAttribute(\"id\",W),(c=createNS(\"feMorphology\")).setAttribute(\"operator\",\"erode\"),c.setAttribute(\"in\",\"SourceGraphic\"),c.setAttribute(\"radius\",\"0\"),p.appendChild(c),n.appendChild(p),r.setAttribute(\"stroke\",\"s\"===d[a].mode?\"#000000\":\"#ffffff\")):(c=null,u=null),this.storedData[a]={elem:r,x:u,expan:c,lastPath:\"\",lastOperator:\"\",filterId:W,lastRadius:0},\"i\"===d[a].mode){o=S.length;var g=createNS(\"g\");for(i=0;i<o;i+=1)g.appendChild(S[i]);var y=createNS(\"mask\");y.setAttribute(\"mask-type\",\"alpha\"),y.setAttribute(\"id\",f+\"_\"+h),y.appendChild(r),n.appendChild(y),g.setAttribute(\"mask\",\"url(\"+getLocationHref()+\"#\"+f+\"_\"+h+\")\"),S.length=0,S.push(g)}else S.push(r);d[a].inv&&!this.solidPath&&(this.solidPath=this.createLayerSolidPath()),this.viewData[a]={elem:r,lastPath:\"\",op:PropertyFactory.getProp(this.element,d[a].o,0,.01,this.element),prop:ShapePropertyFactory.getShapeProp(this.element,d[a],3),invRect:s},this.viewData[a].prop.k||this.drawPath(d[a],this.viewData[a].prop.v,this.viewData[a])}for(this.maskElement=createNS(m),l=S.length,a=0;a<l;a+=1)this.maskElement.appendChild(S[a]);h>0&&(this.maskElement.setAttribute(\"id\",f),this.element.maskedElement.setAttribute(L,\"url(\"+getLocationHref()+\"#\"+f+\")\"),n.appendChild(this.maskElement)),this.viewData.length&&this.element.addRenderableComponent(this)}TransformElement.prototype={initTransform:function(){var A=new Matrix;this.finalTransform={mProp:this.data.ks?TransformPropertyFactory.getTransformProperty(this,this.data.ks,this):{o:0},_matMdf:!1,_localMatMdf:!1,_opMdf:!1,mat:A,localMat:A,localOpacity:1},this.data.ao&&(this.finalTransform.mProp.autoOriented=!0),this.data.ty},renderTransform:function(){if(this.finalTransform._opMdf=this.finalTransform.mProp.o._mdf||this._isFirstFrame,this.finalTransform._matMdf=this.finalTransform.mProp._mdf||this._isFirstFrame,this.hierarchy){var A,t=this.finalTransform.mat,e=0,a=this.hierarchy.length;if(!this.finalTransform._matMdf)for(;e<a;){if(this.hierarchy[e].finalTransform.mProp._mdf){this.finalTransform._matMdf=!0;break}e+=1}if(this.finalTransform._matMdf)for(A=this.finalTransform.mProp.v.props,t.cloneFromProps(A),e=0;e<a;e+=1)t.multiply(this.hierarchy[e].finalTransform.mProp.v)}this.finalTransform._matMdf&&(this.finalTransform._localMatMdf=this.finalTransform._matMdf),this.finalTransform._opMdf&&(this.finalTransform.localOpacity=this.finalTransform.mProp.o.v)},renderLocalTransform:function(){if(this.localTransforms){var A=0,t=this.localTransforms.length;if(this.finalTransform._localMatMdf=this.finalTransform._matMdf,!this.finalTransform._localMatMdf||!this.finalTransform._opMdf)for(;A<t;)this.localTransforms[A]._mdf&&(this.finalTransform._localMatMdf=!0),this.localTransforms[A]._opMdf&&!this.finalTransform._opMdf&&(this.finalTransform.localOpacity=this.finalTransform.mProp.o.v,this.finalTransform._opMdf=!0),A+=1;if(this.finalTransform._localMatMdf){var e=this.finalTransform.localMat;for(this.localTransforms[0].matrix.clone(e),A=1;A<t;A+=1){var a=this.localTransforms[A].matrix;e.multiply(a)}e.multiply(this.finalTransform.mat)}if(this.finalTransform._opMdf){var r=this.finalTransform.localOpacity;for(A=0;A<t;A+=1)r*=.01*this.localTransforms[A].opacity;this.finalTransform.localOpacity=r}}},searchEffectTransforms:function(){if(this.renderableEffectsManager){var A=this.renderableEffectsManager.getEffects(effectTypes.TRANSFORM_EFFECT);if(A.length){this.localTransforms=[],this.finalTransform.localMat=new Matrix;var t=0,e=A.length;for(t=0;t<e;t+=1)this.localTransforms.push(A[t])}}},globalToLocal:function(A){var t=[];t.push(this.finalTransform);for(var e,a=!0,r=this.comp;a;)r.finalTransform?(r.data.hasMask&&t.splice(0,0,r.finalTransform),r=r.comp):a=!1;var n,l=t.length;for(e=0;e<l;e+=1)n=t[e].mat.applyToPointArray(0,0,0),A=[A[0]-n[0],A[1]-n[1],0];return A},mHelper:new Matrix},MaskElement.prototype.getMaskProperty=function(A){return this.viewData[A].prop},MaskElement.prototype.renderFrame=function(A){var t,e=this.element.finalTransform.mat,a=this.masksProperties.length;for(t=0;t<a;t+=1)if((this.viewData[t].prop._mdf||A)&&this.drawPath(this.masksProperties[t],this.viewData[t].prop.v,this.viewData[t]),(this.viewData[t].op._mdf||A)&&this.viewData[t].elem.setAttribute(\"fill-opacity\",this.viewData[t].op.v),\"n\"!==this.masksProperties[t].mode&&(this.viewData[t].invRect&&(this.element.finalTransform.mProp._mdf||A)&&this.viewData[t].invRect.setAttribute(\"transform\",e.getInverseMatrix().to2dCSS()),this.storedData[t].x&&(this.storedData[t].x._mdf||A))){var r=this.storedData[t].expan;this.storedData[t].x.v<0?(\"erode\"!==this.storedData[t].lastOperator&&(this.storedData[t].lastOperator=\"erode\",this.storedData[t].elem.setAttribute(\"filter\",\"url(\"+getLocationHref()+\"#\"+this.storedData[t].filterId+\")\")),r.setAttribute(\"radius\",-this.storedData[t].x.v)):(\"dilate\"!==this.storedData[t].lastOperator&&(this.storedData[t].lastOperator=\"dilate\",this.storedData[t].elem.setAttribute(\"filter\",null)),this.storedData[t].elem.setAttribute(\"stroke-width\",2*this.storedData[t].x.v))}},MaskElement.prototype.getMaskelement=function(){return this.maskElement},MaskElement.prototype.createLayerSolidPath=function(){var A=\"M0,0 \";return A+=\" h\"+this.globalData.compSize.w,A+=\" v\"+this.globalData.compSize.h,(A+=\" h-\"+this.globalData.compSize.w)+\" v-\"+this.globalData.compSize.h+\" \"},MaskElement.prototype.drawPath=function(A,t,e){var a,r,n=\" M\"+t.v[0][0]+\",\"+t.v[0][1];for(r=t._length,a=1;a<r;a+=1)n+=\" C\"+t.o[a-1][0]+\",\"+t.o[a-1][1]+\" \"+t.i[a][0]+\",\"+t.i[a][1]+\" \"+t.v[a][0]+\",\"+t.v[a][1];if(t.c&&r>1&&(n+=\" C\"+t.o[a-1][0]+\",\"+t.o[a-1][1]+\" \"+t.i[0][0]+\",\"+t.i[0][1]+\" \"+t.v[0][0]+\",\"+t.v[0][1]),e.lastPath!==n){var l=\"\";e.elem&&(t.c&&(l=A.inv?this.solidPath+n:n),e.elem.setAttribute(\"d\",l)),e.lastPath=n}},MaskElement.prototype.destroy=function(){this.element=null,this.globalData=null,this.maskElement=null,this.data=null,this.masksProperties=null};var filtersFactory=function(){var A={createFilter:function(A,t){var e=createNS(\"filter\");return e.setAttribute(\"id\",A),!0!==t&&(e.setAttribute(\"filterUnits\",\"objectBoundingBox\"),e.setAttribute(\"x\",\"0%\"),e.setAttribute(\"y\",\"0%\"),e.setAttribute(\"width\",\"100%\"),e.setAttribute(\"height\",\"100%\")),e},createAlphaToLuminanceFilter:function(){var A=createNS(\"feColorMatrix\");return A.setAttribute(\"type\",\"matrix\"),A.setAttribute(\"color-interpolation-filters\",\"sRGB\"),A.setAttribute(\"values\",\"0 0 0 1 0  0 0 0 1 0  0 0 0 1 0  0 0 0 1 1\"),A}};return A}(),featureSupport=function(){var A={maskType:!0,svgLumaHidden:!0,offscreenCanvas:\"undefined\"!=typeof OffscreenCanvas};return(/MSIE 10/i.test(navigator.userAgent)||/MSIE 9/i.test(navigator.userAgent)||/rv:11.0/i.test(navigator.userAgent)||/Edge\\/\\d./i.test(navigator.userAgent))&&(A.maskType=!1),/firefox/i.test(navigator.userAgent)&&(A.svgLumaHidden=!1),A}(),registeredEffects$1={},idPrefix=\"filter_result_\";function SVGEffects(A){var t,e,a=\"SourceGraphic\",r=A.data.ef?A.data.ef.length:0,n=createElementID(),l=filtersFactory.createFilter(n,!0),i=0;for(this.filters=[],t=0;t<r;t+=1){e=null;var o=A.data.ef[t].ty;registeredEffects$1[o]&&(e=new registeredEffects$1[o].effect(l,A.effectsManager.effectElements[t],A,idPrefix+i,a),a=idPrefix+i,registeredEffects$1[o].countsAsEffect&&(i+=1)),e&&this.filters.push(e)}i&&(A.globalData.defs.appendChild(l),A.layerElement.setAttribute(\"filter\",\"url(\"+getLocationHref()+\"#\"+n+\")\")),this.filters.length&&A.addRenderableComponent(this)}function registerEffect$1(A,t,e){registeredEffects$1[A]={effect:t,countsAsEffect:e}}function SVGBaseElement(){}function HierarchyElement(){}function RenderableDOMElement(){}function IImageElement(A,t,e){this.assetData=t.getAssetData(A.refId),this.assetData&&this.assetData.sid&&(this.assetData=t.slotManager.getProp(this.assetData)),this.initElement(A,t,e),this.sourceRect={top:0,left:0,width:this.assetData.w,height:this.assetData.h}}function ProcessedElement(A,t){this.elem=A,this.pos=t}function IShapeElement(){}SVGEffects.prototype.renderFrame=function(A){var t,e=this.filters.length;for(t=0;t<e;t+=1)this.filters[t].renderFrame(A)},SVGEffects.prototype.getEffects=function(A){var t,e=this.filters.length,a=[];for(t=0;t<e;t+=1)this.filters[t].type===A&&a.push(this.filters[t]);return a},SVGBaseElement.prototype={initRendererElement:function(){this.layerElement=createNS(\"g\")},createContainerElements:function(){this.matteElement=createNS(\"g\"),this.transformedElement=this.layerElement,this.maskedElement=this.layerElement,this._sizeChanged=!1;var A=null;if(this.data.td){this.matteMasks={};var t=createNS(\"g\");t.setAttribute(\"id\",this.layerId),t.appendChild(this.layerElement),A=t,this.globalData.defs.appendChild(t)}else this.data.tt?(this.matteElement.appendChild(this.layerElement),A=this.matteElement,this.baseElement=this.matteElement):this.baseElement=this.layerElement;if(this.data.ln&&this.layerElement.setAttribute(\"id\",this.data.ln),this.data.cl&&this.layerElement.setAttribute(\"class\",this.data.cl),0===this.data.ty&&!this.data.hd){var e=createNS(\"clipPath\"),a=createNS(\"path\");a.setAttribute(\"d\",\"M0,0 L\"+this.data.w+\",0 L\"+this.data.w+\",\"+this.data.h+\" L0,\"+this.data.h+\"z\");var r=createElementID();if(e.setAttribute(\"id\",r),e.appendChild(a),this.globalData.defs.appendChild(e),this.checkMasks()){var n=createNS(\"g\");n.setAttribute(\"clip-path\",\"url(\"+getLocationHref()+\"#\"+r+\")\"),n.appendChild(this.layerElement),this.transformedElement=n,A?A.appendChild(this.transformedElement):this.baseElement=this.transformedElement}else this.layerElement.setAttribute(\"clip-path\",\"url(\"+getLocationHref()+\"#\"+r+\")\")}0!==this.data.bm&&this.setBlendMode()},renderElement:function(){this.finalTransform._localMatMdf&&this.transformedElement.setAttribute(\"transform\",this.finalTransform.localMat.to2dCSS()),this.finalTransform._opMdf&&this.transformedElement.setAttribute(\"opacity\",this.finalTransform.localOpacity)},destroyBaseElement:function(){this.layerElement=null,this.matteElement=null,this.maskManager.destroy()},getBaseElement:function(){return this.data.hd?null:this.baseElement},createRenderableComponents:function(){this.maskManager=new MaskElement(this.data,this,this.globalData),this.renderableEffectsManager=new SVGEffects(this),this.searchEffectTransforms()},getMatte:function(A){if(this.matteMasks||(this.matteMasks={}),!this.matteMasks[A]){var t,e,a,r,n=this.layerId+\"_\"+A;if(1===A||3===A){var l=createNS(\"mask\");l.setAttribute(\"id\",n),l.setAttribute(\"mask-type\",3===A?\"luminance\":\"alpha\"),(a=createNS(\"use\")).setAttributeNS(\"http://www.w3.org/1999/xlink\",\"href\",\"#\"+this.layerId),l.appendChild(a),this.globalData.defs.appendChild(l),featureSupport.maskType||1!==A||(l.setAttribute(\"mask-type\",\"luminance\"),t=createElementID(),e=filtersFactory.createFilter(t),this.globalData.defs.appendChild(e),e.appendChild(filtersFactory.createAlphaToLuminanceFilter()),(r=createNS(\"g\")).appendChild(a),l.appendChild(r),r.setAttribute(\"filter\",\"url(\"+getLocationHref()+\"#\"+t+\")\"))}else if(2===A){var i=createNS(\"mask\");i.setAttribute(\"id\",n),i.setAttribute(\"mask-type\",\"alpha\");var o=createNS(\"g\");i.appendChild(o),t=createElementID(),e=filtersFactory.createFilter(t);var s=createNS(\"feComponentTransfer\");s.setAttribute(\"in\",\"SourceGraphic\"),e.appendChild(s);var p=createNS(\"feFuncA\");p.setAttribute(\"type\",\"table\"),p.setAttribute(\"tableValues\",\"1.0 0.0\"),s.appendChild(p),this.globalData.defs.appendChild(e);var c=createNS(\"rect\");c.setAttribute(\"width\",this.comp.data.w),c.setAttribute(\"height\",this.comp.data.h),c.setAttribute(\"x\",\"0\"),c.setAttribute(\"y\",\"0\"),c.setAttribute(\"fill\",\"#ffffff\"),c.setAttribute(\"opacity\",\"0\"),o.setAttribute(\"filter\",\"url(\"+getLocationHref()+\"#\"+t+\")\"),o.appendChild(c),(a=createNS(\"use\")).setAttributeNS(\"http://www.w3.org/1999/xlink\",\"href\",\"#\"+this.layerId),o.appendChild(a),featureSupport.maskType||(i.setAttribute(\"mask-type\",\"luminance\"),e.appendChild(filtersFactory.createAlphaToLuminanceFilter()),r=createNS(\"g\"),o.appendChild(c),r.appendChild(this.layerElement),o.appendChild(r)),this.globalData.defs.appendChild(i)}this.matteMasks[A]=n}return this.matteMasks[A]},setMatte:function(A){this.matteElement&&this.matteElement.setAttribute(\"mask\",\"url(\"+getLocationHref()+\"#\"+A+\")\")}},HierarchyElement.prototype={initHierarchy:function(){this.hierarchy=[],this._isParent=!1,this.checkParenting()},setHierarchy:function(A){this.hierarchy=A},setAsParent:function(){this._isParent=!0},checkParenting:function(){void 0!==this.data.parent&&this.comp.buildElementParenting(this,this.data.parent,[])}},extendPrototype([RenderableElement,createProxyFunction({initElement:function(A,t,e){this.initFrame(),this.initBaseData(A,t,e),this.initTransform(A,t,e),this.initHierarchy(),this.initRenderable(),this.initRendererElement(),this.createContainerElements(),this.createRenderableComponents(),this.createContent(),this.hide()},hide:function(){this.hidden||this.isInRange&&!this.isTransparent||((this.baseElement||this.layerElement).style.display=\"none\",this.hidden=!0)},show:function(){this.isInRange&&!this.isTransparent&&(this.data.hd||((this.baseElement||this.layerElement).style.display=\"block\"),this.hidden=!1,this._isFirstFrame=!0)},renderFrame:function(){this.data.hd||this.hidden||(this.renderTransform(),this.renderRenderable(),this.renderLocalTransform(),this.renderElement(),this.renderInnerContent(),this._isFirstFrame&&(this._isFirstFrame=!1))},renderInnerContent:function(){},prepareFrame:function(A){this._mdf=!1,this.prepareRenderableFrame(A),this.prepareProperties(A,this.isInRange),this.checkTransparency()},destroy:function(){this.innerElem=null,this.destroyBaseElement()}})],RenderableDOMElement),extendPrototype([BaseElement,TransformElement,SVGBaseElement,HierarchyElement,FrameElement,RenderableDOMElement],IImageElement),IImageElement.prototype.createContent=function(){var A=this.globalData.getAssetsPath(this.assetData);this.innerElem=createNS(\"image\"),this.innerElem.setAttribute(\"width\",this.assetData.w+\"px\"),this.innerElem.setAttribute(\"height\",this.assetData.h+\"px\"),this.innerElem.setAttribute(\"preserveAspectRatio\",this.assetData.pr||this.globalData.renderConfig.imagePreserveAspectRatio),this.innerElem.setAttributeNS(\"http://www.w3.org/1999/xlink\",\"href\",A),this.layerElement.appendChild(this.innerElem)},IImageElement.prototype.sourceRectAtTime=function(){return this.sourceRect},IShapeElement.prototype={addShapeToModifiers:function(A){var t,e=this.shapeModifiers.length;for(t=0;t<e;t+=1)this.shapeModifiers[t].addShape(A)},isShapeInAnimatedModifiers:function(A){for(var t=this.shapeModifiers.length;0<t;)if(this.shapeModifiers[0].isAnimatedWithShape(A))return!0;return!1},renderModifiers:function(){if(this.shapeModifiers.length){var A,t=this.shapes.length;for(A=0;A<t;A+=1)this.shapes[A].sh.reset();for(A=(t=this.shapeModifiers.length)-1;A>=0&&!this.shapeModifiers[A].processShapes(this._isFirstFrame);A-=1);}},searchProcessedElement:function(A){for(var t=this.processedElements,e=0,a=t.length;e<a;){if(t[e].elem===A)return t[e].pos;e+=1}return 0},addProcessedElement:function(A,t){for(var e=this.processedElements,a=e.length;a;)if(e[a-=1].elem===A)return void(e[a].pos=t);e.push(new ProcessedElement(A,t))},prepareFrame:function(A){this.prepareRenderableFrame(A),this.prepareProperties(A,this.isInRange)}};var lineCapEnum={1:\"butt\",2:\"round\",3:\"square\"},lineJoinEnum={1:\"miter\",2:\"round\",3:\"bevel\"};function SVGShapeData(A,t,e){this.caches=[],this.styles=[],this.transformers=A,this.lStr=\"\",this.sh=e,this.lvl=t,this._isAnimated=!!e.k;for(var a=0,r=A.length;a<r;){if(A[a].mProps.dynamicProperties.length){this._isAnimated=!0;break}a+=1}}function SVGStyleData(A,t){this.data=A,this.type=A.ty,this.d=\"\",this.lvl=t,this._mdf=!1,this.closed=!0===A.hd,this.pElem=createNS(\"path\"),this.msElem=null}function DashProperty(A,t,e,a){var r;this.elem=A,this.frameId=-1,this.dataProps=createSizedArray(t.length),this.renderer=e,this.k=!1,this.dashStr=\"\",this.dashArray=createTypedArray(\"float32\",t.length?t.length-1:0),this.dashoffset=createTypedArray(\"float32\",1),this.initDynamicPropertyContainer(a);var n,l=t.length||0;for(r=0;r<l;r+=1)n=PropertyFactory.getProp(A,t[r].v,0,0,this),this.k=n.k||this.k,this.dataProps[r]={n:t[r].n,p:n};this.k||this.getValue(!0),this._isAnimated=this.k}function SVGStrokeStyleData(A,t,e){this.initDynamicPropertyContainer(A),this.getValue=this.iterateDynamicProperties,this.o=PropertyFactory.getProp(A,t.o,0,.01,this),this.w=PropertyFactory.getProp(A,t.w,0,null,this),this.d=new DashProperty(A,t.d||{},\"svg\",this),this.c=PropertyFactory.getProp(A,t.c,1,255,this),this.style=e,this._isAnimated=!!this._isAnimated}function SVGFillStyleData(A,t,e){this.initDynamicPropertyContainer(A),this.getValue=this.iterateDynamicProperties,this.o=PropertyFactory.getProp(A,t.o,0,.01,this),this.c=PropertyFactory.getProp(A,t.c,1,255,this),this.style=e}function SVGNoStyleData(A,t,e){this.initDynamicPropertyContainer(A),this.getValue=this.iterateDynamicProperties,this.style=e}function GradientProperty(A,t,e){this.data=t,this.c=createTypedArray(\"uint8c\",4*t.p);var a=t.k.k[0].s?t.k.k[0].s.length-4*t.p:t.k.k.length-4*t.p;this.o=createTypedArray(\"float32\",a),this._cmdf=!1,this._omdf=!1,this._collapsable=this.checkCollapsable(),this._hasOpacity=a,this.initDynamicPropertyContainer(e),this.prop=PropertyFactory.getProp(A,t.k,1,null,this),this.k=this.prop.k,this.getValue(!0)}function SVGGradientFillStyleData(A,t,e){this.initDynamicPropertyContainer(A),this.getValue=this.iterateDynamicProperties,this.initGradientData(A,t,e)}function SVGGradientStrokeStyleData(A,t,e){this.initDynamicPropertyContainer(A),this.getValue=this.iterateDynamicProperties,this.w=PropertyFactory.getProp(A,t.w,0,null,this),this.d=new DashProperty(A,t.d||{},\"svg\",this),this.initGradientData(A,t,e),this._isAnimated=!!this._isAnimated}function ShapeGroupData(){this.it=[],this.prevViewData=[],this.gr=createNS(\"g\")}function SVGTransformData(A,t,e){this.transform={mProps:A,op:t,container:e},this.elements=[],this._isAnimated=this.transform.mProps.dynamicProperties.length||this.transform.op.effectsSequence.length}SVGShapeData.prototype.setAsAnimated=function(){this._isAnimated=!0},SVGStyleData.prototype.reset=function(){this.d=\"\",this._mdf=!1},DashProperty.prototype.getValue=function(A){if((this.elem.globalData.frameId!==this.frameId||A)&&(this.frameId=this.elem.globalData.frameId,this.iterateDynamicProperties(),this._mdf=this._mdf||A,this._mdf)){var t=0,e=this.dataProps.length;for(\"svg\"===this.renderer&&(this.dashStr=\"\"),t=0;t<e;t+=1)\"o\"!==this.dataProps[t].n?\"svg\"===this.renderer?this.dashStr+=\" \"+this.dataProps[t].p.v:this.dashArray[t]=this.dataProps[t].p.v:this.dashoffset[0]=this.dataProps[t].p.v}},extendPrototype([DynamicPropertyContainer],DashProperty),extendPrototype([DynamicPropertyContainer],SVGStrokeStyleData),extendPrototype([DynamicPropertyContainer],SVGFillStyleData),extendPrototype([DynamicPropertyContainer],SVGNoStyleData),GradientProperty.prototype.comparePoints=function(A,t){for(var e=0,a=this.o.length/2;e<a;){if(Math.abs(A[4*e]-A[4*t+2*e])>.01)return!1;e+=1}return!0},GradientProperty.prototype.checkCollapsable=function(){if(this.o.length/2!=this.c.length/4)return!1;if(this.data.k.k[0].s)for(var A=0,t=this.data.k.k.length;A<t;){if(!this.comparePoints(this.data.k.k[A].s,this.data.p))return!1;A+=1}else if(!this.comparePoints(this.data.k.k,this.data.p))return!1;return!0},GradientProperty.prototype.getValue=function(A){if(this.prop.getValue(),this._mdf=!1,this._cmdf=!1,this._omdf=!1,this.prop._mdf||A){var t,e,a,r=4*this.data.p;for(t=0;t<r;t+=1)e=t%4==0?100:255,a=Math.round(this.prop.v[t]*e),this.c[t]!==a&&(this.c[t]=a,this._cmdf=!A);if(this.o.length)for(r=this.prop.v.length,t=4*this.data.p;t<r;t+=1)e=t%2==0?100:1,a=t%2==0?Math.round(100*this.prop.v[t]):this.prop.v[t],this.o[t-4*this.data.p]!==a&&(this.o[t-4*this.data.p]=a,this._omdf=!A);this._mdf=!A}},extendPrototype([DynamicPropertyContainer],GradientProperty),SVGGradientFillStyleData.prototype.initGradientData=function(A,t,e){this.o=PropertyFactory.getProp(A,t.o,0,.01,this),this.s=PropertyFactory.getProp(A,t.s,1,null,this),this.e=PropertyFactory.getProp(A,t.e,1,null,this),this.h=PropertyFactory.getProp(A,t.h||{k:0},0,.01,this),this.a=PropertyFactory.getProp(A,t.a||{k:0},0,degToRads,this),this.g=new GradientProperty(A,t.g,this),this.style=e,this.stops=[],this.setGradientData(e.pElem,t),this.setGradientOpacity(t,e),this._isAnimated=!!this._isAnimated},SVGGradientFillStyleData.prototype.setGradientData=function(A,t){var e=createElementID(),a=createNS(1===t.t?\"linearGradient\":\"radialGradient\");a.setAttribute(\"id\",e),a.setAttribute(\"spreadMethod\",\"pad\"),a.setAttribute(\"gradientUnits\",\"userSpaceOnUse\");var r,n,l,i=[];for(l=4*t.g.p,n=0;n<l;n+=4)r=createNS(\"stop\"),a.appendChild(r),i.push(r);A.setAttribute(\"gf\"===t.ty?\"fill\":\"stroke\",\"url(\"+getLocationHref()+\"#\"+e+\")\"),this.gf=a,this.cst=i},SVGGradientFillStyleData.prototype.setGradientOpacity=function(A,t){if(this.g._hasOpacity&&!this.g._collapsable){var e,a,r,n=createNS(\"mask\"),l=createNS(\"path\");n.appendChild(l);var i=createElementID(),o=createElementID();n.setAttribute(\"id\",o);var s=createNS(1===A.t?\"linearGradient\":\"radialGradient\");s.setAttribute(\"id\",i),s.setAttribute(\"spreadMethod\",\"pad\"),s.setAttribute(\"gradientUnits\",\"userSpaceOnUse\"),r=A.g.k.k[0].s?A.g.k.k[0].s.length:A.g.k.k.length;var p=this.stops;for(a=4*A.g.p;a<r;a+=2)(e=createNS(\"stop\")).setAttribute(\"stop-color\",\"rgb(255,255,255)\"),s.appendChild(e),p.push(e);l.setAttribute(\"gf\"===A.ty?\"fill\":\"stroke\",\"url(\"+getLocationHref()+\"#\"+i+\")\"),\"gs\"===A.ty&&(l.setAttribute(\"stroke-linecap\",lineCapEnum[A.lc||2]),l.setAttribute(\"stroke-linejoin\",lineJoinEnum[A.lj||2]),1===A.lj&&l.setAttribute(\"stroke-miterlimit\",A.ml)),this.of=s,this.ms=n,this.ost=p,this.maskId=o,t.msElem=l}},extendPrototype([DynamicPropertyContainer],SVGGradientFillStyleData),extendPrototype([SVGGradientFillStyleData,DynamicPropertyContainer],SVGGradientStrokeStyleData);var buildShapeString=function(A,t,e,a){if(0===t)return\"\";var r,n=A.o,l=A.i,i=A.v,o=\" M\"+a.applyToPointStringified(i[0][0],i[0][1]);for(r=1;r<t;r+=1)o+=\" C\"+a.applyToPointStringified(n[r-1][0],n[r-1][1])+\" \"+a.applyToPointStringified(l[r][0],l[r][1])+\" \"+a.applyToPointStringified(i[r][0],i[r][1]);return e&&t&&(o+=\" C\"+a.applyToPointStringified(n[r-1][0],n[r-1][1])+\" \"+a.applyToPointStringified(l[0][0],l[0][1])+\" \"+a.applyToPointStringified(i[0][0],i[0][1]),o+=\"z\"),o},SVGElementsRenderer=function(){var A=new Matrix,t=new Matrix;function e(A,t,e){(e||t.transform.op._mdf)&&t.transform.container.setAttribute(\"opacity\",t.transform.op.v),(e||t.transform.mProps._mdf)&&t.transform.container.setAttribute(\"transform\",t.transform.mProps.v.to2dCSS())}function a(){}function r(e,a,r){var n,l,i,o,s,p,c,u,d,h,S=a.styles.length,f=a.lvl;for(p=0;p<S;p+=1){if(o=a.sh._mdf||r,a.styles[p].lvl<f){for(u=t.reset(),d=f-a.styles[p].lvl,h=a.transformers.length-1;!o&&d>0;)o=a.transformers[h].mProps._mdf||o,d-=1,h-=1;if(o)for(d=f-a.styles[p].lvl,h=a.transformers.length-1;d>0;)u.multiply(a.transformers[h].mProps.v),d-=1,h-=1}else u=A;if(l=(c=a.sh.paths)._length,o){for(i=\"\",n=0;n<l;n+=1)(s=c.shapes[n])&&s._length&&(i+=buildShapeString(s,s._length,s.c,u));a.caches[p]=i}else i=a.caches[p];a.styles[p].d+=!0===e.hd?\"\":i,a.styles[p]._mdf=o||a.styles[p]._mdf}}function n(A,t,e){var a=t.style;(t.c._mdf||e)&&a.pElem.setAttribute(\"fill\",\"rgb(\"+bmFloor(t.c.v[0])+\",\"+bmFloor(t.c.v[1])+\",\"+bmFloor(t.c.v[2])+\")\"),(t.o._mdf||e)&&a.pElem.setAttribute(\"fill-opacity\",t.o.v)}function l(A,t,e){i(A,t,e),o(0,t,e)}function i(A,t,e){var a,r,n,l,i,o=t.gf,s=t.g._hasOpacity,p=t.s.v,c=t.e.v;if(t.o._mdf||e){var u=\"gf\"===A.ty?\"fill-opacity\":\"stroke-opacity\";t.style.pElem.setAttribute(u,t.o.v)}if(t.s._mdf||e){var d=1===A.t?\"x1\":\"cx\",h=\"x1\"===d?\"y1\":\"cy\";o.setAttribute(d,p[0]),o.setAttribute(h,p[1]),s&&!t.g._collapsable&&(t.of.setAttribute(d,p[0]),t.of.setAttribute(h,p[1]))}if(t.g._cmdf||e){a=t.cst;var S=t.g.c;for(n=a.length,r=0;r<n;r+=1)(l=a[r]).setAttribute(\"offset\",S[4*r]+\"%\"),l.setAttribute(\"stop-color\",\"rgb(\"+S[4*r+1]+\",\"+S[4*r+2]+\",\"+S[4*r+3]+\")\")}if(s&&(t.g._omdf||e)){var f=t.g.o;for(n=(a=t.g._collapsable?t.cst:t.ost).length,r=0;r<n;r+=1)l=a[r],t.g._collapsable||l.setAttribute(\"offset\",f[2*r]+\"%\"),l.setAttribute(\"stop-opacity\",f[2*r+1])}if(1===A.t)(t.e._mdf||e)&&(o.setAttribute(\"x2\",c[0]),o.setAttribute(\"y2\",c[1]),s&&!t.g._collapsable&&(t.of.setAttribute(\"x2\",c[0]),t.of.setAttribute(\"y2\",c[1])));else if((t.s._mdf||t.e._mdf||e)&&(i=Math.sqrt(Math.pow(p[0]-c[0],2)+Math.pow(p[1]-c[1],2)),o.setAttribute(\"r\",i),s&&!t.g._collapsable&&t.of.setAttribute(\"r\",i)),t.e._mdf||t.h._mdf||t.a._mdf||e){i||(i=Math.sqrt(Math.pow(p[0]-c[0],2)+Math.pow(p[1]-c[1],2)));var m=Math.atan2(c[1]-p[1],c[0]-p[0]),L=t.h.v;L>=1?L=.99:L<=-1&&(L=-.99);var W=i*L,g=Math.cos(m+t.a.v)*W+p[0],y=Math.sin(m+t.a.v)*W+p[1];o.setAttribute(\"fx\",g),o.setAttribute(\"fy\",y),s&&!t.g._collapsable&&(t.of.setAttribute(\"fx\",g),t.of.setAttribute(\"fy\",y))}}function o(A,t,e){var a=t.style,r=t.d;r&&(r._mdf||e)&&r.dashStr&&(a.pElem.setAttribute(\"stroke-dasharray\",r.dashStr),a.pElem.setAttribute(\"stroke-dashoffset\",r.dashoffset[0])),t.c&&(t.c._mdf||e)&&a.pElem.setAttribute(\"stroke\",\"rgb(\"+bmFloor(t.c.v[0])+\",\"+bmFloor(t.c.v[1])+\",\"+bmFloor(t.c.v[2])+\")\"),(t.o._mdf||e)&&a.pElem.setAttribute(\"stroke-opacity\",t.o.v),(t.w._mdf||e)&&(a.pElem.setAttribute(\"stroke-width\",t.w.v),a.msElem&&a.msElem.setAttribute(\"stroke-width\",t.w.v))}return{createRenderFunction:function(A){switch(A.ty){case\"fl\":return n;case\"gf\":return i;case\"gs\":return l;case\"st\":return o;case\"sh\":case\"el\":case\"rc\":case\"sr\":return r;case\"tr\":return e;case\"no\":return a;default:return null}}}}();function SVGShapeElement(A,t,e){this.shapes=[],this.shapesData=A.shapes,this.stylesList=[],this.shapeModifiers=[],this.itemsData=[],this.processedElements=[],this.animatedContents=[],this.initElement(A,t,e),this.prevViewData=[]}function LetterProps(A,t,e,a,r,n){this.o=A,this.sw=t,this.sc=e,this.fc=a,this.m=r,this.p=n,this._mdf={o:!0,sw:!!t,sc:!!e,fc:!!a,m:!0,p:!0}}function TextProperty(A,t){this._frameId=initialDefaultFrame,this.pv=\"\",this.v=\"\",this.kf=!1,this._isFirstFrame=!0,this._mdf=!1,t.d&&t.d.sid&&(t.d=A.globalData.slotManager.getProp(t.d)),this.data=t,this.elem=A,this.comp=this.elem.comp,this.keysIndex=0,this.canResize=!1,this.minimumFontSize=1,this.effectsSequence=[],this.currentData={ascent:0,boxWidth:this.defaultBoxWidth,f:\"\",fStyle:\"\",fWeight:\"\",fc:\"\",j:\"\",justifyOffset:\"\",l:[],lh:0,lineWidths:[],ls:\"\",of:\"\",s:\"\",sc:\"\",sw:0,t:0,tr:0,sz:0,ps:null,fillColorAnim:!1,strokeColorAnim:!1,strokeWidthAnim:!1,yOffset:0,finalSize:0,finalText:[],finalLineHeight:0,__complete:!1},this.copyData(this.currentData,this.data.d.k[0].s),this.searchProperty()||this.completeTextData(this.currentData)}extendPrototype([BaseElement,TransformElement,SVGBaseElement,IShapeElement,HierarchyElement,FrameElement,RenderableDOMElement],SVGShapeElement),SVGShapeElement.prototype.initSecondaryElement=function(){},SVGShapeElement.prototype.identityMatrix=new Matrix,SVGShapeElement.prototype.buildExpressionInterface=function(){},SVGShapeElement.prototype.createContent=function(){this.searchShapes(this.shapesData,this.itemsData,this.prevViewData,this.layerElement,0,[],!0),this.filterUniqueShapes()},SVGShapeElement.prototype.filterUniqueShapes=function(){var A,t,e,a,r=this.shapes.length,n=this.stylesList.length,l=[],i=!1;for(e=0;e<n;e+=1){for(a=this.stylesList[e],i=!1,l.length=0,A=0;A<r;A+=1)-1!==(t=this.shapes[A]).styles.indexOf(a)&&(l.push(t),i=t._isAnimated||i);l.length>1&&i&&this.setShapesAsAnimated(l)}},SVGShapeElement.prototype.setShapesAsAnimated=function(A){var t,e=A.length;for(t=0;t<e;t+=1)A[t].setAsAnimated()},SVGShapeElement.prototype.createStyleElement=function(A,t){var e,a=new SVGStyleData(A,t),r=a.pElem;return\"st\"===A.ty?e=new SVGStrokeStyleData(this,A,a):\"fl\"===A.ty?e=new SVGFillStyleData(this,A,a):\"gf\"===A.ty||\"gs\"===A.ty?(e=new(\"gf\"===A.ty?SVGGradientFillStyleData:SVGGradientStrokeStyleData)(this,A,a),this.globalData.defs.appendChild(e.gf),e.maskId&&(this.globalData.defs.appendChild(e.ms),this.globalData.defs.appendChild(e.of),r.setAttribute(\"mask\",\"url(\"+getLocationHref()+\"#\"+e.maskId+\")\"))):\"no\"===A.ty&&(e=new SVGNoStyleData(this,A,a)),\"st\"!==A.ty&&\"gs\"!==A.ty||(r.setAttribute(\"stroke-linecap\",lineCapEnum[A.lc||2]),r.setAttribute(\"stroke-linejoin\",lineJoinEnum[A.lj||2]),r.setAttribute(\"fill-opacity\",\"0\"),1===A.lj&&r.setAttribute(\"stroke-miterlimit\",A.ml)),2===A.r&&r.setAttribute(\"fill-rule\",\"evenodd\"),A.ln&&r.setAttribute(\"id\",A.ln),A.cl&&r.setAttribute(\"class\",A.cl),A.bm&&(r.style[\"mix-blend-mode\"]=getBlendMode(A.bm)),this.stylesList.push(a),this.addToAnimatedContents(A,e),e},SVGShapeElement.prototype.createGroupElement=function(A){var t=new ShapeGroupData;return A.ln&&t.gr.setAttribute(\"id\",A.ln),A.cl&&t.gr.setAttribute(\"class\",A.cl),A.bm&&(t.gr.style[\"mix-blend-mode\"]=getBlendMode(A.bm)),t},SVGShapeElement.prototype.createTransformElement=function(A,t){var e=TransformPropertyFactory.getTransformProperty(this,A,this),a=new SVGTransformData(e,e.o,t);return this.addToAnimatedContents(A,a),a},SVGShapeElement.prototype.createShapeElement=function(A,t,e){var a=4;\"rc\"===A.ty?a=5:\"el\"===A.ty?a=6:\"sr\"===A.ty&&(a=7);var r=new SVGShapeData(t,e,ShapePropertyFactory.getShapeProp(this,A,a,this));return this.shapes.push(r),this.addShapeToModifiers(r),this.addToAnimatedContents(A,r),r},SVGShapeElement.prototype.addToAnimatedContents=function(A,t){for(var e=0,a=this.animatedContents.length;e<a;){if(this.animatedContents[e].element===t)return;e+=1}this.animatedContents.push({fn:SVGElementsRenderer.createRenderFunction(A),element:t,data:A})},SVGShapeElement.prototype.setElementStyles=function(A){var t,e=A.styles,a=this.stylesList.length;for(t=0;t<a;t+=1)this.stylesList[t].closed||e.push(this.stylesList[t])},SVGShapeElement.prototype.reloadShapes=function(){var A;this._isFirstFrame=!0;var t=this.itemsData.length;for(A=0;A<t;A+=1)this.prevViewData[A]=this.itemsData[A];for(this.searchShapes(this.shapesData,this.itemsData,this.prevViewData,this.layerElement,0,[],!0),this.filterUniqueShapes(),t=this.dynamicProperties.length,A=0;A<t;A+=1)this.dynamicProperties[A].getValue();this.renderModifiers()},SVGShapeElement.prototype.searchShapes=function(A,t,e,a,r,n,l){var i,o,s,p,c,u,d=[].concat(n),h=A.length-1,S=[],f=[];for(i=h;i>=0;i-=1){if((u=this.searchProcessedElement(A[i]))?t[i]=e[u-1]:A[i]._render=l,\"fl\"===A[i].ty||\"st\"===A[i].ty||\"gf\"===A[i].ty||\"gs\"===A[i].ty||\"no\"===A[i].ty)u?t[i].style.closed=!1:t[i]=this.createStyleElement(A[i],r),A[i]._render&&t[i].style.pElem.parentNode!==a&&a.appendChild(t[i].style.pElem),S.push(t[i].style);else if(\"gr\"===A[i].ty){if(u)for(s=t[i].it.length,o=0;o<s;o+=1)t[i].prevViewData[o]=t[i].it[o];else t[i]=this.createGroupElement(A[i]);this.searchShapes(A[i].it,t[i].it,t[i].prevViewData,t[i].gr,r+1,d,l),A[i]._render&&t[i].gr.parentNode!==a&&a.appendChild(t[i].gr)}else\"tr\"===A[i].ty?(u||(t[i]=this.createTransformElement(A[i],a)),p=t[i].transform,d.push(p)):\"sh\"===A[i].ty||\"rc\"===A[i].ty||\"el\"===A[i].ty||\"sr\"===A[i].ty?(u||(t[i]=this.createShapeElement(A[i],d,r)),this.setElementStyles(t[i])):\"tm\"===A[i].ty||\"rd\"===A[i].ty||\"ms\"===A[i].ty||\"pb\"===A[i].ty||\"zz\"===A[i].ty||\"op\"===A[i].ty?(u?(c=t[i]).closed=!1:((c=ShapeModifiers.getModifier(A[i].ty)).init(this,A[i]),t[i]=c,this.shapeModifiers.push(c)),f.push(c)):\"rp\"===A[i].ty&&(u?(c=t[i]).closed=!0:(c=ShapeModifiers.getModifier(A[i].ty),t[i]=c,c.init(this,A,i,t),this.shapeModifiers.push(c),l=!1),f.push(c));this.addProcessedElement(A[i],i+1)}for(h=S.length,i=0;i<h;i+=1)S[i].closed=!0;for(h=f.length,i=0;i<h;i+=1)f[i].closed=!0},SVGShapeElement.prototype.renderInnerContent=function(){var A;this.renderModifiers();var t=this.stylesList.length;for(A=0;A<t;A+=1)this.stylesList[A].reset();for(this.renderShape(),A=0;A<t;A+=1)(this.stylesList[A]._mdf||this._isFirstFrame)&&(this.stylesList[A].msElem&&(this.stylesList[A].msElem.setAttribute(\"d\",this.stylesList[A].d),this.stylesList[A].d=\"M0 0\"+this.stylesList[A].d),this.stylesList[A].pElem.setAttribute(\"d\",this.stylesList[A].d||\"M0 0\"))},SVGShapeElement.prototype.renderShape=function(){var A,t,e=this.animatedContents.length;for(A=0;A<e;A+=1)t=this.animatedContents[A],(this._isFirstFrame||t.element._isAnimated)&&!0!==t.data&&t.fn(t.data,t.element,this._isFirstFrame)},SVGShapeElement.prototype.destroy=function(){this.destroyBaseElement(),this.shapesData=null,this.itemsData=null},LetterProps.prototype.update=function(A,t,e,a,r,n){this._mdf.o=!1,this._mdf.sw=!1,this._mdf.sc=!1,this._mdf.fc=!1,this._mdf.m=!1,this._mdf.p=!1;var l=!1;return this.o!==A&&(this.o=A,this._mdf.o=!0,l=!0),this.sw!==t&&(this.sw=t,this._mdf.sw=!0,l=!0),this.sc!==e&&(this.sc=e,this._mdf.sc=!0,l=!0),this.fc!==a&&(this.fc=a,this._mdf.fc=!0,l=!0),this.m!==r&&(this.m=r,this._mdf.m=!0,l=!0),!n.length||this.p[0]===n[0]&&this.p[1]===n[1]&&this.p[4]===n[4]&&this.p[5]===n[5]&&this.p[12]===n[12]&&this.p[13]===n[13]||(this.p=n,this._mdf.p=!0,l=!0),l},TextProperty.prototype.defaultBoxWidth=[0,0],TextProperty.prototype.copyData=function(A,t){for(var e in t)Object.prototype.hasOwnProperty.call(t,e)&&(A[e]=t[e]);return A},TextProperty.prototype.setCurrentData=function(A){A.__complete||this.completeTextData(A),this.currentData=A,this.currentData.boxWidth=this.currentData.boxWidth||this.defaultBoxWidth,this._mdf=!0},TextProperty.prototype.searchProperty=function(){return this.searchKeyframes()},TextProperty.prototype.searchKeyframes=function(){return this.kf=this.data.d.k.length>1,this.kf&&this.addEffect(this.getKeyframeValue.bind(this)),this.kf},TextProperty.prototype.addEffect=function(A){this.effectsSequence.push(A),this.elem.addDynamicProperty(this)},TextProperty.prototype.getValue=function(A){if(this.elem.globalData.frameId!==this.frameId&&this.effectsSequence.length||A){this.currentData.t=this.data.d.k[this.keysIndex].s.t;var t=this.currentData,e=this.keysIndex;if(this.lock)this.setCurrentData(this.currentData);else{var a;this.lock=!0,this._mdf=!1;var r=this.effectsSequence.length,n=A||this.data.d.k[this.keysIndex].s;for(a=0;a<r;a+=1)n=e!==this.keysIndex?this.effectsSequence[a](n,n.t):this.effectsSequence[a](this.currentData,n.t);t!==n&&this.setCurrentData(n),this.v=this.currentData,this.pv=this.v,this.lock=!1,this.frameId=this.elem.globalData.frameId}}},TextProperty.prototype.getKeyframeValue=function(){for(var A=this.data.d.k,t=this.elem.comp.renderedFrame,e=0,a=A.length;e<=a-1&&!(e===a-1||A[e+1].t>t);)e+=1;return this.keysIndex!==e&&(this.keysIndex=e),this.data.d.k[this.keysIndex].s},TextProperty.prototype.buildFinalText=function(A){for(var t,e,a=[],r=0,n=A.length,l=!1,i=!1,o=\"\";r<n;)l=i,i=!1,t=A.charCodeAt(r),o=A.charAt(r),FontManager.isCombinedCharacter(t)?l=!0:t>=55296&&t<=56319?FontManager.isRegionalFlag(A,r)?o=A.substr(r,14):(e=A.charCodeAt(r+1))>=56320&&e<=57343&&(FontManager.isModifier(t,e)?(o=A.substr(r,2),l=!0):o=FontManager.isFlagEmoji(A.substr(r,4))?A.substr(r,4):A.substr(r,2)):t>56319?(e=A.charCodeAt(r+1),FontManager.isVariationSelector(t)&&(l=!0)):FontManager.isZeroWidthJoiner(t)&&(l=!0,i=!0),l?(a[a.length-1]+=o,l=!1):a.push(o),r+=o.length;return a},TextProperty.prototype.completeTextData=function(A){A.__complete=!0;var t,e,a,r,n,l,i,o=this.elem.globalData.fontManager,s=this.data,p=[],c=0,u=s.m.g,d=0,h=0,S=0,f=[],m=0,L=0,W=o.getFontByName(A.f),g=0,y=getFontProperties(W);A.fWeight=y.weight,A.fStyle=y.style,A.finalSize=A.s,A.finalText=this.buildFinalText(A.t),e=A.finalText.length,A.finalLineHeight=A.lh;var v,b=A.tr/1e3*A.finalSize;if(A.sz)for(var x,E,k=!0,w=A.sz[0],C=A.sz[1];k;){x=0,m=0,e=(E=this.buildFinalText(A.t)).length,b=A.tr/1e3*A.finalSize;var P=-1;for(t=0;t<e;t+=1)v=E[t].charCodeAt(0),a=!1,\" \"===E[t]?P=t:13!==v&&3!==v||(m=0,a=!0,x+=A.finalLineHeight||1.2*A.finalSize),o.chars?(i=o.getCharData(E[t],W.fStyle,W.fFamily),g=a?0:i.w*A.finalSize/100):g=o.measureText(E[t],A.f,A.finalSize),m+g>w&&\" \"!==E[t]?(-1===P?e+=1:t=P,x+=A.finalLineHeight||1.2*A.finalSize,E.splice(t,P===t?1:0,\"\\r\"),P=-1,m=0):(m+=g,m+=b);x+=W.ascent*A.finalSize/100,this.canResize&&A.finalSize>this.minimumFontSize&&C<x?(A.finalSize-=1,A.finalLineHeight=A.finalSize*A.lh/A.s):(A.finalText=E,e=A.finalText.length,k=!1)}m=-b,g=0;var T,_=0;for(t=0;t<e;t+=1)if(a=!1,13===(v=(T=A.finalText[t]).charCodeAt(0))||3===v?(_=0,f.push(m),L=m>L?m:L,m=-2*b,r=\"\",a=!0,S+=1):r=T,o.chars?(i=o.getCharData(T,W.fStyle,o.getFontByName(A.f).fFamily),g=a?0:i.w*A.finalSize/100):g=o.measureText(r,A.f,A.finalSize),\" \"===T?_+=g+b:(m+=g+b+_,_=0),p.push({l:g,an:g,add:d,n:a,anIndexes:[],val:r,line:S,animatorJustifyOffset:0}),2==u){if(d+=g,\"\"===r||\" \"===r||t===e-1){for(\"\"!==r&&\" \"!==r||(d-=g);h<=t;)p[h].an=d,p[h].ind=c,p[h].extra=g,h+=1;c+=1,d=0}}else if(3==u){if(d+=g,\"\"===r||t===e-1){for(\"\"===r&&(d-=g);h<=t;)p[h].an=d,p[h].ind=c,p[h].extra=g,h+=1;d=0,c+=1}}else p[c].ind=c,p[c].extra=0,c+=1;if(A.l=p,L=m>L?m:L,f.push(m),A.sz)A.boxWidth=A.sz[0],A.justifyOffset=0;else switch(A.boxWidth=L,A.j){case 1:A.justifyOffset=-A.boxWidth;break;case 2:A.justifyOffset=-A.boxWidth/2;break;default:A.justifyOffset=0}A.lineWidths=f;var M,D,R,N,I=s.a;l=I.length;var F=[];for(n=0;n<l;n+=1){for((M=I[n]).a.sc&&(A.strokeColorAnim=!0),M.a.sw&&(A.strokeWidthAnim=!0),(M.a.fc||M.a.fh||M.a.fs||M.a.fb)&&(A.fillColorAnim=!0),N=0,R=M.s.b,t=0;t<e;t+=1)(D=p[t]).anIndexes[n]=N,(1==R&&\"\"!==D.val||2==R&&\"\"!==D.val&&\" \"!==D.val||3==R&&(D.n||\" \"==D.val||t==e-1)||4==R&&(D.n||t==e-1))&&(1===M.s.rn&&F.push(N),N+=1);s.a[n].s.totalChars=N;var O,V=-1;if(1===M.s.rn)for(t=0;t<e;t+=1)V!=(D=p[t]).anIndexes[n]&&(V=D.anIndexes[n],O=F.splice(Math.floor(Math.random()*F.length),1)[0]),D.anIndexes[n]=O}A.yOffset=A.finalLineHeight||1.2*A.finalSize,A.ls=A.ls||0,A.ascent=W.ascent*A.finalSize/100},TextProperty.prototype.updateDocumentData=function(A,t){t=void 0===t?this.keysIndex:t;var e=this.copyData({},this.data.d.k[t].s);e=this.copyData(e,A),this.data.d.k[t].s=e,this.recalculate(t),this.setCurrentData(e),this.elem.addDynamicProperty(this)},TextProperty.prototype.recalculate=function(A){var t=this.data.d.k[A].s;t.__complete=!1,this.keysIndex=0,this._isFirstFrame=!0,this.getValue(t)},TextProperty.prototype.canResizeFont=function(A){this.canResize=A,this.recalculate(this.keysIndex),this.elem.addDynamicProperty(this)},TextProperty.prototype.setMinimumFontSize=function(A){this.minimumFontSize=Math.floor(A)||1,this.recalculate(this.keysIndex),this.elem.addDynamicProperty(this)};var TextSelectorProp=function(){var A=Math.max,t=Math.min,e=Math.floor;function a(A,t){this._currentTextLength=-1,this.k=!1,this.data=t,this.elem=A,this.comp=A.comp,this.finalS=0,this.finalE=0,this.initDynamicPropertyContainer(A),this.s=PropertyFactory.getProp(A,t.s||{k:0},0,0,this),this.e=\"e\"in t?PropertyFactory.getProp(A,t.e,0,0,this):{v:100},this.o=PropertyFactory.getProp(A,t.o||{k:0},0,0,this),this.xe=PropertyFactory.getProp(A,t.xe||{k:0},0,0,this),this.ne=PropertyFactory.getProp(A,t.ne||{k:0},0,0,this),this.sm=PropertyFactory.getProp(A,t.sm||{k:100},0,0,this),this.a=PropertyFactory.getProp(A,t.a,0,.01,this),this.dynamicProperties.length||this.getValue()}return a.prototype={getMult:function(a){this._currentTextLength!==this.elem.textProperty.currentData.l.length&&this.getValue();var r=0,n=0,l=1,i=1;this.ne.v>0?r=this.ne.v/100:n=-this.ne.v/100,this.xe.v>0?l=1-this.xe.v/100:i=1+this.xe.v/100;var o=BezierFactory.getBezierEasing(r,n,l,i).get,s=0,p=this.finalS,c=this.finalE,u=this.data.sh;if(2===u)s=o(s=c===p?a>=c?1:0:A(0,t(.5/(c-p)+(a-p)/(c-p),1)));else if(3===u)s=o(s=c===p?a>=c?0:1:1-A(0,t(.5/(c-p)+(a-p)/(c-p),1)));else if(4===u)c===p?s=0:(s=A(0,t(.5/(c-p)+(a-p)/(c-p),1)))<.5?s*=2:s=1-2*(s-.5),s=o(s);else if(5===u){if(c===p)s=0;else{var d=c-p,h=-d/2+(a=t(A(0,a+.5-p),c-p)),S=d/2;s=Math.sqrt(1-h*h/(S*S))}s=o(s)}else 6===u?(c===p?s=0:(a=t(A(0,a+.5-p),c-p),s=(1+Math.cos(Math.PI+2*Math.PI*a/(c-p)))/2),s=o(s)):(a>=e(p)&&(s=A(0,t(a-p<0?t(c,1)-(p-a):c-a,1))),s=o(s));if(100!==this.sm.v){var f=.01*this.sm.v;0===f&&(f=1e-8);var m=.5-.5*f;s<m?s=0:(s=(s-m)/f)>1&&(s=1)}return s*this.a.v},getValue:function(A){this.iterateDynamicProperties(),this._mdf=A||this._mdf,this._currentTextLength=this.elem.textProperty.currentData.l.length||0,A&&2===this.data.r&&(this.e.v=this._currentTextLength);var t=2===this.data.r?1:100/this.data.totalChars,e=this.o.v/t,a=this.s.v/t+e,r=this.e.v/t+e;if(a>r){var n=a;a=r,r=n}this.finalS=a,this.finalE=r}},extendPrototype([DynamicPropertyContainer],a),{getTextSelectorProp:function(A,t,e){return new a(A,t)}}}();function TextAnimatorDataProperty(A,t,e){var a={propType:!1},r=PropertyFactory.getProp,n=t.a;this.a={r:n.r?r(A,n.r,0,degToRads,e):a,rx:n.rx?r(A,n.rx,0,degToRads,e):a,ry:n.ry?r(A,n.ry,0,degToRads,e):a,sk:n.sk?r(A,n.sk,0,degToRads,e):a,sa:n.sa?r(A,n.sa,0,degToRads,e):a,s:n.s?r(A,n.s,1,.01,e):a,a:n.a?r(A,n.a,1,0,e):a,o:n.o?r(A,n.o,0,.01,e):a,p:n.p?r(A,n.p,1,0,e):a,sw:n.sw?r(A,n.sw,0,0,e):a,sc:n.sc?r(A,n.sc,1,0,e):a,fc:n.fc?r(A,n.fc,1,0,e):a,fh:n.fh?r(A,n.fh,0,0,e):a,fs:n.fs?r(A,n.fs,0,.01,e):a,fb:n.fb?r(A,n.fb,0,.01,e):a,t:n.t?r(A,n.t,0,0,e):a},this.s=TextSelectorProp.getTextSelectorProp(A,t.s,e),this.s.t=t.s.t}function TextAnimatorProperty(A,t,e){this._isFirstFrame=!0,this._hasMaskedPath=!1,this._frameId=-1,this._textData=A,this._renderType=t,this._elem=e,this._animatorsData=createSizedArray(this._textData.a.length),this._pathData={},this._moreOptions={alignment:{}},this.renderedLetters=[],this.lettersChangedFlag=!1,this.initDynamicPropertyContainer(e)}function ITextElement(){}TextAnimatorProperty.prototype.searchProperties=function(){var A,t,e=this._textData.a.length,a=PropertyFactory.getProp;for(A=0;A<e;A+=1)t=this._textData.a[A],this._animatorsData[A]=new TextAnimatorDataProperty(this._elem,t,this);this._textData.p&&\"m\"in this._textData.p?(this._pathData={a:a(this._elem,this._textData.p.a,0,0,this),f:a(this._elem,this._textData.p.f,0,0,this),l:a(this._elem,this._textData.p.l,0,0,this),r:a(this._elem,this._textData.p.r,0,0,this),p:a(this._elem,this._textData.p.p,0,0,this),m:this._elem.maskManager.getMaskProperty(this._textData.p.m)},this._hasMaskedPath=!0):this._hasMaskedPath=!1,this._moreOptions.alignment=a(this._elem,this._textData.m.a,1,0,this)},TextAnimatorProperty.prototype.getMeasures=function(A,t){if(this.lettersChangedFlag=t,this._mdf||this._isFirstFrame||t||this._hasMaskedPath&&this._pathData.m._mdf){this._isFirstFrame=!1;var e,a,r,n,l,i,o,s,p,c,u,d,h,S,f,m,L,W,g,y=this._moreOptions.alignment.v,v=this._animatorsData,b=this._textData,x=this.mHelper,E=this._renderType,k=this.renderedLetters.length,w=A.l;if(this._hasMaskedPath){if(g=this._pathData.m,!this._pathData.n||this._pathData._mdf){var C,P=g.v;for(this._pathData.r.v&&(P=P.reverse()),l={tLength:0,segments:[]},n=P._length-1,m=0,r=0;r<n;r+=1)C=bez.buildBezierData(P.v[r],P.v[r+1],[P.o[r][0]-P.v[r][0],P.o[r][1]-P.v[r][1]],[P.i[r+1][0]-P.v[r+1][0],P.i[r+1][1]-P.v[r+1][1]]),l.tLength+=C.segmentLength,l.segments.push(C),m+=C.segmentLength;r=n,g.v.c&&(C=bez.buildBezierData(P.v[r],P.v[0],[P.o[r][0]-P.v[r][0],P.o[r][1]-P.v[r][1]],[P.i[0][0]-P.v[0][0],P.i[0][1]-P.v[0][1]]),l.tLength+=C.segmentLength,l.segments.push(C),m+=C.segmentLength),this._pathData.pi=l}if(l=this._pathData.pi,i=this._pathData.f.v,u=0,c=1,s=0,p=!0,S=l.segments,i<0&&g.v.c)for(l.tLength<Math.abs(i)&&(i=-Math.abs(i)%l.tLength),c=(h=S[u=S.length-1].points).length-1;i<0;)i+=h[c].partialLength,(c-=1)<0&&(c=(h=S[u-=1].points).length-1);d=(h=S[u].points)[c-1],f=(o=h[c]).partialLength}n=w.length,e=0,a=0;var T,_,M,D,R,N=1.2*A.finalSize*.714,I=!0;M=v.length;var F,O,V,B,z,j,$,H,G,q,U,X,J=-1,Y=i,K=u,Q=c,Z=-1,AA=\"\",tA=this.defaultPropsArray;if(2===A.j||1===A.j){var eA=0,aA=0,rA=2===A.j?-.5:-1,nA=0,lA=!0;for(r=0;r<n;r+=1)if(w[r].n){for(eA&&(eA+=aA);nA<r;)w[nA].animatorJustifyOffset=eA,nA+=1;eA=0,lA=!0}else{for(_=0;_<M;_+=1)(T=v[_].a).t.propType&&(lA&&2===A.j&&(aA+=T.t.v*rA),(R=v[_].s.getMult(w[r].anIndexes[_],b.a[_].s.totalChars)).length?eA+=T.t.v*R[0]*rA:eA+=T.t.v*R*rA);lA=!1}for(eA&&(eA+=aA);nA<r;)w[nA].animatorJustifyOffset=eA,nA+=1}for(r=0;r<n;r+=1){if(x.reset(),B=1,w[r].n)e=0,a+=A.yOffset,a+=I?1:0,i=Y,I=!1,this._hasMaskedPath&&(c=Q,d=(h=S[u=K].points)[c-1],f=(o=h[c]).partialLength,s=0),AA=\"\",U=\"\",G=\"\",X=\"\",tA=this.defaultPropsArray;else{if(this._hasMaskedPath){if(Z!==w[r].line){switch(A.j){case 1:i+=m-A.lineWidths[w[r].line];break;case 2:i+=(m-A.lineWidths[w[r].line])/2}Z=w[r].line}J!==w[r].ind&&(w[J]&&(i+=w[J].extra),i+=w[r].an/2,J=w[r].ind),i+=y[0]*w[r].an*.005;var iA=0;for(_=0;_<M;_+=1)(T=v[_].a).p.propType&&((R=v[_].s.getMult(w[r].anIndexes[_],b.a[_].s.totalChars)).length?iA+=T.p.v[0]*R[0]:iA+=T.p.v[0]*R),T.a.propType&&((R=v[_].s.getMult(w[r].anIndexes[_],b.a[_].s.totalChars)).length?iA+=T.a.v[0]*R[0]:iA+=T.a.v[0]*R);for(p=!0,this._pathData.a.v&&(i=.5*w[0].an+(m-this._pathData.f.v-.5*w[0].an-.5*w[w.length-1].an)*J/(n-1),i+=this._pathData.f.v);p;)s+f>=i+iA||!h?(L=(i+iA-s)/o.partialLength,O=d.point[0]+(o.point[0]-d.point[0])*L,V=d.point[1]+(o.point[1]-d.point[1])*L,x.translate(-y[0]*w[r].an*.005,-y[1]*N*.01),p=!1):h&&(s+=o.partialLength,(c+=1)>=h.length&&(c=0,S[u+=1]?h=S[u].points:g.v.c?(c=0,h=S[u=0].points):(s-=o.partialLength,h=null)),h&&(d=o,f=(o=h[c]).partialLength));F=w[r].an/2-w[r].add,x.translate(-F,0,0)}else F=w[r].an/2-w[r].add,x.translate(-F,0,0),x.translate(-y[0]*w[r].an*.005,-y[1]*N*.01,0);for(_=0;_<M;_+=1)(T=v[_].a).t.propType&&(R=v[_].s.getMult(w[r].anIndexes[_],b.a[_].s.totalChars),0===e&&0===A.j||(this._hasMaskedPath?R.length?i+=T.t.v*R[0]:i+=T.t.v*R:R.length?e+=T.t.v*R[0]:e+=T.t.v*R));for(A.strokeWidthAnim&&(j=A.sw||0),A.strokeColorAnim&&(z=A.sc?[A.sc[0],A.sc[1],A.sc[2]]:[0,0,0]),A.fillColorAnim&&A.fc&&($=[A.fc[0],A.fc[1],A.fc[2]]),_=0;_<M;_+=1)(T=v[_].a).a.propType&&((R=v[_].s.getMult(w[r].anIndexes[_],b.a[_].s.totalChars)).length?x.translate(-T.a.v[0]*R[0],-T.a.v[1]*R[1],T.a.v[2]*R[2]):x.translate(-T.a.v[0]*R,-T.a.v[1]*R,T.a.v[2]*R));for(_=0;_<M;_+=1)(T=v[_].a).s.propType&&((R=v[_].s.getMult(w[r].anIndexes[_],b.a[_].s.totalChars)).length?x.scale(1+(T.s.v[0]-1)*R[0],1+(T.s.v[1]-1)*R[1],1):x.scale(1+(T.s.v[0]-1)*R,1+(T.s.v[1]-1)*R,1));for(_=0;_<M;_+=1){if(T=v[_].a,R=v[_].s.getMult(w[r].anIndexes[_],b.a[_].s.totalChars),T.sk.propType&&(R.length?x.skewFromAxis(-T.sk.v*R[0],T.sa.v*R[1]):x.skewFromAxis(-T.sk.v*R,T.sa.v*R)),T.r.propType&&(R.length?x.rotateZ(-T.r.v*R[2]):x.rotateZ(-T.r.v*R)),T.ry.propType&&(R.length?x.rotateY(T.ry.v*R[1]):x.rotateY(T.ry.v*R)),T.rx.propType&&(R.length?x.rotateX(T.rx.v*R[0]):x.rotateX(T.rx.v*R)),T.o.propType&&(R.length?B+=(T.o.v*R[0]-B)*R[0]:B+=(T.o.v*R-B)*R),A.strokeWidthAnim&&T.sw.propType&&(R.length?j+=T.sw.v*R[0]:j+=T.sw.v*R),A.strokeColorAnim&&T.sc.propType)for(H=0;H<3;H+=1)R.length?z[H]+=(T.sc.v[H]-z[H])*R[0]:z[H]+=(T.sc.v[H]-z[H])*R;if(A.fillColorAnim&&A.fc){if(T.fc.propType)for(H=0;H<3;H+=1)R.length?$[H]+=(T.fc.v[H]-$[H])*R[0]:$[H]+=(T.fc.v[H]-$[H])*R;T.fh.propType&&($=R.length?addHueToRGB($,T.fh.v*R[0]):addHueToRGB($,T.fh.v*R)),T.fs.propType&&($=R.length?addSaturationToRGB($,T.fs.v*R[0]):addSaturationToRGB($,T.fs.v*R)),T.fb.propType&&($=R.length?addBrightnessToRGB($,T.fb.v*R[0]):addBrightnessToRGB($,T.fb.v*R))}}for(_=0;_<M;_+=1)(T=v[_].a).p.propType&&(R=v[_].s.getMult(w[r].anIndexes[_],b.a[_].s.totalChars),this._hasMaskedPath?R.length?x.translate(0,T.p.v[1]*R[0],-T.p.v[2]*R[1]):x.translate(0,T.p.v[1]*R,-T.p.v[2]*R):R.length?x.translate(T.p.v[0]*R[0],T.p.v[1]*R[1],-T.p.v[2]*R[2]):x.translate(T.p.v[0]*R,T.p.v[1]*R,-T.p.v[2]*R));if(A.strokeWidthAnim&&(G=j<0?0:j),A.strokeColorAnim&&(q=\"rgb(\"+Math.round(255*z[0])+\",\"+Math.round(255*z[1])+\",\"+Math.round(255*z[2])+\")\"),A.fillColorAnim&&A.fc&&(U=\"rgb(\"+Math.round(255*$[0])+\",\"+Math.round(255*$[1])+\",\"+Math.round(255*$[2])+\")\"),this._hasMaskedPath){if(x.translate(0,-A.ls),x.translate(0,y[1]*N*.01+a,0),this._pathData.p.v){W=(o.point[1]-d.point[1])/(o.point[0]-d.point[0]);var oA=180*Math.atan(W)/Math.PI;o.point[0]<d.point[0]&&(oA+=180),x.rotate(-oA*Math.PI/180)}x.translate(O,V,0),i-=y[0]*w[r].an*.005,w[r+1]&&J!==w[r+1].ind&&(i+=w[r].an/2,i+=.001*A.tr*A.finalSize)}else{switch(x.translate(e,a,0),A.ps&&x.translate(A.ps[0],A.ps[1]+A.ascent,0),A.j){case 1:x.translate(w[r].animatorJustifyOffset+A.justifyOffset+(A.boxWidth-A.lineWidths[w[r].line]),0,0);break;case 2:x.translate(w[r].animatorJustifyOffset+A.justifyOffset+(A.boxWidth-A.lineWidths[w[r].line])/2,0,0)}x.translate(0,-A.ls),x.translate(F,0,0),x.translate(y[0]*w[r].an*.005,y[1]*N*.01,0),e+=w[r].l+.001*A.tr*A.finalSize}\"html\"===E?AA=x.toCSS():\"svg\"===E?AA=x.to2dCSS():tA=[x.props[0],x.props[1],x.props[2],x.props[3],x.props[4],x.props[5],x.props[6],x.props[7],x.props[8],x.props[9],x.props[10],x.props[11],x.props[12],x.props[13],x.props[14],x.props[15]],X=B}k<=r?(D=new LetterProps(X,G,q,U,AA,tA),this.renderedLetters.push(D),k+=1,this.lettersChangedFlag=!0):(D=this.renderedLetters[r],this.lettersChangedFlag=D.update(X,G,q,U,AA,tA)||this.lettersChangedFlag)}}},TextAnimatorProperty.prototype.getValue=function(){this._elem.globalData.frameId!==this._frameId&&(this._frameId=this._elem.globalData.frameId,this.iterateDynamicProperties())},TextAnimatorProperty.prototype.mHelper=new Matrix,TextAnimatorProperty.prototype.defaultPropsArray=[],extendPrototype([DynamicPropertyContainer],TextAnimatorProperty),ITextElement.prototype.initElement=function(A,t,e){this.lettersChangedFlag=!0,this.initFrame(),this.initBaseData(A,t,e),this.textProperty=new TextProperty(this,A.t,this.dynamicProperties),this.textAnimator=new TextAnimatorProperty(A.t,this.renderType,this),this.initTransform(A,t,e),this.initHierarchy(),this.initRenderable(),this.initRendererElement(),this.createContainerElements(),this.createRenderableComponents(),this.createContent(),this.hide(),this.textAnimator.searchProperties(this.dynamicProperties)},ITextElement.prototype.prepareFrame=function(A){this._mdf=!1,this.prepareRenderableFrame(A),this.prepareProperties(A,this.isInRange)},ITextElement.prototype.createPathShape=function(A,t){var e,a,r=t.length,n=\"\";for(e=0;e<r;e+=1)\"sh\"===t[e].ty&&(a=t[e].ks.k,n+=buildShapeString(a,a.i.length,!0,A));return n},ITextElement.prototype.updateDocumentData=function(A,t){this.textProperty.updateDocumentData(A,t)},ITextElement.prototype.canResizeFont=function(A){this.textProperty.canResizeFont(A)},ITextElement.prototype.setMinimumFontSize=function(A){this.textProperty.setMinimumFontSize(A)},ITextElement.prototype.applyTextPropertiesToMatrix=function(A,t,e,a,r){switch(A.ps&&t.translate(A.ps[0],A.ps[1]+A.ascent,0),t.translate(0,-A.ls,0),A.j){case 1:t.translate(A.justifyOffset+(A.boxWidth-A.lineWidths[e]),0,0);break;case 2:t.translate(A.justifyOffset+(A.boxWidth-A.lineWidths[e])/2,0,0)}t.translate(a,r,0)},ITextElement.prototype.buildColor=function(A){return\"rgb(\"+Math.round(255*A[0])+\",\"+Math.round(255*A[1])+\",\"+Math.round(255*A[2])+\")\"},ITextElement.prototype.emptyProp=new LetterProps,ITextElement.prototype.destroy=function(){},ITextElement.prototype.validateText=function(){(this.textProperty._mdf||this.textProperty._isFirstFrame)&&(this.buildNewText(),this.textProperty._isFirstFrame=!1,this.textProperty._mdf=!1)};var emptyShapeData={shapes:[]};function SVGTextLottieElement(A,t,e){this.textSpans=[],this.renderType=\"svg\",this.initElement(A,t,e)}function ISolidElement(A,t,e){this.initElement(A,t,e)}function NullElement(A,t,e){this.initFrame(),this.initBaseData(A,t,e),this.initFrame(),this.initTransform(A,t,e),this.initHierarchy()}function SVGRendererBase(){}function ICompElement(){}function SVGCompElement(A,t,e){this.layers=A.layers,this.supports3d=!0,this.completeLayers=!1,this.pendingElements=[],this.elements=this.layers?createSizedArray(this.layers.length):[],this.initElement(A,t,e),this.tm=A.tm?PropertyFactory.getProp(this,A.tm,0,t.frameRate,this):{_placeholder:!0}}function SVGRenderer(A,t){this.animationItem=A,this.layers=null,this.renderedFrame=-1,this.svgElement=createNS(\"svg\");var e=\"\";if(t&&t.title){var a=createNS(\"title\"),r=createElementID();a.setAttribute(\"id\",r),a.textContent=t.title,this.svgElement.appendChild(a),e+=r}if(t&&t.description){var n=createNS(\"desc\"),l=createElementID();n.setAttribute(\"id\",l),n.textContent=t.description,this.svgElement.appendChild(n),e+=\" \"+l}e&&this.svgElement.setAttribute(\"aria-labelledby\",e);var i=createNS(\"defs\");this.svgElement.appendChild(i);var o=createNS(\"g\");this.svgElement.appendChild(o),this.layerElement=o,this.renderConfig={preserveAspectRatio:t&&t.preserveAspectRatio||\"xMidYMid meet\",imagePreserveAspectRatio:t&&t.imagePreserveAspectRatio||\"xMidYMid slice\",contentVisibility:t&&t.contentVisibility||\"visible\",progressiveLoad:t&&t.progressiveLoad||!1,hideOnTransparent:!(t&&!1===t.hideOnTransparent),viewBoxOnly:t&&t.viewBoxOnly||!1,viewBoxSize:t&&t.viewBoxSize||!1,className:t&&t.className||\"\",id:t&&t.id||\"\",focusable:t&&t.focusable,filterSize:{width:t&&t.filterSize&&t.filterSize.width||\"100%\",height:t&&t.filterSize&&t.filterSize.height||\"100%\",x:t&&t.filterSize&&t.filterSize.x||\"0%\",y:t&&t.filterSize&&t.filterSize.y||\"0%\"},width:t&&t.width,height:t&&t.height,runExpressions:!t||void 0===t.runExpressions||t.runExpressions},this.globalData={_mdf:!1,frameNum:-1,defs:i,renderConfig:this.renderConfig},this.elements=[],this.pendingElements=[],this.destroyed=!1,this.rendererType=\"svg\"}function ShapeTransformManager(){this.sequences={},this.sequenceList=[],this.transform_key_count=0}extendPrototype([BaseElement,TransformElement,SVGBaseElement,HierarchyElement,FrameElement,RenderableDOMElement,ITextElement],SVGTextLottieElement),SVGTextLottieElement.prototype.createContent=function(){this.data.singleShape&&!this.globalData.fontManager.chars&&(this.textContainer=createNS(\"text\"))},SVGTextLottieElement.prototype.buildTextContents=function(A){for(var t=0,e=A.length,a=[],r=\"\";t<e;)A[t]===String.fromCharCode(13)||A[t]===String.fromCharCode(3)?(a.push(r),r=\"\"):r+=A[t],t+=1;return a.push(r),a},SVGTextLottieElement.prototype.buildShapeData=function(A,t){if(A.shapes&&A.shapes.length){var e=A.shapes[0];if(e.it){var a=e.it[e.it.length-1];a.s&&(a.s.k[0]=t,a.s.k[1]=t)}}return A},SVGTextLottieElement.prototype.buildNewText=function(){var A,t;this.addDynamicProperty(this);var e=this.textProperty.currentData;this.renderedLetters=createSizedArray(e?e.l.length:0),e.fc?this.layerElement.setAttribute(\"fill\",this.buildColor(e.fc)):this.layerElement.setAttribute(\"fill\",\"rgba(0,0,0,0)\"),e.sc&&(this.layerElement.setAttribute(\"stroke\",this.buildColor(e.sc)),this.layerElement.setAttribute(\"stroke-width\",e.sw)),this.layerElement.setAttribute(\"font-size\",e.finalSize);var a=this.globalData.fontManager.getFontByName(e.f);if(a.fClass)this.layerElement.setAttribute(\"class\",a.fClass);else{this.layerElement.setAttribute(\"font-family\",a.fFamily);var r=e.fWeight,n=e.fStyle;this.layerElement.setAttribute(\"font-style\",n),this.layerElement.setAttribute(\"font-weight\",r)}this.layerElement.setAttribute(\"aria-label\",e.t);var l,i=e.l||[],o=!!this.globalData.fontManager.chars;t=i.length;var s=this.mHelper,p=this.data.singleShape,c=0,u=0,d=!0,h=.001*e.tr*e.finalSize;if(!p||o||e.sz){var S,f=this.textSpans.length;for(A=0;A<t;A+=1){if(this.textSpans[A]||(this.textSpans[A]={span:null,childSpan:null,glyph:null}),!o||!p||0===A){if(l=f>A?this.textSpans[A].span:createNS(o?\"g\":\"text\"),f<=A){if(l.setAttribute(\"stroke-linecap\",\"butt\"),l.setAttribute(\"stroke-linejoin\",\"round\"),l.setAttribute(\"stroke-miterlimit\",\"4\"),this.textSpans[A].span=l,o){var m=createNS(\"g\");l.appendChild(m),this.textSpans[A].childSpan=m}this.textSpans[A].span=l,this.layerElement.appendChild(l)}l.style.display=\"inherit\"}if(s.reset(),p&&(i[A].n&&(c=-h,u+=e.yOffset,u+=d?1:0,d=!1),this.applyTextPropertiesToMatrix(e,s,i[A].line,c,u),c+=i[A].l||0,c+=h),o){var L;if(1===(S=this.globalData.fontManager.getCharData(e.finalText[A],a.fStyle,this.globalData.fontManager.getFontByName(e.f).fFamily)).t)L=new SVGCompElement(S.data,this.globalData,this);else{var W=emptyShapeData;S.data&&S.data.shapes&&(W=this.buildShapeData(S.data,e.finalSize)),L=new SVGShapeElement(W,this.globalData,this)}if(this.textSpans[A].glyph){var g=this.textSpans[A].glyph;this.textSpans[A].childSpan.removeChild(g.layerElement),g.destroy()}this.textSpans[A].glyph=L,L._debug=!0,L.prepareFrame(0),L.renderFrame(),this.textSpans[A].childSpan.appendChild(L.layerElement),1===S.t&&this.textSpans[A].childSpan.setAttribute(\"transform\",\"scale(\"+e.finalSize/100+\",\"+e.finalSize/100+\")\")}else p&&l.setAttribute(\"transform\",\"translate(\"+s.props[12]+\",\"+s.props[13]+\")\"),l.textContent=i[A].val,l.setAttributeNS(\"http://www.w3.org/XML/1998/namespace\",\"xml:space\",\"preserve\")}p&&l&&l.setAttribute(\"d\",\"\")}else{var y=this.textContainer,v=\"start\";switch(e.j){case 1:v=\"end\";break;case 2:v=\"middle\";break;default:v=\"start\"}y.setAttribute(\"text-anchor\",v),y.setAttribute(\"letter-spacing\",h);var b=this.buildTextContents(e.finalText);for(t=b.length,u=e.ps?e.ps[1]+e.ascent:0,A=0;A<t;A+=1)(l=this.textSpans[A].span||createNS(\"tspan\")).textContent=b[A],l.setAttribute(\"x\",0),l.setAttribute(\"y\",u),l.style.display=\"inherit\",y.appendChild(l),this.textSpans[A]||(this.textSpans[A]={span:null,glyph:null}),this.textSpans[A].span=l,u+=e.finalLineHeight;this.layerElement.appendChild(y)}for(;A<this.textSpans.length;)this.textSpans[A].span.style.display=\"none\",A+=1;this._sizeChanged=!0},SVGTextLottieElement.prototype.sourceRectAtTime=function(){if(this.prepareFrame(this.comp.renderedFrame-this.data.st),this.renderInnerContent(),this._sizeChanged){this._sizeChanged=!1;var A=this.layerElement.getBBox();this.bbox={top:A.y,left:A.x,width:A.width,height:A.height}}return this.bbox},SVGTextLottieElement.prototype.getValue=function(){var A,t,e=this.textSpans.length;for(this.renderedFrame=this.comp.renderedFrame,A=0;A<e;A+=1)(t=this.textSpans[A].glyph)&&(t.prepareFrame(this.comp.renderedFrame-this.data.st),t._mdf&&(this._mdf=!0))},SVGTextLottieElement.prototype.renderInnerContent=function(){if(this.validateText(),(!this.data.singleShape||this._mdf)&&(this.textAnimator.getMeasures(this.textProperty.currentData,this.lettersChangedFlag),this.lettersChangedFlag||this.textAnimator.lettersChangedFlag)){var A,t;this._sizeChanged=!0;var e,a,r,n=this.textAnimator.renderedLetters,l=this.textProperty.currentData.l;for(t=l.length,A=0;A<t;A+=1)l[A].n||(e=n[A],a=this.textSpans[A].span,(r=this.textSpans[A].glyph)&&r.renderFrame(),e._mdf.m&&a.setAttribute(\"transform\",e.m),e._mdf.o&&a.setAttribute(\"opacity\",e.o),e._mdf.sw&&a.setAttribute(\"stroke-width\",e.sw),e._mdf.sc&&a.setAttribute(\"stroke\",e.sc),e._mdf.fc&&a.setAttribute(\"fill\",e.fc))}},extendPrototype([IImageElement],ISolidElement),ISolidElement.prototype.createContent=function(){var A=createNS(\"rect\");A.setAttribute(\"width\",this.data.sw),A.setAttribute(\"height\",this.data.sh),A.setAttribute(\"fill\",this.data.sc),this.layerElement.appendChild(A)},NullElement.prototype.prepareFrame=function(A){this.prepareProperties(A,!0)},NullElement.prototype.renderFrame=function(){},NullElement.prototype.getBaseElement=function(){return null},NullElement.prototype.destroy=function(){},NullElement.prototype.sourceRectAtTime=function(){},NullElement.prototype.hide=function(){},extendPrototype([BaseElement,TransformElement,HierarchyElement,FrameElement],NullElement),extendPrototype([BaseRenderer],SVGRendererBase),SVGRendererBase.prototype.createNull=function(A){return new NullElement(A,this.globalData,this)},SVGRendererBase.prototype.createShape=function(A){return new SVGShapeElement(A,this.globalData,this)},SVGRendererBase.prototype.createText=function(A){return new SVGTextLottieElement(A,this.globalData,this)},SVGRendererBase.prototype.createImage=function(A){return new IImageElement(A,this.globalData,this)},SVGRendererBase.prototype.createSolid=function(A){return new ISolidElement(A,this.globalData,this)},SVGRendererBase.prototype.configAnimation=function(A){this.svgElement.setAttribute(\"xmlns\",\"http://www.w3.org/2000/svg\"),this.svgElement.setAttribute(\"xmlns:xlink\",\"http://www.w3.org/1999/xlink\"),this.renderConfig.viewBoxSize?this.svgElement.setAttribute(\"viewBox\",this.renderConfig.viewBoxSize):this.svgElement.setAttribute(\"viewBox\",\"0 0 \"+A.w+\" \"+A.h),this.renderConfig.viewBoxOnly||(this.svgElement.setAttribute(\"width\",A.w),this.svgElement.setAttribute(\"height\",A.h),this.svgElement.style.width=\"100%\",this.svgElement.style.height=\"100%\",this.svgElement.style.transform=\"translate3d(0,0,0)\",this.svgElement.style.contentVisibility=this.renderConfig.contentVisibility),this.renderConfig.width&&this.svgElement.setAttribute(\"width\",this.renderConfig.width),this.renderConfig.height&&this.svgElement.setAttribute(\"height\",this.renderConfig.height),this.renderConfig.className&&this.svgElement.setAttribute(\"class\",this.renderConfig.className),this.renderConfig.id&&this.svgElement.setAttribute(\"id\",this.renderConfig.id),void 0!==this.renderConfig.focusable&&this.svgElement.setAttribute(\"focusable\",this.renderConfig.focusable),this.svgElement.setAttribute(\"preserveAspectRatio\",this.renderConfig.preserveAspectRatio),this.animationItem.wrapper.appendChild(this.svgElement);var t=this.globalData.defs;this.setupGlobalData(A,t),this.globalData.progressiveLoad=this.renderConfig.progressiveLoad,this.data=A;var e=createNS(\"clipPath\"),a=createNS(\"rect\");a.setAttribute(\"width\",A.w),a.setAttribute(\"height\",A.h),a.setAttribute(\"x\",0),a.setAttribute(\"y\",0);var r=createElementID();e.setAttribute(\"id\",r),e.appendChild(a),this.layerElement.setAttribute(\"clip-path\",\"url(\"+getLocationHref()+\"#\"+r+\")\"),t.appendChild(e),this.layers=A.layers,this.elements=createSizedArray(A.layers.length)},SVGRendererBase.prototype.destroy=function(){var A;this.animationItem.wrapper&&(this.animationItem.wrapper.innerText=\"\"),this.layerElement=null,this.globalData.defs=null;var t=this.layers?this.layers.length:0;for(A=0;A<t;A+=1)this.elements[A]&&this.elements[A].destroy&&this.elements[A].destroy();this.elements.length=0,this.destroyed=!0,this.animationItem=null},SVGRendererBase.prototype.updateContainerSize=function(){},SVGRendererBase.prototype.findIndexByInd=function(A){var t=0,e=this.layers.length;for(t=0;t<e;t+=1)if(this.layers[t].ind===A)return t;return-1},SVGRendererBase.prototype.buildItem=function(A){var t=this.elements;if(!t[A]&&99!==this.layers[A].ty){t[A]=!0;var e=this.createItem(this.layers[A]);if(t[A]=e,getExpressionsPlugin()&&(0===this.layers[A].ty&&this.globalData.projectInterface.registerComposition(e),e.initExpressions()),this.appendElementInPos(e,A),this.layers[A].tt){var a=\"tp\"in this.layers[A]?this.findIndexByInd(this.layers[A].tp):A-1;if(-1===a)return;if(this.elements[a]&&!0!==this.elements[a]){var r=t[a].getMatte(this.layers[A].tt);e.setMatte(r)}else this.buildItem(a),this.addPendingElement(e)}}},SVGRendererBase.prototype.checkPendingElements=function(){for(;this.pendingElements.length;){var A=this.pendingElements.pop();if(A.checkParenting(),A.data.tt)for(var t=0,e=this.elements.length;t<e;){if(this.elements[t]===A){var a=\"tp\"in A.data?this.findIndexByInd(A.data.tp):t-1,r=this.elements[a].getMatte(this.layers[t].tt);A.setMatte(r);break}t+=1}}},SVGRendererBase.prototype.renderFrame=function(A){if(this.renderedFrame!==A&&!this.destroyed){var t;null===A?A=this.renderedFrame:this.renderedFrame=A,this.globalData.frameNum=A,this.globalData.frameId+=1,this.globalData.projectInterface.currentFrame=A,this.globalData._mdf=!1;var e=this.layers.length;for(this.completeLayers||this.checkLayers(A),t=e-1;t>=0;t-=1)(this.completeLayers||this.elements[t])&&this.elements[t].prepareFrame(A-this.layers[t].st);if(this.globalData._mdf)for(t=0;t<e;t+=1)(this.completeLayers||this.elements[t])&&this.elements[t].renderFrame()}},SVGRendererBase.prototype.appendElementInPos=function(A,t){var e=A.getBaseElement();if(e){for(var a,r=0;r<t;)this.elements[r]&&!0!==this.elements[r]&&this.elements[r].getBaseElement()&&(a=this.elements[r].getBaseElement()),r+=1;a?this.layerElement.insertBefore(e,a):this.layerElement.appendChild(e)}},SVGRendererBase.prototype.hide=function(){this.layerElement.style.display=\"none\"},SVGRendererBase.prototype.show=function(){this.layerElement.style.display=\"block\"},extendPrototype([BaseElement,TransformElement,HierarchyElement,FrameElement,RenderableDOMElement],ICompElement),ICompElement.prototype.initElement=function(A,t,e){this.initFrame(),this.initBaseData(A,t,e),this.initTransform(A,t,e),this.initRenderable(),this.initHierarchy(),this.initRendererElement(),this.createContainerElements(),this.createRenderableComponents(),!this.data.xt&&t.progressiveLoad||this.buildAllItems(),this.hide()},ICompElement.prototype.prepareFrame=function(A){if(this._mdf=!1,this.prepareRenderableFrame(A),this.prepareProperties(A,this.isInRange),this.isInRange||this.data.xt){if(this.tm._placeholder)this.renderedFrame=A/this.data.sr;else{var t=this.tm.v;t===this.data.op&&(t=this.data.op-1),this.renderedFrame=t}var e,a=this.elements.length;for(this.completeLayers||this.checkLayers(this.renderedFrame),e=a-1;e>=0;e-=1)(this.completeLayers||this.elements[e])&&(this.elements[e].prepareFrame(this.renderedFrame-this.layers[e].st),this.elements[e]._mdf&&(this._mdf=!0))}},ICompElement.prototype.renderInnerContent=function(){var A,t=this.layers.length;for(A=0;A<t;A+=1)(this.completeLayers||this.elements[A])&&this.elements[A].renderFrame()},ICompElement.prototype.setElements=function(A){this.elements=A},ICompElement.prototype.getElements=function(){return this.elements},ICompElement.prototype.destroyElements=function(){var A,t=this.layers.length;for(A=0;A<t;A+=1)this.elements[A]&&this.elements[A].destroy()},ICompElement.prototype.destroy=function(){this.destroyElements(),this.destroyBaseElement()},extendPrototype([SVGRendererBase,ICompElement,SVGBaseElement],SVGCompElement),SVGCompElement.prototype.createComp=function(A){return new SVGCompElement(A,this.globalData,this)},extendPrototype([SVGRendererBase],SVGRenderer),SVGRenderer.prototype.createComp=function(A){return new SVGCompElement(A,this.globalData,this)},ShapeTransformManager.prototype={addTransformSequence:function(A){var t,e=A.length,a=\"_\";for(t=0;t<e;t+=1)a+=A[t].transform.key+\"_\";var r=this.sequences[a];return r||(r={transforms:[].concat(A),finalTransform:new Matrix,_mdf:!1},this.sequences[a]=r,this.sequenceList.push(r)),r},processSequence:function(A,t){for(var e=0,a=A.transforms.length,r=t;e<a&&!t;){if(A.transforms[e].transform.mProps._mdf){r=!0;break}e+=1}if(r)for(A.finalTransform.reset(),e=a-1;e>=0;e-=1)A.finalTransform.multiply(A.transforms[e].transform.mProps.v);A._mdf=r},processSequences:function(A){var t,e=this.sequenceList.length;for(t=0;t<e;t+=1)this.processSequence(this.sequenceList[t],A)},getNewKey:function(){return this.transform_key_count+=1,\"_\"+this.transform_key_count}};var lumaLoader=function(){var A=\"__lottie_element_luma_buffer\",t=null,e=null,a=null;function r(){var r,n,l;t||(r=createNS(\"svg\"),n=createNS(\"filter\"),l=createNS(\"feColorMatrix\"),n.setAttribute(\"id\",A),l.setAttribute(\"type\",\"matrix\"),l.setAttribute(\"color-interpolation-filters\",\"sRGB\"),l.setAttribute(\"values\",\"0.3, 0.3, 0.3, 0, 0, 0.3, 0.3, 0.3, 0, 0, 0.3, 0.3, 0.3, 0, 0, 0.3, 0.3, 0.3, 0, 0\"),n.appendChild(l),r.appendChild(n),r.setAttribute(\"id\",A+\"_svg\"),featureSupport.svgLumaHidden&&(r.style.display=\"none\"),a=r,document.body.appendChild(a),t=createTag(\"canvas\"),(e=t.getContext(\"2d\")).filter=\"url(#\"+A+\")\",e.fillStyle=\"rgba(0,0,0,0)\",e.fillRect(0,0,1,1))}return{load:r,get:function(a){return t||r(),t.width=a.width,t.height=a.height,e.filter=\"url(#\"+A+\")\",t}}};function createCanvas(A,t){if(featureSupport.offscreenCanvas)return new OffscreenCanvas(A,t);var e=createTag(\"canvas\");return e.width=A,e.height=t,e}var assetLoader={loadLumaCanvas:lumaLoader.load,getLumaCanvas:lumaLoader.get,createCanvas:createCanvas},registeredEffects={};function CVEffects(A){var t,e,a=A.data.ef?A.data.ef.length:0;for(this.filters=[],t=0;t<a;t+=1){e=null;var r=A.data.ef[t].ty;registeredEffects[r]&&(e=new registeredEffects[r].effect(A.effectsManager.effectElements[t],A)),e&&this.filters.push(e)}this.filters.length&&A.addRenderableComponent(this)}function registerEffect(A,t){registeredEffects[A]={effect:t}}function CVMaskElement(A,t){var e;this.data=A,this.element=t,this.masksProperties=this.data.masksProperties||[],this.viewData=createSizedArray(this.masksProperties.length);var a=this.masksProperties.length,r=!1;for(e=0;e<a;e+=1)\"n\"!==this.masksProperties[e].mode&&(r=!0),this.viewData[e]=ShapePropertyFactory.getShapeProp(this.element,this.masksProperties[e],3);this.hasMasks=r,r&&this.element.addRenderableComponent(this)}function CVBaseElement(){}CVEffects.prototype.renderFrame=function(A){var t,e=this.filters.length;for(t=0;t<e;t+=1)this.filters[t].renderFrame(A)},CVEffects.prototype.getEffects=function(A){var t,e=this.filters.length,a=[];for(t=0;t<e;t+=1)this.filters[t].type===A&&a.push(this.filters[t]);return a},CVMaskElement.prototype.renderFrame=function(){if(this.hasMasks){var A,t,e,a,r=this.element.finalTransform.mat,n=this.element.canvasContext,l=this.masksProperties.length;for(n.beginPath(),A=0;A<l;A+=1)if(\"n\"!==this.masksProperties[A].mode){var i;this.masksProperties[A].inv&&(n.moveTo(0,0),n.lineTo(this.element.globalData.compSize.w,0),n.lineTo(this.element.globalData.compSize.w,this.element.globalData.compSize.h),n.lineTo(0,this.element.globalData.compSize.h),n.lineTo(0,0)),a=this.viewData[A].v,t=r.applyToPointArray(a.v[0][0],a.v[0][1],0),n.moveTo(t[0],t[1]);var o=a._length;for(i=1;i<o;i+=1)e=r.applyToTriplePoints(a.o[i-1],a.i[i],a.v[i]),n.bezierCurveTo(e[0],e[1],e[2],e[3],e[4],e[5]);e=r.applyToTriplePoints(a.o[i-1],a.i[0],a.v[0]),n.bezierCurveTo(e[0],e[1],e[2],e[3],e[4],e[5])}this.element.globalData.renderer.save(!0),n.clip()}},CVMaskElement.prototype.getMaskProperty=MaskElement.prototype.getMaskProperty,CVMaskElement.prototype.destroy=function(){this.element=null};var operationsMap={1:\"source-in\",2:\"source-out\",3:\"source-in\",4:\"source-out\"};function CVShapeData(A,t,e,a){this.styledShapes=[],this.tr=[0,0,0,0,0,0];var r,n=4;\"rc\"===t.ty?n=5:\"el\"===t.ty?n=6:\"sr\"===t.ty&&(n=7),this.sh=ShapePropertyFactory.getShapeProp(A,t,n,A);var l,i=e.length;for(r=0;r<i;r+=1)e[r].closed||(l={transforms:a.addTransformSequence(e[r].transforms),trNodes:[]},this.styledShapes.push(l),e[r].elements.push(l))}function CVShapeElement(A,t,e){this.shapes=[],this.shapesData=A.shapes,this.stylesList=[],this.itemsData=[],this.prevViewData=[],this.shapeModifiers=[],this.processedElements=[],this.transformsManager=new ShapeTransformManager,this.initElement(A,t,e)}function CVTextElement(A,t,e){this.textSpans=[],this.yOffset=0,this.fillColorAnim=!1,this.strokeColorAnim=!1,this.strokeWidthAnim=!1,this.stroke=!1,this.fill=!1,this.justifyOffset=0,this.currentRender=null,this.renderType=\"canvas\",this.values={fill:\"rgba(0,0,0,0)\",stroke:\"rgba(0,0,0,0)\",sWidth:0,fValue:\"\"},this.initElement(A,t,e)}function CVImageElement(A,t,e){this.assetData=t.getAssetData(A.refId),this.img=t.imageLoader.getAsset(this.assetData),this.initElement(A,t,e)}function CVSolidElement(A,t,e){this.initElement(A,t,e)}function CanvasRendererBase(){}function CanvasContext(){this.opacity=-1,this.transform=createTypedArray(\"float32\",16),this.fillStyle=\"\",this.strokeStyle=\"\",this.lineWidth=\"\",this.lineCap=\"\",this.lineJoin=\"\",this.miterLimit=\"\",this.id=Math.random()}function CVContextData(){var A;for(this.stack=[],this.cArrPos=0,this.cTr=new Matrix,A=0;A<15;A+=1){var t=new CanvasContext;this.stack[A]=t}this._length=15,this.nativeContext=null,this.transformMat=new Matrix,this.currentOpacity=1,this.currentFillStyle=\"\",this.appliedFillStyle=\"\",this.currentStrokeStyle=\"\",this.appliedStrokeStyle=\"\",this.currentLineWidth=\"\",this.appliedLineWidth=\"\",this.currentLineCap=\"\",this.appliedLineCap=\"\",this.currentLineJoin=\"\",this.appliedLineJoin=\"\",this.appliedMiterLimit=\"\",this.currentMiterLimit=\"\"}function CVCompElement(A,t,e){this.completeLayers=!1,this.layers=A.layers,this.pendingElements=[],this.elements=createSizedArray(this.layers.length),this.initElement(A,t,e),this.tm=A.tm?PropertyFactory.getProp(this,A.tm,0,t.frameRate,this):{_placeholder:!0}}function CanvasRenderer(A,t){this.animationItem=A,this.renderConfig={clearCanvas:!t||void 0===t.clearCanvas||t.clearCanvas,context:t&&t.context||null,progressiveLoad:t&&t.progressiveLoad||!1,preserveAspectRatio:t&&t.preserveAspectRatio||\"xMidYMid meet\",imagePreserveAspectRatio:t&&t.imagePreserveAspectRatio||\"xMidYMid slice\",contentVisibility:t&&t.contentVisibility||\"visible\",className:t&&t.className||\"\",id:t&&t.id||\"\",runExpressions:!t||void 0===t.runExpressions||t.runExpressions},this.renderConfig.dpr=t&&t.dpr||1,this.animationItem.wrapper&&(this.renderConfig.dpr=t&&t.dpr||window.devicePixelRatio||1),this.renderedFrame=-1,this.globalData={frameNum:-1,_mdf:!1,renderConfig:this.renderConfig,currentGlobalAlpha:-1},this.contextData=new CVContextData,this.elements=[],this.pendingElements=[],this.transformMat=new Matrix,this.completeLayers=!1,this.rendererType=\"canvas\",this.renderConfig.clearCanvas&&(this.ctxTransform=this.contextData.transform.bind(this.contextData),this.ctxOpacity=this.contextData.opacity.bind(this.contextData),this.ctxFillStyle=this.contextData.fillStyle.bind(this.contextData),this.ctxStrokeStyle=this.contextData.strokeStyle.bind(this.contextData),this.ctxLineWidth=this.contextData.lineWidth.bind(this.contextData),this.ctxLineCap=this.contextData.lineCap.bind(this.contextData),this.ctxLineJoin=this.contextData.lineJoin.bind(this.contextData),this.ctxMiterLimit=this.contextData.miterLimit.bind(this.contextData),this.ctxFill=this.contextData.fill.bind(this.contextData),this.ctxFillRect=this.contextData.fillRect.bind(this.contextData),this.ctxStroke=this.contextData.stroke.bind(this.contextData),this.save=this.contextData.save.bind(this.contextData))}function HBaseElement(){}function HSolidElement(A,t,e){this.initElement(A,t,e)}function HShapeElement(A,t,e){this.shapes=[],this.shapesData=A.shapes,this.stylesList=[],this.shapeModifiers=[],this.itemsData=[],this.processedElements=[],this.animatedContents=[],this.shapesContainer=createNS(\"g\"),this.initElement(A,t,e),this.prevViewData=[],this.currentBBox={x:999999,y:-999999,h:0,w:0}}function HTextElement(A,t,e){this.textSpans=[],this.textPaths=[],this.currentBBox={x:999999,y:-999999,h:0,w:0},this.renderType=\"svg\",this.isMasked=!1,this.initElement(A,t,e)}function HCameraElement(A,t,e){this.initFrame(),this.initBaseData(A,t,e),this.initHierarchy();var a=PropertyFactory.getProp;if(this.pe=a(this,A.pe,0,0,this),A.ks.p.s?(this.px=a(this,A.ks.p.x,1,0,this),this.py=a(this,A.ks.p.y,1,0,this),this.pz=a(this,A.ks.p.z,1,0,this)):this.p=a(this,A.ks.p,1,0,this),A.ks.a&&(this.a=a(this,A.ks.a,1,0,this)),A.ks.or.k.length&&A.ks.or.k[0].to){var r,n=A.ks.or.k.length;for(r=0;r<n;r+=1)A.ks.or.k[r].to=null,A.ks.or.k[r].ti=null}this.or=a(this,A.ks.or,1,degToRads,this),this.or.sh=!0,this.rx=a(this,A.ks.rx,0,degToRads,this),this.ry=a(this,A.ks.ry,0,degToRads,this),this.rz=a(this,A.ks.rz,0,degToRads,this),this.mat=new Matrix,this._prevMat=new Matrix,this._isFirstFrame=!0,this.finalTransform={mProp:this}}function HImageElement(A,t,e){this.assetData=t.getAssetData(A.refId),this.initElement(A,t,e)}function HybridRendererBase(A,t){this.animationItem=A,this.layers=null,this.renderedFrame=-1,this.renderConfig={className:t&&t.className||\"\",imagePreserveAspectRatio:t&&t.imagePreserveAspectRatio||\"xMidYMid slice\",hideOnTransparent:!(t&&!1===t.hideOnTransparent),filterSize:{width:t&&t.filterSize&&t.filterSize.width||\"400%\",height:t&&t.filterSize&&t.filterSize.height||\"400%\",x:t&&t.filterSize&&t.filterSize.x||\"-100%\",y:t&&t.filterSize&&t.filterSize.y||\"-100%\"}},this.globalData={_mdf:!1,frameNum:-1,renderConfig:this.renderConfig},this.pendingElements=[],this.elements=[],this.threeDElements=[],this.destroyed=!1,this.camera=null,this.supports3d=!0,this.rendererType=\"html\"}function HCompElement(A,t,e){this.layers=A.layers,this.supports3d=!A.hasMask,this.completeLayers=!1,this.pendingElements=[],this.elements=this.layers?createSizedArray(this.layers.length):[],this.initElement(A,t,e),this.tm=A.tm?PropertyFactory.getProp(this,A.tm,0,t.frameRate,this):{_placeholder:!0}}function HybridRenderer(A,t){this.animationItem=A,this.layers=null,this.renderedFrame=-1,this.renderConfig={className:t&&t.className||\"\",imagePreserveAspectRatio:t&&t.imagePreserveAspectRatio||\"xMidYMid slice\",hideOnTransparent:!(t&&!1===t.hideOnTransparent),filterSize:{width:t&&t.filterSize&&t.filterSize.width||\"400%\",height:t&&t.filterSize&&t.filterSize.height||\"400%\",x:t&&t.filterSize&&t.filterSize.x||\"-100%\",y:t&&t.filterSize&&t.filterSize.y||\"-100%\"},runExpressions:!t||void 0===t.runExpressions||t.runExpressions},this.globalData={_mdf:!1,frameNum:-1,renderConfig:this.renderConfig},this.pendingElements=[],this.elements=[],this.threeDElements=[],this.destroyed=!1,this.camera=null,this.supports3d=!0,this.rendererType=\"html\"}CVBaseElement.prototype={createElements:function(){},initRendererElement:function(){},createContainerElements:function(){if(this.data.tt>=1){this.buffers=[];var A=this.globalData.canvasContext,t=assetLoader.createCanvas(A.canvas.width,A.canvas.height);this.buffers.push(t);var e=assetLoader.createCanvas(A.canvas.width,A.canvas.height);this.buffers.push(e),this.data.tt>=3&&!document._isProxy&&assetLoader.loadLumaCanvas()}this.canvasContext=this.globalData.canvasContext,this.transformCanvas=this.globalData.transformCanvas,this.renderableEffectsManager=new CVEffects(this),this.searchEffectTransforms()},createContent:function(){},setBlendMode:function(){var A=this.globalData;if(A.blendMode!==this.data.bm){A.blendMode=this.data.bm;var t=getBlendMode(this.data.bm);A.canvasContext.globalCompositeOperation=t}},createRenderableComponents:function(){this.maskManager=new CVMaskElement(this.data,this),this.transformEffects=this.renderableEffectsManager.getEffects(effectTypes.TRANSFORM_EFFECT)},hideElement:function(){this.hidden||this.isInRange&&!this.isTransparent||(this.hidden=!0)},showElement:function(){this.isInRange&&!this.isTransparent&&(this.hidden=!1,this._isFirstFrame=!0,this.maskManager._isFirstFrame=!0)},clearCanvas:function(A){A.clearRect(this.transformCanvas.tx,this.transformCanvas.ty,this.transformCanvas.w*this.transformCanvas.sx,this.transformCanvas.h*this.transformCanvas.sy)},prepareLayer:function(){if(this.data.tt>=1){var A=this.buffers[0].getContext(\"2d\");this.clearCanvas(A),A.drawImage(this.canvasContext.canvas,0,0),this.currentTransform=this.canvasContext.getTransform(),this.canvasContext.setTransform(1,0,0,1,0,0),this.clearCanvas(this.canvasContext),this.canvasContext.setTransform(this.currentTransform)}},exitLayer:function(){if(this.data.tt>=1){var A=this.buffers[1],t=A.getContext(\"2d\");if(this.clearCanvas(t),t.drawImage(this.canvasContext.canvas,0,0),this.canvasContext.setTransform(1,0,0,1,0,0),this.clearCanvas(this.canvasContext),this.canvasContext.setTransform(this.currentTransform),this.comp.getElementById(\"tp\"in this.data?this.data.tp:this.data.ind-1).renderFrame(!0),this.canvasContext.setTransform(1,0,0,1,0,0),this.data.tt>=3&&!document._isProxy){var e=assetLoader.getLumaCanvas(this.canvasContext.canvas);e.getContext(\"2d\").drawImage(this.canvasContext.canvas,0,0),this.clearCanvas(this.canvasContext),this.canvasContext.drawImage(e,0,0)}this.canvasContext.globalCompositeOperation=operationsMap[this.data.tt],this.canvasContext.drawImage(A,0,0),this.canvasContext.globalCompositeOperation=\"destination-over\",this.canvasContext.drawImage(this.buffers[0],0,0),this.canvasContext.setTransform(this.currentTransform),this.canvasContext.globalCompositeOperation=\"source-over\"}},renderFrame:function(A){if(!this.hidden&&!this.data.hd&&(1!==this.data.td||A)){this.renderTransform(),this.renderRenderable(),this.renderLocalTransform(),this.setBlendMode();var t=0===this.data.ty;this.prepareLayer(),this.globalData.renderer.save(t),this.globalData.renderer.ctxTransform(this.finalTransform.localMat.props),this.globalData.renderer.ctxOpacity(this.finalTransform.localOpacity),this.renderInnerContent(),this.globalData.renderer.restore(t),this.exitLayer(),this.maskManager.hasMasks&&this.globalData.renderer.restore(!0),this._isFirstFrame&&(this._isFirstFrame=!1)}},destroy:function(){this.canvasContext=null,this.data=null,this.globalData=null,this.maskManager.destroy()},mHelper:new Matrix},CVBaseElement.prototype.hide=CVBaseElement.prototype.hideElement,CVBaseElement.prototype.show=CVBaseElement.prototype.showElement,CVShapeData.prototype.setAsAnimated=SVGShapeData.prototype.setAsAnimated,extendPrototype([BaseElement,TransformElement,CVBaseElement,IShapeElement,HierarchyElement,FrameElement,RenderableElement],CVShapeElement),CVShapeElement.prototype.initElement=RenderableDOMElement.prototype.initElement,CVShapeElement.prototype.transformHelper={opacity:1,_opMdf:!1},CVShapeElement.prototype.dashResetter=[],CVShapeElement.prototype.createContent=function(){this.searchShapes(this.shapesData,this.itemsData,this.prevViewData,!0,[])},CVShapeElement.prototype.createStyleElement=function(A,t){var e={data:A,type:A.ty,preTransforms:this.transformsManager.addTransformSequence(t),transforms:[],elements:[],closed:!0===A.hd},a={};if(\"fl\"===A.ty||\"st\"===A.ty?(a.c=PropertyFactory.getProp(this,A.c,1,255,this),a.c.k||(e.co=\"rgb(\"+bmFloor(a.c.v[0])+\",\"+bmFloor(a.c.v[1])+\",\"+bmFloor(a.c.v[2])+\")\")):\"gf\"!==A.ty&&\"gs\"!==A.ty||(a.s=PropertyFactory.getProp(this,A.s,1,null,this),a.e=PropertyFactory.getProp(this,A.e,1,null,this),a.h=PropertyFactory.getProp(this,A.h||{k:0},0,.01,this),a.a=PropertyFactory.getProp(this,A.a||{k:0},0,degToRads,this),a.g=new GradientProperty(this,A.g,this)),a.o=PropertyFactory.getProp(this,A.o,0,.01,this),\"st\"===A.ty||\"gs\"===A.ty){if(e.lc=lineCapEnum[A.lc||2],e.lj=lineJoinEnum[A.lj||2],1==A.lj&&(e.ml=A.ml),a.w=PropertyFactory.getProp(this,A.w,0,null,this),a.w.k||(e.wi=a.w.v),A.d){var r=new DashProperty(this,A.d,\"canvas\",this);a.d=r,a.d.k||(e.da=a.d.dashArray,e.do=a.d.dashoffset[0])}}else e.r=2===A.r?\"evenodd\":\"nonzero\";return this.stylesList.push(e),a.style=e,a},CVShapeElement.prototype.createGroupElement=function(){return{it:[],prevViewData:[]}},CVShapeElement.prototype.createTransformElement=function(A){return{transform:{opacity:1,_opMdf:!1,key:this.transformsManager.getNewKey(),op:PropertyFactory.getProp(this,A.o,0,.01,this),mProps:TransformPropertyFactory.getTransformProperty(this,A,this)}}},CVShapeElement.prototype.createShapeElement=function(A){var t=new CVShapeData(this,A,this.stylesList,this.transformsManager);return this.shapes.push(t),this.addShapeToModifiers(t),t},CVShapeElement.prototype.reloadShapes=function(){var A;this._isFirstFrame=!0;var t=this.itemsData.length;for(A=0;A<t;A+=1)this.prevViewData[A]=this.itemsData[A];for(this.searchShapes(this.shapesData,this.itemsData,this.prevViewData,!0,[]),t=this.dynamicProperties.length,A=0;A<t;A+=1)this.dynamicProperties[A].getValue();this.renderModifiers(),this.transformsManager.processSequences(this._isFirstFrame)},CVShapeElement.prototype.addTransformToStyleList=function(A){var t,e=this.stylesList.length;for(t=0;t<e;t+=1)this.stylesList[t].closed||this.stylesList[t].transforms.push(A)},CVShapeElement.prototype.removeTransformFromStyleList=function(){var A,t=this.stylesList.length;for(A=0;A<t;A+=1)this.stylesList[A].closed||this.stylesList[A].transforms.pop()},CVShapeElement.prototype.closeStyles=function(A){var t,e=A.length;for(t=0;t<e;t+=1)A[t].closed=!0},CVShapeElement.prototype.searchShapes=function(A,t,e,a,r){var n,l,i,o,s,p,c=A.length-1,u=[],d=[],h=[].concat(r);for(n=c;n>=0;n-=1){if((o=this.searchProcessedElement(A[n]))?t[n]=e[o-1]:A[n]._shouldRender=a,\"fl\"===A[n].ty||\"st\"===A[n].ty||\"gf\"===A[n].ty||\"gs\"===A[n].ty)o?t[n].style.closed=!1:t[n]=this.createStyleElement(A[n],h),u.push(t[n].style);else if(\"gr\"===A[n].ty){if(o)for(i=t[n].it.length,l=0;l<i;l+=1)t[n].prevViewData[l]=t[n].it[l];else t[n]=this.createGroupElement(A[n]);this.searchShapes(A[n].it,t[n].it,t[n].prevViewData,a,h)}else\"tr\"===A[n].ty?(o||(p=this.createTransformElement(A[n]),t[n]=p),h.push(t[n]),this.addTransformToStyleList(t[n])):\"sh\"===A[n].ty||\"rc\"===A[n].ty||\"el\"===A[n].ty||\"sr\"===A[n].ty?o||(t[n]=this.createShapeElement(A[n])):\"tm\"===A[n].ty||\"rd\"===A[n].ty||\"pb\"===A[n].ty||\"zz\"===A[n].ty||\"op\"===A[n].ty?(o?(s=t[n]).closed=!1:((s=ShapeModifiers.getModifier(A[n].ty)).init(this,A[n]),t[n]=s,this.shapeModifiers.push(s)),d.push(s)):\"rp\"===A[n].ty&&(o?(s=t[n]).closed=!0:(s=ShapeModifiers.getModifier(A[n].ty),t[n]=s,s.init(this,A,n,t),this.shapeModifiers.push(s),a=!1),d.push(s));this.addProcessedElement(A[n],n+1)}for(this.removeTransformFromStyleList(),this.closeStyles(u),c=d.length,n=0;n<c;n+=1)d[n].closed=!0},CVShapeElement.prototype.renderInnerContent=function(){this.transformHelper.opacity=1,this.transformHelper._opMdf=!1,this.renderModifiers(),this.transformsManager.processSequences(this._isFirstFrame),this.renderShape(this.transformHelper,this.shapesData,this.itemsData,!0)},CVShapeElement.prototype.renderShapeTransform=function(A,t){(A._opMdf||t.op._mdf||this._isFirstFrame)&&(t.opacity=A.opacity,t.opacity*=t.op.v,t._opMdf=!0)},CVShapeElement.prototype.drawLayer=function(){var A,t,e,a,r,n,l,i,o,s=this.stylesList.length,p=this.globalData.renderer,c=this.globalData.canvasContext;for(A=0;A<s;A+=1)if((\"st\"!==(i=(o=this.stylesList[A]).type)&&\"gs\"!==i||0!==o.wi)&&o.data._shouldRender&&0!==o.coOp&&0!==this.globalData.currentGlobalAlpha){for(p.save(),n=o.elements,\"st\"===i||\"gs\"===i?(p.ctxStrokeStyle(\"st\"===i?o.co:o.grd),p.ctxLineWidth(o.wi),p.ctxLineCap(o.lc),p.ctxLineJoin(o.lj),p.ctxMiterLimit(o.ml||0)):p.ctxFillStyle(\"fl\"===i?o.co:o.grd),p.ctxOpacity(o.coOp),\"st\"!==i&&\"gs\"!==i&&c.beginPath(),p.ctxTransform(o.preTransforms.finalTransform.props),e=n.length,t=0;t<e;t+=1){for(\"st\"!==i&&\"gs\"!==i||(c.beginPath(),o.da&&(c.setLineDash(o.da),c.lineDashOffset=o.do)),r=(l=n[t].trNodes).length,a=0;a<r;a+=1)\"m\"===l[a].t?c.moveTo(l[a].p[0],l[a].p[1]):\"c\"===l[a].t?c.bezierCurveTo(l[a].pts[0],l[a].pts[1],l[a].pts[2],l[a].pts[3],l[a].pts[4],l[a].pts[5]):c.closePath();\"st\"!==i&&\"gs\"!==i||(p.ctxStroke(),o.da&&c.setLineDash(this.dashResetter))}\"st\"!==i&&\"gs\"!==i&&this.globalData.renderer.ctxFill(o.r),p.restore()}},CVShapeElement.prototype.renderShape=function(A,t,e,a){var r,n;for(n=A,r=t.length-1;r>=0;r-=1)\"tr\"===t[r].ty?(n=e[r].transform,this.renderShapeTransform(A,n)):\"sh\"===t[r].ty||\"el\"===t[r].ty||\"rc\"===t[r].ty||\"sr\"===t[r].ty?this.renderPath(t[r],e[r]):\"fl\"===t[r].ty?this.renderFill(t[r],e[r],n):\"st\"===t[r].ty?this.renderStroke(t[r],e[r],n):\"gf\"===t[r].ty||\"gs\"===t[r].ty?this.renderGradientFill(t[r],e[r],n):\"gr\"===t[r].ty?this.renderShape(n,t[r].it,e[r].it):t[r].ty;a&&this.drawLayer()},CVShapeElement.prototype.renderStyledShape=function(A,t){if(this._isFirstFrame||t._mdf||A.transforms._mdf){var e,a,r,n=A.trNodes,l=t.paths,i=l._length;n.length=0;var o=A.transforms.finalTransform;for(r=0;r<i;r+=1){var s=l.shapes[r];if(s&&s.v){for(a=s._length,e=1;e<a;e+=1)1===e&&n.push({t:\"m\",p:o.applyToPointArray(s.v[0][0],s.v[0][1],0)}),n.push({t:\"c\",pts:o.applyToTriplePoints(s.o[e-1],s.i[e],s.v[e])});1===a&&n.push({t:\"m\",p:o.applyToPointArray(s.v[0][0],s.v[0][1],0)}),s.c&&a&&(n.push({t:\"c\",pts:o.applyToTriplePoints(s.o[e-1],s.i[0],s.v[0])}),n.push({t:\"z\"}))}}A.trNodes=n}},CVShapeElement.prototype.renderPath=function(A,t){if(!0!==A.hd&&A._shouldRender){var e,a=t.styledShapes.length;for(e=0;e<a;e+=1)this.renderStyledShape(t.styledShapes[e],t.sh)}},CVShapeElement.prototype.renderFill=function(A,t,e){var a=t.style;(t.c._mdf||this._isFirstFrame)&&(a.co=\"rgb(\"+bmFloor(t.c.v[0])+\",\"+bmFloor(t.c.v[1])+\",\"+bmFloor(t.c.v[2])+\")\"),(t.o._mdf||e._opMdf||this._isFirstFrame)&&(a.coOp=t.o.v*e.opacity)},CVShapeElement.prototype.renderGradientFill=function(A,t,e){var a,r=t.style;if(!r.grd||t.g._mdf||t.s._mdf||t.e._mdf||1!==A.t&&(t.h._mdf||t.a._mdf)){var n,l=this.globalData.canvasContext,i=t.s.v,o=t.e.v;if(1===A.t)a=l.createLinearGradient(i[0],i[1],o[0],o[1]);else{var s=Math.sqrt(Math.pow(i[0]-o[0],2)+Math.pow(i[1]-o[1],2)),p=Math.atan2(o[1]-i[1],o[0]-i[0]),c=t.h.v;c>=1?c=.99:c<=-1&&(c=-.99);var u=s*c,d=Math.cos(p+t.a.v)*u+i[0],h=Math.sin(p+t.a.v)*u+i[1];a=l.createRadialGradient(d,h,0,i[0],i[1],s)}var S=A.g.p,f=t.g.c,m=1;for(n=0;n<S;n+=1)t.g._hasOpacity&&t.g._collapsable&&(m=t.g.o[2*n+1]),a.addColorStop(f[4*n]/100,\"rgba(\"+f[4*n+1]+\",\"+f[4*n+2]+\",\"+f[4*n+3]+\",\"+m+\")\");r.grd=a}r.coOp=t.o.v*e.opacity},CVShapeElement.prototype.renderStroke=function(A,t,e){var a=t.style,r=t.d;r&&(r._mdf||this._isFirstFrame)&&(a.da=r.dashArray,a.do=r.dashoffset[0]),(t.c._mdf||this._isFirstFrame)&&(a.co=\"rgb(\"+bmFloor(t.c.v[0])+\",\"+bmFloor(t.c.v[1])+\",\"+bmFloor(t.c.v[2])+\")\"),(t.o._mdf||e._opMdf||this._isFirstFrame)&&(a.coOp=t.o.v*e.opacity),(t.w._mdf||this._isFirstFrame)&&(a.wi=t.w.v)},CVShapeElement.prototype.destroy=function(){this.shapesData=null,this.globalData=null,this.canvasContext=null,this.stylesList.length=0,this.itemsData.length=0},extendPrototype([BaseElement,TransformElement,CVBaseElement,HierarchyElement,FrameElement,RenderableElement,ITextElement],CVTextElement),CVTextElement.prototype.tHelper=createTag(\"canvas\").getContext(\"2d\"),CVTextElement.prototype.buildNewText=function(){var A=this.textProperty.currentData;this.renderedLetters=createSizedArray(A.l?A.l.length:0);var t=!1;A.fc?(t=!0,this.values.fill=this.buildColor(A.fc)):this.values.fill=\"rgba(0,0,0,0)\",this.fill=t;var e=!1;A.sc&&(e=!0,this.values.stroke=this.buildColor(A.sc),this.values.sWidth=A.sw);var a,r,n,l,i,o,s,p,c,u,d,h,S=this.globalData.fontManager.getFontByName(A.f),f=A.l,m=this.mHelper;this.stroke=e,this.values.fValue=A.finalSize+\"px \"+this.globalData.fontManager.getFontByName(A.f).fFamily,r=A.finalText.length;var L=this.data.singleShape,W=.001*A.tr*A.finalSize,g=0,y=0,v=!0,b=0;for(a=0;a<r;a+=1){l=(n=this.globalData.fontManager.getCharData(A.finalText[a],S.fStyle,this.globalData.fontManager.getFontByName(A.f).fFamily))&&n.data||{},m.reset(),L&&f[a].n&&(g=-W,y+=A.yOffset,y+=v?1:0,v=!1),c=(s=l.shapes?l.shapes[0].it:[]).length,m.scale(A.finalSize/100,A.finalSize/100),L&&this.applyTextPropertiesToMatrix(A,m,f[a].line,g,y),d=createSizedArray(c-1);var x=0;for(p=0;p<c;p+=1)if(\"sh\"===s[p].ty){for(o=s[p].ks.k.i.length,u=s[p].ks.k,h=[],i=1;i<o;i+=1)1===i&&h.push(m.applyToX(u.v[0][0],u.v[0][1],0),m.applyToY(u.v[0][0],u.v[0][1],0)),h.push(m.applyToX(u.o[i-1][0],u.o[i-1][1],0),m.applyToY(u.o[i-1][0],u.o[i-1][1],0),m.applyToX(u.i[i][0],u.i[i][1],0),m.applyToY(u.i[i][0],u.i[i][1],0),m.applyToX(u.v[i][0],u.v[i][1],0),m.applyToY(u.v[i][0],u.v[i][1],0));h.push(m.applyToX(u.o[i-1][0],u.o[i-1][1],0),m.applyToY(u.o[i-1][0],u.o[i-1][1],0),m.applyToX(u.i[0][0],u.i[0][1],0),m.applyToY(u.i[0][0],u.i[0][1],0),m.applyToX(u.v[0][0],u.v[0][1],0),m.applyToY(u.v[0][0],u.v[0][1],0)),d[x]=h,x+=1}L&&(g+=f[a].l,g+=W),this.textSpans[b]?this.textSpans[b].elem=d:this.textSpans[b]={elem:d},b+=1}},CVTextElement.prototype.renderInnerContent=function(){var A,t,e,a,r,n;this.validateText(),this.canvasContext.font=this.values.fValue,this.globalData.renderer.ctxLineCap(\"butt\"),this.globalData.renderer.ctxLineJoin(\"miter\"),this.globalData.renderer.ctxMiterLimit(4),this.data.singleShape||this.textAnimator.getMeasures(this.textProperty.currentData,this.lettersChangedFlag);var l,i=this.textAnimator.renderedLetters,o=this.textProperty.currentData.l;t=o.length;var s,p,c=null,u=null,d=null,h=this.globalData.renderer;for(A=0;A<t;A+=1)if(!o[A].n){if((l=i[A])&&(h.save(),h.ctxTransform(l.p),h.ctxOpacity(l.o)),this.fill){for(l&&l.fc?c!==l.fc&&(h.ctxFillStyle(l.fc),c=l.fc):c!==this.values.fill&&(c=this.values.fill,h.ctxFillStyle(this.values.fill)),a=(s=this.textSpans[A].elem).length,this.globalData.canvasContext.beginPath(),e=0;e<a;e+=1)for(n=(p=s[e]).length,this.globalData.canvasContext.moveTo(p[0],p[1]),r=2;r<n;r+=6)this.globalData.canvasContext.bezierCurveTo(p[r],p[r+1],p[r+2],p[r+3],p[r+4],p[r+5]);this.globalData.canvasContext.closePath(),h.ctxFill()}if(this.stroke){for(l&&l.sw?d!==l.sw&&(d=l.sw,h.ctxLineWidth(l.sw)):d!==this.values.sWidth&&(d=this.values.sWidth,h.ctxLineWidth(this.values.sWidth)),l&&l.sc?u!==l.sc&&(u=l.sc,h.ctxStrokeStyle(l.sc)):u!==this.values.stroke&&(u=this.values.stroke,h.ctxStrokeStyle(this.values.stroke)),a=(s=this.textSpans[A].elem).length,this.globalData.canvasContext.beginPath(),e=0;e<a;e+=1)for(n=(p=s[e]).length,this.globalData.canvasContext.moveTo(p[0],p[1]),r=2;r<n;r+=6)this.globalData.canvasContext.bezierCurveTo(p[r],p[r+1],p[r+2],p[r+3],p[r+4],p[r+5]);this.globalData.canvasContext.closePath(),h.ctxStroke()}l&&this.globalData.renderer.restore()}},extendPrototype([BaseElement,TransformElement,CVBaseElement,HierarchyElement,FrameElement,RenderableElement],CVImageElement),CVImageElement.prototype.initElement=SVGShapeElement.prototype.initElement,CVImageElement.prototype.prepareFrame=IImageElement.prototype.prepareFrame,CVImageElement.prototype.createContent=function(){if(this.img.width&&(this.assetData.w!==this.img.width||this.assetData.h!==this.img.height)){var A=createTag(\"canvas\");A.width=this.assetData.w,A.height=this.assetData.h;var t,e,a=A.getContext(\"2d\"),r=this.img.width,n=this.img.height,l=r/n,i=this.assetData.w/this.assetData.h,o=this.assetData.pr||this.globalData.renderConfig.imagePreserveAspectRatio;l>i&&\"xMidYMid slice\"===o||l<i&&\"xMidYMid slice\"!==o?t=(e=n)*i:e=(t=r)/i,a.drawImage(this.img,(r-t)/2,(n-e)/2,t,e,0,0,this.assetData.w,this.assetData.h),this.img=A}},CVImageElement.prototype.renderInnerContent=function(){this.canvasContext.drawImage(this.img,0,0)},CVImageElement.prototype.destroy=function(){this.img=null},extendPrototype([BaseElement,TransformElement,CVBaseElement,HierarchyElement,FrameElement,RenderableElement],CVSolidElement),CVSolidElement.prototype.initElement=SVGShapeElement.prototype.initElement,CVSolidElement.prototype.prepareFrame=IImageElement.prototype.prepareFrame,CVSolidElement.prototype.renderInnerContent=function(){this.globalData.renderer.ctxFillStyle(this.data.sc),this.globalData.renderer.ctxFillRect(0,0,this.data.sw,this.data.sh)},extendPrototype([BaseRenderer],CanvasRendererBase),CanvasRendererBase.prototype.createShape=function(A){return new CVShapeElement(A,this.globalData,this)},CanvasRendererBase.prototype.createText=function(A){return new CVTextElement(A,this.globalData,this)},CanvasRendererBase.prototype.createImage=function(A){return new CVImageElement(A,this.globalData,this)},CanvasRendererBase.prototype.createSolid=function(A){return new CVSolidElement(A,this.globalData,this)},CanvasRendererBase.prototype.createNull=SVGRenderer.prototype.createNull,CanvasRendererBase.prototype.ctxTransform=function(A){1===A[0]&&0===A[1]&&0===A[4]&&1===A[5]&&0===A[12]&&0===A[13]||this.canvasContext.transform(A[0],A[1],A[4],A[5],A[12],A[13])},CanvasRendererBase.prototype.ctxOpacity=function(A){this.canvasContext.globalAlpha*=A<0?0:A},CanvasRendererBase.prototype.ctxFillStyle=function(A){this.canvasContext.fillStyle=A},CanvasRendererBase.prototype.ctxStrokeStyle=function(A){this.canvasContext.strokeStyle=A},CanvasRendererBase.prototype.ctxLineWidth=function(A){this.canvasContext.lineWidth=A},CanvasRendererBase.prototype.ctxLineCap=function(A){this.canvasContext.lineCap=A},CanvasRendererBase.prototype.ctxLineJoin=function(A){this.canvasContext.lineJoin=A},CanvasRendererBase.prototype.ctxMiterLimit=function(A){this.canvasContext.miterLimit=A},CanvasRendererBase.prototype.ctxFill=function(A){this.canvasContext.fill(A)},CanvasRendererBase.prototype.ctxFillRect=function(A,t,e,a){this.canvasContext.fillRect(A,t,e,a)},CanvasRendererBase.prototype.ctxStroke=function(){this.canvasContext.stroke()},CanvasRendererBase.prototype.reset=function(){this.renderConfig.clearCanvas?this.contextData.reset():this.canvasContext.restore()},CanvasRendererBase.prototype.save=function(){this.canvasContext.save()},CanvasRendererBase.prototype.restore=function(A){this.renderConfig.clearCanvas?(A&&(this.globalData.blendMode=\"source-over\"),this.contextData.restore(A)):this.canvasContext.restore()},CanvasRendererBase.prototype.configAnimation=function(A){if(this.animationItem.wrapper){this.animationItem.container=createTag(\"canvas\");var t=this.animationItem.container.style;t.width=\"100%\",t.height=\"100%\";var e=\"0px 0px 0px\";t.transformOrigin=e,t.mozTransformOrigin=e,t.webkitTransformOrigin=e,t[\"-webkit-transform\"]=e,t.contentVisibility=this.renderConfig.contentVisibility,this.animationItem.wrapper.appendChild(this.animationItem.container),this.canvasContext=this.animationItem.container.getContext(\"2d\"),this.renderConfig.className&&this.animationItem.container.setAttribute(\"class\",this.renderConfig.className),this.renderConfig.id&&this.animationItem.container.setAttribute(\"id\",this.renderConfig.id)}else this.canvasContext=this.renderConfig.context;this.contextData.setContext(this.canvasContext),this.data=A,this.layers=A.layers,this.transformCanvas={w:A.w,h:A.h,sx:0,sy:0,tx:0,ty:0},this.setupGlobalData(A,document.body),this.globalData.canvasContext=this.canvasContext,this.globalData.renderer=this,this.globalData.isDashed=!1,this.globalData.progressiveLoad=this.renderConfig.progressiveLoad,this.globalData.transformCanvas=this.transformCanvas,this.elements=createSizedArray(A.layers.length),this.updateContainerSize()},CanvasRendererBase.prototype.updateContainerSize=function(A,t){var e,a,r,n;if(this.reset(),A?(e=A,a=t,this.canvasContext.canvas.width=e,this.canvasContext.canvas.height=a):(this.animationItem.wrapper&&this.animationItem.container?(e=this.animationItem.wrapper.offsetWidth,a=this.animationItem.wrapper.offsetHeight):(e=this.canvasContext.canvas.width,a=this.canvasContext.canvas.height),this.canvasContext.canvas.width=e*this.renderConfig.dpr,this.canvasContext.canvas.height=a*this.renderConfig.dpr),-1!==this.renderConfig.preserveAspectRatio.indexOf(\"meet\")||-1!==this.renderConfig.preserveAspectRatio.indexOf(\"slice\")){var l=this.renderConfig.preserveAspectRatio.split(\" \"),i=l[1]||\"meet\",o=l[0]||\"xMidYMid\",s=o.substr(0,4),p=o.substr(4);r=e/a,(n=this.transformCanvas.w/this.transformCanvas.h)>r&&\"meet\"===i||n<r&&\"slice\"===i?(this.transformCanvas.sx=e/(this.transformCanvas.w/this.renderConfig.dpr),this.transformCanvas.sy=e/(this.transformCanvas.w/this.renderConfig.dpr)):(this.transformCanvas.sx=a/(this.transformCanvas.h/this.renderConfig.dpr),this.transformCanvas.sy=a/(this.transformCanvas.h/this.renderConfig.dpr)),this.transformCanvas.tx=\"xMid\"===s&&(n<r&&\"meet\"===i||n>r&&\"slice\"===i)?(e-this.transformCanvas.w*(a/this.transformCanvas.h))/2*this.renderConfig.dpr:\"xMax\"===s&&(n<r&&\"meet\"===i||n>r&&\"slice\"===i)?(e-this.transformCanvas.w*(a/this.transformCanvas.h))*this.renderConfig.dpr:0,this.transformCanvas.ty=\"YMid\"===p&&(n>r&&\"meet\"===i||n<r&&\"slice\"===i)?(a-this.transformCanvas.h*(e/this.transformCanvas.w))/2*this.renderConfig.dpr:\"YMax\"===p&&(n>r&&\"meet\"===i||n<r&&\"slice\"===i)?(a-this.transformCanvas.h*(e/this.transformCanvas.w))*this.renderConfig.dpr:0}else\"none\"===this.renderConfig.preserveAspectRatio?(this.transformCanvas.sx=e/(this.transformCanvas.w/this.renderConfig.dpr),this.transformCanvas.sy=a/(this.transformCanvas.h/this.renderConfig.dpr),this.transformCanvas.tx=0,this.transformCanvas.ty=0):(this.transformCanvas.sx=this.renderConfig.dpr,this.transformCanvas.sy=this.renderConfig.dpr,this.transformCanvas.tx=0,this.transformCanvas.ty=0);this.transformCanvas.props=[this.transformCanvas.sx,0,0,0,0,this.transformCanvas.sy,0,0,0,0,1,0,this.transformCanvas.tx,this.transformCanvas.ty,0,1],this.ctxTransform(this.transformCanvas.props),this.canvasContext.beginPath(),this.canvasContext.rect(0,0,this.transformCanvas.w,this.transformCanvas.h),this.canvasContext.closePath(),this.canvasContext.clip(),this.renderFrame(this.renderedFrame,!0)},CanvasRendererBase.prototype.destroy=function(){var A;for(this.renderConfig.clearCanvas&&this.animationItem.wrapper&&(this.animationItem.wrapper.innerText=\"\"),A=(this.layers?this.layers.length:0)-1;A>=0;A-=1)this.elements[A]&&this.elements[A].destroy&&this.elements[A].destroy();this.elements.length=0,this.globalData.canvasContext=null,this.animationItem.container=null,this.destroyed=!0},CanvasRendererBase.prototype.renderFrame=function(A,t){if((this.renderedFrame!==A||!0!==this.renderConfig.clearCanvas||t)&&!this.destroyed&&-1!==A){var e;this.renderedFrame=A,this.globalData.frameNum=A-this.animationItem._isFirstFrame,this.globalData.frameId+=1,this.globalData._mdf=!this.renderConfig.clearCanvas||t,this.globalData.projectInterface.currentFrame=A;var a=this.layers.length;for(this.completeLayers||this.checkLayers(A),e=a-1;e>=0;e-=1)(this.completeLayers||this.elements[e])&&this.elements[e].prepareFrame(A-this.layers[e].st);if(this.globalData._mdf){for(!0===this.renderConfig.clearCanvas?this.canvasContext.clearRect(0,0,this.transformCanvas.w,this.transformCanvas.h):this.save(),e=a-1;e>=0;e-=1)(this.completeLayers||this.elements[e])&&this.elements[e].renderFrame();!0!==this.renderConfig.clearCanvas&&this.restore()}}},CanvasRendererBase.prototype.buildItem=function(A){var t=this.elements;if(!t[A]&&99!==this.layers[A].ty){var e=this.createItem(this.layers[A],this,this.globalData);t[A]=e,e.initExpressions()}},CanvasRendererBase.prototype.checkPendingElements=function(){for(;this.pendingElements.length;)this.pendingElements.pop().checkParenting()},CanvasRendererBase.prototype.hide=function(){this.animationItem.container.style.display=\"none\"},CanvasRendererBase.prototype.show=function(){this.animationItem.container.style.display=\"block\"},CVContextData.prototype.duplicate=function(){var A=2*this._length,t=0;for(t=this._length;t<A;t+=1)this.stack[t]=new CanvasContext;this._length=A},CVContextData.prototype.reset=function(){this.cArrPos=0,this.cTr.reset(),this.stack[this.cArrPos].opacity=1},CVContextData.prototype.restore=function(A){this.cArrPos-=1;var t,e=this.stack[this.cArrPos],a=e.transform,r=this.cTr.props;for(t=0;t<16;t+=1)r[t]=a[t];if(A){this.nativeContext.restore();var n=this.stack[this.cArrPos+1];this.appliedFillStyle=n.fillStyle,this.appliedStrokeStyle=n.strokeStyle,this.appliedLineWidth=n.lineWidth,this.appliedLineCap=n.lineCap,this.appliedLineJoin=n.lineJoin,this.appliedMiterLimit=n.miterLimit}this.nativeContext.setTransform(a[0],a[1],a[4],a[5],a[12],a[13]),(A||-1!==e.opacity&&this.currentOpacity!==e.opacity)&&(this.nativeContext.globalAlpha=e.opacity,this.currentOpacity=e.opacity),this.currentFillStyle=e.fillStyle,this.currentStrokeStyle=e.strokeStyle,this.currentLineWidth=e.lineWidth,this.currentLineCap=e.lineCap,this.currentLineJoin=e.lineJoin,this.currentMiterLimit=e.miterLimit},CVContextData.prototype.save=function(A){A&&this.nativeContext.save();var t=this.cTr.props;this._length<=this.cArrPos&&this.duplicate();var e,a=this.stack[this.cArrPos];for(e=0;e<16;e+=1)a.transform[e]=t[e];this.cArrPos+=1;var r=this.stack[this.cArrPos];r.opacity=a.opacity,r.fillStyle=a.fillStyle,r.strokeStyle=a.strokeStyle,r.lineWidth=a.lineWidth,r.lineCap=a.lineCap,r.lineJoin=a.lineJoin,r.miterLimit=a.miterLimit},CVContextData.prototype.setOpacity=function(A){this.stack[this.cArrPos].opacity=A},CVContextData.prototype.setContext=function(A){this.nativeContext=A},CVContextData.prototype.fillStyle=function(A){this.stack[this.cArrPos].fillStyle!==A&&(this.currentFillStyle=A,this.stack[this.cArrPos].fillStyle=A)},CVContextData.prototype.strokeStyle=function(A){this.stack[this.cArrPos].strokeStyle!==A&&(this.currentStrokeStyle=A,this.stack[this.cArrPos].strokeStyle=A)},CVContextData.prototype.lineWidth=function(A){this.stack[this.cArrPos].lineWidth!==A&&(this.currentLineWidth=A,this.stack[this.cArrPos].lineWidth=A)},CVContextData.prototype.lineCap=function(A){this.stack[this.cArrPos].lineCap!==A&&(this.currentLineCap=A,this.stack[this.cArrPos].lineCap=A)},CVContextData.prototype.lineJoin=function(A){this.stack[this.cArrPos].lineJoin!==A&&(this.currentLineJoin=A,this.stack[this.cArrPos].lineJoin=A)},CVContextData.prototype.miterLimit=function(A){this.stack[this.cArrPos].miterLimit!==A&&(this.currentMiterLimit=A,this.stack[this.cArrPos].miterLimit=A)},CVContextData.prototype.transform=function(A){this.transformMat.cloneFromProps(A);var t=this.cTr;this.transformMat.multiply(t),t.cloneFromProps(this.transformMat.props);var e=t.props;this.nativeContext.setTransform(e[0],e[1],e[4],e[5],e[12],e[13])},CVContextData.prototype.opacity=function(A){var t=this.stack[this.cArrPos].opacity;t*=A<0?0:A,this.stack[this.cArrPos].opacity!==t&&(this.currentOpacity!==A&&(this.nativeContext.globalAlpha=A,this.currentOpacity=A),this.stack[this.cArrPos].opacity=t)},CVContextData.prototype.fill=function(A){this.appliedFillStyle!==this.currentFillStyle&&(this.appliedFillStyle=this.currentFillStyle,this.nativeContext.fillStyle=this.appliedFillStyle),this.nativeContext.fill(A)},CVContextData.prototype.fillRect=function(A,t,e,a){this.appliedFillStyle!==this.currentFillStyle&&(this.appliedFillStyle=this.currentFillStyle,this.nativeContext.fillStyle=this.appliedFillStyle),this.nativeContext.fillRect(A,t,e,a)},CVContextData.prototype.stroke=function(){this.appliedStrokeStyle!==this.currentStrokeStyle&&(this.appliedStrokeStyle=this.currentStrokeStyle,this.nativeContext.strokeStyle=this.appliedStrokeStyle),this.appliedLineWidth!==this.currentLineWidth&&(this.appliedLineWidth=this.currentLineWidth,this.nativeContext.lineWidth=this.appliedLineWidth),this.appliedLineCap!==this.currentLineCap&&(this.appliedLineCap=this.currentLineCap,this.nativeContext.lineCap=this.appliedLineCap),this.appliedLineJoin!==this.currentLineJoin&&(this.appliedLineJoin=this.currentLineJoin,this.nativeContext.lineJoin=this.appliedLineJoin),this.appliedMiterLimit!==this.currentMiterLimit&&(this.appliedMiterLimit=this.currentMiterLimit,this.nativeContext.miterLimit=this.appliedMiterLimit),this.nativeContext.stroke()},extendPrototype([CanvasRendererBase,ICompElement,CVBaseElement],CVCompElement),CVCompElement.prototype.renderInnerContent=function(){var A,t=this.canvasContext;for(t.beginPath(),t.moveTo(0,0),t.lineTo(this.data.w,0),t.lineTo(this.data.w,this.data.h),t.lineTo(0,this.data.h),t.lineTo(0,0),t.clip(),A=this.layers.length-1;A>=0;A-=1)(this.completeLayers||this.elements[A])&&this.elements[A].renderFrame()},CVCompElement.prototype.destroy=function(){var A;for(A=this.layers.length-1;A>=0;A-=1)this.elements[A]&&this.elements[A].destroy();this.layers=null,this.elements=null},CVCompElement.prototype.createComp=function(A){return new CVCompElement(A,this.globalData,this)},extendPrototype([CanvasRendererBase],CanvasRenderer),CanvasRenderer.prototype.createComp=function(A){return new CVCompElement(A,this.globalData,this)},HBaseElement.prototype={checkBlendMode:function(){},initRendererElement:function(){this.baseElement=createTag(this.data.tg||\"div\"),this.data.hasMask?(this.svgElement=createNS(\"svg\"),this.layerElement=createNS(\"g\"),this.maskedElement=this.layerElement,this.svgElement.appendChild(this.layerElement),this.baseElement.appendChild(this.svgElement)):this.layerElement=this.baseElement,styleDiv(this.baseElement)},createContainerElements:function(){this.renderableEffectsManager=new CVEffects(this),this.transformedElement=this.baseElement,this.maskedElement=this.layerElement,this.data.ln&&this.layerElement.setAttribute(\"id\",this.data.ln),this.data.cl&&this.layerElement.setAttribute(\"class\",this.data.cl),0!==this.data.bm&&this.setBlendMode()},renderElement:function(){var A=this.transformedElement?this.transformedElement.style:{};if(this.finalTransform._matMdf){var t=this.finalTransform.mat.toCSS();A.transform=t,A.webkitTransform=t}this.finalTransform._opMdf&&(A.opacity=this.finalTransform.mProp.o.v)},renderFrame:function(){this.data.hd||this.hidden||(this.renderTransform(),this.renderRenderable(),this.renderElement(),this.renderInnerContent(),this._isFirstFrame&&(this._isFirstFrame=!1))},destroy:function(){this.layerElement=null,this.transformedElement=null,this.matteElement&&(this.matteElement=null),this.maskManager&&(this.maskManager.destroy(),this.maskManager=null)},createRenderableComponents:function(){this.maskManager=new MaskElement(this.data,this,this.globalData)},addEffects:function(){},setMatte:function(){}},HBaseElement.prototype.getBaseElement=SVGBaseElement.prototype.getBaseElement,HBaseElement.prototype.destroyBaseElement=HBaseElement.prototype.destroy,HBaseElement.prototype.buildElementParenting=BaseRenderer.prototype.buildElementParenting,extendPrototype([BaseElement,TransformElement,HBaseElement,HierarchyElement,FrameElement,RenderableDOMElement],HSolidElement),HSolidElement.prototype.createContent=function(){var A;this.data.hasMask?((A=createNS(\"rect\")).setAttribute(\"width\",this.data.sw),A.setAttribute(\"height\",this.data.sh),A.setAttribute(\"fill\",this.data.sc),this.svgElement.setAttribute(\"width\",this.data.sw),this.svgElement.setAttribute(\"height\",this.data.sh)):((A=createTag(\"div\")).style.width=this.data.sw+\"px\",A.style.height=this.data.sh+\"px\",A.style.backgroundColor=this.data.sc),this.layerElement.appendChild(A)},extendPrototype([BaseElement,TransformElement,HSolidElement,SVGShapeElement,HBaseElement,HierarchyElement,FrameElement,RenderableElement],HShapeElement),HShapeElement.prototype._renderShapeFrame=HShapeElement.prototype.renderInnerContent,HShapeElement.prototype.createContent=function(){var A;if(this.baseElement.style.fontSize=0,this.data.hasMask)this.layerElement.appendChild(this.shapesContainer),A=this.svgElement;else{A=createNS(\"svg\");var t=this.comp.data?this.comp.data:this.globalData.compSize;A.setAttribute(\"width\",t.w),A.setAttribute(\"height\",t.h),A.appendChild(this.shapesContainer),this.layerElement.appendChild(A)}this.searchShapes(this.shapesData,this.itemsData,this.prevViewData,this.shapesContainer,0,[],!0),this.filterUniqueShapes(),this.shapeCont=A},HShapeElement.prototype.getTransformedPoint=function(A,t){var e,a=A.length;for(e=0;e<a;e+=1)t=A[e].mProps.v.applyToPointArray(t[0],t[1],0);return t},HShapeElement.prototype.calculateShapeBoundingBox=function(A,t){var e,a,r,n,l,i=A.sh.v,o=A.transformers,s=i._length;if(!(s<=1)){for(e=0;e<s-1;e+=1)a=this.getTransformedPoint(o,i.v[e]),r=this.getTransformedPoint(o,i.o[e]),n=this.getTransformedPoint(o,i.i[e+1]),l=this.getTransformedPoint(o,i.v[e+1]),this.checkBounds(a,r,n,l,t);i.c&&(a=this.getTransformedPoint(o,i.v[e]),r=this.getTransformedPoint(o,i.o[e]),n=this.getTransformedPoint(o,i.i[0]),l=this.getTransformedPoint(o,i.v[0]),this.checkBounds(a,r,n,l,t))}},HShapeElement.prototype.checkBounds=function(A,t,e,a,r){this.getBoundsOfCurve(A,t,e,a);var n=this.shapeBoundingBox;r.x=bmMin(n.left,r.x),r.xMax=bmMax(n.right,r.xMax),r.y=bmMin(n.top,r.y),r.yMax=bmMax(n.bottom,r.yMax)},HShapeElement.prototype.shapeBoundingBox={left:0,right:0,top:0,bottom:0},HShapeElement.prototype.tempBoundingBox={x:0,xMax:0,y:0,yMax:0,width:0,height:0},HShapeElement.prototype.getBoundsOfCurve=function(A,t,e,a){for(var r,n,l,i,o,s,p,c=[[A[0],a[0]],[A[1],a[1]]],u=0;u<2;++u)n=6*A[u]-12*t[u]+6*e[u],r=-3*A[u]+9*t[u]-9*e[u]+3*a[u],l=3*t[u]-3*A[u],n|=0,l|=0,0==(r|=0)&&0===n||(0===r?(i=-l/n)>0&&i<1&&c[u].push(this.calculateF(i,A,t,e,a,u)):(o=n*n-4*l*r)>=0&&((s=(-n+bmSqrt(o))/(2*r))>0&&s<1&&c[u].push(this.calculateF(s,A,t,e,a,u)),(p=(-n-bmSqrt(o))/(2*r))>0&&p<1&&c[u].push(this.calculateF(p,A,t,e,a,u))));this.shapeBoundingBox.left=bmMin.apply(null,c[0]),this.shapeBoundingBox.top=bmMin.apply(null,c[1]),this.shapeBoundingBox.right=bmMax.apply(null,c[0]),this.shapeBoundingBox.bottom=bmMax.apply(null,c[1])},HShapeElement.prototype.calculateF=function(A,t,e,a,r,n){return bmPow(1-A,3)*t[n]+3*bmPow(1-A,2)*A*e[n]+3*(1-A)*bmPow(A,2)*a[n]+bmPow(A,3)*r[n]},HShapeElement.prototype.calculateBoundingBox=function(A,t){var e,a=A.length;for(e=0;e<a;e+=1)A[e]&&A[e].sh?this.calculateShapeBoundingBox(A[e],t):A[e]&&A[e].it?this.calculateBoundingBox(A[e].it,t):A[e]&&A[e].style&&A[e].w&&this.expandStrokeBoundingBox(A[e].w,t)},HShapeElement.prototype.expandStrokeBoundingBox=function(A,t){var e=0;if(A.keyframes){for(var a=0;a<A.keyframes.length;a+=1){var r=A.keyframes[a].s;r>e&&(e=r)}e*=A.mult}else e=A.v*A.mult;t.x-=e,t.xMax+=e,t.y-=e,t.yMax+=e},HShapeElement.prototype.currentBoxContains=function(A){return this.currentBBox.x<=A.x&&this.currentBBox.y<=A.y&&this.currentBBox.width+this.currentBBox.x>=A.x+A.width&&this.currentBBox.height+this.currentBBox.y>=A.y+A.height},HShapeElement.prototype.renderInnerContent=function(){if(this._renderShapeFrame(),!this.hidden&&(this._isFirstFrame||this._mdf)){var A=this.tempBoundingBox,t=999999;if(A.x=t,A.xMax=-t,A.y=t,A.yMax=-t,this.calculateBoundingBox(this.itemsData,A),A.width=A.xMax<A.x?0:A.xMax-A.x,A.height=A.yMax<A.y?0:A.yMax-A.y,this.currentBoxContains(A))return;var e=!1;if(this.currentBBox.w!==A.width&&(this.currentBBox.w=A.width,this.shapeCont.setAttribute(\"width\",A.width),e=!0),this.currentBBox.h!==A.height&&(this.currentBBox.h=A.height,this.shapeCont.setAttribute(\"height\",A.height),e=!0),e||this.currentBBox.x!==A.x||this.currentBBox.y!==A.y){this.currentBBox.w=A.width,this.currentBBox.h=A.height,this.currentBBox.x=A.x,this.currentBBox.y=A.y,this.shapeCont.setAttribute(\"viewBox\",this.currentBBox.x+\" \"+this.currentBBox.y+\" \"+this.currentBBox.w+\" \"+this.currentBBox.h);var a=this.shapeCont.style,r=\"translate(\"+this.currentBBox.x+\"px,\"+this.currentBBox.y+\"px)\";a.transform=r,a.webkitTransform=r}}},extendPrototype([BaseElement,TransformElement,HBaseElement,HierarchyElement,FrameElement,RenderableDOMElement,ITextElement],HTextElement),HTextElement.prototype.createContent=function(){if(this.isMasked=this.checkMasks(),this.isMasked){this.renderType=\"svg\",this.compW=this.comp.data.w,this.compH=this.comp.data.h,this.svgElement.setAttribute(\"width\",this.compW),this.svgElement.setAttribute(\"height\",this.compH);var A=createNS(\"g\");this.maskedElement.appendChild(A),this.innerElem=A}else this.renderType=\"html\",this.innerElem=this.layerElement;this.checkParenting()},HTextElement.prototype.buildNewText=function(){var A=this.textProperty.currentData;this.renderedLetters=createSizedArray(A.l?A.l.length:0);var t=this.innerElem.style,e=A.fc?this.buildColor(A.fc):\"rgba(0,0,0,0)\";t.fill=e,t.color=e,A.sc&&(t.stroke=this.buildColor(A.sc),t.strokeWidth=A.sw+\"px\");var a,r,n=this.globalData.fontManager.getFontByName(A.f);if(!this.globalData.fontManager.chars)if(t.fontSize=A.finalSize+\"px\",t.lineHeight=A.finalSize+\"px\",n.fClass)this.innerElem.className=n.fClass;else{t.fontFamily=n.fFamily;var l=A.fWeight,i=A.fStyle;t.fontStyle=i,t.fontWeight=l}var o,s,p,c=A.l;r=c.length;var u,d=this.mHelper,h=\"\",S=0;for(a=0;a<r;a+=1){if(this.globalData.fontManager.chars?(this.textPaths[S]?o=this.textPaths[S]:((o=createNS(\"path\")).setAttribute(\"stroke-linecap\",lineCapEnum[1]),o.setAttribute(\"stroke-linejoin\",lineJoinEnum[2]),o.setAttribute(\"stroke-miterlimit\",\"4\")),this.isMasked||(this.textSpans[S]?p=(s=this.textSpans[S]).children[0]:((s=createTag(\"div\")).style.lineHeight=0,(p=createNS(\"svg\")).appendChild(o),styleDiv(s)))):this.isMasked?o=this.textPaths[S]?this.textPaths[S]:createNS(\"text\"):this.textSpans[S]?(s=this.textSpans[S],o=this.textPaths[S]):(styleDiv(s=createTag(\"span\")),styleDiv(o=createTag(\"span\")),s.appendChild(o)),this.globalData.fontManager.chars){var f,m=this.globalData.fontManager.getCharData(A.finalText[a],n.fStyle,this.globalData.fontManager.getFontByName(A.f).fFamily);if(f=m?m.data:null,d.reset(),f&&f.shapes&&f.shapes.length&&(u=f.shapes[0].it,d.scale(A.finalSize/100,A.finalSize/100),h=this.createPathShape(d,u),o.setAttribute(\"d\",h)),this.isMasked)this.innerElem.appendChild(o);else{if(this.innerElem.appendChild(s),f&&f.shapes){document.body.appendChild(p);var L=p.getBBox();p.setAttribute(\"width\",L.width+2),p.setAttribute(\"height\",L.height+2),p.setAttribute(\"viewBox\",L.x-1+\" \"+(L.y-1)+\" \"+(L.width+2)+\" \"+(L.height+2));var W=p.style,g=\"translate(\"+(L.x-1)+\"px,\"+(L.y-1)+\"px)\";W.transform=g,W.webkitTransform=g,c[a].yOffset=L.y-1}else p.setAttribute(\"width\",1),p.setAttribute(\"height\",1);s.appendChild(p)}}else if(o.textContent=c[a].val,o.setAttributeNS(\"http://www.w3.org/XML/1998/namespace\",\"xml:space\",\"preserve\"),this.isMasked)this.innerElem.appendChild(o);else{this.innerElem.appendChild(s);var y=o.style,v=\"translate3d(0,\"+-A.finalSize/1.2+\"px,0)\";y.transform=v,y.webkitTransform=v}this.isMasked?this.textSpans[S]=o:this.textSpans[S]=s,this.textSpans[S].style.display=\"block\",this.textPaths[S]=o,S+=1}for(;S<this.textSpans.length;)this.textSpans[S].style.display=\"none\",S+=1},HTextElement.prototype.renderInnerContent=function(){var A;if(this.validateText(),this.data.singleShape){if(!this._isFirstFrame&&!this.lettersChangedFlag)return;if(this.isMasked&&this.finalTransform._matMdf){this.svgElement.setAttribute(\"viewBox\",-this.finalTransform.mProp.p.v[0]+\" \"+-this.finalTransform.mProp.p.v[1]+\" \"+this.compW+\" \"+this.compH),A=this.svgElement.style;var t=\"translate(\"+-this.finalTransform.mProp.p.v[0]+\"px,\"+-this.finalTransform.mProp.p.v[1]+\"px)\";A.transform=t,A.webkitTransform=t}}if(this.textAnimator.getMeasures(this.textProperty.currentData,this.lettersChangedFlag),this.lettersChangedFlag||this.textAnimator.lettersChangedFlag){var e,a,r,n,l,i=0,o=this.textAnimator.renderedLetters,s=this.textProperty.currentData.l;for(a=s.length,e=0;e<a;e+=1)s[e].n?i+=1:(n=this.textSpans[e],l=this.textPaths[e],r=o[i],i+=1,r._mdf.m&&(this.isMasked?n.setAttribute(\"transform\",r.m):(n.style.webkitTransform=r.m,n.style.transform=r.m)),n.style.opacity=r.o,r.sw&&r._mdf.sw&&l.setAttribute(\"stroke-width\",r.sw),r.sc&&r._mdf.sc&&l.setAttribute(\"stroke\",r.sc),r.fc&&r._mdf.fc&&(l.setAttribute(\"fill\",r.fc),l.style.color=r.fc));if(this.innerElem.getBBox&&!this.hidden&&(this._isFirstFrame||this._mdf)){var p=this.innerElem.getBBox();if(this.currentBBox.w!==p.width&&(this.currentBBox.w=p.width,this.svgElement.setAttribute(\"width\",p.width)),this.currentBBox.h!==p.height&&(this.currentBBox.h=p.height,this.svgElement.setAttribute(\"height\",p.height)),this.currentBBox.w!==p.width+2||this.currentBBox.h!==p.height+2||this.currentBBox.x!==p.x-1||this.currentBBox.y!==p.y-1){this.currentBBox.w=p.width+2,this.currentBBox.h=p.height+2,this.currentBBox.x=p.x-1,this.currentBBox.y=p.y-1,this.svgElement.setAttribute(\"viewBox\",this.currentBBox.x+\" \"+this.currentBBox.y+\" \"+this.currentBBox.w+\" \"+this.currentBBox.h),A=this.svgElement.style;var c=\"translate(\"+this.currentBBox.x+\"px,\"+this.currentBBox.y+\"px)\";A.transform=c,A.webkitTransform=c}}}},extendPrototype([BaseElement,FrameElement,HierarchyElement],HCameraElement),HCameraElement.prototype.setup=function(){var A,t,e,a,r=this.comp.threeDElements.length;for(A=0;A<r;A+=1)if(\"3d\"===(t=this.comp.threeDElements[A]).type){e=t.perspectiveElem.style,a=t.container.style;var n=this.pe.v+\"px\",l=\"matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)\";e.perspective=n,e.webkitPerspective=n,a.transformOrigin=\"0px 0px 0px\",a.mozTransformOrigin=\"0px 0px 0px\",a.webkitTransformOrigin=\"0px 0px 0px\",e.transform=l,e.webkitTransform=l}},HCameraElement.prototype.createElements=function(){},HCameraElement.prototype.hide=function(){},HCameraElement.prototype.renderFrame=function(){var A,t,e=this._isFirstFrame;if(this.hierarchy)for(t=this.hierarchy.length,A=0;A<t;A+=1)e=this.hierarchy[A].finalTransform.mProp._mdf||e;if(e||this.pe._mdf||this.p&&this.p._mdf||this.px&&(this.px._mdf||this.py._mdf||this.pz._mdf)||this.rx._mdf||this.ry._mdf||this.rz._mdf||this.or._mdf||this.a&&this.a._mdf){if(this.mat.reset(),this.hierarchy)for(A=t=this.hierarchy.length-1;A>=0;A-=1){var a=this.hierarchy[A].finalTransform.mProp;this.mat.translate(-a.p.v[0],-a.p.v[1],a.p.v[2]),this.mat.rotateX(-a.or.v[0]).rotateY(-a.or.v[1]).rotateZ(a.or.v[2]),this.mat.rotateX(-a.rx.v).rotateY(-a.ry.v).rotateZ(a.rz.v),this.mat.scale(1/a.s.v[0],1/a.s.v[1],1/a.s.v[2]),this.mat.translate(a.a.v[0],a.a.v[1],a.a.v[2])}if(this.p?this.mat.translate(-this.p.v[0],-this.p.v[1],this.p.v[2]):this.mat.translate(-this.px.v,-this.py.v,this.pz.v),this.a){var r;r=this.p?[this.p.v[0]-this.a.v[0],this.p.v[1]-this.a.v[1],this.p.v[2]-this.a.v[2]]:[this.px.v-this.a.v[0],this.py.v-this.a.v[1],this.pz.v-this.a.v[2]];var n=Math.sqrt(Math.pow(r[0],2)+Math.pow(r[1],2)+Math.pow(r[2],2)),l=[r[0]/n,r[1]/n,r[2]/n],i=Math.sqrt(l[2]*l[2]+l[0]*l[0]),o=Math.atan2(l[1],i),s=Math.atan2(l[0],-l[2]);this.mat.rotateY(s).rotateX(-o)}this.mat.rotateX(-this.rx.v).rotateY(-this.ry.v).rotateZ(this.rz.v),this.mat.rotateX(-this.or.v[0]).rotateY(-this.or.v[1]).rotateZ(this.or.v[2]),this.mat.translate(this.globalData.compSize.w/2,this.globalData.compSize.h/2,0),this.mat.translate(0,0,this.pe.v);var p=!this._prevMat.equals(this.mat);if((p||this.pe._mdf)&&this.comp.threeDElements){var c,u,d;for(t=this.comp.threeDElements.length,A=0;A<t;A+=1)if(\"3d\"===(c=this.comp.threeDElements[A]).type){if(p){var h=this.mat.toCSS();(d=c.container.style).transform=h,d.webkitTransform=h}this.pe._mdf&&((u=c.perspectiveElem.style).perspective=this.pe.v+\"px\",u.webkitPerspective=this.pe.v+\"px\")}this.mat.clone(this._prevMat)}}this._isFirstFrame=!1},HCameraElement.prototype.prepareFrame=function(A){this.prepareProperties(A,!0)},HCameraElement.prototype.destroy=function(){},HCameraElement.prototype.getBaseElement=function(){return null},extendPrototype([BaseElement,TransformElement,HBaseElement,HSolidElement,HierarchyElement,FrameElement,RenderableElement],HImageElement),HImageElement.prototype.createContent=function(){var A=this.globalData.getAssetsPath(this.assetData),t=new Image;this.data.hasMask?(this.imageElem=createNS(\"image\"),this.imageElem.setAttribute(\"width\",this.assetData.w+\"px\"),this.imageElem.setAttribute(\"height\",this.assetData.h+\"px\"),this.imageElem.setAttributeNS(\"http://www.w3.org/1999/xlink\",\"href\",A),this.layerElement.appendChild(this.imageElem),this.baseElement.setAttribute(\"width\",this.assetData.w),this.baseElement.setAttribute(\"height\",this.assetData.h)):this.layerElement.appendChild(t),t.crossOrigin=\"anonymous\",t.src=A,this.data.ln&&this.baseElement.setAttribute(\"id\",this.data.ln)},extendPrototype([BaseRenderer],HybridRendererBase),HybridRendererBase.prototype.buildItem=SVGRenderer.prototype.buildItem,HybridRendererBase.prototype.checkPendingElements=function(){for(;this.pendingElements.length;)this.pendingElements.pop().checkParenting()},HybridRendererBase.prototype.appendElementInPos=function(A,t){var e=A.getBaseElement();if(e){var a=this.layers[t];if(a.ddd&&this.supports3d)this.addTo3dContainer(e,t);else if(this.threeDElements)this.addTo3dContainer(e,t);else{for(var r,n,l=0;l<t;)this.elements[l]&&!0!==this.elements[l]&&this.elements[l].getBaseElement&&(n=this.elements[l],r=(this.layers[l].ddd?this.getThreeDContainerByPos(l):n.getBaseElement())||r),l+=1;r?a.ddd&&this.supports3d||this.layerElement.insertBefore(e,r):a.ddd&&this.supports3d||this.layerElement.appendChild(e)}}},HybridRendererBase.prototype.createShape=function(A){return this.supports3d?new HShapeElement(A,this.globalData,this):new SVGShapeElement(A,this.globalData,this)},HybridRendererBase.prototype.createText=function(A){return this.supports3d?new HTextElement(A,this.globalData,this):new SVGTextLottieElement(A,this.globalData,this)},HybridRendererBase.prototype.createCamera=function(A){return this.camera=new HCameraElement(A,this.globalData,this),this.camera},HybridRendererBase.prototype.createImage=function(A){return this.supports3d?new HImageElement(A,this.globalData,this):new IImageElement(A,this.globalData,this)},HybridRendererBase.prototype.createSolid=function(A){return this.supports3d?new HSolidElement(A,this.globalData,this):new ISolidElement(A,this.globalData,this)},HybridRendererBase.prototype.createNull=SVGRenderer.prototype.createNull,HybridRendererBase.prototype.getThreeDContainerByPos=function(A){for(var t=0,e=this.threeDElements.length;t<e;){if(this.threeDElements[t].startPos<=A&&this.threeDElements[t].endPos>=A)return this.threeDElements[t].perspectiveElem;t+=1}return null},HybridRendererBase.prototype.createThreeDContainer=function(A,t){var e,a,r=createTag(\"div\");styleDiv(r);var n=createTag(\"div\");if(styleDiv(n),\"3d\"===t){(e=r.style).width=this.globalData.compSize.w+\"px\",e.height=this.globalData.compSize.h+\"px\",e.webkitTransformOrigin=\"50% 50%\",e.mozTransformOrigin=\"50% 50%\",e.transformOrigin=\"50% 50%\";var l=\"matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)\";(a=n.style).transform=l,a.webkitTransform=l}r.appendChild(n);var i={container:n,perspectiveElem:r,startPos:A,endPos:A,type:t};return this.threeDElements.push(i),i},HybridRendererBase.prototype.build3dContainers=function(){var A,t,e=this.layers.length,a=\"\";for(A=0;A<e;A+=1)this.layers[A].ddd&&3!==this.layers[A].ty?(\"3d\"!==a&&(a=\"3d\",t=this.createThreeDContainer(A,\"3d\")),t.endPos=Math.max(t.endPos,A)):(\"2d\"!==a&&(a=\"2d\",t=this.createThreeDContainer(A,\"2d\")),t.endPos=Math.max(t.endPos,A));for(A=(e=this.threeDElements.length)-1;A>=0;A-=1)this.resizerElem.appendChild(this.threeDElements[A].perspectiveElem)},HybridRendererBase.prototype.addTo3dContainer=function(A,t){for(var e=0,a=this.threeDElements.length;e<a;){if(t<=this.threeDElements[e].endPos){for(var r,n=this.threeDElements[e].startPos;n<t;)this.elements[n]&&this.elements[n].getBaseElement&&(r=this.elements[n].getBaseElement()),n+=1;r?this.threeDElements[e].container.insertBefore(A,r):this.threeDElements[e].container.appendChild(A);break}e+=1}},HybridRendererBase.prototype.configAnimation=function(A){var t=createTag(\"div\"),e=this.animationItem.wrapper,a=t.style;a.width=A.w+\"px\",a.height=A.h+\"px\",this.resizerElem=t,styleDiv(t),a.transformStyle=\"flat\",a.mozTransformStyle=\"flat\",a.webkitTransformStyle=\"flat\",this.renderConfig.className&&t.setAttribute(\"class\",this.renderConfig.className),e.appendChild(t),a.overflow=\"hidden\";var r=createNS(\"svg\");r.setAttribute(\"width\",\"1\"),r.setAttribute(\"height\",\"1\"),styleDiv(r),this.resizerElem.appendChild(r);var n=createNS(\"defs\");r.appendChild(n),this.data=A,this.setupGlobalData(A,r),this.globalData.defs=n,this.layers=A.layers,this.layerElement=this.resizerElem,this.build3dContainers(),this.updateContainerSize()},HybridRendererBase.prototype.destroy=function(){var A;this.animationItem.wrapper&&(this.animationItem.wrapper.innerText=\"\"),this.animationItem.container=null,this.globalData.defs=null;var t=this.layers?this.layers.length:0;for(A=0;A<t;A+=1)this.elements[A]&&this.elements[A].destroy&&this.elements[A].destroy();this.elements.length=0,this.destroyed=!0,this.animationItem=null},HybridRendererBase.prototype.updateContainerSize=function(){var A,t,e,a,r=this.animationItem.wrapper.offsetWidth,n=this.animationItem.wrapper.offsetHeight,l=r/n;this.globalData.compSize.w/this.globalData.compSize.h>l?(A=r/this.globalData.compSize.w,t=r/this.globalData.compSize.w,e=0,a=(n-this.globalData.compSize.h*(r/this.globalData.compSize.w))/2):(A=n/this.globalData.compSize.h,t=n/this.globalData.compSize.h,e=(r-this.globalData.compSize.w*(n/this.globalData.compSize.h))/2,a=0);var i=this.resizerElem.style;i.webkitTransform=\"matrix3d(\"+A+\",0,0,0,0,\"+t+\",0,0,0,0,1,0,\"+e+\",\"+a+\",0,1)\",i.transform=i.webkitTransform},HybridRendererBase.prototype.renderFrame=SVGRenderer.prototype.renderFrame,HybridRendererBase.prototype.hide=function(){this.resizerElem.style.display=\"none\"},HybridRendererBase.prototype.show=function(){this.resizerElem.style.display=\"block\"},HybridRendererBase.prototype.initItems=function(){if(this.buildAllItems(),this.camera)this.camera.setup();else{var A,t=this.globalData.compSize.w,e=this.globalData.compSize.h,a=this.threeDElements.length;for(A=0;A<a;A+=1){var r=this.threeDElements[A].perspectiveElem.style;r.webkitPerspective=Math.sqrt(Math.pow(t,2)+Math.pow(e,2))+\"px\",r.perspective=r.webkitPerspective}}},HybridRendererBase.prototype.searchExtraCompositions=function(A){var t,e=A.length,a=createTag(\"div\");for(t=0;t<e;t+=1)if(A[t].xt){var r=this.createComp(A[t],a,this.globalData.comp,null);r.initExpressions(),this.globalData.projectInterface.registerComposition(r)}},extendPrototype([HybridRendererBase,ICompElement,HBaseElement],HCompElement),HCompElement.prototype._createBaseContainerElements=HCompElement.prototype.createContainerElements,HCompElement.prototype.createContainerElements=function(){this._createBaseContainerElements(),this.data.hasMask?(this.svgElement.setAttribute(\"width\",this.data.w),this.svgElement.setAttribute(\"height\",this.data.h),this.transformedElement=this.baseElement):this.transformedElement=this.layerElement},HCompElement.prototype.addTo3dContainer=function(A,t){for(var e,a=0;a<t;)this.elements[a]&&this.elements[a].getBaseElement&&(e=this.elements[a].getBaseElement()),a+=1;e?this.layerElement.insertBefore(A,e):this.layerElement.appendChild(A)},HCompElement.prototype.createComp=function(A){return this.supports3d?new HCompElement(A,this.globalData,this):new SVGCompElement(A,this.globalData,this)},extendPrototype([HybridRendererBase],HybridRenderer),HybridRenderer.prototype.createComp=function(A){return this.supports3d?new HCompElement(A,this.globalData,this):new SVGCompElement(A,this.globalData,this)};var CompExpressionInterface=function(A){function t(t){for(var e=0,a=A.layers.length;e<a;){if(A.layers[e].nm===t||A.layers[e].ind===t)return A.elements[e].layerInterface;e+=1}return null}return Object.defineProperty(t,\"_name\",{value:A.data.nm}),t.layer=t,t.pixelAspect=1,t.height=A.data.h||A.globalData.compSize.h,t.width=A.data.w||A.globalData.compSize.w,t.pixelAspect=1,t.frameDuration=1/A.globalData.frameRate,t.displayStartTime=0,t.numLayers=A.layers.length,t};function _typeof$2(A){return(_typeof$2=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(A){return typeof A}:function(A){return A&&\"function\"==typeof Symbol&&A.constructor===Symbol&&A!==Symbol.prototype?\"symbol\":typeof A})(A)}function seedRandom(A,t){var e=this,a=t.pow(256,6),r=t.pow(2,52),n=2*r;function l(A){var t,e=A.length,a=this,r=0,n=a.i=a.j=0,l=a.S=[];for(e||(A=[e++]);r<256;)l[r]=r++;for(r=0;r<256;r++)l[r]=l[n=255&n+A[r%e]+(t=l[r])],l[n]=t;a.g=function(A){for(var t,e=0,r=a.i,n=a.j,l=a.S;A--;)t=l[r=255&r+1],e=256*e+l[255&(l[r]=l[n=255&n+t])+(l[n]=t)];return a.i=r,a.j=n,e}}function i(A,t){return t.i=A.i,t.j=A.j,t.S=A.S.slice(),t}function o(A,t){for(var e,a=A+\"\",r=0;r<a.length;)t[255&r]=255&(e^=19*t[255&r])+a.charCodeAt(r++);return s(t)}function s(A){return String.fromCharCode.apply(0,A)}t.seedrandom=function(p,c,u){var d=[],h=o(function A(t,e){var a,r=[],n=_typeof$2(t);if(e&&\"object\"==n)for(a in t)try{r.push(A(t[a],e-1))}catch(l){}return r.length?r:\"string\"==n?t:t+\"\\0\"}((c=!0===c?{entropy:!0}:c||{}).entropy?[p,s(A)]:null===p?function(){try{var t=new Uint8Array(256);return(e.crypto||e.msCrypto).getRandomValues(t),s(t)}catch(n){var a=e.navigator,r=a&&a.plugins;return[+new Date,e,r,e.screen,s(A)]}}():p,3),d),S=new l(d),f=function(){for(var A=S.g(6),t=a,e=0;A<r;)A=256*(A+e),t*=256,e=S.g(1);for(;A>=n;)A/=2,t/=2,e>>>=1;return(A+e)/t};return f.int32=function(){return 0|S.g(4)},f.quick=function(){return S.g(4)/4294967296},f.double=f,o(s(S.S),A),(c.pass||u||function(A,e,a,r){return r&&(r.S&&i(r,S),A.state=function(){return i(S,{})}),a?(t.random=A,e):A})(f,h,\"global\"in c?c.global:this==t,c.state)},o(t.random(),A)}function initialize$2(A){seedRandom([],A)}var propTypes={SHAPE:\"shape\"};function _typeof$1(A){return(_typeof$1=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(A){return typeof A}:function(A){return A&&\"function\"==typeof Symbol&&A.constructor===Symbol&&A!==Symbol.prototype?\"symbol\":typeof A})(A)}var ExpressionManager=function(){var ob={},Math=BMMath,window=null,document=null,XMLHttpRequest=null,fetch=null,frames=null,_lottieGlobal={};function resetFrame(){_lottieGlobal={}}function $bm_isInstanceOfArray(A){return A.constructor===Array||A.constructor===Float32Array}function isNumerable(A,t){return\"number\"===A||t instanceof Number||\"boolean\"===A||\"string\"===A}function $bm_neg(A){var t=_typeof$1(A);if(\"number\"===t||A instanceof Number||\"boolean\"===t)return-A;if($bm_isInstanceOfArray(A)){var e,a=A.length,r=[];for(e=0;e<a;e+=1)r[e]=-A[e];return r}return A.propType?A.v:-A}initialize$2(BMMath);var easeInBez=BezierFactory.getBezierEasing(.333,0,.833,.833,\"easeIn\").get,easeOutBez=BezierFactory.getBezierEasing(.167,.167,.667,1,\"easeOut\").get,easeInOutBez=BezierFactory.getBezierEasing(.33,0,.667,1,\"easeInOut\").get;function sum(A,t){var e=_typeof$1(A),a=_typeof$1(t);if(isNumerable(e,A)&&isNumerable(a,t)||\"string\"===e||\"string\"===a)return A+t;if($bm_isInstanceOfArray(A)&&isNumerable(a,t))return(A=A.slice(0))[0]+=t,A;if(isNumerable(e,A)&&$bm_isInstanceOfArray(t))return(t=t.slice(0))[0]=A+t[0],t;if($bm_isInstanceOfArray(A)&&$bm_isInstanceOfArray(t)){for(var r=0,n=A.length,l=t.length,i=[];r<n||r<l;)(\"number\"==typeof A[r]||A[r]instanceof Number)&&(\"number\"==typeof t[r]||t[r]instanceof Number)?i[r]=A[r]+t[r]:i[r]=void 0===t[r]?A[r]:A[r]||t[r],r+=1;return i}return 0}var add=sum;function sub(A,t){var e=_typeof$1(A),a=_typeof$1(t);if(isNumerable(e,A)&&isNumerable(a,t))return\"string\"===e&&(A=parseInt(A,10)),\"string\"===a&&(t=parseInt(t,10)),A-t;if($bm_isInstanceOfArray(A)&&isNumerable(a,t))return(A=A.slice(0))[0]-=t,A;if(isNumerable(e,A)&&$bm_isInstanceOfArray(t))return(t=t.slice(0))[0]=A-t[0],t;if($bm_isInstanceOfArray(A)&&$bm_isInstanceOfArray(t)){for(var r=0,n=A.length,l=t.length,i=[];r<n||r<l;)(\"number\"==typeof A[r]||A[r]instanceof Number)&&(\"number\"==typeof t[r]||t[r]instanceof Number)?i[r]=A[r]-t[r]:i[r]=void 0===t[r]?A[r]:A[r]||t[r],r+=1;return i}return 0}function mul(A,t){var e,a,r,n=_typeof$1(A),l=_typeof$1(t);if(isNumerable(n,A)&&isNumerable(l,t))return A*t;if($bm_isInstanceOfArray(A)&&isNumerable(l,t)){for(r=A.length,e=createTypedArray(\"float32\",r),a=0;a<r;a+=1)e[a]=A[a]*t;return e}if(isNumerable(n,A)&&$bm_isInstanceOfArray(t)){for(r=t.length,e=createTypedArray(\"float32\",r),a=0;a<r;a+=1)e[a]=A*t[a];return e}return 0}function div(A,t){var e,a,r,n=_typeof$1(A),l=_typeof$1(t);if(isNumerable(n,A)&&isNumerable(l,t))return A/t;if($bm_isInstanceOfArray(A)&&isNumerable(l,t)){for(r=A.length,e=createTypedArray(\"float32\",r),a=0;a<r;a+=1)e[a]=A[a]/t;return e}if(isNumerable(n,A)&&$bm_isInstanceOfArray(t)){for(r=t.length,e=createTypedArray(\"float32\",r),a=0;a<r;a+=1)e[a]=A/t[a];return e}return 0}function mod(A,t){return\"string\"==typeof A&&(A=parseInt(A,10)),\"string\"==typeof t&&(t=parseInt(t,10)),A%t}var $bm_sum=sum,$bm_sub=sub,$bm_mul=mul,$bm_div=div,$bm_mod=mod;function clamp(A,t,e){if(t>e){var a=e;e=t,t=a}return Math.min(Math.max(A,t),e)}function radiansToDegrees(A){return A/degToRads}var radians_to_degrees=radiansToDegrees;function degreesToRadians(A){return A*degToRads}var degrees_to_radians=radiansToDegrees,helperLengthArray=[0,0,0,0,0,0];function length(A,t){if(\"number\"==typeof A||A instanceof Number)return t=t||0,Math.abs(A-t);var e;t||(t=helperLengthArray);var a=Math.min(A.length,t.length),r=0;for(e=0;e<a;e+=1)r+=Math.pow(t[e]-A[e],2);return Math.sqrt(r)}function normalize(A){return div(A,length(A))}function rgbToHsl(A){var t,e,a=A[0],r=A[1],n=A[2],l=Math.max(a,r,n),i=Math.min(a,r,n),o=(l+i)/2;if(l===i)t=0,e=0;else{var s=l-i;switch(e=o>.5?s/(2-l-i):s/(l+i),l){case a:t=(r-n)/s+(r<n?6:0);break;case r:t=(n-a)/s+2;break;case n:t=(a-r)/s+4}t/=6}return[t,e,o,A[3]]}function hue2rgb(A,t,e){return e<0&&(e+=1),e>1&&(e-=1),e<1/6?A+6*(t-A)*e:e<.5?t:e<2/3?A+(t-A)*(2/3-e)*6:A}function hslToRgb(A){var t,e,a,r=A[0],n=A[1],l=A[2];if(0===n)t=l,a=l,e=l;else{var i=l<.5?l*(1+n):l+n-l*n,o=2*l-i;t=hue2rgb(o,i,r+1/3),e=hue2rgb(o,i,r),a=hue2rgb(o,i,r-1/3)}return[t,e,a,A[3]]}function linear(A,t,e,a,r){if(void 0!==a&&void 0!==r||(a=t,r=e,t=0,e=1),e<t){var n=e;e=t,t=n}if(A<=t)return a;if(A>=e)return r;var l,i=e===t?0:(A-t)/(e-t);if(!a.length)return a+(r-a)*i;var o=a.length,s=createTypedArray(\"float32\",o);for(l=0;l<o;l+=1)s[l]=a[l]+(r[l]-a[l])*i;return s}function random(A,t){if(void 0===t&&(void 0===A?(A=0,t=1):(t=A,A=void 0)),t.length){var e,a=t.length;A||(A=createTypedArray(\"float32\",a));var r=createTypedArray(\"float32\",a),n=BMMath.random();for(e=0;e<a;e+=1)r[e]=A[e]+n*(t[e]-A[e]);return r}return void 0===A&&(A=0),A+BMMath.random()*(t-A)}function createPath(A,t,e,a){var r,n=A.length,l=shapePool.newElement();l.setPathData(!!a,n);var i,o,s=[0,0];for(r=0;r<n;r+=1)i=t&&t[r]?t[r]:s,o=e&&e[r]?e[r]:s,l.setTripleAt(A[r][0],A[r][1],o[0]+A[r][0],o[1]+A[r][1],i[0]+A[r][0],i[1]+A[r][1],r,!0);return l}function initiateExpression(elem,data,property){function noOp(A){return A}if(!elem.globalData.renderConfig.runExpressions)return noOp;var val=data.x,needsVelocity=/velocity(?![\\w\\d])/.test(val),_needsRandom=-1!==val.indexOf(\"random\"),elemType=elem.data.ty,transform,$bm_transform,content,effect,thisProperty=property;thisProperty.valueAtTime=thisProperty.getValueAtTime,Object.defineProperty(thisProperty,\"value\",{get:function(){return thisProperty.v}}),elem.comp.frameDuration=1/elem.comp.globalData.frameRate,elem.comp.displayStartTime=0;var inPoint=elem.data.ip/elem.comp.globalData.frameRate,outPoint=elem.data.op/elem.comp.globalData.frameRate,width=elem.data.sw?elem.data.sw:0,height=elem.data.sh?elem.data.sh:0,name=elem.data.nm,loopIn,loop_in,loopOut,loop_out,smooth,toWorld,fromWorld,fromComp,toComp,fromCompToSurface,position,rotation,anchorPoint,scale,thisLayer,thisComp,mask,valueAtTime,velocityAtTime,scoped_bm_rt,expression_function=eval(\"[function _expression_function(){\"+val+\";scoped_bm_rt=$bm_rt}]\")[0],numKeys=property.kf?data.k.length:0,active=!this.data||!0!==this.data.hd,wiggle=function(A,t){var e,a,r=this.pv.length?this.pv.length:1,n=createTypedArray(\"float32\",r),l=Math.floor(5*time);for(e=0,a=0;e<l;){for(a=0;a<r;a+=1)n[a]+=-t+2*t*BMMath.random();e+=1}var i=5*time,o=i-Math.floor(i),s=createTypedArray(\"float32\",r);if(r>1){for(a=0;a<r;a+=1)s[a]=this.pv[a]+n[a]+(-t+2*t*BMMath.random())*o;return s}return this.pv+n[0]+(-t+2*t*BMMath.random())*o}.bind(this);function loopInDuration(A,t){return loopIn(A,t,!0)}function loopOutDuration(A,t){return loopOut(A,t,!0)}thisProperty.loopIn&&(loopIn=thisProperty.loopIn.bind(thisProperty),loop_in=loopIn),thisProperty.loopOut&&(loopOut=thisProperty.loopOut.bind(thisProperty),loop_out=loopOut),thisProperty.smooth&&(smooth=thisProperty.smooth.bind(thisProperty)),this.getValueAtTime&&(valueAtTime=this.getValueAtTime.bind(this)),this.getVelocityAtTime&&(velocityAtTime=this.getVelocityAtTime.bind(this));var comp=elem.comp.globalData.projectInterface.bind(elem.comp.globalData.projectInterface),time,velocity,value,text,textIndex,textTotal,selectorValue;function lookAt(A,t){var e=[t[0]-A[0],t[1]-A[1],t[2]-A[2]],a=Math.atan2(e[0],Math.sqrt(e[1]*e[1]+e[2]*e[2]))/degToRads;return[-Math.atan2(e[1],e[2])/degToRads,a,0]}function easeOut(A,t,e,a,r){return applyEase(easeOutBez,A,t,e,a,r)}function easeIn(A,t,e,a,r){return applyEase(easeInBez,A,t,e,a,r)}function ease(A,t,e,a,r){return applyEase(easeInOutBez,A,t,e,a,r)}function applyEase(A,t,e,a,r,n){void 0===r?(r=e,n=a):t=(t-e)/(a-e),t>1?t=1:t<0&&(t=0);var l=A(t);if($bm_isInstanceOfArray(r)){var i,o=r.length,s=createTypedArray(\"float32\",o);for(i=0;i<o;i+=1)s[i]=(n[i]-r[i])*l+r[i];return s}return(n-r)*l+r}function nearestKey(A){var t,e,a,r=data.k.length;if(data.k.length&&\"number\"!=typeof data.k[0])if(e=-1,(A*=elem.comp.globalData.frameRate)<data.k[0].t)e=1,a=data.k[0].t;else{for(t=0;t<r-1;t+=1){if(A===data.k[t].t){e=t+1,a=data.k[t].t;break}if(A>data.k[t].t&&A<data.k[t+1].t){A-data.k[t].t>data.k[t+1].t-A?(e=t+2,a=data.k[t+1].t):(e=t+1,a=data.k[t].t);break}}-1===e&&(e=t+1,a=data.k[t].t)}else e=0,a=0;var n={};return n.index=e,n.time=a/elem.comp.globalData.frameRate,n}function key(A){var t,e,a;if(!data.k.length||\"number\"==typeof data.k[0])throw new Error(\"The property has no keyframe at index \"+A);A-=1,t={time:data.k[A].t/elem.comp.globalData.frameRate,value:[]};var r=Object.prototype.hasOwnProperty.call(data.k[A],\"s\")?data.k[A].s:data.k[A-1].e;for(a=r.length,e=0;e<a;e+=1)t[e]=r[e],t.value[e]=r[e];return t}function framesToTime(A,t){return t||(t=elem.comp.globalData.frameRate),A/t}function timeToFrames(A,t){return A||0===A||(A=time),t||(t=elem.comp.globalData.frameRate),A*t}function seedRandom(A){BMMath.seedrandom(randSeed+A)}function sourceRectAtTime(){return elem.sourceRectAtTime()}function substring(A,t){return\"string\"==typeof value?void 0===t?value.substring(A):value.substring(A,t):\"\"}function substr(A,t){return\"string\"==typeof value?void 0===t?value.substr(A):value.substr(A,t):\"\"}function posterizeTime(A){time=0===A?0:Math.floor(time*A)/A,value=valueAtTime(time)}var index=elem.data.ind;elem.hierarchy&&elem.hierarchy.length;var parent,randSeed=Math.floor(1e6*Math.random()),globalData=elem.globalData;function executeExpression(A){return value=A,this.frameExpressionId===elem.globalData.frameId&&\"textSelector\"!==this.propType?value:(\"textSelector\"===this.propType&&(textIndex=this.textIndex,textTotal=this.textTotal,selectorValue=this.selectorValue),thisLayer||(text=elem.layerInterface.text,thisLayer=elem.layerInterface,thisComp=elem.comp.compInterface,toWorld=thisLayer.toWorld.bind(thisLayer),fromWorld=thisLayer.fromWorld.bind(thisLayer),fromComp=thisLayer.fromComp.bind(thisLayer),toComp=thisLayer.toComp.bind(thisLayer),mask=thisLayer.mask?thisLayer.mask.bind(thisLayer):null,fromCompToSurface=fromComp),transform||(transform=elem.layerInterface(\"ADBE Transform Group\"),$bm_transform=transform,transform&&(anchorPoint=transform.anchorPoint)),4!==elemType||content||(content=thisLayer(\"ADBE Root Vectors Group\")),effect||(effect=thisLayer(4)),!(!elem.hierarchy||!elem.hierarchy.length)&&!parent&&(parent=elem.hierarchy[0].layerInterface),time=this.comp.renderedFrame/this.comp.globalData.frameRate,_needsRandom&&seedRandom(randSeed+time),needsVelocity&&(velocity=velocityAtTime(time)),expression_function(),this.frameExpressionId=elem.globalData.frameId,scoped_bm_rt=scoped_bm_rt.propType===propTypes.SHAPE?scoped_bm_rt.v:scoped_bm_rt)}return executeExpression.__preventDeadCodeRemoval=[$bm_transform,anchorPoint,time,velocity,inPoint,outPoint,width,height,name,loop_in,loop_out,smooth,toComp,fromCompToSurface,toWorld,fromWorld,mask,position,rotation,scale,thisComp,numKeys,active,wiggle,loopInDuration,loopOutDuration,comp,lookAt,easeOut,easeIn,ease,nearestKey,key,text,textIndex,textTotal,selectorValue,framesToTime,timeToFrames,sourceRectAtTime,substring,substr,posterizeTime,index,globalData],executeExpression}return ob.initiateExpression=initiateExpression,ob.__preventDeadCodeRemoval=[window,document,XMLHttpRequest,fetch,frames,$bm_neg,add,$bm_sum,$bm_sub,$bm_mul,$bm_div,$bm_mod,clamp,radians_to_degrees,degreesToRadians,degrees_to_radians,normalize,rgbToHsl,hslToRgb,linear,random,createPath,_lottieGlobal],ob.resetFrame=resetFrame,ob}(),Expressions=function(){var A={initExpressions:function(A){var t=0,e=[];A.renderer.compInterface=CompExpressionInterface(A.renderer),A.renderer.globalData.projectInterface.registerComposition(A.renderer),A.renderer.globalData.pushExpression=function(){t+=1},A.renderer.globalData.popExpression=function(){0==(t-=1)&&function(){var A,t=e.length;for(A=0;A<t;A+=1)e[A].release();e.length=0}()},A.renderer.globalData.registerExpressionProperty=function(A){-1===e.indexOf(A)&&e.push(A)}}};return A.resetFrame=ExpressionManager.resetFrame,A}(),MaskManagerInterface=function(){function A(A,t){this._mask=A,this._data=t}return Object.defineProperty(A.prototype,\"maskPath\",{get:function(){return this._mask.prop.k&&this._mask.prop.getValue(),this._mask.prop}}),Object.defineProperty(A.prototype,\"maskOpacity\",{get:function(){return this._mask.op.k&&this._mask.op.getValue(),100*this._mask.op.v}}),function(t){var e,a=createSizedArray(t.viewData.length),r=t.viewData.length;for(e=0;e<r;e+=1)a[e]=new A(t.viewData[e],t.masksProperties[e]);return function(A){for(e=0;e<r;){if(t.masksProperties[e].nm===A)return a[e];e+=1}return null}}}(),ExpressionPropertyInterface=function(){var A={pv:0,v:0,mult:1},t={pv:[0,0,0],v:[0,0,0],mult:1};function e(A,t,e){Object.defineProperty(A,\"velocity\",{get:function(){return t.getVelocityAtTime(t.comp.currentFrame)}}),A.numKeys=t.keyframes?t.keyframes.length:0,A.key=function(a){if(!A.numKeys)return 0;var r;r=\"s\"in t.keyframes[a-1]?t.keyframes[a-1].s:\"e\"in t.keyframes[a-2]?t.keyframes[a-2].e:t.keyframes[a-2].s;var n=\"unidimensional\"===e?new Number(r):Object.assign({},r);return n.time=t.keyframes[a-1].t/t.elem.comp.globalData.frameRate,n.value=\"unidimensional\"===e?r[0]:r,n},A.valueAtTime=t.getValueAtTime,A.speedAtTime=t.getSpeedAtTime,A.velocityAtTime=t.getVelocityAtTime,A.propertyGroup=t.propertyGroup}function a(){return A}return function(r){return r?\"unidimensional\"===r.propType?function(t){t&&\"pv\"in t||(t=A);var a=1/t.mult,r=t.pv*a,n=new Number(r);return n.value=r,e(n,t,\"unidimensional\"),function(){return t.k&&t.getValue(),r=t.v*a,n.value!==r&&((n=new Number(r)).value=r,e(n,t,\"unidimensional\")),n}}(r):function(A){A&&\"pv\"in A||(A=t);var a=1/A.mult,r=A.data&&A.data.l||A.pv.length,n=createTypedArray(\"float32\",r),l=createTypedArray(\"float32\",r);return n.value=l,e(n,A,\"multidimensional\"),function(){A.k&&A.getValue();for(var t=0;t<r;t+=1)l[t]=A.v[t]*a,n[t]=l[t];return n}}(r):a}}(),TransformExpressionInterface=function(A){function t(A){switch(A){case\"scale\":case\"Scale\":case\"ADBE Scale\":case 6:return t.scale;case\"rotation\":case\"Rotation\":case\"ADBE Rotation\":case\"ADBE Rotate Z\":case 10:return t.rotation;case\"ADBE Rotate X\":return t.xRotation;case\"ADBE Rotate Y\":return t.yRotation;case\"position\":case\"Position\":case\"ADBE Position\":case 2:return t.position;case\"ADBE Position_0\":return t.xPosition;case\"ADBE Position_1\":return t.yPosition;case\"ADBE Position_2\":return t.zPosition;case\"anchorPoint\":case\"AnchorPoint\":case\"Anchor Point\":case\"ADBE AnchorPoint\":case 1:return t.anchorPoint;case\"opacity\":case\"Opacity\":case 11:return t.opacity;default:return null}}var e,a,r,n;return Object.defineProperty(t,\"rotation\",{get:ExpressionPropertyInterface(A.r||A.rz)}),Object.defineProperty(t,\"zRotation\",{get:ExpressionPropertyInterface(A.rz||A.r)}),Object.defineProperty(t,\"xRotation\",{get:ExpressionPropertyInterface(A.rx)}),Object.defineProperty(t,\"yRotation\",{get:ExpressionPropertyInterface(A.ry)}),Object.defineProperty(t,\"scale\",{get:ExpressionPropertyInterface(A.s)}),A.p?n=ExpressionPropertyInterface(A.p):(e=ExpressionPropertyInterface(A.px),a=ExpressionPropertyInterface(A.py),A.pz&&(r=ExpressionPropertyInterface(A.pz))),Object.defineProperty(t,\"position\",{get:function(){return A.p?n():[e(),a(),r?r():0]}}),Object.defineProperty(t,\"xPosition\",{get:ExpressionPropertyInterface(A.px)}),Object.defineProperty(t,\"yPosition\",{get:ExpressionPropertyInterface(A.py)}),Object.defineProperty(t,\"zPosition\",{get:ExpressionPropertyInterface(A.pz)}),Object.defineProperty(t,\"anchorPoint\",{get:ExpressionPropertyInterface(A.a)}),Object.defineProperty(t,\"opacity\",{get:ExpressionPropertyInterface(A.o)}),Object.defineProperty(t,\"skew\",{get:ExpressionPropertyInterface(A.sk)}),Object.defineProperty(t,\"skewAxis\",{get:ExpressionPropertyInterface(A.sa)}),Object.defineProperty(t,\"orientation\",{get:ExpressionPropertyInterface(A.or)}),t},LayerExpressionInterface=function(){function A(A){var t=new Matrix;return void 0!==A?this._elem.finalTransform.mProp.getValueAtTime(A).clone(t):this._elem.finalTransform.mProp.applyToMatrix(t),t}function t(A,t){var e=this.getMatrix(t);return e.props[12]=0,e.props[13]=0,e.props[14]=0,this.applyPoint(e,A)}function e(A,t){var e=this.getMatrix(t);return this.applyPoint(e,A)}function a(A,t){var e=this.getMatrix(t);return e.props[12]=0,e.props[13]=0,e.props[14]=0,this.invertPoint(e,A)}function r(A,t){var e=this.getMatrix(t);return this.invertPoint(e,A)}function n(A,t){if(this._elem.hierarchy&&this._elem.hierarchy.length){var e,a=this._elem.hierarchy.length;for(e=0;e<a;e+=1)this._elem.hierarchy[e].finalTransform.mProp.applyToMatrix(A)}return A.applyToPointArray(t[0],t[1],t[2]||0)}function l(A,t){if(this._elem.hierarchy&&this._elem.hierarchy.length){var e,a=this._elem.hierarchy.length;for(e=0;e<a;e+=1)this._elem.hierarchy[e].finalTransform.mProp.applyToMatrix(A)}return A.inversePoint(t)}function i(A){var t=new Matrix;if(t.reset(),this._elem.finalTransform.mProp.applyToMatrix(t),this._elem.hierarchy&&this._elem.hierarchy.length){var e,a=this._elem.hierarchy.length;for(e=0;e<a;e+=1)this._elem.hierarchy[e].finalTransform.mProp.applyToMatrix(t);return t.inversePoint(A)}return t.inversePoint(A)}function o(){return[1,1,1,1]}return function(s){var p;function c(A){switch(A){case\"ADBE Root Vectors Group\":case\"Contents\":case 2:return c.shapeInterface;case 1:case 6:case\"Transform\":case\"transform\":case\"ADBE Transform Group\":return p;case 4:case\"ADBE Effect Parade\":case\"effects\":case\"Effects\":return c.effect;case\"ADBE Text Properties\":return c.textInterface;default:return null}}c.getMatrix=A,c.invertPoint=l,c.applyPoint=n,c.toWorld=e,c.toWorldVec=t,c.fromWorld=r,c.fromWorldVec=a,c.toComp=e,c.fromComp=i,c.sampleImage=o,c.sourceRectAtTime=s.sourceRectAtTime.bind(s),c._elem=s;var u=getDescriptor(p=TransformExpressionInterface(s.finalTransform.mProp),\"anchorPoint\");return Object.defineProperties(c,{hasParent:{get:function(){return s.hierarchy.length}},parent:{get:function(){return s.hierarchy[0].layerInterface}},rotation:getDescriptor(p,\"rotation\"),scale:getDescriptor(p,\"scale\"),position:getDescriptor(p,\"position\"),opacity:getDescriptor(p,\"opacity\"),anchorPoint:u,anchor_point:u,transform:{get:function(){return p}},active:{get:function(){return s.isInRange}}}),c.startTime=s.data.st,c.index=s.data.ind,c.source=s.data.refId,c.height=0===s.data.ty?s.data.h:100,c.width=0===s.data.ty?s.data.w:100,c.inPoint=s.data.ip/s.comp.globalData.frameRate,c.outPoint=s.data.op/s.comp.globalData.frameRate,c._name=s.data.nm,c.registerMaskInterface=function(A){c.mask=new MaskManagerInterface(A,s)},c.registerEffectsInterface=function(A){c.effect=A},c}}(),propertyGroupFactory=function(A,t){return function(e){return(e=void 0===e?1:e)<=0?A:t(e-1)}},PropertyInterface=function(A,t){var e={_name:A};return function(A){return(A=void 0===A?1:A)<=0?e:t(A-1)}},EffectsExpressionInterface=function(){function A(e,a,r,n){function l(A){for(var t=e.ef,a=0,r=t.length;a<r;){if(A===t[a].nm||A===t[a].mn||A===t[a].ix)return 5===t[a].ty?s[a]:s[a]();a+=1}throw new Error}var i,o=propertyGroupFactory(l,r),s=[],p=e.ef.length;for(i=0;i<p;i+=1)5===e.ef[i].ty?s.push(A(e.ef[i],a.effectElements[i],a.effectElements[i].propertyGroup,n)):s.push(t(a.effectElements[i],e.ef[i].ty,n,o));return\"ADBE Color Control\"===e.mn&&Object.defineProperty(l,\"color\",{get:function(){return s[0]()}}),Object.defineProperties(l,{numProperties:{get:function(){return e.np}},_name:{value:e.nm},propertyGroup:{value:o}}),l.enabled=0!==e.en,l.active=l.enabled,l}function t(A,t,e,a){var r=ExpressionPropertyInterface(A.p);return A.p.setGroupProperty&&A.p.setGroupProperty(PropertyInterface(\"\",a)),function(){return 10===t?e.comp.compInterface(A.p.v):r()}}return{createEffectsInterface:function(t,e){if(t.effectsManager){var a,r=[],n=t.data.ef,l=t.effectsManager.effectElements.length;for(a=0;a<l;a+=1)r.push(A(n[a],t.effectsManager.effectElements[a],e,t));var i=t.data.ef||[],o=function(A){for(a=0,l=i.length;a<l;){if(A===i[a].nm||A===i[a].mn||A===i[a].ix)return r[a];a+=1}return null};return Object.defineProperty(o,\"numProperties\",{get:function(){return i.length}}),o}return null}}}(),ShapePathInterface=function(A,t,e){var a=t.sh;function r(A){return\"Shape\"===A||\"shape\"===A||\"Path\"===A||\"path\"===A||\"ADBE Vector Shape\"===A||2===A?r.path:null}var n=propertyGroupFactory(r,e);return a.setGroupProperty(PropertyInterface(\"Path\",n)),Object.defineProperties(r,{path:{get:function(){return a.k&&a.getValue(),a}},shape:{get:function(){return a.k&&a.getValue(),a}},_name:{value:A.nm},ix:{value:A.ix},propertyIndex:{value:A.ix},mn:{value:A.mn},propertyGroup:{value:e}}),r},ShapeExpressionInterface=function(){function A(A,i,d){var h,S=[],f=A?A.length:0;for(h=0;h<f;h+=1)\"gr\"===A[h].ty?S.push(t(A[h],i[h],d)):\"fl\"===A[h].ty?S.push(e(A[h],i[h],d)):\"st\"===A[h].ty?S.push(n(A[h],i[h],d)):\"tm\"===A[h].ty?S.push(l(A[h],i[h],d)):\"tr\"===A[h].ty||(\"el\"===A[h].ty?S.push(o(A[h],i[h],d)):\"sr\"===A[h].ty?S.push(s(A[h],i[h],d)):\"sh\"===A[h].ty?S.push(ShapePathInterface(A[h],i[h],d)):\"rc\"===A[h].ty?S.push(p(A[h],i[h],d)):\"rd\"===A[h].ty?S.push(c(A[h],i[h],d)):\"rp\"===A[h].ty?S.push(u(A[h],i[h],d)):\"gf\"===A[h].ty?S.push(a(A[h],i[h],d)):S.push(r(A[h],i[h])));return S}function t(t,e,a){var r=function(A){switch(A){case\"ADBE Vectors Group\":case\"Contents\":case 2:return r.content;default:return r.transform}};r.propertyGroup=propertyGroupFactory(r,a);var n=function(t,e,a){var r,n=function(A){for(var t=0,e=r.length;t<e;){if(r[t]._name===A||r[t].mn===A||r[t].propertyIndex===A||r[t].ix===A||r[t].ind===A)return r[t];t+=1}return\"number\"==typeof A?r[A-1]:null};n.propertyGroup=propertyGroupFactory(n,a),r=A(t.it,e.it,n.propertyGroup),n.numProperties=r.length;var l=i(t.it[t.it.length-1],e.it[e.it.length-1],n.propertyGroup);return n.transform=l,n.propertyIndex=t.cix,n._name=t.nm,n}(t,e,r.propertyGroup),l=i(t.it[t.it.length-1],e.it[e.it.length-1],r.propertyGroup);return r.content=n,r.transform=l,Object.defineProperty(r,\"_name\",{get:function(){return t.nm}}),r.numProperties=t.np,r.propertyIndex=t.ix,r.nm=t.nm,r.mn=t.mn,r}function e(A,t,e){function a(A){return\"Color\"===A||\"color\"===A?a.color:\"Opacity\"===A||\"opacity\"===A?a.opacity:null}return Object.defineProperties(a,{color:{get:ExpressionPropertyInterface(t.c)},opacity:{get:ExpressionPropertyInterface(t.o)},_name:{value:A.nm},mn:{value:A.mn}}),t.c.setGroupProperty(PropertyInterface(\"Color\",e)),t.o.setGroupProperty(PropertyInterface(\"Opacity\",e)),a}function a(A,t,e){function a(A){return\"Start Point\"===A||\"start point\"===A?a.startPoint:\"End Point\"===A||\"end point\"===A?a.endPoint:\"Opacity\"===A||\"opacity\"===A?a.opacity:null}return Object.defineProperties(a,{startPoint:{get:ExpressionPropertyInterface(t.s)},endPoint:{get:ExpressionPropertyInterface(t.e)},opacity:{get:ExpressionPropertyInterface(t.o)},type:{get:function(){return\"a\"}},_name:{value:A.nm},mn:{value:A.mn}}),t.s.setGroupProperty(PropertyInterface(\"Start Point\",e)),t.e.setGroupProperty(PropertyInterface(\"End Point\",e)),t.o.setGroupProperty(PropertyInterface(\"Opacity\",e)),a}function r(){return function(){return null}}function n(A,t,e){var a,r=propertyGroupFactory(s,e),n=propertyGroupFactory(o,r);function l(e){Object.defineProperty(o,A.d[e].nm,{get:ExpressionPropertyInterface(t.d.dataProps[e].p)})}var i=A.d?A.d.length:0,o={};for(a=0;a<i;a+=1)l(a),t.d.dataProps[a].p.setGroupProperty(n);function s(A){return\"Color\"===A||\"color\"===A?s.color:\"Opacity\"===A||\"opacity\"===A?s.opacity:\"Stroke Width\"===A||\"stroke width\"===A?s.strokeWidth:null}return Object.defineProperties(s,{color:{get:ExpressionPropertyInterface(t.c)},opacity:{get:ExpressionPropertyInterface(t.o)},strokeWidth:{get:ExpressionPropertyInterface(t.w)},dash:{get:function(){return o}},_name:{value:A.nm},mn:{value:A.mn}}),t.c.setGroupProperty(PropertyInterface(\"Color\",r)),t.o.setGroupProperty(PropertyInterface(\"Opacity\",r)),t.w.setGroupProperty(PropertyInterface(\"Stroke Width\",r)),s}function l(A,t,e){function a(t){return t===A.e.ix||\"End\"===t||\"end\"===t?a.end:t===A.s.ix?a.start:t===A.o.ix?a.offset:null}var r=propertyGroupFactory(a,e);return a.propertyIndex=A.ix,t.s.setGroupProperty(PropertyInterface(\"Start\",r)),t.e.setGroupProperty(PropertyInterface(\"End\",r)),t.o.setGroupProperty(PropertyInterface(\"Offset\",r)),a.propertyIndex=A.ix,a.propertyGroup=e,Object.defineProperties(a,{start:{get:ExpressionPropertyInterface(t.s)},end:{get:ExpressionPropertyInterface(t.e)},offset:{get:ExpressionPropertyInterface(t.o)},_name:{value:A.nm}}),a.mn=A.mn,a}function i(A,t,e){function a(t){return A.a.ix===t||\"Anchor Point\"===t?a.anchorPoint:A.o.ix===t||\"Opacity\"===t?a.opacity:A.p.ix===t||\"Position\"===t?a.position:A.r.ix===t||\"Rotation\"===t||\"ADBE Vector Rotation\"===t?a.rotation:A.s.ix===t||\"Scale\"===t?a.scale:A.sk&&A.sk.ix===t||\"Skew\"===t?a.skew:A.sa&&A.sa.ix===t||\"Skew Axis\"===t?a.skewAxis:null}var r=propertyGroupFactory(a,e);return t.transform.mProps.o.setGroupProperty(PropertyInterface(\"Opacity\",r)),t.transform.mProps.p.setGroupProperty(PropertyInterface(\"Position\",r)),t.transform.mProps.a.setGroupProperty(PropertyInterface(\"Anchor Point\",r)),t.transform.mProps.s.setGroupProperty(PropertyInterface(\"Scale\",r)),t.transform.mProps.r.setGroupProperty(PropertyInterface(\"Rotation\",r)),t.transform.mProps.sk&&(t.transform.mProps.sk.setGroupProperty(PropertyInterface(\"Skew\",r)),t.transform.mProps.sa.setGroupProperty(PropertyInterface(\"Skew Angle\",r))),t.transform.op.setGroupProperty(PropertyInterface(\"Opacity\",r)),Object.defineProperties(a,{opacity:{get:ExpressionPropertyInterface(t.transform.mProps.o)},position:{get:ExpressionPropertyInterface(t.transform.mProps.p)},anchorPoint:{get:ExpressionPropertyInterface(t.transform.mProps.a)},scale:{get:ExpressionPropertyInterface(t.transform.mProps.s)},rotation:{get:ExpressionPropertyInterface(t.transform.mProps.r)},skew:{get:ExpressionPropertyInterface(t.transform.mProps.sk)},skewAxis:{get:ExpressionPropertyInterface(t.transform.mProps.sa)},_name:{value:A.nm}}),a.ty=\"tr\",a.mn=A.mn,a.propertyGroup=e,a}function o(A,t,e){function a(t){return A.p.ix===t?a.position:A.s.ix===t?a.size:null}var r=propertyGroupFactory(a,e);a.propertyIndex=A.ix;var n=\"tm\"===t.sh.ty?t.sh.prop:t.sh;return n.s.setGroupProperty(PropertyInterface(\"Size\",r)),n.p.setGroupProperty(PropertyInterface(\"Position\",r)),Object.defineProperties(a,{size:{get:ExpressionPropertyInterface(n.s)},position:{get:ExpressionPropertyInterface(n.p)},_name:{value:A.nm}}),a.mn=A.mn,a}function s(A,t,e){function a(t){return A.p.ix===t?a.position:A.r.ix===t?a.rotation:A.pt.ix===t?a.points:A.or.ix===t||\"ADBE Vector Star Outer Radius\"===t?a.outerRadius:A.os.ix===t?a.outerRoundness:!A.ir||A.ir.ix!==t&&\"ADBE Vector Star Inner Radius\"!==t?A.is&&A.is.ix===t?a.innerRoundness:null:a.innerRadius}var r=propertyGroupFactory(a,e),n=\"tm\"===t.sh.ty?t.sh.prop:t.sh;return a.propertyIndex=A.ix,n.or.setGroupProperty(PropertyInterface(\"Outer Radius\",r)),n.os.setGroupProperty(PropertyInterface(\"Outer Roundness\",r)),n.pt.setGroupProperty(PropertyInterface(\"Points\",r)),n.p.setGroupProperty(PropertyInterface(\"Position\",r)),n.r.setGroupProperty(PropertyInterface(\"Rotation\",r)),A.ir&&(n.ir.setGroupProperty(PropertyInterface(\"Inner Radius\",r)),n.is.setGroupProperty(PropertyInterface(\"Inner Roundness\",r))),Object.defineProperties(a,{position:{get:ExpressionPropertyInterface(n.p)},rotation:{get:ExpressionPropertyInterface(n.r)},points:{get:ExpressionPropertyInterface(n.pt)},outerRadius:{get:ExpressionPropertyInterface(n.or)},outerRoundness:{get:ExpressionPropertyInterface(n.os)},innerRadius:{get:ExpressionPropertyInterface(n.ir)},innerRoundness:{get:ExpressionPropertyInterface(n.is)},_name:{value:A.nm}}),a.mn=A.mn,a}function p(A,t,e){function a(t){return A.p.ix===t?a.position:A.r.ix===t?a.roundness:A.s.ix===t||\"Size\"===t||\"ADBE Vector Rect Size\"===t?a.size:null}var r=propertyGroupFactory(a,e),n=\"tm\"===t.sh.ty?t.sh.prop:t.sh;return a.propertyIndex=A.ix,n.p.setGroupProperty(PropertyInterface(\"Position\",r)),n.s.setGroupProperty(PropertyInterface(\"Size\",r)),n.r.setGroupProperty(PropertyInterface(\"Rotation\",r)),Object.defineProperties(a,{position:{get:ExpressionPropertyInterface(n.p)},roundness:{get:ExpressionPropertyInterface(n.r)},size:{get:ExpressionPropertyInterface(n.s)},_name:{value:A.nm}}),a.mn=A.mn,a}function c(A,t,e){function a(t){return A.r.ix===t||\"Round Corners 1\"===t?a.radius:null}var r=propertyGroupFactory(a,e),n=t;return a.propertyIndex=A.ix,n.rd.setGroupProperty(PropertyInterface(\"Radius\",r)),Object.defineProperties(a,{radius:{get:ExpressionPropertyInterface(n.rd)},_name:{value:A.nm}}),a.mn=A.mn,a}function u(A,t,e){function a(t){return A.c.ix===t||\"Copies\"===t?a.copies:A.o.ix===t||\"Offset\"===t?a.offset:null}var r=propertyGroupFactory(a,e),n=t;return a.propertyIndex=A.ix,n.c.setGroupProperty(PropertyInterface(\"Copies\",r)),n.o.setGroupProperty(PropertyInterface(\"Offset\",r)),Object.defineProperties(a,{copies:{get:ExpressionPropertyInterface(n.c)},offset:{get:ExpressionPropertyInterface(n.o)},_name:{value:A.nm}}),a.mn=A.mn,a}return function(t,e,a){var r;function n(A){if(\"number\"==typeof A)return 0===(A=void 0===A?1:A)?a:r[A-1];for(var t=0,e=r.length;t<e;){if(r[t]._name===A)return r[t];t+=1}return null}return n.propertyGroup=propertyGroupFactory(n,function(){return a}),r=A(t,e,n.propertyGroup),n.numProperties=r.length,n._name=\"Contents\",n}}(),TextExpressionInterface=function(A){var t;function e(A){return\"ADBE Text Document\"===A?e.sourceText:null}return Object.defineProperty(e,\"sourceText\",{get:function(){A.textProperty.getValue();var e=A.textProperty.currentData.t;return t&&e===t.value||((t=new String(e)).value=e||new String(e),Object.defineProperty(t,\"style\",{get:function(){return{fillColor:A.textProperty.currentData.fc}}})),t}}),e};function _typeof(A){return(_typeof=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(A){return typeof A}:function(A){return A&&\"function\"==typeof Symbol&&A.constructor===Symbol&&A!==Symbol.prototype?\"symbol\":typeof A})(A)}var FootageInterface=(dataInterfaceFactory=function(A){function t(A){return\"Outline\"===A?t.outlineInterface():null}return t._name=\"Outline\",t.outlineInterface=function(A){var t=\"\",e=A.getFootageData();function a(A){if(e[A])return t=A,\"object\"===_typeof(e=e[A])?a:e;var r=A.indexOf(t);if(-1!==r){var n=parseInt(A.substr(r+t.length),10);return\"object\"===_typeof(e=e[n])?a:e}return\"\"}return function(){return t=\"\",e=A.getFootageData(),a}}(A),t},function(A){function t(A){return\"Data\"===A?t.dataInterface:null}return t._name=\"Data\",t.dataInterface=dataInterfaceFactory(A),t}),dataInterfaceFactory,interfaces={layer:LayerExpressionInterface,effects:EffectsExpressionInterface,comp:CompExpressionInterface,shape:ShapeExpressionInterface,text:TextExpressionInterface,footage:FootageInterface};function getInterface(A){return interfaces[A]||null}var expressionHelpers={searchExpressions:function(A,t,e){t.x&&(e.k=!0,e.x=!0,e.initiateExpression=ExpressionManager.initiateExpression,e.effectsSequence.push(e.initiateExpression(A,t,e).bind(e)))},getSpeedAtTime:function(A){var t=this.getValueAtTime(A),e=this.getValueAtTime(A+-.01),a=0;if(t.length){var r;for(r=0;r<t.length;r+=1)a+=Math.pow(e[r]-t[r],2);a=100*Math.sqrt(a)}else a=0;return a},getVelocityAtTime:function(A){if(void 0!==this.vel)return this.vel;var t,e,a=this.getValueAtTime(A),r=this.getValueAtTime(A+-.001);if(a.length)for(t=createTypedArray(\"float32\",a.length),e=0;e<a.length;e+=1)t[e]=(r[e]-a[e])/-.001;else t=(r-a)/-.001;return t},getValueAtTime:function(A){return A*=this.elem.globalData.frameRate,(A-=this.offsetTime)!==this._cachingAtTime.lastFrame&&(this._cachingAtTime.lastIndex=this._cachingAtTime.lastFrame<A?this._cachingAtTime.lastIndex:0,this._cachingAtTime.value=this.interpolateValue(A,this._cachingAtTime),this._cachingAtTime.lastFrame=A),this._cachingAtTime.value},getStaticValueAtTime:function(){return this.pv},setGroupProperty:function(A){this.propertyGroup=A}};function addPropertyDecorator(){function A(A,t,e){if(!this.k||!this.keyframes)return this.pv;A=A?A.toLowerCase():\"\";var a,r,n,l,i,o=this.comp.renderedFrame,s=this.keyframes,p=s[s.length-1].t;if(o<=p)return this.pv;if(e?r=p-(a=t?Math.abs(p-this.elem.comp.globalData.frameRate*t):Math.max(0,p-this.elem.data.ip)):((!t||t>s.length-1)&&(t=s.length-1),a=p-(r=s[s.length-1-t].t)),\"pingpong\"===A){if(Math.floor((o-r)/a)%2!=0)return this.getValueAtTime((a-(o-r)%a+r)/this.comp.globalData.frameRate,0)}else{if(\"offset\"===A){var c=this.getValueAtTime(r/this.comp.globalData.frameRate,0),u=this.getValueAtTime(p/this.comp.globalData.frameRate,0),d=this.getValueAtTime(((o-r)%a+r)/this.comp.globalData.frameRate,0),h=Math.floor((o-r)/a);if(this.pv.length){for(l=(i=new Array(c.length)).length,n=0;n<l;n+=1)i[n]=(u[n]-c[n])*h+d[n];return i}return(u-c)*h+d}if(\"continue\"===A){var S=this.getValueAtTime(p/this.comp.globalData.frameRate,0),f=this.getValueAtTime((p-.001)/this.comp.globalData.frameRate,0);if(this.pv.length){for(l=(i=new Array(S.length)).length,n=0;n<l;n+=1)i[n]=S[n]+(S[n]-f[n])*((o-p)/this.comp.globalData.frameRate)/5e-4;return i}return S+(o-p)/.001*(S-f)}}return this.getValueAtTime(((o-r)%a+r)/this.comp.globalData.frameRate,0)}function t(A,t,e){if(!this.k)return this.pv;A=A?A.toLowerCase():\"\";var a,r,n,l,i,o=this.comp.renderedFrame,s=this.keyframes,p=s[0].t;if(o>=p)return this.pv;if(e?r=p+(a=t?Math.abs(this.elem.comp.globalData.frameRate*t):Math.max(0,this.elem.data.op-p)):((!t||t>s.length-1)&&(t=s.length-1),a=(r=s[t].t)-p),\"pingpong\"===A){if(Math.floor((p-o)/a)%2==0)return this.getValueAtTime(((p-o)%a+p)/this.comp.globalData.frameRate,0)}else{if(\"offset\"===A){var c=this.getValueAtTime(p/this.comp.globalData.frameRate,0),u=this.getValueAtTime(r/this.comp.globalData.frameRate,0),d=this.getValueAtTime((a-(p-o)%a+p)/this.comp.globalData.frameRate,0),h=Math.floor((p-o)/a)+1;if(this.pv.length){for(l=(i=new Array(c.length)).length,n=0;n<l;n+=1)i[n]=d[n]-(u[n]-c[n])*h;return i}return d-(u-c)*h}if(\"continue\"===A){var S=this.getValueAtTime(p/this.comp.globalData.frameRate,0),f=this.getValueAtTime((p+.001)/this.comp.globalData.frameRate,0);if(this.pv.length){for(l=(i=new Array(S.length)).length,n=0;n<l;n+=1)i[n]=S[n]+(S[n]-f[n])*(p-o)/.001;return i}return S+(S-f)*(p-o)/.001}}return this.getValueAtTime((a-((p-o)%a+p))/this.comp.globalData.frameRate,0)}function e(A,t){if(!this.k)return this.pv;if(A=.5*(A||.4),(t=Math.floor(t||5))<=1)return this.pv;var e,a,r=this.comp.renderedFrame/this.comp.globalData.frameRate,n=r-A,l=t>1?(r+A-n)/(t-1):1,i=0,o=0;for(e=this.pv.length?createTypedArray(\"float32\",this.pv.length):0;i<t;){if(a=this.getValueAtTime(n+i*l),this.pv.length)for(o=0;o<this.pv.length;o+=1)e[o]+=a[o];else e+=a;i+=1}if(this.pv.length)for(o=0;o<this.pv.length;o+=1)e[o]/=t;else e/=t;return e}function a(A){this._transformCachingAtTime||(this._transformCachingAtTime={v:new Matrix});var t=this._transformCachingAtTime.v;if(t.cloneFromProps(this.pre.props),this.appliedTransformations<1){var e=this.a.getValueAtTime(A);t.translate(-e[0]*this.a.mult,-e[1]*this.a.mult,e[2]*this.a.mult)}if(this.appliedTransformations<2){var a=this.s.getValueAtTime(A);t.scale(a[0]*this.s.mult,a[1]*this.s.mult,a[2]*this.s.mult)}if(this.sk&&this.appliedTransformations<3){var r=this.sk.getValueAtTime(A),n=this.sa.getValueAtTime(A);t.skewFromAxis(-r*this.sk.mult,n*this.sa.mult)}if(this.r&&this.appliedTransformations<4){var l=this.r.getValueAtTime(A);t.rotate(-l*this.r.mult)}else if(!this.r&&this.appliedTransformations<4){var i=this.rz.getValueAtTime(A),o=this.ry.getValueAtTime(A),s=this.rx.getValueAtTime(A),p=this.or.getValueAtTime(A);t.rotateZ(-i*this.rz.mult).rotateY(o*this.ry.mult).rotateX(s*this.rx.mult).rotateZ(-p[2]*this.or.mult).rotateY(p[1]*this.or.mult).rotateX(p[0]*this.or.mult)}if(this.data.p&&this.data.p.s){var c=this.px.getValueAtTime(A),u=this.py.getValueAtTime(A);if(this.data.p.z){var d=this.pz.getValueAtTime(A);t.translate(c*this.px.mult,u*this.py.mult,-d*this.pz.mult)}else t.translate(c*this.px.mult,u*this.py.mult,0)}else{var h=this.p.getValueAtTime(A);t.translate(h[0]*this.p.mult,h[1]*this.p.mult,-h[2]*this.p.mult)}return t}function r(){return this.v.clone(new Matrix)}var n=TransformPropertyFactory.getTransformProperty;TransformPropertyFactory.getTransformProperty=function(A,t,e){var l=n(A,t,e);return l.dynamicProperties.length?l.getValueAtTime=a.bind(l):l.getValueAtTime=r.bind(l),l.setGroupProperty=expressionHelpers.setGroupProperty,l};var l=PropertyFactory.getProp;PropertyFactory.getProp=function(a,r,n,i,o){var s=l(a,r,n,i,o);s.kf?s.getValueAtTime=expressionHelpers.getValueAtTime.bind(s):s.getValueAtTime=expressionHelpers.getStaticValueAtTime.bind(s),s.setGroupProperty=expressionHelpers.setGroupProperty,s.loopOut=A,s.loopIn=t,s.smooth=e,s.getVelocityAtTime=expressionHelpers.getVelocityAtTime.bind(s),s.getSpeedAtTime=expressionHelpers.getSpeedAtTime.bind(s),s.numKeys=1===r.a?r.k.length:0,s.propertyIndex=r.ix;var p=0;return 0!==n&&(p=createTypedArray(\"float32\",1===r.a?r.k[0].s.length:r.k.length)),s._cachingAtTime={lastFrame:initialDefaultFrame,lastIndex:0,value:p},expressionHelpers.searchExpressions(a,r,s),s.k&&o.addDynamicProperty(s),s};var i=ShapePropertyFactory.getConstructorFunction(),o=ShapePropertyFactory.getKeyframedConstructorFunction();function s(){}s.prototype={vertices:function(A,t){this.k&&this.getValue();var e,a=this.v;void 0!==t&&(a=this.getValueAtTime(t,0));var r=a._length,n=a[A],l=a.v,i=createSizedArray(r);for(e=0;e<r;e+=1)i[e]=\"i\"===A||\"o\"===A?[n[e][0]-l[e][0],n[e][1]-l[e][1]]:[n[e][0],n[e][1]];return i},points:function(A){return this.vertices(\"v\",A)},inTangents:function(A){return this.vertices(\"i\",A)},outTangents:function(A){return this.vertices(\"o\",A)},isClosed:function(){return this.v.c},pointOnPath:function(A,t){var e=this.v;void 0!==t&&(e=this.getValueAtTime(t,0)),this._segmentsLength||(this._segmentsLength=bez.getSegmentsLength(e));for(var a,r=this._segmentsLength,n=r.lengths,l=r.totalLength*A,i=0,o=n.length,s=0;i<o;){if(s+n[i].addedLength>l){var p=i,c=e.c&&i===o-1?0:i+1,u=(l-s)/n[i].addedLength;a=bez.getPointInSegment(e.v[p],e.v[c],e.o[p],e.i[c],u,n[i]);break}s+=n[i].addedLength,i+=1}return a||(a=e.c?[e.v[0][0],e.v[0][1]]:[e.v[e._length-1][0],e.v[e._length-1][1]]),a},vectorOnPath:function(A,t,e){1==A?A=this.v.c:0==A&&(A=.999);var a=this.pointOnPath(A,t),r=this.pointOnPath(A+.001,t),n=r[0]-a[0],l=r[1]-a[1],i=Math.sqrt(Math.pow(n,2)+Math.pow(l,2));return 0===i?[0,0]:\"tangent\"===e?[n/i,l/i]:[-l/i,n/i]},tangentOnPath:function(A,t){return this.vectorOnPath(A,t,\"tangent\")},normalOnPath:function(A,t){return this.vectorOnPath(A,t,\"normal\")},setGroupProperty:expressionHelpers.setGroupProperty,getValueAtTime:expressionHelpers.getStaticValueAtTime},extendPrototype([s],i),extendPrototype([s],o),o.prototype.getValueAtTime=function(A){return this._cachingAtTime||(this._cachingAtTime={shapeValue:shapePool.clone(this.pv),lastIndex:0,lastTime:initialDefaultFrame}),A*=this.elem.globalData.frameRate,(A-=this.offsetTime)!==this._cachingAtTime.lastTime&&(this._cachingAtTime.lastIndex=this._cachingAtTime.lastTime<A?this._caching.lastIndex:0,this._cachingAtTime.lastTime=A,this.interpolateShape(A,this._cachingAtTime.shapeValue,this._cachingAtTime)),this._cachingAtTime.shapeValue},o.prototype.initiateExpression=ExpressionManager.initiateExpression;var p=ShapePropertyFactory.getShapeProp;ShapePropertyFactory.getShapeProp=function(A,t,e,a,r){var n=p(A,t,e,a,r);return n.propertyIndex=t.ix,n.lock=!1,3===e?expressionHelpers.searchExpressions(A,t.pt,n):4===e&&expressionHelpers.searchExpressions(A,t.ks,n),n.k&&A.addDynamicProperty(n),n}}function initialize$1(){addPropertyDecorator()}function addDecorator(){TextProperty.prototype.getExpressionValue=function(A,t){var e=this.calculateExpression(t);if(A.t!==e){var a={};return this.copyData(a,A),a.t=e.toString(),a.__complete=!1,a}return A},TextProperty.prototype.searchProperty=function(){var A=this.searchKeyframes(),t=this.searchExpressions();return this.kf=A||t,this.kf},TextProperty.prototype.searchExpressions=function(){return this.data.d.x?(this.calculateExpression=ExpressionManager.initiateExpression.bind(this)(this.elem,this.data.d,this),this.addEffect(this.getExpressionValue.bind(this)),!0):null}}function initialize(){addDecorator()}function SVGComposableEffect(){}SVGComposableEffect.prototype={createMergeNode:function(A,t){var e,a,r=createNS(\"feMerge\");for(r.setAttribute(\"result\",A),a=0;a<t.length;a+=1)(e=createNS(\"feMergeNode\")).setAttribute(\"in\",t[a]),r.appendChild(e),r.appendChild(e);return r}};var linearFilterValue=\"0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0\";function SVGTintFilter(A,t,e,a,r){this.filterManager=t;var n=createNS(\"feColorMatrix\");n.setAttribute(\"type\",\"matrix\"),n.setAttribute(\"color-interpolation-filters\",\"linearRGB\"),n.setAttribute(\"values\",linearFilterValue+\" 1 0\"),this.linearFilter=n,n.setAttribute(\"result\",a+\"_tint_1\"),A.appendChild(n),(n=createNS(\"feColorMatrix\")).setAttribute(\"type\",\"matrix\"),n.setAttribute(\"color-interpolation-filters\",\"sRGB\"),n.setAttribute(\"values\",\"1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0\"),n.setAttribute(\"result\",a+\"_tint_2\"),A.appendChild(n),this.matrixFilter=n;var l=this.createMergeNode(a,[r,a+\"_tint_1\",a+\"_tint_2\"]);A.appendChild(l)}function SVGFillFilter(A,t,e,a){this.filterManager=t;var r=createNS(\"feColorMatrix\");r.setAttribute(\"type\",\"matrix\"),r.setAttribute(\"color-interpolation-filters\",\"sRGB\"),r.setAttribute(\"values\",\"1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0\"),r.setAttribute(\"result\",a),A.appendChild(r),this.matrixFilter=r}function SVGStrokeEffect(A,t,e){this.initialized=!1,this.filterManager=t,this.elem=e,this.paths=[]}function SVGTritoneFilter(A,t,e,a){this.filterManager=t;var r=createNS(\"feColorMatrix\");r.setAttribute(\"type\",\"matrix\"),r.setAttribute(\"color-interpolation-filters\",\"linearRGB\"),r.setAttribute(\"values\",\"0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\"),A.appendChild(r);var n=createNS(\"feComponentTransfer\");n.setAttribute(\"color-interpolation-filters\",\"sRGB\"),n.setAttribute(\"result\",a),this.matrixFilter=n;var l=createNS(\"feFuncR\");l.setAttribute(\"type\",\"table\"),n.appendChild(l),this.feFuncR=l;var i=createNS(\"feFuncG\");i.setAttribute(\"type\",\"table\"),n.appendChild(i),this.feFuncG=i;var o=createNS(\"feFuncB\");o.setAttribute(\"type\",\"table\"),n.appendChild(o),this.feFuncB=o,A.appendChild(n)}function SVGProLevelsFilter(A,t,e,a){this.filterManager=t;var r=this.filterManager.effectElements,n=createNS(\"feComponentTransfer\");(r[10].p.k||0!==r[10].p.v||r[11].p.k||1!==r[11].p.v||r[12].p.k||1!==r[12].p.v||r[13].p.k||0!==r[13].p.v||r[14].p.k||1!==r[14].p.v)&&(this.feFuncR=this.createFeFunc(\"feFuncR\",n)),(r[17].p.k||0!==r[17].p.v||r[18].p.k||1!==r[18].p.v||r[19].p.k||1!==r[19].p.v||r[20].p.k||0!==r[20].p.v||r[21].p.k||1!==r[21].p.v)&&(this.feFuncG=this.createFeFunc(\"feFuncG\",n)),(r[24].p.k||0!==r[24].p.v||r[25].p.k||1!==r[25].p.v||r[26].p.k||1!==r[26].p.v||r[27].p.k||0!==r[27].p.v||r[28].p.k||1!==r[28].p.v)&&(this.feFuncB=this.createFeFunc(\"feFuncB\",n)),(r[31].p.k||0!==r[31].p.v||r[32].p.k||1!==r[32].p.v||r[33].p.k||1!==r[33].p.v||r[34].p.k||0!==r[34].p.v||r[35].p.k||1!==r[35].p.v)&&(this.feFuncA=this.createFeFunc(\"feFuncA\",n)),(this.feFuncR||this.feFuncG||this.feFuncB||this.feFuncA)&&(n.setAttribute(\"color-interpolation-filters\",\"sRGB\"),A.appendChild(n)),(r[3].p.k||0!==r[3].p.v||r[4].p.k||1!==r[4].p.v||r[5].p.k||1!==r[5].p.v||r[6].p.k||0!==r[6].p.v||r[7].p.k||1!==r[7].p.v)&&((n=createNS(\"feComponentTransfer\")).setAttribute(\"color-interpolation-filters\",\"sRGB\"),n.setAttribute(\"result\",a),A.appendChild(n),this.feFuncRComposed=this.createFeFunc(\"feFuncR\",n),this.feFuncGComposed=this.createFeFunc(\"feFuncG\",n),this.feFuncBComposed=this.createFeFunc(\"feFuncB\",n))}function SVGDropShadowEffect(A,t,e,a,r){var n=t.container.globalData.renderConfig.filterSize,l=t.data.fs||n;A.setAttribute(\"x\",l.x||n.x),A.setAttribute(\"y\",l.y||n.y),A.setAttribute(\"width\",l.width||n.width),A.setAttribute(\"height\",l.height||n.height),this.filterManager=t;var i=createNS(\"feGaussianBlur\");i.setAttribute(\"in\",\"SourceAlpha\"),i.setAttribute(\"result\",a+\"_drop_shadow_1\"),i.setAttribute(\"stdDeviation\",\"0\"),this.feGaussianBlur=i,A.appendChild(i);var o=createNS(\"feOffset\");o.setAttribute(\"dx\",\"25\"),o.setAttribute(\"dy\",\"0\"),o.setAttribute(\"in\",a+\"_drop_shadow_1\"),o.setAttribute(\"result\",a+\"_drop_shadow_2\"),this.feOffset=o,A.appendChild(o);var s=createNS(\"feFlood\");s.setAttribute(\"flood-color\",\"#00ff00\"),s.setAttribute(\"flood-opacity\",\"1\"),s.setAttribute(\"result\",a+\"_drop_shadow_3\"),this.feFlood=s,A.appendChild(s);var p=createNS(\"feComposite\");p.setAttribute(\"in\",a+\"_drop_shadow_3\"),p.setAttribute(\"in2\",a+\"_drop_shadow_2\"),p.setAttribute(\"operator\",\"in\"),p.setAttribute(\"result\",a+\"_drop_shadow_4\"),A.appendChild(p);var c=this.createMergeNode(a,[a+\"_drop_shadow_4\",r]);A.appendChild(c)}extendPrototype([SVGComposableEffect],SVGTintFilter),SVGTintFilter.prototype.renderFrame=function(A){if(A||this.filterManager._mdf){var t=this.filterManager.effectElements[0].p.v,e=this.filterManager.effectElements[1].p.v,a=this.filterManager.effectElements[2].p.v/100;this.linearFilter.setAttribute(\"values\",linearFilterValue+\" \"+a+\" 0\"),this.matrixFilter.setAttribute(\"values\",e[0]-t[0]+\" 0 0 0 \"+t[0]+\" \"+(e[1]-t[1])+\" 0 0 0 \"+t[1]+\" \"+(e[2]-t[2])+\" 0 0 0 \"+t[2]+\" 0 0 0 1 0\")}},SVGFillFilter.prototype.renderFrame=function(A){if(A||this.filterManager._mdf){var t=this.filterManager.effectElements[2].p.v,e=this.filterManager.effectElements[6].p.v;this.matrixFilter.setAttribute(\"values\",\"0 0 0 0 \"+t[0]+\" 0 0 0 0 \"+t[1]+\" 0 0 0 0 \"+t[2]+\" 0 0 0 \"+e+\" 0\")}},SVGStrokeEffect.prototype.initialize=function(){var A,t,e,a,r=this.elem.layerElement.children||this.elem.layerElement.childNodes;for(1===this.filterManager.effectElements[1].p.v?(a=this.elem.maskManager.masksProperties.length,e=0):a=1+(e=this.filterManager.effectElements[0].p.v-1),(t=createNS(\"g\")).setAttribute(\"fill\",\"none\"),t.setAttribute(\"stroke-linecap\",\"round\"),t.setAttribute(\"stroke-dashoffset\",1);e<a;e+=1)A=createNS(\"path\"),t.appendChild(A),this.paths.push({p:A,m:e});if(3===this.filterManager.effectElements[10].p.v){var n=createNS(\"mask\"),l=createElementID();n.setAttribute(\"id\",l),n.setAttribute(\"mask-type\",\"alpha\"),n.appendChild(t),this.elem.globalData.defs.appendChild(n);var i=createNS(\"g\");for(i.setAttribute(\"mask\",\"url(\"+getLocationHref()+\"#\"+l+\")\");r[0];)i.appendChild(r[0]);this.elem.layerElement.appendChild(i),this.masker=n,t.setAttribute(\"stroke\",\"#fff\")}else if(1===this.filterManager.effectElements[10].p.v||2===this.filterManager.effectElements[10].p.v){if(2===this.filterManager.effectElements[10].p.v)for(r=this.elem.layerElement.children||this.elem.layerElement.childNodes;r.length;)this.elem.layerElement.removeChild(r[0]);this.elem.layerElement.appendChild(t),this.elem.layerElement.removeAttribute(\"mask\"),t.setAttribute(\"stroke\",\"#fff\")}this.initialized=!0,this.pathMasker=t},SVGStrokeEffect.prototype.renderFrame=function(A){var t;this.initialized||this.initialize();var e,a,r=this.paths.length;for(t=0;t<r;t+=1)if(-1!==this.paths[t].m&&(e=this.elem.maskManager.viewData[this.paths[t].m],a=this.paths[t].p,(A||this.filterManager._mdf||e.prop._mdf)&&a.setAttribute(\"d\",e.lastPath),A||this.filterManager.effectElements[9].p._mdf||this.filterManager.effectElements[4].p._mdf||this.filterManager.effectElements[7].p._mdf||this.filterManager.effectElements[8].p._mdf||e.prop._mdf)){var n;if(0!==this.filterManager.effectElements[7].p.v||100!==this.filterManager.effectElements[8].p.v){var l=.01*Math.min(this.filterManager.effectElements[7].p.v,this.filterManager.effectElements[8].p.v),i=.01*Math.max(this.filterManager.effectElements[7].p.v,this.filterManager.effectElements[8].p.v),o=a.getTotalLength();n=\"0 0 0 \"+o*l+\" \";var s,p=o*(i-l),c=1+2*this.filterManager.effectElements[4].p.v*this.filterManager.effectElements[9].p.v*.01,u=Math.floor(p/c);for(s=0;s<u;s+=1)n+=\"1 \"+2*this.filterManager.effectElements[4].p.v*this.filterManager.effectElements[9].p.v*.01+\" \";n+=\"0 \"+10*o+\" 0 0\"}else n=\"1 \"+2*this.filterManager.effectElements[4].p.v*this.filterManager.effectElements[9].p.v*.01;a.setAttribute(\"stroke-dasharray\",n)}if((A||this.filterManager.effectElements[4].p._mdf)&&this.pathMasker.setAttribute(\"stroke-width\",2*this.filterManager.effectElements[4].p.v),(A||this.filterManager.effectElements[6].p._mdf)&&this.pathMasker.setAttribute(\"opacity\",this.filterManager.effectElements[6].p.v),(1===this.filterManager.effectElements[10].p.v||2===this.filterManager.effectElements[10].p.v)&&(A||this.filterManager.effectElements[3].p._mdf)){var d=this.filterManager.effectElements[3].p.v;this.pathMasker.setAttribute(\"stroke\",\"rgb(\"+bmFloor(255*d[0])+\",\"+bmFloor(255*d[1])+\",\"+bmFloor(255*d[2])+\")\")}},SVGTritoneFilter.prototype.renderFrame=function(A){if(A||this.filterManager._mdf){var t=this.filterManager.effectElements[0].p.v,e=this.filterManager.effectElements[1].p.v,a=this.filterManager.effectElements[2].p.v,r=a[0]+\" \"+e[0]+\" \"+t[0],n=a[1]+\" \"+e[1]+\" \"+t[1],l=a[2]+\" \"+e[2]+\" \"+t[2];this.feFuncR.setAttribute(\"tableValues\",r),this.feFuncG.setAttribute(\"tableValues\",n),this.feFuncB.setAttribute(\"tableValues\",l)}},SVGProLevelsFilter.prototype.createFeFunc=function(A,t){var e=createNS(A);return e.setAttribute(\"type\",\"table\"),t.appendChild(e),e},SVGProLevelsFilter.prototype.getTableValue=function(A,t,e,a,r){for(var n,l,i=0,o=Math.min(A,t),s=Math.max(A,t),p=Array.call(null,{length:256}),c=0,u=r-a,d=t-A;i<=256;)l=(n=i/256)<=o?d<0?r:a:n>=s?d<0?a:r:a+u*Math.pow((n-A)/d,1/e),p[c]=l,c+=1,i+=256/255;return p.join(\" \")},SVGProLevelsFilter.prototype.renderFrame=function(A){if(A||this.filterManager._mdf){var t,e=this.filterManager.effectElements;this.feFuncRComposed&&(A||e[3].p._mdf||e[4].p._mdf||e[5].p._mdf||e[6].p._mdf||e[7].p._mdf)&&(t=this.getTableValue(e[3].p.v,e[4].p.v,e[5].p.v,e[6].p.v,e[7].p.v),this.feFuncRComposed.setAttribute(\"tableValues\",t),this.feFuncGComposed.setAttribute(\"tableValues\",t),this.feFuncBComposed.setAttribute(\"tableValues\",t)),this.feFuncR&&(A||e[10].p._mdf||e[11].p._mdf||e[12].p._mdf||e[13].p._mdf||e[14].p._mdf)&&(t=this.getTableValue(e[10].p.v,e[11].p.v,e[12].p.v,e[13].p.v,e[14].p.v),this.feFuncR.setAttribute(\"tableValues\",t)),this.feFuncG&&(A||e[17].p._mdf||e[18].p._mdf||e[19].p._mdf||e[20].p._mdf||e[21].p._mdf)&&(t=this.getTableValue(e[17].p.v,e[18].p.v,e[19].p.v,e[20].p.v,e[21].p.v),this.feFuncG.setAttribute(\"tableValues\",t)),this.feFuncB&&(A||e[24].p._mdf||e[25].p._mdf||e[26].p._mdf||e[27].p._mdf||e[28].p._mdf)&&(t=this.getTableValue(e[24].p.v,e[25].p.v,e[26].p.v,e[27].p.v,e[28].p.v),this.feFuncB.setAttribute(\"tableValues\",t)),this.feFuncA&&(A||e[31].p._mdf||e[32].p._mdf||e[33].p._mdf||e[34].p._mdf||e[35].p._mdf)&&(t=this.getTableValue(e[31].p.v,e[32].p.v,e[33].p.v,e[34].p.v,e[35].p.v),this.feFuncA.setAttribute(\"tableValues\",t))}},extendPrototype([SVGComposableEffect],SVGDropShadowEffect),SVGDropShadowEffect.prototype.renderFrame=function(A){if(A||this.filterManager._mdf){if((A||this.filterManager.effectElements[4].p._mdf)&&this.feGaussianBlur.setAttribute(\"stdDeviation\",this.filterManager.effectElements[4].p.v/4),A||this.filterManager.effectElements[0].p._mdf){var t=this.filterManager.effectElements[0].p.v;this.feFlood.setAttribute(\"flood-color\",rgbToHex(Math.round(255*t[0]),Math.round(255*t[1]),Math.round(255*t[2])))}if((A||this.filterManager.effectElements[1].p._mdf)&&this.feFlood.setAttribute(\"flood-opacity\",this.filterManager.effectElements[1].p.v/255),A||this.filterManager.effectElements[2].p._mdf||this.filterManager.effectElements[3].p._mdf){var e=this.filterManager.effectElements[3].p.v,a=(this.filterManager.effectElements[2].p.v-90)*degToRads,r=e*Math.cos(a),n=e*Math.sin(a);this.feOffset.setAttribute(\"dx\",r),this.feOffset.setAttribute(\"dy\",n)}}};var _svgMatteSymbols=[];function SVGMatte3Effect(A,t,e){this.initialized=!1,this.filterManager=t,this.filterElem=A,this.elem=e,e.matteElement=createNS(\"g\"),e.matteElement.appendChild(e.layerElement),e.matteElement.appendChild(e.transformedElement),e.baseElement=e.matteElement}function SVGGaussianBlurEffect(A,t,e,a){A.setAttribute(\"x\",\"-100%\"),A.setAttribute(\"y\",\"-100%\"),A.setAttribute(\"width\",\"300%\"),A.setAttribute(\"height\",\"300%\"),this.filterManager=t;var r=createNS(\"feGaussianBlur\");r.setAttribute(\"result\",a),A.appendChild(r),this.feGaussianBlur=r}function TransformEffect(){}function SVGTransformEffect(A,t){this.init(t)}function CVTransformEffect(A){this.init(A)}return SVGMatte3Effect.prototype.findSymbol=function(A){for(var t=0,e=_svgMatteSymbols.length;t<e;){if(_svgMatteSymbols[t]===A)return _svgMatteSymbols[t];t+=1}return null},SVGMatte3Effect.prototype.replaceInParent=function(A,t){var e=A.layerElement.parentNode;if(e){for(var a,r=e.children,n=0,l=r.length;n<l&&r[n]!==A.layerElement;)n+=1;n<=l-2&&(a=r[n+1]);var i=createNS(\"use\");i.setAttribute(\"href\",\"#\"+t),a?e.insertBefore(i,a):e.appendChild(i)}},SVGMatte3Effect.prototype.setElementAsMask=function(A,t){if(!this.findSymbol(t)){var e=createElementID(),a=createNS(\"mask\");a.setAttribute(\"id\",t.layerId),a.setAttribute(\"mask-type\",\"alpha\"),_svgMatteSymbols.push(t);var r=A.globalData.defs;r.appendChild(a);var n=createNS(\"symbol\");n.setAttribute(\"id\",e),this.replaceInParent(t,e),n.appendChild(t.layerElement),r.appendChild(n);var l=createNS(\"use\");l.setAttribute(\"href\",\"#\"+e),a.appendChild(l),t.data.hd=!1,t.show()}A.setMatte(t.layerId)},SVGMatte3Effect.prototype.initialize=function(){for(var A=this.filterManager.effectElements[0].p.v,t=this.elem.comp.elements,e=0,a=t.length;e<a;)t[e]&&t[e].data.ind===A&&this.setElementAsMask(this.elem,t[e]),e+=1;this.initialized=!0},SVGMatte3Effect.prototype.renderFrame=function(){this.initialized||this.initialize()},SVGGaussianBlurEffect.prototype.renderFrame=function(A){if(A||this.filterManager._mdf){var t=.3*this.filterManager.effectElements[0].p.v,e=this.filterManager.effectElements[1].p.v,a=3==e?0:t,r=2==e?0:t;this.feGaussianBlur.setAttribute(\"stdDeviation\",a+\" \"+r);var n=1==this.filterManager.effectElements[2].p.v?\"wrap\":\"duplicate\";this.feGaussianBlur.setAttribute(\"edgeMode\",n)}},TransformEffect.prototype.init=function(A){this.effectsManager=A,this.type=effectTypes.TRANSFORM_EFFECT,this.matrix=new Matrix,this.opacity=-1,this._mdf=!1,this._opMdf=!1},TransformEffect.prototype.renderFrame=function(A){if(this._opMdf=!1,this._mdf=!1,A||this.effectsManager._mdf){var t=this.effectsManager.effectElements,e=t[0].p.v,a=t[1].p.v,r=1===t[2].p.v,n=t[3].p.v,l=r?n:t[4].p.v,i=t[5].p.v,o=t[6].p.v,s=t[7].p.v;this.matrix.reset(),this.matrix.translate(-e[0],-e[1],e[2]),this.matrix.scale(.01*l,.01*n,1),this.matrix.rotate(-s*degToRads),this.matrix.skewFromAxis(-i*degToRads,(o+90)*degToRads),this.matrix.translate(a[0],a[1],0),this._mdf=!0,this.opacity!==t[8].p.v&&(this.opacity=t[8].p.v,this._opMdf=!0)}},extendPrototype([TransformEffect],SVGTransformEffect),extendPrototype([TransformEffect],CVTransformEffect),registerRenderer(\"canvas\",CanvasRenderer),registerRenderer(\"html\",HybridRenderer),registerRenderer(\"svg\",SVGRenderer),ShapeModifiers.registerModifier(\"tm\",TrimModifier),ShapeModifiers.registerModifier(\"pb\",PuckerAndBloatModifier),ShapeModifiers.registerModifier(\"rp\",RepeaterModifier),ShapeModifiers.registerModifier(\"rd\",RoundCornersModifier),ShapeModifiers.registerModifier(\"zz\",ZigZagModifier),ShapeModifiers.registerModifier(\"op\",OffsetPathModifier),setExpressionsPlugin(Expressions),setExpressionInterfaces(getInterface),initialize$1(),initialize(),registerEffect$1(20,SVGTintFilter,!0),registerEffect$1(21,SVGFillFilter,!0),registerEffect$1(22,SVGStrokeEffect,!1),registerEffect$1(23,SVGTritoneFilter,!0),registerEffect$1(24,SVGProLevelsFilter,!0),registerEffect$1(25,SVGDropShadowEffect,!0),registerEffect$1(28,SVGMatte3Effect,!1),registerEffect$1(29,SVGGaussianBlurEffect,!0),registerEffect$1(35,SVGTransformEffect,!1),registerEffect(35,CVTransformEffect),lottie},module.exports=e())}),REACT_LOTTIE_PLAYER_VERSION=\"3.6.0\",LOTTIE_WEB_VERSION=\"^5.12.2\",PlayerState,PlayerEvent,t;function parseSrc(A){if(\"object\"==typeof A)return A;try{return JSON.parse(A)}catch(t){}try{return new URL(A).toString()}catch(t){}return A}t=PlayerState||(PlayerState={}),t.Loading=\"loading\",t.Playing=\"playing\",t.Paused=\"paused\",t.Stopped=\"stopped\",t.Frozen=\"frozen\",t.Error=\"error\",function(A){A.Load=\"load\",A.InstanceSaved=\"instanceSaved\",A.Error=\"error\",A.Ready=\"ready\",A.Play=\"play\",A.Pause=\"pause\",A.Stop=\"stop\",A.Freeze=\"freeze\",A.Loop=\"loop\",A.Complete=\"complete\",A.Frame=\"frame\"}(PlayerEvent||(PlayerEvent={}));var defaultOptions={clearCanvas:!1,hideOnTransparent:!0,progressiveLoad:!0},Player=function(A){function t(t){var e=A.call(this,t)||this;return e.container=null,e.unmounted=!1,e.handleBgChange=function(A){e.setState({background:A})},e.triggerDownload=function(A,t){var e=document.createElement(\"a\");e.href=A,e.download=t,document.body.appendChild(e),e.click(),document.body.removeChild(e)},e.snapshot=function(A){var t;void 0===A&&(A=!0);var a=e.props.id?e.props.id:\"lottie\",r=document.getElementById(a);if(\"svg\"===e.props.renderer){if(r){var n=r.querySelector(\"svg\");if(n){var l=(new XMLSerializer).serializeToString(n);t=\"data:image/svg+xml;charset=utf-8,\"+encodeURIComponent(l)}}A&&e.triggerDownload(t,\"snapshot.svg\")}else if(\"canvas\"===e.props.renderer){if(r){var i=r.querySelector(\"canvas\");i&&(t=i.toDataURL(\"image/png\"))}A&&e.triggerDownload(t,\"snapshot.png\")}return t},\"undefined\"!=typeof window&&(window.lottie=lottie),e.state={animationData:null,background:\"transparent\",containerRef:reactExports.createRef(),debug:!0,instance:null,playerState:PlayerState.Loading,seeker:0},e}return __extends(t,A),t.getDerivedStateFromProps=function(A,t){return __awaiter(this,void 0,void 0,function(){return __generator(this,function(e){return A.background!==t.background?[2,{background:A.background}]:[2,null]})})},t.prototype.getVersions=function(){return{lottieWebVersion:LOTTIE_WEB_VERSION,lottiePlayerVersion:REACT_LOTTIE_PLAYER_VERSION}},t.prototype.componentDidMount=function(){return __awaiter(this,void 0,void 0,function(){return __generator(this,function(A){switch(A.label){case 0:return this.unmounted?[3,2]:[4,this.createLottie()];case 1:A.sent(),A.label=2;case 2:return[2]}})})},t.prototype.componentWillUnmount=function(){this.unmounted=!0,this.state.instance&&this.state.instance.destroy()},t.prototype.componentDidUpdate=function(A){return __awaiter(this,void 0,void 0,function(){return __generator(this,function(t){switch(t.label){case 0:return this.props.src===A.src?[3,2]:(this.state.instance&&this.state.instance.destroy(),[4,this.createLottie()]);case 1:t.sent(),t.label=2;case 2:return[2]}})})},t.prototype.render=function(){var A=this,t=this.props,e=t.children,a=t.loop,r=t.style,n=t.onBackgroundChange,l=t.className,i=this.state,o=i.animationData,s=i.instance,p=i.playerState,c=i.seeker,u=i.debug,d=i.background;return reactExports.createElement(\"div\",{className:\"lf-player-container\"},this.state.playerState===PlayerState.Error?reactExports.createElement(\"div\",{className:\"lf-error\"},reactExports.createElement(\"span\",{\"aria-label\":\"error-symbol\",role:\"img\"},\"⚠️\")):reactExports.createElement(\"div\",{id:this.props.id?this.props.id:\"lottie\",ref:function(t){return A.container=t},style:__assign({background:d,margin:\"0 auto\",outline:\"none\",overflow:\"hidden\"},r),className:l}),reactExports.Children.map(e,function(t){return reactExports.isValidElement(t)?reactExports.cloneElement(t,{animationData:o,background:d,debug:u,instance:s,loop:a,pause:function(){return A.pause()},play:function(){return A.play()},playerState:p,seeker:c,setBackground:function(t){A.setState({background:t}),\"function\"==typeof n&&n(t)},setSeeker:function(t,e){return A.setSeeker(t,e)},stop:function(){return A.stop()},toggleDebug:function(){return A.toggleDebug()},setLoop:function(t){return A.setLoop(t)},colorChangedEvent:function(t){A.handleBgChange(t)},snapshot:function(){A.snapshot()}}):null}))},t.prototype.toggleDebug=function(){this.setState({debug:!this.state.debug})},t.prototype.createLottie=function(){return __awaiter(this,void 0,void 0,function(){var A,t,e,a,r,n,l,i,o,s,p,c,u,d,h=this;return __generator(this,function(S){switch(S.label){case 0:if(A=this.props,t=A.autoplay,e=A.direction,a=A.loop,r=A.lottieRef,n=A.renderer,l=A.speed,i=A.src,o=A.background,s=A.rendererSettings,p=A.hover,c=this.state.instance,!i||!this.container)return[2];S.label=1;case 1:return S.trys.push([1,5,,6]),\"string\"!=typeof(u=parseSrc(i))?[3,4]:[4,fetch(u).catch(function(){throw h.setState({playerState:PlayerState.Error}),h.triggerEvent(PlayerEvent.Error),new Error(\"@LottieFiles/lottie-react: Animation data could not be fetched.\")})];case 2:return[4,S.sent().json().catch(function(){throw h.setState({playerState:PlayerState.Error}),h.triggerEvent(PlayerEvent.Error),new Error(\"@LottieFiles/lottie-react: Animation data could not be fetched.\")})];case 3:u=S.sent(),S.label=4;case 4:return c&&c.destroy(),d=lottie.loadAnimation({rendererSettings:s||defaultOptions,animationData:u,autoplay:t||!1,container:this.container,loop:a||!1,renderer:n}),l&&d.setSpeed(l),this.setState({animationData:u}),this.setState({instance:d},function(){h.triggerEvent(PlayerEvent.InstanceSaved),\"function\"==typeof r&&r(d),t&&h.play()}),d.addEventListener(\"enterFrame\",function(){h.triggerEvent(PlayerEvent.Frame),h.setState({seeker:Math.floor(d.currentFrame)})}),d.addEventListener(\"DOMLoaded\",function(){h.triggerEvent(PlayerEvent.Load)}),d.addEventListener(\"data_ready\",function(){h.triggerEvent(PlayerEvent.Ready)}),d.addEventListener(\"data_failed\",function(){h.setState({playerState:PlayerState.Error}),h.triggerEvent(PlayerEvent.Error)}),d.addEventListener(\"loopComplete\",function(){h.triggerEvent(PlayerEvent.Loop)}),d.addEventListener(\"complete\",function(){h.triggerEvent(PlayerEvent.Complete),h.setState({playerState:PlayerState.Paused}),h.props.keepLastFrame&&!h.props.loop||h.setSeeker(0)}),this.container&&(this.container.addEventListener(\"mouseenter\",function(){p&&h.state.playerState!==PlayerState.Playing&&(h.props.keepLastFrame&&h.stop(),h.play())}),this.container.addEventListener(\"mouseleave\",function(){p&&h.state.playerState===PlayerState.Playing&&h.stop()})),l&&this.setPlayerSpeed(l),e&&this.setPlayerDirection(e),o&&this.setState({background:o}),[3,6];case 5:return S.sent(),this.setState({playerState:PlayerState.Error}),this.triggerEvent(PlayerEvent.Error),[3,6];case 6:return[2]}})})},t.prototype.play=function(){var A=this.state.instance;A&&(this.triggerEvent(PlayerEvent.Play),A.play(),this.setState({playerState:PlayerState.Playing}))},t.prototype.pause=function(){var A=this.state.instance;A&&(this.triggerEvent(PlayerEvent.Pause),A.pause(),this.setState({playerState:PlayerState.Paused}))},t.prototype.stop=function(){var A=this.state.instance;A&&(this.triggerEvent(PlayerEvent.Stop),A.stop(),this.setState({playerState:PlayerState.Stopped}))},t.prototype.setPlayerSpeed=function(A){var t=this.state.instance;t&&t.setSpeed(A)},t.prototype.setPlayerDirection=function(A){var t=this.state.instance;t&&t.setDirection(A)},t.prototype.setSeeker=function(A,t){void 0===t&&(t=!1);var e=this.state,a=e.instance,r=e.playerState;a&&(t&&r===PlayerState.Playing?a.goToAndPlay(A,!0):(a.goToAndStop(A,!0),this.triggerEvent(PlayerEvent.Pause),this.setState({playerState:PlayerState.Paused})))},t.prototype.setLoop=function(A){var t=this.state.instance;t&&(t.loop=A,this.setState({instance:t}))},t.prototype.triggerEvent=function(A){var t=this.props.onEvent;t&&t(A)},t.defaultProps={loop:!1},t}(reactExports.Component);function styleInject(A,t){void 0===t&&(t={});var e=t.insertAt;if(\"undefined\"!=typeof document){var a=document.head||document.getElementsByTagName(\"head\")[0],r=document.createElement(\"style\");r.type=\"text/css\",\"top\"===e&&a.firstChild?a.insertBefore(r,a.firstChild):a.appendChild(r),r.styleSheet?r.styleSheet.cssText=A:r.appendChild(document.createTextNode(A))}}var css_248z=\".lf-progress {\\n  -webkit-appearance: none;\\n  -moz-apperance: none;\\n  width: 100%;\\n  /* margin: 0 10px; */\\n  height: 4px;\\n  border-radius: 3px;\\n  cursor: pointer;\\n}\\n.lf-progress:focus {\\n  outline: none;\\n  border: none;\\n}\\n.lf-progress::-moz-range-track {\\n  cursor: pointer;\\n  background: none;\\n  border: none;\\n  outline: none;\\n}\\n.lf-progress::-webkit-slider-thumb {\\n  -webkit-appearance: none !important;\\n  height: 13px;\\n  width: 13px;\\n  border: 0;\\n  border-radius: 50%;\\n  background: #0fccce;\\n  cursor: pointer;\\n}\\n.lf-progress::-moz-range-thumb {\\n  -moz-appearance: none !important;\\n  height: 13px;\\n  width: 13px;\\n  border: 0;\\n  border-radius: 50%;\\n  background: #0fccce;\\n  cursor: pointer;\\n}\\n.lf-progress::-ms-track {\\n  width: 100%;\\n  height: 3px;\\n  cursor: pointer;\\n  background: transparent;\\n  border-color: transparent;\\n  color: transparent;\\n}\\n.lf-progress::-ms-fill-lower {\\n  background: #ccc;\\n  border-radius: 3px;\\n}\\n.lf-progress::-ms-fill-upper {\\n  background: #ccc;\\n  border-radius: 3px;\\n}\\n.lf-progress::-ms-thumb {\\n  border: 0;\\n  height: 15px;\\n  width: 15px;\\n  border-radius: 50%;\\n  background: #0fccce;\\n  cursor: pointer;\\n}\\n.lf-progress:focus::-ms-fill-lower {\\n  background: #ccc;\\n}\\n.lf-progress:focus::-ms-fill-upper {\\n  background: #ccc;\\n}\\n.lf-player-container :focus {\\n  outline: 0;\\n}\\n.lf-popover {\\n  position: relative;\\n}\\n\\n.lf-popover-content {\\n  display: inline-block;\\n  position: absolute;\\n  opacity: 1;\\n  visibility: visible;\\n  transform: translate(0, -10px);\\n  box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26);\\n  transition: all 0.3s cubic-bezier(0.75, -0.02, 0.2, 0.97);\\n}\\n\\n.lf-popover-content.hidden {\\n  opacity: 0;\\n  visibility: hidden;\\n  transform: translate(0, 0px);\\n}\\n\\n.lf-player-btn-container {\\n  display: flex;\\n  align-items: center;\\n}\\n.lf-player-btn {\\n  cursor: pointer;\\n  fill: #999;\\n  width: 14px;\\n}\\n\\n.lf-player-btn.active {\\n  fill: #555;\\n}\\n\\n.lf-popover {\\n  position: relative;\\n}\\n\\n.lf-popover-content {\\n  display: inline-block;\\n  position: absolute;\\n  background-color: #ffffff;\\n  opacity: 1;\\n\\n  transform: translate(0, -10px);\\n  box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26);\\n  transition: all 0.3s cubic-bezier(0.75, -0.02, 0.2, 0.97);\\n  padding: 10px;\\n}\\n\\n.lf-popover-content.hidden {\\n  opacity: 0;\\n  visibility: hidden;\\n  transform: translate(0, 0px);\\n}\\n\\n.lf-arrow {\\n  position: absolute;\\n  z-index: -1;\\n  content: '';\\n  bottom: -9px;\\n  border-style: solid;\\n  border-width: 10px 10px 0px 10px;\\n}\\n\\n.lf-left-align,\\n.lf-left-align .lfarrow {\\n  left: 0;\\n  right: unset;\\n}\\n\\n.lf-right-align,\\n.lf-right-align .lf-arrow {\\n  right: 0;\\n  left: unset;\\n}\\n\\n.lf-text-input {\\n  border: 1px #ccc solid;\\n  border-radius: 5px;\\n  padding: 3px;\\n  width: 60px;\\n  margin: 0;\\n}\\n\\n.lf-color-picker {\\n  display: flex;\\n  flex-direction: row;\\n  justify-content: space-between;\\n  height: 90px;\\n}\\n\\n.lf-color-selectors {\\n  display: flex;\\n  flex-direction: column;\\n  justify-content: space-between;\\n}\\n\\n.lf-color-component {\\n  display: flex;\\n  flex-direction: row;\\n  font-size: 12px;\\n  align-items: center;\\n  justify-content: center;\\n}\\n\\n.lf-color-component strong {\\n  width: 40px;\\n}\\n\\n.lf-color-component input[type='range'] {\\n  margin: 0 0 0 10px;\\n}\\n\\n.lf-color-component input[type='number'] {\\n  width: 50px;\\n  margin: 0 0 0 10px;\\n}\\n\\n.lf-color-preview {\\n  font-size: 12px;\\n  display: flex;\\n  flex-direction: column;\\n  align-items: center;\\n  justify-content: space-between;\\n  padding-left: 5px;\\n}\\n\\n.lf-preview {\\n  height: 60px;\\n  width: 60px;\\n}\\n\\n.lf-popover-snapshot {\\n  width: 150px;\\n}\\n.lf-popover-snapshot h5 {\\n  margin: 5px 0 10px 0;\\n  font-size: 0.75rem;\\n}\\n.lf-popover-snapshot a {\\n  display: block;\\n  text-decoration: none;\\n}\\n.lf-popover-snapshot a:before {\\n  content: '⥼';\\n  margin-right: 5px;\\n}\\n.lf-popover-snapshot .lf-note {\\n  display: block;\\n  margin-top: 10px;\\n  color: #999;\\n}\\n.lf-player-controls > div {\\n  margin-right: 5px;\\n  margin-left: 5px;\\n}\\n.lf-player-controls > div:first-child {\\n  margin-left: 0px;\\n}\\n.lf-player-controls > div:last-child {\\n  margin-right: 0px;\\n}\\n\";styleInject(css_248z);var ColorPicker=function(A){function t(){var t=null!==A&&A.apply(this,arguments)||this;return t.state={red:0,green:0,blue:0,rgba:null,hex:\"#000000\",colorComponents:[]},t.handleChange=function(A,e){if(\"r\"===A){var a=\"#\"+(256|e).toString(16).slice(1)+(256|t.state.green).toString(16).slice(1)+(256|t.state.blue).toString(16).slice(1);t.setState({hex:a})}else\"g\"===A?(a=\"#\"+(256|t.state.red).toString(16).slice(1)+(256|e).toString(16).slice(1)+(256|t.state.blue).toString(16).slice(1),t.setState({hex:a})):\"b\"===A&&(a=\"#\"+(256|t.state.red).toString(16).slice(1)+(256|t.state.green).toString(16).slice(1)+(256|e).toString(16).slice(1),t.setState({hex:a}))},t.parseColor=function(A){var e;if(\"string\"==typeof A){if(\"#\"===A[0]){var a=4===A.length?[A.slice(1,2),A.slice(2,3),A.slice(3,4)].map(function(A){return parseInt(\"\"+A+A,16)}):[A.slice(1,3),A.slice(3,5),A.slice(5,7)].map(function(A){return parseInt(A,16)});t.setState({colorComponents:a})}else A.startsWith(\"rgb\")&&void 0!==(a=null===(e=A.match(/\\d+/g))||void 0===e?void 0:e.map(function(A){return parseInt(A)}))&&t.setState({colorComponents:a});t.state.colorComponents.length&&(t.setState({red:t.state.colorComponents[0]}),t.setState({green:t.state.colorComponents[1]}),t.setState({blue:t.state.colorComponents[2]}))}},t}return __extends(t,A),t.prototype.componentDidUpdate=function(A,t){return this.props.colorChangedEvent&&this.state.hex!==t.hex&&this.props.colorChangedEvent(this.state.hex),!0},t.prototype.render=function(){var A=this;return reactExports.createElement(\"div\",{className:\"lf-color-picker\"},reactExports.createElement(\"div\",{className:\"lf-color-selectors\"},reactExports.createElement(\"div\",{className:\"lf-color-component\"},reactExports.createElement(\"strong\",null,\"Red\"),reactExports.createElement(\"input\",{type:\"range\",min:\"0\",max:\"255\",value:this.state.red,onChange:function(t){A.setState({red:t.target.value}),A.handleChange(\"r\",t.target.value)}}),reactExports.createElement(\"input\",{className:\"lf-text-input\",type:\"number\",min:\"0\",max:\"255\",value:this.state.red,onChange:function(t){A.setState({red:t.target.value}),A.handleChange(\"r\",t.target.value)}})),reactExports.createElement(\"div\",{className:\"lf-color-component\"},reactExports.createElement(\"strong\",null,\"Green\"),reactExports.createElement(\"input\",{type:\"range\",min:\"0\",max:\"255\",value:this.state.green,onChange:function(t){A.setState({green:t.target.value}),A.handleChange(\"g\",t.target.value)}}),reactExports.createElement(\"input\",{className:\"lf-text-input\",type:\"number\",min:\"0\",max:\"255\",value:this.state.green,onChange:function(t){A.setState({green:t.target.value}),A.handleChange(\"g\",t.target.value)}})),reactExports.createElement(\"div\",{className:\"lf-color-component\"},reactExports.createElement(\"strong\",null,\"Blue\"),reactExports.createElement(\"input\",{type:\"range\",min:\"0\",max:\"255\",value:this.state.blue,onChange:function(t){A.setState({blue:t.target.value}),A.handleChange(\"b\",t.target.value)}}),reactExports.createElement(\"input\",{className:\"lf-text-input\",type:\"number\",min:\"0\",max:\"255\",value:this.state.blue,onChange:function(t){A.setState({blue:t.target.value}),A.handleChange(\"b\",t.target.value)}}))),reactExports.createElement(\"div\",{className:\"lf-color-preview\"},reactExports.createElement(\"div\",{className:\"lf-preview\",style:{background:\"rgb(\"+this.state.red+\", \"+this.state.green+\", \"+this.state.blue+\")\"}}),reactExports.createElement(\"div\",null,reactExports.createElement(\"input\",{className:\"lf-text-input\",type:\"text\",value:this.state.hex,onChange:function(t){A.setState({hex:t.target.value}),A.parseColor(t.target.value)}}))))},t}(reactExports.Component),Popover=function(A){var t=A.children,e=A.icon,a=reactExports.useState(null),r=a[0],n=a[1],l=reactExports.useState(null),i=l[0],o=l[1],s=reactExports.useState(null),p=s[0],c=s[1],u=reactExports.useState(!1),d=u[0],h=u[1];return reactExports.useEffect(function(){if(r&&i){var A=r.getBoundingClientRect(),t=i.getBoundingClientRect(),e=A.left+t.width>window.innerWidth?-1:0;c(e)}},[p,i,r]),reactExports.createElement(\"div\",{className:\"lf-popover\",onMouseOver:function(){h(!0)},onMouseLeave:function(){h(!1)},ref:function(A){n(A)}},reactExports.createElement(\"div\",{className:\" lf-player-btn\"},e),reactExports.createElement(\"div\",{className:\"lf-popover-content\",ref:function(A){o(A)},style:{bottom:\"22px\",right:\"0px\",zIndex:2,visibility:d?\"visible\":\"hidden\"}},t))},Seeker=function(A){function t(t){var e=A.call(this,t)||this;return e.inputRef=reactExports.createRef(),e.handleChange=function(){return function(A){var t=A.target.value,a=Math.floor(t/100*e.props.max);e.props.onChange(a)}},e.state={value:0},e}return __extends(t,A),t.prototype.render=function(){var A=this.props.value/this.props.max*100,t={backgroundImage:\"-webkit-gradient(linear, left top, right top, color-stop(\"+A+\"%, rgba(15, 204, 206, 0.4)), color-stop(\"+A+\"%, #DAE1E7))\"},e={position:\"absolute\",left:0,marginTop:\"8px\",width:\"20px\",display:\"block\",border:\"0px\",backgroundColor:this.props.darkTheme?\"#505050\":\"rgb(218, 225, 231)\",color:this.props.darkTheme?\"#B9B9B9\":\"#555\",padding:\"2px\",textAlign:\"center\",borderRadius:\"3px\",fontSize:\"8px\",fontWeight:\"bold\"},a={position:\"absolute\",right:0,marginTop:\"8px\",width:\"20px\",display:\"block\",border:\"0px\",backgroundColor:this.props.darkTheme?\"#505050\":\"rgb(218, 225, 231)\",color:this.props.darkTheme?\"#B9B9B9\":\"#555\",padding:\"2px\",textAlign:\"center\",borderRadius:\"3px\",fontSize:\"8px\",fontWeight:\"bold\"};return reactExports.createElement(\"div\",{style:{display:\"flex\",flexDirection:\"column\",alignItems:\"center\",width:\"100%\",marginRight:\"5px\",marginLeft:\"5px\",position:\"relative\"}},reactExports.createElement(\"input\",{ref:this.inputRef,id:\"track\",className:\"lf-progress\",name:\"progress\",\"aria-label\":\"progress\",type:\"range\",min:\"0\",max:\"100\",step:\"0.1\",value:A,onInput:this.handleChange(),onChange:this.handleChange(),style:t}),this.props.showLabels&&reactExports.createElement(\"div\",{style:{display:\"flex\",justifyContent:\"space-between\"}},reactExports.createElement(\"div\",{style:e},this.props.min),reactExports.createElement(\"div\",{style:a},this.props.max)))},t}(reactExports.Component),ControlButtonStyle={display:\"inline-flex\",cursor:\"pointer\"};!function(A){function t(t){var e=A.call(this,t)||this;return e.state={activeFrame:0,mouseDown:!1},e}__extends(t,A),t.prototype.render=function(){var A=this,t=this.props,e=t.instance,a=t.playerState,r=t.seeker,n=t.setLoop,l=t.setSeeker,i=t.play,o=t.pause,s=t.stop,p=t.visible,c=t.buttons;if(!e)return null;if(!p)return null;var u=!c||c.includes(\"play\"),d=!c||c.includes(\"stop\"),h=!c||c.includes(\"repeat\"),S=!c||c.includes(\"frame\"),f=!c||c.includes(\"background\"),m=!c||c.includes(\"snapshot\"),L={width:14,height:14,viewBox:\"0 0 24 24\"},W=Math.round(e.currentFrame);return reactExports.createElement(\"div\",{className:\"lf-player-controls\",style:{display:\"flex\",justifyContent:\"space-between\",height:\"60px\",alignItems:\"center\",backgroundColor:this.props.transparentTheme?\"transparent\":this.props.darkTheme?\"#3C3C3C\":\"#ffffff\",paddingLeft:\"10px\",paddingRight:\"10px\"}},u&&reactExports.createElement(\"div\",{role:\"button\",\"aria-label\":a===PlayerState.Playing?PlayerEvent.Pause:PlayerEvent.Play,tabIndex:0,onClick:function(){a===PlayerState.Playing?\"function\"==typeof o&&o():\"function\"==typeof i&&i()},onKeyDown:function(){a===PlayerState.Playing?\"function\"==typeof o&&o():\"function\"==typeof i&&i()},className:\"lf-player-btn\",style:ControlButtonStyle},a===PlayerState.Playing?reactExports.createElement(\"svg\",__assign({},L),reactExports.createElement(\"rect\",{height:\"22.9\",rx:\"1.9\",width:\"7.6\",x:\"14\",y:\".5\"}),reactExports.createElement(\"rect\",{height:\"22.9\",rx:\"1.9\",width:\"7.6\",x:\"2\",y:\".5\"})):reactExports.createElement(\"svg\",__assign({},L),reactExports.createElement(\"path\",{d:\"M2 3.4C2 1.9 3.5 1 4.8 1.8l16.5 9.6c1.2.7 1.2 2.5 0 3.2L4.8 24.2C3.5 25 2 24.1 2 22.6V3.4z\"}))),d&&reactExports.createElement(\"div\",{tabIndex:0,role:\"button\",\"aria-label\":PlayerEvent.Stop,onClick:function(){return s&&s()},onKeyDown:function(){return s&&s()},className:a===PlayerState.Stopped?\"lf-player-btn active\":\"lf-player-btn\",style:ControlButtonStyle},reactExports.createElement(\"svg\",__assign({},L),reactExports.createElement(\"path\",{d:\"M2 3.667A1.67 1.67 0 0 1 3.667 2h16.666A1.67 1.67 0 0 1 22 3.667v16.666A1.67 1.67 0 0 1 20.333\\n            22H3.667A1.67 1.67 0 0 1 2 20.333z\"}))),reactExports.createElement(Seeker,{min:0,step:1,max:e?e.totalFrames:1,value:r||0,onChange:function(t){l&&A.setState({activeFrame:t},function(){l(t,!1)})},onChangeEnd:function(t){l&&A.setState({activeFrame:t},function(){l(t,!1)})},showLabels:this.props.showLabels,darkTheme:this.props.darkTheme}),S&&reactExports.createElement(\"div\",{role:\"button\",className:\"lf-player-btn-container\"},reactExports.createElement(\"input\",{style:{outline:\"none\",border:this.props.darkTheme?\"1px #505050 solid\":\"1px #ccc solid\",borderRadius:\"3px\",width:\"40px\",textAlign:\"center\",backgroundColor:this.props.darkTheme?\"#505050\":\"#ffffff\",color:this.props.darkTheme?\"#B9B9B9\":\"#999\",fontSize:\"0.7rem\",padding:\"0\",fontFamily:\"inherit\"},type:\"text\",value:W,readOnly:!0})),h&&reactExports.createElement(\"div\",{role:\"button\",\"aria-label\":PlayerEvent.Loop,tabIndex:0,onClick:function(){e&&n&&n(!e.loop)},onKeyDown:function(){e&&n&&n(!e.loop)},className:e.loop?\"lf-player-btn active\":\"lf-player-btn\",style:ControlButtonStyle},reactExports.createElement(\"svg\",__assign({},L),reactExports.createElement(\"path\",{d:\"M12.5 16.8137h-.13v1.8939h4.9696c3.6455 0 6.6113-2.9658 6.6113-6.6116\\n            0-3.64549-2.9658-6.61131-6.6113-6.61131-.5231 0-.947.42391-.947.94696 0 .52304.4239.94696.947.94696 2.6011 0\\n            4.7174 2.11634 4.7174 4.71739 0 2.6014-2.1166 4.7177-4.7174 4.7177H12.5zM13.6025\\n            5.61469v-.13H7.48137C3.83582 5.48469.87 8.45051.87 12.096c0 3.6509 3.17269 6.6117 6.81304 6.6117.52304 0\\n            .94696-.424.94696-.947 0-.5231-.42392-.947-.94696-.947-2.60804 0-4.91907-2.1231-4.91907-4.7176 0-2.60115\\n            2.11634-4.71744 4.7174-4.71744h6.12113V5.61469z\",stroke:\"#8795A1\",strokeWidth:\".26\"}),reactExports.createElement(\"path\",{d:\"M11.1482\\n            2.20355h0l-.001-.00116c-.3412-.40061-.9405-.44558-1.33668-.0996h-.00001c-.39526.34519-.43936.94795-.09898\\n            1.34767l2.51487 3.03683-2.51894 3.06468c-.33872.40088-.29282 1.00363.10347\\n            1.34723l.08517-.0982-.08517.0982c.17853.1549.39807.2308.61647.2308.2671 0 .5328-.114.72-.3347h0l.0011-.0014\\n            3.0435-3.68655.0006-.00068c.3035-.35872.3025-.88754-.0019-1.24526l-3.0425-3.65786zM13.9453\\n            21.7965h0l.001.0011c.3413.4006.9407.4456 1.337.0996h0c.3953-.3452.4395-.9479.099-1.3477l-2.5154-3.0368\\n            2.5195-3.0647c.3388-.4008.2929-1.0036-.1035-1.3472l-.0852.0982.0852-.0982c-.1786-.1549-.3981-.2308-.6166-.2308-.2671\\n            0-.5329.114-.7202.3347h0l-.0011.0014-3.0442\\n            3.6865c-.0001.0003-.0003.0005-.0005.0007-.3036.3587-.3027.8876.0019 1.2453l3.0431 3.6579z\",fill:\"#8795A1\",stroke:\"#8795A1\",strokeWidth:\".26\"}))),f&&reactExports.createElement(Popover,{icon:reactExports.createElement(\"svg\",__assign({},L),reactExports.createElement(\"path\",{d:\"M12 3.1L6.1 8.6a7.6 7.6 0 00-2.2 4 7.2 7.2 0 00.4 4.4 7.9 7.9 0 003 3.5 8.7 8.7 0 004.7 1.3c1.6 0\\n            3.2-.5 4.6-1.3s2.4-2 3-3.5a7.2 7.2 0 00.5-4.5 7.6 7.6 0 00-2.2-4L12 3.2zM12 0l7.5 7a9.8 9.8 0 013 5.1\\n            9.3 9.3 0 01-.6 5.8c-.9 1.8-2.2 3.3-4 4.4A11.2 11.2 0 0112 24a11.2 11.2 0\\n            01-6-1.7c-1.7-1-3-2.6-3.9-4.4a9.3 9.3 0 01-.6-5.8c.4-2 1.5-3.7 3-5L12 0zM6 14h12c0 1.5-.7 3-1.8 4s-2.6\\n            1.6-4.2 1.6S9 19 7.8 18s-1.7-2.5-1.7-4z\"}))},reactExports.createElement(\"div\",{slot:\"content\",className:\"lf-popover popover-background\"},reactExports.createElement(ColorPicker,{colorChangedEvent:this.props.colorChangedEvent}))),m&&reactExports.createElement(Popover,{icon:reactExports.createElement(\"svg\",__assign({},L),reactExports.createElement(\"path\",{clipRule:\"evenodd\",d:\"M0 3.01A2.983 2.983 0 012.983.027H16.99a2.983 2.983 0 012.983 2.983v14.008a2.982 2.982 0 01-2.983\\n              2.983H2.983A2.983 2.983 0 010 17.018zm2.983-.941a.941.941 0 00-.942.94v14.01c0\\n              .52.422.94.942.94H16.99a.94.94 0 00.941-.94V3.008a.941.941 0 00-.94-.94H2.981z\",fillRule:\"evenodd\"}),reactExports.createElement(\"path\",{d:\"M12.229 7.945l-2.07 4.598-2.586-2.605-2.414 2.758v2.146h9.656V11.93z\"}),reactExports.createElement(\"circle\",{cx:\"7.444\",cy:\"6.513\",r:\"2.032\"}),reactExports.createElement(\"path\",{d:\"M9.561 23.916h11.25a2.929 2.929 0 002.926-2.927V9.954a1.06 1.06 0 10-2.122 0v11.035a.805.805 0\\n              01-.803.804H9.562a1.061 1.061 0 100 2.123z\",stroke:\"#8795a1\",strokeWidth:\".215\"}))},reactExports.createElement(\"div\",{slot:\"content\",className:\"lf-popover lf-popover-snapshot\",onWheel:function(A){l&&l(W+(A.deltaY>0?-1:1),!1)}},reactExports.createElement(\"h5\",null,\"Frame \",W),reactExports.createElement(\"div\",{style:{cursor:\"pointer\",color:\"#0FCCCE\"},onClick:this.props.snapshot},\"Download SVG\"),reactExports.createElement(\"div\",{style:{cursor:\"pointer\",color:\"#0FCCCE\"},onClick:this.props.snapshot},\"Download PNG\"),reactExports.createElement(\"i\",{className:\"lf-note\"},\"Scroll with mousewheel to find exact frame\"))))}}(reactExports.Component);const v$1=\"5.6.8\",fr$1=30,ip$1=0,op$1=450,w$1=3840,h$1=2160,nm$1=\"背景\",ddd$1=0,assets$1=JSON.parse('[{\"id\":\"image_0\",\"w\":3840,\"h\":2160,\"u\":\"\",\"p\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAADwAAAAhwCAYAAACQvstyAAAACXBIWXMAAAABAAAAAQBPJcTWAAAAJHpUWHRDcmVhdG9yAAAImXNMyU9KVXBMK0ktUnBNS0tNLikGAEF6Bs5qehXFAAAgAElEQVR4nOzOsa2FMBBFwSvRCKW5VEqggxUdQAc/cUCEbEtfesFM7OuzCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/6slqYVd9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1mBrG3hzJ7mSnJNHPEmOvh+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvO1J2sKu9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1nzr87Na2FXmj9fS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAf1ZLUwq76VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trsLUNvLmTXEnOySOeJEffj9LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADe9iRtYdf6VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trvvX5WS3sKvPHa2lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwI9qSWphV32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXY2gbe3EmuJOfkEU+So+9HaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG97krawa32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXf+vysFnaV+eO1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgR7UktbCrvtXS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSGmxtA2/uJFeSc/KIJ8nR96O0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAtz1JW9i1vtXS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSmm99flYLu8r88VpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPCjWpJa2FXfamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkNtraBN3eSK8k5ecST5Oj7UVpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDbnqQt7FrfamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWnNtz4/q4VdZf54LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+FEtSS3sqm+1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAZb28CbO8mV5Jw84kly9P0oLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4G1P0hZ2rW+1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tOZbn5/Vwq4yf7yWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8qJakFnbVt1paWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpag61t4M2d5EpyTh7xJDn6fpSWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwtidpC7vWt1paWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpa863Pz2phV5k/XktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH5US1ILu+pbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa3B1jbw5k5yJTknj3iSHH0/SktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHjbk7SFXetbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa351udntbCrzB+vpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP6olqYVd9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1mBrG3hzJ7mSnJNHPEmOvh+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvO1J2sKu9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1nzr87Na2FXmj9fS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAf1ZLUwq76VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trsLUNvLmTXEnOySOeJEffj9LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADe9iRtYdf6VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trvvX5WS3sKvPHa2lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAfu3NsAyEMBFF0JBq50lwqJdDBig6gg0scECHbEcF7scd/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+qiWphV31rZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbWYGsbeHMlOZMck0fcSfa+H6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8/ZK0hV3rWy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2t+dbrZ7Wwq8wfr6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/VktTCrvpWS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2uwtQ28uZKcSY7JI+4ke9+P0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ5+SdrCrvWtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltZ86/WzWthV5o/X0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAj2pJamFXfaulpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlNdjaBt5cSc4kx+QRd5K970dpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT78kbWHX+lZLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa771+lkt7Crzx2tpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAON5B6cAACAASURBVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBHtSS1sKu+1dLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIabG0Db64kZ5Jj8og7yd73o7S0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICnX5K2sGt9q6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaU133r9rBZ2lfnjtbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4KNaklrYVd9qaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQ22toE3V5IzyTF5xJ1k7/tRWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwNMvSVvYtb7V0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0ppvvX5WC7vK/PFaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwUS1JLeyqb7W0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0BlvbwJsryZnkmDziTrL3/SgtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg6ZekLexa32ppaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpzbdeP6uFXWX+eC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPiolqQWdtW3WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlqDrW3gzZXkTHJMHnEn2ft+lJaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpYWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPD0S9IWdq1vtbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLTmW6+f1cKuMn+8lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlhYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfFRLUgu76lstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trcHWNvDmSnImOSaPuJPsfT9KS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAePolaQu71rdaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWvOt189qYVeZP15LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+qiWphV31rZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbWYGsbeHMlOZMck0fcSfa+H6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8/ZK0hV3rWy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2t+dbrZ7Wwq8wfr6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/VktTCrvpWS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2uwtQ28uZKcSY7JI+4ke9+P0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ5+SdrCrvWtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltZ86/WzWthV5o/X0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODP7hzbQAgDQRQdiUauNJdKCXSwogPo4BIHRMh2RPBe7PFfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD6qJamFXfWtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltZgaxt4cyU5kxyTR9xJ9r4fpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADz9krSFXetbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa351utntbCrzB+vpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH9WS1MKu+lZLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa7C1Dby5kpxJjskj7iR734/S0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnn5J2sKu9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1nzr9bNa2FXmj9fS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICPaklqYVd9q6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaU12NoG3lxJziTH5BF3kr3vR2lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPvyRtYdf6VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trvvX6WS3sKvPHa2lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwEe1JLWwq77V0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0hpsbQNvriRnkmPyiDvJ3vejtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKdfkrawa32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXfev2sFnaV+eO1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgo1qSWthV32ppaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpDba2gTdXkjPJMXnEnWTv+1FaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADA0y9JW9i1vtXS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSmm+9flYLu8r88VpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPBRLUkt7KpvtbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQGW9vAmyvJmeSYPOJOsvf9KC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODpl6Qt7FrfamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWnNt14/q4VdZf54LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+KiWpBZ21bdaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWoOtbeDNYQSAHwAAIABJREFUleRMckwecSfZ+36UlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlhYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8PRL0hZ2rW+1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tOZbr5/Vwq4yf7yWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8VEtSC7vqWy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2twdY28OZKciY5Jo+4k+x9P0pLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4+iVpC7vWt1paWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpa863Xz2phV5k/XktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD6qJamFXfWtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltZgaxt4cyU5kxyTR9xJ9r4fpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADz9krSFXetbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa351utntbCrzB+vpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH9WS1MKu+lZLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa7C1Dby5kpxJjskj7iR734/S0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnn5J2sKu9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1nzr9bNa2FXmj9fS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAvizO8c2EMJAEEVHohFKc6mUQAcrOoAOLnFAdLIdEbwXe/wXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPqolqYVd9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1mBrG3hzJ7mSnJNHPEmOvh+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvO1J2sKu9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1nzr72e1sKvMH6+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf1ZLUwq76VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trsLUNvLmTXEnOySOeJEffj9LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADe9iRtYdf6VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trvvX3s1rYVeaP19LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgI9qSWphV32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXY2gbe3EmuJOfkEU+So+9HaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG97krawa32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXf+vtZLewq88draWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAR7UktbCrvtXS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSGmxtA2/uJFeSc/KIJ8nR96O0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAtz1JW9i1vtXS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSmm/9/awWdpX547W0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOCjWpJa2FXfamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkNtraBN3eSK8k5ecST5Oj7UVpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDbnqQt7FrfamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWnNt/5+Vgu7yvzxWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8FEtSS3sqm+1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAZb28CbO8mV5Jw84kly9P0oLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4G1P0hZ2rW+1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tOZbfz+rhV1l/ngtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4qJakFnbVt1paWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpag61t4M2d5EpyTh7xJDn6fpSWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwtidpC7vWt1paWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpa862/n9XCrjJ/vJaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpYWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHxUS1ILu+pbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa3B1jbw5k5yJTknj3iSHH0/SktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHjbk7SFXetbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa351t/PamFXmT9eS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPqolqYVd9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1mBrG3hzJ7mSnJNHPEmOvh+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvO1J2sKu9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1nzr72e1sKvMH6+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf1ZLUwq76VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trsLUNvLmTXEnOySOeJEffj9LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUYV1VAAAgAElEQVQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADe9iRtYdf6VktLS0tLS0tLS0tL68fuHNtACANBFB2JRq40l0oJdLCiA+jgEgdEyHZE8F7s8V8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trfnW62e1sKvMH6+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf1ZLUwq76VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trsLUNvLmSnEmOySPuJHvfj9LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACefknawq71rZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbWfOv1s1rYVeaP19LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgI9qSWphV32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXY2gbeXEnOJMfkEXeSve9HaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE+/JG1h1/pWS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2u+9fpZLewq88draWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAR7UktbCrvtXS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSGmxtA2+uJGeSY/KIO8ne96O0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAp1+StrBrfaulpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlNd96/awWdpX547W0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOCjWpJa2FXfamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkNtraBN1eSM8kxecSdZO/7UVpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDTL0lb2LW+1dLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKab71+Vgu7yvzxWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8FEtSS3sqm+1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAZb28CbK8mZ5Jg84k6y9/0oLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4OmXpC3sWt9qaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpac23Xj+rhV1l/ngtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4qJakFnbVt1paWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpag61t4M2V5ExyTB5xJ9n7fpSWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADw9EvSFnatb7W0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS05luvn9XCrjJ/vJaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpYWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHxUS1ILu+pbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa3B1jbw5kpyJjkmj7iT7H0/SktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHj6JWkLu9a3WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlrzrdfPamFXmT9eS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPqolqYVd9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1mBrG3hzJTmTHJNH3En2vh+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPP2StIVd61stLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trfnW62e1sKvMH6+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf1ZLUwq76VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trsLUNvLmSnEmOySPuJHvfj9LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACefknawq71rZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbWfOv1s1rYVeaP19LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgI9qSWphV32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXY2gbeXEnOJMfkEXeSve9HaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH9259gGQhgIouhINHKluVRKoIMVHUAHlzggQrYjgvdij/8CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8/ZK0hV3rWy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2t+dbrZ7Wwq8wfr6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/VktTCrvpWS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2uwtQ28uZKcSY7JI+4ke9+P0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ5+SdrCrvWtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltZ86/WzWthV5o/X0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAj2pJamFXfaulpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlNdjaBt5cSc4kx+QRd5K970dpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT78kbWHX+lZLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa771+lkt7Crzx2tpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBHtSS1sKu+1dLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIabG0Db64kZ5Jj8og7yd73o7S0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKF2WzYAACAASURBVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICnX5K2sGt9q6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaU133r9rBZ2lfnjtbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4KNaklrYVd9qaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQ22toE3V5IzyTF5xJ1k7/tRWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwNMvSVvYtb7V0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0ppvvX5WC7vK/PFaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwUS1JLeyqb7W0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0BlvbwJsryZnkmDziTrL3/SgtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg6ZekLexa32ppaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpzbdeP6uFXWX+eC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPiolqQWdtW3WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlqDrW3gzZXkTHJMHnEn2ft+lJaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpYWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPD0S9IWdq1vtbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLTmW6+f1cKuMn+8lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlhYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfFRLUgu76lstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trcHWNvDmSnImOSaPuJPsfT9KS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAePolaQu71rdaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWvOt189qYVeZP15LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+qiWphV31rZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbWYGsbeHMlOZMck0fcSfa+H6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8/ZK0hV3rWy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2t+dbrZ7Wwq8wfr6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/VktTCrvpWS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2uwtQ28uZKcSY7JI+4ke9+P0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ5+SdrCrvWtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltZ86/WzWthV5o/X0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAj2pJamFXfaulpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlNdjaBt5cSc4kx+QRd5K970dpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwJ/dObaBEAaCKDoSjVxpLpUS6GBFB9DBJQ6IkO2I4L3Y478AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADz9krSFXetbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa351utntbCrzB+vpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH9WS1MKu+lZLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa7C1Dby5kpxJjskj7iR734/S0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnn5J2sKu9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1nzr9bNa2FXmj9fS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICPaklqYVd9q6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaU12NoG3lxJziTH5BF3kr3vR2lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPvyRtYdf6VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trvvX6WS3sKvPHa2lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwEe1JLWwq77V0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0hpsbQNvriRnkmPyiDvJ3vejtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKdfkrawa32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXfev2sFnaV+eO1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgo1qSWthV32ppaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpDba2gTdXkjPJMXnEnWTv+1FaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADA0y9JW9i1vtXS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSmm+9flYLu8r88VpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPBRLUkt7KpvtbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQGW9vAmyvJmeSYPOJOsvf9KC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODpl6Qt7FrfamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWnNt14/q4VdZf54LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+KiWpBZ21bdaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWoOtbeDNleRMckwecSfZ+36UlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlhYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8PRL0hZ2rW+1tLS0tLS0tLS0tLS0tLS0tLS0tLS0D9swwgAAIABJREFUtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tOZbr5/Vwq4yf7yWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8VEtSC7vqWy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2twdY28OZKciY5Jo+4k+x9P0pLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4+iVpC7vWt1paWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpa863Xz2phV5k/XktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD6qJamFXfWtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltZgaxt4cyU5kxyTR9xJ9r4fpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADz9krSFXetbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa351utntbCrzB+vpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH9WS1MKu+lZLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa7C1Dby5kpxJjskj7iR734/S0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnn5J2sKu9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1nzr9bNa2FXmj9fS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICPaklqYVd9q6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaU12NoG3lxJziTH5BF3kr3vR2lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8Gd3jm0ghIEgio5EI1eaS6UEOljRAXRwiQMiZDsieC/2+C8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPP2StIVd61stLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trfnW62e1sKvMH6+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf1ZLUwq76VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trsLUNvLmSnEmOySPuJHvfj9LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACefknawq71rZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbWfOv1s1rYVeaP19LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgI9qSWphV32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXY2gbeXEnOJMfkEXeSve9HaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE+/JG1h1/pWS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2u+9fpZLewq88draWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAR7UktbCrvtXS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSGmxtA2+uJGeSY/KIO8ne96O0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAp1+StrBrfaulpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlNd96/awWdpX547W0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOCjWpJa2FXfamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkNtraBN1eSM8kxecSdZO/7UVpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDTL0lb2LW+1dLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKab71+Vgu7yvzxWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8FEtSS3sqm+1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAZb28CbK8mZ5Jg84k6y9/0oLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4OmXpC3sWt9qaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpac23Xj+rhV1l/ngtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4qJakFnbVt1paWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpag61t4M2V5ExyTB5xJ9n7fpSWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADw9EvSFnatb7W0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS05luvn9XCrjJ/vJaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpYWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHxUS1ILu+pbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa3B1jbw5kpyJjkmj7iT7H0/SktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHj6JWkLu9a3WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlrzrdfPamFXmT9eS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPqolqYVd9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1mBrG3hzJTmTHJNH3En2vh+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPP2StIVd61stLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trfnW62e1sKvMH6+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf1ZLUwq76VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trsLUNvLmSnEmOySPuJHvfj9LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACefknawq71rZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbWfOv1s1rYVeaP19LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIol5+rAAAC6UlEQVTS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgI9qSWphV32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXY2gbeXEnOJMfkEXeSve9HaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/NudQyMAQBgIgl8KpdF5WsEgUMwketd/LgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBrJdmD3b5bLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa1+63usBrtK/3ktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhg6fp/1fNSHxpgAAAABJRU5ErkJggg==\",\"e\":1}]'),layers$1=JSON.parse('[{\"ddd\":0,\"ind\":1,\"ty\":2,\"nm\":\"7\",\"td\":1,\"refId\":\"image_0\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":2,\"ty\":4,\"nm\":\"Union 2\",\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"t\":0,\"s\":[30]},{\"t\":449,\"s\":[390]}],\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":-1,\"s\":[2754.631,718.632,0],\"to\":[-21.333,27.333,0],\"ti\":[63.333,34,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":0,\"s\":[2626.631,882.632,0],\"to\":[-63.333,-34,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":143,\"s\":[2374.631,514.632,0],\"to\":[0,0,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":262,\"s\":[1938.631,730.632,0],\"to\":[0,0,0],\"ti\":[-114.667,-25.333,0]},{\"t\":449,\"s\":[2626.631,882.632,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[50,50,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"d\":1,\"ty\":\"el\",\"s\":{\"a\":0,\"k\":[64.973,64.973],\"ix\":2},\"p\":{\"a\":0,\"k\":[-11.891,-44.377],\"ix\":3},\"nm\":\"椭圆路径 1\",\"mn\":\"ADBE Vector Shape - Ellipse\",\"hd\":false},{\"d\":1,\"ty\":\"el\",\"s\":{\"a\":0,\"k\":[64.973,64.973],\"ix\":2},\"p\":{\"a\":0,\"k\":[-44.377,11.891],\"ix\":3},\"nm\":\"椭圆路径 2\",\"mn\":\"ADBE Vector Shape - Ellipse\",\"hd\":false},{\"d\":1,\"ty\":\"el\",\"s\":{\"a\":0,\"k\":[64.973,64.973],\"ix\":2},\"p\":{\"a\":0,\"k\":[44.377,-11.891],\"ix\":3},\"nm\":\"椭圆路径 3\",\"mn\":\"ADBE Vector Shape - Ellipse\",\"hd\":false},{\"d\":1,\"ty\":\"el\",\"s\":{\"a\":0,\"k\":[64.973,64.973],\"ix\":2},\"p\":{\"a\":0,\"k\":[11.891,44.377],\"ix\":3},\"nm\":\"椭圆路径 4\",\"mn\":\"ADBE Vector Shape - Ellipse\",\"hd\":false},{\"ty\":\"rc\",\"d\":1,\"s\":{\"a\":0,\"k\":[64.973,64.973],\"ix\":2},\"p\":{\"a\":0,\"k\":[-0.253,-0.944],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":4},\"nm\":\"矩形路径 1\",\"mn\":\"ADBE Vector Shape - Rect\",\"hd\":false},{\"ty\":\"mm\",\"mm\":1,\"nm\":\"合并路径 1\",\"mn\":\"ADBE Vector Filter - Merge\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.772548959769,0.772548959769,0.772548959769,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[300,300],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"Union\",\"np\":7,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":3,\"ty\":2,\"nm\":\"6\",\"td\":1,\"refId\":\"image_0\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":4,\"ty\":4,\"nm\":\"Ellipse 6\",\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":0,\"s\":[2776,1502.5,0],\"to\":[0,0,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":87,\"s\":[2548,842.5,0],\"to\":[0,0,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":271,\"s\":[1896,1638.5,0],\"to\":[0,0,0],\"ti\":[-146.667,22.667,0]},{\"t\":449,\"s\":[2776,1502.5,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[33.333,33.333,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"d\":1,\"ty\":\"el\",\"s\":{\"a\":0,\"k\":[240,240],\"ix\":2},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":3},\"nm\":\"椭圆路径 1\",\"mn\":\"ADBE Vector Shape - Ellipse\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.772548959769,0.772548959769,0.772548959769,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":60,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"描边 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[300,300],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"Ellipse 6\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":5,\"ty\":2,\"nm\":\"5\",\"td\":1,\"refId\":\"image_0\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":6,\"ty\":4,\"nm\":\"Rectangle 40214\",\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"t\":0,\"s\":[0]},{\"t\":449,\"s\":[360]}],\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":0,\"s\":[1633.662,1134.953,0],\"to\":[-38.667,-67.333,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":127,\"s\":[1401.662,730.953,0],\"to\":[0,0,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":297,\"s\":[937.662,1070.953,0],\"to\":[0,0,0],\"ti\":[-116,-10.667,0]},{\"t\":449,\"s\":[1633.662,1134.953,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[50,50,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ty\":\"rc\",\"d\":1,\"s\":{\"a\":0,\"k\":[184.032,120.069],\"ix\":2},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":4},\"nm\":\"矩形路径 1\",\"mn\":\"ADBE Vector Shape - Rect\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.772548959769,0.772548959769,0.772548959769,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[300,300],\"ix\":3},\"r\":{\"a\":0,\"k\":-35.49,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"Rectangle 40214\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":7,\"ty\":2,\"nm\":\"4\",\"td\":1,\"refId\":\"image_0\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":8,\"ty\":4,\"nm\":\"Union\",\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"t\":0,\"s\":[0]},{\"t\":449,\"s\":[360]}],\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":0,\"s\":[1161.704,1439.513,0],\"to\":[87.333,-102.667,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":141,\"s\":[1685.704,823.513,0],\"to\":[0,0,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":299,\"s\":[1853.704,1471.513,0],\"to\":[0,0,0],\"ti\":[115.333,5.333,0]},{\"t\":449,\"s\":[1161.704,1439.513,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[36.756,36.756,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],\"o\":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],\"v\":[[28.8,-115.2],[-28.8,-115.2],[-28.8,-49.883],[-85.366,-82.542],[-114.166,-32.659],[-57.6,0],[-114.166,32.659],[-85.366,82.542],[-28.8,49.883],[-28.8,115.2],[28.8,115.2],[28.8,49.883],[85.366,82.541],[114.166,32.658],[57.6,0],[114.166,-32.658],[85.366,-82.541],[28.8,-49.883]],\"c\":true},\"ix\":2},\"nm\":\"路径 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.772548959769,0.772548959769,0.772548959769,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[300,300],\"ix\":3},\"r\":{\"a\":0,\"k\":23.344,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"Union\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":9,\"ty\":2,\"nm\":\"3\",\"td\":1,\"refId\":\"image_0\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":10,\"ty\":4,\"nm\":\"Vector 725\",\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"t\":0,\"s\":[0]},{\"t\":449,\"s\":[360]}],\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":-1,\"s\":[2336.819,728.49,0],\"to\":[66.667,38.667,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":151,\"s\":[2736.819,960.49,0],\"to\":[0,0,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":331,\"s\":[2148.819,1280.49,0],\"to\":[0,0,0],\"ti\":[-31.333,92,0]},{\"t\":449,\"s\":[2336.819,728.49,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[37.149,37.149,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0],[0,0]],\"o\":[[0,0],[0,0],[0,0]],\"v\":[[-108.013,-46.95],[108.013,-108.683],[46.987,108.683]],\"c\":true},\"ix\":2},\"nm\":\"路径 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.772548959769,0.772548959769,0.772548959769,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[300,300],\"ix\":3},\"r\":{\"a\":0,\"k\":-16.85,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"Vector 725\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":11,\"ty\":2,\"nm\":\"2\",\"td\":1,\"refId\":\"image_0\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":12,\"ty\":4,\"nm\":\"Ellipse 509\",\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":0,\"s\":[2575,942.5,0],\"to\":[-74,44.667,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":148,\"s\":[2131,1210.5,0],\"to\":[0,0,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":315,\"s\":[2391,1510.5,0],\"to\":[0,0,0],\"ti\":[-30.667,94.667,0]},{\"t\":449,\"s\":[2575,942.5,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[50,50,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"d\":1,\"ty\":\"el\",\"s\":{\"a\":0,\"k\":[122,122],\"ix\":2},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":3},\"nm\":\"椭圆路径 1\",\"mn\":\"ADBE Vector Shape - Ellipse\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.772548959769,0.772548959769,0.772548959769,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[300,300],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"Ellipse 509\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":13,\"ty\":2,\"nm\":\"1\",\"td\":1,\"refId\":\"image_0\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":14,\"ty\":4,\"nm\":\"Ellipse 508\",\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":0,\"s\":[1416.75,631.25,0],\"to\":[151.333,34,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":153,\"s\":[2324.75,835.25,0],\"to\":[0,0,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":309,\"s\":[1608.75,1215.25,0],\"to\":[0,0,0],\"ti\":[32,97.333,0]},{\"t\":449,\"s\":[1416.75,631.25,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[44.693,44.693,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"d\":1,\"ty\":\"el\",\"s\":{\"a\":0,\"k\":[201,201],\"ix\":2},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":3},\"nm\":\"椭圆路径 1\",\"mn\":\"ADBE Vector Shape - Ellipse\",\"hd\":false},{\"ty\":\"fl\",\"c\":{\"a\":0,\"k\":[0.772548959769,0.772548959769,0.772548959769,1],\"ix\":4},\"o\":{\"a\":0,\"k\":100,\"ix\":5},\"r\":1,\"bm\":0,\"nm\":\"填充 1\",\"mn\":\"ADBE Vector Graphic - Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[300,300],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"Ellipse 508\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":15,\"ty\":2,\"nm\":\"background\",\"refId\":\"image_0\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":12,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"ip\":0,\"op\":450,\"st\":0,\"bm\":0}]'),markers$1=[],defaultLight={v:v$1,fr:fr$1,ip:ip$1,op:op$1,w:w$1,h:h$1,nm:nm$1,ddd:ddd$1,assets:assets$1,layers:layers$1,markers:markers$1},v=\"5.6.8\",fr=30,ip=0,op=450,w=3840,h=2160,nm=\"暗色模式\",ddd=0,assets=JSON.parse('[{\"id\":\"image_0\",\"w\":3840,\"h\":2160,\"u\":\"\",\"p\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAADwAAAAhwCAYAAACQvstyAAAACXBIWXMAAAABAAAAAQBPJcTWAAAAJHpUWHRDcmVhdG9yAAAImXNMyU9KVXBMK0ktUnBNS0tNLikGAEF6Bs5qehXFAAAgAElEQVR4nOzOsa2FMBBFwSvRCKW5VEqggxUdQAc/cUCEbEtfesFM7OuzCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/6slqYVd9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1mBrG3hzJ7mSnJNHPEmOvh+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvO1J2sKu9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1nzr87Na2FXmj9fS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAf1ZLUwq76VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trsLUNvLmTXEnOySOeJEffj9LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADe9iRtYdf6VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trvvX5WS3sKvPHa2lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwI9qSWphV32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXY2gbe3EmuJOfkEU+So+9HaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG97krawa32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXf+vysFnaV+eO1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgR7UktbCrvtXS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSGmxtA2/uJFeSc/KIJ8nR96O0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAtz1JW9i1vtXS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSmm99flYLu8r88VpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPCjWpJa2FXfamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkNtraBN3eSK8k5ecST5Oj7UVpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDbnqQt7FrfamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWnNtz4/q4VdZf54LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+FEtSS3sqm+1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAZb28CbO8mV5Jw84kly9P0oLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4G1P0hZ2rW+1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tOZbn5/Vwq4yf7yWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8qJakFnbVt1paWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpag61t4M2d5EpyTh7xJDn6fpSWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwtidpC7vWt1paWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpa863Pz2phV5k/XktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH5US1ILu+pbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa3B1jbw5k5yJTknj3iSHH0/SktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHjbk7SFXetbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa351udntbCrzB+vpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP6olqYVd9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1mBrG3hzJ7mSnJNHPEmOvh+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvO1J2sKu9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1nzr87Na2FXmj9fS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAf1ZLUwq76VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trsLUNvLmTXEnOySOeJEffj9LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADe9iRtYdf6VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trvvX5WS3sKvPHa2lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAfu3NsAyEMBFF0JBq50lwqJdDBig6gg0scECHbEcF7scd/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+qiWphV31rZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbWYGsbeHMlOZMck0fcSfa+H6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8/ZK0hV3rWy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2t+dbrZ7Wwq8wfr6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/VktTCrvpWS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2uwtQ28uZKcSY7JI+4ke9+P0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ5+SdrCrvWtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltZ86/WzWthV5o/X0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAj2pJamFXfaulpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlNdjaBt5cSc4kx+QRd5K970dpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT78kbWHX+lZLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa771+lkt7Crzx2tpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAON5B6cAACAASURBVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBHtSS1sKu+1dLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIabG0Db64kZ5Jj8og7yd73o7S0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICnX5K2sGt9q6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaU133r9rBZ2lfnjtbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4KNaklrYVd9qaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQ22toE3V5IzyTF5xJ1k7/tRWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwNMvSVvYtb7V0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0ppvvX5WC7vK/PFaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwUS1JLeyqb7W0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0BlvbwJsryZnkmDziTrL3/SgtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg6ZekLexa32ppaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpzbdeP6uFXWX+eC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPiolqQWdtW3WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlqDrW3gzZXkTHJMHnEn2ft+lJaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpYWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPD0S9IWdq1vtbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLTmW6+f1cKuMn+8lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlhYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfFRLUgu76lstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trcHWNvDmSnImOSaPuJPsfT9KS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAePolaQu71rdaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWvOt189qYVeZP15LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+qiWphV31rZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbWYGsbeHMlOZMck0fcSfa+H6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8/ZK0hV3rWy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2t+dbrZ7Wwq8wfr6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/VktTCrvpWS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2uwtQ28uZKcSY7JI+4ke9+P0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ5+SdrCrvWtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltZ86/WzWthV5o/X0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODP7hzbQAgDQRQdiUauNJdKCXSwogPo4BIHRMh2RPBe7PFfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD6qJamFXfWtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltZgaxt4cyU5kxyTR9xJ9r4fpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADz9krSFXetbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa351utntbCrzB+vpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH9WS1MKu+lZLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa7C1Dby5kpxJjskj7iR734/S0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnn5J2sKu9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1nzr9bNa2FXmj9fS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICPaklqYVd9q6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaU12NoG3lxJziTH5BF3kr3vR2lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPvyRtYdf6VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trvvX6WS3sKvPHa2lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwEe1JLWwq77V0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0hpsbQNvriRnkmPyiDvJ3vejtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKdfkrawa32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXfev2sFnaV+eO1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgo1qSWthV32ppaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpDba2gTdXkjPJMXnEnWTv+1FaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADA0y9JW9i1vtXS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSmm+9flYLu8r88VpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPBRLUkt7KpvtbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQGW9vAmyvJmeSYPOJOsvf9KC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODpl6Qt7FrfamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWnNt14/q4VdZf54LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+KiWpBZ21bdaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWoOtbeDNYQSAHwAAIABJREFUleRMckwecSfZ+36UlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlhYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8PRL0hZ2rW+1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tOZbr5/Vwq4yf7yWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8VEtSC7vqWy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2twdY28OZKciY5Jo+4k+x9P0pLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4+iVpC7vWt1paWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpa863Xz2phV5k/XktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD6qJamFXfWtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltZgaxt4cyU5kxyTR9xJ9r4fpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADz9krSFXetbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa351utntbCrzB+vpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH9WS1MKu+lZLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa7C1Dby5kpxJjskj7iR734/S0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnn5J2sKu9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1nzr9bNa2FXmj9fS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAvizO8c2EMJAEEVHohFKc6mUQAcrOoAOLnFAdLIdEbwXe/wXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPqolqYVd9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1mBrG3hzJ7mSnJNHPEmOvh+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvO1J2sKu9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1nzr72e1sKvMH6+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf1ZLUwq76VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trsLUNvLmTXEnOySOeJEffj9LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADe9iRtYdf6VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trvvX3s1rYVeaP19LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgI9qSWphV32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXY2gbe3EmuJOfkEU+So+9HaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG97krawa32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXf+vtZLewq88draWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAR7UktbCrvtXS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSGmxtA2/uJFeSc/KIJ8nR96O0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAtz1JW9i1vtXS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSmm/9/awWdpX547W0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOCjWpJa2FXfamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkNtraBN3eSK8k5ecST5Oj7UVpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDbnqQt7FrfamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWnNt/5+Vgu7yvzxWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8FEtSS3sqm+1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAZb28CbO8mV5Jw84kly9P0oLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4G1P0hZ2rW+1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tOZbfz+rhV1l/ngtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4qJakFnbVt1paWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpag61t4M2d5EpyTh7xJDn6fpSWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwtidpC7vWt1paWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpa862/n9XCrjJ/vJaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpYWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHxUS1ILu+pbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa3B1jbw5k5yJTknj3iSHH0/SktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHjbk7SFXetbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa351t/PamFXmT9eS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPqolqYVd9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1mBrG3hzJ7mSnJNHPEmOvh+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvO1J2sKu9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1nzr72e1sKvMH6+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf1ZLUwq76VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trsLUNvLmTXEnOySOeJEffj9LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUYV1VAAAgAElEQVQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADe9iRtYdf6VktLS0tLS0tLS0tL68fuHNtACANBFB2JRq40l0oJdLCiA+jgEgdEyHZE8F7s8V8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trfnW62e1sKvMH6+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf1ZLUwq76VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trsLUNvLmSnEmOySPuJHvfj9LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACefknawq71rZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbWfOv1s1rYVeaP19LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgI9qSWphV32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXY2gbeXEnOJMfkEXeSve9HaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE+/JG1h1/pWS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2u+9fpZLewq88draWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAR7UktbCrvtXS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSGmxtA2+uJGeSY/KIO8ne96O0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAp1+StrBrfaulpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlNd96/awWdpX547W0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOCjWpJa2FXfamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkNtraBN1eSM8kxecSdZO/7UVpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDTL0lb2LW+1dLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKab71+Vgu7yvzxWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8FEtSS3sqm+1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAZb28CbK8mZ5Jg84k6y9/0oLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4OmXpC3sWt9qaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpac23Xj+rhV1l/ngtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4qJakFnbVt1paWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpag61t4M2V5ExyTB5xJ9n7fpSWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADw9EvSFnatb7W0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS05luvn9XCrjJ/vJaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpYWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHxUS1ILu+pbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa3B1jbw5kpyJjkmj7iT7H0/SktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHj6JWkLu9a3WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlrzrdfPamFXmT9eS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPqolqYVd9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1mBrG3hzJTmTHJNH3En2vh+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPP2StIVd61stLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trfnW62e1sKvMH6+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf1ZLUwq76VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trsLUNvLmSnEmOySPuJHvfj9LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACefknawq71rZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbWfOv1s1rYVeaP19LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgI9qSWphV32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXY2gbeXEnOJMfkEXeSve9HaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH9259gGQhgIouhINHKluVRKoIMVHUAHlzggQrYjgvdij/8CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8/ZK0hV3rWy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2t+dbrZ7Wwq8wfr6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/VktTCrvpWS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2uwtQ28uZKcSY7JI+4ke9+P0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ5+SdrCrvWtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltZ86/WzWthV5o/X0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAj2pJamFXfaulpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlNdjaBt5cSc4kx+QRd5K970dpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT78kbWHX+lZLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa771+lkt7Crzx2tpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBHtSS1sKu+1dLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIabG0Db64kZ5Jj8og7yd73o7S0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKF2WzYAACAASURBVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICnX5K2sGt9q6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaU133r9rBZ2lfnjtbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4KNaklrYVd9qaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQ22toE3V5IzyTF5xJ1k7/tRWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwNMvSVvYtb7V0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0ppvvX5WC7vK/PFaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwUS1JLeyqb7W0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0BlvbwJsryZnkmDziTrL3/SgtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg6ZekLexa32ppaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpzbdeP6uFXWX+eC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPiolqQWdtW3WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlqDrW3gzZXkTHJMHnEn2ft+lJaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpYWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPD0S9IWdq1vtbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLTmW6+f1cKuMn+8lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlhYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfFRLUgu76lstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trcHWNvDmSnImOSaPuJPsfT9KS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAePolaQu71rdaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWvOt189qYVeZP15LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+qiWphV31rZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbWYGsbeHMlOZMck0fcSfa+H6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8/ZK0hV3rWy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2t+dbrZ7Wwq8wfr6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/VktTCrvpWS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2uwtQ28uZKcSY7JI+4ke9+P0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ5+SdrCrvWtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltZ86/WzWthV5o/X0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAj2pJamFXfaulpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlNdjaBt5cSc4kx+QRd5K970dpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwJ/dObaBEAaCKDoSjVxpLpUS6GBFB9DBJQ6IkO2I4L3Y478AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADz9krSFXetbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa351utntbCrzB+vpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH9WS1MKu+lZLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa7C1Dby5kpxJjskj7iR734/S0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnn5J2sKu9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1nzr9bNa2FXmj9fS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICPaklqYVd9q6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaU12NoG3lxJziTH5BF3kr3vR2lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPvyRtYdf6VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trvvX6WS3sKvPHa2lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwEe1JLWwq77V0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0hpsbQNvriRnkmPyiDvJ3vejtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKdfkrawa32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXfev2sFnaV+eO1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgo1qSWthV32ppaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpDba2gTdXkjPJMXnEnWTv+1FaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADA0y9JW9i1vtXS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSmm+9flYLu8r88VpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPBRLUkt7KpvtbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQGW9vAmyvJmeSYPOJOsvf9KC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODpl6Qt7FrfamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWnNt14/q4VdZf54LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+KiWpBZ21bdaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWoOtbeDNleRMckwecSfZ+36UlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlhYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8PRL0hZ2rW+1tLS0tLS0tLS0tLS0tLS0tLS0tLS0D9swwgAAIABJREFUtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tOZbr5/Vwq4yf7yWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8VEtSC7vqWy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2twdY28OZKciY5Jo+4k+x9P0pLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4+iVpC7vWt1paWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpa863Xz2phV5k/XktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD6qJamFXfWtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltZgaxt4cyU5kxyTR9xJ9r4fpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADz9krSFXetbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa351utntbCrzB+vpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH9WS1MKu+lZLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa7C1Dby5kpxJjskj7iR734/S0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnn5J2sKu9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1nzr9bNa2FXmj9fS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICPaklqYVd9q6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaU12NoG3lxJziTH5BF3kr3vR2lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8Gd3jm0ghIEgio5EI1eaS6UEOljRAXRwiQMiZDsieC/2+C8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPP2StIVd61stLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trfnW62e1sKvMH6+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf1ZLUwq76VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trsLUNvLmSnEmOySPuJHvfj9LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACefknawq71rZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbWfOv1s1rYVeaP19LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgI9qSWphV32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXY2gbeXEnOJMfkEXeSve9HaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE+/JG1h1/pWS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2u+9fpZLewq88draWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAR7UktbCrvtXS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSGmxtA2+uJGeSY/KIO8ne96O0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAp1+StrBrfaulpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlNd96/awWdpX547W0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOCjWpJa2FXfamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkNtraBN1eSM8kxecSdZO/7UVpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDTL0lb2LW+1dLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKab71+Vgu7yvzxWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8FEtSS3sqm+1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAZb28CbK8mZ5Jg84k6y9/0oLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4OmXpC3sWt9qaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpac23Xj+rhV1l/ngtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4qJakFnbVt1paWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpag61t4M2V5ExyTB5xJ9n7fpSWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADw9EvSFnatb7W0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS05luvn9XCrjJ/vJaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpYWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHxUS1ILu+pbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa3B1jbw5kpyJjkmj7iT7H0/SktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHj6JWkLu9a3WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlrzrdfPamFXmT9eS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPqolqYVd9a2WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1mBrG3hzJTmTHJNH3En2vh+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPP2StIVd61stLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trfnW62e1sKvMH6+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf1ZLUwq76VktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trsLUNvLmSnEmOySPuJHvfj9LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACefknawq71rZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbWfOv1s1rYVeaP19LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIol5+rAAAC6UlEQVTS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgI9qSWphV32rpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpTXY2gbeXEnOJMfkEXeSve9HaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/NudQyMAQBgIgl8KpdF5WsEgUMwketd/LgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBrJdmD3b5bLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa1+63usBrtK/3ktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhg6fp/1fNSHxpgAAAABJRU5ErkJggg==\",\"e\":1},{\"id\":\"image_1\",\"w\":3840,\"h\":2160,\"u\":\"\",\"p\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAADwAAAAhwCAYAAACQvstyAAAACXBIWXMAAAABAAAAAQBPJcTWAAAAJHpUWHRDcmVhdG9yAAAImXNMyU9KVXBMK0ktUnBNS0tNLikGAEF6Bs5qehXFAAAgAElEQVR4nOzOsY3kMBQFwTUmEIXGVJSZMmEofc5ArkQCB4xRZfOx/98fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/UTWqubGb1dDS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSet/6vHhzrR7wdW5stbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgFt1VGNjN6pDS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2u99fTZ3NjN1eO1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgR1Wjmhu7WQ0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trfetz4s31+oBX+fGVktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALhVRzU2dqM6tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLTWW0+fzY3dXD1eS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAflQ1qrmxm9XQ0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0nrf+rx4c60e8HVubLW0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBbdVRjYzeqQ0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trvfX02dzYzdXjtbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4EdVo5obu1kNLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa33rc+LN9fqAV/nxlZLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4VUc1NnajOrS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS01ltPn82N3Vw9XktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH5UNaq5sZvV0NLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tJ63/q8eHOtHvB1bmy1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAW3VUY2M3qkNLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa7319Nnc2M3V47W0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOBHVaOaG7tZDS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2t963PizfX6gFf58ZWS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuFVHNTZ2ozq0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tNZbT5/Njd1cPV5LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+VDWqubGb1dDS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSet/6vHhzrR7wdW5stbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgFt1VGNjN6pDS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2u99fTZ3NjN1eO1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgR1Wjmhu7WQ0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trfetz4s31+oBX+fGVktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALhVRzU2dqM6tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLTWW0+fzY3dXD1eS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAflQ1qrmxm9XQ0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0nrf+rx4c60e8HVubLW0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBbdVRjYzeqQ0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trvfX02dzYzdXjtbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4EdVo5obu1kNLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa33rc+LN9fqAV/nxlZLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4VUc1NnajOrS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS01ltPn82N3Vw9XktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAfu3NsxTAIBUEwUSGURivuTJ1Qyjrxc2pB5GAm5tgPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/qprVOtitamppaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpPW9dD97cuwd8vA62WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwFc1qnmwm9XQ0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tpv/fpsHezW7vFaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwp6pZrYPdqqaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1vPW9eDNvXvAx+tgq6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHxVo5oHu1kNLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa391q/P1sFu7R6vpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf6qa1TrYrWpqaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaT1vXQ/e3LsHfLwOtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBXNap5sJvV0NLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLab/36bB3s1u7xWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8KeqWa2D3aqmlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWljQ2AwsAACAASURBVJaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltbz1vXgzb17wMfrYKulpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8VaOaB7tZDS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2t/davz9bBbu0er6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH+qmtU62K1qamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWk9b10P3ty7B3y8DrZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAVzWqebCb1dDS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2m/9+mwd7Nbu8VpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPCnqlmtg92qppaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbW89b14M29e8DH62CrpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfFWjmge7WQ0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trf3Wr8/WwW7tHq+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/qprVOtitamppaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpPW9dD97cuwd8vA62WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwFc1qnmwm9XQ0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tpv/fpsHezW7vFaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwp6pZrYPdqqaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1vPW9eDNvXvAx+tgq6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHxVo5oHu1kNLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa391q/P1sFu7R6vpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf6qa1TrYrWpqaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaT1vXQ/e3LsHfLwOtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBXNap5sJvV0NLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLab/36bB3s1u7xWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8KeqWa2D3aqmlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltbz1vXgzb17wMfrYKulpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8VaOaB7tZDS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2t/davz9bBbu0er6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMCb3Tm2YhiEgiCYqBBKoxV3pk4oZZ34ObUgcjATc+wHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOBPVbNaB7tVTS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2t563rwZt794CP18FWS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+KpGNQ92sxpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWvutX5+tg93aPV5LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+VDWrdbBb1dTS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSet66Hry5dw/4eB1stbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgK9qVPNgN6uhpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbXf+vXZOtit3eO1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgT1WzWge7VU0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0treet68Gbe/eAj9fBVktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPiqRjUPdrMaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlr7rV+frYPd2j1eS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/lQ1q3WwW9XU0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0nreuh68uXcP+HgdbLW0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICvalTzYDeroaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW13/r12TrYrd3jtbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4E9Vs1oHu1VNLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa3nrevBm3v3gI/XwVZLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4qkY1D3azGlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpa+61fn62D3do9XktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP5UNat1sFvV1NLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tJ63roevLl3D/h4HWy1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAr2pU82A3q6GlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltd/69dk62K3d47W0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOBPVbNaB7tVTS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2t563rwZt794CP18FWS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASn86cQAAIABJREFUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+KpGNQ92sxpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWvutX5+tg93aPV5LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+VDWrdbBb1dTS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSet66Hry5dw/4eB1stbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgK9qVPNgN6uhpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbXf+vXZOtit3eO1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgT1WzWge7VU0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0treet68Gbe/eAj9fBVktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPiqRjUPdrMaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlr7rV+frYPd2j1eS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/lQ1q3WwW9XU0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0nreuh68uXcP+HgdbLW0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICvalTzYDeroaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW13/r12TrYrd3jtbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAODN7hxbMQxCQRBMXAil0Yo6cyeUsk70nFoQOZiJOfYDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPypalbrYLeqqaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaX1vPV68Oa9e8DtOthqaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAX9Wo5sFuVkNLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa7/167N1sFu7x2tpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMCfqma1DnarmlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaz1uvB2/euwfcroOtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlhYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8FWNah7sZjW0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tPZbvz5bB7u1e7yWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8qWpW62C3qqmlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWl9bz1evDmvXvA7TrYamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF/VqObBblZDS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2u/9euzdbBbu8draWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAn6pmtQ52q5paWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWs9brwdv3rsH3K6DrZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpYWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPBVjWoe7GY1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLT2W78+Wwe7tXu8lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlhYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/KlqVutgt6qppaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpfW89Xrw5r17wO062GppaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABf1ajmwW5WQ0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trv/Xrs3WwW7vHa2lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwJ+qZrUOdquaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlrPW68Hb967B9yug62WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwVY1qHuxmNbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS09lu/PlsHu7V7vJaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpYWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPypalbrYLeqqaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaX1vPV68Oa9e8DtOthqaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAX9Wo5sFuVkNLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa7/167N1sFu7x2tpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMCfqma1DnarmlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaz1uvB2/euwfcroOtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlhYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8FWNah7sZjW0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tPZbvz5bB7u1e7yWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8qWpW62C3qqmlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWl9bz1evDmvXvA7TrYamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF/VqObBblZDS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2u/9euzdbBbu8draWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAn6pmtQ52q5paWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWs9brwdv3rsH3K6DrZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpYWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPBVjWoe7GY1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSi/iJCAAAgAElEQVS0tLS0tLS0PuzOsRXDIBQEwcSFqDRaUWfuhFLWiZ5TA5GDmZhjv5aWlpaWlpbWfuvXZ/NgN3eP19LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgD9VjWoe7GY1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLTWW6+FN+/dAx73wVZLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4qq5qHOxGdWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlp7bd+fTYPdnP3eC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPhT1ajmwW5WQ0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trvfVaePPePeBxH2y1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAr+qqxsFuVJeWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1n7r12fzYDd3j9fS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIA/VY1qHuxmNbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS01luvhTfv3QMe98FWS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+KquahzsRnVpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpae23fn02D3Zz93gtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4U9Wo5sFuVkNLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa731Wnjz3j3gcR9stbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgK/qqsbBblSXlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltZ+69dn82A3d4/X0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAP1WNah7sZjW0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tNZbr4U3790DHvfBVktLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPiqrmoc7EZ1aWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWntt359Ng92c/d4LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+FPVqObBblZDS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2u99Vp489494HEfbLW0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICv6qrGwW5Ul5aWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbWfuvXZ/NgN3eP19LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgD9VjWoe7GY1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLTWW6+FN+/dAx73wVZLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4qq5qHOxGdWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlp7bd+fTYPdnP3eC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPhT1ajmwW5WQ0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trvfVaePPePeBxH2y1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAr+qqxsFuVJeWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1n7r12fzYDd3j9fS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIA/VY1qHuxmNbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS01luvhTfv3QMe98FWS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+KquahzsRnVpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpae23fn02D3Zz93gtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4U9Wo5sFuVkNLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa731Wnjz3j3gcR9stbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgw+4cWzEMQkEQTFQIpdGKO1MnlLJO/JxaEDmYiTn2AwAAAAAAAAAAAPBVjWoe7GY1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLT2W78+Wwe7tXu8lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlhYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/KlqVutgt6qppaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpfW8dT14c+8e8PE62GppaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABf1ajmwW5WQ0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trv/Xrs3WwW7vHa2lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwJ+qZrUOdquaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlrPW9eDN/fuAR+vg62WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwVY1qHuxmNbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS09lu/PlsHu7V7vJaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpYWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPypalbrYLeqqaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaX1vHU9eHPvHvDxOthqaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAX9Wo5sFuVkNLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa7/167N1sFu7x2tpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCO83gAACAASURBVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMCfqma1DnarmlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaz1vXgzf37gEfr4OtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlhYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8FWNah7sZjW0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tPZbvz5bB7u1e7yWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8qWpW62C3qqmlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWl9bx1PXhz7x7w8TrYamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF/VqObBblZDS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2u/9euzdbBbu8draWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAn6pmtQ52q5paWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWs9b14M39+4BH6+DrZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpYWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPBVjWoe7GY1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLT2W78+Wwe7tXu8lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlhYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/KlqVutgt6qppaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpfW8dT14c+8e8PE62GppaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABf1ajmwW5WQ0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trv/Xrs3WwW7vHa2lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwJ+qZrUOdquaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlrPW9eDN/fuAR+vg62WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwVY1qHuxmNbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS09lu/PlsHu7V7vJaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpYWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPypalbrYLeqqaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaX1vHU9eHPvHvDxOthqaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAX9Wo5sFuVkNLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa7/167N1sFu7x2tpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMCfqma1DnarmlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaz1vXgzf37gEfr4OtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlhYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwZneOrRgGoSAIJiqE0mjFnakTSlknfk4tiBzMxBz7AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL6qUc2D3ayGlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltZ+69dn62C3do/X0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAP1XNah3sVjW1tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tJ63rgdv7t0DPl4HWy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOCrGtU82M1qaGlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWntt359tg52a/d4LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+FPVrNbBblVTS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS+t563rw5t494ON1sNXS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC+qlHNg92shpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbWfuvXZ+tgt3aP19LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgD9VzWod7FY1tbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSet64Hb+7dAz5eB1stLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgqxrVPNjNamhpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlp7bd+fbYOdmv3eC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPhT1azWwW5VU0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0vreet68ObePeDjdbDV0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvqpRzYPdrIaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1n7r12frYLd2j9fS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIA/Vc1qHexWNbW0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0nreuB2/u3QM+XgdbLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4Ksa1TzYzWpoaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpae23fn22DnZr93gtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4U9Ws1sFuVVNLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tL63nrevDm3j3g43Ww1dLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL6qUc2D3ayGlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltZ+69dn62C3do/X0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAP1XNah3sVjW1tLS0tLS0tLS0tLS0tLS0tLS0rZkCmgAAIABJREFUtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tJ63rgdv7t0DPl4HWy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOCrGtU82M1qaGlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWntt359tg52a/d4LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+FPVrNbBblVTS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS+t563rw5t494ON1sNXS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC+qlHNg92shpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbWfuvXZ+tgt3aP19LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgD9VzWod7FY1tbS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSet64Hb+7dAz5eB1stLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgqxrVPNjNamhpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlp7bd+fbYOdmv3eC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPhT1azWwW5VU0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0vreet68ObePeDjdbDV0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4szvHVgyDUBAEExVCabTiztQJpawTP6cWRA5mYo79AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwFc1qnmwm9XQ0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tpv/fpsHezW7vFaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwp6pZrYPdqqaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1vPW9eDNvXvAx+tgq6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHxVo5oHu1kNLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa391q/P1sFu7R6vpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf6qa1TrYrWpqaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaT1vXQ/e3LsHfLwOtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBXNap5sJvV0NLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLab/36bB3s1u7xWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8KeqWa2D3aqmlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltbz1vXgzb17wMfrYKulpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8VaOaB7tZDS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2t/davz9bBbu0er6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH+qmtU62K1qamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWk9b10P3ty7B3y8DrZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAVzWqebCb1dDS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2m/9+mwd7Nbu8VpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPCnqlmtg92qppaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpbW89b14M29e8DH62CrpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfFWjmge7WQ0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0trf3Wr8/WwW7tHq+lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/qprVOtitamppaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpPW9dD97cuwd8vA62WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwFc1qnmwm9XQ0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tpv/fpsHezW7vFaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwp6pZrYPdqqaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaW1vPW9eDNvXvAx+tgq6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHxVo5oHu1kNLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLa391q/P1sFu7R6vpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf6qa1TrYrWpqaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaT1vXQ/e3LsHfLwOtlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBXNap5sJvV0NLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLab/36bB3s1u7xWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8KeqWa2D3aqmlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWltbz1vXgzb17wMfrYKulpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8VaOaB7tZDS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS2t/davz9bBbu0er6WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH+qmtU62K1qamlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWk9b10P3ty7B3y8DrZaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnkug0AAABcUlEQVQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC8251jIwBCGAhirVAanVPKkn36g2MpP68BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgU61qD3a7WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpa762/Y2ewO6/Pa2lpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADB3AYAKMKfDa8ULAAAAAElFTkSuQmCC\",\"e\":1}]'),layers=JSON.parse('[{\"ddd\":0,\"ind\":1,\"ty\":2,\"nm\":\"7\",\"td\":1,\"refId\":\"image_0\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":2,\"ty\":4,\"nm\":\"7\",\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":50,\"ix\":11},\"r\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"t\":0,\"s\":[30]},{\"t\":449,\"s\":[390]}],\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":-1,\"s\":[2754.631,718.632,0],\"to\":[-21.333,27.333,0],\"ti\":[63.333,34,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":0,\"s\":[2626.631,882.632,0],\"to\":[-63.333,-34,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":143,\"s\":[2374.631,514.632,0],\"to\":[0,0,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":262,\"s\":[1938.631,730.632,0],\"to\":[0,0,0],\"ti\":[-114.667,-25.333,0]},{\"t\":449,\"s\":[2626.631,882.632,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[50,50,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"d\":1,\"ty\":\"el\",\"s\":{\"a\":0,\"k\":[64.973,64.973],\"ix\":2},\"p\":{\"a\":0,\"k\":[-11.891,-44.377],\"ix\":3},\"nm\":\"椭圆路径 1\",\"mn\":\"ADBE Vector Shape - Ellipse\",\"hd\":false},{\"d\":1,\"ty\":\"el\",\"s\":{\"a\":0,\"k\":[64.973,64.973],\"ix\":2},\"p\":{\"a\":0,\"k\":[-44.377,11.891],\"ix\":3},\"nm\":\"椭圆路径 2\",\"mn\":\"ADBE Vector Shape - Ellipse\",\"hd\":false},{\"d\":1,\"ty\":\"el\",\"s\":{\"a\":0,\"k\":[64.973,64.973],\"ix\":2},\"p\":{\"a\":0,\"k\":[44.377,-11.891],\"ix\":3},\"nm\":\"椭圆路径 3\",\"mn\":\"ADBE Vector Shape - Ellipse\",\"hd\":false},{\"d\":1,\"ty\":\"el\",\"s\":{\"a\":0,\"k\":[64.973,64.973],\"ix\":2},\"p\":{\"a\":0,\"k\":[11.891,44.377],\"ix\":3},\"nm\":\"椭圆路径 4\",\"mn\":\"ADBE Vector Shape - Ellipse\",\"hd\":false},{\"ty\":\"rc\",\"d\":1,\"s\":{\"a\":0,\"k\":[64.973,64.973],\"ix\":2},\"p\":{\"a\":0,\"k\":[-0.253,-0.944],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":4},\"nm\":\"矩形路径 1\",\"mn\":\"ADBE Vector Shape - Rect\",\"hd\":false},{\"ty\":\"mm\",\"mm\":1,\"nm\":\"合并路径 1\",\"mn\":\"ADBE Vector Filter - Merge\",\"hd\":false},{\"ty\":\"gf\",\"o\":{\"a\":0,\"k\":100,\"ix\":10},\"r\":1,\"bm\":0,\"g\":{\"p\":3,\"k\":{\"a\":0,\"k\":[0,0.969,0.835,0.914,0.5,0.924,0.594,0.78,1,0.878,0.353,0.647],\"ix\":9}},\"s\":{\"a\":0,\"k\":[50.786,-60.773],\"ix\":5},\"e\":{\"a\":0,\"k\":[-50.795,57.735],\"ix\":6},\"t\":1,\"nm\":\"Gradient fill 7\",\"mn\":\"ADBE Vector Graphic - G-Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[300,300],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"Union\",\"np\":7,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false},{\"ty\":\"gr\",\"it\":[{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"�?1\",\"np\":0,\"cix\":2,\"bm\":0,\"ix\":2,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":3,\"ty\":2,\"nm\":\"6\",\"td\":1,\"refId\":\"image_0\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":4,\"ty\":4,\"nm\":\"6\",\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":50,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":0,\"s\":[2776,1502.5,0],\"to\":[0,0,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":87,\"s\":[2548,842.5,0],\"to\":[0,0,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":271,\"s\":[1896,1638.5,0],\"to\":[0,0,0],\"ti\":[-146.667,22.667,0]},{\"t\":449,\"s\":[2776,1502.5,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[33.333,33.333,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"d\":1,\"ty\":\"el\",\"s\":{\"a\":0,\"k\":[240,240],\"ix\":2},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":3},\"nm\":\"椭圆路径 1\",\"mn\":\"ADBE Vector Shape - Ellipse\",\"hd\":false},{\"ty\":\"gs\",\"o\":{\"a\":0,\"k\":100,\"ix\":9},\"w\":{\"a\":0,\"k\":60,\"ix\":10},\"g\":{\"p\":3,\"k\":{\"a\":0,\"k\":[0,0.367,1,0.958,0.5,0.218,0.666,0.979,1,0.068,0.332,1],\"ix\":8}},\"s\":{\"a\":0,\"k\":[-93.333,-74.667],\"ix\":4},\"e\":{\"a\":0,\"k\":[81.333,89.333],\"ix\":5},\"t\":1,\"lc\":1,\"lj\":1,\"ml\":4,\"ml2\":{\"a\":0,\"k\":4,\"ix\":13},\"bm\":0,\"nm\":\"Gradient fill 6\",\"mn\":\"ADBE Vector Graphic - G-Stroke\",\"hd\":false},{\"ty\":\"st\",\"c\":{\"a\":0,\"k\":[0.772548959769,0.772548959769,0.772548959769,1],\"ix\":3},\"o\":{\"a\":0,\"k\":100,\"ix\":4},\"w\":{\"a\":0,\"k\":60,\"ix\":5},\"lc\":1,\"lj\":1,\"ml\":4,\"bm\":0,\"nm\":\"描边 1\",\"mn\":\"ADBE Vector Graphic - Stroke\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[300,300],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"Ellipse 6\",\"np\":3,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":5,\"ty\":2,\"nm\":\"5\",\"td\":1,\"refId\":\"image_0\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":6,\"ty\":4,\"nm\":\"5\",\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":50,\"ix\":11},\"r\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"t\":0,\"s\":[0]},{\"t\":449,\"s\":[360]}],\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":0,\"s\":[1633.662,1134.953,0],\"to\":[-38.667,-67.333,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":127,\"s\":[1401.662,730.953,0],\"to\":[0,0,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":297,\"s\":[937.662,1070.953,0],\"to\":[0,0,0],\"ti\":[-116,-10.667,0]},{\"t\":449,\"s\":[1633.662,1134.953,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[50,50,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ty\":\"rc\",\"d\":1,\"s\":{\"a\":0,\"k\":[184.032,120.069],\"ix\":2},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":4},\"nm\":\"矩形路径 1\",\"mn\":\"ADBE Vector Shape - Rect\",\"hd\":false},{\"ty\":\"gf\",\"o\":{\"a\":0,\"k\":100,\"ix\":10},\"r\":1,\"bm\":0,\"g\":{\"p\":3,\"k\":{\"a\":0,\"k\":[0,0.271,1,0.708,0.5,0.217,0.8,0.567,1,0.163,0.6,0.425],\"ix\":9}},\"s\":{\"a\":0,\"k\":[0,-60.035],\"ix\":5},\"e\":{\"a\":0,\"k\":[0,60.035],\"ix\":6},\"t\":1,\"nm\":\"Gradient fill 5\",\"mn\":\"ADBE Vector Graphic - G-Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[300,300],\"ix\":3},\"r\":{\"a\":0,\"k\":-35.49,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"Rectangle 40214\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":7,\"ty\":2,\"nm\":\"4\",\"td\":1,\"refId\":\"image_0\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":8,\"ty\":4,\"nm\":\"4\",\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":50,\"ix\":11},\"r\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"t\":0,\"s\":[0]},{\"t\":449,\"s\":[360]}],\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":0,\"s\":[1161.704,1439.513,0],\"to\":[87.333,-102.667,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":141,\"s\":[1685.704,823.513,0],\"to\":[0,0,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":299,\"s\":[1853.704,1471.513,0],\"to\":[0,0,0],\"ti\":[115.333,5.333,0]},{\"t\":449,\"s\":[1161.704,1439.513,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[36.756,36.756,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],\"o\":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],\"v\":[[28.8,-115.2],[-28.8,-115.2],[-28.8,-49.883],[-85.366,-82.542],[-114.166,-32.659],[-57.6,0],[-114.166,32.659],[-85.366,82.542],[-28.8,49.883],[-28.8,115.2],[28.8,115.2],[28.8,49.883],[85.366,82.541],[114.166,32.658],[57.6,0],[114.166,-32.658],[85.366,-82.541],[28.8,-49.883]],\"c\":true},\"ix\":2},\"nm\":\"路径 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"gf\",\"o\":{\"a\":0,\"k\":100,\"ix\":10},\"r\":1,\"bm\":0,\"g\":{\"p\":3,\"k\":{\"a\":0,\"k\":[0,1,0.493,0.323,0.5,1,0.727,0.626,1,1,0.961,0.928],\"ix\":9}},\"s\":{\"a\":0,\"k\":[0,-115.2],\"ix\":5},\"e\":{\"a\":0,\"k\":[0,115.2],\"ix\":6},\"t\":1,\"nm\":\"Gradient fill 4\",\"mn\":\"ADBE Vector Graphic - G-Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[300,300],\"ix\":3},\"r\":{\"a\":0,\"k\":23.344,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"Union\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":9,\"ty\":2,\"nm\":\"3\",\"td\":1,\"refId\":\"image_0\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":10,\"ty\":4,\"nm\":\"3\",\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":50,\"ix\":11},\"r\":{\"a\":1,\"k\":[{\"i\":{\"x\":[0.833],\"y\":[0.833]},\"o\":{\"x\":[0.167],\"y\":[0.167]},\"t\":0,\"s\":[0]},{\"t\":449,\"s\":[360]}],\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":-1,\"s\":[2336.819,728.49,0],\"to\":[66.667,38.667,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":151,\"s\":[2736.819,960.49,0],\"to\":[0,0,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":331,\"s\":[2148.819,1280.49,0],\"to\":[0,0,0],\"ti\":[-31.333,92,0]},{\"t\":449,\"s\":[2336.819,728.49,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[37.149,37.149,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"ind\":0,\"ty\":\"sh\",\"ix\":1,\"ks\":{\"a\":0,\"k\":{\"i\":[[0,0],[0,0],[0,0]],\"o\":[[0,0],[0,0],[0,0]],\"v\":[[-108.013,-46.95],[108.013,-108.683],[46.987,108.683]],\"c\":true},\"ix\":2},\"nm\":\"路径 1\",\"mn\":\"ADBE Vector Shape - Group\",\"hd\":false},{\"ty\":\"gf\",\"o\":{\"a\":0,\"k\":100,\"ix\":10},\"r\":1,\"bm\":0,\"g\":{\"p\":3,\"k\":{\"a\":0,\"k\":[0,1,0.727,0.732,0.5,0.854,0.364,0.372,1,0.708,0,0.012],\"ix\":9}},\"s\":{\"a\":0,\"k\":[0,-108.683],\"ix\":5},\"e\":{\"a\":0,\"k\":[0,108.683],\"ix\":6},\"t\":1,\"nm\":\"Gradient fill 3\",\"mn\":\"ADBE Vector Graphic - G-Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[300,300],\"ix\":3},\"r\":{\"a\":0,\"k\":-16.85,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"Vector 725\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":11,\"ty\":2,\"nm\":\"2\",\"td\":1,\"refId\":\"image_0\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":12,\"ty\":4,\"nm\":\"2\",\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":50,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":0,\"s\":[2575,942.5,0],\"to\":[-74,44.667,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":148,\"s\":[2131,1210.5,0],\"to\":[0,0,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":315,\"s\":[2391,1510.5,0],\"to\":[0,0,0],\"ti\":[-30.667,94.667,0]},{\"t\":449,\"s\":[2575,942.5,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[50,50,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"d\":1,\"ty\":\"el\",\"s\":{\"a\":0,\"k\":[122,122],\"ix\":2},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":3},\"nm\":\"椭圆路径 1\",\"mn\":\"ADBE Vector Shape - Ellipse\",\"hd\":false},{\"ty\":\"gf\",\"o\":{\"a\":0,\"k\":100,\"ix\":10},\"r\":1,\"bm\":0,\"g\":{\"p\":3,\"k\":{\"a\":0,\"k\":[0,1,0.85,0.527,0.5,0.916,0.72,0.297,1,0.833,0.59,0.068],\"ix\":9}},\"s\":{\"a\":0,\"k\":[0,-61],\"ix\":5},\"e\":{\"a\":0,\"k\":[0,61],\"ix\":6},\"t\":1,\"nm\":\"Gradient fill 2\",\"mn\":\"ADBE Vector Graphic - G-Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[300,300],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"Ellipse 509\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":13,\"ty\":2,\"nm\":\"1\",\"td\":1,\"refId\":\"image_0\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":100,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":14,\"ty\":4,\"nm\":\"1\",\"tt\":1,\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":50,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":1,\"k\":[{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":0,\"s\":[1416.75,631.25,0],\"to\":[151.333,34,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":153,\"s\":[2324.75,835.25,0],\"to\":[0,0,0],\"ti\":[0,0,0]},{\"i\":{\"x\":0.833,\"y\":0.833},\"o\":{\"x\":0.167,\"y\":0.167},\"t\":309,\"s\":[1608.75,1215.25,0],\"to\":[0,0,0],\"ti\":[32,97.333,0]},{\"t\":449,\"s\":[1416.75,631.25,0]}],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[44.693,44.693,100],\"ix\":6}},\"ao\":0,\"shapes\":[{\"ty\":\"gr\",\"it\":[{\"d\":1,\"ty\":\"el\",\"s\":{\"a\":0,\"k\":[201,201],\"ix\":2},\"p\":{\"a\":0,\"k\":[0,0],\"ix\":3},\"nm\":\"椭圆路径 1\",\"mn\":\"ADBE Vector Shape - Ellipse\",\"hd\":false},{\"ty\":\"gf\",\"o\":{\"a\":0,\"k\":100,\"ix\":10},\"r\":1,\"bm\":0,\"g\":{\"p\":3,\"k\":{\"a\":0,\"k\":[0,0.538,0.87,1,0.5,0.269,0.674,0.95,1,0,0.478,0.9],\"ix\":9}},\"s\":{\"a\":0,\"k\":[0,-100.5],\"ix\":5},\"e\":{\"a\":0,\"k\":[0,100.5],\"ix\":6},\"t\":1,\"nm\":\"Gradient fill 1\",\"mn\":\"ADBE Vector Graphic - G-Fill\",\"hd\":false},{\"ty\":\"tr\",\"p\":{\"a\":0,\"k\":[0,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[0,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[300,300],\"ix\":3},\"r\":{\"a\":0,\"k\":0,\"ix\":6},\"o\":{\"a\":0,\"k\":100,\"ix\":7},\"sk\":{\"a\":0,\"k\":0,\"ix\":4},\"sa\":{\"a\":0,\"k\":0,\"ix\":5},\"nm\":\"变换\"}],\"nm\":\"Ellipse 508\",\"np\":2,\"cix\":2,\"bm\":0,\"ix\":1,\"mn\":\"ADBE Vector Group\",\"hd\":false}],\"ip\":0,\"op\":450,\"st\":0,\"bm\":0},{\"ddd\":0,\"ind\":15,\"ty\":2,\"nm\":\"背景-暗色模式.png\",\"cl\":\"png\",\"refId\":\"image_1\",\"sr\":1,\"ks\":{\"o\":{\"a\":0,\"k\":18,\"ix\":11},\"r\":{\"a\":0,\"k\":0,\"ix\":10},\"p\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":2},\"a\":{\"a\":0,\"k\":[1920,1080,0],\"ix\":1},\"s\":{\"a\":0,\"k\":[100,100,100],\"ix\":6}},\"ao\":0,\"ip\":0,\"op\":450,\"st\":0,\"bm\":0}]'),markers=[],defaultDark={v:v,fr:fr,ip:ip,op:op,w:w,h:h,nm:nm,ddd:ddd,assets:assets,layers:layers,markers:markers},AnimatedBackground=reactExports.memo(()=>{const{resolvedTheme:A}=useTheme(),[t,e]=reactExports.useState(defaultDark),[a,r]=reactExports.useState(!1);return reactExports.useEffect(()=>{e(\"dark\"===A?defaultDark:defaultLight)},[A]),reactExports.useEffect(()=>{const A=()=>{r(document.hidden)},t=()=>{r(window.innerWidth<1920||document.hidden)};return t(),document.addEventListener(\"visibilitychange\",A),window.addEventListener(\"resize\",t),()=>{document.removeEventListener(\"visibilitychange\",A),window.removeEventListener(\"resize\",t)}},[]),jsxRuntimeExports.jsxs(\"div\",{className:\"fixed inset-0 w-full h-full z-0 pointer-events-none transition-colors duration-500 overflow-hidden select-none \"+(\"dark\"===A?\"bg-black\":\"bg-white\"),children:[jsxRuntimeExports.jsx(\"div\",{className:\"absolute inset-0 w-full h-full opacity-100 flex items-center justify-center overflow-hidden scale-200 sm:scale-175\",children:!a&&jsxRuntimeExports.jsx(Player,{autoplay:!0,loop:!0,src:t,rendererSettings:{preserveAspectRatio:\"xMidYMid slice\"},style:{width:\"100%\",height:\"100%\"}})}),jsxRuntimeExports.jsx(\"div\",{className:\"absolute inset-0 auth-default-grid z-10 transition-opacity duration-500 \"+(\"dark\"===A?\"opacity-100\":\"opacity-40\")}),jsxRuntimeExports.jsx(\"div\",{className:\"absolute inset-0 auth-vignette-mask z-20 transition-opacity duration-500 \"+(\"dark\"===A?\"opacity-100\":\"opacity-30\")}),jsxRuntimeExports.jsx(\"div\",{className:\"absolute inset-0 auth-noise-overlay z-30 opacity-40\"})]})});function createCollection(A){const t=A+\"CollectionProvider\",[e,a]=createContextScope(t),[r,n]=e(t,{collectionRef:{current:null},itemMap:new Map}),l=A=>{const{scope:t,children:e}=A,a=React.useRef(null),n=React.useRef(new Map).current;return jsxRuntimeExports.jsx(r,{scope:t,itemMap:n,collectionRef:a,children:e})};l.displayName=t;const i=A+\"CollectionSlot\",o=createSlot$1(i),s=React.forwardRef((A,t)=>{const{scope:e,children:a}=A,r=useComposedRefs(t,n(i,e).collectionRef);return jsxRuntimeExports.jsx(o,{ref:r,children:a})});s.displayName=i;const p=A+\"CollectionItemSlot\",c=\"data-radix-collection-item\",u=createSlot$1(p),d=React.forwardRef((A,t)=>{const{scope:e,children:a,...r}=A,l=React.useRef(null),i=useComposedRefs(t,l),o=n(p,e);return React.useEffect(()=>(o.itemMap.set(l,{ref:l,...r}),()=>{o.itemMap.delete(l)})),jsxRuntimeExports.jsx(u,{[c]:\"\",ref:i,children:a})});return d.displayName=p,[{Provider:l,Slot:s,ItemSlot:d},function(t){const e=n(A+\"CollectionConsumer\",t);return React.useCallback(()=>{const A=e.collectionRef.current;if(!A)return[];const t=Array.from(A.querySelectorAll(`[${c}]`));return Array.from(e.itemMap.values()).sort((A,e)=>t.indexOf(A.ref.current)-t.indexOf(e.ref.current))},[e.collectionRef,e.itemMap])},a]}AnimatedBackground.displayName=\"AnimatedBackground\";var DirectionContext=reactExports.createContext(void 0);function useDirection(A){const t=reactExports.useContext(DirectionContext);return A||t||\"ltr\"}const sides=[\"top\",\"right\",\"bottom\",\"left\"],min=Math.min,max=Math.max,round=Math.round,floor=Math.floor,createCoords=A=>({x:A,y:A}),oppositeSideMap={left:\"right\",right:\"left\",bottom:\"top\",top:\"bottom\"},oppositeAlignmentMap={start:\"end\",end:\"start\"};function clamp(A,t,e){return max(A,min(t,e))}function evaluate(A,t){return\"function\"==typeof A?A(t):A}function getSide(A){return A.split(\"-\")[0]}function getAlignment(A){return A.split(\"-\")[1]}function getOppositeAxis(A){return\"x\"===A?\"y\":\"x\"}function getAxisLength(A){return\"y\"===A?\"height\":\"width\"}const yAxisSides=new Set([\"top\",\"bottom\"]);function getSideAxis(A){return yAxisSides.has(getSide(A))?\"y\":\"x\"}function getAlignmentAxis(A){return getOppositeAxis(getSideAxis(A))}function getAlignmentSides(A,t,e){void 0===e&&(e=!1);const a=getAlignment(A),r=getAlignmentAxis(A),n=getAxisLength(r);let l=\"x\"===r?a===(e?\"end\":\"start\")?\"right\":\"left\":\"start\"===a?\"bottom\":\"top\";return t.reference[n]>t.floating[n]&&(l=getOppositePlacement(l)),[l,getOppositePlacement(l)]}function getExpandedPlacements(A){const t=getOppositePlacement(A);return[getOppositeAlignmentPlacement(A),t,getOppositeAlignmentPlacement(t)]}function getOppositeAlignmentPlacement(A){return A.replace(/start|end/g,A=>oppositeAlignmentMap[A])}const lrPlacement=[\"left\",\"right\"],rlPlacement=[\"right\",\"left\"],tbPlacement=[\"top\",\"bottom\"],btPlacement=[\"bottom\",\"top\"];function getSideList(A,t,e){switch(A){case\"top\":case\"bottom\":return e?t?rlPlacement:lrPlacement:t?lrPlacement:rlPlacement;case\"left\":case\"right\":return t?tbPlacement:btPlacement;default:return[]}}function getOppositeAxisPlacements(A,t,e,a){const r=getAlignment(A);let n=getSideList(getSide(A),\"start\"===e,a);return r&&(n=n.map(A=>A+\"-\"+r),t&&(n=n.concat(n.map(getOppositeAlignmentPlacement)))),n}function getOppositePlacement(A){return A.replace(/left|right|bottom|top/g,A=>oppositeSideMap[A])}function expandPaddingObject(A){return{top:0,right:0,bottom:0,left:0,...A}}function getPaddingObject(A){return\"number\"!=typeof A?expandPaddingObject(A):{top:A,right:A,bottom:A,left:A}}function rectToClientRect(A){const{x:t,y:e,width:a,height:r}=A;return{width:a,height:r,top:e,left:t,right:t+a,bottom:e+r,x:t,y:e}}function computeCoordsFromPlacement(A,t,e){let{reference:a,floating:r}=A;const n=getSideAxis(t),l=getAlignmentAxis(t),i=getAxisLength(l),o=getSide(t),s=\"y\"===n,p=a.x+a.width/2-r.width/2,c=a.y+a.height/2-r.height/2,u=a[i]/2-r[i]/2;let d;switch(o){case\"top\":d={x:p,y:a.y-r.height};break;case\"bottom\":d={x:p,y:a.y+a.height};break;case\"right\":d={x:a.x+a.width,y:c};break;case\"left\":d={x:a.x-r.width,y:c};break;default:d={x:a.x,y:a.y}}switch(getAlignment(t)){case\"start\":d[l]-=u*(e&&s?-1:1);break;case\"end\":d[l]+=u*(e&&s?-1:1)}return d}const computePosition$1=async(A,t,e)=>{const{placement:a=\"bottom\",strategy:r=\"absolute\",middleware:n=[],platform:l}=e,i=n.filter(Boolean),o=await(null==l.isRTL?void 0:l.isRTL(t));let s=await l.getElementRects({reference:A,floating:t,strategy:r}),{x:p,y:c}=computeCoordsFromPlacement(s,a,o),u=a,d={},h=0;for(let S=0;S<i.length;S++){const{name:e,fn:n}=i[S],{x:f,y:m,data:L,reset:W}=await n({x:p,y:c,initialPlacement:a,placement:u,strategy:r,middlewareData:d,rects:s,platform:l,elements:{reference:A,floating:t}});p=null!=f?f:p,c=null!=m?m:c,d={...d,[e]:{...d[e],...L}},W&&h<=50&&(h++,\"object\"==typeof W&&(W.placement&&(u=W.placement),W.rects&&(s=!0===W.rects?await l.getElementRects({reference:A,floating:t,strategy:r}):W.rects),({x:p,y:c}=computeCoordsFromPlacement(s,u,o))),S=-1)}return{x:p,y:c,placement:u,strategy:r,middlewareData:d}};async function detectOverflow(A,t){var e;void 0===t&&(t={});const{x:a,y:r,platform:n,rects:l,elements:i,strategy:o}=A,{boundary:s=\"clippingAncestors\",rootBoundary:p=\"viewport\",elementContext:c=\"floating\",altBoundary:u=!1,padding:d=0}=evaluate(t,A),h=getPaddingObject(d),S=i[u?\"floating\"===c?\"reference\":\"floating\":c],f=rectToClientRect(await n.getClippingRect({element:null==(e=await(null==n.isElement?void 0:n.isElement(S)))||e?S:S.contextElement||await(null==n.getDocumentElement?void 0:n.getDocumentElement(i.floating)),boundary:s,rootBoundary:p,strategy:o})),m=\"floating\"===c?{x:a,y:r,width:l.floating.width,height:l.floating.height}:l.reference,L=await(null==n.getOffsetParent?void 0:n.getOffsetParent(i.floating)),W=await(null==n.isElement?void 0:n.isElement(L))&&await(null==n.getScale?void 0:n.getScale(L))||{x:1,y:1},g=rectToClientRect(n.convertOffsetParentRelativeRectToViewportRelativeRect?await n.convertOffsetParentRelativeRectToViewportRelativeRect({elements:i,rect:m,offsetParent:L,strategy:o}):m);return{top:(f.top-g.top+h.top)/W.y,bottom:(g.bottom-f.bottom+h.bottom)/W.y,left:(f.left-g.left+h.left)/W.x,right:(g.right-f.right+h.right)/W.x}}const arrow$3=A=>({name:\"arrow\",options:A,async fn(t){const{x:e,y:a,placement:r,rects:n,platform:l,elements:i,middlewareData:o}=t,{element:s,padding:p=0}=evaluate(A,t)||{};if(null==s)return{};const c=getPaddingObject(p),u={x:e,y:a},d=getAlignmentAxis(r),h=getAxisLength(d),S=await l.getDimensions(s),f=\"y\"===d,m=f?\"top\":\"left\",L=f?\"bottom\":\"right\",W=f?\"clientHeight\":\"clientWidth\",g=n.reference[h]+n.reference[d]-u[d]-n.floating[h],y=u[d]-n.reference[d],v=await(null==l.getOffsetParent?void 0:l.getOffsetParent(s));let b=v?v[W]:0;b&&await(null==l.isElement?void 0:l.isElement(v))||(b=i.floating[W]||n.floating[h]);const x=g/2-y/2,E=b/2-S[h]/2-1,k=min(c[m],E),w=min(c[L],E),C=k,P=b-S[h]-w,T=b/2-S[h]/2+x,_=clamp(C,T,P),M=!o.arrow&&null!=getAlignment(r)&&T!==_&&n.reference[h]/2-(T<C?k:w)-S[h]/2<0,D=M?T<C?T-C:T-P:0;return{[d]:u[d]+D,data:{[d]:_,centerOffset:T-_-D,...M&&{alignmentOffset:D}},reset:M}}}),flip$2=function(A){return void 0===A&&(A={}),{name:\"flip\",options:A,async fn(t){var e,a;const{placement:r,middlewareData:n,rects:l,initialPlacement:i,platform:o,elements:s}=t,{mainAxis:p=!0,crossAxis:c=!0,fallbackPlacements:u,fallbackStrategy:d=\"bestFit\",fallbackAxisSideDirection:h=\"none\",flipAlignment:S=!0,...f}=evaluate(A,t);if(null!=(e=n.arrow)&&e.alignmentOffset)return{};const m=getSide(r),L=getSideAxis(i),W=getSide(i)===i,g=await(null==o.isRTL?void 0:o.isRTL(s.floating)),y=u||(W||!S?[getOppositePlacement(i)]:getExpandedPlacements(i)),v=\"none\"!==h;!u&&v&&y.push(...getOppositeAxisPlacements(i,S,h,g));const b=[i,...y],x=await detectOverflow(t,f),E=[];let k=(null==(a=n.flip)?void 0:a.overflows)||[];if(p&&E.push(x[m]),c){const A=getAlignmentSides(r,l,g);E.push(x[A[0]],x[A[1]])}if(k=[...k,{placement:r,overflows:E}],!E.every(A=>A<=0)){var w,C;const A=((null==(w=n.flip)?void 0:w.index)||0)+1,t=b[A];if(t){if(!(\"alignment\"===c&&L!==getSideAxis(t))||k.every(A=>getSideAxis(A.placement)!==L||A.overflows[0]>0))return{data:{index:A,overflows:k},reset:{placement:t}}}let e=null==(C=k.filter(A=>A.overflows[0]<=0).sort((A,t)=>A.overflows[1]-t.overflows[1])[0])?void 0:C.placement;if(!e)switch(d){case\"bestFit\":{var P;const A=null==(P=k.filter(A=>{if(v){const t=getSideAxis(A.placement);return t===L||\"y\"===t}return!0}).map(A=>[A.placement,A.overflows.filter(A=>A>0).reduce((A,t)=>A+t,0)]).sort((A,t)=>A[1]-t[1])[0])?void 0:P[0];A&&(e=A);break}case\"initialPlacement\":e=i}if(r!==e)return{reset:{placement:e}}}return{}}}};function getSideOffsets(A,t){return{top:A.top-t.height,right:A.right-t.width,bottom:A.bottom-t.height,left:A.left-t.width}}function isAnySideFullyClipped(A){return sides.some(t=>A[t]>=0)}const hide$2=function(A){return void 0===A&&(A={}),{name:\"hide\",options:A,async fn(t){const{rects:e}=t,{strategy:a=\"referenceHidden\",...r}=evaluate(A,t);switch(a){case\"referenceHidden\":{const A=getSideOffsets(await detectOverflow(t,{...r,elementContext:\"reference\"}),e.reference);return{data:{referenceHiddenOffsets:A,referenceHidden:isAnySideFullyClipped(A)}}}case\"escaped\":{const A=getSideOffsets(await detectOverflow(t,{...r,altBoundary:!0}),e.floating);return{data:{escapedOffsets:A,escaped:isAnySideFullyClipped(A)}}}default:return{}}}}},originSides=new Set([\"left\",\"top\"]);async function convertValueToCoords(A,t){const{placement:e,platform:a,elements:r}=A,n=await(null==a.isRTL?void 0:a.isRTL(r.floating)),l=getSide(e),i=getAlignment(e),o=\"y\"===getSideAxis(e),s=originSides.has(l)?-1:1,p=n&&o?-1:1,c=evaluate(t,A);let{mainAxis:u,crossAxis:d,alignmentAxis:h}=\"number\"==typeof c?{mainAxis:c,crossAxis:0,alignmentAxis:null}:{mainAxis:c.mainAxis||0,crossAxis:c.crossAxis||0,alignmentAxis:c.alignmentAxis};return i&&\"number\"==typeof h&&(d=\"end\"===i?-1*h:h),o?{x:d*p,y:u*s}:{x:u*s,y:d*p}}const offset$2=function(A){return void 0===A&&(A=0),{name:\"offset\",options:A,async fn(t){var e,a;const{x:r,y:n,placement:l,middlewareData:i}=t,o=await convertValueToCoords(t,A);return l===(null==(e=i.offset)?void 0:e.placement)&&null!=(a=i.arrow)&&a.alignmentOffset?{}:{x:r+o.x,y:n+o.y,data:{...o,placement:l}}}}},shift$2=function(A){return void 0===A&&(A={}),{name:\"shift\",options:A,async fn(t){const{x:e,y:a,placement:r}=t,{mainAxis:n=!0,crossAxis:l=!1,limiter:i={fn:A=>{let{x:t,y:e}=A;return{x:t,y:e}}},...o}=evaluate(A,t),s={x:e,y:a},p=await detectOverflow(t,o),c=getSideAxis(getSide(r)),u=getOppositeAxis(c);let d=s[u],h=s[c];if(n){const A=\"y\"===u?\"bottom\":\"right\";d=clamp(d+p[\"y\"===u?\"top\":\"left\"],d,d-p[A])}if(l){const A=\"y\"===c?\"bottom\":\"right\";h=clamp(h+p[\"y\"===c?\"top\":\"left\"],h,h-p[A])}const S=i.fn({...t,[u]:d,[c]:h});return{...S,data:{x:S.x-e,y:S.y-a,enabled:{[u]:n,[c]:l}}}}}},limitShift$2=function(A){return void 0===A&&(A={}),{options:A,fn(t){const{x:e,y:a,placement:r,rects:n,middlewareData:l}=t,{offset:i=0,mainAxis:o=!0,crossAxis:s=!0}=evaluate(A,t),p={x:e,y:a},c=getSideAxis(r),u=getOppositeAxis(c);let d=p[u],h=p[c];const S=evaluate(i,t),f=\"number\"==typeof S?{mainAxis:S,crossAxis:0}:{mainAxis:0,crossAxis:0,...S};if(o){const A=\"y\"===u?\"height\":\"width\",t=n.reference[u]-n.floating[A]+f.mainAxis,e=n.reference[u]+n.reference[A]-f.mainAxis;d<t?d=t:d>e&&(d=e)}if(s){var m,L;const A=\"y\"===u?\"width\":\"height\",t=originSides.has(getSide(r)),e=n.reference[c]-n.floating[A]+(t&&(null==(m=l.offset)?void 0:m[c])||0)+(t?0:f.crossAxis),a=n.reference[c]+n.reference[A]+(t?0:(null==(L=l.offset)?void 0:L[c])||0)-(t?f.crossAxis:0);h<e?h=e:h>a&&(h=a)}return{[u]:d,[c]:h}}}},size$2=function(A){return void 0===A&&(A={}),{name:\"size\",options:A,async fn(t){var e,a;const{placement:r,rects:n,platform:l,elements:i}=t,{apply:o=()=>{},...s}=evaluate(A,t),p=await detectOverflow(t,s),c=getSide(r),u=getAlignment(r),d=\"y\"===getSideAxis(r),{width:h,height:S}=n.floating;let f,m;\"top\"===c||\"bottom\"===c?(f=c,m=u===(await(null==l.isRTL?void 0:l.isRTL(i.floating))?\"start\":\"end\")?\"left\":\"right\"):(m=c,f=\"end\"===u?\"top\":\"bottom\");const L=S-p.top-p.bottom,W=h-p.left-p.right,g=min(S-p[f],L),y=min(h-p[m],W),v=!t.middlewareData.shift;let b=g,x=y;if(null!=(e=t.middlewareData.shift)&&e.enabled.x&&(x=W),null!=(a=t.middlewareData.shift)&&a.enabled.y&&(b=L),v&&!u){const A=max(p.left,0),t=max(p.right,0),e=max(p.top,0),a=max(p.bottom,0);d?x=h-2*(0!==A||0!==t?A+t:max(p.left,p.right)):b=S-2*(0!==e||0!==a?e+a:max(p.top,p.bottom))}await o({...t,availableWidth:x,availableHeight:b});const E=await l.getDimensions(i.floating);return h!==E.width||S!==E.height?{reset:{rects:!0}}:{}}}};function hasWindow(){return\"undefined\"!=typeof window}function getNodeName(A){return isNode(A)?(A.nodeName||\"\").toLowerCase():\"#document\"}function getWindow(A){var t;return(null==A||null==(t=A.ownerDocument)?void 0:t.defaultView)||window}function getDocumentElement(A){var t;return null==(t=(isNode(A)?A.ownerDocument:A.document)||window.document)?void 0:t.documentElement}function isNode(A){return!!hasWindow()&&(A instanceof Node||A instanceof getWindow(A).Node)}function isElement(A){return!!hasWindow()&&(A instanceof Element||A instanceof getWindow(A).Element)}function isHTMLElement(A){return!!hasWindow()&&(A instanceof HTMLElement||A instanceof getWindow(A).HTMLElement)}function isShadowRoot(A){return!(!hasWindow()||\"undefined\"==typeof ShadowRoot)&&(A instanceof ShadowRoot||A instanceof getWindow(A).ShadowRoot)}const invalidOverflowDisplayValues=new Set([\"inline\",\"contents\"]);function isOverflowElement(A){const{overflow:t,overflowX:e,overflowY:a,display:r}=getComputedStyle$1(A);return/auto|scroll|overlay|hidden|clip/.test(t+a+e)&&!invalidOverflowDisplayValues.has(r)}const tableElements=new Set([\"table\",\"td\",\"th\"]);function isTableElement(A){return tableElements.has(getNodeName(A))}const topLayerSelectors=[\":popover-open\",\":modal\"];function isTopLayer(A){return topLayerSelectors.some(t=>{try{return A.matches(t)}catch(e){return!1}})}const transformProperties=[\"transform\",\"translate\",\"scale\",\"rotate\",\"perspective\"],willChangeValues=[\"transform\",\"translate\",\"scale\",\"rotate\",\"perspective\",\"filter\"],containValues=[\"paint\",\"layout\",\"strict\",\"content\"];function isContainingBlock(A){const t=isWebKit(),e=isElement(A)?getComputedStyle$1(A):A;return transformProperties.some(A=>!!e[A]&&\"none\"!==e[A])||!!e.containerType&&\"normal\"!==e.containerType||!t&&!!e.backdropFilter&&\"none\"!==e.backdropFilter||!t&&!!e.filter&&\"none\"!==e.filter||willChangeValues.some(A=>(e.willChange||\"\").includes(A))||containValues.some(A=>(e.contain||\"\").includes(A))}function getContainingBlock(A){let t=getParentNode(A);for(;isHTMLElement(t)&&!isLastTraversableNode(t);){if(isContainingBlock(t))return t;if(isTopLayer(t))return null;t=getParentNode(t)}return null}function isWebKit(){return!(\"undefined\"==typeof CSS||!CSS.supports)&&CSS.supports(\"-webkit-backdrop-filter\",\"none\")}const lastTraversableNodeNames=new Set([\"html\",\"body\",\"#document\"]);function isLastTraversableNode(A){return lastTraversableNodeNames.has(getNodeName(A))}function getComputedStyle$1(A){return getWindow(A).getComputedStyle(A)}function getNodeScroll(A){return isElement(A)?{scrollLeft:A.scrollLeft,scrollTop:A.scrollTop}:{scrollLeft:A.scrollX,scrollTop:A.scrollY}}function getParentNode(A){if(\"html\"===getNodeName(A))return A;const t=A.assignedSlot||A.parentNode||isShadowRoot(A)&&A.host||getDocumentElement(A);return isShadowRoot(t)?t.host:t}function getNearestOverflowAncestor(A){const t=getParentNode(A);return isLastTraversableNode(t)?A.ownerDocument?A.ownerDocument.body:A.body:isHTMLElement(t)&&isOverflowElement(t)?t:getNearestOverflowAncestor(t)}function getOverflowAncestors(A,t,e){var a;void 0===t&&(t=[]),void 0===e&&(e=!0);const r=getNearestOverflowAncestor(A),n=r===(null==(a=A.ownerDocument)?void 0:a.body),l=getWindow(r);if(n){const A=getFrameElement(l);return t.concat(l,l.visualViewport||[],isOverflowElement(r)?r:[],A&&e?getOverflowAncestors(A):[])}return t.concat(r,getOverflowAncestors(r,[],e))}function getFrameElement(A){return A.parent&&Object.getPrototypeOf(A.parent)?A.frameElement:null}function getCssDimensions(A){const t=getComputedStyle$1(A);let e=parseFloat(t.width)||0,a=parseFloat(t.height)||0;const r=isHTMLElement(A),n=r?A.offsetWidth:e,l=r?A.offsetHeight:a,i=round(e)!==n||round(a)!==l;return i&&(e=n,a=l),{width:e,height:a,$:i}}function unwrapElement(A){return isElement(A)?A:A.contextElement}function getScale(A){const t=unwrapElement(A);if(!isHTMLElement(t))return createCoords(1);const e=t.getBoundingClientRect(),{width:a,height:r,$:n}=getCssDimensions(t);let l=(n?round(e.width):e.width)/a,i=(n?round(e.height):e.height)/r;return l&&Number.isFinite(l)||(l=1),i&&Number.isFinite(i)||(i=1),{x:l,y:i}}const noOffsets=createCoords(0);function getVisualOffsets(A){const t=getWindow(A);return isWebKit()&&t.visualViewport?{x:t.visualViewport.offsetLeft,y:t.visualViewport.offsetTop}:noOffsets}function shouldAddVisualOffsets(A,t,e){return void 0===t&&(t=!1),!(!e||t&&e!==getWindow(A))&&t}function getBoundingClientRect(A,t,e,a){void 0===t&&(t=!1),void 0===e&&(e=!1);const r=A.getBoundingClientRect(),n=unwrapElement(A);let l=createCoords(1);t&&(a?isElement(a)&&(l=getScale(a)):l=getScale(A));const i=shouldAddVisualOffsets(n,e,a)?getVisualOffsets(n):createCoords(0);let o=(r.left+i.x)/l.x,s=(r.top+i.y)/l.y,p=r.width/l.x,c=r.height/l.y;if(n){const A=getWindow(n),t=a&&isElement(a)?getWindow(a):a;let e=A,r=getFrameElement(e);for(;r&&a&&t!==e;){const A=getScale(r),t=r.getBoundingClientRect(),a=getComputedStyle$1(r),n=t.left+(r.clientLeft+parseFloat(a.paddingLeft))*A.x,l=t.top+(r.clientTop+parseFloat(a.paddingTop))*A.y;o*=A.x,s*=A.y,p*=A.x,c*=A.y,o+=n,s+=l,e=getWindow(r),r=getFrameElement(e)}}return rectToClientRect({width:p,height:c,x:o,y:s})}function getWindowScrollBarX(A,t){const e=getNodeScroll(A).scrollLeft;return t?t.left+e:getBoundingClientRect(getDocumentElement(A)).left+e}function getHTMLOffset(A,t){const e=A.getBoundingClientRect();return{x:e.left+t.scrollLeft-getWindowScrollBarX(A,e),y:e.top+t.scrollTop}}function convertOffsetParentRelativeRectToViewportRelativeRect(A){let{elements:t,rect:e,offsetParent:a,strategy:r}=A;const n=\"fixed\"===r,l=getDocumentElement(a),i=!!t&&isTopLayer(t.floating);if(a===l||i&&n)return e;let o={scrollLeft:0,scrollTop:0},s=createCoords(1);const p=createCoords(0),c=isHTMLElement(a);if((c||!c&&!n)&&((\"body\"!==getNodeName(a)||isOverflowElement(l))&&(o=getNodeScroll(a)),isHTMLElement(a))){const A=getBoundingClientRect(a);s=getScale(a),p.x=A.x+a.clientLeft,p.y=A.y+a.clientTop}const u=!l||c||n?createCoords(0):getHTMLOffset(l,o);return{width:e.width*s.x,height:e.height*s.y,x:e.x*s.x-o.scrollLeft*s.x+p.x+u.x,y:e.y*s.y-o.scrollTop*s.y+p.y+u.y}}function getClientRects(A){return Array.from(A.getClientRects())}function getDocumentRect(A){const t=getDocumentElement(A),e=getNodeScroll(A),a=A.ownerDocument.body,r=max(t.scrollWidth,t.clientWidth,a.scrollWidth,a.clientWidth),n=max(t.scrollHeight,t.clientHeight,a.scrollHeight,a.clientHeight);let l=-e.scrollLeft+getWindowScrollBarX(A);const i=-e.scrollTop;return\"rtl\"===getComputedStyle$1(a).direction&&(l+=max(t.clientWidth,a.clientWidth)-r),{width:r,height:n,x:l,y:i}}const SCROLLBAR_MAX=25;function getViewportRect(A,t){const e=getWindow(A),a=getDocumentElement(A),r=e.visualViewport;let n=a.clientWidth,l=a.clientHeight,i=0,o=0;if(r){n=r.width,l=r.height;const A=isWebKit();(!A||A&&\"fixed\"===t)&&(i=r.offsetLeft,o=r.offsetTop)}const s=getWindowScrollBarX(a);if(s<=0){const A=a.ownerDocument,t=A.body,e=getComputedStyle(t),r=\"CSS1Compat\"===A.compatMode&&parseFloat(e.marginLeft)+parseFloat(e.marginRight)||0,l=Math.abs(a.clientWidth-t.clientWidth-r);l<=SCROLLBAR_MAX&&(n-=l)}else s<=SCROLLBAR_MAX&&(n+=s);return{width:n,height:l,x:i,y:o}}const absoluteOrFixed=new Set([\"absolute\",\"fixed\"]);function getInnerBoundingClientRect(A,t){const e=getBoundingClientRect(A,!0,\"fixed\"===t),a=e.top+A.clientTop,r=e.left+A.clientLeft,n=isHTMLElement(A)?getScale(A):createCoords(1);return{width:A.clientWidth*n.x,height:A.clientHeight*n.y,x:r*n.x,y:a*n.y}}function getClientRectFromClippingAncestor(A,t,e){let a;if(\"viewport\"===t)a=getViewportRect(A,e);else if(\"document\"===t)a=getDocumentRect(getDocumentElement(A));else if(isElement(t))a=getInnerBoundingClientRect(t,e);else{const e=getVisualOffsets(A);a={x:t.x-e.x,y:t.y-e.y,width:t.width,height:t.height}}return rectToClientRect(a)}function hasFixedPositionAncestor(A,t){const e=getParentNode(A);return!(e===t||!isElement(e)||isLastTraversableNode(e))&&(\"fixed\"===getComputedStyle$1(e).position||hasFixedPositionAncestor(e,t))}function getClippingElementAncestors(A,t){const e=t.get(A);if(e)return e;let a=getOverflowAncestors(A,[],!1).filter(A=>isElement(A)&&\"body\"!==getNodeName(A)),r=null;const n=\"fixed\"===getComputedStyle$1(A).position;let l=n?getParentNode(A):A;for(;isElement(l)&&!isLastTraversableNode(l);){const t=getComputedStyle$1(l),e=isContainingBlock(l);e||\"fixed\"!==t.position||(r=null);(n?!e&&!r:!e&&\"static\"===t.position&&!!r&&absoluteOrFixed.has(r.position)||isOverflowElement(l)&&!e&&hasFixedPositionAncestor(A,l))?a=a.filter(A=>A!==l):r=t,l=getParentNode(l)}return t.set(A,a),a}function getClippingRect(A){let{element:t,boundary:e,rootBoundary:a,strategy:r}=A;const n=[...\"clippingAncestors\"===e?isTopLayer(t)?[]:getClippingElementAncestors(t,this._c):[].concat(e),a],l=n[0],i=n.reduce((A,e)=>{const a=getClientRectFromClippingAncestor(t,e,r);return A.top=max(a.top,A.top),A.right=min(a.right,A.right),A.bottom=min(a.bottom,A.bottom),A.left=max(a.left,A.left),A},getClientRectFromClippingAncestor(t,l,r));return{width:i.right-i.left,height:i.bottom-i.top,x:i.left,y:i.top}}function getDimensions(A){const{width:t,height:e}=getCssDimensions(A);return{width:t,height:e}}function getRectRelativeToOffsetParent(A,t,e){const a=isHTMLElement(t),r=getDocumentElement(t),n=\"fixed\"===e,l=getBoundingClientRect(A,!0,n,t);let i={scrollLeft:0,scrollTop:0};const o=createCoords(0);function s(){o.x=getWindowScrollBarX(r)}if(a||!a&&!n)if((\"body\"!==getNodeName(t)||isOverflowElement(r))&&(i=getNodeScroll(t)),a){const A=getBoundingClientRect(t,!0,n,t);o.x=A.x+t.clientLeft,o.y=A.y+t.clientTop}else r&&s();n&&!a&&r&&s();const p=!r||a||n?createCoords(0):getHTMLOffset(r,i);return{x:l.left+i.scrollLeft-o.x-p.x,y:l.top+i.scrollTop-o.y-p.y,width:l.width,height:l.height}}function isStaticPositioned(A){return\"static\"===getComputedStyle$1(A).position}function getTrueOffsetParent(A,t){if(!isHTMLElement(A)||\"fixed\"===getComputedStyle$1(A).position)return null;if(t)return t(A);let e=A.offsetParent;return getDocumentElement(A)===e&&(e=e.ownerDocument.body),e}function getOffsetParent(A,t){const e=getWindow(A);if(isTopLayer(A))return e;if(!isHTMLElement(A)){let t=getParentNode(A);for(;t&&!isLastTraversableNode(t);){if(isElement(t)&&!isStaticPositioned(t))return t;t=getParentNode(t)}return e}let a=getTrueOffsetParent(A,t);for(;a&&isTableElement(a)&&isStaticPositioned(a);)a=getTrueOffsetParent(a,t);return a&&isLastTraversableNode(a)&&isStaticPositioned(a)&&!isContainingBlock(a)?e:a||getContainingBlock(A)||e}const getElementRects=async function(A){const t=this.getOffsetParent||getOffsetParent,e=this.getDimensions,a=await e(A.floating);return{reference:getRectRelativeToOffsetParent(A.reference,await t(A.floating),A.strategy),floating:{x:0,y:0,width:a.width,height:a.height}}};function isRTL(A){return\"rtl\"===getComputedStyle$1(A).direction}const platform={convertOffsetParentRelativeRectToViewportRelativeRect:convertOffsetParentRelativeRectToViewportRelativeRect,getDocumentElement:getDocumentElement,getClippingRect:getClippingRect,getOffsetParent:getOffsetParent,getElementRects:getElementRects,getClientRects:getClientRects,getDimensions:getDimensions,getScale:getScale,isElement:isElement,isRTL:isRTL};function rectsAreEqual(A,t){return A.x===t.x&&A.y===t.y&&A.width===t.width&&A.height===t.height}function observeMove(A,t){let e,a=null;const r=getDocumentElement(A);function n(){var A;clearTimeout(e),null==(A=a)||A.disconnect(),a=null}return function l(i,o){void 0===i&&(i=!1),void 0===o&&(o=1),n();const s=A.getBoundingClientRect(),{left:p,top:c,width:u,height:d}=s;if(i||t(),!u||!d)return;const h={rootMargin:-floor(c)+\"px \"+-floor(r.clientWidth-(p+u))+\"px \"+-floor(r.clientHeight-(c+d))+\"px \"+-floor(p)+\"px\",threshold:max(0,min(1,o))||1};let S=!0;function f(t){const a=t[0].intersectionRatio;if(a!==o){if(!S)return l();a?l(!1,a):e=setTimeout(()=>{l(!1,1e-7)},1e3)}1!==a||rectsAreEqual(s,A.getBoundingClientRect())||l(),S=!1}try{a=new IntersectionObserver(f,{...h,root:r.ownerDocument})}catch(m){a=new IntersectionObserver(f,h)}a.observe(A)}(!0),n}function autoUpdate(A,t,e,a){void 0===a&&(a={});const{ancestorScroll:r=!0,ancestorResize:n=!0,elementResize:l=\"function\"==typeof ResizeObserver,layoutShift:i=\"function\"==typeof IntersectionObserver,animationFrame:o=!1}=a,s=unwrapElement(A),p=r||n?[...s?getOverflowAncestors(s):[],...getOverflowAncestors(t)]:[];p.forEach(A=>{r&&A.addEventListener(\"scroll\",e,{passive:!0}),n&&A.addEventListener(\"resize\",e)});const c=s&&i?observeMove(s,e):null;let u,d=-1,h=null;l&&(h=new ResizeObserver(A=>{let[a]=A;a&&a.target===s&&h&&(h.unobserve(t),cancelAnimationFrame(d),d=requestAnimationFrame(()=>{var A;null==(A=h)||A.observe(t)})),e()}),s&&!o&&h.observe(s),h.observe(t));let S=o?getBoundingClientRect(A):null;return o&&function t(){const a=getBoundingClientRect(A);S&&!rectsAreEqual(S,a)&&e();S=a,u=requestAnimationFrame(t)}(),e(),()=>{var A;p.forEach(A=>{r&&A.removeEventListener(\"scroll\",e),n&&A.removeEventListener(\"resize\",e)}),null==c||c(),null==(A=h)||A.disconnect(),h=null,o&&cancelAnimationFrame(u)}}const offset$1=offset$2,shift$1=shift$2,flip$1=flip$2,size$1=size$2,hide$1=hide$2,arrow$2=arrow$3,limitShift$1=limitShift$2,computePosition=(A,t,e)=>{const a=new Map,r={platform:platform,...e},n={...r.platform,_c:a};return computePosition$1(A,t,{...r,platform:n})};var isClient=\"undefined\"!=typeof document,noop=function(){},index=isClient?reactExports.useLayoutEffect:noop;function deepEqual(A,t){if(A===t)return!0;if(typeof A!=typeof t)return!1;if(\"function\"==typeof A&&A.toString()===t.toString())return!0;let e,a,r;if(A&&t&&\"object\"==typeof A){if(Array.isArray(A)){if(e=A.length,e!==t.length)return!1;for(a=e;0!==a--;)if(!deepEqual(A[a],t[a]))return!1;return!0}if(r=Object.keys(A),e=r.length,e!==Object.keys(t).length)return!1;for(a=e;0!==a--;)if(!{}.hasOwnProperty.call(t,r[a]))return!1;for(a=e;0!==a--;){const e=r[a];if((\"_owner\"!==e||!A.$$typeof)&&!deepEqual(A[e],t[e]))return!1}return!0}return A!=A&&t!=t}function getDPR(A){if(\"undefined\"==typeof window)return 1;return(A.ownerDocument.defaultView||window).devicePixelRatio||1}function roundByDPR(A,t){const e=getDPR(A);return Math.round(t*e)/e}function useLatestRef(A){const t=reactExports.useRef(A);return index(()=>{t.current=A}),t}function useFloating(A){void 0===A&&(A={});const{placement:t=\"bottom\",strategy:e=\"absolute\",middleware:a=[],platform:r,elements:{reference:n,floating:l}={},transform:i=!0,whileElementsMounted:o,open:s}=A,[p,c]=reactExports.useState({x:0,y:0,strategy:e,placement:t,middlewareData:{},isPositioned:!1}),[u,d]=reactExports.useState(a);deepEqual(u,a)||d(a);const[h,S]=reactExports.useState(null),[f,m]=reactExports.useState(null),L=reactExports.useCallback(A=>{A!==v.current&&(v.current=A,S(A))},[]),W=reactExports.useCallback(A=>{A!==b.current&&(b.current=A,m(A))},[]),g=n||h,y=l||f,v=reactExports.useRef(null),b=reactExports.useRef(null),x=reactExports.useRef(p),E=null!=o,k=useLatestRef(o),w=useLatestRef(r),C=useLatestRef(s),P=reactExports.useCallback(()=>{if(!v.current||!b.current)return;const A={placement:t,strategy:e,middleware:u};w.current&&(A.platform=w.current),computePosition(v.current,b.current,A).then(A=>{const t={...A,isPositioned:!1!==C.current};T.current&&!deepEqual(x.current,t)&&(x.current=t,reactDomExports.flushSync(()=>{c(t)}))})},[u,t,e,w,C]);index(()=>{!1===s&&x.current.isPositioned&&(x.current.isPositioned=!1,c(A=>({...A,isPositioned:!1})))},[s]);const T=reactExports.useRef(!1);index(()=>(T.current=!0,()=>{T.current=!1}),[]),index(()=>{if(g&&(v.current=g),y&&(b.current=y),g&&y){if(k.current)return k.current(g,y,P);P()}},[g,y,P,k,E]);const _=reactExports.useMemo(()=>({reference:v,floating:b,setReference:L,setFloating:W}),[L,W]),M=reactExports.useMemo(()=>({reference:g,floating:y}),[g,y]),D=reactExports.useMemo(()=>{const A={position:e,left:0,top:0};if(!M.floating)return A;const t=roundByDPR(M.floating,p.x),a=roundByDPR(M.floating,p.y);return i?{...A,transform:\"translate(\"+t+\"px, \"+a+\"px)\",...getDPR(M.floating)>=1.5&&{willChange:\"transform\"}}:{position:e,left:t,top:a}},[e,i,M.floating,p.x,p.y]);return reactExports.useMemo(()=>({...p,update:P,refs:_,elements:M,floatingStyles:D}),[p,P,_,M,D])}const arrow$1=A=>({name:\"arrow\",options:A,fn(t){const{element:e,padding:a}=\"function\"==typeof A?A(t):A;return e&&(r=e,{}.hasOwnProperty.call(r,\"current\"))?null!=e.current?arrow$2({element:e.current,padding:a}).fn(t):{}:e?arrow$2({element:e,padding:a}).fn(t):{};var r}}),offset=(A,t)=>({...offset$1(A),options:[A,t]}),shift=(A,t)=>({...shift$1(A),options:[A,t]}),limitShift=(A,t)=>({...limitShift$1(A),options:[A,t]}),flip=(A,t)=>({...flip$1(A),options:[A,t]}),size=(A,t)=>({...size$1(A),options:[A,t]}),hide=(A,t)=>({...hide$1(A),options:[A,t]}),arrow=(A,t)=>({...arrow$1(A),options:[A,t]});var NAME=\"Arrow\",Arrow$1=reactExports.forwardRef((A,t)=>{const{children:e,width:a=10,height:r=5,...n}=A;return jsxRuntimeExports.jsx(Primitive.svg,{...n,ref:t,width:a,height:r,viewBox:\"0 0 30 10\",preserveAspectRatio:\"none\",children:A.asChild?e:jsxRuntimeExports.jsx(\"polygon\",{points:\"0,0 30,0 15,10\"})})});Arrow$1.displayName=NAME;var Root$1=Arrow$1;function useSize(A){const[t,e]=reactExports.useState(void 0);return useLayoutEffect2(()=>{if(A){e({width:A.offsetWidth,height:A.offsetHeight});const t=new ResizeObserver(t=>{if(!Array.isArray(t))return;if(!t.length)return;const a=t[0];let r,n;if(\"borderBoxSize\"in a){const A=a.borderBoxSize,t=Array.isArray(A)?A[0]:A;r=t.inlineSize,n=t.blockSize}else r=A.offsetWidth,n=A.offsetHeight;e({width:r,height:n})});return t.observe(A,{box:\"border-box\"}),()=>t.unobserve(A)}e(void 0)},[A]),t}var POPPER_NAME=\"Popper\",[createPopperContext,createPopperScope]=createContextScope(POPPER_NAME),[PopperProvider,usePopperContext]=createPopperContext(POPPER_NAME),Popper=A=>{const{__scopePopper:t,children:e}=A,[a,r]=reactExports.useState(null);return jsxRuntimeExports.jsx(PopperProvider,{scope:t,anchor:a,onAnchorChange:r,children:e})};Popper.displayName=POPPER_NAME;var ANCHOR_NAME$1=\"PopperAnchor\",PopperAnchor=reactExports.forwardRef((A,t)=>{const{__scopePopper:e,virtualRef:a,...r}=A,n=usePopperContext(ANCHOR_NAME$1,e),l=reactExports.useRef(null),i=useComposedRefs(t,l),o=reactExports.useRef(null);return reactExports.useEffect(()=>{const A=o.current;o.current=(null==a?void 0:a.current)||l.current,A!==o.current&&n.onAnchorChange(o.current)}),a?null:jsxRuntimeExports.jsx(Primitive.div,{...r,ref:i})});PopperAnchor.displayName=ANCHOR_NAME$1;var CONTENT_NAME$2=\"PopperContent\",[PopperContentProvider,useContentContext]=createPopperContext(CONTENT_NAME$2),PopperContent=reactExports.forwardRef((A,t)=>{var e,a,r,n,l,i;const{__scopePopper:o,side:s=\"bottom\",sideOffset:p=0,align:c=\"center\",alignOffset:u=0,arrowPadding:d=0,avoidCollisions:h=!0,collisionBoundary:S=[],collisionPadding:f=0,sticky:m=\"partial\",hideWhenDetached:L=!1,updatePositionStrategy:W=\"optimized\",onPlaced:g,...y}=A,v=usePopperContext(CONTENT_NAME$2,o),[b,x]=reactExports.useState(null),E=useComposedRefs(t,A=>x(A)),[k,w]=reactExports.useState(null),C=useSize(k),P=(null==C?void 0:C.width)??0,T=(null==C?void 0:C.height)??0,_=s+(\"center\"!==c?\"-\"+c:\"\"),M=\"number\"==typeof f?f:{top:0,right:0,bottom:0,left:0,...f},D=Array.isArray(S)?S:[S],R=D.length>0,N={padding:M,boundary:D.filter(isNotNull),altBoundary:R},{refs:I,floatingStyles:F,placement:O,isPositioned:V,middlewareData:B}=useFloating({strategy:\"fixed\",placement:_,whileElementsMounted:(...A)=>autoUpdate(...A,{animationFrame:\"always\"===W}),elements:{reference:v.anchor},middleware:[offset({mainAxis:p+T,alignmentAxis:u}),h&&shift({mainAxis:!0,crossAxis:!1,limiter:\"partial\"===m?limitShift():void 0,...N}),h&&flip({...N}),size({...N,apply:({elements:A,rects:t,availableWidth:e,availableHeight:a})=>{const{width:r,height:n}=t.reference,l=A.floating.style;l.setProperty(\"--radix-popper-available-width\",`${e}px`),l.setProperty(\"--radix-popper-available-height\",`${a}px`),l.setProperty(\"--radix-popper-anchor-width\",`${r}px`),l.setProperty(\"--radix-popper-anchor-height\",`${n}px`)}}),k&&arrow({element:k,padding:d}),transformOrigin({arrowWidth:P,arrowHeight:T}),L&&hide({strategy:\"referenceHidden\",...N})]}),[z,j]=getSideAndAlignFromPlacement(O),$=useCallbackRef$1(g);useLayoutEffect2(()=>{V&&(null==$||$())},[V,$]);const H=null==(e=B.arrow)?void 0:e.x,G=null==(a=B.arrow)?void 0:a.y,q=0!==(null==(r=B.arrow)?void 0:r.centerOffset),[U,X]=reactExports.useState();return useLayoutEffect2(()=>{b&&X(window.getComputedStyle(b).zIndex)},[b]),jsxRuntimeExports.jsx(\"div\",{ref:I.setFloating,\"data-radix-popper-content-wrapper\":\"\",style:{...F,transform:V?F.transform:\"translate(0, -200%)\",minWidth:\"max-content\",zIndex:U,\"--radix-popper-transform-origin\":[null==(n=B.transformOrigin)?void 0:n.x,null==(l=B.transformOrigin)?void 0:l.y].join(\" \"),...(null==(i=B.hide)?void 0:i.referenceHidden)&&{visibility:\"hidden\",pointerEvents:\"none\"}},dir:A.dir,children:jsxRuntimeExports.jsx(PopperContentProvider,{scope:o,placedSide:z,onArrowChange:w,arrowX:H,arrowY:G,shouldHideArrow:q,children:jsxRuntimeExports.jsx(Primitive.div,{\"data-side\":z,\"data-align\":j,...y,ref:E,style:{...y.style,animation:V?void 0:\"none\"}})})})});PopperContent.displayName=CONTENT_NAME$2;var ARROW_NAME$2=\"PopperArrow\",OPPOSITE_SIDE={top:\"bottom\",right:\"left\",bottom:\"top\",left:\"right\"},PopperArrow=reactExports.forwardRef(function(A,t){const{__scopePopper:e,...a}=A,r=useContentContext(ARROW_NAME$2,e),n=OPPOSITE_SIDE[r.placedSide];return jsxRuntimeExports.jsx(\"span\",{ref:r.onArrowChange,style:{position:\"absolute\",left:r.arrowX,top:r.arrowY,[n]:0,transformOrigin:{top:\"\",right:\"0 0\",bottom:\"center 0\",left:\"100% 0\"}[r.placedSide],transform:{top:\"translateY(100%)\",right:\"translateY(50%) rotate(90deg) translateX(-50%)\",bottom:\"rotate(180deg)\",left:\"translateY(50%) rotate(-90deg) translateX(50%)\"}[r.placedSide],visibility:r.shouldHideArrow?\"hidden\":void 0},children:jsxRuntimeExports.jsx(Root$1,{...a,ref:t,style:{...a.style,display:\"block\"}})})});function isNotNull(A){return null!==A}PopperArrow.displayName=ARROW_NAME$2;var transformOrigin=A=>({name:\"transformOrigin\",options:A,fn(t){var e,a,r;const{placement:n,rects:l,middlewareData:i}=t,o=0!==(null==(e=i.arrow)?void 0:e.centerOffset),s=o?0:A.arrowWidth,p=o?0:A.arrowHeight,[c,u]=getSideAndAlignFromPlacement(n),d={start:\"0%\",center:\"50%\",end:\"100%\"}[u],h=((null==(a=i.arrow)?void 0:a.x)??0)+s/2,S=((null==(r=i.arrow)?void 0:r.y)??0)+p/2;let f=\"\",m=\"\";return\"bottom\"===c?(f=o?d:`${h}px`,m=-p+\"px\"):\"top\"===c?(f=o?d:`${h}px`,m=`${l.floating.height+p}px`):\"right\"===c?(f=-p+\"px\",m=o?d:`${S}px`):\"left\"===c&&(f=`${l.floating.width+p}px`,m=o?d:`${S}px`),{data:{x:f,y:m}}}});function getSideAndAlignFromPlacement(A){const[t,e=\"center\"]=A.split(\"-\");return[t,e]}var Root2$1=Popper,Anchor=PopperAnchor,Content=PopperContent,Arrow=PopperArrow,ENTRY_FOCUS=\"rovingFocusGroup.onEntryFocus\",EVENT_OPTIONS={bubbles:!1,cancelable:!0},GROUP_NAME$2=\"RovingFocusGroup\",[Collection$1,useCollection$1,createCollectionScope$1]=createCollection(GROUP_NAME$2),[createRovingFocusGroupContext,createRovingFocusGroupScope]=createContextScope(GROUP_NAME$2,[createCollectionScope$1]),[RovingFocusProvider,useRovingFocusContext]=createRovingFocusGroupContext(GROUP_NAME$2),RovingFocusGroup=reactExports.forwardRef((A,t)=>jsxRuntimeExports.jsx(Collection$1.Provider,{scope:A.__scopeRovingFocusGroup,children:jsxRuntimeExports.jsx(Collection$1.Slot,{scope:A.__scopeRovingFocusGroup,children:jsxRuntimeExports.jsx(RovingFocusGroupImpl,{...A,ref:t})})}));RovingFocusGroup.displayName=GROUP_NAME$2;var RovingFocusGroupImpl=reactExports.forwardRef((A,t)=>{const{__scopeRovingFocusGroup:e,orientation:a,loop:r=!1,dir:n,currentTabStopId:l,defaultCurrentTabStopId:i,onCurrentTabStopIdChange:o,onEntryFocus:s,preventScrollOnEntryFocus:p=!1,...c}=A,u=reactExports.useRef(null),d=useComposedRefs(t,u),h=useDirection(n),[S,f]=useControllableState({prop:l,defaultProp:i??null,onChange:o,caller:GROUP_NAME$2}),[m,L]=reactExports.useState(!1),W=useCallbackRef$1(s),g=useCollection$1(e),y=reactExports.useRef(!1),[v,b]=reactExports.useState(0);return reactExports.useEffect(()=>{const A=u.current;if(A)return A.addEventListener(ENTRY_FOCUS,W),()=>A.removeEventListener(ENTRY_FOCUS,W)},[W]),jsxRuntimeExports.jsx(RovingFocusProvider,{scope:e,orientation:a,dir:h,loop:r,currentTabStopId:S,onItemFocus:reactExports.useCallback(A=>f(A),[f]),onItemShiftTab:reactExports.useCallback(()=>L(!0),[]),onFocusableItemAdd:reactExports.useCallback(()=>b(A=>A+1),[]),onFocusableItemRemove:reactExports.useCallback(()=>b(A=>A-1),[]),children:jsxRuntimeExports.jsx(Primitive.div,{tabIndex:m||0===v?-1:0,\"data-orientation\":a,...c,ref:d,style:{outline:\"none\",...A.style},onMouseDown:composeEventHandlers(A.onMouseDown,()=>{y.current=!0}),onFocus:composeEventHandlers(A.onFocus,A=>{const t=!y.current;if(A.target===A.currentTarget&&t&&!m){const t=new CustomEvent(ENTRY_FOCUS,EVENT_OPTIONS);if(A.currentTarget.dispatchEvent(t),!t.defaultPrevented){const A=g().filter(A=>A.focusable);focusFirst$1([A.find(A=>A.active),A.find(A=>A.id===S),...A].filter(Boolean).map(A=>A.ref.current),p)}}y.current=!1}),onBlur:composeEventHandlers(A.onBlur,()=>L(!1))})})}),ITEM_NAME$2=\"RovingFocusGroupItem\",RovingFocusGroupItem=reactExports.forwardRef((A,t)=>{const{__scopeRovingFocusGroup:e,focusable:a=!0,active:r=!1,tabStopId:n,children:l,...i}=A,o=useId(),s=n||o,p=useRovingFocusContext(ITEM_NAME$2,e),c=p.currentTabStopId===s,u=useCollection$1(e),{onFocusableItemAdd:d,onFocusableItemRemove:h,currentTabStopId:S}=p;return reactExports.useEffect(()=>{if(a)return d(),()=>h()},[a,d,h]),jsxRuntimeExports.jsx(Collection$1.ItemSlot,{scope:e,id:s,focusable:a,active:r,children:jsxRuntimeExports.jsx(Primitive.span,{tabIndex:c?0:-1,\"data-orientation\":p.orientation,...i,ref:t,onMouseDown:composeEventHandlers(A.onMouseDown,A=>{a?p.onItemFocus(s):A.preventDefault()}),onFocus:composeEventHandlers(A.onFocus,()=>p.onItemFocus(s)),onKeyDown:composeEventHandlers(A.onKeyDown,A=>{if(\"Tab\"===A.key&&A.shiftKey)return void p.onItemShiftTab();if(A.target!==A.currentTarget)return;const t=getFocusIntent(A,p.orientation,p.dir);if(void 0!==t){if(A.metaKey||A.ctrlKey||A.altKey||A.shiftKey)return;A.preventDefault();let e=u().filter(A=>A.focusable).map(A=>A.ref.current);if(\"last\"===t)e.reverse();else if(\"prev\"===t||\"next\"===t){\"prev\"===t&&e.reverse();const a=e.indexOf(A.currentTarget);e=p.loop?wrapArray$1(e,a+1):e.slice(a+1)}setTimeout(()=>focusFirst$1(e))}}),children:\"function\"==typeof l?l({isCurrentTabStop:c,hasTabStop:null!=S}):l})})});RovingFocusGroupItem.displayName=ITEM_NAME$2;var MAP_KEY_TO_FOCUS_INTENT={ArrowLeft:\"prev\",ArrowUp:\"prev\",ArrowRight:\"next\",ArrowDown:\"next\",PageUp:\"first\",Home:\"first\",PageDown:\"last\",End:\"last\"};function getDirectionAwareKey(A,t){return\"rtl\"!==t?A:\"ArrowLeft\"===A?\"ArrowRight\":\"ArrowRight\"===A?\"ArrowLeft\":A}function getFocusIntent(A,t,e){const a=getDirectionAwareKey(A.key,e);if(!(\"vertical\"===t&&[\"ArrowLeft\",\"ArrowRight\"].includes(a)||\"horizontal\"===t&&[\"ArrowUp\",\"ArrowDown\"].includes(a)))return MAP_KEY_TO_FOCUS_INTENT[a]}function focusFirst$1(A,t=!1){const e=document.activeElement;for(const a of A){if(a===e)return;if(a.focus({preventScroll:t}),document.activeElement!==e)return}}function wrapArray$1(A,t){return A.map((e,a)=>A[(t+a)%A.length])}var Root=RovingFocusGroup,Item=RovingFocusGroupItem,SELECTION_KEYS=[\"Enter\",\" \"],FIRST_KEYS=[\"ArrowDown\",\"PageUp\",\"Home\"],LAST_KEYS=[\"ArrowUp\",\"PageDown\",\"End\"],FIRST_LAST_KEYS=[...FIRST_KEYS,...LAST_KEYS],SUB_OPEN_KEYS={ltr:[...SELECTION_KEYS,\"ArrowRight\"],rtl:[...SELECTION_KEYS,\"ArrowLeft\"]},SUB_CLOSE_KEYS={ltr:[\"ArrowLeft\"],rtl:[\"ArrowRight\"]},MENU_NAME=\"Menu\",[Collection,useCollection,createCollectionScope]=createCollection(MENU_NAME),[createMenuContext,createMenuScope]=createContextScope(MENU_NAME,[createCollectionScope,createPopperScope,createRovingFocusGroupScope]),usePopperScope=createPopperScope(),useRovingFocusGroupScope=createRovingFocusGroupScope(),[MenuProvider,useMenuContext]=createMenuContext(MENU_NAME),[MenuRootProvider,useMenuRootContext]=createMenuContext(MENU_NAME),Menu=A=>{const{__scopeMenu:t,open:e=!1,children:a,dir:r,onOpenChange:n,modal:l=!0}=A,i=usePopperScope(t),[o,s]=reactExports.useState(null),p=reactExports.useRef(!1),c=useCallbackRef$1(n),u=useDirection(r);return reactExports.useEffect(()=>{const A=()=>{p.current=!0,document.addEventListener(\"pointerdown\",t,{capture:!0,once:!0}),document.addEventListener(\"pointermove\",t,{capture:!0,once:!0})},t=()=>p.current=!1;return document.addEventListener(\"keydown\",A,{capture:!0}),()=>{document.removeEventListener(\"keydown\",A,{capture:!0}),document.removeEventListener(\"pointerdown\",t,{capture:!0}),document.removeEventListener(\"pointermove\",t,{capture:!0})}},[]),jsxRuntimeExports.jsx(Root2$1,{...i,children:jsxRuntimeExports.jsx(MenuProvider,{scope:t,open:e,onOpenChange:c,content:o,onContentChange:s,children:jsxRuntimeExports.jsx(MenuRootProvider,{scope:t,onClose:reactExports.useCallback(()=>c(!1),[c]),isUsingKeyboardRef:p,dir:u,modal:l,children:a})})})};Menu.displayName=MENU_NAME;var ANCHOR_NAME=\"MenuAnchor\",MenuAnchor=reactExports.forwardRef((A,t)=>{const{__scopeMenu:e,...a}=A,r=usePopperScope(e);return jsxRuntimeExports.jsx(Anchor,{...r,...a,ref:t})});MenuAnchor.displayName=ANCHOR_NAME;var PORTAL_NAME$1=\"MenuPortal\",[PortalProvider,usePortalContext]=createMenuContext(PORTAL_NAME$1,{forceMount:void 0}),MenuPortal=A=>{const{__scopeMenu:t,forceMount:e,children:a,container:r}=A,n=useMenuContext(PORTAL_NAME$1,t);return jsxRuntimeExports.jsx(PortalProvider,{scope:t,forceMount:e,children:jsxRuntimeExports.jsx(Presence,{present:e||n.open,children:jsxRuntimeExports.jsx(Portal$2,{asChild:!0,container:r,children:a})})})};MenuPortal.displayName=PORTAL_NAME$1;var CONTENT_NAME$1=\"MenuContent\",[MenuContentProvider,useMenuContentContext]=createMenuContext(CONTENT_NAME$1),MenuContent=reactExports.forwardRef((A,t)=>{const e=usePortalContext(CONTENT_NAME$1,A.__scopeMenu),{forceMount:a=e.forceMount,...r}=A,n=useMenuContext(CONTENT_NAME$1,A.__scopeMenu),l=useMenuRootContext(CONTENT_NAME$1,A.__scopeMenu);return jsxRuntimeExports.jsx(Collection.Provider,{scope:A.__scopeMenu,children:jsxRuntimeExports.jsx(Presence,{present:a||n.open,children:jsxRuntimeExports.jsx(Collection.Slot,{scope:A.__scopeMenu,children:l.modal?jsxRuntimeExports.jsx(MenuRootContentModal,{...r,ref:t}):jsxRuntimeExports.jsx(MenuRootContentNonModal,{...r,ref:t})})})})}),MenuRootContentModal=reactExports.forwardRef((A,t)=>{const e=useMenuContext(CONTENT_NAME$1,A.__scopeMenu),a=reactExports.useRef(null),r=useComposedRefs(t,a);return reactExports.useEffect(()=>{const A=a.current;if(A)return hideOthers(A)},[]),jsxRuntimeExports.jsx(MenuContentImpl,{...A,ref:r,trapFocus:e.open,disableOutsidePointerEvents:e.open,disableOutsideScroll:!0,onFocusOutside:composeEventHandlers(A.onFocusOutside,A=>A.preventDefault(),{checkForDefaultPrevented:!1}),onDismiss:()=>e.onOpenChange(!1)})}),MenuRootContentNonModal=reactExports.forwardRef((A,t)=>{const e=useMenuContext(CONTENT_NAME$1,A.__scopeMenu);return jsxRuntimeExports.jsx(MenuContentImpl,{...A,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,disableOutsideScroll:!1,onDismiss:()=>e.onOpenChange(!1)})}),Slot=createSlot$1(\"MenuContent.ScrollLock\"),MenuContentImpl=reactExports.forwardRef((A,t)=>{const{__scopeMenu:e,loop:a=!1,trapFocus:r,onOpenAutoFocus:n,onCloseAutoFocus:l,disableOutsidePointerEvents:i,onEntryFocus:o,onEscapeKeyDown:s,onPointerDownOutside:p,onFocusOutside:c,onInteractOutside:u,onDismiss:d,disableOutsideScroll:h,...S}=A,f=useMenuContext(CONTENT_NAME$1,e),m=useMenuRootContext(CONTENT_NAME$1,e),L=usePopperScope(e),W=useRovingFocusGroupScope(e),g=useCollection(e),[y,v]=reactExports.useState(null),b=reactExports.useRef(null),x=useComposedRefs(t,b,f.onContentChange),E=reactExports.useRef(0),k=reactExports.useRef(\"\"),w=reactExports.useRef(0),C=reactExports.useRef(null),P=reactExports.useRef(\"right\"),T=reactExports.useRef(0),_=h?ReactRemoveScroll:reactExports.Fragment,M=h?{as:Slot,allowPinchZoom:!0}:void 0;reactExports.useEffect(()=>()=>window.clearTimeout(E.current),[]),useFocusGuards();const D=reactExports.useCallback(A=>{var t,e;return P.current===(null==(t=C.current)?void 0:t.side)&&isPointerInGraceArea(A,null==(e=C.current)?void 0:e.area)},[]);return jsxRuntimeExports.jsx(MenuContentProvider,{scope:e,searchRef:k,onItemEnter:reactExports.useCallback(A=>{D(A)&&A.preventDefault()},[D]),onItemLeave:reactExports.useCallback(A=>{var t;D(A)||(null==(t=b.current)||t.focus(),v(null))},[D]),onTriggerLeave:reactExports.useCallback(A=>{D(A)&&A.preventDefault()},[D]),pointerGraceTimerRef:w,onPointerGraceIntentChange:reactExports.useCallback(A=>{C.current=A},[]),children:jsxRuntimeExports.jsx(_,{...M,children:jsxRuntimeExports.jsx(FocusScope,{asChild:!0,trapped:r,onMountAutoFocus:composeEventHandlers(n,A=>{var t;A.preventDefault(),null==(t=b.current)||t.focus({preventScroll:!0})}),onUnmountAutoFocus:l,children:jsxRuntimeExports.jsx(DismissableLayer,{asChild:!0,disableOutsidePointerEvents:i,onEscapeKeyDown:s,onPointerDownOutside:p,onFocusOutside:c,onInteractOutside:u,onDismiss:d,children:jsxRuntimeExports.jsx(Root,{asChild:!0,...W,dir:m.dir,orientation:\"vertical\",loop:a,currentTabStopId:y,onCurrentTabStopIdChange:v,onEntryFocus:composeEventHandlers(o,A=>{m.isUsingKeyboardRef.current||A.preventDefault()}),preventScrollOnEntryFocus:!0,children:jsxRuntimeExports.jsx(Content,{role:\"menu\",\"aria-orientation\":\"vertical\",\"data-state\":getOpenState(f.open),\"data-radix-menu-content\":\"\",dir:m.dir,...L,...S,ref:x,style:{outline:\"none\",...S.style},onKeyDown:composeEventHandlers(S.onKeyDown,A=>{const t=A.target.closest(\"[data-radix-menu-content]\")===A.currentTarget,e=A.ctrlKey||A.altKey||A.metaKey,a=1===A.key.length;t&&(\"Tab\"===A.key&&A.preventDefault(),!e&&a&&(A=>{var t,e;const a=k.current+A,r=g().filter(A=>!A.disabled),n=document.activeElement,l=null==(t=r.find(A=>A.ref.current===n))?void 0:t.textValue,i=getNextMatch(r.map(A=>A.textValue),a,l),o=null==(e=r.find(A=>A.textValue===i))?void 0:e.ref.current;!function A(t){k.current=t,window.clearTimeout(E.current),\"\"!==t&&(E.current=window.setTimeout(()=>A(\"\"),1e3))}(a),o&&setTimeout(()=>o.focus())})(A.key));const r=b.current;if(A.target!==r)return;if(!FIRST_LAST_KEYS.includes(A.key))return;A.preventDefault();const n=g().filter(A=>!A.disabled).map(A=>A.ref.current);LAST_KEYS.includes(A.key)&&n.reverse(),focusFirst(n)}),onBlur:composeEventHandlers(A.onBlur,A=>{A.currentTarget.contains(A.target)||(window.clearTimeout(E.current),k.current=\"\")}),onPointerMove:composeEventHandlers(A.onPointerMove,whenMouse(A=>{const t=A.target,e=T.current!==A.clientX;if(A.currentTarget.contains(t)&&e){const t=A.clientX>T.current?\"right\":\"left\";P.current=t,T.current=A.clientX}}))})})})})})})});MenuContent.displayName=CONTENT_NAME$1;var GROUP_NAME$1=\"MenuGroup\",MenuGroup=reactExports.forwardRef((A,t)=>{const{__scopeMenu:e,...a}=A;return jsxRuntimeExports.jsx(Primitive.div,{role:\"group\",...a,ref:t})});MenuGroup.displayName=GROUP_NAME$1;var LABEL_NAME$1=\"MenuLabel\",MenuLabel=reactExports.forwardRef((A,t)=>{const{__scopeMenu:e,...a}=A;return jsxRuntimeExports.jsx(Primitive.div,{...a,ref:t})});MenuLabel.displayName=LABEL_NAME$1;var ITEM_NAME$1=\"MenuItem\",ITEM_SELECT=\"menu.itemSelect\",MenuItem=reactExports.forwardRef((A,t)=>{const{disabled:e=!1,onSelect:a,...r}=A,n=reactExports.useRef(null),l=useMenuRootContext(ITEM_NAME$1,A.__scopeMenu),i=useMenuContentContext(ITEM_NAME$1,A.__scopeMenu),o=useComposedRefs(t,n),s=reactExports.useRef(!1);return jsxRuntimeExports.jsx(MenuItemImpl,{...r,ref:o,disabled:e,onClick:composeEventHandlers(A.onClick,()=>{const A=n.current;if(!e&&A){const t=new CustomEvent(ITEM_SELECT,{bubbles:!0,cancelable:!0});A.addEventListener(ITEM_SELECT,A=>null==a?void 0:a(A),{once:!0}),dispatchDiscreteCustomEvent(A,t),t.defaultPrevented?s.current=!1:l.onClose()}}),onPointerDown:t=>{var e;null==(e=A.onPointerDown)||e.call(A,t),s.current=!0},onPointerUp:composeEventHandlers(A.onPointerUp,A=>{var t;s.current||null==(t=A.currentTarget)||t.click()}),onKeyDown:composeEventHandlers(A.onKeyDown,A=>{const t=\"\"!==i.searchRef.current;e||t&&\" \"===A.key||SELECTION_KEYS.includes(A.key)&&(A.currentTarget.click(),A.preventDefault())})})});MenuItem.displayName=ITEM_NAME$1;var MenuItemImpl=reactExports.forwardRef((A,t)=>{const{__scopeMenu:e,disabled:a=!1,textValue:r,...n}=A,l=useMenuContentContext(ITEM_NAME$1,e),i=useRovingFocusGroupScope(e),o=reactExports.useRef(null),s=useComposedRefs(t,o),[p,c]=reactExports.useState(!1),[u,d]=reactExports.useState(\"\");return reactExports.useEffect(()=>{const A=o.current;A&&d((A.textContent??\"\").trim())},[n.children]),jsxRuntimeExports.jsx(Collection.ItemSlot,{scope:e,disabled:a,textValue:r??u,children:jsxRuntimeExports.jsx(Item,{asChild:!0,...i,focusable:!a,children:jsxRuntimeExports.jsx(Primitive.div,{role:\"menuitem\",\"data-highlighted\":p?\"\":void 0,\"aria-disabled\":a||void 0,\"data-disabled\":a?\"\":void 0,...n,ref:s,onPointerMove:composeEventHandlers(A.onPointerMove,whenMouse(A=>{if(a)l.onItemLeave(A);else if(l.onItemEnter(A),!A.defaultPrevented){A.currentTarget.focus({preventScroll:!0})}})),onPointerLeave:composeEventHandlers(A.onPointerLeave,whenMouse(A=>l.onItemLeave(A))),onFocus:composeEventHandlers(A.onFocus,()=>c(!0)),onBlur:composeEventHandlers(A.onBlur,()=>c(!1))})})})}),CHECKBOX_ITEM_NAME$1=\"MenuCheckboxItem\",MenuCheckboxItem=reactExports.forwardRef((A,t)=>{const{checked:e=!1,onCheckedChange:a,...r}=A;return jsxRuntimeExports.jsx(ItemIndicatorProvider,{scope:A.__scopeMenu,checked:e,children:jsxRuntimeExports.jsx(MenuItem,{role:\"menuitemcheckbox\",\"aria-checked\":isIndeterminate(e)?\"mixed\":e,...r,ref:t,\"data-state\":getCheckedState(e),onSelect:composeEventHandlers(r.onSelect,()=>null==a?void 0:a(!!isIndeterminate(e)||!e),{checkForDefaultPrevented:!1})})})});MenuCheckboxItem.displayName=CHECKBOX_ITEM_NAME$1;var RADIO_GROUP_NAME$1=\"MenuRadioGroup\",[RadioGroupProvider,useRadioGroupContext]=createMenuContext(RADIO_GROUP_NAME$1,{value:void 0,onValueChange:()=>{}}),MenuRadioGroup=reactExports.forwardRef((A,t)=>{const{value:e,onValueChange:a,...r}=A,n=useCallbackRef$1(a);return jsxRuntimeExports.jsx(RadioGroupProvider,{scope:A.__scopeMenu,value:e,onValueChange:n,children:jsxRuntimeExports.jsx(MenuGroup,{...r,ref:t})})});MenuRadioGroup.displayName=RADIO_GROUP_NAME$1;var RADIO_ITEM_NAME$1=\"MenuRadioItem\",MenuRadioItem=reactExports.forwardRef((A,t)=>{const{value:e,...a}=A,r=useRadioGroupContext(RADIO_ITEM_NAME$1,A.__scopeMenu),n=e===r.value;return jsxRuntimeExports.jsx(ItemIndicatorProvider,{scope:A.__scopeMenu,checked:n,children:jsxRuntimeExports.jsx(MenuItem,{role:\"menuitemradio\",\"aria-checked\":n,...a,ref:t,\"data-state\":getCheckedState(n),onSelect:composeEventHandlers(a.onSelect,()=>{var A;return null==(A=r.onValueChange)?void 0:A.call(r,e)},{checkForDefaultPrevented:!1})})})});MenuRadioItem.displayName=RADIO_ITEM_NAME$1;var ITEM_INDICATOR_NAME=\"MenuItemIndicator\",[ItemIndicatorProvider,useItemIndicatorContext]=createMenuContext(ITEM_INDICATOR_NAME,{checked:!1}),MenuItemIndicator=reactExports.forwardRef((A,t)=>{const{__scopeMenu:e,forceMount:a,...r}=A,n=useItemIndicatorContext(ITEM_INDICATOR_NAME,e);return jsxRuntimeExports.jsx(Presence,{present:a||isIndeterminate(n.checked)||!0===n.checked,children:jsxRuntimeExports.jsx(Primitive.span,{...r,ref:t,\"data-state\":getCheckedState(n.checked)})})});MenuItemIndicator.displayName=ITEM_INDICATOR_NAME;var SEPARATOR_NAME$1=\"MenuSeparator\",MenuSeparator=reactExports.forwardRef((A,t)=>{const{__scopeMenu:e,...a}=A;return jsxRuntimeExports.jsx(Primitive.div,{role:\"separator\",\"aria-orientation\":\"horizontal\",...a,ref:t})});MenuSeparator.displayName=SEPARATOR_NAME$1;var ARROW_NAME$1=\"MenuArrow\",MenuArrow=reactExports.forwardRef((A,t)=>{const{__scopeMenu:e,...a}=A,r=usePopperScope(e);return jsxRuntimeExports.jsx(Arrow,{...r,...a,ref:t})});MenuArrow.displayName=ARROW_NAME$1;var SUB_NAME=\"MenuSub\",[MenuSubProvider,useMenuSubContext]=createMenuContext(SUB_NAME),MenuSub=A=>{const{__scopeMenu:t,children:e,open:a=!1,onOpenChange:r}=A,n=useMenuContext(SUB_NAME,t),l=usePopperScope(t),[i,o]=reactExports.useState(null),[s,p]=reactExports.useState(null),c=useCallbackRef$1(r);return reactExports.useEffect(()=>(!1===n.open&&c(!1),()=>c(!1)),[n.open,c]),jsxRuntimeExports.jsx(Root2$1,{...l,children:jsxRuntimeExports.jsx(MenuProvider,{scope:t,open:a,onOpenChange:c,content:s,onContentChange:p,children:jsxRuntimeExports.jsx(MenuSubProvider,{scope:t,contentId:useId(),triggerId:useId(),trigger:i,onTriggerChange:o,children:e})})})};MenuSub.displayName=SUB_NAME;var SUB_TRIGGER_NAME$1=\"MenuSubTrigger\",MenuSubTrigger=reactExports.forwardRef((A,t)=>{const e=useMenuContext(SUB_TRIGGER_NAME$1,A.__scopeMenu),a=useMenuRootContext(SUB_TRIGGER_NAME$1,A.__scopeMenu),r=useMenuSubContext(SUB_TRIGGER_NAME$1,A.__scopeMenu),n=useMenuContentContext(SUB_TRIGGER_NAME$1,A.__scopeMenu),l=reactExports.useRef(null),{pointerGraceTimerRef:i,onPointerGraceIntentChange:o}=n,s={__scopeMenu:A.__scopeMenu},p=reactExports.useCallback(()=>{l.current&&window.clearTimeout(l.current),l.current=null},[]);return reactExports.useEffect(()=>p,[p]),reactExports.useEffect(()=>{const A=i.current;return()=>{window.clearTimeout(A),o(null)}},[i,o]),jsxRuntimeExports.jsx(MenuAnchor,{asChild:!0,...s,children:jsxRuntimeExports.jsx(MenuItemImpl,{id:r.triggerId,\"aria-haspopup\":\"menu\",\"aria-expanded\":e.open,\"aria-controls\":r.contentId,\"data-state\":getOpenState(e.open),...A,ref:composeRefs(t,r.onTriggerChange),onClick:t=>{var a;null==(a=A.onClick)||a.call(A,t),A.disabled||t.defaultPrevented||(t.currentTarget.focus(),e.open||e.onOpenChange(!0))},onPointerMove:composeEventHandlers(A.onPointerMove,whenMouse(t=>{n.onItemEnter(t),t.defaultPrevented||A.disabled||e.open||l.current||(n.onPointerGraceIntentChange(null),l.current=window.setTimeout(()=>{e.onOpenChange(!0),p()},100))})),onPointerLeave:composeEventHandlers(A.onPointerLeave,whenMouse(A=>{var t,a;p();const r=null==(t=e.content)?void 0:t.getBoundingClientRect();if(r){const t=null==(a=e.content)?void 0:a.dataset.side,l=\"right\"===t,o=l?-5:5,s=r[l?\"left\":\"right\"],p=r[l?\"right\":\"left\"];n.onPointerGraceIntentChange({area:[{x:A.clientX+o,y:A.clientY},{x:s,y:r.top},{x:p,y:r.top},{x:p,y:r.bottom},{x:s,y:r.bottom}],side:t}),window.clearTimeout(i.current),i.current=window.setTimeout(()=>n.onPointerGraceIntentChange(null),300)}else{if(n.onTriggerLeave(A),A.defaultPrevented)return;n.onPointerGraceIntentChange(null)}})),onKeyDown:composeEventHandlers(A.onKeyDown,t=>{var r;const l=\"\"!==n.searchRef.current;A.disabled||l&&\" \"===t.key||SUB_OPEN_KEYS[a.dir].includes(t.key)&&(e.onOpenChange(!0),null==(r=e.content)||r.focus(),t.preventDefault())})})})});MenuSubTrigger.displayName=SUB_TRIGGER_NAME$1;var SUB_CONTENT_NAME$1=\"MenuSubContent\",MenuSubContent=reactExports.forwardRef((A,t)=>{const e=usePortalContext(CONTENT_NAME$1,A.__scopeMenu),{forceMount:a=e.forceMount,...r}=A,n=useMenuContext(CONTENT_NAME$1,A.__scopeMenu),l=useMenuRootContext(CONTENT_NAME$1,A.__scopeMenu),i=useMenuSubContext(SUB_CONTENT_NAME$1,A.__scopeMenu),o=reactExports.useRef(null),s=useComposedRefs(t,o);return jsxRuntimeExports.jsx(Collection.Provider,{scope:A.__scopeMenu,children:jsxRuntimeExports.jsx(Presence,{present:a||n.open,children:jsxRuntimeExports.jsx(Collection.Slot,{scope:A.__scopeMenu,children:jsxRuntimeExports.jsx(MenuContentImpl,{id:i.contentId,\"aria-labelledby\":i.triggerId,...r,ref:s,align:\"start\",side:\"rtl\"===l.dir?\"left\":\"right\",disableOutsidePointerEvents:!1,disableOutsideScroll:!1,trapFocus:!1,onOpenAutoFocus:A=>{var t;l.isUsingKeyboardRef.current&&(null==(t=o.current)||t.focus()),A.preventDefault()},onCloseAutoFocus:A=>A.preventDefault(),onFocusOutside:composeEventHandlers(A.onFocusOutside,A=>{A.target!==i.trigger&&n.onOpenChange(!1)}),onEscapeKeyDown:composeEventHandlers(A.onEscapeKeyDown,A=>{l.onClose(),A.preventDefault()}),onKeyDown:composeEventHandlers(A.onKeyDown,A=>{var t;const e=A.currentTarget.contains(A.target),a=SUB_CLOSE_KEYS[l.dir].includes(A.key);e&&a&&(n.onOpenChange(!1),null==(t=i.trigger)||t.focus(),A.preventDefault())})})})})})});function getOpenState(A){return A?\"open\":\"closed\"}function isIndeterminate(A){return\"indeterminate\"===A}function getCheckedState(A){return isIndeterminate(A)?\"indeterminate\":A?\"checked\":\"unchecked\"}function focusFirst(A){const t=document.activeElement;for(const e of A){if(e===t)return;if(e.focus(),document.activeElement!==t)return}}function wrapArray(A,t){return A.map((e,a)=>A[(t+a)%A.length])}function getNextMatch(A,t,e){const a=t.length>1&&Array.from(t).every(A=>A===t[0])?t[0]:t,r=e?A.indexOf(e):-1;let n=wrapArray(A,Math.max(r,0));1===a.length&&(n=n.filter(A=>A!==e));const l=n.find(A=>A.toLowerCase().startsWith(a.toLowerCase()));return l!==e?l:void 0}function isPointInPolygon(A,t){const{x:e,y:a}=A;let r=!1;for(let n=0,l=t.length-1;n<t.length;l=n++){const A=t[n],i=t[l],o=A.x,s=A.y,p=i.x,c=i.y;s>a!=c>a&&e<(p-o)*(a-s)/(c-s)+o&&(r=!r)}return r}function isPointerInGraceArea(A,t){if(!t)return!1;return isPointInPolygon({x:A.clientX,y:A.clientY},t)}function whenMouse(A){return t=>\"mouse\"===t.pointerType?A(t):void 0}MenuSubContent.displayName=SUB_CONTENT_NAME$1;var Root3=Menu,Anchor2=MenuAnchor,Portal=MenuPortal,Content2$1=MenuContent,Group=MenuGroup,Label=MenuLabel,Item2$1=MenuItem,CheckboxItem=MenuCheckboxItem,RadioGroup=MenuRadioGroup,RadioItem=MenuRadioItem,ItemIndicator=MenuItemIndicator,Separator=MenuSeparator,Arrow2=MenuArrow,Sub=MenuSub,SubTrigger=MenuSubTrigger,SubContent=MenuSubContent,DROPDOWN_MENU_NAME=\"DropdownMenu\",[createDropdownMenuContext]=createContextScope(DROPDOWN_MENU_NAME,[createMenuScope]),useMenuScope=createMenuScope(),[DropdownMenuProvider,useDropdownMenuContext]=createDropdownMenuContext(DROPDOWN_MENU_NAME),DropdownMenu$1=A=>{const{__scopeDropdownMenu:t,children:e,dir:a,open:r,defaultOpen:n,onOpenChange:l,modal:i=!0}=A,o=useMenuScope(t),s=reactExports.useRef(null),[p,c]=useControllableState({prop:r,defaultProp:n??!1,onChange:l,caller:DROPDOWN_MENU_NAME});return jsxRuntimeExports.jsx(DropdownMenuProvider,{scope:t,triggerId:useId(),triggerRef:s,contentId:useId(),open:p,onOpenChange:c,onOpenToggle:reactExports.useCallback(()=>c(A=>!A),[c]),modal:i,children:jsxRuntimeExports.jsx(Root3,{...o,open:p,onOpenChange:c,dir:a,modal:i,children:e})})};DropdownMenu$1.displayName=DROPDOWN_MENU_NAME;var TRIGGER_NAME=\"DropdownMenuTrigger\",DropdownMenuTrigger$1=reactExports.forwardRef((A,t)=>{const{__scopeDropdownMenu:e,disabled:a=!1,...r}=A,n=useDropdownMenuContext(TRIGGER_NAME,e),l=useMenuScope(e);return jsxRuntimeExports.jsx(Anchor2,{asChild:!0,...l,children:jsxRuntimeExports.jsx(Primitive.button,{type:\"button\",id:n.triggerId,\"aria-haspopup\":\"menu\",\"aria-expanded\":n.open,\"aria-controls\":n.open?n.contentId:void 0,\"data-state\":n.open?\"open\":\"closed\",\"data-disabled\":a?\"\":void 0,disabled:a,...r,ref:composeRefs(t,n.triggerRef),onPointerDown:composeEventHandlers(A.onPointerDown,A=>{a||0!==A.button||!1!==A.ctrlKey||(n.onOpenToggle(),n.open||A.preventDefault())}),onKeyDown:composeEventHandlers(A.onKeyDown,A=>{a||([\"Enter\",\" \"].includes(A.key)&&n.onOpenToggle(),\"ArrowDown\"===A.key&&n.onOpenChange(!0),[\"Enter\",\" \",\"ArrowDown\"].includes(A.key)&&A.preventDefault())})})})});DropdownMenuTrigger$1.displayName=TRIGGER_NAME;var PORTAL_NAME=\"DropdownMenuPortal\",DropdownMenuPortal$1=A=>{const{__scopeDropdownMenu:t,...e}=A,a=useMenuScope(t);return jsxRuntimeExports.jsx(Portal,{...a,...e})};DropdownMenuPortal$1.displayName=PORTAL_NAME;var CONTENT_NAME=\"DropdownMenuContent\",DropdownMenuContent$1=reactExports.forwardRef((A,t)=>{const{__scopeDropdownMenu:e,...a}=A,r=useDropdownMenuContext(CONTENT_NAME,e),n=useMenuScope(e),l=reactExports.useRef(!1);return jsxRuntimeExports.jsx(Content2$1,{id:r.contentId,\"aria-labelledby\":r.triggerId,...n,...a,ref:t,onCloseAutoFocus:composeEventHandlers(A.onCloseAutoFocus,A=>{var t;l.current||null==(t=r.triggerRef.current)||t.focus(),l.current=!1,A.preventDefault()}),onInteractOutside:composeEventHandlers(A.onInteractOutside,A=>{const t=A.detail.originalEvent,e=0===t.button&&!0===t.ctrlKey,a=2===t.button||e;r.modal&&!a||(l.current=!0)}),style:{...A.style,\"--radix-dropdown-menu-content-transform-origin\":\"var(--radix-popper-transform-origin)\",\"--radix-dropdown-menu-content-available-width\":\"var(--radix-popper-available-width)\",\"--radix-dropdown-menu-content-available-height\":\"var(--radix-popper-available-height)\",\"--radix-dropdown-menu-trigger-width\":\"var(--radix-popper-anchor-width)\",\"--radix-dropdown-menu-trigger-height\":\"var(--radix-popper-anchor-height)\"}})});DropdownMenuContent$1.displayName=CONTENT_NAME;var GROUP_NAME=\"DropdownMenuGroup\",DropdownMenuGroup$1=reactExports.forwardRef((A,t)=>{const{__scopeDropdownMenu:e,...a}=A,r=useMenuScope(e);return jsxRuntimeExports.jsx(Group,{...r,...a,ref:t})});DropdownMenuGroup$1.displayName=GROUP_NAME;var LABEL_NAME=\"DropdownMenuLabel\",DropdownMenuLabel$1=reactExports.forwardRef((A,t)=>{const{__scopeDropdownMenu:e,...a}=A,r=useMenuScope(e);return jsxRuntimeExports.jsx(Label,{...r,...a,ref:t})});DropdownMenuLabel$1.displayName=LABEL_NAME;var ITEM_NAME=\"DropdownMenuItem\",DropdownMenuItem$1=reactExports.forwardRef((A,t)=>{const{__scopeDropdownMenu:e,...a}=A,r=useMenuScope(e);return jsxRuntimeExports.jsx(Item2$1,{...r,...a,ref:t})});DropdownMenuItem$1.displayName=ITEM_NAME;var CHECKBOX_ITEM_NAME=\"DropdownMenuCheckboxItem\",DropdownMenuCheckboxItem$1=reactExports.forwardRef((A,t)=>{const{__scopeDropdownMenu:e,...a}=A,r=useMenuScope(e);return jsxRuntimeExports.jsx(CheckboxItem,{...r,...a,ref:t})});DropdownMenuCheckboxItem$1.displayName=CHECKBOX_ITEM_NAME;var RADIO_GROUP_NAME=\"DropdownMenuRadioGroup\",DropdownMenuRadioGroup$1=reactExports.forwardRef((A,t)=>{const{__scopeDropdownMenu:e,...a}=A,r=useMenuScope(e);return jsxRuntimeExports.jsx(RadioGroup,{...r,...a,ref:t})});DropdownMenuRadioGroup$1.displayName=RADIO_GROUP_NAME;var RADIO_ITEM_NAME=\"DropdownMenuRadioItem\",DropdownMenuRadioItem$1=reactExports.forwardRef((A,t)=>{const{__scopeDropdownMenu:e,...a}=A,r=useMenuScope(e);return jsxRuntimeExports.jsx(RadioItem,{...r,...a,ref:t})});DropdownMenuRadioItem$1.displayName=RADIO_ITEM_NAME;var INDICATOR_NAME=\"DropdownMenuItemIndicator\",DropdownMenuItemIndicator=reactExports.forwardRef((A,t)=>{const{__scopeDropdownMenu:e,...a}=A,r=useMenuScope(e);return jsxRuntimeExports.jsx(ItemIndicator,{...r,...a,ref:t})});DropdownMenuItemIndicator.displayName=INDICATOR_NAME;var SEPARATOR_NAME=\"DropdownMenuSeparator\",DropdownMenuSeparator$1=reactExports.forwardRef((A,t)=>{const{__scopeDropdownMenu:e,...a}=A,r=useMenuScope(e);return jsxRuntimeExports.jsx(Separator,{...r,...a,ref:t})});DropdownMenuSeparator$1.displayName=SEPARATOR_NAME;var ARROW_NAME=\"DropdownMenuArrow\",DropdownMenuArrow=reactExports.forwardRef((A,t)=>{const{__scopeDropdownMenu:e,...a}=A,r=useMenuScope(e);return jsxRuntimeExports.jsx(Arrow2,{...r,...a,ref:t})});DropdownMenuArrow.displayName=ARROW_NAME;var DropdownMenuSub$1=A=>{const{__scopeDropdownMenu:t,children:e,open:a,onOpenChange:r,defaultOpen:n}=A,l=useMenuScope(t),[i,o]=useControllableState({prop:a,defaultProp:n??!1,onChange:r,caller:\"DropdownMenuSub\"});return jsxRuntimeExports.jsx(Sub,{...l,open:i,onOpenChange:o,children:e})},SUB_TRIGGER_NAME=\"DropdownMenuSubTrigger\",DropdownMenuSubTrigger$1=reactExports.forwardRef((A,t)=>{const{__scopeDropdownMenu:e,...a}=A,r=useMenuScope(e);return jsxRuntimeExports.jsx(SubTrigger,{...r,...a,ref:t})});DropdownMenuSubTrigger$1.displayName=SUB_TRIGGER_NAME;var SUB_CONTENT_NAME=\"DropdownMenuSubContent\",DropdownMenuSubContent$1=reactExports.forwardRef((A,t)=>{const{__scopeDropdownMenu:e,...a}=A,r=useMenuScope(e);return jsxRuntimeExports.jsx(SubContent,{...r,...a,ref:t,style:{...A.style,\"--radix-dropdown-menu-content-transform-origin\":\"var(--radix-popper-transform-origin)\",\"--radix-dropdown-menu-content-available-width\":\"var(--radix-popper-available-width)\",\"--radix-dropdown-menu-content-available-height\":\"var(--radix-popper-available-height)\",\"--radix-dropdown-menu-trigger-width\":\"var(--radix-popper-anchor-width)\",\"--radix-dropdown-menu-trigger-height\":\"var(--radix-popper-anchor-height)\"}})});DropdownMenuSubContent$1.displayName=SUB_CONTENT_NAME;var Root2=DropdownMenu$1,Trigger=DropdownMenuTrigger$1,Portal2=DropdownMenuPortal$1,Content2=DropdownMenuContent$1,Group2=DropdownMenuGroup$1,Label2=DropdownMenuLabel$1,Item2=DropdownMenuItem$1,CheckboxItem2=DropdownMenuCheckboxItem$1,RadioGroup2=DropdownMenuRadioGroup$1,RadioItem2=DropdownMenuRadioItem$1,ItemIndicator2=DropdownMenuItemIndicator,Separator2=DropdownMenuSeparator$1,Sub2=DropdownMenuSub$1,SubTrigger2=DropdownMenuSubTrigger$1,SubContent2=DropdownMenuSubContent$1;const DropdownMenu=Root2,DropdownMenuTrigger=Trigger,DropdownMenuGroup=Group2,DropdownMenuPortal=Portal2,DropdownMenuSub=Sub2,DropdownMenuRadioGroup=RadioGroup2,DropdownMenuSubTrigger=reactExports.forwardRef(({className:A,inset:t,children:e,...a},r)=>jsxRuntimeExports.jsxs(SubTrigger2,{ref:r,className:cn$1(\"flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent\",t&&\"pl-8\",A),...a,children:[e,jsxRuntimeExports.jsx(ChevronRight,{className:\"ml-auto h-4 w-4\"})]}));DropdownMenuSubTrigger.displayName=SubTrigger2.displayName;const DropdownMenuSubContent=reactExports.forwardRef(({className:A,...t},e)=>jsxRuntimeExports.jsx(SubContent2,{ref:e,className:cn$1(\"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",A),...t}));DropdownMenuSubContent.displayName=SubContent2.displayName;const DropdownMenuContent=reactExports.forwardRef(({className:A,sideOffset:t=4,...e},a)=>jsxRuntimeExports.jsx(Portal2,{children:jsxRuntimeExports.jsx(Content2,{ref:a,sideOffset:t,className:cn$1(\"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",A),...e})}));DropdownMenuContent.displayName=Content2.displayName;const DropdownMenuItem=reactExports.forwardRef(({className:A,inset:t,...e},a)=>jsxRuntimeExports.jsx(Item2,{ref:a,className:cn$1(\"relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",t&&\"pl-8\",A),...e}));DropdownMenuItem.displayName=Item2.displayName;const DropdownMenuCheckboxItem=reactExports.forwardRef(({className:A,children:t,checked:e,...a},r)=>jsxRuntimeExports.jsxs(CheckboxItem2,{ref:r,className:cn$1(\"relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",A),checked:e,...a,children:[jsxRuntimeExports.jsx(\"span\",{className:\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\",children:jsxRuntimeExports.jsx(ItemIndicator2,{children:jsxRuntimeExports.jsx(Check,{className:\"h-4 w-4\"})})}),t]}));DropdownMenuCheckboxItem.displayName=CheckboxItem2.displayName;const DropdownMenuRadioItem=reactExports.forwardRef(({className:A,children:t,...e},a)=>jsxRuntimeExports.jsxs(RadioItem2,{ref:a,className:cn$1(\"relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",A),...e,children:[jsxRuntimeExports.jsx(\"span\",{className:\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\",children:jsxRuntimeExports.jsx(ItemIndicator2,{children:jsxRuntimeExports.jsx(Circle,{className:\"h-2 w-2 fill-current\"})})}),t]}));DropdownMenuRadioItem.displayName=RadioItem2.displayName;const DropdownMenuLabel=reactExports.forwardRef(({className:A,inset:t,...e},a)=>jsxRuntimeExports.jsx(Label2,{ref:a,className:cn$1(\"px-2 py-1.5 text-sm font-semibold\",t&&\"pl-8\",A),...e}));DropdownMenuLabel.displayName=Label2.displayName;const DropdownMenuSeparator=reactExports.forwardRef(({className:A,...t},e)=>jsxRuntimeExports.jsx(Separator2,{ref:e,className:cn$1(\"-mx-1 my-1 h-px bg-muted\",A),...t}));DropdownMenuSeparator.displayName=Separator2.displayName;const Input=reactExports.forwardRef(({className:A,type:t,...e},a)=>jsxRuntimeExports.jsx(\"input\",{type:t,className:cn$1(\"flex h-10 w-full rounded-lg border border-input bg-background px-3 py-2 text-base transition-all file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",A),ref:a,...e}));function ColorSchemeSwitcher({className:A,isShare:t=!1}){const{t:e}=useTranslation(),a=useSettingsStore(),r=useShareSettingsStore(),n=t?r:a,{colorScheme:l,setColorScheme:i}=n;return jsxRuntimeExports.jsxs(DropdownMenu,{children:[jsxRuntimeExports.jsx(DropdownMenuTrigger,{asChild:!0,children:jsxRuntimeExports.jsx(Button,{variant:\"ghost\",size:\"icon\",className:cn$1(\"size-9\",A),\"aria-label\":e(\"ui.settings.colorScheme\"),children:jsxRuntimeExports.jsx(Palette,{className:\"size-5\"})})}),jsxRuntimeExports.jsx(DropdownMenuContent,{align:\"end\",className:\"w-48 rounded-xl\",children:jsxRuntimeExports.jsx(DropdownMenuRadioGroup,{value:l,onValueChange:A=>{const t=COLOR_SCHEMES.find(t=>t.value===A);t&&(i(t.value),toast.success(e(\"ui.settings.colorSchemeSwitched\",{scheme:e(t.label)})))},children:COLOR_SCHEMES.map(A=>jsxRuntimeExports.jsxs(DropdownMenuRadioItem,{value:A.value,className:\"rounded-lg cursor-pointer\",children:[jsxRuntimeExports.jsx(\"span\",{className:\"mr-2 flex h-2 w-2 rounded-full\",style:{backgroundColor:A.color}}),e(A.label),l===A.value&&jsxRuntimeExports.jsx(Check,{className:\"ml-auto h-4 w-4\"})]},A.value))})})]})}function LanguageSwitcher({className:A,showText:t=!1,storageKey:e=\"lang\"}){const{t:a}=useTranslation(),r=A=>{changeLang(A,e)};return jsxRuntimeExports.jsxs(DropdownMenu,{children:[jsxRuntimeExports.jsx(DropdownMenuTrigger,{asChild:!0,children:jsxRuntimeExports.jsxs(Button,{variant:\"ghost\",size:t?\"default\":\"icon\",className:A,children:[jsxRuntimeExports.jsx(Languages,{className:t?\"mr-2 h-4 w-4\":\"h-5 w-5\"}),t&&a(\"ui.common.switchLanguage\")]})}),jsxRuntimeExports.jsxs(DropdownMenuContent,{align:\"end\",className:\"max-h-[300px] overflow-y-auto\",children:[jsxRuntimeExports.jsx(DropdownMenuItem,{onClick:()=>r(\"en\"),children:\"🇺🇸 English\"}),jsxRuntimeExports.jsx(DropdownMenuItem,{onClick:()=>r(\"zh-CN\"),children:\"🇨🇳 简体中文\"}),jsxRuntimeExports.jsx(DropdownMenuItem,{onClick:()=>r(\"zh-TW\"),children:\"🇭🇰 繁體中文\"}),jsxRuntimeExports.jsx(DropdownMenuItem,{onClick:()=>r(\"ja\"),children:\"🇯🇵 日本語\"}),jsxRuntimeExports.jsx(DropdownMenuItem,{onClick:()=>r(\"ko\"),children:\"🇰🇷 한국어\"})]})]})}Input.displayName=\"Input\";const handleFontsUpdate=A=>{const t=document.getElementById(\"dynamic-font-link\");t&&t.remove();const e=document.getElementById(\"dynamic-font-style\");if(e&&e.remove(),document.body.style.fontFamily=\"\",!A)return;let a=A;A.includes(\"/\")||A.includes(\"://\")||(a=`/static/fonts/${A}.css`);const r=a,n=a.split(\"?\")[0].split(\"#\")[0],l=n.toLowerCase().endsWith(\".css\")||a.includes(\"fonts.googleapis.com\"),i=/\\.(woff2|woff|ttf|otf)$/i.test(n);if(l){const A=document.createElement(\"link\");if(A.id=\"dynamic-font-link\",A.rel=\"stylesheet\",A.href=r,A.crossOrigin=\"anonymous\",document.head.appendChild(A),r.includes(\"fonts.googleapis.com\")){const A=r.match(/family=([^&:]+)/);if(A){const t=decodeURIComponent(A[1]).replace(/\\+/g,\" \");document.body.style.fontFamily=`'${t}', sans-serif`}}}else if(i){const A=document.createElement(\"style\");A.id=\"dynamic-font-style\";const t=\"DynamicCustomFont\";A.textContent=`\\n        @font-face {\\n          font-family: '${t}';\\n          src: url('${r}');\\n          font-weight: normal;\\n          font-style: normal;\\n          font-display: swap;\\n        }\\n        body { font-family: '${t}', sans-serif !important; }\\n      `,document.head.appendChild(A)}};export{useShareSettingsStore as $,DropdownMenuRadioGroup as A,Button as B,ChevronRight as C,Description as D,DropdownMenuRadioItem as E,DropdownMenuItem as F,useTheme as G,Sun as H,Input as I,ColorSchemeSwitcher as J,LanguageSwitcher as K,Lock as L,Moon as M,AnimatedBackground as N,Overlay as O,Portal$1 as P,handleFontsUpdate as Q,Root$2 as R,SunMoon as S,Title as T,ReactDOM as U,React as V,ThemeProvider as W,X,ConfirmDialogProvider as Y,Toaster as Z,__vitePreload as _,addCacheBuster as a,LoaderCircle as a0,Palette as a1,COLOR_SCHEMES as a2,Languages as a3,changeLang as a4,Check as a5,CircleCheck as a6,useConfirmDialog as a7,getBrowserLang as a8,getDefaultExportFromCjs as a9,CircleX as aA,useComposedRefs as aB,useSize as aC,createPopperScope as aD,Root2$1 as aE,createCollection as aF,useLayoutEffect2 as aG,Anchor as aH,Portal$2 as aI,useCallbackRef$1 as aJ,hideOthers as aK,useFocusGuards as aL,ReactRemoveScroll as aM,createSlot$1 as aN,FocusScope as aO,DismissableLayer as aP,Content as aQ,Arrow as aR,utils as aS,requireReact as aa,requireReactDom as ab,TriangleAlert as ac,CircleHelp as ad,Info as ae,useDirection as af,useControllableState as ag,createContextScope as ah,useId as ai,Primitive as aj,createRovingFocusGroupScope as ak,Root as al,Item as am,composeEventHandlers as an,Presence as ao,CircleCheckBig as ap,DropdownMenuLabel as aq,Root2$2 as ar,Portal2$1 as as,Content2$2 as at,Title2 as au,Description2 as av,Cancel as aw,buttonVariants as ax,Action as ay,Overlay2 as az,buildApiHeaders as b,createLucideIcon as c,create as d,env as e,cn$1 as f,reactDomExports as g,Content$1 as h,Close as i,jsxRuntimeExports as j,createSlot as k,cva as l,useSettingsStore as m,DropdownMenu as n,DropdownMenuTrigger as o,persist as p,DropdownMenuContent as q,reactExports as r,DropdownMenuSeparator as s,toast as t,useTranslation as u,DropdownMenuGroup as v,DropdownMenuSub as w,DropdownMenuSubTrigger as x,DropdownMenuPortal as y,DropdownMenuSubContent as z};\n"
  },
  {
    "path": "frontend/assets/format-CdHm7RWL.js",
    "content": "import{c as t}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const e=t(\"FileText\",[[\"path\",{d:\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\",key:\"1rqfz7\"}],[\"path\",{d:\"M14 2v4a2 2 0 0 0 2 2h4\",key:\"tnqrlb\"}],[\"path\",{d:\"M10 9H8\",key:\"b1mrlr\"}],[\"path\",{d:\"M16 13H8\",key:\"t4e002\"}],[\"path\",{d:\"M16 17H8\",key:\"z1uh3a\"}]]),n=6048e5,r=Symbol.for(\"constructDateFrom\");function a(t,e){return\"function\"==typeof t?t(e):t&&\"object\"==typeof t&&r in t?t[r](e):t instanceof Date?new t.constructor(e):new Date(e)}function o(t,e){return a(e||t,t)}let i={};function u(){return i}function s(t,e){var n,r,a,i;const s=u(),c=(null==e?void 0:e.weekStartsOn)??(null==(r=null==(n=null==e?void 0:e.locale)?void 0:n.options)?void 0:r.weekStartsOn)??s.weekStartsOn??(null==(i=null==(a=s.locale)?void 0:a.options)?void 0:i.weekStartsOn)??0,d=o(t,null==e?void 0:e.in),l=d.getDay(),h=(l<c?7:0)+l-c;return d.setDate(d.getDate()-h),d.setHours(0,0,0,0),d}function c(t,e){return s(t,{...e,weekStartsOn:1})}function d(t,e){const n=o(t,null==e?void 0:e.in),r=n.getFullYear(),i=a(n,0);i.setFullYear(r+1,0,4),i.setHours(0,0,0,0);const u=c(i),s=a(n,0);s.setFullYear(r,0,4),s.setHours(0,0,0,0);const d=c(s);return n.getTime()>=u.getTime()?r+1:n.getTime()>=d.getTime()?r:r-1}function l(t){const e=o(t),n=new Date(Date.UTC(e.getFullYear(),e.getMonth(),e.getDate(),e.getHours(),e.getMinutes(),e.getSeconds(),e.getMilliseconds()));return n.setUTCFullYear(e.getFullYear()),+t-+n}function h(t,e){const n=o(t,null==e?void 0:e.in);return n.setHours(0,0,0,0),n}function m(t,e,n){const[r,o]=function(t,...e){const n=a.bind(null,e.find(t=>\"object\"==typeof t));return e.map(n)}(null==n||n.in,t,e),i=h(r),u=h(o),s=+i-l(i),c=+u-l(u);return Math.round((s-c)/864e5)}function f(t){return!(!((e=t)instanceof Date||\"object\"==typeof e&&\"[object Date]\"===Object.prototype.toString.call(e))&&\"number\"!=typeof t||isNaN(+o(t)));var e}const g={lessThanXSeconds:{one:\"less than a second\",other:\"less than {{count}} seconds\"},xSeconds:{one:\"1 second\",other:\"{{count}} seconds\"},halfAMinute:\"half a minute\",lessThanXMinutes:{one:\"less than a minute\",other:\"less than {{count}} minutes\"},xMinutes:{one:\"1 minute\",other:\"{{count}} minutes\"},aboutXHours:{one:\"about 1 hour\",other:\"about {{count}} hours\"},xHours:{one:\"1 hour\",other:\"{{count}} hours\"},xDays:{one:\"1 day\",other:\"{{count}} days\"},aboutXWeeks:{one:\"about 1 week\",other:\"about {{count}} weeks\"},xWeeks:{one:\"1 week\",other:\"{{count}} weeks\"},aboutXMonths:{one:\"about 1 month\",other:\"about {{count}} months\"},xMonths:{one:\"1 month\",other:\"{{count}} months\"},aboutXYears:{one:\"about 1 year\",other:\"about {{count}} years\"},xYears:{one:\"1 year\",other:\"{{count}} years\"},overXYears:{one:\"over 1 year\",other:\"over {{count}} years\"},almostXYears:{one:\"almost 1 year\",other:\"almost {{count}} years\"}};function w(t){return(e={})=>{const n=e.width?String(e.width):t.defaultWidth;return t.formats[n]||t.formats[t.defaultWidth]}}const b={date:w({formats:{full:\"EEEE, MMMM do, y\",long:\"MMMM do, y\",medium:\"MMM d, y\",short:\"MM/dd/yyyy\"},defaultWidth:\"full\"}),time:w({formats:{full:\"h:mm:ss a zzzz\",long:\"h:mm:ss a z\",medium:\"h:mm:ss a\",short:\"h:mm a\"},defaultWidth:\"full\"}),dateTime:w({formats:{full:\"{{date}} 'at' {{time}}\",long:\"{{date}} 'at' {{time}}\",medium:\"{{date}}, {{time}}\",short:\"{{date}}, {{time}}\"},defaultWidth:\"full\"})},y={lastWeek:\"'last' eeee 'at' p\",yesterday:\"'yesterday at' p\",today:\"'today at' p\",tomorrow:\"'tomorrow at' p\",nextWeek:\"eeee 'at' p\",other:\"P\"};function v(t){return(e,n)=>{let r;if(\"formatting\"===((null==n?void 0:n.context)?String(n.context):\"standalone\")&&t.formattingValues){const e=t.defaultFormattingWidth||t.defaultWidth,a=(null==n?void 0:n.width)?String(n.width):e;r=t.formattingValues[a]||t.formattingValues[e]}else{const e=t.defaultWidth,a=(null==n?void 0:n.width)?String(n.width):t.defaultWidth;r=t.values[a]||t.values[e]}return r[t.argumentCallback?t.argumentCallback(e):e]}}function p(t){return(e,n={})=>{const r=n.width,a=r&&t.matchPatterns[r]||t.matchPatterns[t.defaultMatchWidth],o=e.match(a);if(!o)return null;const i=o[0],u=r&&t.parsePatterns[r]||t.parsePatterns[t.defaultParseWidth],s=Array.isArray(u)?function(t,e){for(let n=0;n<t.length;n++)if(e(t[n]))return n;return}(u,t=>t.test(i)):function(t,e){for(const n in t)if(Object.prototype.hasOwnProperty.call(t,n)&&e(t[n]))return n;return}(u,t=>t.test(i));let c;c=t.valueCallback?t.valueCallback(s):s,c=n.valueCallback?n.valueCallback(c):c;return{value:c,rest:e.slice(i.length)}}}var M;const k={code:\"en-US\",formatDistance:(t,e,n)=>{let r;const a=g[t];return r=\"string\"==typeof a?a:1===e?a.one:a.other.replace(\"{{count}}\",e.toString()),(null==n?void 0:n.addSuffix)?n.comparison&&n.comparison>0?\"in \"+r:r+\" ago\":r},formatLong:b,formatRelative:(t,e,n,r)=>y[t],localize:{ordinalNumber:(t,e)=>{const n=Number(t),r=n%100;if(r>20||r<10)switch(r%10){case 1:return n+\"st\";case 2:return n+\"nd\";case 3:return n+\"rd\"}return n+\"th\"},era:v({values:{narrow:[\"B\",\"A\"],abbreviated:[\"BC\",\"AD\"],wide:[\"Before Christ\",\"Anno Domini\"]},defaultWidth:\"wide\"}),quarter:v({values:{narrow:[\"1\",\"2\",\"3\",\"4\"],abbreviated:[\"Q1\",\"Q2\",\"Q3\",\"Q4\"],wide:[\"1st quarter\",\"2nd quarter\",\"3rd quarter\",\"4th quarter\"]},defaultWidth:\"wide\",argumentCallback:t=>t-1}),month:v({values:{narrow:[\"J\",\"F\",\"M\",\"A\",\"M\",\"J\",\"J\",\"A\",\"S\",\"O\",\"N\",\"D\"],abbreviated:[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"],wide:[\"January\",\"February\",\"March\",\"April\",\"May\",\"June\",\"July\",\"August\",\"September\",\"October\",\"November\",\"December\"]},defaultWidth:\"wide\"}),day:v({values:{narrow:[\"S\",\"M\",\"T\",\"W\",\"T\",\"F\",\"S\"],short:[\"Su\",\"Mo\",\"Tu\",\"We\",\"Th\",\"Fr\",\"Sa\"],abbreviated:[\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"],wide:[\"Sunday\",\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\"]},defaultWidth:\"wide\"}),dayPeriod:v({values:{narrow:{am:\"a\",pm:\"p\",midnight:\"mi\",noon:\"n\",morning:\"morning\",afternoon:\"afternoon\",evening:\"evening\",night:\"night\"},abbreviated:{am:\"AM\",pm:\"PM\",midnight:\"midnight\",noon:\"noon\",morning:\"morning\",afternoon:\"afternoon\",evening:\"evening\",night:\"night\"},wide:{am:\"a.m.\",pm:\"p.m.\",midnight:\"midnight\",noon:\"noon\",morning:\"morning\",afternoon:\"afternoon\",evening:\"evening\",night:\"night\"}},defaultWidth:\"wide\",formattingValues:{narrow:{am:\"a\",pm:\"p\",midnight:\"mi\",noon:\"n\",morning:\"in the morning\",afternoon:\"in the afternoon\",evening:\"in the evening\",night:\"at night\"},abbreviated:{am:\"AM\",pm:\"PM\",midnight:\"midnight\",noon:\"noon\",morning:\"in the morning\",afternoon:\"in the afternoon\",evening:\"in the evening\",night:\"at night\"},wide:{am:\"a.m.\",pm:\"p.m.\",midnight:\"midnight\",noon:\"noon\",morning:\"in the morning\",afternoon:\"in the afternoon\",evening:\"in the evening\",night:\"at night\"}},defaultFormattingWidth:\"wide\"})},match:{ordinalNumber:(M={matchPattern:/^(\\d+)(th|st|nd|rd)?/i,parsePattern:/\\d+/i,valueCallback:t=>parseInt(t,10)},(t,e={})=>{const n=t.match(M.matchPattern);if(!n)return null;const r=n[0],a=t.match(M.parsePattern);if(!a)return null;let o=M.valueCallback?M.valueCallback(a[0]):a[0];return o=e.valueCallback?e.valueCallback(o):o,{value:o,rest:t.slice(r.length)}}),era:p({matchPatterns:{narrow:/^(b|a)/i,abbreviated:/^(b\\.?\\s?c\\.?|b\\.?\\s?c\\.?\\s?e\\.?|a\\.?\\s?d\\.?|c\\.?\\s?e\\.?)/i,wide:/^(before christ|before common era|anno domini|common era)/i},defaultMatchWidth:\"wide\",parsePatterns:{any:[/^b/i,/^(a|c)/i]},defaultParseWidth:\"any\"}),quarter:p({matchPatterns:{narrow:/^[1234]/i,abbreviated:/^q[1234]/i,wide:/^[1234](th|st|nd|rd)? quarter/i},defaultMatchWidth:\"wide\",parsePatterns:{any:[/1/i,/2/i,/3/i,/4/i]},defaultParseWidth:\"any\",valueCallback:t=>t+1}),month:p({matchPatterns:{narrow:/^[jfmasond]/i,abbreviated:/^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i,wide:/^(january|february|march|april|may|june|july|august|september|october|november|december)/i},defaultMatchWidth:\"wide\",parsePatterns:{narrow:[/^j/i,/^f/i,/^m/i,/^a/i,/^m/i,/^j/i,/^j/i,/^a/i,/^s/i,/^o/i,/^n/i,/^d/i],any:[/^ja/i,/^f/i,/^mar/i,/^ap/i,/^may/i,/^jun/i,/^jul/i,/^au/i,/^s/i,/^o/i,/^n/i,/^d/i]},defaultParseWidth:\"any\"}),day:p({matchPatterns:{narrow:/^[smtwf]/i,short:/^(su|mo|tu|we|th|fr|sa)/i,abbreviated:/^(sun|mon|tue|wed|thu|fri|sat)/i,wide:/^(sunday|monday|tuesday|wednesday|thursday|friday|saturday)/i},defaultMatchWidth:\"wide\",parsePatterns:{narrow:[/^s/i,/^m/i,/^t/i,/^w/i,/^t/i,/^f/i,/^s/i],any:[/^su/i,/^m/i,/^tu/i,/^w/i,/^th/i,/^f/i,/^sa/i]},defaultParseWidth:\"any\"}),dayPeriod:p({matchPatterns:{narrow:/^(a|p|mi|n|(in the|at) (morning|afternoon|evening|night))/i,any:/^([ap]\\.?\\s?m\\.?|midnight|noon|(in the|at) (morning|afternoon|evening|night))/i},defaultMatchWidth:\"any\",parsePatterns:{any:{am:/^a/i,pm:/^p/i,midnight:/^mi/i,noon:/^no/i,morning:/morning/i,afternoon:/afternoon/i,evening:/evening/i,night:/night/i}},defaultParseWidth:\"any\"})},options:{weekStartsOn:0,firstWeekContainsDate:1}};function x(t,e){const n=o(t,null==e?void 0:e.in),r=m(n,function(t,e){const n=o(t,null==e?void 0:e.in);return n.setFullYear(n.getFullYear(),0,1),n.setHours(0,0,0,0),n}(n));return r+1}function P(t,e){const r=o(t,null==e?void 0:e.in),i=+c(r)-+function(t,e){const n=d(t,e),r=a(t,0);return r.setFullYear(n,0,4),r.setHours(0,0,0,0),c(r)}(r);return Math.round(i/n)+1}function S(t,e){var n,r,i,c;const d=o(t,null==e?void 0:e.in),l=d.getFullYear(),h=u(),m=(null==e?void 0:e.firstWeekContainsDate)??(null==(r=null==(n=null==e?void 0:e.locale)?void 0:n.options)?void 0:r.firstWeekContainsDate)??h.firstWeekContainsDate??(null==(c=null==(i=h.locale)?void 0:i.options)?void 0:c.firstWeekContainsDate)??1,f=a((null==e?void 0:e.in)||t,0);f.setFullYear(l+1,0,m),f.setHours(0,0,0,0);const g=s(f,e),w=a((null==e?void 0:e.in)||t,0);w.setFullYear(l,0,m),w.setHours(0,0,0,0);const b=s(w,e);return+d>=+g?l+1:+d>=+b?l:l-1}function W(t,e){const r=o(t,null==e?void 0:e.in),i=+s(r,e)-+function(t,e){var n,r,o,i;const c=u(),d=(null==e?void 0:e.firstWeekContainsDate)??(null==(r=null==(n=null==e?void 0:e.locale)?void 0:n.options)?void 0:r.firstWeekContainsDate)??c.firstWeekContainsDate??(null==(i=null==(o=c.locale)?void 0:o.options)?void 0:i.firstWeekContainsDate)??1,l=S(t,e),h=a((null==e?void 0:e.in)||t,0);return h.setFullYear(l,0,d),h.setHours(0,0,0,0),s(h,e)}(r,e);return Math.round(i/n)+1}function D(t,e){return(t<0?\"-\":\"\")+Math.abs(t).toString().padStart(e,\"0\")}const T={y(t,e){const n=t.getFullYear(),r=n>0?n:1-n;return D(\"yy\"===e?r%100:r,e.length)},M(t,e){const n=t.getMonth();return\"M\"===e?String(n+1):D(n+1,2)},d:(t,e)=>D(t.getDate(),e.length),a(t,e){const n=t.getHours()/12>=1?\"pm\":\"am\";switch(e){case\"a\":case\"aa\":return n.toUpperCase();case\"aaa\":return n;case\"aaaaa\":return n[0];default:return\"am\"===n?\"a.m.\":\"p.m.\"}},h:(t,e)=>D(t.getHours()%12||12,e.length),H:(t,e)=>D(t.getHours(),e.length),m:(t,e)=>D(t.getMinutes(),e.length),s:(t,e)=>D(t.getSeconds(),e.length),S(t,e){const n=e.length,r=t.getMilliseconds();return D(Math.trunc(r*Math.pow(10,n-3)),e.length)}},Y=\"midnight\",C=\"noon\",q=\"morning\",H=\"afternoon\",F=\"evening\",O=\"night\",N={G:function(t,e,n){const r=t.getFullYear()>0?1:0;switch(e){case\"G\":case\"GG\":case\"GGG\":return n.era(r,{width:\"abbreviated\"});case\"GGGGG\":return n.era(r,{width:\"narrow\"});default:return n.era(r,{width:\"wide\"})}},y:function(t,e,n){if(\"yo\"===e){const e=t.getFullYear(),r=e>0?e:1-e;return n.ordinalNumber(r,{unit:\"year\"})}return T.y(t,e)},Y:function(t,e,n,r){const a=S(t,r),o=a>0?a:1-a;if(\"YY\"===e){return D(o%100,2)}return\"Yo\"===e?n.ordinalNumber(o,{unit:\"year\"}):D(o,e.length)},R:function(t,e){return D(d(t),e.length)},u:function(t,e){return D(t.getFullYear(),e.length)},Q:function(t,e,n){const r=Math.ceil((t.getMonth()+1)/3);switch(e){case\"Q\":return String(r);case\"QQ\":return D(r,2);case\"Qo\":return n.ordinalNumber(r,{unit:\"quarter\"});case\"QQQ\":return n.quarter(r,{width:\"abbreviated\",context:\"formatting\"});case\"QQQQQ\":return n.quarter(r,{width:\"narrow\",context:\"formatting\"});default:return n.quarter(r,{width:\"wide\",context:\"formatting\"})}},q:function(t,e,n){const r=Math.ceil((t.getMonth()+1)/3);switch(e){case\"q\":return String(r);case\"qq\":return D(r,2);case\"qo\":return n.ordinalNumber(r,{unit:\"quarter\"});case\"qqq\":return n.quarter(r,{width:\"abbreviated\",context:\"standalone\"});case\"qqqqq\":return n.quarter(r,{width:\"narrow\",context:\"standalone\"});default:return n.quarter(r,{width:\"wide\",context:\"standalone\"})}},M:function(t,e,n){const r=t.getMonth();switch(e){case\"M\":case\"MM\":return T.M(t,e);case\"Mo\":return n.ordinalNumber(r+1,{unit:\"month\"});case\"MMM\":return n.month(r,{width:\"abbreviated\",context:\"formatting\"});case\"MMMMM\":return n.month(r,{width:\"narrow\",context:\"formatting\"});default:return n.month(r,{width:\"wide\",context:\"formatting\"})}},L:function(t,e,n){const r=t.getMonth();switch(e){case\"L\":return String(r+1);case\"LL\":return D(r+1,2);case\"Lo\":return n.ordinalNumber(r+1,{unit:\"month\"});case\"LLL\":return n.month(r,{width:\"abbreviated\",context:\"standalone\"});case\"LLLLL\":return n.month(r,{width:\"narrow\",context:\"standalone\"});default:return n.month(r,{width:\"wide\",context:\"standalone\"})}},w:function(t,e,n,r){const a=W(t,r);return\"wo\"===e?n.ordinalNumber(a,{unit:\"week\"}):D(a,e.length)},I:function(t,e,n){const r=P(t);return\"Io\"===e?n.ordinalNumber(r,{unit:\"week\"}):D(r,e.length)},d:function(t,e,n){return\"do\"===e?n.ordinalNumber(t.getDate(),{unit:\"date\"}):T.d(t,e)},D:function(t,e,n){const r=x(t);return\"Do\"===e?n.ordinalNumber(r,{unit:\"dayOfYear\"}):D(r,e.length)},E:function(t,e,n){const r=t.getDay();switch(e){case\"E\":case\"EE\":case\"EEE\":return n.day(r,{width:\"abbreviated\",context:\"formatting\"});case\"EEEEE\":return n.day(r,{width:\"narrow\",context:\"formatting\"});case\"EEEEEE\":return n.day(r,{width:\"short\",context:\"formatting\"});default:return n.day(r,{width:\"wide\",context:\"formatting\"})}},e:function(t,e,n,r){const a=t.getDay(),o=(a-r.weekStartsOn+8)%7||7;switch(e){case\"e\":return String(o);case\"ee\":return D(o,2);case\"eo\":return n.ordinalNumber(o,{unit:\"day\"});case\"eee\":return n.day(a,{width:\"abbreviated\",context:\"formatting\"});case\"eeeee\":return n.day(a,{width:\"narrow\",context:\"formatting\"});case\"eeeeee\":return n.day(a,{width:\"short\",context:\"formatting\"});default:return n.day(a,{width:\"wide\",context:\"formatting\"})}},c:function(t,e,n,r){const a=t.getDay(),o=(a-r.weekStartsOn+8)%7||7;switch(e){case\"c\":return String(o);case\"cc\":return D(o,e.length);case\"co\":return n.ordinalNumber(o,{unit:\"day\"});case\"ccc\":return n.day(a,{width:\"abbreviated\",context:\"standalone\"});case\"ccccc\":return n.day(a,{width:\"narrow\",context:\"standalone\"});case\"cccccc\":return n.day(a,{width:\"short\",context:\"standalone\"});default:return n.day(a,{width:\"wide\",context:\"standalone\"})}},i:function(t,e,n){const r=t.getDay(),a=0===r?7:r;switch(e){case\"i\":return String(a);case\"ii\":return D(a,e.length);case\"io\":return n.ordinalNumber(a,{unit:\"day\"});case\"iii\":return n.day(r,{width:\"abbreviated\",context:\"formatting\"});case\"iiiii\":return n.day(r,{width:\"narrow\",context:\"formatting\"});case\"iiiiii\":return n.day(r,{width:\"short\",context:\"formatting\"});default:return n.day(r,{width:\"wide\",context:\"formatting\"})}},a:function(t,e,n){const r=t.getHours()/12>=1?\"pm\":\"am\";switch(e){case\"a\":case\"aa\":return n.dayPeriod(r,{width:\"abbreviated\",context:\"formatting\"});case\"aaa\":return n.dayPeriod(r,{width:\"abbreviated\",context:\"formatting\"}).toLowerCase();case\"aaaaa\":return n.dayPeriod(r,{width:\"narrow\",context:\"formatting\"});default:return n.dayPeriod(r,{width:\"wide\",context:\"formatting\"})}},b:function(t,e,n){const r=t.getHours();let a;switch(a=12===r?C:0===r?Y:r/12>=1?\"pm\":\"am\",e){case\"b\":case\"bb\":return n.dayPeriod(a,{width:\"abbreviated\",context:\"formatting\"});case\"bbb\":return n.dayPeriod(a,{width:\"abbreviated\",context:\"formatting\"}).toLowerCase();case\"bbbbb\":return n.dayPeriod(a,{width:\"narrow\",context:\"formatting\"});default:return n.dayPeriod(a,{width:\"wide\",context:\"formatting\"})}},B:function(t,e,n){const r=t.getHours();let a;switch(a=r>=17?F:r>=12?H:r>=4?q:O,e){case\"B\":case\"BB\":case\"BBB\":return n.dayPeriod(a,{width:\"abbreviated\",context:\"formatting\"});case\"BBBBB\":return n.dayPeriod(a,{width:\"narrow\",context:\"formatting\"});default:return n.dayPeriod(a,{width:\"wide\",context:\"formatting\"})}},h:function(t,e,n){if(\"ho\"===e){let e=t.getHours()%12;return 0===e&&(e=12),n.ordinalNumber(e,{unit:\"hour\"})}return T.h(t,e)},H:function(t,e,n){return\"Ho\"===e?n.ordinalNumber(t.getHours(),{unit:\"hour\"}):T.H(t,e)},K:function(t,e,n){const r=t.getHours()%12;return\"Ko\"===e?n.ordinalNumber(r,{unit:\"hour\"}):D(r,e.length)},k:function(t,e,n){let r=t.getHours();return 0===r&&(r=24),\"ko\"===e?n.ordinalNumber(r,{unit:\"hour\"}):D(r,e.length)},m:function(t,e,n){return\"mo\"===e?n.ordinalNumber(t.getMinutes(),{unit:\"minute\"}):T.m(t,e)},s:function(t,e,n){return\"so\"===e?n.ordinalNumber(t.getSeconds(),{unit:\"second\"}):T.s(t,e)},S:function(t,e){return T.S(t,e)},X:function(t,e,n){const r=t.getTimezoneOffset();if(0===r)return\"Z\";switch(e){case\"X\":return z(r);case\"XXXX\":case\"XX\":return j(r);default:return j(r,\":\")}},x:function(t,e,n){const r=t.getTimezoneOffset();switch(e){case\"x\":return z(r);case\"xxxx\":case\"xx\":return j(r);default:return j(r,\":\")}},O:function(t,e,n){const r=t.getTimezoneOffset();switch(e){case\"O\":case\"OO\":case\"OOO\":return\"GMT\"+E(r,\":\");default:return\"GMT\"+j(r,\":\")}},z:function(t,e,n){const r=t.getTimezoneOffset();switch(e){case\"z\":case\"zz\":case\"zzz\":return\"GMT\"+E(r,\":\");default:return\"GMT\"+j(r,\":\")}},t:function(t,e,n){return D(Math.trunc(+t/1e3),e.length)},T:function(t,e,n){return D(+t,e.length)}};function E(t,e=\"\"){const n=t>0?\"-\":\"+\",r=Math.abs(t),a=Math.trunc(r/60),o=r%60;return 0===o?n+String(a):n+String(a)+e+D(o,2)}function z(t,e){if(t%60==0){return(t>0?\"-\":\"+\")+D(Math.abs(t)/60,2)}return j(t,e)}function j(t,e=\"\"){const n=t>0?\"-\":\"+\",r=Math.abs(t);return n+D(Math.trunc(r/60),2)+e+D(r%60,2)}const L=(t,e)=>{switch(t){case\"P\":return e.date({width:\"short\"});case\"PP\":return e.date({width:\"medium\"});case\"PPP\":return e.date({width:\"long\"});default:return e.date({width:\"full\"})}},Q=(t,e)=>{switch(t){case\"p\":return e.time({width:\"short\"});case\"pp\":return e.time({width:\"medium\"});case\"ppp\":return e.time({width:\"long\"});default:return e.time({width:\"full\"})}},G={p:Q,P:(t,e)=>{const n=t.match(/(P+)(p+)?/)||[],r=n[1],a=n[2];if(!a)return L(t,e);let o;switch(r){case\"P\":o=e.dateTime({width:\"short\"});break;case\"PP\":o=e.dateTime({width:\"medium\"});break;case\"PPP\":o=e.dateTime({width:\"long\"});break;default:o=e.dateTime({width:\"full\"})}return o.replace(\"{{date}}\",L(r,e)).replace(\"{{time}}\",Q(a,e))}},X=/^D+$/,A=/^Y+$/,B=[\"D\",\"DD\",\"YY\",\"YYYY\"];const $=/[yYQqMLwIdDecihHKkms]o|(\\w)\\1*|''|'(''|[^'])+('|$)|./g,J=/P+p+|P+|p+|''|'(''|[^'])+('|$)|./g,I=/^'([^]*?)'?$/,R=/''/g,U=/[a-zA-Z]/;function V(t,e,n){var r,a,i,s;const c=u(),d=c.locale??k,l=c.firstWeekContainsDate??(null==(a=null==(r=c.locale)?void 0:r.options)?void 0:a.firstWeekContainsDate)??1,h=c.weekStartsOn??(null==(s=null==(i=c.locale)?void 0:i.options)?void 0:s.weekStartsOn)??0,m=o(t,null==n?void 0:n.in);if(!f(m))throw new RangeError(\"Invalid time value\");let g=e.match(J).map(t=>{const e=t[0];if(\"p\"===e||\"P\"===e){return(0,G[e])(t,d.formatLong)}return t}).join(\"\").match($).map(t=>{if(\"''\"===t)return{isToken:!1,value:\"'\"};const e=t[0];if(\"'\"===e)return{isToken:!1,value:K(t)};if(N[e])return{isToken:!0,value:t};if(e.match(U))throw new RangeError(\"Format string contains an unescaped latin alphabet character `\"+e+\"`\");return{isToken:!1,value:t}});d.localize.preprocessor&&(g=d.localize.preprocessor(m,g));const w={firstWeekContainsDate:l,weekStartsOn:h,locale:d};return g.map(n=>{if(!n.isToken)return n.value;const r=n.value;(function(t){return A.test(t)}(r)||function(t){return X.test(t)}(r))&&function(t,e,n){const r=function(t,e,n){const r=\"Y\"===t[0]?\"years\":\"days of the month\";return`Use \\`${t.toLowerCase()}\\` instead of \\`${t}\\` (in \\`${e}\\`) for formatting ${r} to the input \\`${n}\\`; see: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md`}(t,e,n);if(B.includes(t))throw new RangeError(r)}(r,e,String(t));return(0,N[r[0]])(m,r,d.localize,w)}).join(\"\")}function K(t){const e=t.match(I);return e?e[1].replace(R,\"'\"):t}export{e as F,V as f};\n"
  },
  {
    "path": "frontend/assets/git-automation-tBJ0Wppw.js",
    "content": "import{c as e,u as s,r as t,b as a,a as r,e as i,t as n,j as l,a0 as o,f as c,B as d,C as m,a6 as x,I as u,a7 as h}from\"./font-loader-CIrh3KnA.js\";import{T as g,a as p,b as f,c as j,d as b,e as v}from\"./table-D9wbHMTA.js\";import{D as N,a as y,b as w,c as k,C as S,L as C,f as E,G as $}from\"./main-BIi-kGYY.js\";import{C as D}from\"./clock-C9LPHszx.js\";import{C as U}from\"./circle-alert-EFzISefA.js\";import{S as T,a as z,b as G,c as I,d as L}from\"./select-CJF_alSt.js\";import{o as P,b as R,n as M,s as A,u as F,t as O}from\"./zod-B54Zg8Xp.js\";import{C as V}from\"./checkbox-DhTHgmeh.js\";import{a as _,E as H}from\"./eye-DrvrOb4o.js\";import{S as q,P as B}from\"./shield-check-CH_gKEpx.js\";import{R as J}from\"./refresh-cw-BxIJAPy3.js\";import{P as K}from\"./plus-BBfuNxDX.js\";import{H as Q}from\"./history-BseqF3eb.js\";import{P as W}from\"./pencil-DqQhr35g.js\";import{T as X}from\"./trash-2-ad7PiUnC.js\";import{G as Y}from\"./git-branch-B1vNHBXG.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const Z=e(\"Eraser\",[[\"path\",{d:\"m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21\",key:\"182aya\"}],[\"path\",{d:\"M22 21H7\",key:\"t4ddhn\"}],[\"path\",{d:\"m5 11 9 9\",key:\"1mo9qw\"}]]),ee=e(\"GitCommitHorizontal\",[[\"circle\",{cx:\"12\",cy:\"12\",r:\"3\",key:\"1v7zrd\"}],[\"line\",{x1:\"3\",x2:\"9\",y1:\"12\",y2:\"12\",key:\"1dyftd\"}],[\"line\",{x1:\"15\",x2:\"21\",y1:\"12\",y2:\"12\",key:\"oup4p8\"}]]);\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */function se(){const{t:e}=s(),l=localStorage.getItem(\"token\"),o=t.useCallback((e=!0)=>a({token:l,includeContentType:e}),[l]),c=t.useCallback(async s=>{try{const t=await fetch(r(`${i.API_URL}/api/git-sync/configs`),{method:\"GET\",headers:o()});if(!t.ok)throw new Error(e(\"api.git.list.error\"));const a=await t.json();a.code>0&&a.code<=200?s(a.data||[]):n.error(a.message||e(\"api.git.list.error\"))}catch(t){n.error(t instanceof Error?t.message:e(\"api.git.list.error\"))}},[o,e]),d=t.useCallback(async(s,t)=>{try{const a=await fetch(r(`${i.API_URL}/api/git-sync/config`),{method:\"POST\",headers:o(),body:JSON.stringify(s)});if(!a.ok)throw new Error(e(\"api.git.save.error\"));const l=await a.json();l.code>0&&l.code<=200?(n.success(l.message||e(\"api.git.save.success\")),t(l.data)):n.error(l.message||e(\"api.git.save.error\"))}catch(a){n.error(a instanceof Error?a.message:e(\"api.git.save.error\"))}},[o,e]),m=t.useCallback(async(s,t)=>{try{const a=await fetch(r(`${i.API_URL}/api/git-sync/config?id=${s}`),{method:\"DELETE\",headers:o(!1)});if(!a.ok)throw new Error(e(\"api.git.delete.error\"));const l=await a.json();l.code>0&&l.code<=200?(n.success(l.message||e(\"api.git.delete.success\")),t()):n.error(l.message||e(\"api.git.delete.error\"))}catch(a){n.error(a instanceof Error?a.message:e(\"api.git.delete.error\"))}},[o,e]),x=t.useCallback(async(s,t)=>{try{const a=await fetch(r(`${i.API_URL}/api/git-sync/config/execute`),{method:\"POST\",headers:o(),body:JSON.stringify({id:s})});if(!a.ok)throw new Error(e(\"api.git.execute.error\"));const l=await a.json();l.code>0&&l.code<=200?(n.success(l.message||e(\"api.git.execute.success\")),t()):n.error(l.message||e(\"api.git.execute.error\"))}catch(a){n.error(a instanceof Error?a.message:e(\"api.git.execute.error\"))}},[o,e]),u=t.useCallback(async(s,t)=>{try{const a=await fetch(r(`${i.API_URL}/api/git-sync/config/clean?configId=${s}`),{method:\"DELETE\",headers:o(!1)});if(!a.ok)throw new Error(e(\"api.git.clean.error\"));const l=await a.json();l.code>0&&l.code<=200?(n.success(l.message||e(\"api.git.clean.success\")),t()):n.error(l.message||e(\"api.git.clean.error\"))}catch(a){n.error(a instanceof Error?a.message:e(\"api.git.clean.error\"))}},[o,e]),h=t.useCallback(async(s,t,a,l)=>{var c,d;try{const m=new URLSearchParams({page:String(s),pageSize:String(t)});void 0!==a&&m.set(\"configId\",String(a));const x=await fetch(r(`${i.API_URL}/api/git-sync/histories?${m.toString()}`),{method:\"GET\",headers:o(!1)});if(!x.ok)throw new Error(e(\"api.git.history.error\"));const u=await x.json();u.code>0&&u.code<=200?l({list:(null==(c=u.data)?void 0:c.list)||[],total:(null==(d=u.data)?void 0:d.total)??-1}):n.error(u.message||e(\"api.git.history.error\"))}catch(m){n.error(m instanceof Error?m.message:e(\"api.git.history.error\"))}},[o,e]),g=t.useCallback(async(s,t)=>{try{const a=await fetch(r(`${i.API_URL}/api/git-sync/validate`),{method:\"POST\",headers:o(),body:JSON.stringify(s)});if(!a.ok)throw new Error(e(\"api.git.validate.error\"));const l=await a.json();if(l.code>0&&l.code<=200)n.success(e(\"api.git.validate.success\")),t();else{const s=l.details||l.message||\"\";n.error(s?`${e(\"api.git.validate.error\")}: ${s}`:e(\"api.git.validate.error\"))}}catch(a){n.error(a instanceof Error?a.message:e(\"api.git.validate.error\"))}},[o,e]);return t.useMemo(()=>({handleGitSyncList:c,handleGitSyncUpdate:d,handleGitSyncDelete:m,handleGitSyncExecute:x,handleGitSyncClean:u,handleGitSyncHistories:h,handleGitSyncValidate:g}),[c,d,m,x,u,h,g])}function te({configId:e,open:a,onOpenChange:r}){const{t:i}=s(),{handleGitSyncHistories:n}=se(),[u,h]=t.useState([]),[C,E]=t.useState(0),[$,T]=t.useState(1),[z,G]=t.useState(!1),I=t.useRef(0),L=t.useCallback(async s=>{const t=++I.current;G(!0),await n(s,10,e,e=>{t===I.current&&(h(e.list),E(e.total))}),t===I.current&&G(!1)},[e,n]);t.useEffect(()=>{if(a)return T(1),void L(1);I.current+=1,G(!1)},[a,L]),t.useEffect(()=>()=>{I.current+=1},[]);const P=C>0?Math.ceil(C/10):1,R=(e,s)=>{if(!e||!s)return\"-\";const t=new Date(s).getTime()-new Date(e).getTime();if(isNaN(t)||t<0)return\"-\";const a=Math.round(t/1e3);return a<60?`${a}s`:`${Math.floor(a/60)}m ${a%60}s`};return l.jsx(N,{open:a,onOpenChange:r,children:l.jsxs(y,{className:\"max-w-3xl max-h-[85vh] flex flex-col p-6\",children:[l.jsx(w,{className:\"mb-4\",children:l.jsxs(k,{className:\"flex items-center gap-2\",children:[l.jsx(ee,{className:\"h-5 w-5 text-primary\"}),void 0!==e?`${i(\"ui.git.history.title\")} #${e}`:i(\"ui.git.history.title\")]})}),l.jsx(\"div\",{className:\"flex-1 overflow-auto border rounded-xl bg-card/50\",children:l.jsxs(g,{children:[l.jsx(p,{className:\"bg-muted/50 sticky top-0 z-10\",children:l.jsxs(f,{children:[l.jsx(j,{className:\"w-12\",children:\"ID\"}),void 0===e&&l.jsx(j,{className:\"w-16\",children:i(\"ui.git.history.configId\")}),l.jsx(j,{className:\"w-40\",children:i(\"ui.git.history.startTime\")}),l.jsx(j,{className:\"w-24\",children:i(\"ui.git.history.duration\")}),l.jsx(j,{className:\"w-24\",children:i(\"ui.git.history.status\")}),l.jsx(j,{children:i(\"ui.git.history.message\")})]})}),l.jsx(b,{children:z?l.jsx(f,{children:l.jsx(v,{colSpan:void 0===e?6:5,className:\"h-48\",children:l.jsxs(\"div\",{className:\"flex flex-col items-center justify-center text-muted-foreground gap-2\",children:[l.jsx(o,{className:\"h-8 w-8 animate-spin opacity-50\"}),l.jsx(\"span\",{className:\"text-xs\",children:i(\"ui.common.loading\")})]})})}):u.length>0?u.map(s=>{return l.jsxs(f,{className:\"text-xs hover:bg-muted/30 transition-colors\",children:[l.jsx(v,{className:\"font-mono text-muted-foreground\",children:s.id}),void 0===e&&l.jsxs(v,{className:\"font-mono text-muted-foreground\",children:[\"#\",s.configId]}),l.jsx(v,{className:\"font-mono text-muted-foreground\",children:s.startTime||\"-\"}),l.jsx(v,{className:\"font-mono text-muted-foreground\",children:R(s.startTime,s.endTime)}),l.jsx(v,{children:l.jsxs(\"span\",{className:c(\"inline-flex items-center gap-1 px-2 py-0.5 rounded-full font-medium\",2===s.status?\"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400\":3===s.status?\"bg-destructive/10 text-destructive dark:bg-destructive/20\":1===s.status?\"bg-orange-100 text-orange-600 dark:bg-orange-900/30 dark:text-orange-400\":\"bg-muted text-muted-foreground\"),children:[(t=s.status,1===t?l.jsx(D,{className:\"h-3 w-3 animate-pulse text-orange-500\"}):2===t?l.jsx(x,{className:\"h-3 w-3 text-green-500\"}):3===t?l.jsx(U,{className:\"h-3 w-3 text-destructive\"}):l.jsx(D,{className:\"h-3 w-3 opacity-40\"})),i(`ui.git.status.${s.status}`)]})}),l.jsx(v,{className:\"max-w-65 truncate opacity-80\",title:s.message,children:s.message||\"-\"})]},s.id);var t}):l.jsx(f,{children:l.jsx(v,{colSpan:void 0===e?6:5,className:\"h-48 text-center text-muted-foreground\",children:i(\"ui.git.history.noData\")})})})]})}),(P>1||-1===C&&(10===u.length||$>1))&&l.jsxs(\"div\",{className:\"flex items-center justify-between mt-4 px-1\",children:[l.jsx(\"div\",{className:\"text-xs text-muted-foreground\",children:C>0&&`${i(\"ui.common.total\")} ${C} ${i(\"ui.common.items\")}`}),l.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[l.jsx(d,{variant:\"outline\",size:\"sm\",className:\"h-8 w-8 p-0\",onClick:()=>{const e=$-1;T(e),L(e)},disabled:$<=1||z,children:l.jsx(S,{className:\"h-4 w-4\"})}),l.jsx(\"span\",{className:\"text-xs font-medium px-2\",children:C>0?`${$} / ${P}`:`${i(\"ui.common.page\")} ${$}`}),l.jsx(d,{variant:\"outline\",size:\"sm\",className:\"h-8 w-8 p-0\",onClick:()=>{const e=$+1;T(e),L(e)},disabled:(C>0?$>=P:u.length<10)||z,children:l.jsx(m,{className:\"h-4 w-4\"})})]})]})]})})}function ae({config:e,vaults:a,onSubmit:r,onCancel:i}){const{t:n}=s(),{handleGitSyncUpdate:o,handleGitSyncValidate:c}=se(),[m,x]=t.useState(!1),[h,g]=t.useState(!1),p=t.useMemo(()=>{return e=n,P({id:M().optional(),vault:A().min(1,e(\"ui.validation.git.vaultRequired\")),repoUrl:A().min(1,e(\"ui.validation.git.repoUrlRequired\")).url(e(\"ui.validation.git.repoUrlInvalid\")),branch:A().min(1,e(\"ui.validation.git.branchRequired\")),username:A().optional(),password:A().optional(),delay:M().min(0,e(\"ui.validation.git.delayMin\")),retentionDays:M().min(-1,e(\"ui.validation.git.retentionDaysMin\")).default(0),isEnabled:R().default(!0)});var e},[n]),f=t.useMemo(()=>e?{id:e.id,vault:e.vault,repoUrl:e.repoUrl,branch:e.branch,username:e.username,password:e.password,isEnabled:e.isEnabled,delay:e.delay,retentionDays:e.retentionDays??30}:{isEnabled:!0,branch:\"main\",delay:10,retentionDays:30},[null==e?void 0:e.id,null==e?void 0:e.vault,null==e?void 0:e.repoUrl,null==e?void 0:e.branch,null==e?void 0:e.username,null==e?void 0:e.password,null==e?void 0:e.isEnabled,null==e?void 0:e.delay,null==e?void 0:e.retentionDays]),{register:j,handleSubmit:b,formState:{errors:v,isSubmitting:N},setValue:y,getValues:w,reset:k,watch:S}=F({resolver:O(p),defaultValues:f}),E=S(\"vault\"),$=S(\"isEnabled\");t.useEffect(()=>{k(f),x(!1)},[f,k]);const D=t.useCallback(()=>{k(f),x(!1),null==i||i()},[f,k,i]);t.useEffect(()=>{const e=e=>{\"Escape\"===e.key&&i&&(e.preventDefault(),D())};return document.addEventListener(\"keydown\",e),()=>document.removeEventListener(\"keydown\",e)},[D,i]);return l.jsxs(\"form\",{onSubmit:b(async e=>{await o(e,()=>{r()})}),className:\"space-y-4\",children:[l.jsxs(\"div\",{className:\"grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-3\",children:[l.jsxs(\"div\",{className:\"space-y-1.5\",children:[l.jsx(C,{htmlFor:\"vault\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:n(\"ui.backup.vault\")}),l.jsxs(T,{name:\"vault\",onValueChange:e=>y(\"vault\",e),value:E||void 0,children:[l.jsx(z,{id:\"vault\",className:\"bg-background border-input focus:ring-primary/20\",children:l.jsx(G,{placeholder:n(\"ui.backup.selectVault\")})}),l.jsx(I,{children:a.map(e=>l.jsx(L,{value:e.vault,children:e.vault},e.id))})]}),v.vault&&l.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:v.vault.message})]}),l.jsxs(\"div\",{className:\"space-y-1.5\",children:[l.jsx(C,{htmlFor:\"branch\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:n(\"ui.git.form.branch\")}),l.jsx(u,{id:\"branch\",autoComplete:\"off\",className:\"bg-background border-input\",...j(\"branch\")}),v.branch&&l.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:v.branch.message})]}),l.jsxs(\"div\",{className:\"md:col-span-2 space-y-1.5\",children:[l.jsx(C,{htmlFor:\"repoUrl\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:n(\"ui.git.repoUrl\")}),l.jsx(u,{id:\"repoUrl\",placeholder:\"https://github.com/user/repo.git\",autoComplete:\"off\",className:\"bg-background border-input\",...j(\"repoUrl\")}),v.repoUrl&&l.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:v.repoUrl.message})]}),l.jsxs(\"div\",{className:\"space-y-1.5\",children:[l.jsx(C,{htmlFor:\"username\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:n(\"ui.auth.username\")}),l.jsx(u,{id:\"username\",autoComplete:\"off\",className:\"bg-background border-input\",...j(\"username\")}),v.username&&l.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:v.username.message})]}),l.jsxs(\"div\",{className:\"space-y-1.5\",children:[l.jsxs(C,{htmlFor:\"password\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:[n(\"ui.auth.password\"),\" / Token\"]}),l.jsxs(\"div\",{className:\"relative\",children:[l.jsx(u,{id:\"password\",type:m?\"text\":\"password\",className:\"bg-background border-input pr-10\",...j(\"password\")}),l.jsx(d,{type:\"button\",variant:\"ghost\",size:\"icon\",className:\"absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent text-muted-foreground hover:text-foreground\",onClick:()=>x(!m),children:m?l.jsx(_,{className:\"h-4 w-4\"}):l.jsx(H,{className:\"h-4 w-4\"})})]}),v.password&&l.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:v.password.message})]}),l.jsxs(\"div\",{className:\"space-y-1.5\",children:[l.jsx(C,{htmlFor:\"delay\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:n(\"ui.git.form.delay\")}),l.jsx(u,{id:\"delay\",type:\"number\",className:\"bg-background border-input\",...j(\"delay\",{valueAsNumber:!0})}),v.delay&&l.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:v.delay.message})]}),l.jsxs(\"div\",{className:\"space-y-1.5\",children:[l.jsx(C,{htmlFor:\"retentionDays\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:n(\"ui.git.retentionDays\")}),l.jsx(u,{id:\"retentionDays\",type:\"number\",className:\"bg-background border-input\",...j(\"retentionDays\",{valueAsNumber:!0})}),l.jsx(\"p\",{className:\"text-[10px] text-muted-foreground ml-1\",children:n(\"ui.git.retentionDaysDesc\")}),v.retentionDays&&l.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:v.retentionDays.message})]})]}),l.jsxs(\"div\",{className:\"flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 pt-3 border-t border-border\",children:[l.jsxs(\"div\",{className:\"flex items-center space-x-2 whitespace-nowrap shrink-0\",children:[l.jsx(V,{id:\"isEnabled\",name:\"isEnabled\",checked:Boolean($),onCheckedChange:e=>y(\"isEnabled\",Boolean(e)),className:\"border-input data-[state=checked]:bg-primary data-[state=checked]:border-primary\"}),l.jsx(C,{htmlFor:\"isEnabled\",className:\"text-sm font-medium text-foreground\",children:n(\"ui.common.isEnabled\")})]}),l.jsxs(\"div\",{className:\"flex items-center gap-3 flex-wrap justify-end\",children:[i&&l.jsx(d,{type:\"button\",variant:\"ghost\",onClick:D,disabled:N||h,children:n(\"ui.common.cancel\")}),l.jsxs(d,{type:\"button\",variant:\"outline\",size:\"sm\",className:\"px-6 rounded-lg\",disabled:N||h,onClick:async()=>{const e=w();e.repoUrl&&(g(!0),await c({repoUrl:e.repoUrl,branch:e.branch,username:e.username,password:e.password},()=>{}),g(!1))},children:[l.jsx(q,{className:\"h-4 w-4 mr-1.5\"}),n(h?\"ui.git.validate.loading\":\"ui.git.validate.title\")]}),l.jsx(d,{type:\"submit\",size:\"sm\",className:\"px-8 rounded-lg shadow-sm\",disabled:N||h,children:n(e?\"ui.common.save\":\"ui.common.add\")})]})]})]})}function re(){const{t:e}=s(),{openConfirmDialog:a}=h(),{handleGitSyncList:r,handleGitSyncDelete:i,handleGitSyncExecute:n,handleGitSyncClean:u,handleGitSyncHistories:g}=se(),{handleVaultList:p}=E(),[f,j]=t.useState([]),[b,v]=t.useState([]),[N,y]=t.useState(!0),[w,k]=t.useState(!1),[C,T]=t.useState(null),[z,G]=t.useState([]),[I,L]=t.useState(0),[P,R]=t.useState(1),[M,A]=t.useState(!1),[F,O]=t.useState(void 0),[V,_]=t.useState(!1),H=t.useCallback(async()=>{try{await r(e=>{j(e)})}catch(e){}},[r]),ee=t.useCallback(async()=>{try{await p(e=>{v(e)})}catch(e){}},[p]),re=t.useCallback(async()=>{y(!0);try{await Promise.all([H(),ee()])}finally{y(!1)}},[H,ee]),ie=t.useCallback(async e=>{A(!0),await g(e,10,void 0,e=>{G(e.list),L(e.total)}),A(!1)},[g]);t.useEffect(()=>{re(),ie(1)},[]);const ne=I>0?Math.ceil(I/10):1,le=(e,s)=>{if(!e||!s)return\"-\";const t=new Date(s).getTime()-new Date(e).getTime();if(isNaN(t)||t<0)return\"-\";const a=Math.round(t/1e3);return a<60?`${a}s`:`${Math.floor(a/60)}m ${a%60}s`};return l.jsxs(\"div\",{className:\"grid grid-cols-1 md:grid-cols-2 gap-4 pb-24 md:pb-4 items-start\",children:[l.jsx(\"div\",{className:\"flex flex-col gap-4\",children:l.jsxs(\"div\",{className:\"rounded-xl border border-border bg-card p-6 custom-shadow\",children:[l.jsxs(\"div\",{className:\"flex flex-col sm:flex-row items-start sm:items-center justify-between mb-4 sm:mb-6 gap-3\",children:[l.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[l.jsx(\"div\",{className:\"p-2 bg-primary/10 rounded-lg\",children:l.jsx($,{className:\"h-5 w-5 text-primary\"})}),l.jsxs(\"div\",{children:[l.jsx(\"h2\",{className:\"text-xl font-bold\",children:e(\"ui.git.config\")}),l.jsx(\"p\",{className:\"text-xs text-muted-foreground\",children:e(\"ui.git.configDesc\")})]})]}),l.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[l.jsx(d,{variant:\"ghost\",size:\"icon\",onClick:re,disabled:N,className:\"rounded-xl h-10 w-10 text-muted-foreground hover:text-primary\",children:l.jsx(J,{className:c(\"h-4 w-4\",N&&\"animate-spin\")})}),l.jsxs(d,{onClick:()=>k(!0),className:\"rounded-xl\",children:[l.jsx(K,{className:\"h-4 w-4 mr-2\"}),e(\"ui.common.add\")]})]})]}),w&&l.jsxs(\"div\",{className:\"p-4 border border-primary/20 rounded-xl bg-primary/5 mb-6 animate-in fade-in slide-in-from-top-4 duration-300\",children:[l.jsxs(\"div\",{className:\"flex items-center mb-4\",children:[l.jsx(\"div\",{className:\"h-6 w-1 bg-primary rounded-full mr-3\"}),l.jsx(\"h3\",{className:\"text-base font-bold text-foreground\",children:e(\"ui.git.addConfig\")})]}),l.jsx(ae,{vaults:b,onSubmit:()=>{k(!1),re()},onCancel:()=>k(!1)})]}),null!==C&&l.jsxs(\"div\",{className:\"p-4 border border-primary/20 rounded-xl bg-primary/5 mb-6 animate-in fade-in slide-in-from-top-4 duration-300\",children:[l.jsxs(\"div\",{className:\"flex items-center mb-4\",children:[l.jsx(\"div\",{className:\"h-6 w-1 bg-primary rounded-full mr-3\"}),l.jsx(\"h3\",{className:\"text-base font-bold text-foreground\",children:e(\"ui.git.editConfig\")})]}),l.jsx(ae,{vaults:b,config:f.find(e=>e.id===C),onSubmit:()=>{T(null),re()},onCancel:()=>T(null)})]}),l.jsx(\"div\",{className:\"flex flex-col gap-3\",children:N&&0===f.length?l.jsxs(\"div\",{className:\"h-[220px] flex flex-col items-center justify-center text-muted-foreground border-2 border-dashed border-border/50 rounded-xl bg-muted/20\",children:[l.jsx(o,{className:\"h-8 w-8 animate-spin mb-2 opacity-50\"}),l.jsx(\"p\",{className:\"text-sm\",children:e(\"ui.git.loading\")})]}):f.length>0?f.map(s=>l.jsxs(\"div\",{className:c(\"group flex flex-col p-4 transition-all duration-200 border rounded-xl bg-background hover:bg-accent/30\",s.isEnabled?\"border-l-4 border-l-orange-500 shadow-sm\":\"border-l-4 border-l-muted border-y-border border-r-border\"),children:[l.jsxs(\"div\",{className:\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 mb-3\",children:[l.jsxs(\"div\",{className:\"flex items-center gap-3 min-w-0\",children:[l.jsx(q,{className:c(\"h-5 w-5 shrink-0\",s.isEnabled?\"text-orange-500\":\"text-muted-foreground\")}),l.jsxs(\"div\",{className:\"min-w-0\",children:[l.jsxs(\"div\",{className:\"flex items-center gap-2 flex-wrap\",children:[l.jsxs(\"span\",{className:\"text-[12px] px-1.5 py-0.5 bg-muted rounded font-mono text-muted-foreground\",children:[\"#\",s.id]}),l.jsx(\"span\",{className:\"font-bold\",children:s.vault}),l.jsx(\"span\",{className:\"text-[12px] px-1.5 py-0.5 bg-accent rounded font-mono text-muted-foreground\",children:s.branch})]}),(()=>{const e=(e=>{if(!e)return null;try{const s=new URL(e);return\"http:\"===s.protocol||\"https:\"===s.protocol?e:null}catch{return null}})(s.repoUrl);return e?l.jsx(\"a\",{href:e,target:\"_blank\",rel:\"noopener noreferrer\",className:\"text-[10px] text-muted-foreground hover:text-primary font-mono truncate max-w-[200px] sm:max-w-[300px] block hover:underline\",children:s.repoUrl}):l.jsx(\"span\",{className:\"text-[10px] text-muted-foreground font-mono truncate max-w-[200px] sm:max-w-[300px] block\",children:s.repoUrl})})()]})]}),l.jsxs(\"div\",{className:\"flex items-center gap-1 flex-wrap shrink-0\",children:[l.jsx(d,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 text-muted-foreground hover:text-green-600 rounded-lg\",onClick:()=>(async e=>{await n(e,()=>{setTimeout(()=>{re(),ie(1)},1e3)})})(s.id),title:e(\"ui.git.execute.title\"),children:l.jsx(B,{className:\"h-4 w-4\"})}),l.jsx(d,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 text-muted-foreground hover:text-primary rounded-lg\",onClick:()=>{return e=s.id,O(e),void _(!0);var e},title:e(\"ui.git.history.title\"),children:l.jsx(Q,{className:\"h-4 w-4\"})}),l.jsx(d,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 text-muted-foreground hover:text-orange-500 rounded-lg\",onClick:()=>{return t=s.id,void a(e(\"ui.git.clean.confirm\"),\"confirm\",async()=>{await u(t,re)});var t},title:e(\"ui.git.clean.title\"),children:l.jsx(Z,{className:\"h-4 w-4\"})}),l.jsx(d,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 text-muted-foreground hover:text-orange-500 rounded-lg\",onClick:()=>T(s.id),children:l.jsx(W,{className:\"h-4 w-4\"})}),l.jsx(d,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 text-muted-foreground hover:text-destructive rounded-lg\",onClick:()=>{return t=s.id,void a(e(\"ui.git.delete.confirm\"),\"confirm\",async()=>{await i(t,re)});var t},children:l.jsx(X,{className:\"h-4 w-4\"})})]})]}),l.jsxs(\"div\",{className:\"flex items-center justify-between pt-2 border-t border-border/50 text-[11px]\",children:[l.jsxs(\"div\",{className:\"flex items-center gap-x-2 gap-y-1 text-muted-foreground flex-wrap\",children:[l.jsxs(\"span\",{className:\"flex items-center gap-1\",children:[l.jsx(D,{className:\"h-3 w-3\"}),e(\"ui.git.lastCommit\"),\": \",s.lastSyncTime||e(\"ui.git.neverRun\")]}),l.jsxs(\"span\",{className:\"flex items-center gap-1\",children:[l.jsx(J,{className:\"h-3 w-3\"}),e(\"ui.git.checkTime\"),\": \",s.updatedAt||e(\"ui.git.neverRun\")]})]}),l.jsxs(\"div\",{className:\"flex items-center gap-1.5 font-medium whitespace-nowrap shrink-0\",children:[1===s.lastStatus?l.jsx(o,{className:\"h-3 w-3 animate-spin text-orange-500\"}):2===s.lastStatus?l.jsx(x,{className:\"h-3 w-3 text-green-500\"}):3===s.lastStatus?l.jsx(U,{className:\"h-3 w-3 text-destructive\"}):l.jsx(D,{className:\"h-3 w-3 opacity-30\"}),l.jsx(\"span\",{className:c(2===s.lastStatus&&\"text-green-600\",3===s.lastStatus&&\"text-destructive\"),children:e(`ui.git.status.${s.lastStatus??0}`)})]})]}),s.lastMessage&&3===s.lastStatus&&l.jsx(\"p\",{className:\"mt-2 p-2 bg-destructive/5 text-destructive border border-destructive/10 rounded-md text-[10px] leading-relaxed\",children:s.lastMessage})]},s.id)):l.jsxs(\"div\",{className:\"h-[220px] flex flex-col items-center justify-center text-muted-foreground border-2 border-dashed border-border/50 rounded-xl bg-muted/20\",children:[l.jsx(Y,{className:\"h-12 w-12 mb-3 opacity-20\"}),l.jsx(\"p\",{className:\"text-sm opacity-70 font-medium\",children:e(\"ui.git.noConfig\")}),l.jsx(d,{variant:\"link\",size:\"sm\",onClick:()=>k(!0),className:\"mt-2\",children:e(\"ui.git.addFirst\")})]})})]})}),l.jsx(\"div\",{className:\"flex flex-col gap-4\",children:l.jsxs(\"div\",{className:\"rounded-xl border border-border bg-card p-6 custom-shadow\",children:[l.jsxs(\"div\",{className:\"flex items-center justify-between mb-6\",children:[l.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[l.jsx(\"div\",{className:\"p-2 bg-green-500/10 rounded-lg\",children:l.jsx(Q,{className:\"h-5 w-5 text-green-600 dark:text-green-400\"})}),l.jsxs(\"div\",{children:[l.jsx(\"h2\",{className:\"text-xl font-bold\",children:e(\"ui.git.history\")}),l.jsx(\"p\",{className:\"text-xs text-muted-foreground\",children:e(\"ui.git.historyDesc\")})]})]}),l.jsx(d,{variant:\"ghost\",size:\"icon\",onClick:()=>{R(1),ie(1)},disabled:M,className:\"rounded-xl h-10 w-10 text-muted-foreground hover:text-primary\",children:l.jsx(J,{className:c(\"h-4 w-4\",M&&\"animate-spin\")})})]}),l.jsx(\"div\",{className:\"flex flex-col gap-2\",children:M&&0===z.length?l.jsxs(\"div\",{className:\"h-[220px] flex flex-col items-center justify-center text-muted-foreground border-2 border-dashed border-border/50 rounded-xl bg-muted/20\",children:[l.jsx(o,{className:\"h-8 w-8 animate-spin mb-2 opacity-50\"}),l.jsx(\"p\",{className:\"text-sm\",children:e(\"ui.common.loading\")})]}):z.length>0?l.jsxs(l.Fragment,{children:[l.jsx(\"div\",{className:\"space-y-1.5\",children:z.map(s=>{return l.jsxs(\"div\",{className:\"flex items-center justify-between px-3 py-2.5 rounded-lg border border-border/60 bg-background hover:bg-accent/30 transition-colors text-sm\",children:[l.jsxs(\"div\",{className:\"flex items-center gap-2.5 min-w-0\",children:[1===s.status?l.jsx(o,{className:\"h-4 w-4 animate-spin text-orange-500 shrink-0\"}):2===s.status?l.jsx(x,{className:\"h-4 w-4 text-green-500 shrink-0\"}):3===s.status?l.jsx(U,{className:\"h-4 w-4 text-destructive shrink-0\"}):l.jsx(D,{className:\"h-4 w-4 opacity-30 shrink-0\"}),l.jsxs(\"div\",{className:\"min-w-0\",children:[l.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[l.jsxs(\"span\",{className:\"font-mono text-muted-foreground text-xs\",children:[\"#\",s.configId]}),l.jsx(\"span\",{className:\"font-medium text-foreground/80\",children:(t=s.configId,(null==(a=f.find(e=>e.id===t))?void 0:a.vault)??`#${t}`)}),l.jsx(\"span\",{className:c(\"font-medium\",2===s.status&&\"text-green-600 dark:text-green-400\",3===s.status&&\"text-destructive\"),children:e(`ui.git.status.${s.status}`)})]}),s.message&&l.jsx(\"p\",{className:\"text-xs text-muted-foreground truncate max-w-65\",title:s.message,children:s.message})]})]}),l.jsxs(\"div\",{className:\"flex flex-col items-end shrink-0 ml-2 gap-0.5\",children:[l.jsx(\"span\",{className:\"text-xs text-muted-foreground font-mono\",children:s.startTime}),l.jsx(\"span\",{className:\"text-xs text-primary/70 font-mono\",children:le(s.startTime,s.endTime)})]})]},s.id);var t,a})}),(ne>1||-1===I&&(10===z.length||P>1))&&l.jsxs(\"div\",{className:\"flex items-center justify-between mt-2 px-1\",children:[l.jsx(\"span\",{className:\"text-[10px] text-muted-foreground\",children:I>0&&`${e(\"ui.common.total\")} ${I} ${e(\"ui.common.items\")}`}),l.jsxs(\"div\",{className:\"flex items-center gap-1.5\",children:[l.jsx(d,{variant:\"outline\",size:\"sm\",className:\"h-6 w-6 p-0\",onClick:()=>{const e=P-1;R(e),ie(e)},disabled:P<=1||M,children:l.jsx(S,{className:\"h-3 w-3\"})}),l.jsx(\"span\",{className:\"text-xs font-medium px-1\",children:I>0?`${P} / ${ne}`:`P${P}`}),l.jsx(d,{variant:\"outline\",size:\"sm\",className:\"h-6 w-6 p-0\",onClick:()=>{const e=P+1;R(e),ie(e)},disabled:(I>0?P>=ne:z.length<10)||M,children:l.jsx(m,{className:\"h-3 w-3\"})})]})]})]}):l.jsxs(\"div\",{className:\"h-[220px] flex flex-col items-center justify-center text-muted-foreground border-2 border-dashed border-border/50 rounded-xl bg-muted/20\",children:[l.jsx(Q,{className:\"h-12 w-12 mb-3 opacity-10\"}),l.jsx(\"p\",{className:\"text-sm font-medium opacity-60\",children:e(\"ui.git.history.noData\")})]})})]})}),l.jsx(te,{configId:F,open:V,onOpenChange:_})]})}export{re as GitAutomation};\n"
  },
  {
    "path": "frontend/assets/git-branch-B1vNHBXG.js",
    "content": "import{c}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const e=c(\"GitBranch\",[[\"line\",{x1:\"6\",x2:\"6\",y1:\"3\",y2:\"15\",key:\"17qcm7\"}],[\"circle\",{cx:\"18\",cy:\"6\",r:\"3\",key:\"1h7g24\"}],[\"circle\",{cx:\"6\",cy:\"18\",r:\"3\",key:\"fqmcym\"}],[\"path\",{d:\"M18 9a9 9 0 0 1-9 9\",key:\"n2h4wq\"}]]);export{e as G};\n"
  },
  {
    "path": "frontend/assets/github-Bzk-4SPC.js",
    "content": "import{c}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const o=c(\"Github\",[[\"path\",{d:\"M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4\",key:\"tonef\"}],[\"path\",{d:\"M9 18c-4.51 2-5-2-7-2\",key:\"9comsn\"}]]);export{o as G};\n"
  },
  {
    "path": "frontend/assets/hard-drive-Dw58lXyp.js",
    "content": "import{c as y}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const e=y(\"HardDrive\",[[\"line\",{x1:\"22\",x2:\"2\",y1:\"12\",y2:\"12\",key:\"1y58io\"}],[\"path\",{d:\"M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z\",key:\"oot6mr\"}],[\"line\",{x1:\"6\",x2:\"6.01\",y1:\"16\",y2:\"16\",key:\"sgf278\"}],[\"line\",{x1:\"10\",x2:\"10.01\",y1:\"16\",y2:\"16\",key:\"1l4acy\"}]]);export{e as H};\n"
  },
  {
    "path": "frontend/assets/history-BseqF3eb.js",
    "content": "import{c as a}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const t=a(\"History\",[[\"path\",{d:\"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\",key:\"1357e3\"}],[\"path\",{d:\"M3 3v5h5\",key:\"1xhq8a\"}],[\"path\",{d:\"M12 7v5l4 2\",key:\"1fdv2h\"}]]);export{t as H};\n"
  },
  {
    "path": "frontend/assets/image-BFJJNQpe.js",
    "content": "import{c as a}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const e=a(\"FileCode\",[[\"path\",{d:\"M10 12.5 8 15l2 2.5\",key:\"1tg20x\"}],[\"path\",{d:\"m14 12.5 2 2.5-2 2.5\",key:\"yinavb\"}],[\"path\",{d:\"M14 2v4a2 2 0 0 0 2 2h4\",key:\"tnqrlb\"}],[\"path\",{d:\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z\",key:\"1mlx9k\"}]]),t=a(\"Image\",[[\"rect\",{width:\"18\",height:\"18\",x:\"3\",y:\"3\",rx:\"2\",ry:\"2\",key:\"1m3agn\"}],[\"circle\",{cx:\"9\",cy:\"9\",r:\"2\",key:\"af1f0g\"}],[\"path\",{d:\"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21\",key:\"1xmnt7\"}]]);\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */export{e as F,t as I};\n"
  },
  {
    "path": "frontend/assets/index-JfsWWBj_.js",
    "content": "import{r as t,j as e}from\"./font-loader-CIrh3KnA.js\";let i=[],n=[];function r(t){if(t<768)return!1;for(let e=0,r=i.length;;){let s=e+r>>1;if(t<i[s])r=s;else{if(!(t>=n[s]))return!0;e=s+1}if(e==r)return!1}}function s(t){return t>=127462&&t<=127487}(()=>{let t=\"lc,34,7n,7,7b,19,,,,2,,2,,,20,b,1c,l,g,,2t,7,2,6,2,2,,4,z,,u,r,2j,b,1m,9,9,,o,4,,9,,3,,5,17,3,3b,f,,w,1j,,,,4,8,4,,3,7,a,2,t,,1m,,,,2,4,8,,9,,a,2,q,,2,2,1l,,4,2,4,2,2,3,3,,u,2,3,,b,2,1l,,4,5,,2,4,,k,2,m,6,,,1m,,,2,,4,8,,7,3,a,2,u,,1n,,,,c,,9,,14,,3,,1l,3,5,3,,4,7,2,b,2,t,,1m,,2,,2,,3,,5,2,7,2,b,2,s,2,1l,2,,,2,4,8,,9,,a,2,t,,20,,4,,2,3,,,8,,29,,2,7,c,8,2q,,2,9,b,6,22,2,r,,,,,,1j,e,,5,,2,5,b,,10,9,,2u,4,,6,,2,2,2,p,2,4,3,g,4,d,,2,2,6,,f,,jj,3,qa,3,t,3,t,2,u,2,1s,2,,7,8,,2,b,9,,19,3,3b,2,y,,3a,3,4,2,9,,6,3,63,2,2,,1m,,,7,,,,,2,8,6,a,2,,1c,h,1r,4,1c,7,,,5,,14,9,c,2,w,4,2,2,,3,1k,,,2,3,,,3,1m,8,2,2,48,3,,d,,7,4,,6,,3,2,5i,1m,,5,ek,,5f,x,2da,3,3x,,2o,w,fe,6,2x,2,n9w,4,,a,w,2,28,2,7k,,3,,4,,p,2,5,,47,2,q,i,d,,12,8,p,b,1a,3,1c,,2,4,2,2,13,,1v,6,2,2,2,2,c,,8,,1b,,1f,,,3,2,2,5,2,,,16,2,8,,6m,,2,,4,,fn4,,kh,g,g,g,a6,2,gt,,6a,,45,5,1ae,3,,2,5,4,14,3,4,,4l,2,fx,4,ar,2,49,b,4w,,1i,f,1k,3,1d,4,2,2,1x,3,10,5,,8,1q,,c,2,1g,9,a,4,2,,2n,3,2,,,2,6,,4g,,3,8,l,2,1l,2,,,,,m,,e,7,3,5,5f,8,2,3,,,n,,29,,2,6,,,2,,,2,,2,6j,,2,4,6,2,,2,r,2,2d,8,2,,,2,2y,,,,2,6,,,2t,3,2,4,,5,77,9,,2,6t,,a,2,,,4,,40,4,2,2,4,,w,a,14,6,2,4,8,,9,6,2,3,1a,d,,2,ba,7,,6,,,2a,m,2,7,,2,,2,3e,6,3,,,2,,7,,,20,2,3,,,,9n,2,f0b,5,1n,7,t4,,1r,4,29,,f5k,2,43q,,,3,4,5,8,8,2,7,u,4,44,3,1iz,1j,4,1e,8,,e,,m,5,,f,11s,7,,h,2,7,,2,,5,79,7,c5,4,15s,7,31,7,240,5,gx7k,2o,3k,6o\".split(\",\").map(t=>t?parseInt(t,36):1);for(let e=0,r=0;e<t.length;e++)(e%2?n:i).push(r+=t[e])})();function o(t,e,i=!0,n=!0){return(i?a:l)(t,e,n)}function a(t,e,i){if(e==t.length)return e;e&&c(t.charCodeAt(e))&&u(t.charCodeAt(e-1))&&e--;let n=h(t,e);for(e+=d(n);e<t.length;){let o=h(t,e);if(8205==n||8205==o||i&&r(o))e+=d(o),n=o;else{if(!s(o))break;{let i=0,n=e-2;for(;n>=0&&s(h(t,n));)i++,n-=2;if(i%2==0)break;e+=2}}}return e}function l(t,e,i){for(;e>0;){let n=a(t,e-2,i);if(n<e)return n;e--}return 0}function h(t,e){let i=t.charCodeAt(e);if(!u(i)||e+1==t.length)return i;let n=t.charCodeAt(e+1);return c(n)?n-56320+(i-55296<<10)+65536:i}function c(t){return t>=56320&&t<57344}function u(t){return t>=55296&&t<56320}function d(t){return t<65536?1:2}class f{lineAt(t){if(t<0||t>this.length)throw new RangeError(`Invalid position ${t} in document of length ${this.length}`);return this.lineInner(t,!1,1,0)}line(t){if(t<1||t>this.lines)throw new RangeError(`Invalid line number ${t} in ${this.lines}-line document`);return this.lineInner(t,!0,1,0)}replace(t,e,i){[t,e]=x(this,t,e);let n=[];return this.decompose(0,t,n,2),i.length&&i.decompose(0,i.length,n,3),this.decompose(e,this.length,n,1),p.from(n,this.length-(e-t)+i.length)}append(t){return this.replace(this.length,this.length,t)}slice(t,e=this.length){[t,e]=x(this,t,e);let i=[];return this.decompose(t,e,i,0),p.from(i,e-t)}eq(t){if(t==this)return!0;if(t.length!=this.length||t.lines!=this.lines)return!1;let e=this.scanIdentical(t,1),i=this.length-this.scanIdentical(t,-1),n=new Q(this),r=new Q(t);for(let s=e,o=e;;){if(n.next(s),r.next(s),s=0,n.lineBreak!=r.lineBreak||n.done!=r.done||n.value!=r.value)return!1;if(o+=n.value.length,n.done||o>=i)return!0}}iter(t=1){return new Q(this,t)}iterRange(t,e=this.length){return new b(this,t,e)}iterLines(t,e){let i;if(null==t)i=this.iter();else{null==e&&(e=this.lines+1);let n=this.line(t).from;i=this.iterRange(n,Math.max(n,e==this.lines+1?this.length:e<=1?0:this.line(e-1).to))}return new v(i)}toString(){return this.sliceString(0)}toJSON(){let t=[];return this.flatten(t),t}constructor(){}static of(t){if(0==t.length)throw new RangeError(\"A document must have at least one line\");return 1!=t.length||t[0]?t.length<=32?new O(t):p.from(O.split(t,[])):f.empty}}class O extends f{constructor(t,e=function(t){let e=-1;for(let i of t)e+=i.length+1;return e}(t)){super(),this.text=t,this.length=e}get lines(){return this.text.length}get children(){return null}lineInner(t,e,i,n){for(let r=0;;r++){let s=this.text[r],o=n+s.length;if((e?i:o)>=t)return new w(n,o,i,s);n=o+1,i++}}decompose(t,e,i,n){let r=t<=0&&e>=this.length?this:new O(g(this.text,t,e),Math.min(e,this.length)-Math.max(0,t));if(1&n){let t=i.pop(),e=m(r.text,t.text.slice(),0,r.length);if(e.length<=32)i.push(new O(e,t.length+r.length));else{let t=e.length>>1;i.push(new O(e.slice(0,t)),new O(e.slice(t)))}}else i.push(r)}replace(t,e,i){if(!(i instanceof O))return super.replace(t,e,i);[t,e]=x(this,t,e);let n=m(this.text,m(i.text,g(this.text,0,t)),e),r=this.length+i.length-(e-t);return n.length<=32?new O(n,r):p.from(O.split(n,[]),r)}sliceString(t,e=this.length,i=\"\\n\"){[t,e]=x(this,t,e);let n=\"\";for(let r=0,s=0;r<=e&&s<this.text.length;s++){let o=this.text[s],a=r+o.length;r>t&&s&&(n+=i),t<a&&e>r&&(n+=o.slice(Math.max(0,t-r),e-r)),r=a+1}return n}flatten(t){for(let e of this.text)t.push(e)}scanIdentical(){return 0}static split(t,e){let i=[],n=-1;for(let r of t)i.push(r),n+=r.length+1,32==i.length&&(e.push(new O(i,n)),i=[],n=-1);return n>-1&&e.push(new O(i,n)),e}}class p extends f{constructor(t,e){super(),this.children=t,this.length=e,this.lines=0;for(let i of t)this.lines+=i.lines}lineInner(t,e,i,n){for(let r=0;;r++){let s=this.children[r],o=n+s.length,a=i+s.lines-1;if((e?a:o)>=t)return s.lineInner(t,e,i,n);n=o+1,i=a+1}}decompose(t,e,i,n){for(let r=0,s=0;s<=e&&r<this.children.length;r++){let o=this.children[r],a=s+o.length;if(t<=a&&e>=s){let r=n&((s<=t?1:0)|(a>=e?2:0));s>=t&&a<=e&&!r?i.push(o):o.decompose(t-s,e-s,i,r)}s=a+1}}replace(t,e,i){if([t,e]=x(this,t,e),i.lines<this.lines)for(let n=0,r=0;n<this.children.length;n++){let s=this.children[n],o=r+s.length;if(t>=r&&e<=o){let a=s.replace(t-r,e-r,i),l=this.lines-s.lines+a.lines;if(a.lines<l>>4&&a.lines>l>>6){let r=this.children.slice();return r[n]=a,new p(r,this.length-(e-t)+i.length)}return super.replace(r,o,a)}r=o+1}return super.replace(t,e,i)}sliceString(t,e=this.length,i=\"\\n\"){[t,e]=x(this,t,e);let n=\"\";for(let r=0,s=0;r<this.children.length&&s<=e;r++){let o=this.children[r],a=s+o.length;s>t&&r&&(n+=i),t<a&&e>s&&(n+=o.sliceString(t-s,e-s,i)),s=a+1}return n}flatten(t){for(let e of this.children)e.flatten(t)}scanIdentical(t,e){if(!(t instanceof p))return 0;let i=0,[n,r,s,o]=e>0?[0,0,this.children.length,t.children.length]:[this.children.length-1,t.children.length-1,-1,-1];for(;;n+=e,r+=e){if(n==s||r==o)return i;let a=this.children[n],l=t.children[r];if(a!=l)return i+a.scanIdentical(l,e);i+=a.length+1}}static from(t,e=t.reduce((t,e)=>t+e.length+1,-1)){let i=0;for(let d of t)i+=d.lines;if(i<32){let i=[];for(let e of t)e.flatten(i);return new O(i,e)}let n=Math.max(32,i>>5),r=n<<1,s=n>>1,o=[],a=0,l=-1,h=[];function c(t){let e;if(t.lines>r&&t instanceof p)for(let i of t.children)c(i);else t.lines>s&&(a>s||!a)?(u(),o.push(t)):t instanceof O&&a&&(e=h[h.length-1])instanceof O&&t.lines+e.lines<=32?(a+=t.lines,l+=t.length+1,h[h.length-1]=new O(e.text.concat(t.text),e.length+1+t.length)):(a+t.lines>n&&u(),a+=t.lines,l+=t.length+1,h.push(t))}function u(){0!=a&&(o.push(1==h.length?h[0]:p.from(h,l)),l=-1,a=h.length=0)}for(let d of t)c(d);return u(),1==o.length?o[0]:new p(o,e)}}function m(t,e,i=0,n=1e9){for(let r=0,s=0,o=!0;s<t.length&&r<=n;s++){let a=t[s],l=r+a.length;l>=i&&(l>n&&(a=a.slice(0,n-r)),r<i&&(a=a.slice(i-r)),o?(e[e.length-1]+=a,o=!1):e.push(a)),r=l+1}return e}function g(t,e,i){return m(t,[\"\"],e,i)}f.empty=new O([\"\"],0);class Q{constructor(t,e=1){this.dir=e,this.done=!1,this.lineBreak=!1,this.value=\"\",this.nodes=[t],this.offsets=[e>0?1:(t instanceof O?t.text.length:t.children.length)<<1]}nextInner(t,e){for(this.done=this.lineBreak=!1;;){let i=this.nodes.length-1,n=this.nodes[i],r=this.offsets[i],s=r>>1,o=n instanceof O?n.text.length:n.children.length;if(s==(e>0?o:0)){if(0==i)return this.done=!0,this.value=\"\",this;e>0&&this.offsets[i-1]++,this.nodes.pop(),this.offsets.pop()}else if((1&r)==(e>0?0:1)){if(this.offsets[i]+=e,0==t)return this.lineBreak=!0,this.value=\"\\n\",this;t--}else if(n instanceof O){let r=n.text[s+(e<0?-1:0)];if(this.offsets[i]+=e,r.length>Math.max(0,t))return this.value=0==t?r:e>0?r.slice(t):r.slice(0,r.length-t),this;t-=r.length}else{let r=n.children[s+(e<0?-1:0)];t>r.length?(t-=r.length,this.offsets[i]+=e):(e<0&&this.offsets[i]--,this.nodes.push(r),this.offsets.push(e>0?1:(r instanceof O?r.text.length:r.children.length)<<1))}}}next(t=0){return t<0&&(this.nextInner(-t,-this.dir),t=this.value.length),this.nextInner(t,this.dir)}}class b{constructor(t,e,i){this.value=\"\",this.done=!1,this.cursor=new Q(t,e>i?-1:1),this.pos=e>i?t.length:0,this.from=Math.min(e,i),this.to=Math.max(e,i)}nextInner(t,e){if(e<0?this.pos<=this.from:this.pos>=this.to)return this.value=\"\",this.done=!0,this;t+=Math.max(0,e<0?this.pos-this.to:this.from-this.pos);let i=e<0?this.pos-this.from:this.to-this.pos;t>i&&(t=i),i-=t;let{value:n}=this.cursor.next(t);return this.pos+=(n.length+t)*e,this.value=n.length<=i?n:e<0?n.slice(n.length-i):n.slice(0,i),this.done=!this.value,this}next(t=0){return t<0?t=Math.max(t,this.from-this.pos):t>0&&(t=Math.min(t,this.to-this.pos)),this.nextInner(t,this.cursor.dir)}get lineBreak(){return this.cursor.lineBreak&&\"\"!=this.value}}class v{constructor(t){this.inner=t,this.afterBreak=!0,this.value=\"\",this.done=!1}next(t=0){let{done:e,lineBreak:i,value:n}=this.inner.next(t);return e&&this.afterBreak?(this.value=\"\",this.afterBreak=!1):e?(this.done=!0,this.value=\"\"):i?this.afterBreak?this.value=\"\":(this.afterBreak=!0,this.next()):(this.value=n,this.afterBreak=!1),this}get lineBreak(){return!1}}\"undefined\"!=typeof Symbol&&(f.prototype[Symbol.iterator]=function(){return this.iter()},Q.prototype[Symbol.iterator]=b.prototype[Symbol.iterator]=v.prototype[Symbol.iterator]=function(){return this});let w=class{constructor(t,e,i,n){this.from=t,this.to=e,this.number=i,this.text=n}get length(){return this.to-this.from}};function x(t,e,i){return[e=Math.max(0,Math.min(t.length,e)),Math.max(e,Math.min(t.length,i))]}function S(t,e,i=!0,n=!0){return o(t,e,i,n)}function y(t,e){let i=t.charCodeAt(e);if(!(n=i,n>=55296&&n<56320&&e+1!=t.length))return i;var n;let r=t.charCodeAt(e+1);return function(t){return t>=56320&&t<57344}(r)?r-56320+(i-55296<<10)+65536:i}function k(t){return t<=65535?String.fromCharCode(t):(t-=65536,String.fromCharCode(55296+(t>>10),56320+(1023&t)))}function $(t){return t<65536?1:2}const P=/\\r\\n?|\\n/;var T=function(t){return t[t.Simple=0]=\"Simple\",t[t.TrackDel=1]=\"TrackDel\",t[t.TrackBefore=2]=\"TrackBefore\",t[t.TrackAfter=3]=\"TrackAfter\",t}(T||(T={}));class C{constructor(t){this.sections=t}get length(){let t=0;for(let e=0;e<this.sections.length;e+=2)t+=this.sections[e];return t}get newLength(){let t=0;for(let e=0;e<this.sections.length;e+=2){let i=this.sections[e+1];t+=i<0?this.sections[e]:i}return t}get empty(){return 0==this.sections.length||2==this.sections.length&&this.sections[1]<0}iterGaps(t){for(let e=0,i=0,n=0;e<this.sections.length;){let r=this.sections[e++],s=this.sections[e++];s<0?(t(i,n,r),n+=r):n+=s,i+=r}}iterChangedRanges(t,e=!1){M(this,t,e)}get invertedDesc(){let t=[];for(let e=0;e<this.sections.length;){let i=this.sections[e++],n=this.sections[e++];n<0?t.push(i,n):t.push(n,i)}return new C(t)}composeDesc(t){return this.empty?t:t.empty?this:z(this,t)}mapDesc(t,e=!1){return t.empty?this:R(this,t,e)}mapPos(t,e=-1,i=T.Simple){let n=0,r=0;for(let s=0;s<this.sections.length;){let o=this.sections[s++],a=this.sections[s++],l=n+o;if(a<0){if(l>t)return r+(t-n);r+=o}else{if(i!=T.Simple&&l>=t&&(i==T.TrackDel&&n<t&&l>t||i==T.TrackBefore&&n<t||i==T.TrackAfter&&l>t))return null;if(l>t||l==t&&e<0&&!o)return t==n||e<0?r:r+a;r+=a}n=l}if(t>n)throw new RangeError(`Position ${t} is out of range for changeset of length ${n}`);return r}touchesRange(t,e=t){for(let i=0,n=0;i<this.sections.length&&n<=e;){let r=n+this.sections[i++];if(this.sections[i++]>=0&&n<=e&&r>=t)return!(n<t&&r>e)||\"cover\";n=r}return!1}toString(){let t=\"\";for(let e=0;e<this.sections.length;){let i=this.sections[e++],n=this.sections[e++];t+=(t?\" \":\"\")+i+(n>=0?\":\"+n:\"\")}return t}toJSON(){return this.sections}static fromJSON(t){if(!Array.isArray(t)||t.length%2||t.some(t=>\"number\"!=typeof t))throw new RangeError(\"Invalid JSON representation of ChangeDesc\");return new C(t)}static create(t){return new C(t)}}class Z extends C{constructor(t,e){super(t),this.inserted=e}apply(t){if(this.length!=t.length)throw new RangeError(\"Applying change set to a document with the wrong length\");return M(this,(e,i,n,r,s)=>t=t.replace(n,n+(i-e),s),!1),t}mapDesc(t,e=!1){return R(this,t,e,!0)}invert(t){let e=this.sections.slice(),i=[];for(let n=0,r=0;n<e.length;n+=2){let s=e[n],o=e[n+1];if(o>=0){e[n]=o,e[n+1]=s;let a=n>>1;for(;i.length<a;)i.push(f.empty);i.push(s?t.slice(r,r+s):f.empty)}r+=s}return new Z(e,i)}compose(t){return this.empty?t:t.empty?this:z(this,t,!0)}map(t,e=!1){return t.empty?this:R(this,t,e,!0)}iterChanges(t,e=!1){M(this,t,e)}get desc(){return C.create(this.sections)}filter(t){let e=[],i=[],n=[],r=new _(this);t:for(let s=0,o=0;;){let a=s==t.length?1e9:t[s++];for(;o<a||o==a&&0==r.len;){if(r.done)break t;let t=Math.min(r.len,a-o);X(n,t,-1);let s=-1==r.ins?-1:0==r.off?r.ins:0;X(e,t,s),s>0&&A(i,e,r.text),r.forward(t),o+=t}let l=t[s++];for(;o<l;){if(r.done)break t;let t=Math.min(r.len,l-o);X(e,t,-1),X(n,t,-1==r.ins?-1:0==r.off?r.ins:0),r.forward(t),o+=t}}return{changes:new Z(e,i),filtered:C.create(n)}}toJSON(){let t=[];for(let e=0;e<this.sections.length;e+=2){let i=this.sections[e],n=this.sections[e+1];n<0?t.push(i):0==n?t.push([i]):t.push([i].concat(this.inserted[e>>1].toJSON()))}return t}static of(t,e,i){let n=[],r=[],s=0,o=null;function a(t=!1){if(!t&&!n.length)return;s<e&&X(n,e-s,-1);let i=new Z(n,r);o=o?o.compose(i.map(o)):i,n=[],r=[],s=0}return function t(l){if(Array.isArray(l))for(let e of l)t(e);else if(l instanceof Z){if(l.length!=e)throw new RangeError(`Mismatched change set length (got ${l.length}, expected ${e})`);a(),o=o?o.compose(l.map(o)):l}else{let{from:t,to:o=t,insert:h}=l;if(t>o||t<0||o>e)throw new RangeError(`Invalid change range ${t} to ${o} (in doc of length ${e})`);let c=h?\"string\"==typeof h?f.of(h.split(i||P)):h:f.empty,u=c.length;if(t==o&&0==u)return;t<s&&a(),t>s&&X(n,t-s,-1),X(n,o-t,u),A(r,n,c),s=o}}(t),a(!o),o}static empty(t){return new Z(t?[t,-1]:[],[])}static fromJSON(t){if(!Array.isArray(t))throw new RangeError(\"Invalid JSON representation of ChangeSet\");let e=[],i=[];for(let n=0;n<t.length;n++){let r=t[n];if(\"number\"==typeof r)e.push(r,-1);else{if(!Array.isArray(r)||\"number\"!=typeof r[0]||r.some((t,e)=>e&&\"string\"!=typeof t))throw new RangeError(\"Invalid JSON representation of ChangeSet\");if(1==r.length)e.push(r[0],0);else{for(;i.length<n;)i.push(f.empty);i[n]=f.of(r.slice(1)),e.push(r[0],i[n].length)}}}return new Z(e,i)}static createSet(t,e){return new Z(t,e)}}function X(t,e,i,n=!1){if(0==e&&i<=0)return;let r=t.length-2;r>=0&&i<=0&&i==t[r+1]?t[r]+=e:r>=0&&0==e&&0==t[r]?t[r+1]+=i:n?(t[r]+=e,t[r+1]+=i):t.push(e,i)}function A(t,e,i){if(0==i.length)return;let n=e.length-2>>1;if(n<t.length)t[t.length-1]=t[t.length-1].append(i);else{for(;t.length<n;)t.push(f.empty);t.push(i)}}function M(t,e,i){let n=t.inserted;for(let r=0,s=0,o=0;o<t.sections.length;){let a=t.sections[o++],l=t.sections[o++];if(l<0)r+=a,s+=a;else{let h=r,c=s,u=f.empty;for(;h+=a,c+=l,l&&n&&(u=u.append(n[o-2>>1])),!(i||o==t.sections.length||t.sections[o+1]<0);)a=t.sections[o++],l=t.sections[o++];e(r,h,s,c,u),r=h,s=c}}}function R(t,e,i,n=!1){let r=[],s=n?[]:null,o=new _(t),a=new _(e);for(let l=-1;;){if(o.done&&a.len||a.done&&o.len)throw new Error(\"Mismatched change set lengths\");if(-1==o.ins&&-1==a.ins){let t=Math.min(o.len,a.len);X(r,t,-1),o.forward(t),a.forward(t)}else if(a.ins>=0&&(o.ins<0||l==o.i||0==o.off&&(a.len<o.len||a.len==o.len&&!i))){let t=a.len;for(X(r,a.ins,-1);t;){let e=Math.min(o.len,t);o.ins>=0&&l<o.i&&o.len<=e&&(X(r,0,o.ins),s&&A(s,r,o.text),l=o.i),o.forward(e),t-=e}a.next()}else{if(!(o.ins>=0)){if(o.done&&a.done)return s?Z.createSet(r,s):C.create(r);throw new Error(\"Mismatched change set lengths\")}{let t=0,e=o.len;for(;e;)if(-1==a.ins){let i=Math.min(e,a.len);t+=i,e-=i,a.forward(i)}else{if(!(0==a.ins&&a.len<e))break;e-=a.len,a.next()}X(r,t,l<o.i?o.ins:0),s&&l<o.i&&A(s,r,o.text),l=o.i,o.forward(o.len-e)}}}}function z(t,e,i=!1){let n=[],r=i?[]:null,s=new _(t),o=new _(e);for(let a=!1;;){if(s.done&&o.done)return r?Z.createSet(n,r):C.create(n);if(0==s.ins)X(n,s.len,0,a),s.next();else if(0!=o.len||o.done){if(s.done||o.done)throw new Error(\"Mismatched change set lengths\");{let t=Math.min(s.len2,o.len),e=n.length;if(-1==s.ins){let e=-1==o.ins?-1:o.off?0:o.ins;X(n,t,e,a),r&&e&&A(r,n,o.text)}else-1==o.ins?(X(n,s.off?0:s.len,t,a),r&&A(r,n,s.textBit(t))):(X(n,s.off?0:s.len,o.off?0:o.ins,a),r&&!o.off&&A(r,n,o.text));a=(s.ins>t||o.ins>=0&&o.len>t)&&(a||n.length>e),s.forward2(t),o.forward(t)}}else X(n,0,o.ins,a),r&&A(r,n,o.text),o.next()}}class _{constructor(t){this.set=t,this.i=0,this.next()}next(){let{sections:t}=this.set;this.i<t.length?(this.len=t[this.i++],this.ins=t[this.i++]):(this.len=0,this.ins=-2),this.off=0}get done(){return-2==this.ins}get len2(){return this.ins<0?this.len:this.ins}get text(){let{inserted:t}=this.set,e=this.i-2>>1;return e>=t.length?f.empty:t[e]}textBit(t){let{inserted:e}=this.set,i=this.i-2>>1;return i>=e.length&&!t?f.empty:e[i].slice(this.off,null==t?void 0:this.off+t)}forward(t){t==this.len?this.next():(this.len-=t,this.off+=t)}forward2(t){-1==this.ins?this.forward(t):t==this.ins?this.next():(this.ins-=t,this.off+=t)}}class E{constructor(t,e,i){this.from=t,this.to=e,this.flags=i}get anchor(){return 32&this.flags?this.to:this.from}get head(){return 32&this.flags?this.from:this.to}get empty(){return this.from==this.to}get assoc(){return 8&this.flags?-1:16&this.flags?1:0}get bidiLevel(){let t=7&this.flags;return 7==t?null:t}get goalColumn(){let t=this.flags>>6;return 16777215==t?void 0:t}map(t,e=-1){let i,n;return this.empty?i=n=t.mapPos(this.from,e):(i=t.mapPos(this.from,1),n=t.mapPos(this.to,-1)),i==this.from&&n==this.to?this:new E(i,n,this.flags)}extend(t,e=t){if(t<=this.anchor&&e>=this.anchor)return Y.range(t,e);let i=Math.abs(t-this.anchor)>Math.abs(e-this.anchor)?t:e;return Y.range(this.anchor,i)}eq(t,e=!1){return!(this.anchor!=t.anchor||this.head!=t.head||e&&this.empty&&this.assoc!=t.assoc)}toJSON(){return{anchor:this.anchor,head:this.head}}static fromJSON(t){if(!t||\"number\"!=typeof t.anchor||\"number\"!=typeof t.head)throw new RangeError(\"Invalid JSON representation for SelectionRange\");return Y.range(t.anchor,t.head)}static create(t,e,i){return new E(t,e,i)}}class Y{constructor(t,e){this.ranges=t,this.mainIndex=e}map(t,e=-1){return t.empty?this:Y.create(this.ranges.map(i=>i.map(t,e)),this.mainIndex)}eq(t,e=!1){if(this.ranges.length!=t.ranges.length||this.mainIndex!=t.mainIndex)return!1;for(let i=0;i<this.ranges.length;i++)if(!this.ranges[i].eq(t.ranges[i],e))return!1;return!0}get main(){return this.ranges[this.mainIndex]}asSingle(){return 1==this.ranges.length?this:new Y([this.main],0)}addRange(t,e=!0){return Y.create([t].concat(this.ranges),e?0:this.mainIndex+1)}replaceRange(t,e=this.mainIndex){let i=this.ranges.slice();return i[e]=t,Y.create(i,this.mainIndex)}toJSON(){return{ranges:this.ranges.map(t=>t.toJSON()),main:this.mainIndex}}static fromJSON(t){if(!t||!Array.isArray(t.ranges)||\"number\"!=typeof t.main||t.main>=t.ranges.length)throw new RangeError(\"Invalid JSON representation for EditorSelection\");return new Y(t.ranges.map(t=>E.fromJSON(t)),t.main)}static single(t,e=t){return new Y([Y.range(t,e)],0)}static create(t,e=0){if(0==t.length)throw new RangeError(\"A selection needs at least one range\");for(let i=0,n=0;n<t.length;n++){let r=t[n];if(r.empty?r.from<=i:r.from<i)return Y.normalized(t.slice(),e);i=r.to}return new Y(t,e)}static cursor(t,e=0,i,n){return E.create(t,t,(0==e?0:e<0?8:16)|(null==i?7:Math.min(6,i))|(null!=n?n:16777215)<<6)}static range(t,e,i,n){let r=(null!=i?i:16777215)<<6|(null==n?7:Math.min(6,n));return e<t?E.create(e,t,48|r):E.create(t,e,(e>t?8:0)|r)}static normalized(t,e=0){let i=t[e];t.sort((t,e)=>t.from-e.from),e=t.indexOf(i);for(let n=1;n<t.length;n++){let i=t[n],r=t[n-1];if(i.empty?i.from<=r.to:i.from<r.to){let s=r.from,o=Math.max(i.to,r.to);n<=e&&e--,t.splice(--n,2,i.anchor>i.head?Y.range(o,s):Y.range(s,o))}}return new Y(t,e)}}function L(t,e){for(let i of t.ranges)if(i.to>e)throw new RangeError(\"Selection points outside of document\")}let q=0;class V{constructor(t,e,i,n,r){this.combine=t,this.compareInput=e,this.compare=i,this.isStatic=n,this.id=q++,this.default=t([]),this.extensions=\"function\"==typeof r?r(this):r}get reader(){return this}static define(t={}){return new V(t.combine||(t=>t),t.compareInput||((t,e)=>t===e),t.compare||(t.combine?(t,e)=>t===e:W),!!t.static,t.enables)}of(t){return new D([],this,0,t)}compute(t,e){if(this.isStatic)throw new Error(\"Can't compute a static facet\");return new D(t,this,1,e)}computeN(t,e){if(this.isStatic)throw new Error(\"Can't compute a static facet\");return new D(t,this,2,e)}from(t,e){return e||(e=t=>t),this.compute([t],i=>e(i.field(t)))}}function W(t,e){return t==e||t.length==e.length&&t.every((t,i)=>t===e[i])}class D{constructor(t,e,i,n){this.dependencies=t,this.facet=e,this.type=i,this.value=n,this.id=q++}dynamicSlot(t){var e;let i=this.value,n=this.facet.compareInput,r=this.id,s=t[r]>>1,o=2==this.type,a=!1,l=!1,h=[];for(let c of this.dependencies)\"doc\"==c?a=!0:\"selection\"==c?l=!0:1&(null!==(e=t[c.id])&&void 0!==e?e:1)||h.push(t[c.id]);return{create:t=>(t.values[s]=i(t),1),update(t,e){if(a&&e.docChanged||l&&(e.docChanged||e.selection)||j(t,h)){let e=i(t);if(o?!B(e,t.values[s],n):!n(e,t.values[s]))return t.values[s]=e,1}return 0},reconfigure:(t,e)=>{let a,l=e.config.address[r];if(null!=l){let r=ot(e,l);if(this.dependencies.every(i=>i instanceof V?e.facet(i)===t.facet(i):!(i instanceof N)||e.field(i,!1)==t.field(i,!1))||(o?B(a=i(t),r,n):n(a=i(t),r)))return t.values[s]=r,0}else a=i(t);return t.values[s]=a,1}}}}function B(t,e,i){if(t.length!=e.length)return!1;for(let n=0;n<t.length;n++)if(!i(t[n],e[n]))return!1;return!0}function j(t,e){let i=!1;for(let n of e)1&st(t,n)&&(i=!0);return i}function I(t,e,i){let n=i.map(e=>t[e.id]),r=i.map(t=>t.type),s=n.filter(t=>!(1&t)),o=t[e.id]>>1;function a(t){let i=[];for(let e=0;e<n.length;e++){let s=ot(t,n[e]);if(2==r[e])for(let t of s)i.push(t);else i.push(s)}return e.combine(i)}return{create(t){for(let e of n)st(t,e);return t.values[o]=a(t),1},update(t,i){if(!j(t,s))return 0;let n=a(t);return e.compare(n,t.values[o])?0:(t.values[o]=n,1)},reconfigure(t,r){let s=j(t,n),l=r.config.facets[e.id],h=r.facet(e);if(l&&!s&&W(i,l))return t.values[o]=h,0;let c=a(t);return e.compare(c,h)?(t.values[o]=h,0):(t.values[o]=c,1)}}}const G=V.define({static:!0});class N{constructor(t,e,i,n,r){this.id=t,this.createF=e,this.updateF=i,this.compareF=n,this.spec=r,this.provides=void 0}static define(t){let e=new N(q++,t.create,t.update,t.compare||((t,e)=>t===e),t);return t.provide&&(e.provides=t.provide(e)),e}create(t){let e=t.facet(G).find(t=>t.field==this);return((null==e?void 0:e.create)||this.createF)(t)}slot(t){let e=t[this.id]>>1;return{create:t=>(t.values[e]=this.create(t),1),update:(t,i)=>{let n=t.values[e],r=this.updateF(n,i);return this.compareF(n,r)?0:(t.values[e]=r,1)},reconfigure:(t,i)=>{let n,r=t.facet(G),s=i.facet(G);return(n=r.find(t=>t.field==this))&&n!=s.find(t=>t.field==this)?(t.values[e]=n.create(t),1):null!=i.config.address[this.id]?(t.values[e]=i.field(this),0):(t.values[e]=this.create(t),1)}}}init(t){return[this,G.of({field:this,create:t})]}get extension(){return this}}const U=4,H=3,F=2,K=1;function J(t){return e=>new et(e,t)}const tt={highest:J(0),high:J(K),default:J(F),low:J(H),lowest:J(U)};class et{constructor(t,e){this.inner=t,this.prec=e}}class it{of(t){return new nt(this,t)}reconfigure(t){return it.reconfigure.of({compartment:this,extension:t})}get(t){return t.config.compartments.get(this)}}class nt{constructor(t,e){this.compartment=t,this.inner=e}}class rt{constructor(t,e,i,n,r,s){for(this.base=t,this.compartments=e,this.dynamicSlots=i,this.address=n,this.staticValues=r,this.facets=s,this.statusTemplate=[];this.statusTemplate.length<i.length;)this.statusTemplate.push(0)}staticFacet(t){let e=this.address[t.id];return null==e?t.default:this.staticValues[e>>1]}static resolve(t,e,i){let n=[],r=Object.create(null),s=new Map;for(let u of function(t,e,i){let n=[[],[],[],[],[]],r=new Map;function s(t,o){let a=r.get(t);if(null!=a){if(a<=o)return;let e=n[a].indexOf(t);e>-1&&n[a].splice(e,1),t instanceof nt&&i.delete(t.compartment)}if(r.set(t,o),Array.isArray(t))for(let e of t)s(e,o);else if(t instanceof nt){if(i.has(t.compartment))throw new RangeError(\"Duplicate use of compartment in extensions\");let n=e.get(t.compartment)||t.inner;i.set(t.compartment,n),s(n,o)}else if(t instanceof et)s(t.inner,t.prec);else if(t instanceof N)n[o].push(t),t.provides&&s(t.provides,o);else if(t instanceof D)n[o].push(t),t.facet.extensions&&s(t.facet.extensions,F);else{let e=t.extension;if(!e)throw new Error(`Unrecognized extension value in extension set (${t}). This sometimes happens because multiple instances of @codemirror/state are loaded, breaking instanceof checks.`);s(e,o)}}return s(t,F),n.reduce((t,e)=>t.concat(e))}(t,e,s))u instanceof N?n.push(u):(r[u.facet.id]||(r[u.facet.id]=[])).push(u);let o=Object.create(null),a=[],l=[];for(let u of n)o[u.id]=l.length<<1,l.push(t=>u.slot(t));let h=null==i?void 0:i.config.facets;for(let u in r){let t=r[u],e=t[0].facet,n=h&&h[u]||[];if(t.every(t=>0==t.type))if(o[e.id]=a.length<<1|1,W(n,t))a.push(i.facet(e));else{let n=e.combine(t.map(t=>t.value));a.push(i&&e.compare(n,i.facet(e))?i.facet(e):n)}else{for(let e of t)0==e.type?(o[e.id]=a.length<<1|1,a.push(e.value)):(o[e.id]=l.length<<1,l.push(t=>e.dynamicSlot(t)));o[e.id]=l.length<<1,l.push(i=>I(i,e,t))}}let c=l.map(t=>t(o));return new rt(t,s,c,o,a,r)}}function st(t,e){if(1&e)return 2;let i=e>>1,n=t.status[i];if(4==n)throw new Error(\"Cyclic dependency between fields and/or facets\");if(2&n)return n;t.status[i]=4;let r=t.computeSlot(t,t.config.dynamicSlots[i]);return t.status[i]=2|r}function ot(t,e){return 1&e?t.config.staticValues[e>>1]:t.values[e>>1]}const at=V.define(),lt=V.define({combine:t=>t.some(t=>t),static:!0}),ht=V.define({combine:t=>t.length?t[0]:void 0,static:!0}),ct=V.define(),ut=V.define(),dt=V.define(),ft=V.define({combine:t=>!!t.length&&t[0]});class Ot{constructor(t,e){this.type=t,this.value=e}static define(){return new pt}}class pt{of(t){return new Ot(this,t)}}class mt{constructor(t){this.map=t}of(t){return new gt(this,t)}}class gt{constructor(t,e){this.type=t,this.value=e}map(t){let e=this.type.map(this.value,t);return void 0===e?void 0:e==this.value?this:new gt(this.type,e)}is(t){return this.type==t}static define(t={}){return new mt(t.map||(t=>t))}static mapEffects(t,e){if(!t.length)return t;let i=[];for(let n of t){let t=n.map(e);t&&i.push(t)}return i}}gt.reconfigure=gt.define(),gt.appendConfig=gt.define();class Qt{constructor(t,e,i,n,r,s){this.startState=t,this.changes=e,this.selection=i,this.effects=n,this.annotations=r,this.scrollIntoView=s,this._doc=null,this._state=null,i&&L(i,e.newLength),r.some(t=>t.type==Qt.time)||(this.annotations=r.concat(Qt.time.of(Date.now())))}static create(t,e,i,n,r,s){return new Qt(t,e,i,n,r,s)}get newDoc(){return this._doc||(this._doc=this.changes.apply(this.startState.doc))}get newSelection(){return this.selection||this.startState.selection.map(this.changes)}get state(){return this._state||this.startState.applyTransaction(this),this._state}annotation(t){for(let e of this.annotations)if(e.type==t)return e.value}get docChanged(){return!this.changes.empty}get reconfigured(){return this.startState.config!=this.state.config}isUserEvent(t){let e=this.annotation(Qt.userEvent);return!(!e||!(e==t||e.length>t.length&&e.slice(0,t.length)==t&&\".\"==e[t.length]))}}function bt(t,e){let i=[];for(let n=0,r=0;;){let s,o;if(n<t.length&&(r==e.length||e[r]>=t[n]))s=t[n++],o=t[n++];else{if(!(r<e.length))return i;s=e[r++],o=e[r++]}!i.length||i[i.length-1]<s?i.push(s,o):i[i.length-1]<o&&(i[i.length-1]=o)}}function vt(t,e,i){var n;let r,s,o;return i?(r=e.changes,s=Z.empty(e.changes.length),o=t.changes.compose(e.changes)):(r=e.changes.map(t.changes),s=t.changes.mapDesc(e.changes,!0),o=t.changes.compose(r)),{changes:o,selection:e.selection?e.selection.map(s):null===(n=t.selection)||void 0===n?void 0:n.map(r),effects:gt.mapEffects(t.effects,r).concat(gt.mapEffects(e.effects,s)),annotations:t.annotations.length?t.annotations.concat(e.annotations):e.annotations,scrollIntoView:t.scrollIntoView||e.scrollIntoView}}function wt(t,e,i){let n=e.selection,r=yt(e.annotations);return e.userEvent&&(r=r.concat(Qt.userEvent.of(e.userEvent))),{changes:e.changes instanceof Z?e.changes:Z.of(e.changes||[],i,t.facet(ht)),selection:n&&(n instanceof Y?n:Y.single(n.anchor,n.head)),effects:yt(e.effects),annotations:r,scrollIntoView:!!e.scrollIntoView}}function xt(t,e,i){let n=wt(t,e.length?e[0]:{},t.doc.length);e.length&&!1===e[0].filter&&(i=!1);for(let s=1;s<e.length;s++){!1===e[s].filter&&(i=!1);let r=!!e[s].sequential;n=vt(n,wt(t,e[s],r?n.changes.newLength:t.doc.length),r)}let r=Qt.create(t,n.changes,n.selection,n.effects,n.annotations,n.scrollIntoView);return function(t){let e=t.startState,i=e.facet(dt),n=t;for(let r=i.length-1;r>=0;r--){let s=i[r](t);s&&Object.keys(s).length&&(n=vt(n,wt(e,s,t.changes.newLength),!0))}return n==t?t:Qt.create(e,t.changes,t.selection,n.effects,n.annotations,n.scrollIntoView)}(i?function(t){let e=t.startState,i=!0;for(let r of e.facet(ct)){let e=r(t);if(!1===e){i=!1;break}Array.isArray(e)&&(i=!0===i?e:bt(i,e))}if(!0!==i){let n,r;if(!1===i)r=t.changes.invertedDesc,n=Z.empty(e.doc.length);else{let e=t.changes.filter(i);n=e.changes,r=e.filtered.mapDesc(e.changes).invertedDesc}t=Qt.create(e,n,t.selection&&t.selection.map(r),gt.mapEffects(t.effects,r),t.annotations,t.scrollIntoView)}let n=e.facet(ut);for(let r=n.length-1;r>=0;r--){let i=n[r](t);t=i instanceof Qt?i:Array.isArray(i)&&1==i.length&&i[0]instanceof Qt?i[0]:xt(e,yt(i),!1)}return t}(r):r)}Qt.time=Ot.define(),Qt.userEvent=Ot.define(),Qt.addToHistory=Ot.define(),Qt.remote=Ot.define();const St=[];function yt(t){return null==t?St:Array.isArray(t)?t:[t]}var kt=function(t){return t[t.Word=0]=\"Word\",t[t.Space=1]=\"Space\",t[t.Other=2]=\"Other\",t}(kt||(kt={}));const $t=/[\\u00df\\u0587\\u0590-\\u05f4\\u0600-\\u06ff\\u3040-\\u309f\\u30a0-\\u30ff\\u3400-\\u4db5\\u4e00-\\u9fcc\\uac00-\\ud7af]/;let Pt;try{Pt=new RegExp(\"[\\\\p{Alphabetic}\\\\p{Number}_]\",\"u\")}catch(Ab){}function Tt(t){return e=>{if(!/\\S/.test(e))return kt.Space;if(function(t){if(Pt)return Pt.test(t);for(let e=0;e<t.length;e++){let i=t[e];if(/\\w/.test(i)||i>\"\"&&(i.toUpperCase()!=i.toLowerCase()||$t.test(i)))return!0}return!1}(e))return kt.Word;for(let i=0;i<t.length;i++)if(e.indexOf(t[i])>-1)return kt.Word;return kt.Other}}class Ct{constructor(t,e,i,n,r,s){this.config=t,this.doc=e,this.selection=i,this.values=n,this.status=t.statusTemplate.slice(),this.computeSlot=r,s&&(s._state=this);for(let o=0;o<this.config.dynamicSlots.length;o++)st(this,o<<1);this.computeSlot=null}field(t,e=!0){let i=this.config.address[t.id];if(null!=i)return st(this,i),ot(this,i);if(e)throw new RangeError(\"Field is not present in this state\")}update(...t){return xt(this,t,!0)}applyTransaction(t){let e,i=this.config,{base:n,compartments:r}=i;for(let o of t.effects)o.is(it.reconfigure)?(i&&(r=new Map,i.compartments.forEach((t,e)=>r.set(e,t)),i=null),r.set(o.value.compartment,o.value.extension)):o.is(gt.reconfigure)?(i=null,n=o.value):o.is(gt.appendConfig)&&(i=null,n=yt(n).concat(o.value));if(i)e=t.startState.values.slice();else{i=rt.resolve(n,r,this),e=new Ct(i,this.doc,this.selection,i.dynamicSlots.map(()=>null),(t,e)=>e.reconfigure(t,this),null).values}let s=t.startState.facet(lt)?t.newSelection:t.newSelection.asSingle();new Ct(i,t.newDoc,s,e,(e,i)=>i.update(e,t),t)}replaceSelection(t){return\"string\"==typeof t&&(t=this.toText(t)),this.changeByRange(e=>({changes:{from:e.from,to:e.to,insert:t},range:Y.cursor(e.from+t.length)}))}changeByRange(t){let e=this.selection,i=t(e.ranges[0]),n=this.changes(i.changes),r=[i.range],s=yt(i.effects);for(let o=1;o<e.ranges.length;o++){let i=t(e.ranges[o]),a=this.changes(i.changes),l=a.map(n);for(let t=0;t<o;t++)r[t]=r[t].map(l);let h=n.mapDesc(a,!0);r.push(i.range.map(h)),n=n.compose(l),s=gt.mapEffects(s,l).concat(gt.mapEffects(yt(i.effects),h))}return{changes:n,selection:Y.create(r,e.mainIndex),effects:s}}changes(t=[]){return t instanceof Z?t:Z.of(t,this.doc.length,this.facet(Ct.lineSeparator))}toText(t){return f.of(t.split(this.facet(Ct.lineSeparator)||P))}sliceDoc(t=0,e=this.doc.length){return this.doc.sliceString(t,e,this.lineBreak)}facet(t){let e=this.config.address[t.id];return null==e?t.default:(st(this,e),ot(this,e))}toJSON(t){let e={doc:this.sliceDoc(),selection:this.selection.toJSON()};if(t)for(let i in t){let n=t[i];n instanceof N&&null!=this.config.address[n.id]&&(e[i]=n.spec.toJSON(this.field(t[i]),this))}return e}static fromJSON(t,e={},i){if(!t||\"string\"!=typeof t.doc)throw new RangeError(\"Invalid JSON representation for EditorState\");let n=[];if(i)for(let r in i)if(Object.prototype.hasOwnProperty.call(t,r)){let e=i[r],s=t[r];n.push(e.init(t=>e.spec.fromJSON(s,t)))}return Ct.create({doc:t.doc,selection:Y.fromJSON(t.selection),extensions:e.extensions?n.concat([e.extensions]):n})}static create(t={}){let e=rt.resolve(t.extensions||[],new Map),i=t.doc instanceof f?t.doc:f.of((t.doc||\"\").split(e.staticFacet(Ct.lineSeparator)||P)),n=t.selection?t.selection instanceof Y?t.selection:Y.single(t.selection.anchor,t.selection.head):Y.single(0);return L(n,i.length),e.staticFacet(lt)||(n=n.asSingle()),new Ct(e,i,n,e.dynamicSlots.map(()=>null),(t,e)=>e.create(t),null)}get tabSize(){return this.facet(Ct.tabSize)}get lineBreak(){return this.facet(Ct.lineSeparator)||\"\\n\"}get readOnly(){return this.facet(ft)}phrase(t,...e){for(let i of this.facet(Ct.phrases))if(Object.prototype.hasOwnProperty.call(i,t)){t=i[t];break}return e.length&&(t=t.replace(/\\$(\\$|\\d*)/g,(t,i)=>{if(\"$\"==i)return\"$\";let n=+(i||1);return!n||n>e.length?t:e[n-1]})),t}languageDataAt(t,e,i=-1){let n=[];for(let r of this.facet(at))for(let s of r(this,e,i))Object.prototype.hasOwnProperty.call(s,t)&&n.push(s[t]);return n}charCategorizer(t){let e=this.languageDataAt(\"wordChars\",t);return Tt(e.length?e[0]:\"\")}wordAt(t){let{text:e,from:i,length:n}=this.doc.lineAt(t),r=this.charCategorizer(t),s=t-i,o=t-i;for(;s>0;){let t=S(e,s,!1);if(r(e.slice(t,s))!=kt.Word)break;s=t}for(;o<n;){let t=S(e,o);if(r(e.slice(o,t))!=kt.Word)break;o=t}return s==o?null:Y.range(s+i,o+i)}}function Zt(t,e,i={}){let n={};for(let r of t)for(let t of Object.keys(r)){let e=r[t],s=n[t];if(void 0===s)n[t]=e;else if(s===e||void 0===e);else{if(!Object.hasOwnProperty.call(i,t))throw new Error(\"Config merge conflict for field \"+t);n[t]=i[t](s,e)}}for(let r in e)void 0===n[r]&&(n[r]=e[r]);return n}Ct.allowMultipleSelections=lt,Ct.tabSize=V.define({combine:t=>t.length?t[0]:4}),Ct.lineSeparator=ht,Ct.readOnly=ft,Ct.phrases=V.define({compare(t,e){let i=Object.keys(t),n=Object.keys(e);return i.length==n.length&&i.every(i=>t[i]==e[i])}}),Ct.languageData=at,Ct.changeFilter=ct,Ct.transactionFilter=ut,Ct.transactionExtender=dt,it.reconfigure=gt.define();class Xt{eq(t){return this==t}range(t,e=t){return Mt.create(t,e,this)}}function At(t,e){return t==e||t.constructor==e.constructor&&t.eq(e)}Xt.prototype.startSide=Xt.prototype.endSide=0,Xt.prototype.point=!1,Xt.prototype.mapMode=T.TrackDel;let Mt=class t{constructor(t,e,i){this.from=t,this.to=e,this.value=i}static create(e,i,n){return new t(e,i,n)}};function Rt(t,e){return t.from-e.from||t.value.startSide-e.value.startSide}class zt{constructor(t,e,i,n){this.from=t,this.to=e,this.value=i,this.maxPoint=n}get length(){return this.to[this.to.length-1]}findIndex(t,e,i,n=0){let r=i?this.to:this.from;for(let s=n,o=r.length;;){if(s==o)return s;let n=s+o>>1,a=r[n]-t||(i?this.value[n].endSide:this.value[n].startSide)-e;if(n==s)return a>=0?s:o;a>=0?o=n:s=n+1}}between(t,e,i,n){for(let r=this.findIndex(e,-1e9,!0),s=this.findIndex(i,1e9,!1,r);r<s;r++)if(!1===n(this.from[r]+t,this.to[r]+t,this.value[r]))return!1}map(t,e){let i=[],n=[],r=[],s=-1,o=-1;for(let a=0;a<this.value.length;a++){let l,h,c=this.value[a],u=this.from[a]+t,d=this.to[a]+t;if(u==d){let t=e.mapPos(u,c.startSide,c.mapMode);if(null==t)continue;if(l=h=t,c.startSide!=c.endSide&&(h=e.mapPos(u,c.endSide),h<l))continue}else if(l=e.mapPos(u,c.startSide),h=e.mapPos(d,c.endSide),l>h||l==h&&c.startSide>0&&c.endSide<=0)continue;(h-l||c.endSide-c.startSide)<0||(s<0&&(s=l),c.point&&(o=Math.max(o,h-l)),i.push(c),n.push(l-s),r.push(h-s))}return{mapped:i.length?new zt(n,r,i,o):null,pos:s}}}class _t{constructor(t,e,i,n){this.chunkPos=t,this.chunk=e,this.nextLayer=i,this.maxPoint=n}static create(t,e,i,n){return new _t(t,e,i,n)}get length(){let t=this.chunk.length-1;return t<0?0:Math.max(this.chunkEnd(t),this.nextLayer.length)}get size(){if(this.isEmpty)return 0;let t=this.nextLayer.size;for(let e of this.chunk)t+=e.value.length;return t}chunkEnd(t){return this.chunkPos[t]+this.chunk[t].length}update(t){let{add:e=[],sort:i=!1,filterFrom:n=0,filterTo:r=this.length}=t,s=t.filter;if(0==e.length&&!s)return this;if(i&&(e=e.slice().sort(Rt)),this.isEmpty)return e.length?_t.of(e):this;let o=new Lt(this,null,-1).goto(0),a=0,l=[],h=new Et;for(;o.value||a<e.length;)if(a<e.length&&(o.from-e[a].from||o.startSide-e[a].value.startSide)>=0){let t=e[a++];h.addInner(t.from,t.to,t.value)||l.push(t)}else 1==o.rangeIndex&&o.chunkIndex<this.chunk.length&&(a==e.length||this.chunkEnd(o.chunkIndex)<e[a].from)&&(!s||n>this.chunkEnd(o.chunkIndex)||r<this.chunkPos[o.chunkIndex])&&h.addChunk(this.chunkPos[o.chunkIndex],this.chunk[o.chunkIndex])?o.nextChunk():((!s||n>o.to||r<o.from||s(o.from,o.to,o.value))&&(h.addInner(o.from,o.to,o.value)||l.push(Mt.create(o.from,o.to,o.value))),o.next());return h.finishInner(this.nextLayer.isEmpty&&!l.length?_t.empty:this.nextLayer.update({add:l,filter:s,filterFrom:n,filterTo:r}))}map(t){if(t.empty||this.isEmpty)return this;let e=[],i=[],n=-1;for(let s=0;s<this.chunk.length;s++){let r=this.chunkPos[s],o=this.chunk[s],a=t.touchesRange(r,r+o.length);if(!1===a)n=Math.max(n,o.maxPoint),e.push(o),i.push(t.mapPos(r));else if(!0===a){let{mapped:s,pos:a}=o.map(r,t);s&&(n=Math.max(n,s.maxPoint),e.push(s),i.push(a))}}let r=this.nextLayer.map(t);return 0==e.length?r:new _t(i,e,r||_t.empty,n)}between(t,e,i){if(!this.isEmpty){for(let n=0;n<this.chunk.length;n++){let r=this.chunkPos[n],s=this.chunk[n];if(e>=r&&t<=r+s.length&&!1===s.between(r,t-r,e-r,i))return}this.nextLayer.between(t,e,i)}}iter(t=0){return qt.from([this]).goto(t)}get isEmpty(){return this.nextLayer==this}static iter(t,e=0){return qt.from(t).goto(e)}static compare(t,e,i,n,r=-1){let s=t.filter(t=>t.maxPoint>0||!t.isEmpty&&t.maxPoint>=r),o=e.filter(t=>t.maxPoint>0||!t.isEmpty&&t.maxPoint>=r),a=Yt(s,o,i),l=new Wt(s,a,r),h=new Wt(o,a,r);i.iterGaps((t,e,i)=>Dt(l,t,h,e,i,n)),i.empty&&0==i.length&&Dt(l,0,h,0,0,n)}static eq(t,e,i=0,n){null==n&&(n=999999999);let r=t.filter(t=>!t.isEmpty&&e.indexOf(t)<0),s=e.filter(e=>!e.isEmpty&&t.indexOf(e)<0);if(r.length!=s.length)return!1;if(!r.length)return!0;let o=Yt(r,s),a=new Wt(r,o,0).goto(i),l=new Wt(s,o,0).goto(i);for(;;){if(a.to!=l.to||!Bt(a.active,l.active)||a.point&&(!l.point||!At(a.point,l.point)))return!1;if(a.to>n)return!0;a.next(),l.next()}}static spans(t,e,i,n,r=-1){let s=new Wt(t,null,r).goto(e),o=e,a=s.openStart;for(;;){let t=Math.min(s.to,i);if(s.point){let i=s.activeForPoint(s.to),r=s.pointFrom<e?i.length+1:s.point.startSide<0?i.length:Math.min(i.length,a);n.point(o,t,s.point,i,r,s.pointRank),a=Math.min(s.openEnd(t),i.length)}else t>o&&(n.span(o,t,s.active,a),a=s.openEnd(t));if(s.to>i)return a+(s.point&&s.to>i?1:0);o=s.to,s.next()}}static of(t,e=!1){let i=new Et;for(let n of t instanceof Mt?[t]:e?function(t){if(t.length>1)for(let e=t[0],i=1;i<t.length;i++){let n=t[i];if(Rt(e,n)>0)return t.slice().sort(Rt);e=n}return t}(t):t)i.add(n.from,n.to,n.value);return i.finish()}static join(t){if(!t.length)return _t.empty;let e=t[t.length-1];for(let i=t.length-2;i>=0;i--)for(let n=t[i];n!=_t.empty;n=n.nextLayer)e=new _t(n.chunkPos,n.chunk,e,Math.max(n.maxPoint,e.maxPoint));return e}}_t.empty=new _t([],[],null,-1),_t.empty.nextLayer=_t.empty;class Et{finishChunk(t){this.chunks.push(new zt(this.from,this.to,this.value,this.maxPoint)),this.chunkPos.push(this.chunkStart),this.chunkStart=-1,this.setMaxPoint=Math.max(this.setMaxPoint,this.maxPoint),this.maxPoint=-1,t&&(this.from=[],this.to=[],this.value=[])}constructor(){this.chunks=[],this.chunkPos=[],this.chunkStart=-1,this.last=null,this.lastFrom=-1e9,this.lastTo=-1e9,this.from=[],this.to=[],this.value=[],this.maxPoint=-1,this.setMaxPoint=-1,this.nextLayer=null}add(t,e,i){this.addInner(t,e,i)||(this.nextLayer||(this.nextLayer=new Et)).add(t,e,i)}addInner(t,e,i){let n=t-this.lastTo||i.startSide-this.last.endSide;if(n<=0&&(t-this.lastFrom||i.startSide-this.last.startSide)<0)throw new Error(\"Ranges must be added sorted by `from` position and `startSide`\");return!(n<0)&&(250==this.from.length&&this.finishChunk(!0),this.chunkStart<0&&(this.chunkStart=t),this.from.push(t-this.chunkStart),this.to.push(e-this.chunkStart),this.last=i,this.lastFrom=t,this.lastTo=e,this.value.push(i),i.point&&(this.maxPoint=Math.max(this.maxPoint,e-t)),!0)}addChunk(t,e){if((t-this.lastTo||e.value[0].startSide-this.last.endSide)<0)return!1;this.from.length&&this.finishChunk(!0),this.setMaxPoint=Math.max(this.setMaxPoint,e.maxPoint),this.chunks.push(e),this.chunkPos.push(t);let i=e.value.length-1;return this.last=e.value[i],this.lastFrom=e.from[i]+t,this.lastTo=e.to[i]+t,!0}finish(){return this.finishInner(_t.empty)}finishInner(t){if(this.from.length&&this.finishChunk(!1),0==this.chunks.length)return t;let e=_t.create(this.chunkPos,this.chunks,this.nextLayer?this.nextLayer.finishInner(t):t,this.setMaxPoint);return this.from=null,e}}function Yt(t,e,i){let n=new Map;for(let s of t)for(let t=0;t<s.chunk.length;t++)s.chunk[t].maxPoint<=0&&n.set(s.chunk[t],s.chunkPos[t]);let r=new Set;for(let s of e)for(let t=0;t<s.chunk.length;t++){let e=n.get(s.chunk[t]);null==e||(i?i.mapPos(e):e)!=s.chunkPos[t]||(null==i?void 0:i.touchesRange(e,e+s.chunk[t].length))||r.add(s.chunk[t])}return r}class Lt{constructor(t,e,i,n=0){this.layer=t,this.skip=e,this.minPoint=i,this.rank=n}get startSide(){return this.value?this.value.startSide:0}get endSide(){return this.value?this.value.endSide:0}goto(t,e=-1e9){return this.chunkIndex=this.rangeIndex=0,this.gotoInner(t,e,!1),this}gotoInner(t,e,i){for(;this.chunkIndex<this.layer.chunk.length;){let e=this.layer.chunk[this.chunkIndex];if(!(this.skip&&this.skip.has(e)||this.layer.chunkEnd(this.chunkIndex)<t||e.maxPoint<this.minPoint))break;this.chunkIndex++,i=!1}if(this.chunkIndex<this.layer.chunk.length){let n=this.layer.chunk[this.chunkIndex].findIndex(t-this.layer.chunkPos[this.chunkIndex],e,!0);(!i||this.rangeIndex<n)&&this.setRangeIndex(n)}this.next()}forward(t,e){(this.to-t||this.endSide-e)<0&&this.gotoInner(t,e,!0)}next(){for(;;){if(this.chunkIndex==this.layer.chunk.length){this.from=this.to=1e9,this.value=null;break}{let t=this.layer.chunkPos[this.chunkIndex],e=this.layer.chunk[this.chunkIndex],i=t+e.from[this.rangeIndex];if(this.from=i,this.to=t+e.to[this.rangeIndex],this.value=e.value[this.rangeIndex],this.setRangeIndex(this.rangeIndex+1),this.minPoint<0||this.value.point&&this.to-this.from>=this.minPoint)break}}}setRangeIndex(t){if(t==this.layer.chunk[this.chunkIndex].value.length){if(this.chunkIndex++,this.skip)for(;this.chunkIndex<this.layer.chunk.length&&this.skip.has(this.layer.chunk[this.chunkIndex]);)this.chunkIndex++;this.rangeIndex=0}else this.rangeIndex=t}nextChunk(){this.chunkIndex++,this.rangeIndex=0,this.next()}compare(t){return this.from-t.from||this.startSide-t.startSide||this.rank-t.rank||this.to-t.to||this.endSide-t.endSide}}class qt{constructor(t){this.heap=t}static from(t,e=null,i=-1){let n=[];for(let r=0;r<t.length;r++)for(let s=t[r];!s.isEmpty;s=s.nextLayer)s.maxPoint>=i&&n.push(new Lt(s,e,i,r));return 1==n.length?n[0]:new qt(n)}get startSide(){return this.value?this.value.startSide:0}goto(t,e=-1e9){for(let i of this.heap)i.goto(t,e);for(let i=this.heap.length>>1;i>=0;i--)Vt(this.heap,i);return this.next(),this}forward(t,e){for(let i of this.heap)i.forward(t,e);for(let i=this.heap.length>>1;i>=0;i--)Vt(this.heap,i);(this.to-t||this.value.endSide-e)<0&&this.next()}next(){if(0==this.heap.length)this.from=this.to=1e9,this.value=null,this.rank=-1;else{let t=this.heap[0];this.from=t.from,this.to=t.to,this.value=t.value,this.rank=t.rank,t.value&&t.next(),Vt(this.heap,0)}}}function Vt(t,e){for(let i=t[e];;){let n=1+(e<<1);if(n>=t.length)break;let r=t[n];if(n+1<t.length&&r.compare(t[n+1])>=0&&(r=t[n+1],n++),i.compare(r)<0)break;t[n]=i,t[e]=r,e=n}}class Wt{constructor(t,e,i){this.minPoint=i,this.active=[],this.activeTo=[],this.activeRank=[],this.minActive=-1,this.point=null,this.pointFrom=0,this.pointRank=0,this.to=-1e9,this.endSide=0,this.openStart=-1,this.cursor=qt.from(t,e,i)}goto(t,e=-1e9){return this.cursor.goto(t,e),this.active.length=this.activeTo.length=this.activeRank.length=0,this.minActive=-1,this.to=t,this.endSide=e,this.openStart=-1,this.next(),this}forward(t,e){for(;this.minActive>-1&&(this.activeTo[this.minActive]-t||this.active[this.minActive].endSide-e)<0;)this.removeActive(this.minActive);this.cursor.forward(t,e)}removeActive(t){jt(this.active,t),jt(this.activeTo,t),jt(this.activeRank,t),this.minActive=Gt(this.active,this.activeTo)}addActive(t){let e=0,{value:i,to:n,rank:r}=this.cursor;for(;e<this.activeRank.length&&(r-this.activeRank[e]||n-this.activeTo[e])>0;)e++;It(this.active,e,i),It(this.activeTo,e,n),It(this.activeRank,e,r),t&&It(t,e,this.cursor.from),this.minActive=Gt(this.active,this.activeTo)}next(){let t=this.to,e=this.point;this.point=null;let i=this.openStart<0?[]:null;for(;;){let n=this.minActive;if(n>-1&&(this.activeTo[n]-this.cursor.from||this.active[n].endSide-this.cursor.startSide)<0){if(this.activeTo[n]>t){this.to=this.activeTo[n],this.endSide=this.active[n].endSide;break}this.removeActive(n),i&&jt(i,n)}else{if(!this.cursor.value){this.to=this.endSide=1e9;break}if(this.cursor.from>t){this.to=this.cursor.from,this.endSide=this.cursor.startSide;break}{let t=this.cursor.value;if(t.point){if(!(e&&this.cursor.to==this.to&&this.cursor.from<this.cursor.to)){this.point=t,this.pointFrom=this.cursor.from,this.pointRank=this.cursor.rank,this.to=this.cursor.to,this.endSide=t.endSide,this.cursor.next(),this.forward(this.to,this.endSide);break}this.cursor.next()}else this.addActive(i),this.cursor.next()}}}if(i){this.openStart=0;for(let e=i.length-1;e>=0&&i[e]<t;e--)this.openStart++}}activeForPoint(t){if(!this.active.length)return this.active;let e=[];for(let i=this.active.length-1;i>=0&&!(this.activeRank[i]<this.pointRank);i--)(this.activeTo[i]>t||this.activeTo[i]==t&&this.active[i].endSide>=this.point.endSide)&&e.push(this.active[i]);return e.reverse()}openEnd(t){let e=0;for(let i=this.activeTo.length-1;i>=0&&this.activeTo[i]>t;i--)e++;return e}}function Dt(t,e,i,n,r,s){t.goto(e),i.goto(n);let o=n+r,a=n,l=n-e,h=!!s.boundChange;for(let c=!1;;){let e=t.to+l-i.to,n=e||t.endSide-i.endSide,r=n<0?t.to+l:i.to,u=Math.min(r,o);if(t.point||i.point?(t.point&&i.point&&At(t.point,i.point)&&Bt(t.activeForPoint(t.to),i.activeForPoint(i.to))||s.comparePoint(a,u,t.point,i.point),c=!1):(c&&s.boundChange(a),u>a&&!Bt(t.active,i.active)&&s.compareRange(a,u,t.active,i.active),h&&u<o&&(e||t.openEnd(r)!=i.openEnd(r))&&(c=!0)),r>o)break;a=r,n<=0&&t.next(),n>=0&&i.next()}}function Bt(t,e){if(t.length!=e.length)return!1;for(let i=0;i<t.length;i++)if(t[i]!=e[i]&&!At(t[i],e[i]))return!1;return!0}function jt(t,e){for(let i=e,n=t.length-1;i<n;i++)t[i]=t[i+1];t.pop()}function It(t,e,i){for(let n=t.length-1;n>=e;n--)t[n+1]=t[n];t[e]=i}function Gt(t,e){let i=-1,n=1e9;for(let r=0;r<e.length;r++)(e[r]-n||t[r].endSide-t[i].endSide)<0&&(i=r,n=e[r]);return i}function Nt(t,e,i=t.length){let n=0;for(let r=0;r<i&&r<t.length;)9==t.charCodeAt(r)?(n+=e-n%e,r++):(n++,r=S(t,r));return n}function Ut(t,e,i,n){for(let r=0,s=0;;){if(s>=e)return r;if(r==t.length)break;s+=9==t.charCodeAt(r)?i-s%i:1,r=S(t,r)}return!0===n?-1:t.length}const Ht=\"undefined\"==typeof Symbol?\"__ͼ\":Symbol.for(\"ͼ\"),Ft=\"undefined\"==typeof Symbol?\"__styleSet\"+Math.floor(1e8*Math.random()):Symbol(\"styleSet\"),Kt=\"undefined\"!=typeof globalThis?globalThis:\"undefined\"!=typeof window?window:{};class Jt{constructor(t,e){this.rules=[];let{finish:i}=e||{};function n(t){return/^@/.test(t)?[t]:t.split(/,\\s*/)}function r(t,e,s,o){let a=[],l=/^@(\\w+)\\b/.exec(t[0]),h=l&&\"keyframes\"==l[1];if(l&&null==e)return s.push(t[0]+\";\");for(let i in e){let o=e[i];if(/&/.test(i))r(i.split(/,\\s*/).map(e=>t.map(t=>e.replace(/&/,t))).reduce((t,e)=>t.concat(e)),o,s);else if(o&&\"object\"==typeof o){if(!l)throw new RangeError(\"The value of a property (\"+i+\") should be a primitive value.\");r(n(i),o,a,h)}else null!=o&&a.push(i.replace(/_.*/,\"\").replace(/[A-Z]/g,t=>\"-\"+t.toLowerCase())+\": \"+o+\";\")}(a.length||h)&&s.push((!i||l||o?t:t.map(i)).join(\", \")+\" {\"+a.join(\" \")+\"}\")}for(let s in t)r(n(s),t[s],this.rules)}getRules(){return this.rules.join(\"\\n\")}static newName(){let t=Kt[Ht]||1;return Kt[Ht]=t+1,\"ͼ\"+t.toString(36)}static mount(t,e,i){let n=t[Ft],r=i&&i.nonce;n?r&&n.setNonce(r):n=new ee(t,r),n.mount(Array.isArray(e)?e:[e],t)}}let te=new Map;class ee{constructor(t,e){let i=t.ownerDocument||t,n=i.defaultView;if(!t.head&&t.adoptedStyleSheets&&n.CSSStyleSheet){let e=te.get(i);if(e)return t[Ft]=e;this.sheet=new n.CSSStyleSheet,te.set(i,this)}else this.styleTag=i.createElement(\"style\"),e&&this.styleTag.setAttribute(\"nonce\",e);this.modules=[],t[Ft]=this}mount(t,e){let i=this.sheet,n=0,r=0;for(let s=0;s<t.length;s++){let e=t[s],o=this.modules.indexOf(e);if(o<r&&o>-1&&(this.modules.splice(o,1),r--,o=-1),-1==o){if(this.modules.splice(r++,0,e),i)for(let t=0;t<e.rules.length;t++)i.insertRule(e.rules[t],n++)}else{for(;r<o;)n+=this.modules[r++].rules.length;n+=e.rules.length,r++}}if(i)e.adoptedStyleSheets.indexOf(this.sheet)<0&&(e.adoptedStyleSheets=[this.sheet,...e.adoptedStyleSheets]);else{let t=\"\";for(let e=0;e<this.modules.length;e++)t+=this.modules[e].getRules()+\"\\n\";this.styleTag.textContent=t;let i=e.head||e;this.styleTag.parentNode!=i&&i.insertBefore(this.styleTag,i.firstChild)}}setNonce(t){this.styleTag&&this.styleTag.getAttribute(\"nonce\")!=t&&this.styleTag.setAttribute(\"nonce\",t)}}for(var ie={8:\"Backspace\",9:\"Tab\",10:\"Enter\",12:\"NumLock\",13:\"Enter\",16:\"Shift\",17:\"Control\",18:\"Alt\",20:\"CapsLock\",27:\"Escape\",32:\" \",33:\"PageUp\",34:\"PageDown\",35:\"End\",36:\"Home\",37:\"ArrowLeft\",38:\"ArrowUp\",39:\"ArrowRight\",40:\"ArrowDown\",44:\"PrintScreen\",45:\"Insert\",46:\"Delete\",59:\";\",61:\"=\",91:\"Meta\",92:\"Meta\",106:\"*\",107:\"+\",108:\",\",109:\"-\",110:\".\",111:\"/\",144:\"NumLock\",145:\"ScrollLock\",160:\"Shift\",161:\"Shift\",162:\"Control\",163:\"Control\",164:\"Alt\",165:\"Alt\",173:\"-\",186:\";\",187:\"=\",188:\",\",189:\"-\",190:\".\",191:\"/\",192:\"`\",219:\"[\",220:\"\\\\\",221:\"]\",222:\"'\"},ne={48:\")\",49:\"!\",50:\"@\",51:\"#\",52:\"$\",53:\"%\",54:\"^\",55:\"&\",56:\"*\",57:\"(\",59:\":\",61:\"+\",173:\"_\",186:\":\",187:\"+\",188:\"<\",189:\"_\",190:\">\",191:\"?\",192:\"~\",219:\"{\",220:\"|\",221:\"}\",222:'\"'},re=\"undefined\"!=typeof navigator&&/Mac/.test(navigator.platform),se=\"undefined\"!=typeof navigator&&/MSIE \\d|Trident\\/(?:[7-9]|\\d{2,})\\..*rv:(\\d+)/.exec(navigator.userAgent),oe=0;oe<10;oe++)ie[48+oe]=ie[96+oe]=String(oe);for(oe=1;oe<=24;oe++)ie[oe+111]=\"F\"+oe;for(oe=65;oe<=90;oe++)ie[oe]=String.fromCharCode(oe+32),ne[oe]=String.fromCharCode(oe);for(var ae in ie)ne.hasOwnProperty(ae)||(ne[ae]=ie[ae]);function le(){var t=arguments[0];\"string\"==typeof t&&(t=document.createElement(t));var e=1,i=arguments[1];if(i&&\"object\"==typeof i&&null==i.nodeType&&!Array.isArray(i)){for(var n in i)if(Object.prototype.hasOwnProperty.call(i,n)){var r=i[n];\"string\"==typeof r?t.setAttribute(n,r):null!=r&&(t[n]=r)}e++}for(;e<arguments.length;e++)he(t,arguments[e]);return t}function he(t,e){if(\"string\"==typeof e)t.appendChild(document.createTextNode(e));else if(null==e);else if(null!=e.nodeType)t.appendChild(e);else{if(!Array.isArray(e))throw new RangeError(\"Unsupported child node: \"+e);for(var i=0;i<e.length;i++)he(t,e[i])}}let ce=\"undefined\"!=typeof navigator?navigator:{userAgent:\"\",vendor:\"\",platform:\"\"},ue=\"undefined\"!=typeof document?document:{documentElement:{style:{}}};const de=/Edge\\/(\\d+)/.exec(ce.userAgent),fe=/MSIE \\d/.test(ce.userAgent),Oe=/Trident\\/(?:[7-9]|\\d{2,})\\..*rv:(\\d+)/.exec(ce.userAgent),pe=!!(fe||Oe||de),me=!pe&&/gecko\\/(\\d+)/i.test(ce.userAgent),ge=!pe&&/Chrome\\/(\\d+)/.exec(ce.userAgent),Qe=\"webkitFontSmoothing\"in ue.documentElement.style,be=!pe&&/Apple Computer/.test(ce.vendor),ve=be&&(/Mobile\\/\\w+/.test(ce.userAgent)||ce.maxTouchPoints>2);var we={mac:ve||/Mac/.test(ce.platform),windows:/Win/.test(ce.platform),linux:/Linux|X11/.test(ce.platform),ie:pe,ie_version:fe?ue.documentMode||6:Oe?+Oe[1]:de?+de[1]:0,gecko:me,gecko_version:me?+(/Firefox\\/(\\d+)/.exec(ce.userAgent)||[0,0])[1]:0,chrome:!!ge,chrome_version:ge?+ge[1]:0,ios:ve,android:/Android\\b/.test(ce.userAgent),webkit_version:Qe?+(/\\bAppleWebKit\\/(\\d+)/.exec(ce.userAgent)||[0,0])[1]:0,safari:be,safari_version:be?+(/\\bVersion\\/(\\d+(\\.\\d+)?)/.exec(ce.userAgent)||[0,0])[1]:0,tabSize:null!=ue.documentElement.style.tabSize?\"tab-size\":\"-moz-tab-size\"};function xe(t,e){for(let i in t)\"class\"==i&&e.class?e.class+=\" \"+t.class:\"style\"==i&&e.style?e.style+=\";\"+t.style:e[i]=t[i];return e}const Se=Object.create(null);function ye(t,e,i){if(t==e)return!0;t||(t=Se),e||(e=Se);let n=Object.keys(t),r=Object.keys(e);if(n.length-0!=r.length-0)return!1;for(let s of n)if(s!=i&&(-1==r.indexOf(s)||t[s]!==e[s]))return!1;return!0}function ke(t,e,i){let n=!1;if(e)for(let r in e)i&&r in i||(n=!0,\"style\"==r?t.style.cssText=\"\":t.removeAttribute(r));if(i)for(let r in i)e&&e[r]==i[r]||(n=!0,\"style\"==r?t.style.cssText=i[r]:t.setAttribute(r,i[r]));return n}function $e(t){let e=Object.create(null);for(let i=0;i<t.attributes.length;i++){let n=t.attributes[i];e[n.name]=n.value}return e}class Pe{eq(t){return!1}updateDOM(t,e){return!1}compare(t){return this==t||this.constructor==t.constructor&&this.eq(t)}get estimatedHeight(){return-1}get lineBreaks(){return 0}ignoreEvent(t){return!0}coordsAt(t,e,i){return null}get isHidden(){return!1}get editable(){return!1}destroy(t){}}var Te=function(t){return t[t.Text=0]=\"Text\",t[t.WidgetBefore=1]=\"WidgetBefore\",t[t.WidgetAfter=2]=\"WidgetAfter\",t[t.WidgetRange=3]=\"WidgetRange\",t}(Te||(Te={}));class Ce extends Xt{constructor(t,e,i,n){super(),this.startSide=t,this.endSide=e,this.widget=i,this.spec=n}get heightRelevant(){return!1}static mark(t){return new Ze(t)}static widget(t){let e=Math.max(-1e4,Math.min(1e4,t.side||0)),i=!!t.block;return e+=i&&!t.inlineOrder?e>0?3e8:-4e8:e>0?1e8:-1e8,new Ae(t,e,e,i,t.widget||null,!1)}static replace(t){let e,i,n=!!t.block;if(t.isBlockGap)e=-5e8,i=4e8;else{let{start:r,end:s}=Me(t,n);e=(r?n?-3e8:-1:5e8)-1,i=1+(s?n?2e8:1:-6e8)}return new Ae(t,e,i,n,t.widget||null,!0)}static line(t){return new Xe(t)}static set(t,e=!1){return _t.of(t,e)}hasHeight(){return!!this.widget&&this.widget.estimatedHeight>-1}}Ce.none=_t.empty;class Ze extends Ce{constructor(t){let{start:e,end:i}=Me(t);super(e?-1:5e8,i?1:-6e8,null,t),this.tagName=t.tagName||\"span\",this.attrs=t.class&&t.attributes?xe(t.attributes,{class:t.class}):t.class?{class:t.class}:t.attributes||Se}eq(t){return this==t||t instanceof Ze&&this.tagName==t.tagName&&ye(this.attrs,t.attrs)}range(t,e=t){if(t>=e)throw new RangeError(\"Mark decorations may not be empty\");return super.range(t,e)}}Ze.prototype.point=!1;class Xe extends Ce{constructor(t){super(-2e8,-2e8,null,t)}eq(t){return t instanceof Xe&&this.spec.class==t.spec.class&&ye(this.spec.attributes,t.spec.attributes)}range(t,e=t){if(e!=t)throw new RangeError(\"Line decoration ranges must be zero-length\");return super.range(t,e)}}Xe.prototype.mapMode=T.TrackBefore,Xe.prototype.point=!0;class Ae extends Ce{constructor(t,e,i,n,r,s){super(e,i,r,t),this.block=n,this.isReplace=s,this.mapMode=n?e<=0?T.TrackBefore:T.TrackAfter:T.TrackDel}get type(){return this.startSide!=this.endSide?Te.WidgetRange:this.startSide<=0?Te.WidgetBefore:Te.WidgetAfter}get heightRelevant(){return this.block||!!this.widget&&(this.widget.estimatedHeight>=5||this.widget.lineBreaks>0)}eq(t){return t instanceof Ae&&(e=this.widget,i=t.widget,e==i||!!(e&&i&&e.compare(i)))&&this.block==t.block&&this.startSide==t.startSide&&this.endSide==t.endSide;var e,i}range(t,e=t){if(this.isReplace&&(t>e||t==e&&this.startSide>0&&this.endSide<=0))throw new RangeError(\"Invalid range for replacement decoration\");if(!this.isReplace&&e!=t)throw new RangeError(\"Widget decorations can only have zero-length ranges\");return super.range(t,e)}}function Me(t,e=!1){let{inclusiveStart:i,inclusiveEnd:n}=t;return null==i&&(i=t.inclusive),null==n&&(n=t.inclusive),{start:null!=i?i:e,end:null!=n?n:e}}function Re(t,e,i,n=0){let r=i.length-1;r>=0&&i[r]+n>=t?i[r]=Math.max(i[r],e):i.push(t,e)}Ae.prototype.point=!0;class ze extends Xt{constructor(t,e){super(),this.tagName=t,this.attributes=e}eq(t){return t==this||t instanceof ze&&this.tagName==t.tagName&&ye(this.attributes,t.attributes)}static create(t){return new ze(t.tagName,t.attributes||Se)}static set(t,e=!1){return _t.of(t,e)}}function _e(t){let e;return e=11==t.nodeType?t.getSelection?t:t.ownerDocument:t,e.getSelection()}function Ee(t,e){return!!e&&(t==e||t.contains(1!=e.nodeType?e.parentNode:e))}function Ye(t,e){if(!e.anchorNode)return!1;try{return Ee(t,e.anchorNode)}catch(Ab){return!1}}function Le(t){return 3==t.nodeType?Ke(t,0,t.nodeValue.length).getClientRects():1==t.nodeType?t.getClientRects():[]}function qe(t,e,i,n){return!!i&&(De(t,e,i,n,-1)||De(t,e,i,n,1))}function Ve(t){for(var e=0;;e++)if(!(t=t.previousSibling))return e}function We(t){return 1==t.nodeType&&/^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\\d|SECTION|PRE)$/.test(t.nodeName)}function De(t,e,i,n,r){for(;;){if(t==i&&e==n)return!0;if(e==(r<0?0:Be(t))){if(\"DIV\"==t.nodeName)return!1;let i=t.parentNode;if(!i||1!=i.nodeType)return!1;e=Ve(t)+(r<0?0:1),t=i}else{if(1!=t.nodeType)return!1;if(1==(t=t.childNodes[e+(r<0?-1:0)]).nodeType&&\"false\"==t.contentEditable)return!1;e=r<0?Be(t):0}}}function Be(t){return 3==t.nodeType?t.nodeValue.length:t.childNodes.length}function je(t,e){let i=e?t.left:t.right;return{left:i,right:i,top:t.top,bottom:t.bottom}}function Ie(t){let e=t.visualViewport;return e?{left:0,right:e.width,top:0,bottom:e.height}:{left:0,right:t.innerWidth,top:0,bottom:t.innerHeight}}function Ge(t,e){let i=e.width/t.offsetWidth,n=e.height/t.offsetHeight;return(i>.995&&i<1.005||!isFinite(i)||Math.abs(e.width-t.offsetWidth)<1)&&(i=1),(n>.995&&n<1.005||!isFinite(n)||Math.abs(e.height-t.offsetHeight)<1)&&(n=1),{scaleX:i,scaleY:n}}ze.prototype.startSide=ze.prototype.endSide=-1;class Ne{constructor(){this.anchorNode=null,this.anchorOffset=0,this.focusNode=null,this.focusOffset=0}eq(t){return this.anchorNode==t.anchorNode&&this.anchorOffset==t.anchorOffset&&this.focusNode==t.focusNode&&this.focusOffset==t.focusOffset}setRange(t){let{anchorNode:e,focusNode:i}=t;this.set(e,Math.min(t.anchorOffset,e?Be(e):0),i,Math.min(t.focusOffset,i?Be(i):0))}set(t,e,i,n){this.anchorNode=t,this.anchorOffset=e,this.focusNode=i,this.focusOffset=n}}let Ue,He=null;function Fe(t){if(t.setActive)return t.setActive();if(He)return t.focus(He);let e=[];for(let i=t;i&&(e.push(i,i.scrollTop,i.scrollLeft),i!=i.ownerDocument);i=i.parentNode);if(t.focus(null==He?{get preventScroll(){return He={preventScroll:!0},!0}}:void 0),!He){He=!1;for(let t=0;t<e.length;){let i=e[t++],n=e[t++],r=e[t++];i.scrollTop!=n&&(i.scrollTop=n),i.scrollLeft!=r&&(i.scrollLeft=r)}}}function Ke(t,e,i=e){let n=Ue||(Ue=document.createRange());return n.setEnd(t,i),n.setStart(t,e),n}function Je(t,e,i,n){let r={key:e,code:e,keyCode:i,which:i,cancelable:!0};n&&({altKey:r.altKey,ctrlKey:r.ctrlKey,shiftKey:r.shiftKey,metaKey:r.metaKey}=n);let s=new KeyboardEvent(\"keydown\",r);s.synthetic=!0,t.dispatchEvent(s);let o=new KeyboardEvent(\"keyup\",r);return o.synthetic=!0,t.dispatchEvent(o),s.defaultPrevented||o.defaultPrevented}function ti(t){return t.scrollTop>Math.max(1,t.scrollHeight-t.clientHeight-4)}function ei(t,e){for(let i=t,n=e;;){if(3==i.nodeType&&n>0)return{node:i,offset:n};if(1==i.nodeType&&n>0){if(\"false\"==i.contentEditable)return null;i=i.childNodes[n-1],n=Be(i)}else{if(!i.parentNode||We(i))return null;n=Ve(i),i=i.parentNode}}}function ii(t,e){for(let i=t,n=e;;){if(3==i.nodeType&&n<i.nodeValue.length)return{node:i,offset:n};if(1==i.nodeType&&n<i.childNodes.length){if(\"false\"==i.contentEditable)return null;i=i.childNodes[n],n=0}else{if(!i.parentNode||We(i))return null;n=Ve(i)+1,i=i.parentNode}}}we.safari&&we.safari_version>=26&&(He=!1);class ni{constructor(t,e,i=!0){this.node=t,this.offset=e,this.precise=i}static before(t,e){return new ni(t.parentNode,Ve(t),e)}static after(t,e){return new ni(t.parentNode,Ve(t)+1,e)}}var ri=function(t){return t[t.LTR=0]=\"LTR\",t[t.RTL=1]=\"RTL\",t}(ri||(ri={}));const si=ri.LTR,oi=ri.RTL;function ai(t){let e=[];for(let i=0;i<t.length;i++)e.push(1<<+t[i]);return e}const li=ai(\"88888888888888888888888888888888888666888888787833333333337888888000000000000000000000000008888880000000000000000000000000088888888888888888888888888888888888887866668888088888663380888308888800000000000000000000000800000000000000000000000000000008\"),hi=ai(\"4444448826627288999999999992222222222222222222222222222222222222222222222229999999999999999999994444444444644222822222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222999999949999999229989999223333333333\"),ci=Object.create(null),ui=[];for(let Mb of[\"()\",\"[]\",\"{}\"]){let t=Mb.charCodeAt(0),e=Mb.charCodeAt(1);ci[t]=e,ci[e]=-t}function di(t){return t<=247?li[t]:1424<=t&&t<=1524?2:1536<=t&&t<=1785?hi[t-1536]:1774<=t&&t<=2220?4:8192<=t&&t<=8204?256:64336<=t&&t<=65023?4:1}const fi=/[\\u0590-\\u05f4\\u0600-\\u06ff\\u0700-\\u08ac\\ufb50-\\ufdff]/;class Oi{get dir(){return this.level%2?oi:si}constructor(t,e,i){this.from=t,this.to=e,this.level=i}side(t,e){return this.dir==e==t?this.to:this.from}forward(t,e){return t==(this.dir==e)}static find(t,e,i,n){let r=-1;for(let s=0;s<t.length;s++){let o=t[s];if(o.from<=e&&o.to>=e){if(o.level==i)return s;(r<0||(0!=n?n<0?o.from<e:o.to>e:t[r].level>o.level))&&(r=s)}}if(r<0)throw new RangeError(\"Index out of range\");return r}}function pi(t,e){if(t.length!=e.length)return!1;for(let i=0;i<t.length;i++){let n=t[i],r=e[i];if(n.from!=r.from||n.to!=r.to||n.direction!=r.direction||!pi(n.inner,r.inner))return!1}return!0}const mi=[];function gi(t,e,i,n,r,s,o){let a=n%2?2:1;if(n%2==r%2)for(let l=e,h=0;l<i;){let e=!0,c=!1;if(h==s.length||l<s[h].from){let t=mi[l];t!=a&&(e=!1,c=16==t)}let u=e||1!=a?null:[],d=e?n:n+1,f=l;t:for(;;)if(h<s.length&&f==s[h].from){if(c)break t;let O=s[h];if(!e)for(let t=O.to,e=h+1;;){if(t==i)break t;if(!(e<s.length&&s[e].from==t)){if(mi[t]==a)break t;break}t=s[e++].to}if(h++,u)u.push(O);else{O.from>l&&o.push(new Oi(l,O.from,d)),Qi(t,O.direction==si!=!(d%2)?n+1:n,r,O.inner,O.from,O.to,o),l=O.to}f=O.to}else{if(f==i||(e?mi[f]!=a:mi[f]==a))break;f++}u?gi(t,l,f,n+1,r,u,o):l<f&&o.push(new Oi(l,f,d)),l=f}else for(let l=i,h=s.length;l>e;){let i=!0,c=!1;if(!h||l>s[h-1].to){let t=mi[l-1];t!=a&&(i=!1,c=16==t)}let u=i||1!=a?null:[],d=i?n:n+1,f=l;t:for(;;)if(h&&f==s[h-1].to){if(c)break t;let O=s[--h];if(!i)for(let t=O.from,i=h;;){if(t==e)break t;if(!i||s[i-1].to!=t){if(mi[t-1]==a)break t;break}t=s[--i].from}if(u)u.push(O);else{O.to<l&&o.push(new Oi(O.to,l,d)),Qi(t,O.direction==si!=!(d%2)?n+1:n,r,O.inner,O.from,O.to,o),l=O.from}f=O.from}else{if(f==e||(i?mi[f-1]!=a:mi[f-1]==a))break;f--}u?gi(t,f,l,n+1,r,u,o):f<l&&o.push(new Oi(f,l,d)),l=f}}function Qi(t,e,i,n,r,s,o){let a=e%2?2:1;!function(t,e,i,n,r){for(let s=0;s<=n.length;s++){let o=s?n[s-1].to:e,a=s<n.length?n[s].from:i,l=s?256:r;for(let e=o,i=l,n=l;e<a;e++){let r=di(t.charCodeAt(e));512==r?r=i:8==r&&4==n&&(r=16),mi[e]=4==r?2:r,7&r&&(n=r),i=r}for(let t=o,e=l,n=l;t<a;t++){let r=mi[t];if(128==r)t<a-1&&e==mi[t+1]&&24&e?r=mi[t]=e:mi[t]=256;else if(64==r){let r=t+1;for(;r<a&&64==mi[r];)r++;let s=t&&8==e||r<i&&8==mi[r]?1==n?1:8:256;for(let e=t;e<r;e++)mi[e]=s;t=r-1}else 8==r&&1==n&&(mi[t]=1);e=r,7&r&&(n=r)}}}(t,r,s,n,a),function(t,e,i,n,r){let s=1==r?2:1;for(let o=0,a=0,l=0;o<=n.length;o++){let h=o?n[o-1].to:e,c=o<n.length?n[o].from:i;for(let e,i,n,o=h;o<c;o++)if(i=ci[e=t.charCodeAt(o)])if(i<0){for(let t=a-3;t>=0;t-=3)if(ui[t+1]==-i){let e=ui[t+2],i=2&e?r:4&e?1&e?s:r:0;i&&(mi[o]=mi[ui[t]]=i),a=t;break}}else{if(189==ui.length)break;ui[a++]=o,ui[a++]=e,ui[a++]=l}else if(2==(n=mi[o])||1==n){let t=n==r;l=t?0:1;for(let e=a-3;e>=0;e-=3){let i=ui[e+2];if(2&i)break;if(t)ui[e+2]|=2;else{if(4&i)break;ui[e+2]|=4}}}}}(t,r,s,n,a),function(t,e,i,n){for(let r=0,s=n;r<=i.length;r++){let o=r?i[r-1].to:t,a=r<i.length?i[r].from:e;for(let l=o;l<a;){let o=mi[l];if(256==o){let o=l+1;for(;;)if(o==a){if(r==i.length)break;o=i[r++].to,a=r<i.length?i[r].from:e}else{if(256!=mi[o])break;o++}let h=1==s,c=h==(1==(o<e?mi[o]:n))?h?1:2:n;for(let e=o,n=r,s=n?i[n-1].to:t;e>l;)e==s&&(e=i[--n].from,s=n?i[n-1].to:t),mi[--e]=c;l=o}else s=o,l++}}}(r,s,n,a),gi(t,r,s,e,i,n,o)}function bi(t){return[new Oi(0,t,0)]}let vi=\"\";function wi(t,e,i,n,r){var s;let o=n.head-t.from,a=Oi.find(e,o,null!==(s=n.bidiLevel)&&void 0!==s?s:-1,n.assoc),l=e[a],h=l.side(r,i);if(o==h){let t=a+=r?1:-1;if(t<0||t>=e.length)return null;l=e[a=t],o=l.side(!r,i),h=l.side(r,i)}let c=S(t.text,o,l.forward(r,i));(c<l.from||c>l.to)&&(c=h),vi=t.text.slice(Math.min(o,c),Math.max(o,c));let u=a==(r?e.length-1:0)?null:e[a+(r?1:-1)];return u&&c==h&&u.level+(r?0:1)<l.level?Y.cursor(u.side(!r,i)+t.from,u.forward(r,i)?1:-1,u.level):Y.cursor(c+t.from,l.forward(r,i)?-1:1,l.level)}function xi(t,e,i){for(let n=e;n<i;n++){let e=di(t.charCodeAt(n));if(1==e)return si;if(2==e||4==e)return oi}return si}const Si=V.define(),yi=V.define(),ki=V.define(),$i=V.define(),Pi=V.define(),Ti=V.define(),Ci=V.define(),Zi=V.define(),Xi=V.define(),Ai=V.define({combine:t=>t.some(t=>t)}),Mi=V.define({combine:t=>t.some(t=>t)}),Ri=V.define();class zi{constructor(t,e=\"nearest\",i=\"nearest\",n=5,r=5,s=!1){this.range=t,this.y=e,this.x=i,this.yMargin=n,this.xMargin=r,this.isSnapshot=s}map(t){return t.empty?this:new zi(this.range.map(t),this.y,this.x,this.yMargin,this.xMargin,this.isSnapshot)}clip(t){return this.range.to<=t.doc.length?this:new zi(Y.cursor(t.doc.length),this.y,this.x,this.yMargin,this.xMargin,this.isSnapshot)}}const _i=gt.define({map:(t,e)=>t.map(e)}),Ei=gt.define();function Yi(t,e,i){let n=t.facet($i);n.length?n[0](e):window.onerror&&window.onerror(String(e),i,void 0,void 0,e)}const Li=V.define({combine:t=>!t.length||t[0]});let qi=0;const Vi=V.define({combine:t=>t.filter((e,i)=>{for(let n=0;n<i;n++)if(t[n].plugin==e.plugin)return!1;return!0})});class Wi{constructor(t,e,i,n,r){this.id=t,this.create=e,this.domEventHandlers=i,this.domEventObservers=n,this.baseExtensions=r(this),this.extension=this.baseExtensions.concat(Vi.of({plugin:this,arg:void 0}))}of(t){return this.baseExtensions.concat(Vi.of({plugin:this,arg:t}))}static define(t,e){const{eventHandlers:i,eventObservers:n,provide:r,decorations:s}=e||{};return new Wi(qi++,t,i,n,t=>{let e=[];return s&&e.push(Ii.of(e=>{let i=e.plugin(t);return i?s(i):Ce.none})),r&&e.push(r(t)),e})}static fromClass(t,e){return Wi.define((e,i)=>new t(e,i),e)}}class Di{constructor(t){this.spec=t,this.mustUpdate=null,this.value=null}get plugin(){return this.spec&&this.spec.plugin}update(t){if(this.value){if(this.mustUpdate){let t=this.mustUpdate;if(this.mustUpdate=null,this.value.update)try{this.value.update(t)}catch(e){if(Yi(t.state,e,\"CodeMirror plugin crashed\"),this.value.destroy)try{this.value.destroy()}catch(Ab){}this.deactivate()}}}else if(this.spec)try{this.value=this.spec.plugin.create(t,this.spec.arg)}catch(e){Yi(t.state,e,\"CodeMirror plugin crashed\"),this.deactivate()}return this}destroy(t){var e;if(null===(e=this.value)||void 0===e?void 0:e.destroy)try{this.value.destroy()}catch(i){Yi(t.state,i,\"CodeMirror plugin crashed\")}}deactivate(){this.spec=this.value=null}}const Bi=V.define(),ji=V.define(),Ii=V.define(),Gi=V.define(),Ni=V.define(),Ui=V.define(),Hi=V.define();function Fi(t,e){let i=t.state.facet(Hi);if(!i.length)return i;let n=i.map(e=>e instanceof Function?e(t):e),r=[];return _t.spans(n,e.from,e.to,{point(){},span(t,i,n,s){let o=t-e.from,a=i-e.from,l=r;for(let r=n.length-1;r>=0;r--,s--){let t,i=n[r].spec.bidiIsolate;if(null==i&&(i=xi(e.text,o,a)),s>0&&l.length&&(t=l[l.length-1]).to==o&&t.direction==i)t.to=a,l=t.inner;else{let t={from:o,to:a,direction:i,inner:[]};l.push(t),l=t.inner}}}}),r}const Ki=V.define();function Ji(t){let e=0,i=0,n=0,r=0;for(let s of t.state.facet(Ki)){let o=s(t);o&&(null!=o.left&&(e=Math.max(e,o.left)),null!=o.right&&(i=Math.max(i,o.right)),null!=o.top&&(n=Math.max(n,o.top)),null!=o.bottom&&(r=Math.max(r,o.bottom)))}return{left:e,right:i,top:n,bottom:r}}const tn=V.define();class en{constructor(t,e,i,n){this.fromA=t,this.toA=e,this.fromB=i,this.toB=n}join(t){return new en(Math.min(this.fromA,t.fromA),Math.max(this.toA,t.toA),Math.min(this.fromB,t.fromB),Math.max(this.toB,t.toB))}addToSet(t){let e=t.length,i=this;for(;e>0;e--){let n=t[e-1];if(!(n.fromA>i.toA)){if(n.toA<i.fromA)break;i=i.join(n),t.splice(e-1,1)}}return t.splice(e,0,i),t}static extendWithRanges(t,e){if(0==e.length)return t;let i=[];for(let n=0,r=0,s=0;;){let o=n<t.length?t[n].fromB:1e9,a=r<e.length?e[r]:1e9,l=Math.min(o,a);if(1e9==l)break;let h=l+s,c=l,u=h;for(;;)if(r<e.length&&e[r]<=c){let i=e[r+1];r+=2,c=Math.max(c,i);for(let e=n;e<t.length&&t[e].fromB<=c;e++)s=t[e].toA-t[e].toB;u=Math.max(u,i+s)}else{if(!(n<t.length&&t[n].fromB<=c))break;{let e=t[n++];c=Math.max(c,e.toB),u=Math.max(u,e.toA),s=e.toA-e.toB}}i.push(new en(h,u,l,c))}return i}}class nn{constructor(t,e,i){this.view=t,this.state=e,this.transactions=i,this.flags=0,this.startState=t.state,this.changes=Z.empty(this.startState.doc.length);for(let r of i)this.changes=this.changes.compose(r.changes);let n=[];this.changes.iterChangedRanges((t,e,i,r)=>n.push(new en(t,e,i,r))),this.changedRanges=n}static create(t,e,i){return new nn(t,e,i)}get viewportChanged(){return(4&this.flags)>0}get viewportMoved(){return(8&this.flags)>0}get heightChanged(){return(2&this.flags)>0}get geometryChanged(){return this.docChanged||(18&this.flags)>0}get focusChanged(){return(1&this.flags)>0}get docChanged(){return!this.changes.empty}get selectionSet(){return this.transactions.some(t=>t.selection)}get empty(){return 0==this.flags&&0==this.transactions.length}}const rn=[];class sn{constructor(t,e,i=0){this.dom=t,this.length=e,this.flags=i,this.parent=null,t.cmTile=this}get breakAfter(){return 1&this.flags}get children(){return rn}isWidget(){return!1}get isHidden(){return!1}isComposite(){return!1}isLine(){return!1}isText(){return!1}isBlock(){return!1}get domAttrs(){return null}sync(t){if(this.flags|=2,4&this.flags){this.flags&=-5;let t=this.domAttrs;t&&function(t,e){for(let i=t.attributes.length-1;i>=0;i--){let n=t.attributes[i].name;null==e[n]&&t.removeAttribute(n)}for(let i in e){let n=e[i];\"style\"==i?t.style.cssText=n:t.getAttribute(i)!=n&&t.setAttribute(i,n)}}(this.dom,t)}}toString(){return this.constructor.name+(this.children.length?`(${this.children})`:\"\")+(this.breakAfter?\"#\":\"\")}destroy(){this.parent=null}setDOM(t){this.dom=t,t.cmTile=this}get posAtStart(){return this.parent?this.parent.posBefore(this):0}get posAtEnd(){return this.posAtStart+this.length}posBefore(t,e=this.posAtStart){let i=e;for(let n of this.children){if(n==t)return i;i+=n.length+n.breakAfter}throw new RangeError(\"Invalid child in posBefore\")}posAfter(t){return this.posBefore(t)+t.length}covers(t){return!0}coordsIn(t,e){return null}domPosFor(t,e){let i=Ve(this.dom),n=this.length?t>0:e>0;return new ni(this.parent.dom,i+(n?1:0),0==t||t==this.length)}markDirty(t){this.flags&=-3,t&&(this.flags|=4),this.parent&&2&this.parent.flags&&this.parent.markDirty(!1)}get overrideDOMText(){return null}get root(){for(let t=this;t;t=t.parent)if(t instanceof ln)return t;return null}static get(t){return t.cmTile}}class on extends sn{constructor(t){super(t,0),this._children=[]}isComposite(){return!0}get children(){return this._children}get lastChild(){return this.children.length?this.children[this.children.length-1]:null}append(t){this.children.push(t),t.parent=this}sync(t){if(2&this.flags)return;super.sync(t);let e,i=this.dom,n=null,r=(null==t?void 0:t.node)==i?t:null,s=0;for(let o of this.children){if(o.sync(t),s+=o.length+o.breakAfter,e=n?n.nextSibling:i.firstChild,r&&e!=o.dom&&(r.written=!0),o.dom.parentNode==i)for(;e&&e!=o.dom;)e=an(e);else i.insertBefore(o.dom,e);n=o.dom}for(e=n?n.nextSibling:i.firstChild,r&&e&&(r.written=!0);e;)e=an(e);this.length=s}}function an(t){let e=t.nextSibling;return t.parentNode.removeChild(t),e}class ln extends on{constructor(t,e){super(e),this.view=t}owns(t){for(;t;t=t.parent)if(t==this)return!0;return!1}isBlock(){return!0}nearest(t){for(;;){if(!t)return null;let e=sn.get(t);if(e&&this.owns(e))return e;t=t.parentNode}}blockTiles(t){for(let e=[],i=this,n=0,r=0;;)if(n==i.children.length){if(!e.length)return;i=i.parent,i.breakAfter&&r++,n=e.pop()}else{let s=i.children[n++];if(s instanceof hn)e.push(n),i=s,n=0;else{let e=r+s.length,i=t(s,r);if(void 0!==i)return i;r=e+s.breakAfter}}}resolveBlock(t,e){let i,n,r=-1,s=-1;if(this.blockTiles((o,a)=>{let l=a+o.length;if(t>=a&&t<=l){if(o.isWidget()&&e>=-1&&e<=1){if(32&o.flags)return!0;16&o.flags&&(i=void 0)}(a<t||t==l&&(e<-1?o.length:o.covers(1)))&&(!i||!o.isWidget()&&i.isWidget())&&(i=o,r=t-a),(l>t||t==a&&(e>1?o.length:o.covers(-1)))&&(!n||!o.isWidget()&&n.isWidget())&&(n=o,s=t-a)}}),!i&&!n)throw new Error(\"No tile at position \"+t);return i&&e<0||!n?{tile:i,offset:r}:{tile:n,offset:s}}}class hn extends on{constructor(t,e){super(t),this.wrapper=e}isBlock(){return!0}covers(t){return!!this.children.length&&(t<0?this.children[0].covers(-1):this.lastChild.covers(1))}get domAttrs(){return this.wrapper.attributes}static of(t,e){let i=new hn(e||document.createElement(t.tagName),t);return e||(i.flags|=4),i}}class cn extends on{constructor(t,e){super(t),this.attrs=e}isLine(){return!0}static start(t,e,i){let n=new cn(e||document.createElement(\"div\"),t);return e&&i||(n.flags|=4),n}get domAttrs(){return this.attrs}resolveInline(t,e,i){let n=null,r=-1,s=null,o=-1;!function t(a,l){for(let h=0,c=0;h<a.children.length&&c<=l;h++){let u=a.children[h],d=c+u.length;d>=l&&(u.isComposite()?t(u,l-c):(!s||s.isHidden&&(e>0||i&&un(s,u)))&&(d>l||32&u.flags)?(s=u,o=l-c):(c<l||16&u.flags&&!u.isHidden)&&(n=u,r=l-c)),c=d}}(this,t);let a=(e<0?n:s)||n||s;return a?{tile:a,offset:a==n?r:o}:null}coordsIn(t,e){let i=this.resolveInline(t,e,!0);return i?i.tile.coordsIn(Math.max(0,i.offset),e):function(t){let e=t.dom.lastChild;if(!e)return t.dom.getBoundingClientRect();let i=Le(e);return i[i.length-1]||null}(this)}domIn(t,e){let i=this.resolveInline(t,e);if(i){let{tile:t,offset:n}=i;if(this.dom.contains(t.dom))return t.isText()?new ni(t.dom,Math.min(t.dom.nodeValue.length,n)):t.domPosFor(n,16&t.flags?1:32&t.flags?-1:e);let r=i.tile.parent,s=!1;for(let e of r.children){if(s)return new ni(e.dom,0);e==i.tile&&(s=!0)}}return new ni(this.dom,0)}}function un(t,e){let i=t.coordsIn(0,1),n=e.coordsIn(0,1);return i&&n&&n.top<i.bottom}class dn extends on{constructor(t,e){super(t),this.mark=e}get domAttrs(){return this.mark.attrs}static of(t,e){let i=new dn(e||document.createElement(t.tagName),t);return e||(i.flags|=4),i}}class fn extends sn{constructor(t,e){super(t,e.length),this.text=e}sync(t){2&this.flags||(super.sync(t),this.dom.nodeValue!=this.text&&(t&&t.node==this.dom&&(t.written=!0),this.dom.nodeValue=this.text))}isText(){return!0}toString(){return JSON.stringify(this.text)}coordsIn(t,e){let i=this.dom.nodeValue.length;t>i&&(t=i);let n=t,r=t,s=0;0==t&&e<0||t==i&&e>=0?we.chrome||we.gecko||(t?(n--,s=1):r<i&&(r++,s=-1)):e<0?n--:r<i&&r++;let o=Ke(this.dom,n,r).getClientRects();if(!o.length)return null;let a=o[(s?s<0:e>=0)?0:o.length-1];return we.safari&&!s&&0==a.width&&(a=Array.prototype.find.call(o,t=>t.width)||a),s?je(a,s<0):a||null}static of(t,e){let i=new fn(e||document.createTextNode(t),t);return e||(i.flags|=2),i}}class On extends sn{constructor(t,e,i,n){super(t,e,n),this.widget=i}isWidget(){return!0}get isHidden(){return this.widget.isHidden}covers(t){return!(48&this.flags)&&(this.flags&(t<0?64:128))>0}coordsIn(t,e){return this.coordsInWidget(t,e,!1)}coordsInWidget(t,e,i){let n=this.widget.coordsAt(this.dom,t,e);if(n)return n;if(i)return je(this.dom.getBoundingClientRect(),this.length?0==t:e<=0);{let e=this.dom.getClientRects(),i=null;if(!e.length)return null;let n=!!(16&this.flags)||!(32&this.flags)&&t>0;for(let r=n?e.length-1:0;i=e[r],!(t>0?0==r:r==e.length-1||i.top<i.bottom);r+=n?-1:1);return je(i,!n)}}get overrideDOMText(){if(!this.length)return f.empty;let{root:t}=this;if(!t)return f.empty;let e=this.posAtStart;return t.view.state.doc.slice(e,e+this.length)}destroy(){super.destroy(),this.widget.destroy(this.dom)}static of(t,e,i,n,r){return r||(r=t.toDOM(e),t.editable||(r.contentEditable=\"false\")),new On(r,i,t,n)}}class pn extends sn{constructor(t){let e=document.createElement(\"img\");e.className=\"cm-widgetBuffer\",e.setAttribute(\"aria-hidden\",\"true\"),super(e,0,t)}get isHidden(){return!0}get overrideDOMText(){return f.empty}coordsIn(t){return this.dom.getBoundingClientRect()}}class mn{constructor(t){this.index=0,this.beforeBreak=!1,this.parents=[],this.tile=t}advance(t,e,i){let{tile:n,index:r,beforeBreak:s,parents:o}=this;for(;t||e>0;)if(n.isComposite())if(s){if(!t)break;i&&i.break(),t--,s=!1}else if(r==n.children.length){if(!t&&!o.length)break;i&&i.leave(n),s=!!n.breakAfter,({tile:n,index:r}=o.pop()),r++}else{let a=n.children[r],l=a.breakAfter;!(e>0?a.length<=t:a.length<t)||i&&!1===i.skip(a,0,a.length)&&a.isComposite?(o.push({tile:n,index:r}),n=a,r=0,i&&a.isComposite()&&i.enter(a)):(s=!!l,r++,t-=a.length)}else if(r==n.length)s=!!n.breakAfter,({tile:n,index:r}=o.pop()),r++;else{if(!t)break;{let e=Math.min(t,n.length-r);i&&i.skip(n,r,r+e),t-=e,r+=e}}return this.tile=n,this.index=r,this.beforeBreak=s,this}get root(){return this.parents.length?this.parents[0].tile:this.tile}}class gn{constructor(t,e,i,n){this.from=t,this.to=e,this.wrapper=i,this.rank=n}}class Qn{constructor(t,e,i){this.cache=t,this.root=e,this.blockWrappers=i,this.curLine=null,this.lastBlock=null,this.afterWidget=null,this.pos=0,this.wrappers=[],this.wrapperPos=0}addText(t,e,i,n){var r;this.flushBuffer();let s=this.ensureMarks(e,i),o=s.lastChild;if(!o||!o.isText()||8&o.flags)s.append(n||fn.of(t,null===(r=this.cache.find(fn))||void 0===r?void 0:r.dom));else{this.cache.reused.set(o,2),(s.children[s.children.length-1]=new fn(o.dom,o.text+t)).parent=s}this.pos+=t.length,this.afterWidget=null}addComposition(t,e){let i=this.curLine;i.dom!=e.line.dom&&(i.setDOM(this.cache.reused.has(e.line)?kn(e.line.dom):e.line.dom),this.cache.reused.set(e.line,2));let n=i;for(let o=e.marks.length-1;o>=0;o--){let t=e.marks[o],i=n.lastChild;if(i instanceof dn&&i.mark.eq(t.mark))i.dom!=t.dom&&i.setDOM(kn(t.dom)),n=i;else{if(this.cache.reused.get(t)){let e=sn.get(t.dom);e&&e.setDOM(kn(t.dom))}let e=dn.of(t.mark,t.dom);n.append(e),n=e}this.cache.reused.set(t,2)}let r=sn.get(t.text);r&&this.cache.reused.set(r,2);let s=new fn(t.text,t.text.nodeValue);s.flags|=8,n.append(s)}addInlineWidget(t,e,i){let n=this.afterWidget&&48&t.flags&&(48&this.afterWidget.flags)==(48&t.flags);n||this.flushBuffer();let r=this.ensureMarks(e,i);n||16&t.flags||r.append(this.getBuffer(1)),r.append(t),this.pos+=t.length,this.afterWidget=t}addMark(t,e,i){this.flushBuffer(),this.ensureMarks(e,i).append(t),this.pos+=t.length,this.afterWidget=null}addBlockWidget(t){this.getBlockPos().append(t),this.pos+=t.length,this.lastBlock=t,this.endLine()}continueWidget(t){(this.afterWidget||this.lastBlock).length+=t,this.pos+=t}addLineStart(t,e){var i;t||(t=yn);let n=cn.start(t,e||(null===(i=this.cache.find(cn))||void 0===i?void 0:i.dom),!!e);this.getBlockPos().append(this.lastBlock=this.curLine=n)}addLine(t){this.getBlockPos().append(t),this.pos+=t.length,this.lastBlock=t,this.endLine()}addBreak(){this.lastBlock.flags|=1,this.endLine(),this.pos++}addLineStartIfNotCovered(t){this.blockPosCovered()||this.addLineStart(t)}ensureLine(t){this.curLine||this.addLineStart(t)}ensureMarks(t,e){var i;let n=this.curLine;for(let r=t.length-1;r>=0;r--){let s,o=t[r];if(e>0&&(s=n.lastChild)&&s instanceof dn&&s.mark.eq(o))n=s,e--;else{let t=dn.of(o,null===(i=this.cache.find(dn,t=>t.mark.eq(o)))||void 0===i?void 0:i.dom);n.append(t),n=t,e=0}}return n}endLine(){if(this.curLine){this.flushBuffer();let t=this.curLine.lastChild;t&&Sn(this.curLine,!1)&&(\"BR\"==t.dom.nodeName||!t.isWidget()||we.ios&&Sn(this.curLine,!0))||this.curLine.append(this.cache.findWidget(Pn,0,32)||new On(Pn.toDOM(),0,Pn,32)),this.curLine=this.afterWidget=null}}updateBlockWrappers(){this.wrapperPos>this.pos+1e4&&(this.blockWrappers.goto(this.pos),this.wrappers.length=0);for(let t=this.wrappers.length-1;t>=0;t--)this.wrappers[t].to<this.pos&&this.wrappers.splice(t,1);for(let t=this.blockWrappers;t.value&&t.from<=this.pos;t.next())if(t.to>=this.pos){let e=new gn(t.from,t.to,t.value,t.rank),i=this.wrappers.length;for(;i>0&&(this.wrappers[i-1].rank-e.rank||this.wrappers[i-1].to-e.to)<0;)i--;this.wrappers.splice(i,0,e)}this.wrapperPos=this.pos}getBlockPos(){var t;this.updateBlockWrappers();let e=this.root;for(let i of this.wrappers){let n=e.lastChild;if(i.from<this.pos&&n instanceof hn&&n.wrapper.eq(i.wrapper))e=n;else{let n=hn.of(i.wrapper,null===(t=this.cache.find(hn,t=>t.wrapper.eq(i.wrapper)))||void 0===t?void 0:t.dom);e.append(n),e=n}}return e}blockPosCovered(){let t=this.lastBlock;return null!=t&&!t.breakAfter&&(!t.isWidget()||(160&t.flags)>0)}getBuffer(t){let e=2|(t<0?16:32),i=this.cache.find(pn,void 0,1);return i&&(i.flags=e),i||new pn(e)}flushBuffer(){!this.afterWidget||32&this.afterWidget.flags||(this.afterWidget.parent.append(this.getBuffer(-1)),this.afterWidget=null)}}class bn{constructor(t){this.skipCount=0,this.text=\"\",this.textOff=0,this.cursor=t.iter()}skip(t){this.textOff+t<=this.text.length?this.textOff+=t:(this.skipCount+=t-(this.text.length-this.textOff),this.text=\"\",this.textOff=0)}next(t){if(this.textOff==this.text.length){let{value:e,lineBreak:i,done:n}=this.cursor.next(this.skipCount);if(this.skipCount=0,n)throw new Error(\"Ran out of text content when drawing inline views\");this.text=e;let r=this.textOff=Math.min(t,e.length);return i?null:e.slice(0,r)}let e=Math.min(this.text.length,this.textOff+t),i=this.text.slice(this.textOff,e);return this.textOff=e,i}}const vn=[On,cn,fn,dn,pn,hn,ln];for(let Mb=0;Mb<vn.length;Mb++)vn[Mb].bucket=Mb;class wn{constructor(t){this.view=t,this.buckets=vn.map(()=>[]),this.index=vn.map(()=>0),this.reused=new Map}add(t){t.demo;let e=t.constructor.bucket,i=this.buckets[e];i.length<6?i.push(t):i[this.index[e]=(this.index[e]+1)%6]=t}find(t,e,i=2){let n=t.bucket,r=this.buckets[n],s=this.index[n];for(let o=r.length-1;o>=0;o--){let t=(o+s)%r.length,a=r[t];if((!e||e(a))&&!this.reused.has(a))return r.splice(t,1),t<s&&this.index[n]--,this.reused.set(a,i),a}return null}findWidget(t,e,i){let n=this.buckets[0];if(t.demo,n.length)for(let r=0,s=0;;r++){if(r==n.length){if(s)return null;s=1,r=0}let o=n[r];if(!this.reused.has(o)&&(0==s?o.widget.compare(t):o.widget.constructor==t.constructor&&t.updateDOM(o.dom,this.view)))return n.splice(r,1),r<this.index[0]&&this.index[0]--,o.length==e&&(497&o.flags)==i?(this.reused.set(o,1),o):(this.reused.set(o,2),new On(o.dom,e,t,-498&o.flags|i))}}reuse(t){return this.reused.set(t,1),t}maybeReuse(t,e=2){if(!this.reused.has(t))return this.reused.set(t,e),t.dom}clear(){for(let t=0;t<this.buckets.length;t++)this.buckets[t].length=this.index[t]=0}}class xn{constructor(t,e,i,n,r){this.view=t,this.decorations=n,this.disallowBlockEffectsFor=r,this.openWidget=!1,this.openMarks=0,this.cache=new wn(t),this.text=new bn(t.state.doc),this.builder=new Qn(this.cache,new ln(t,t.contentDOM),_t.iter(i)),this.cache.reused.set(e,2),this.old=new mn(e),this.reuseWalker={skip:(t,e,i)=>{if(this.cache.add(t),t.isComposite())return!1},enter:t=>this.cache.add(t),leave:()=>{},break:()=>{}}}run(t,e){let i=e&&this.getCompositionContext(e.text);for(let n=0,r=0,s=0;;){let o=s<t.length?t[s++]:null,a=o?o.fromA:this.old.root.length;if(a>n){let t=a-n;this.preserve(t,!s,!o),n=a,r+=t}if(!o)break;e&&o.fromA<=e.range.fromA&&o.toA>=e.range.toA?(this.forward(o.fromA,e.range.fromA),this.emit(r,e.range.fromB),this.cache.clear(),this.builder.addComposition(e,i),this.text.skip(e.range.toB-e.range.fromB),this.forward(e.range.fromA,o.toA),this.emit(e.range.toB,o.toB)):(this.forward(o.fromA,o.toA),this.emit(r,o.toB)),r=o.toB,n=o.toA}return this.builder.curLine&&this.builder.endLine(),this.builder.root}preserve(t,e,i){let n=function(t){let e=[];for(let i=t.parents.length;i>1;i--){let n=i==t.parents.length?t.tile:t.parents[i].tile;n instanceof dn&&e.push(n.mark)}return e}(this.old),r=this.openMarks;this.old.advance(t,i?1:-1,{skip:(t,e,i)=>{if(t.isWidget())if(this.openWidget)this.builder.continueWidget(i-e);else{let s=i>0||e<t.length?On.of(t.widget,this.view,i-e,496&t.flags,this.cache.maybeReuse(t)):this.cache.reuse(t);256&s.flags?(s.flags&=-2,this.builder.addBlockWidget(s)):(this.builder.ensureLine(null),this.builder.addInlineWidget(s,n,r),r=n.length)}else if(t.isText())this.builder.ensureLine(null),e||i!=t.length?(this.cache.add(t),this.builder.addText(t.text.slice(e,i),n,r)):this.builder.addText(t.text,n,r,this.cache.reuse(t)),r=n.length;else if(t.isLine())t.flags&=-2,this.cache.reused.set(t,1),this.builder.addLine(t);else if(t instanceof pn)this.cache.add(t);else{if(!(t instanceof dn))return!1;this.builder.ensureLine(null),this.builder.addMark(t,n,r),this.cache.reused.set(t,1),r=n.length}this.openWidget=!1},enter:t=>{t.isLine()?this.builder.addLineStart(t.attrs,this.cache.maybeReuse(t)):(this.cache.add(t),t instanceof dn&&n.unshift(t.mark)),this.openWidget=!1},leave:t=>{t.isLine()?n.length&&(n.length=r=0):t instanceof dn&&(n.shift(),r=Math.min(r,n.length))},break:()=>{this.builder.addBreak(),this.openWidget=!1}}),this.text.skip(t)}emit(t,e){let i=null,n=this.builder,r=0,s=_t.spans(this.decorations,t,e,{point:(t,e,s,o,a,l)=>{if(s instanceof Ae){if(this.disallowBlockEffectsFor[l]){if(s.block)throw new RangeError(\"Block decorations may not be specified via plugins\");if(e>this.view.state.doc.lineAt(t).to)throw new RangeError(\"Decorations that replace line breaks may not be specified via plugins\")}if(r=o.length,a>o.length)n.continueWidget(e-t);else{let r=s.widget||(s.block?$n.block:$n.inline),l=function(t){let e=t.isReplace?(t.startSide<0?64:0)|(t.endSide>0?128:0):t.startSide>0?32:16;t.block&&(e|=256);return e}(s),h=this.cache.findWidget(r,e-t,l)||On.of(r,this.view,e-t,l);s.block?(s.startSide>0&&n.addLineStartIfNotCovered(i),n.addBlockWidget(h)):(n.ensureLine(i),n.addInlineWidget(h,o,a))}i=null}else i=function(t,e){let i=e.spec.attributes,n=e.spec.class;if(!i&&!n)return t;t||(t={class:\"cm-line\"});i&&xe(i,t);n&&(t.class+=\" \"+n);return t}(i,s);e>t&&this.text.skip(e-t)},span:(t,e,r,s)=>{for(let o=t;o<e;){let t=this.text.next(Math.min(512,e-o));null==t?(n.addLineStartIfNotCovered(i),n.addBreak(),o++):(n.ensureLine(i),n.addText(t,r,s),o+=t.length),i=null}}});n.addLineStartIfNotCovered(i),this.openWidget=s>r,this.openMarks=s}forward(t,e){e-t<=10?this.old.advance(e-t,1,this.reuseWalker):(this.old.advance(5,-1,this.reuseWalker),this.old.advance(e-t-10,-1),this.old.advance(5,1,this.reuseWalker))}getCompositionContext(t){let e=[],i=null;for(let n=t.parentNode;;n=n.parentNode){let t=sn.get(n);if(n==this.view.contentDOM)break;t instanceof dn?e.push(t):(null==t?void 0:t.isLine())?i=t:\"DIV\"!=n.nodeName||i||n==this.view.contentDOM?e.push(dn.of(new Ze({tagName:n.nodeName.toLowerCase(),attributes:$e(n)}),n)):i=new cn(n,yn)}return{line:i,marks:e}}}function Sn(t,e){let i=t=>{for(let n of t.children)if((e?n.isText():n.length)||i(n))return!0;return!1};return i(t)}const yn={class:\"cm-line\"};function kn(t){let e=sn.get(t);return e&&e.setDOM(t.cloneNode()),t}class $n extends Pe{constructor(t){super(),this.tag=t}eq(t){return t.tag==this.tag}toDOM(){return document.createElement(this.tag)}updateDOM(t){return t.nodeName.toLowerCase()==this.tag}get isHidden(){return!0}}$n.inline=new $n(\"span\"),$n.block=new $n(\"div\");const Pn=new class extends Pe{toDOM(){return document.createElement(\"br\")}get isHidden(){return!0}get editable(){return!0}};class Tn{constructor(t){this.view=t,this.decorations=[],this.blockWrappers=[],this.dynamicDecorationMap=[!1],this.domChanged=null,this.hasComposition=null,this.editContextFormatting=Ce.none,this.lastCompositionAfterCursor=!1,this.minWidth=0,this.minWidthFrom=0,this.minWidthTo=0,this.impreciseAnchor=null,this.impreciseHead=null,this.forceSelection=!1,this.lastUpdate=Date.now(),this.updateDeco(),this.tile=new ln(t,t.contentDOM),this.updateInner([new en(0,0,0,t.state.doc.length)],null)}update(t){var e;let i=t.changedRanges;this.minWidth>0&&i.length&&(i.every(({fromA:t,toA:e})=>e<this.minWidthFrom||t>this.minWidthTo)?(this.minWidthFrom=t.changes.mapPos(this.minWidthFrom,1),this.minWidthTo=t.changes.mapPos(this.minWidthTo,1)):this.minWidth=this.minWidthFrom=this.minWidthTo=0),this.updateEditContextFormatting(t);let n=-1;this.view.inputState.composing>=0&&!this.view.observer.editContext&&((null===(e=this.domChanged)||void 0===e?void 0:e.newSel)?n=this.domChanged.newSel.head:function(t,e){let i=!1;e&&t.iterChangedRanges((t,n)=>{t<e.to&&n>e.from&&(i=!0)});return i}(t.changes,this.hasComposition)||t.selectionSet||(n=t.state.selection.main.head));let r=n>-1?function(t,e,i){let n=Zn(t,i);if(!n)return null;let{node:r,from:s,to:o}=n,a=r.nodeValue;if(/[\\n\\r]/.test(a))return null;if(t.state.doc.sliceString(n.from,n.to)!=a)return null;let l=e.invertedDesc;return{range:new en(l.mapPos(s),l.mapPos(o),s,o),text:r}}(this.view,t.changes,n):null;if(this.domChanged=null,this.hasComposition){let{from:e,to:n}=this.hasComposition;i=new en(e,n,t.changes.mapPos(e,-1),t.changes.mapPos(n,1)).addToSet(i.slice())}this.hasComposition=r?{from:r.range.fromB,to:r.range.toB}:null,(we.ie||we.chrome)&&!r&&t&&t.state.doc.lines!=t.startState.doc.lines&&(this.forceSelection=!0);let s=this.decorations,o=this.blockWrappers;this.updateDeco();let a=function(t,e,i){let n=new Xn;return _t.compare(t,e,i,n),n.changes}(s,this.decorations,t.changes);a.length&&(i=en.extendWithRanges(i,a));let l=function(t,e,i){let n=new An;return _t.compare(t,e,i,n),n.changes}(o,this.blockWrappers,t.changes);return l.length&&(i=en.extendWithRanges(i,l)),r&&!i.some(t=>t.fromA<=r.range.fromA&&t.toA>=r.range.toA)&&(i=r.range.addToSet(i.slice())),!(2&this.tile.flags&&0==i.length)&&(this.updateInner(i,r),t.transactions.length&&(this.lastUpdate=Date.now()),!0)}updateInner(t,e){this.view.viewState.mustMeasureContent=!0;let{observer:i}=this.view;i.ignore(()=>{if(e||t.length){let i=this.tile,n=new xn(this.view,i,this.blockWrappers,this.decorations,this.dynamicDecorationMap);this.tile=n.run(t,e),Cn(i,n.cache.reused)}this.tile.dom.style.height=this.view.viewState.contentHeight/this.view.scaleY+\"px\",this.tile.dom.style.flexBasis=this.minWidth?this.minWidth+\"px\":\"\";let n=we.chrome||we.ios?{node:i.selectionRange.focusNode,written:!1}:void 0;this.tile.sync(n),!n||!n.written&&i.selectionRange.focusNode==n.node&&this.tile.dom.contains(n.node)||(this.forceSelection=!0),this.tile.dom.style.height=\"\"});let n=[];if(this.view.viewport.from||this.view.viewport.to<this.view.state.doc.length)for(let r of this.tile.children)r.isWidget()&&r.widget instanceof Mn&&n.push(r.dom);i.updateGaps(n)}updateEditContextFormatting(t){this.editContextFormatting=this.editContextFormatting.map(t.changes);for(let e of t.transactions)for(let t of e.effects)t.is(Ei)&&(this.editContextFormatting=t.value)}updateSelection(t=!1,e=!1){!t&&this.view.observer.selectionRange.focusNode||this.view.observer.readSelectionRange();let{dom:i}=this.tile,n=this.view.root.activeElement,r=n==i,s=!r&&!(this.view.state.facet(Li)||i.tabIndex>-1)&&Ye(i,this.view.observer.selectionRange)&&!(n&&i.contains(n));if(!(r||e||s))return;let o=this.forceSelection;this.forceSelection=!1;let a,l,h=this.view.state.selection.main;if(h.empty?l=a=this.inlineDOMNearPos(h.anchor,h.assoc||1):(l=this.inlineDOMNearPos(h.head,h.head==h.from?1:-1),a=this.inlineDOMNearPos(h.anchor,h.anchor==h.from?1:-1)),we.gecko&&h.empty&&!this.hasComposition&&(1==(c=a).node.nodeType&&c.node.firstChild&&(0==c.offset||\"false\"==c.node.childNodes[c.offset-1].contentEditable)&&(c.offset==c.node.childNodes.length||\"false\"==c.node.childNodes[c.offset].contentEditable))){let t=document.createTextNode(\"\");this.view.observer.ignore(()=>a.node.insertBefore(t,a.node.childNodes[a.offset]||null)),a=l=new ni(t,0),o=!0}var c;let u=this.view.observer.selectionRange;!o&&u.focusNode&&(qe(a.node,a.offset,u.anchorNode,u.anchorOffset)&&qe(l.node,l.offset,u.focusNode,u.focusOffset)||this.suppressWidgetCursorChange(u,h))||(this.view.observer.ignore(()=>{we.android&&we.chrome&&i.contains(u.focusNode)&&function(t,e){for(let i=t;i&&i!=e;i=i.assignedSlot||i.parentNode)if(1==i.nodeType&&\"false\"==i.contentEditable)return!0;return!1}(u.focusNode,i)&&(i.blur(),i.focus({preventScroll:!0}));let t=_e(this.view.root);if(t)if(h.empty){if(we.gecko){let t=(e=a.node,r=a.offset,1!=e.nodeType?0:(r&&\"false\"==e.childNodes[r-1].contentEditable?1:0)|(r<e.childNodes.length&&\"false\"==e.childNodes[r].contentEditable?2:0));if(t&&3!=t){let e=(1==t?ei:ii)(a.node,a.offset);e&&(a=new ni(e.node,e.offset))}}t.collapse(a.node,a.offset),null!=h.bidiLevel&&void 0!==t.caretBidiLevel&&(t.caretBidiLevel=h.bidiLevel)}else if(t.extend){t.collapse(a.node,a.offset);try{t.extend(l.node,l.offset)}catch(Ab){}}else{let e=document.createRange();h.anchor>h.head&&([a,l]=[l,a]),e.setEnd(l.node,l.offset),e.setStart(a.node,a.offset),t.removeAllRanges(),t.addRange(e)}else;var e,r;s&&this.view.root.activeElement==i&&(i.blur(),n&&n.focus())}),this.view.observer.setSelectionRange(a,l)),this.impreciseAnchor=a.precise?null:new ni(u.anchorNode,u.anchorOffset),this.impreciseHead=l.precise?null:new ni(u.focusNode,u.focusOffset)}suppressWidgetCursorChange(t,e){return this.hasComposition&&e.empty&&qe(t.focusNode,t.focusOffset,t.anchorNode,t.anchorOffset)&&this.posFromDOM(t.focusNode,t.focusOffset)==e.head}enforceCursorAssoc(){if(this.hasComposition)return;let{view:t}=this,e=t.state.selection.main,i=_e(t.root),{anchorNode:n,anchorOffset:r}=t.observer.selectionRange;if(!(i&&e.empty&&e.assoc&&i.modify))return;let s=this.lineAt(e.head,e.assoc);if(!s)return;let o=s.posAtStart;if(e.head==o||e.head==o+s.length)return;let a=this.coordsAt(e.head,-1),l=this.coordsAt(e.head,1);if(!a||!l||a.bottom>l.top)return;let h=this.domAtPos(e.head+e.assoc,e.assoc);i.collapse(h.node,h.offset),i.modify(\"move\",e.assoc<0?\"forward\":\"backward\",\"lineboundary\"),t.observer.readSelectionRange();let c=t.observer.selectionRange;t.docView.posFromDOM(c.anchorNode,c.anchorOffset)!=e.from&&i.collapse(n,r)}posFromDOM(t,e){let i=this.tile.nearest(t);if(!i)return 2&this.tile.dom.compareDocumentPosition(t)?0:this.view.state.doc.length;let n=i.posAtStart;if(!i.isComposite())return i.isText()?t==i.dom?n+e:n+(e?i.length:0):n;{let r;if(t==i.dom)r=i.dom.childNodes[e];else{let n=0==Be(t)?0:0==e?-1:1;for(;;){let e=t.parentNode;if(e==i.dom)break;0==n&&e.firstChild!=e.lastChild&&(n=t==e.firstChild?-1:1),t=e}r=n<0?t:t.nextSibling}if(r==i.dom.firstChild)return n;for(;r&&!sn.get(r);)r=r.nextSibling;if(!r)return n+i.length;for(let t=0,e=n;;t++){let n=i.children[t];if(n.dom==r)return e;e+=n.length+n.breakAfter}}}domAtPos(t,e){let{tile:i,offset:n}=this.tile.resolveBlock(t,e);return i.isWidget()?i.domPosFor(t,e):i.domIn(n,e)}inlineDOMNearPos(t,e){let i,n,r=-1,s=!1,o=-1,a=!1;return this.tile.blockTiles((e,l)=>{if(e.isWidget()){if(32&e.flags&&l>=t)return!0;16&e.flags&&(s=!0)}else{let h=l+e.length;if(l<=t&&(i=e,r=t-l,s=h<t),h>=t&&!n&&(n=e,o=t-l,a=l>t),l>t&&n)return!0}}),i||n?(s&&n?i=null:a&&i&&(n=null),i&&e<0||!n?i.domIn(r,e):n.domIn(o,e)):this.domAtPos(t,e)}coordsAt(t,e){let{tile:i,offset:n}=this.tile.resolveBlock(t,e);return i.isWidget()?i.widget instanceof Mn?null:i.coordsInWidget(n,e,!0):i.coordsIn(n,e)}lineAt(t,e){let{tile:i}=this.tile.resolveBlock(t,e);return i.isLine()?i:null}coordsForChar(t){let{tile:e,offset:i}=this.tile.resolveBlock(t,1);if(!e.isLine())return null;return function t(e,i){if(e.isComposite())for(let n of e.children){if(n.length>=i){let e=t(n,i);if(e)return e}if((i-=n.length)<0)break}else if(e.isText()&&i<e.length){let t=S(e.text,i);if(t==i)return null;let n=Ke(e.dom,i,t).getClientRects();for(let e=0;e<n.length;e++){let t=n[e];if(e==n.length-1||t.top<t.bottom&&t.left<t.right)return t}}return null}(e,i)}measureVisibleLineHeights(t){let e=[],{from:i,to:n}=t,r=this.view.contentDOM.clientWidth,s=r>Math.max(this.view.scrollDOM.clientWidth,this.minWidth)+1,o=-1,a=this.view.textDirection==ri.LTR,l=0,h=(t,c,u)=>{for(let d=0;d<t.children.length&&!(c>n);d++){let n=t.children[d],f=c+n.length,O=n.dom.getBoundingClientRect(),{height:p}=O;if(u&&!d&&(l+=O.top-u.top),n instanceof hn)f>i&&h(n,c,O);else if(c>=i&&(l>0&&e.push(-l),e.push(p+l),l=0,s)){let t=n.dom.lastChild,e=t?Le(t):[];if(e.length){let t=e[e.length-1],i=a?t.right-O.left:O.right-t.left;i>o&&(o=i,this.minWidth=r,this.minWidthFrom=c,this.minWidthTo=f)}}u&&d==t.children.length-1&&(l+=u.bottom-O.bottom),c=f+n.breakAfter}};return h(this.tile,0,null),e}textDirectionAt(t){let{tile:e}=this.tile.resolveBlock(t,1);return\"rtl\"==getComputedStyle(e.dom).direction?ri.RTL:ri.LTR}measureTextSize(){let t=this.tile.blockTiles(t=>{if(t.isLine()&&t.children.length&&t.length<=20){let e,i=0;for(let n of t.children){if(!n.isText()||/[^ -~]/.test(n.text))return;let t=Le(n.dom);if(1!=t.length)return;i+=t[0].width,e=t[0].height}if(i)return{lineHeight:t.dom.getBoundingClientRect().height,charWidth:i/t.length,textHeight:e}}});if(t)return t;let e,i,n,r=document.createElement(\"div\");return r.className=\"cm-line\",r.style.width=\"99999px\",r.style.position=\"absolute\",r.textContent=\"abc def ghi jkl mno pqr stu\",this.view.observer.ignore(()=>{this.tile.dom.appendChild(r);let t=Le(r.firstChild)[0];e=r.getBoundingClientRect().height,i=t&&t.width?t.width/27:7,n=t&&t.height?t.height:e,r.remove()}),{lineHeight:e,charWidth:i,textHeight:n}}computeBlockGapDeco(){let t=[],e=this.view.viewState;for(let i=0,n=0;;n++){let r=n==e.viewports.length?null:e.viewports[n],s=r?r.from-1:this.view.state.doc.length;if(s>i){let n=(e.lineBlockAt(s).bottom-e.lineBlockAt(i).top)/this.view.scaleY;t.push(Ce.replace({widget:new Mn(n),block:!0,inclusive:!0,isBlockGap:!0}).range(i,s))}if(!r)break;i=r.to+1}return Ce.set(t)}updateDeco(){let t=1,e=this.view.state.facet(Ii).map(e=>(this.dynamicDecorationMap[t++]=\"function\"==typeof e)?e(this.view):e),i=!1,n=this.view.state.facet(Ni).map((t,e)=>{let n=\"function\"==typeof t;return n&&(i=!0),n?t(this.view):t});for(n.length&&(this.dynamicDecorationMap[t++]=i,e.push(_t.join(n))),this.decorations=[this.editContextFormatting,...e,this.computeBlockGapDeco(),this.view.viewState.lineGapDeco];t<this.decorations.length;)this.dynamicDecorationMap[t++]=!1;this.blockWrappers=this.view.state.facet(Gi).map(t=>\"function\"==typeof t?t(this.view):t)}scrollIntoView(t){if(t.isSnapshot){let e=this.view.viewState.lineBlockAt(t.range.head);return this.view.scrollDOM.scrollTop=e.top-t.yMargin,void(this.view.scrollDOM.scrollLeft=t.xMargin)}for(let h of this.view.state.facet(Ri))try{if(h(this.view,t.range,t))return!0}catch(l){Yi(this.view.state,l,\"scroll handler\")}let e,{range:i}=t,n=this.coordsAt(i.head,i.empty?i.assoc:i.head>i.anchor?-1:1);if(!n)return;!i.empty&&(e=this.coordsAt(i.anchor,i.anchor>i.head?-1:1))&&(n={left:Math.min(n.left,e.left),top:Math.min(n.top,e.top),right:Math.max(n.right,e.right),bottom:Math.max(n.bottom,e.bottom)});let r=Ji(this.view),s={left:n.left-r.left,top:n.top-r.top,right:n.right+r.right,bottom:n.bottom+r.bottom},{offsetWidth:o,offsetHeight:a}=this.view.scrollDOM;!function(t,e,i,n,r,s,o,a){let l=t.ownerDocument,h=l.defaultView||window;for(let c=t,u=!1;c&&!u;)if(1==c.nodeType){let t,d=c==l.body,f=1,O=1;if(d)t=Ie(h);else{if(/^(fixed|sticky)$/.test(getComputedStyle(c).position)&&(u=!0),c.scrollHeight<=c.clientHeight&&c.scrollWidth<=c.clientWidth){c=c.assignedSlot||c.parentNode;continue}let e=c.getBoundingClientRect();({scaleX:f,scaleY:O}=Ge(c,e)),t={left:e.left,right:e.left+c.clientWidth*f,top:e.top,bottom:e.top+c.clientHeight*O}}let p=0,m=0;if(\"nearest\"==r)e.top<t.top?(m=e.top-(t.top+o),i>0&&e.bottom>t.bottom+m&&(m=e.bottom-t.bottom+o)):e.bottom>t.bottom&&(m=e.bottom-t.bottom+o,i<0&&e.top-m<t.top&&(m=e.top-(t.top+o)));else{let n=e.bottom-e.top,s=t.bottom-t.top;m=(\"center\"==r&&n<=s?e.top+n/2-s/2:\"start\"==r||\"center\"==r&&i<0?e.top-o:e.bottom-s+o)-t.top}if(\"nearest\"==n?e.left<t.left?(p=e.left-(t.left+s),i>0&&e.right>t.right+p&&(p=e.right-t.right+s)):e.right>t.right&&(p=e.right-t.right+s,i<0&&e.left<t.left+p&&(p=e.left-(t.left+s))):p=(\"center\"==n?e.left+(e.right-e.left)/2-(t.right-t.left)/2:\"start\"==n==a?e.left-s:e.right-(t.right-t.left)+s)-t.left,p||m)if(d)h.scrollBy(p,m);else{let t=0,i=0;if(m){let t=c.scrollTop;c.scrollTop+=m/O,i=(c.scrollTop-t)*O}if(p){let e=c.scrollLeft;c.scrollLeft+=p/f,t=(c.scrollLeft-e)*f}e={left:e.left-t,top:e.top-i,right:e.right-t,bottom:e.bottom-i},t&&Math.abs(t-p)<1&&(n=\"nearest\"),i&&Math.abs(i-m)<1&&(r=\"nearest\")}if(d)break;(e.top<t.top||e.bottom>t.bottom||e.left<t.left||e.right>t.right)&&(e={left:Math.max(e.left,t.left),right:Math.min(e.right,t.right),top:Math.max(e.top,t.top),bottom:Math.min(e.bottom,t.bottom)}),c=c.assignedSlot||c.parentNode}else{if(11!=c.nodeType)break;c=c.host}}(this.view.scrollDOM,s,i.head<i.anchor?-1:1,t.x,t.y,Math.max(Math.min(t.xMargin,o),-o),Math.max(Math.min(t.yMargin,a),-a),this.view.textDirection==ri.LTR)}lineHasWidget(t){let e=t=>t.isWidget()||t.children.some(e);return e(this.tile.resolveBlock(t,1).tile)}destroy(){Cn(this.tile)}}function Cn(t,e){let i=null==e?void 0:e.get(t);if(1!=i){null==i&&t.destroy();for(let i of t.children)Cn(i,e)}}function Zn(t,e){let i=t.observer.selectionRange;if(!i.focusNode)return null;let n=ei(i.focusNode,i.focusOffset),r=ii(i.focusNode,i.focusOffset),s=n||r;if(r&&n&&r.node!=n.node){let e=sn.get(r.node);if(!e||e.isText()&&e.text!=r.node.nodeValue)s=r;else if(t.docView.lastCompositionAfterCursor){let t=sn.get(n.node);!t||t.isText()&&t.text!=n.node.nodeValue||(s=r)}}if(t.docView.lastCompositionAfterCursor=s!=n,!s)return null;let o=e-s.offset;return{from:o,to:o+s.node.nodeValue.length,node:s.node}}let Xn=class{constructor(){this.changes=[]}compareRange(t,e){Re(t,e,this.changes)}comparePoint(t,e){Re(t,e,this.changes)}boundChange(t){Re(t,t,this.changes)}};class An{constructor(){this.changes=[]}compareRange(t,e){Re(t,e,this.changes)}comparePoint(){}boundChange(t){Re(t,t,this.changes)}}class Mn extends Pe{constructor(t){super(),this.height=t}toDOM(){let t=document.createElement(\"div\");return t.className=\"cm-gap\",this.updateDOM(t),t}eq(t){return t.height==this.height}updateDOM(t){return t.style.height=this.height+\"px\",!0}get editable(){return!0}get estimatedHeight(){return this.height}ignoreEvent(){return!1}}function Rn(t,e,i){let n=t.lineBlockAt(e);if(Array.isArray(n.type)){let t;for(let r of n.type){if(r.from>e)break;if(!(r.to<e)){if(r.from<e&&r.to>e)return r;t&&(r.type!=Te.Text||t.type==r.type&&!(i<0?r.from<e:r.to>e))||(t=r)}}return t||n}return n}function zn(t,e,i,n){let r=t.state.doc.lineAt(e.head),s=t.bidiSpans(r),o=t.textDirectionAt(r.from);for(let a=e,l=null;;){let e=wi(r,s,o,a,i),h=vi;if(!e){if(r.number==(i?t.state.doc.lines:1))return a;h=\"\\n\",r=t.state.doc.line(r.number+(i?1:-1)),s=t.bidiSpans(r),e=t.visualLineSide(r,!i)}if(l){if(!l(h))return a}else{if(!n)return e;l=n(h)}a=e}}function _n(t,e,i){for(;;){let n=0;for(let r of t)r.between(e-1,e+1,(t,r,s)=>{if(e>t&&e<r){let s=n||i||(e-t<r-e?-1:1);e=s<0?t:r,n=s}});if(!n)return e}}function En(t,e){let i=null;for(let n=0;n<e.ranges.length;n++){let r=e.ranges[n],s=null;if(r.empty){let e=_n(t,r.from,0);e!=r.from&&(s=Y.cursor(e,-1))}else{let e=_n(t,r.from,-1),i=_n(t,r.to,1);e==r.from&&i==r.to||(s=Y.range(r.from==r.anchor?e:i,r.from==r.head?e:i))}s&&(i||(i=e.ranges.slice()),i[n]=s)}return i?Y.create(i,e.mainIndex):e}function Yn(t,e,i){let n=_n(t.state.facet(Ui).map(e=>e(t)),i.from,e.head>i.from?-1:1);return n==i.from?i:Y.cursor(n,n<i.from?1:-1)}class Ln{constructor(t,e){this.pos=t,this.assoc=e}}function qn(t,e,i,n){let r,s=t.contentDOM.getBoundingClientRect(),o=s.top+t.viewState.paddingTop,{x:a,y:l}=e,h=l-o;for(;;){if(h<0)return new Ln(0,1);if(h>t.viewState.docHeight)return new Ln(t.state.doc.length,-1);if(r=t.elementAtHeight(h),null==n)break;if(r.type==Te.Text){let e=t.docView.coordsAt(n<0?r.from:r.to,n);if(e&&(n<0?e.top<=h+o:e.bottom>=h+o))break}let e=t.viewState.heightOracle.textHeight/2;h=n>0?r.bottom+e:r.top-e}if(t.viewport.from>=r.to||t.viewport.to<=r.from){if(i)return null;if(r.type==Te.Text){let e=function(t,e,i,n,r){let s=Math.round((n-e.left)*t.defaultCharacterWidth);if(t.lineWrapping&&i.height>1.5*t.defaultLineHeight){let e=t.viewState.heightOracle.textHeight;s+=Math.floor((r-i.top-.5*(t.defaultLineHeight-e))/e)*t.viewState.heightOracle.lineLength}let o=t.state.sliceDoc(i.from,i.to);return i.from+Ut(o,s,t.state.tabSize)}(t,s,r,a,l);return new Ln(e,e==r.from?1:-1)}}if(r.type!=Te.Text)return h<(r.top+r.bottom)/2?new Ln(r.from,1):new Ln(r.to,-1);let c=t.docView.lineAt(r.from,2);return c&&c.length==r.length||(c=t.docView.lineAt(r.from,-2)),Vn(t,c,r.from,a,l)}function Vn(t,e,i,n,r){let s=-1,o=null,a=1e9,l=1e9,h=r,c=r,u=(t,e)=>{for(let i=0;i<t.length;i++){let u=t[i];if(u.top==u.bottom)continue;let d=u.left>n?u.left-n:u.right<n?n-u.right:0,f=u.top>r?u.top-r:u.bottom<r?r-u.bottom:0;u.top<=c&&u.bottom>=h&&(h=Math.min(u.top,h),c=Math.max(u.bottom,c),f=0),(s<0||(f-l||d-a)<0)&&(s>=0&&l&&a<d&&o.top<=c-2&&o.bottom>=h+2?l=0:(s=e,a=d,l=f,o=u))}};if(e.isText()){for(let t=0;t<e.length;){let i=S(e.text,t);if(u(Ke(e.dom,t,i).getClientRects(),t),!a&&!l)break;t=i}return n>(o.left+o.right)/2==(Wn(t,s+i)==ri.LTR)?new Ln(i+S(e.text,s),-1):new Ln(i+s,1)}{if(!e.length)return new Ln(i,1);for(let t=0;t<e.children.length;t++){let i=e.children[t];if(!(48&i.flags)&&(u((1==i.dom.nodeType?i.dom:Ke(i.dom,0,i.length)).getClientRects(),t),!a&&!l))break}let h=e.children[s],c=e.posBefore(h,i);return h.isComposite()||h.isText()?Vn(t,h,c,Math.max(o.left,Math.min(o.right,n)),r):n>(o.left+o.right)/2==(Wn(t,s+i)==ri.LTR)?new Ln(c+h.length,-1):new Ln(c,1)}}function Wn(t,e){let i=t.state.doc.lineAt(e);return t.bidiSpans(i)[Oi.find(t.bidiSpans(i),e-i.from,-1,1)].dir}const Dn=\"￿\";class Bn{constructor(t,e){this.points=t,this.view=e,this.text=\"\",this.lineSeparator=e.state.facet(Ct.lineSeparator)}append(t){this.text+=t}lineBreak(){this.text+=Dn}readRange(t,e){if(!t)return this;let i=t.parentNode;for(let n=t;;){this.findPointBefore(i,n);let t=this.text.length;this.readNode(n);let r=sn.get(n),s=n.nextSibling;if(s==e){(null==r?void 0:r.breakAfter)&&!s&&i!=this.view.contentDOM&&this.lineBreak();break}let o=sn.get(s);(r&&o?r.breakAfter:(r?r.breakAfter:We(n))||We(s)&&(\"BR\"!=n.nodeName||(null==r?void 0:r.isWidget()))&&this.text.length>t)&&!In(s,e)&&this.lineBreak(),n=s}return this.findPointBefore(i,e),this}readTextNode(t){let e=t.nodeValue;for(let i of this.points)i.node==t&&(i.pos=this.text.length+Math.min(i.offset,e.length));for(let i=0,n=this.lineSeparator?null:/\\r\\n?|\\n/g;;){let r,s=-1,o=1;if(this.lineSeparator?(s=e.indexOf(this.lineSeparator,i),o=this.lineSeparator.length):(r=n.exec(e))&&(s=r.index,o=r[0].length),this.append(e.slice(i,s<0?e.length:s)),s<0)break;if(this.lineBreak(),o>1)for(let e of this.points)e.node==t&&e.pos>this.text.length&&(e.pos-=o-1);i=s+o}}readNode(t){let e=sn.get(t),i=e&&e.overrideDOMText;if(null!=i){this.findPointInside(t,i.length);for(let t=i.iter();!t.next().done;)t.lineBreak?this.lineBreak():this.append(t.value)}else 3==t.nodeType?this.readTextNode(t):\"BR\"==t.nodeName?t.nextSibling&&this.lineBreak():1==t.nodeType&&this.readRange(t.firstChild,null)}findPointBefore(t,e){for(let i of this.points)i.node==t&&t.childNodes[i.offset]==e&&(i.pos=this.text.length)}findPointInside(t,e){for(let i of this.points)(3==t.nodeType?i.node==t:t.contains(i.node))&&(i.pos=this.text.length+(jn(t,i.node,i.offset)?e:0))}}function jn(t,e,i){for(;;){if(!e||i<Be(e))return!1;if(e==t)return!0;i=Ve(e)+1,e=e.parentNode}}function In(t,e){let i;for(;t!=e&&t;t=t.nextSibling){let e=sn.get(t);if(!(null==e?void 0:e.isWidget()))return!1;e&&(i||(i=[])).push(e)}if(i)for(let n of i){let t=n.overrideDOMText;if(null==t?void 0:t.length)return!1}return!0}class Gn{constructor(t,e){this.node=t,this.offset=e,this.pos=-1}}class Nn{constructor(t,e,i,n){this.typeOver=n,this.bounds=null,this.text=\"\",this.domChanged=e>-1;let{impreciseHead:r,impreciseAnchor:s}=t.docView;if(t.state.readOnly&&e>-1)this.newSel=null;else if(e>-1&&(this.bounds=Un(t.docView.tile,e,i,0))){let e=r||s?[]:function(t){let e=[];if(t.root.activeElement!=t.contentDOM)return e;let{anchorNode:i,anchorOffset:n,focusNode:r,focusOffset:s}=t.observer.selectionRange;i&&(e.push(new Gn(i,n)),r==i&&s==n||e.push(new Gn(r,s)));return e}(t),i=new Bn(e,t);i.readRange(this.bounds.startDOM,this.bounds.endDOM),this.text=i.text,this.newSel=function(t,e){if(0==t.length)return null;let i=t[0].pos,n=2==t.length?t[1].pos:i;return i>-1&&n>-1?Y.single(i+e,n+e):null}(e,this.bounds.from)}else{let e=t.observer.selectionRange,i=r&&r.node==e.focusNode&&r.offset==e.focusOffset||!Ee(t.contentDOM,e.focusNode)?t.state.selection.main.head:t.docView.posFromDOM(e.focusNode,e.focusOffset),n=s&&s.node==e.anchorNode&&s.offset==e.anchorOffset||!Ee(t.contentDOM,e.anchorNode)?t.state.selection.main.anchor:t.docView.posFromDOM(e.anchorNode,e.anchorOffset),o=t.viewport;if((we.ios||we.chrome)&&t.state.selection.main.empty&&i!=n&&(o.from>0||o.to<t.state.doc.length)){let e=Math.min(i,n),r=Math.max(i,n),s=o.from-e,a=o.to-r;0!=s&&1!=s&&0!=e||0!=a&&-1!=a&&r!=t.state.doc.length||(i=0,n=t.state.doc.length)}t.inputState.composing>-1&&t.state.selection.ranges.length>1?this.newSel=t.state.selection.replaceRange(Y.range(n,i)):this.newSel=Y.single(n,i)}}}function Un(t,e,i,n){if(t.isComposite()){let r=-1,s=-1,o=-1,a=-1;for(let l=0,h=n,c=n;l<t.children.length;l++){let n=t.children[l],u=h+n.length;if(h<e&&u>i)return Un(n,e,i,h);if(u>=e&&-1==r&&(r=l,s=h),h>i&&n.dom.parentNode==t.dom){o=l,a=c;break}c=u,h=u+n.breakAfter}return{from:s,to:a<0?n+t.length:a,startDOM:(r?t.children[r-1].dom.nextSibling:null)||t.dom.firstChild,endDOM:o<t.children.length&&o>=0?t.children[o].dom:null}}return t.isText()?{from:n,to:n+t.length,startDOM:t.dom,endDOM:t.dom.nextSibling}:null}function Hn(t,e){let i,{newSel:n}=e,r=t.state.selection.main,s=t.inputState.lastKeyTime>Date.now()-100?t.inputState.lastKeyCode:-1;if(e.bounds){let{from:n,to:o}=e.bounds,a=r.from,l=null;(8===s||we.android&&e.text.length<o-n)&&(a=r.to,l=\"end\");let h=Kn(t.state.doc.sliceString(n,o,Dn),e.text,a-n,l);h&&(we.chrome&&13==s&&h.toB==h.from+2&&e.text.slice(h.from,h.toB)==Dn+Dn&&h.toB--,i={from:n+h.from,to:n+h.toA,insert:f.of(e.text.slice(h.from,h.toB).split(Dn))})}else n&&(!t.hasFocus&&t.state.facet(Li)||n.main.eq(r))&&(n=null);if(!i&&!n)return!1;if(!i&&e.typeOver&&!r.empty&&n&&n.main.empty?i={from:r.from,to:r.to,insert:t.state.doc.slice(r.from,r.to)}:(we.mac||we.android)&&i&&i.from==i.to&&i.from==r.head-1&&/^\\. ?$/.test(i.insert.toString())&&\"off\"==t.contentDOM.getAttribute(\"autocorrect\")?(n&&2==i.insert.length&&(n=Y.single(n.main.anchor-1,n.main.head-1)),i={from:i.from,to:i.to,insert:f.of([i.insert.toString().replace(\".\",\" \")])}):i&&i.from>=r.from&&i.to<=r.to&&(i.from!=r.from||i.to!=r.to)&&r.to-r.from-(i.to-i.from)<=4?i={from:r.from,to:r.to,insert:t.state.doc.slice(r.from,i.from).append(i.insert).append(t.state.doc.slice(i.to,r.to))}:t.state.doc.lineAt(r.from).to<r.to&&t.docView.lineHasWidget(r.to)&&t.inputState.insertingTextAt>Date.now()-50?i={from:r.from,to:r.to,insert:t.state.toText(t.inputState.insertingText)}:we.chrome&&i&&i.from==i.to&&i.from==r.head&&\"\\n \"==i.insert.toString()&&t.lineWrapping&&(n&&(n=Y.single(n.main.anchor-1,n.main.head-1)),i={from:r.from,to:r.to,insert:f.of([\" \"])}),i)return Fn(t,i,n,s);if(n&&!n.main.eq(r)){let e=!1,i=\"select\";return t.inputState.lastSelectionTime>Date.now()-50&&(\"select\"==t.inputState.lastSelectionOrigin&&(e=!0),i=t.inputState.lastSelectionOrigin,\"select.pointer\"==i&&(n=En(t.state.facet(Ui).map(e=>e(t)),n))),t.dispatch({selection:n,scrollIntoView:e,userEvent:i}),!0}return!1}function Fn(t,e,i,n=-1){if(we.ios&&t.inputState.flushIOSKey(e))return!0;let r=t.state.selection.main;if(we.android&&(e.to==r.to&&(e.from==r.from||e.from==r.from-1&&\" \"==t.state.sliceDoc(e.from,r.from))&&1==e.insert.length&&2==e.insert.lines&&Je(t.contentDOM,\"Enter\",13)||(e.from==r.from-1&&e.to==r.to&&0==e.insert.length||8==n&&e.insert.length<e.to-e.from&&e.to>r.head)&&Je(t.contentDOM,\"Backspace\",8)||e.from==r.from&&e.to==r.to+1&&0==e.insert.length&&Je(t.contentDOM,\"Delete\",46)))return!0;let s,o=e.insert.toString();t.inputState.composing>=0&&t.inputState.composing++;let a=()=>s||(s=function(t,e,i){let n,r=t.state,s=r.selection.main,o=-1;if(e.from==e.to&&e.from<s.from||e.from>s.to){let i=e.from<s.from?-1:1,n=i<0?s.from:s.to,a=_n(r.facet(Ui).map(e=>e(t)),n,i);e.from==a&&(o=a)}if(o>-1)n={changes:e,selection:Y.cursor(e.from+e.insert.length,-1)};else if(e.from>=s.from&&e.to<=s.to&&e.to-e.from>=(s.to-s.from)/3&&(!i||i.main.empty&&i.main.from==e.from+e.insert.length)&&t.inputState.composing<0){let i=s.from<e.from?r.sliceDoc(s.from,e.from):\"\",o=s.to>e.to?r.sliceDoc(e.to,s.to):\"\";n=r.replaceSelection(t.state.toText(i+e.insert.sliceString(0,void 0,t.state.lineBreak)+o))}else{let o=r.changes(e),a=i&&i.main.to<=o.newLength?i.main:void 0;if(r.selection.ranges.length>1&&(t.inputState.composing>=0||t.inputState.compositionPendingChange)&&e.to<=s.to+10&&e.to>=s.to-10){let l,h=t.state.sliceDoc(e.from,e.to),c=i&&Zn(t,i.main.head);if(c){let t=e.insert.length-(e.to-e.from);l={from:c.from,to:c.to-t}}else l=t.state.doc.lineAt(s.head);let u=s.to-e.to;n=r.changeByRange(i=>{if(i.from==s.from&&i.to==s.to)return{changes:o,range:a||i.map(o)};let n=i.to-u,c=n-h.length;if(t.state.sliceDoc(c,n)!=h||n>=l.from&&c<=l.to)return{range:i};let d=r.changes({from:c,to:n,insert:e.insert}),f=i.to-s.to;return{changes:d,range:a?Y.range(Math.max(0,a.anchor+f),Math.max(0,a.head+f)):i.map(d)}})}else n={changes:o,selection:a&&r.selection.replaceRange(a)}}let a=\"input.type\";(t.composing||t.inputState.compositionPendingChange&&t.inputState.compositionEndedAt>Date.now()-50)&&(t.inputState.compositionPendingChange=!1,a+=\".compose\",t.inputState.compositionFirstChange&&(a+=\".start\",t.inputState.compositionFirstChange=!1));return r.update(n,{userEvent:a,scrollIntoView:!0})}(t,e,i));return t.state.facet(Ti).some(i=>i(t,e.from,e.to,o,a))||t.dispatch(a()),!0}function Kn(t,e,i,n){let r=Math.min(t.length,e.length),s=0;for(;s<r&&t.charCodeAt(s)==e.charCodeAt(s);)s++;if(s==r&&t.length==e.length)return null;let o=t.length,a=e.length;for(;o>0&&a>0&&t.charCodeAt(o-1)==e.charCodeAt(a-1);)o--,a--;if(\"end\"==n){i-=o+Math.max(0,s-Math.min(o,a))-s}if(o<s&&t.length<e.length){s-=i<=s&&i>=o?s-i:0,a=s+(a-o),o=s}else if(a<s){s-=i<=s&&i>=a?s-i:0,o=s+(o-a),a=s}return{from:s,toA:o,toB:a}}class Jn{setSelectionOrigin(t){this.lastSelectionOrigin=t,this.lastSelectionTime=Date.now()}constructor(t){var e;this.view=t,this.lastKeyCode=0,this.lastKeyTime=0,this.lastTouchTime=0,this.lastFocusTime=0,this.lastScrollTop=0,this.lastScrollLeft=0,this.pendingIOSKey=void 0,this.tabFocusMode=-1,this.lastSelectionOrigin=null,this.lastSelectionTime=0,this.lastContextMenu=0,this.scrollHandlers=[],this.handlers=Object.create(null),this.composing=-1,this.compositionFirstChange=null,this.compositionEndedAt=0,this.compositionPendingKey=!1,this.compositionPendingChange=!1,this.insertingText=\"\",this.insertingTextAt=0,this.mouseSelection=null,this.draggedContent=null,this.handleEvent=this.handleEvent.bind(this),this.notifiedFocused=t.hasFocus,we.safari&&t.contentDOM.addEventListener(\"input\",()=>null),we.gecko&&(e=t.contentDOM.ownerDocument,xr.has(e)||(xr.add(e),e.addEventListener(\"copy\",()=>{}),e.addEventListener(\"cut\",()=>{})))}handleEvent(t){(function(t,e){if(!e.bubbles)return!0;if(e.defaultPrevented)return!1;for(let i,n=e.target;n!=t.contentDOM;n=n.parentNode)if(!n||11==n.nodeType||(i=sn.get(n))&&i.isWidget()&&!i.isHidden&&i.widget.ignoreEvent(e))return!1;return!0})(this.view,t)&&!this.ignoreDuringComposition(t)&&(\"keydown\"==t.type&&this.keydown(t)||(0!=this.view.updateState?Promise.resolve().then(()=>this.runHandlers(t.type,t)):this.runHandlers(t.type,t)))}runHandlers(t,e){let i=this.handlers[t];if(i){for(let t of i.observers)t(this.view,e);for(let t of i.handlers){if(e.defaultPrevented)break;if(t(this.view,e)){e.preventDefault();break}}}}ensureHandlers(t){let e=function(t){let e=Object.create(null);function i(t){return e[t]||(e[t]={observers:[],handlers:[]})}for(let n of t){let t=n.spec,e=t&&t.plugin.domEventHandlers,r=t&&t.plugin.domEventObservers;if(e)for(let s in e){let t=e[s];t&&i(s).handlers.push(tr(n.value,t))}if(r)for(let s in r){let t=r[s];t&&i(s).observers.push(tr(n.value,t))}}for(let n in or)i(n).handlers.push(or[n]);for(let n in ar)i(n).observers.push(ar[n]);return e}(t),i=this.handlers,n=this.view.contentDOM;for(let r in e)if(\"scroll\"!=r){let t=!e[r].handlers.length,s=i[r];s&&t!=!s.handlers.length&&(n.removeEventListener(r,this.handleEvent),s=null),s||n.addEventListener(r,this.handleEvent,{passive:t})}for(let r in i)\"scroll\"==r||e[r]||n.removeEventListener(r,this.handleEvent);this.handlers=e}keydown(t){if(this.lastKeyCode=t.keyCode,this.lastKeyTime=Date.now(),9==t.keyCode&&this.tabFocusMode>-1&&(!this.tabFocusMode||Date.now()<=this.tabFocusMode))return!0;if(this.tabFocusMode>0&&27!=t.keyCode&&nr.indexOf(t.keyCode)<0&&(this.tabFocusMode=-1),we.android&&we.chrome&&!t.synthetic&&(13==t.keyCode||8==t.keyCode))return this.view.observer.delayAndroidKey(t.key,t.keyCode),!0;let e;return!we.ios||t.synthetic||t.altKey||t.metaKey||!((e=er.find(e=>e.keyCode==t.keyCode))&&!t.ctrlKey||ir.indexOf(t.key)>-1&&t.ctrlKey&&!t.shiftKey)?(229!=t.keyCode&&this.view.observer.forceFlush(),!1):(this.pendingIOSKey=e||t,setTimeout(()=>this.flushIOSKey(),250),!0)}flushIOSKey(t){let e=this.pendingIOSKey;return!!e&&(!(\"Enter\"==e.key&&t&&t.from<t.to&&/^\\S+$/.test(t.insert.toString()))&&(this.pendingIOSKey=void 0,Je(this.view.contentDOM,e.key,e.keyCode,e instanceof KeyboardEvent?e:void 0)))}ignoreDuringComposition(t){return!(!/^key/.test(t.type)||t.synthetic)&&(this.composing>0||!!(we.safari&&!we.ios&&this.compositionPendingKey&&Date.now()-this.compositionEndedAt<100)&&(this.compositionPendingKey=!1,!0))}startMouseSelection(t){this.mouseSelection&&this.mouseSelection.destroy(),this.mouseSelection=t}update(t){this.view.observer.update(t),this.mouseSelection&&this.mouseSelection.update(t),this.draggedContent&&t.docChanged&&(this.draggedContent=this.draggedContent.map(t.changes)),t.transactions.length&&(this.lastKeyCode=this.lastSelectionTime=0)}destroy(){this.mouseSelection&&this.mouseSelection.destroy()}}function tr(t,e){return(i,n)=>{try{return e.call(t,n,i)}catch(r){Yi(i.state,r)}}}const er=[{key:\"Backspace\",keyCode:8,inputType:\"deleteContentBackward\"},{key:\"Enter\",keyCode:13,inputType:\"insertParagraph\"},{key:\"Enter\",keyCode:13,inputType:\"insertLineBreak\"},{key:\"Delete\",keyCode:46,inputType:\"deleteContentForward\"}],ir=\"dthko\",nr=[16,17,18,20,91,92,224,225];function rr(t){return.7*Math.max(0,t)+8}class sr{constructor(t,e,i,n){this.view=t,this.startEvent=e,this.style=i,this.mustSelect=n,this.scrollSpeed={x:0,y:0},this.scrolling=-1,this.lastEvent=e,this.scrollParents=function(t){let e,i,n=t.ownerDocument;for(let r=t.parentNode;r&&!(r==n.body||e&&i);)if(1==r.nodeType)!i&&r.scrollHeight>r.clientHeight&&(i=r),!e&&r.scrollWidth>r.clientWidth&&(e=r),r=r.assignedSlot||r.parentNode;else{if(11!=r.nodeType)break;r=r.host}return{x:e,y:i}}(t.contentDOM),this.atoms=t.state.facet(Ui).map(e=>e(t));let r=t.contentDOM.ownerDocument;r.addEventListener(\"mousemove\",this.move=this.move.bind(this)),r.addEventListener(\"mouseup\",this.up=this.up.bind(this)),this.extend=e.shiftKey,this.multiple=t.state.facet(Ct.allowMultipleSelections)&&function(t,e){let i=t.state.facet(Si);return i.length?i[0](e):we.mac?e.metaKey:e.ctrlKey}(t,e),this.dragging=!(!function(t,e){let{main:i}=t.state.selection;if(i.empty)return!1;let n=_e(t.root);if(!n||0==n.rangeCount)return!0;let r=n.getRangeAt(0).getClientRects();for(let s=0;s<r.length;s++){let t=r[s];if(t.left<=e.clientX&&t.right>=e.clientX&&t.top<=e.clientY&&t.bottom>=e.clientY)return!0}return!1}(t,e)||1!=mr(e))&&null}start(t){!1===this.dragging&&this.select(t)}move(t){if(0==t.buttons)return this.destroy();if(this.dragging||null==this.dragging&&(e=this.startEvent,i=t,Math.max(Math.abs(e.clientX-i.clientX),Math.abs(e.clientY-i.clientY))<10))return;var e,i;this.select(this.lastEvent=t);let n=0,r=0,s=0,o=0,a=this.view.win.innerWidth,l=this.view.win.innerHeight;this.scrollParents.x&&({left:s,right:a}=this.scrollParents.x.getBoundingClientRect()),this.scrollParents.y&&({top:o,bottom:l}=this.scrollParents.y.getBoundingClientRect());let h=Ji(this.view);t.clientX-h.left<=s+6?n=-rr(s-t.clientX):t.clientX+h.right>=a-6&&(n=rr(t.clientX-a)),t.clientY-h.top<=o+6?r=-rr(o-t.clientY):t.clientY+h.bottom>=l-6&&(r=rr(t.clientY-l)),this.setScrollSpeed(n,r)}up(t){null==this.dragging&&this.select(this.lastEvent),this.dragging||t.preventDefault(),this.destroy()}destroy(){this.setScrollSpeed(0,0);let t=this.view.contentDOM.ownerDocument;t.removeEventListener(\"mousemove\",this.move),t.removeEventListener(\"mouseup\",this.up),this.view.inputState.mouseSelection=this.view.inputState.draggedContent=null}setScrollSpeed(t,e){this.scrollSpeed={x:t,y:e},t||e?this.scrolling<0&&(this.scrolling=setInterval(()=>this.scroll(),50)):this.scrolling>-1&&(clearInterval(this.scrolling),this.scrolling=-1)}scroll(){let{x:t,y:e}=this.scrollSpeed;t&&this.scrollParents.x&&(this.scrollParents.x.scrollLeft+=t,t=0),e&&this.scrollParents.y&&(this.scrollParents.y.scrollTop+=e,e=0),(t||e)&&this.view.win.scrollBy(t,e),!1===this.dragging&&this.select(this.lastEvent)}select(t){let{view:e}=this,i=En(this.atoms,this.style.get(t,this.extend,this.multiple));!this.mustSelect&&i.eq(e.state.selection,!1===this.dragging)||this.view.dispatch({selection:i,userEvent:\"select.pointer\"}),this.mustSelect=!1}update(t){t.transactions.some(t=>t.isUserEvent(\"input.type\"))?this.destroy():this.style.update(t)&&setTimeout(()=>this.select(this.lastEvent),20)}}const or=Object.create(null),ar=Object.create(null),lr=we.ie&&we.ie_version<15||we.ios&&we.webkit_version<604;function hr(t,e,i){for(let n of t.facet(e))i=n(i,t);return i}function cr(t,e){e=hr(t.state,Zi,e);let i,{state:n}=t,r=1,s=n.toText(e),o=s.lines==n.selection.ranges.length;if(null!=Qr&&n.selection.ranges.every(t=>t.empty)&&Qr==s.toString()){let t=-1;i=n.changeByRange(i=>{let a=n.doc.lineAt(i.from);if(a.from==t)return{range:i};t=a.from;let l=n.toText((o?s.line(r++).text:e)+n.lineBreak);return{changes:{from:a.from,insert:l},range:Y.cursor(i.from+l.length)}})}else i=o?n.changeByRange(t=>{let e=s.line(r++);return{changes:{from:t.from,to:t.to,insert:e.text},range:Y.cursor(t.from+e.length)}}):n.replaceSelection(s);t.dispatch(i,{userEvent:\"input.paste\",scrollIntoView:!0})}function ur(t,e,i,n){if(1==n)return Y.cursor(e,i);if(2==n)return function(t,e,i=1){let n=t.charCategorizer(e),r=t.doc.lineAt(e),s=e-r.from;if(0==r.length)return Y.cursor(e);0==s?i=1:s==r.length&&(i=-1);let o=s,a=s;i<0?o=S(r.text,s,!1):a=S(r.text,s);let l=n(r.text.slice(o,a));for(;o>0;){let t=S(r.text,o,!1);if(n(r.text.slice(t,o))!=l)break;o=t}for(;a<r.length;){let t=S(r.text,a);if(n(r.text.slice(a,t))!=l)break;a=t}return Y.range(o+r.from,a+r.from)}(t.state,e,i);{let n=t.docView.lineAt(e,i),r=t.state.doc.lineAt(n?n.posAtEnd:e),s=n?n.posAtStart:r.from,o=n?n.posAtEnd:r.to;return o<t.state.doc.length&&o==r.to&&o++,Y.range(s,o)}}ar.scroll=t=>{t.inputState.lastScrollTop=t.scrollDOM.scrollTop,t.inputState.lastScrollLeft=t.scrollDOM.scrollLeft},or.keydown=(t,e)=>(t.inputState.setSelectionOrigin(\"select\"),27==e.keyCode&&0!=t.inputState.tabFocusMode&&(t.inputState.tabFocusMode=Date.now()+2e3),!1),ar.touchstart=(t,e)=>{t.inputState.lastTouchTime=Date.now(),t.inputState.setSelectionOrigin(\"select.pointer\")},ar.touchmove=t=>{t.inputState.setSelectionOrigin(\"select.pointer\")},or.mousedown=(t,e)=>{if(t.observer.flush(),t.inputState.lastTouchTime>Date.now()-2e3)return!1;let i=null;for(let n of t.state.facet(ki))if(i=n(t,e),i)break;if(i||0!=e.button||(i=function(t,e){let i=t.posAndSideAtCoords({x:e.clientX,y:e.clientY},!1),n=mr(e),r=t.state.selection;return{update(t){t.docChanged&&(i.pos=t.changes.mapPos(i.pos),r=r.map(t.changes))},get(e,s,o){let a,l=t.posAndSideAtCoords({x:e.clientX,y:e.clientY},!1),h=ur(t,l.pos,l.assoc,n);if(i.pos!=l.pos&&!s){let e=ur(t,i.pos,i.assoc,n),r=Math.min(e.from,h.from),s=Math.max(e.to,h.to);h=r<h.from?Y.range(r,s):Y.range(s,r)}return s?r.replaceRange(r.main.extend(h.from,h.to)):o&&1==n&&r.ranges.length>1&&(a=function(t,e){for(let i=0;i<t.ranges.length;i++){let{from:n,to:r}=t.ranges[i];if(n<=e&&r>=e)return Y.create(t.ranges.slice(0,i).concat(t.ranges.slice(i+1)),t.mainIndex==i?0:t.mainIndex-(t.mainIndex>i?1:0))}return null}(r,l.pos))?a:o?r.addRange(h):Y.create([h])}}}(t,e)),i){let n=!t.hasFocus;t.inputState.startMouseSelection(new sr(t,e,i,n)),n&&t.observer.ignore(()=>{Fe(t.contentDOM);let e=t.root.activeElement;e&&!e.contains(t.contentDOM)&&e.blur()});let r=t.inputState.mouseSelection;if(r)return r.start(e),!1===r.dragging}else t.inputState.setSelectionOrigin(\"select.pointer\");return!1};const dr=we.ie&&we.ie_version<=11;let fr=null,Or=0,pr=0;function mr(t){if(!dr)return t.detail;let e=fr,i=pr;return fr=t,pr=Date.now(),Or=!e||i>Date.now()-400&&Math.abs(e.clientX-t.clientX)<2&&Math.abs(e.clientY-t.clientY)<2?(Or+1)%3:1}function gr(t,e,i,n){if(!(i=hr(t.state,Zi,i)))return;let r=t.posAtCoords({x:e.clientX,y:e.clientY},!1),{draggedContent:s}=t.inputState,o=n&&s&&function(t,e){let i=t.state.facet(yi);return i.length?i[0](e):we.mac?!e.altKey:!e.ctrlKey}(t,e)?{from:s.from,to:s.to}:null,a={from:r,insert:i},l=t.state.changes(o?[o,a]:a);t.focus(),t.dispatch({changes:l,selection:{anchor:l.mapPos(r,-1),head:l.mapPos(r,1)},userEvent:o?\"move.drop\":\"input.drop\"}),t.inputState.draggedContent=null}or.dragstart=(t,e)=>{let{selection:{main:i}}=t.state;if(e.target.draggable){let n=t.docView.tile.nearest(e.target);if(n&&n.isWidget()){let t=n.posAtStart,e=t+n.length;(t>=i.to||e<=i.from)&&(i=Y.range(t,e))}}let{inputState:n}=t;return n.mouseSelection&&(n.mouseSelection.dragging=!0),n.draggedContent=i,e.dataTransfer&&(e.dataTransfer.setData(\"Text\",hr(t.state,Xi,t.state.sliceDoc(i.from,i.to))),e.dataTransfer.effectAllowed=\"copyMove\"),!1},or.dragend=t=>(t.inputState.draggedContent=null,!1),or.drop=(t,e)=>{if(!e.dataTransfer)return!1;if(t.state.readOnly)return!0;let i=e.dataTransfer.files;if(i&&i.length){let n=Array(i.length),r=0,s=()=>{++r==i.length&&gr(t,e,n.filter(t=>null!=t).join(t.state.lineBreak),!1)};for(let t=0;t<i.length;t++){let e=new FileReader;e.onerror=s,e.onload=()=>{/[\\x00-\\x08\\x0e-\\x1f]{2}/.test(e.result)||(n[t]=e.result),s()},e.readAsText(i[t])}return!0}{let i=e.dataTransfer.getData(\"Text\");if(i)return gr(t,e,i,!0),!0}return!1},or.paste=(t,e)=>{if(t.state.readOnly)return!0;t.observer.flush();let i=lr?null:e.clipboardData;return i?(cr(t,i.getData(\"text/plain\")||i.getData(\"text/uri-list\")),!0):(function(t){let e=t.dom.parentNode;if(!e)return;let i=e.appendChild(document.createElement(\"textarea\"));i.style.cssText=\"position: fixed; left: -10000px; top: 10px\",i.focus(),setTimeout(()=>{t.focus(),i.remove(),cr(t,i.value)},50)}(t),!1)};let Qr=null;or.copy=or.cut=(t,e)=>{let{text:i,ranges:n,linewise:r}=function(t){let e=[],i=[],n=!1;for(let r of t.selection.ranges)r.empty||(e.push(t.sliceDoc(r.from,r.to)),i.push(r));if(!e.length){let r=-1;for(let{from:n}of t.selection.ranges){let s=t.doc.lineAt(n);s.number>r&&(e.push(s.text),i.push({from:s.from,to:Math.min(t.doc.length,s.to+1)})),r=s.number}n=!0}return{text:hr(t,Xi,e.join(t.lineBreak)),ranges:i,linewise:n}}(t.state);if(!i&&!r)return!1;Qr=r?i:null,\"cut\"!=e.type||t.state.readOnly||t.dispatch({changes:n,scrollIntoView:!0,userEvent:\"delete.cut\"});let s=lr?null:e.clipboardData;return s?(s.clearData(),s.setData(\"text/plain\",i),!0):(function(t,e){let i=t.dom.parentNode;if(!i)return;let n=i.appendChild(document.createElement(\"textarea\"));n.style.cssText=\"position: fixed; left: -10000px; top: 10px\",n.value=e,n.focus(),n.selectionEnd=e.length,n.selectionStart=0,setTimeout(()=>{n.remove(),t.focus()},50)}(t,i),!1)};const br=Ot.define();function vr(t,e){let i=[];for(let n of t.facet(Ci)){let r=n(t,e);r&&i.push(r)}return i.length?t.update({effects:i,annotations:br.of(!0)}):null}function wr(t){setTimeout(()=>{let e=t.hasFocus;if(e!=t.inputState.notifiedFocused){let i=vr(t.state,e);i?t.dispatch(i):t.update([])}},10)}ar.focus=t=>{t.inputState.lastFocusTime=Date.now(),t.scrollDOM.scrollTop||!t.inputState.lastScrollTop&&!t.inputState.lastScrollLeft||(t.scrollDOM.scrollTop=t.inputState.lastScrollTop,t.scrollDOM.scrollLeft=t.inputState.lastScrollLeft),wr(t)},ar.blur=t=>{t.observer.clearSelectionRange(),wr(t)},ar.compositionstart=ar.compositionupdate=t=>{t.observer.editContext||(null==t.inputState.compositionFirstChange&&(t.inputState.compositionFirstChange=!0),t.inputState.composing<0&&(t.inputState.composing=0))},ar.compositionend=t=>{t.observer.editContext||(t.inputState.composing=-1,t.inputState.compositionEndedAt=Date.now(),t.inputState.compositionPendingKey=!0,t.inputState.compositionPendingChange=t.observer.pendingRecords().length>0,t.inputState.compositionFirstChange=null,we.chrome&&we.android?t.observer.flushSoon():t.inputState.compositionPendingChange?Promise.resolve().then(()=>t.observer.flush()):setTimeout(()=>{t.inputState.composing<0&&t.docView.hasComposition&&t.update([])},50))},ar.contextmenu=t=>{t.inputState.lastContextMenu=Date.now()},or.beforeinput=(t,e)=>{var i,n;if(\"insertText\"!=e.inputType&&\"insertCompositionText\"!=e.inputType||(t.inputState.insertingText=e.data,t.inputState.insertingTextAt=Date.now()),\"insertReplacementText\"==e.inputType&&t.observer.editContext){let n=null===(i=e.dataTransfer)||void 0===i?void 0:i.getData(\"text/plain\"),r=e.getTargetRanges();if(n&&r.length){let e=r[0],i=t.posAtDOM(e.startContainer,e.startOffset),s=t.posAtDOM(e.endContainer,e.endOffset);return Fn(t,{from:i,to:s,insert:t.state.toText(n)},null),!0}}let r;if(we.chrome&&we.android&&(r=er.find(t=>t.inputType==e.inputType))&&(t.observer.delayAndroidKey(r.key,r.keyCode),\"Backspace\"==r.key||\"Delete\"==r.key)){let e=(null===(n=window.visualViewport)||void 0===n?void 0:n.height)||0;setTimeout(()=>{var i;((null===(i=window.visualViewport)||void 0===i?void 0:i.height)||0)>e+10&&t.hasFocus&&(t.contentDOM.blur(),t.focus())},100)}return we.ios&&\"deleteContentForward\"==e.inputType&&t.observer.flushSoon(),we.safari&&\"insertText\"==e.inputType&&t.inputState.composing>=0&&setTimeout(()=>ar.compositionend(t,e),20),!1};const xr=new Set;const Sr=[\"pre-wrap\",\"normal\",\"pre-line\",\"break-spaces\"];let yr=!1;function kr(){yr=!1}class $r{constructor(t){this.lineWrapping=t,this.doc=f.empty,this.heightSamples={},this.lineHeight=14,this.charWidth=7,this.textHeight=14,this.lineLength=30}heightForGap(t,e){let i=this.doc.lineAt(e).number-this.doc.lineAt(t).number+1;return this.lineWrapping&&(i+=Math.max(0,Math.ceil((e-t-i*this.lineLength*.5)/this.lineLength))),this.lineHeight*i}heightForLine(t){if(!this.lineWrapping)return this.lineHeight;return(1+Math.max(0,Math.ceil((t-this.lineLength)/Math.max(1,this.lineLength-5))))*this.lineHeight}setDoc(t){return this.doc=t,this}mustRefreshForWrapping(t){return Sr.indexOf(t)>-1!=this.lineWrapping}mustRefreshForHeights(t){let e=!1;for(let i=0;i<t.length;i++){let n=t[i];n<0?i++:this.heightSamples[Math.floor(10*n)]||(e=!0,this.heightSamples[Math.floor(10*n)]=!0)}return e}refresh(t,e,i,n,r,s){let o=Sr.indexOf(t)>-1,a=Math.round(e)!=Math.round(this.lineHeight)||this.lineWrapping!=o;if(this.lineWrapping=o,this.lineHeight=e,this.charWidth=i,this.textHeight=n,this.lineLength=r,a){this.heightSamples={};for(let t=0;t<s.length;t++){let e=s[t];e<0?t++:this.heightSamples[Math.floor(10*e)]=!0}}return a}}class Pr{constructor(t,e){this.from=t,this.heights=e,this.index=0}get more(){return this.index<this.heights.length}}class Tr{constructor(t,e,i,n,r){this.from=t,this.length=e,this.top=i,this.height=n,this._content=r}get type(){return\"number\"==typeof this._content?Te.Text:Array.isArray(this._content)?this._content:this._content.type}get to(){return this.from+this.length}get bottom(){return this.top+this.height}get widget(){return this._content instanceof Ae?this._content.widget:null}get widgetLineBreaks(){return\"number\"==typeof this._content?this._content:0}join(t){let e=(Array.isArray(this._content)?this._content:[this]).concat(Array.isArray(t._content)?t._content:[t]);return new Tr(this.from,this.length+t.length,this.top,this.height+t.height,e)}}var Cr=function(t){return t[t.ByPos=0]=\"ByPos\",t[t.ByHeight=1]=\"ByHeight\",t[t.ByPosNoHeight=2]=\"ByPosNoHeight\",t}(Cr||(Cr={}));const Zr=.001;class Xr{constructor(t,e,i=2){this.length=t,this.height=e,this.flags=i}get outdated(){return(2&this.flags)>0}set outdated(t){this.flags=(t?2:0)|-3&this.flags}setHeight(t){this.height!=t&&(Math.abs(this.height-t)>Zr&&(yr=!0),this.height=t)}replace(t,e,i){return Xr.of(i)}decomposeLeft(t,e){e.push(this)}decomposeRight(t,e){e.push(this)}applyChanges(t,e,i,n){let r=this,s=i.doc;for(let o=n.length-1;o>=0;o--){let{fromA:a,toA:l,fromB:h,toB:c}=n[o],u=r.lineAt(a,Cr.ByPosNoHeight,i.setDoc(e),0,0),d=u.to>=l?u:r.lineAt(l,Cr.ByPosNoHeight,i,0,0);for(c+=d.to-l,l=d.to;o>0&&u.from<=n[o-1].toA;)a=n[o-1].fromA,h=n[o-1].fromB,o--,a<u.from&&(u=r.lineAt(a,Cr.ByPosNoHeight,i,0,0));h+=u.from-a,a=u.from;let f=Lr.build(i.setDoc(s),t,h,c);r=Ar(r,r.replace(a,l,f))}return r.updateHeight(i,0)}static empty(){return new zr(0,0,0)}static of(t){if(1==t.length)return t[0];let e=0,i=t.length,n=0,r=0;for(;;)if(e==i)if(n>2*r){let r=t[e-1];r.break?t.splice(--e,1,r.left,null,r.right):t.splice(--e,1,r.left,r.right),i+=1+r.break,n-=r.size}else{if(!(r>2*n))break;{let e=t[i];e.break?t.splice(i,1,e.left,null,e.right):t.splice(i,1,e.left,e.right),i+=2+e.break,r-=e.size}}else if(n<r){let i=t[e++];i&&(n+=i.size)}else{let e=t[--i];e&&(r+=e.size)}let s=0;return null==t[e-1]?(s=1,e--):null==t[e]&&(s=1,i++),new Er(Xr.of(t.slice(0,e)),s,Xr.of(t.slice(i)))}}function Ar(t,e){return t==e?t:(t.constructor!=e.constructor&&(yr=!0),e)}Xr.prototype.size=1;const Mr=Ce.replace({});class Rr extends Xr{constructor(t,e,i){super(t,e),this.deco=i,this.spaceAbove=0}mainBlock(t,e){return new Tr(e,this.length,t+this.spaceAbove,this.height-this.spaceAbove,this.deco||0)}blockAt(t,e,i,n){return this.spaceAbove&&t<i+this.spaceAbove?new Tr(n,0,i,this.spaceAbove,Mr):this.mainBlock(i,n)}lineAt(t,e,i,n,r){let s=this.mainBlock(n,r);return this.spaceAbove?this.blockAt(0,i,n,r).join(s):s}forEachLine(t,e,i,n,r,s){t<=r+this.length&&e>=r&&s(this.lineAt(0,Cr.ByPos,i,n,r))}setMeasuredHeight(t){let e=t.heights[t.index++];e<0?(this.spaceAbove=-e,e=t.heights[t.index++]):this.spaceAbove=0,this.setHeight(e)}updateHeight(t,e=0,i=!1,n){return n&&n.from<=e&&n.more&&this.setMeasuredHeight(n),this.outdated=!1,this}toString(){return`block(${this.length})`}}class zr extends Rr{constructor(t,e,i){super(t,e,null),this.collapsed=0,this.widgetHeight=0,this.breaks=0,this.spaceAbove=i}mainBlock(t,e){return new Tr(e,this.length,t+this.spaceAbove,this.height-this.spaceAbove,this.breaks)}replace(t,e,i){let n=i[0];return 1==i.length&&(n instanceof zr||n instanceof _r&&4&n.flags)&&Math.abs(this.length-n.length)<10?(n instanceof _r?n=new zr(n.length,this.height,this.spaceAbove):n.height=this.height,this.outdated||(n.outdated=!1),n):Xr.of(i)}updateHeight(t,e=0,i=!1,n){return n&&n.from<=e&&n.more?this.setMeasuredHeight(n):(i||this.outdated)&&(this.spaceAbove=0,this.setHeight(Math.max(this.widgetHeight,t.heightForLine(this.length-this.collapsed))+this.breaks*t.lineHeight)),this.outdated=!1,this}toString(){return`line(${this.length}${this.collapsed?-this.collapsed:\"\"}${this.widgetHeight?\":\"+this.widgetHeight:\"\"})`}}class _r extends Xr{constructor(t){super(t,0)}heightMetrics(t,e){let i,n=t.doc.lineAt(e).number,r=t.doc.lineAt(e+this.length).number,s=r-n+1,o=0;if(t.lineWrapping){let e=Math.min(this.height,t.lineHeight*s);i=e/s,this.length>s+1&&(o=(this.height-e)/(this.length-s-1))}else i=this.height/s;return{firstLine:n,lastLine:r,perLine:i,perChar:o}}blockAt(t,e,i,n){let{firstLine:r,lastLine:s,perLine:o,perChar:a}=this.heightMetrics(e,n);if(e.lineWrapping){let r=n+(t<e.lineHeight?0:Math.round(Math.max(0,Math.min(1,(t-i)/this.height))*this.length)),s=e.doc.lineAt(r),l=o+s.length*a,h=Math.max(i,t-l/2);return new Tr(s.from,s.length,h,l,0)}{let n=Math.max(0,Math.min(s-r,Math.floor((t-i)/o))),{from:a,length:l}=e.doc.line(r+n);return new Tr(a,l,i+o*n,o,0)}}lineAt(t,e,i,n,r){if(e==Cr.ByHeight)return this.blockAt(t,i,n,r);if(e==Cr.ByPosNoHeight){let{from:e,to:n}=i.doc.lineAt(t);return new Tr(e,n-e,0,0,0)}let{firstLine:s,perLine:o,perChar:a}=this.heightMetrics(i,r),l=i.doc.lineAt(t),h=o+l.length*a,c=l.number-s,u=n+o*c+a*(l.from-r-c);return new Tr(l.from,l.length,Math.max(n,Math.min(u,n+this.height-h)),h,0)}forEachLine(t,e,i,n,r,s){t=Math.max(t,r),e=Math.min(e,r+this.length);let{firstLine:o,perLine:a,perChar:l}=this.heightMetrics(i,r);for(let h=t,c=n;h<=e;){let e=i.doc.lineAt(h);if(h==t){let i=e.number-o;c+=a*i+l*(t-r-i)}let n=a+l*e.length;s(new Tr(e.from,e.length,c,n,0)),c+=n,h=e.to+1}}replace(t,e,i){let n=this.length-e;if(n>0){let t=i[i.length-1];t instanceof _r?i[i.length-1]=new _r(t.length+n):i.push(null,new _r(n-1))}if(t>0){let e=i[0];e instanceof _r?i[0]=new _r(t+e.length):i.unshift(new _r(t-1),null)}return Xr.of(i)}decomposeLeft(t,e){e.push(new _r(t-1),null)}decomposeRight(t,e){e.push(null,new _r(this.length-t-1))}updateHeight(t,e=0,i=!1,n){let r=e+this.length;if(n&&n.from<=e+this.length&&n.more){let i=[],s=Math.max(e,n.from),o=-1;for(n.from>e&&i.push(new _r(n.from-e-1).updateHeight(t,e));s<=r&&n.more;){let e=t.doc.lineAt(s).length;i.length&&i.push(null);let r=n.heights[n.index++],a=0;r<0&&(a=-r,r=n.heights[n.index++]),-1==o?o=r:Math.abs(r-o)>=Zr&&(o=-2);let l=new zr(e,r,a);l.outdated=!1,i.push(l),s+=e+1}s<=r&&i.push(null,new _r(r-s).updateHeight(t,s));let a=Xr.of(i);return(o<0||Math.abs(a.height-this.height)>=Zr||Math.abs(o-this.heightMetrics(t,e).perLine)>=Zr)&&(yr=!0),Ar(this,a)}return(i||this.outdated)&&(this.setHeight(t.heightForGap(e,e+this.length)),this.outdated=!1),this}toString(){return`gap(${this.length})`}}class Er extends Xr{constructor(t,e,i){super(t.length+e+i.length,t.height+i.height,e|(t.outdated||i.outdated?2:0)),this.left=t,this.right=i,this.size=t.size+i.size}get break(){return 1&this.flags}blockAt(t,e,i,n){let r=i+this.left.height;return t<r?this.left.blockAt(t,e,i,n):this.right.blockAt(t,e,r,n+this.left.length+this.break)}lineAt(t,e,i,n,r){let s=n+this.left.height,o=r+this.left.length+this.break,a=e==Cr.ByHeight?t<s:t<o,l=a?this.left.lineAt(t,e,i,n,r):this.right.lineAt(t,e,i,s,o);if(this.break||(a?l.to<o:l.from>o))return l;let h=e==Cr.ByPosNoHeight?Cr.ByPosNoHeight:Cr.ByPos;return a?l.join(this.right.lineAt(o,h,i,s,o)):this.left.lineAt(o,h,i,n,r).join(l)}forEachLine(t,e,i,n,r,s){let o=n+this.left.height,a=r+this.left.length+this.break;if(this.break)t<a&&this.left.forEachLine(t,e,i,n,r,s),e>=a&&this.right.forEachLine(t,e,i,o,a,s);else{let l=this.lineAt(a,Cr.ByPos,i,n,r);t<l.from&&this.left.forEachLine(t,l.from-1,i,n,r,s),l.to>=t&&l.from<=e&&s(l),e>l.to&&this.right.forEachLine(l.to+1,e,i,o,a,s)}}replace(t,e,i){let n=this.left.length+this.break;if(e<n)return this.balanced(this.left.replace(t,e,i),this.right);if(t>this.left.length)return this.balanced(this.left,this.right.replace(t-n,e-n,i));let r=[];t>0&&this.decomposeLeft(t,r);let s=r.length;for(let o of i)r.push(o);if(t>0&&Yr(r,s-1),e<this.length){let t=r.length;this.decomposeRight(e,r),Yr(r,t)}return Xr.of(r)}decomposeLeft(t,e){let i=this.left.length;if(t<=i)return this.left.decomposeLeft(t,e);e.push(this.left),this.break&&(i++,t>=i&&e.push(null)),t>i&&this.right.decomposeLeft(t-i,e)}decomposeRight(t,e){let i=this.left.length,n=i+this.break;if(t>=n)return this.right.decomposeRight(t-n,e);t<i&&this.left.decomposeRight(t,e),this.break&&t<n&&e.push(null),e.push(this.right)}balanced(t,e){return t.size>2*e.size||e.size>2*t.size?Xr.of(this.break?[t,null,e]:[t,e]):(this.left=Ar(this.left,t),this.right=Ar(this.right,e),this.setHeight(t.height+e.height),this.outdated=t.outdated||e.outdated,this.size=t.size+e.size,this.length=t.length+this.break+e.length,this)}updateHeight(t,e=0,i=!1,n){let{left:r,right:s}=this,o=e+r.length+this.break,a=null;return n&&n.from<=e+r.length&&n.more?a=r=r.updateHeight(t,e,i,n):r.updateHeight(t,e,i),n&&n.from<=o+s.length&&n.more?a=s=s.updateHeight(t,o,i,n):s.updateHeight(t,o,i),a?this.balanced(r,s):(this.height=this.left.height+this.right.height,this.outdated=!1,this)}toString(){return this.left+(this.break?\" \":\"-\")+this.right}}function Yr(t,e){let i,n;null==t[e]&&(i=t[e-1])instanceof _r&&(n=t[e+1])instanceof _r&&t.splice(e-1,3,new _r(i.length+1+n.length))}class Lr{constructor(t,e){this.pos=t,this.oracle=e,this.nodes=[],this.lineStart=-1,this.lineEnd=-1,this.covering=null,this.writtenTo=t}get isCovered(){return this.covering&&this.nodes[this.nodes.length-1]==this.covering}span(t,e){if(this.lineStart>-1){let t=Math.min(e,this.lineEnd),i=this.nodes[this.nodes.length-1];i instanceof zr?i.length+=t-this.pos:(t>this.pos||!this.isCovered)&&this.nodes.push(new zr(t-this.pos,-1,0)),this.writtenTo=t,e>t&&(this.nodes.push(null),this.writtenTo++,this.lineStart=-1)}this.pos=e}point(t,e,i){if(t<e||i.heightRelevant){let n=i.widget?i.widget.estimatedHeight:0,r=i.widget?i.widget.lineBreaks:0;n<0&&(n=this.oracle.lineHeight);let s=e-t;i.block?this.addBlock(new Rr(s,n,i)):(s||r||n>=5)&&this.addLineDeco(n,r,s)}else e>t&&this.span(t,e);this.lineEnd>-1&&this.lineEnd<this.pos&&(this.lineEnd=this.oracle.doc.lineAt(this.pos).to)}enterLine(){if(this.lineStart>-1)return;let{from:t,to:e}=this.oracle.doc.lineAt(this.pos);this.lineStart=t,this.lineEnd=e,this.writtenTo<t&&((this.writtenTo<t-1||null==this.nodes[this.nodes.length-1])&&this.nodes.push(this.blankContent(this.writtenTo,t-1)),this.nodes.push(null)),this.pos>t&&this.nodes.push(new zr(this.pos-t,-1,0)),this.writtenTo=this.pos}blankContent(t,e){let i=new _r(e-t);return this.oracle.doc.lineAt(t).to==e&&(i.flags|=4),i}ensureLine(){this.enterLine();let t=this.nodes.length?this.nodes[this.nodes.length-1]:null;if(t instanceof zr)return t;let e=new zr(0,-1,0);return this.nodes.push(e),e}addBlock(t){this.enterLine();let e=t.deco;e&&e.startSide>0&&!this.isCovered&&this.ensureLine(),this.nodes.push(t),this.writtenTo=this.pos=this.pos+t.length,e&&e.endSide>0&&(this.covering=t)}addLineDeco(t,e,i){let n=this.ensureLine();n.length+=i,n.collapsed+=i,n.widgetHeight=Math.max(n.widgetHeight,t),n.breaks+=e,this.writtenTo=this.pos=this.pos+i}finish(t){let e=0==this.nodes.length?null:this.nodes[this.nodes.length-1];!(this.lineStart>-1)||e instanceof zr||this.isCovered?(this.writtenTo<this.pos||null==e)&&this.nodes.push(this.blankContent(this.writtenTo,this.pos)):this.nodes.push(new zr(0,-1,0));let i=t;for(let n of this.nodes)n instanceof zr&&n.updateHeight(this.oracle,i),i+=n?n.length:1;return this.nodes}static build(t,e,i,n){let r=new Lr(i,t);return _t.spans(e,i,n,r,0),r.finish(i)}}class qr{constructor(){this.changes=[]}compareRange(){}comparePoint(t,e,i,n){(t<e||i&&i.heightRelevant||n&&n.heightRelevant)&&Re(t,e,this.changes,5)}}function Vr(t,e){let i=t.getBoundingClientRect(),n=t.ownerDocument,r=n.defaultView||window,s=Math.max(0,i.left),o=Math.min(r.innerWidth,i.right),a=Math.max(0,i.top),l=Math.min(r.innerHeight,i.bottom);for(let h=t.parentNode;h&&h!=n.body;)if(1==h.nodeType){let e=h,i=window.getComputedStyle(e);if((e.scrollHeight>e.clientHeight||e.scrollWidth>e.clientWidth)&&\"visible\"!=i.overflow){let i=e.getBoundingClientRect();s=Math.max(s,i.left),o=Math.min(o,i.right),a=Math.max(a,i.top),l=Math.min(h==t.parentNode?r.innerHeight:l,i.bottom)}h=\"absolute\"==i.position||\"fixed\"==i.position?e.offsetParent:e.parentNode}else{if(11!=h.nodeType)break;h=h.host}return{left:s-i.left,right:Math.max(s,o)-i.left,top:a-(i.top+e),bottom:Math.max(a,l)-(i.top+e)}}function Wr(t,e){let i=t.getBoundingClientRect();return{left:0,right:i.right-i.left,top:e,bottom:i.bottom-(i.top+e)}}class Dr{constructor(t,e,i,n){this.from=t,this.to=e,this.size=i,this.displaySize=n}static same(t,e){if(t.length!=e.length)return!1;for(let i=0;i<t.length;i++){let n=t[i],r=e[i];if(n.from!=r.from||n.to!=r.to||n.size!=r.size)return!1}return!0}draw(t,e){return Ce.replace({widget:new Br(this.displaySize*(e?t.scaleY:t.scaleX),e)}).range(this.from,this.to)}}class Br extends Pe{constructor(t,e){super(),this.size=t,this.vertical=e}eq(t){return t.size==this.size&&t.vertical==this.vertical}toDOM(){let t=document.createElement(\"div\");return this.vertical?t.style.height=this.size+\"px\":(t.style.width=this.size+\"px\",t.style.height=\"2px\",t.style.display=\"inline-block\"),t}get estimatedHeight(){return this.vertical?this.size:-1}}class jr{constructor(t){this.state=t,this.pixelViewport={left:0,right:window.innerWidth,top:0,bottom:0},this.inView=!0,this.paddingTop=0,this.paddingBottom=0,this.contentDOMWidth=0,this.contentDOMHeight=0,this.editorHeight=0,this.editorWidth=0,this.scrollTop=0,this.scrolledToBottom=!1,this.scaleX=1,this.scaleY=1,this.scrollAnchorPos=0,this.scrollAnchorHeight=-1,this.scaler=Ur,this.scrollTarget=null,this.printing=!1,this.mustMeasureContent=!0,this.defaultTextDirection=ri.LTR,this.visibleRanges=[],this.mustEnforceCursorAssoc=!1;let e=t.facet(ji).some(t=>\"function\"!=typeof t&&\"cm-lineWrapping\"==t.class);this.heightOracle=new $r(e),this.stateDeco=Hr(t),this.heightMap=Xr.empty().applyChanges(this.stateDeco,f.empty,this.heightOracle.setDoc(t.doc),[new en(0,0,0,t.doc.length)]);for(let i=0;i<2&&(this.viewport=this.getViewport(0,null),this.updateForViewport());i++);this.updateViewportLines(),this.lineGaps=this.ensureLineGaps([]),this.lineGapDeco=Ce.set(this.lineGaps.map(t=>t.draw(this,!1))),this.computeVisibleRanges()}updateForViewport(){let t=[this.viewport],{main:e}=this.state.selection;for(let i=0;i<=1;i++){let n=i?e.head:e.anchor;if(!t.some(({from:t,to:e})=>n>=t&&n<=e)){let{from:e,to:i}=this.lineBlockAt(n);t.push(new Ir(e,i))}}return this.viewports=t.sort((t,e)=>t.from-e.from),this.updateScaler()}updateScaler(){let t=this.scaler;return this.scaler=this.heightMap.height<=7e6?Ur:new Fr(this.heightOracle,this.heightMap,this.viewports),t.eq(this.scaler)?0:2}updateViewportLines(){this.viewportLines=[],this.heightMap.forEachLine(this.viewport.from,this.viewport.to,this.heightOracle.setDoc(this.state.doc),0,0,t=>{this.viewportLines.push(Kr(t,this.scaler))})}update(t,e=null){this.state=t.state;let i=this.stateDeco;this.stateDeco=Hr(this.state);let n=t.changedRanges,r=en.extendWithRanges(n,function(t,e,i){let n=new qr;return _t.compare(t,e,i,n,0),n.changes}(i,this.stateDeco,t?t.changes:Z.empty(this.state.doc.length))),s=this.heightMap.height,o=this.scrolledToBottom?null:this.scrollAnchorAt(this.scrollTop);kr(),this.heightMap=this.heightMap.applyChanges(this.stateDeco,t.startState.doc,this.heightOracle.setDoc(this.state.doc),r),(this.heightMap.height!=s||yr)&&(t.flags|=2),o?(this.scrollAnchorPos=t.changes.mapPos(o.from,-1),this.scrollAnchorHeight=o.top):(this.scrollAnchorPos=-1,this.scrollAnchorHeight=s);let a=r.length?this.mapViewport(this.viewport,t.changes):this.viewport;(e&&(e.range.head<a.from||e.range.head>a.to)||!this.viewportIsAppropriate(a))&&(a=this.getViewport(0,e));let l=a.from!=this.viewport.from||a.to!=this.viewport.to;this.viewport=a,t.flags|=this.updateForViewport(),(l||!t.changes.empty||2&t.flags)&&this.updateViewportLines(),(this.lineGaps.length||this.viewport.to-this.viewport.from>4e3)&&this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps,t.changes))),t.flags|=this.computeVisibleRanges(t.changes),e&&(this.scrollTarget=e),!this.mustEnforceCursorAssoc&&(t.selectionSet||t.focusChanged)&&t.view.lineWrapping&&t.state.selection.main.empty&&t.state.selection.main.assoc&&!t.state.facet(Mi)&&(this.mustEnforceCursorAssoc=!0)}measure(t){let e=t.contentDOM,i=window.getComputedStyle(e),n=this.heightOracle,r=i.whiteSpace;this.defaultTextDirection=\"rtl\"==i.direction?ri.RTL:ri.LTR;let s=this.heightOracle.mustRefreshForWrapping(r),o=e.getBoundingClientRect(),a=s||this.mustMeasureContent||this.contentDOMHeight!=o.height;this.contentDOMHeight=o.height,this.mustMeasureContent=!1;let l=0,h=0;if(o.width&&o.height){let{scaleX:t,scaleY:i}=Ge(e,o);(t>.005&&Math.abs(this.scaleX-t)>.005||i>.005&&Math.abs(this.scaleY-i)>.005)&&(this.scaleX=t,this.scaleY=i,l|=16,s=a=!0)}let c=(parseInt(i.paddingTop)||0)*this.scaleY,u=(parseInt(i.paddingBottom)||0)*this.scaleY;this.paddingTop==c&&this.paddingBottom==u||(this.paddingTop=c,this.paddingBottom=u,l|=18),this.editorWidth!=t.scrollDOM.clientWidth&&(n.lineWrapping&&(a=!0),this.editorWidth=t.scrollDOM.clientWidth,l|=16);let d=t.scrollDOM.scrollTop*this.scaleY;this.scrollTop!=d&&(this.scrollAnchorHeight=-1,this.scrollTop=d),this.scrolledToBottom=ti(t.scrollDOM);let O=(this.printing?Wr:Vr)(e,this.paddingTop),p=O.top-this.pixelViewport.top,m=O.bottom-this.pixelViewport.bottom;this.pixelViewport=O;let g=this.pixelViewport.bottom>this.pixelViewport.top&&this.pixelViewport.right>this.pixelViewport.left;if(g!=this.inView&&(this.inView=g,g&&(a=!0)),!this.inView&&!this.scrollTarget&&!function(t){let e=t.getBoundingClientRect(),i=t.ownerDocument.defaultView||window;return e.left<i.innerWidth&&e.right>0&&e.top<i.innerHeight&&e.bottom>0}(t.dom))return 0;let Q=o.width;if(this.contentDOMWidth==Q&&this.editorHeight==t.scrollDOM.clientHeight||(this.contentDOMWidth=o.width,this.editorHeight=t.scrollDOM.clientHeight,l|=16),a){let e=t.docView.measureVisibleLineHeights(this.viewport);if(n.mustRefreshForHeights(e)&&(s=!0),s||n.lineWrapping&&Math.abs(Q-this.contentDOMWidth)>n.charWidth){let{lineHeight:i,charWidth:o,textHeight:a}=t.docView.measureTextSize();s=i>0&&n.refresh(r,i,o,a,Math.max(5,Q/o),e),s&&(t.docView.minWidth=0,l|=16)}p>0&&m>0?h=Math.max(p,m):p<0&&m<0&&(h=Math.min(p,m)),kr();for(let i of this.viewports){let r=i.from==this.viewport.from?e:t.docView.measureVisibleLineHeights(i);this.heightMap=(s?Xr.empty().applyChanges(this.stateDeco,f.empty,this.heightOracle,[new en(0,0,0,t.state.doc.length)]):this.heightMap).updateHeight(n,0,s,new Pr(i.from,r))}yr&&(l|=2)}let b=!this.viewportIsAppropriate(this.viewport,h)||this.scrollTarget&&(this.scrollTarget.range.head<this.viewport.from||this.scrollTarget.range.head>this.viewport.to);return b&&(2&l&&(l|=this.updateScaler()),this.viewport=this.getViewport(h,this.scrollTarget),l|=this.updateForViewport()),(2&l||b)&&this.updateViewportLines(),(this.lineGaps.length||this.viewport.to-this.viewport.from>4e3)&&this.updateLineGaps(this.ensureLineGaps(s?[]:this.lineGaps,t)),l|=this.computeVisibleRanges(),this.mustEnforceCursorAssoc&&(this.mustEnforceCursorAssoc=!1,t.docView.enforceCursorAssoc()),l}get visibleTop(){return this.scaler.fromDOM(this.pixelViewport.top)}get visibleBottom(){return this.scaler.fromDOM(this.pixelViewport.bottom)}getViewport(t,e){let i=.5-Math.max(-.5,Math.min(.5,t/1e3/2)),n=this.heightMap,r=this.heightOracle,{visibleTop:s,visibleBottom:o}=this,a=new Ir(n.lineAt(s-1e3*i,Cr.ByHeight,r,0,0).from,n.lineAt(o+1e3*(1-i),Cr.ByHeight,r,0,0).to);if(e){let{head:t}=e.range;if(t<a.from||t>a.to){let i,s=Math.min(this.editorHeight,this.pixelViewport.bottom-this.pixelViewport.top),o=n.lineAt(t,Cr.ByPos,r,0,0);i=\"center\"==e.y?(o.top+o.bottom)/2-s/2:\"start\"==e.y||\"nearest\"==e.y&&t<a.from?o.top:o.bottom-s,a=new Ir(n.lineAt(i-500,Cr.ByHeight,r,0,0).from,n.lineAt(i+s+500,Cr.ByHeight,r,0,0).to)}}return a}mapViewport(t,e){let i=e.mapPos(t.from,-1),n=e.mapPos(t.to,1);return new Ir(this.heightMap.lineAt(i,Cr.ByPos,this.heightOracle,0,0).from,this.heightMap.lineAt(n,Cr.ByPos,this.heightOracle,0,0).to)}viewportIsAppropriate({from:t,to:e},i=0){if(!this.inView)return!0;let{top:n}=this.heightMap.lineAt(t,Cr.ByPos,this.heightOracle,0,0),{bottom:r}=this.heightMap.lineAt(e,Cr.ByPos,this.heightOracle,0,0),{visibleTop:s,visibleBottom:o}=this;return(0==t||n<=s-Math.max(10,Math.min(-i,250)))&&(e==this.state.doc.length||r>=o+Math.max(10,Math.min(i,250)))&&n>s-2e3&&r<o+2e3}mapLineGaps(t,e){if(!t.length||e.empty)return t;let i=[];for(let n of t)e.touchesRange(n.from,n.to)||i.push(new Dr(e.mapPos(n.from),e.mapPos(n.to),n.size,n.displaySize));return i}ensureLineGaps(t,e){let i=this.heightOracle.lineWrapping,n=i?1e4:2e3,r=n>>1,s=n<<1;if(this.defaultTextDirection!=ri.LTR&&!i)return[];let o=[],a=(n,s,l,h)=>{if(s-n<r)return;let c=this.state.selection.main,u=[c.from];c.empty||u.push(c.to);for(let t of u)if(t>n&&t<s)return a(n,t-10,l,h),void a(t+10,s,l,h);let d=function(t,e){for(let i of t)if(e(i))return i;return}(t,t=>t.from>=l.from&&t.to<=l.to&&Math.abs(t.from-n)<r&&Math.abs(t.to-s)<r&&!u.some(e=>t.from<e&&t.to>e));if(!d){if(s<l.to&&e&&i&&e.visibleRanges.some(t=>t.from<=s&&t.to>=s)){let t=e.moveToLineBoundary(Y.cursor(s),!1,!0).head;t>n&&(s=t)}let t=this.gapSize(l,n,s,h);d=new Dr(n,s,t,i||t<2e6?t:2e6)}o.push(d)},l=e=>{if(e.length<s||e.type!=Te.Text)return;let r=function(t,e,i){let n=[],r=t,s=0;_t.spans(i,t,e,{span(){},point(t,e){t>r&&(n.push({from:r,to:t}),s+=t-r),r=e}},20),r<e&&(n.push({from:r,to:e}),s+=e-r);return{total:s,ranges:n}}(e.from,e.to,this.stateDeco);if(r.total<s)return;let o,l,h=this.scrollTarget?this.scrollTarget.range.head:null;if(i){let t,i,s=n/this.heightOracle.lineLength*this.heightOracle.lineHeight;if(null!=h){let n=Nr(r,h),o=((this.visibleBottom-this.visibleTop)/2+s)/e.height;t=n-o,i=n+o}else t=(this.visibleTop-e.top-s)/e.height,i=(this.visibleBottom-e.top+s)/e.height;o=Gr(r,t),l=Gr(r,i)}else{let i=r.total*this.heightOracle.charWidth,s=n*this.heightOracle.charWidth,a=0;if(i>2e6)for(let n of t)n.from>=e.from&&n.from<e.to&&n.size!=n.displaySize&&n.from*this.heightOracle.charWidth+a<this.pixelViewport.left&&(a=n.size-n.displaySize);let c,u,d=this.pixelViewport.left+a,f=this.pixelViewport.right+a;if(null!=h){let t=Nr(r,h),e=((f-d)/2+s)/i;c=t-e,u=t+e}else c=(d-s)/i,u=(f+s)/i;o=Gr(r,c),l=Gr(r,u)}o>e.from&&a(e.from,o,e,r),l<e.to&&a(l,e.to,e,r)};for(let h of this.viewportLines)Array.isArray(h.type)?h.type.forEach(l):l(h);return o}gapSize(t,e,i,n){let r=Nr(n,i)-Nr(n,e);return this.heightOracle.lineWrapping?t.height*r:n.total*this.heightOracle.charWidth*r}updateLineGaps(t){Dr.same(t,this.lineGaps)||(this.lineGaps=t,this.lineGapDeco=Ce.set(t.map(t=>t.draw(this,this.heightOracle.lineWrapping))))}computeVisibleRanges(t){let e=this.stateDeco;this.lineGaps.length&&(e=e.concat(this.lineGapDeco));let i=[];_t.spans(e,this.viewport.from,this.viewport.to,{span(t,e){i.push({from:t,to:e})},point(){}},20);let n=0;if(i.length!=this.visibleRanges.length)n=12;else for(let r=0;r<i.length&&!(8&n);r++){let e=this.visibleRanges[r],s=i[r];e.from==s.from&&e.to==s.to||(n|=4,t&&t.mapPos(e.from,-1)==s.from&&t.mapPos(e.to,1)==s.to||(n|=8))}return this.visibleRanges=i,n}lineBlockAt(t){return t>=this.viewport.from&&t<=this.viewport.to&&this.viewportLines.find(e=>e.from<=t&&e.to>=t)||Kr(this.heightMap.lineAt(t,Cr.ByPos,this.heightOracle,0,0),this.scaler)}lineBlockAtHeight(t){return t>=this.viewportLines[0].top&&t<=this.viewportLines[this.viewportLines.length-1].bottom&&this.viewportLines.find(e=>e.top<=t&&e.bottom>=t)||Kr(this.heightMap.lineAt(this.scaler.fromDOM(t),Cr.ByHeight,this.heightOracle,0,0),this.scaler)}scrollAnchorAt(t){let e=this.lineBlockAtHeight(t+8);return e.from>=this.viewport.from||this.viewportLines[0].top-t>200?e:this.viewportLines[0]}elementAtHeight(t){return Kr(this.heightMap.blockAt(this.scaler.fromDOM(t),this.heightOracle,0,0),this.scaler)}get docHeight(){return this.scaler.toDOM(this.heightMap.height)}get contentHeight(){return this.docHeight+this.paddingTop+this.paddingBottom}}class Ir{constructor(t,e){this.from=t,this.to=e}}function Gr({total:t,ranges:e},i){if(i<=0)return e[0].from;if(i>=1)return e[e.length-1].to;let n=Math.floor(t*i);for(let r=0;;r++){let{from:t,to:i}=e[r],s=i-t;if(n<=s)return t+n;n-=s}}function Nr(t,e){let i=0;for(let{from:n,to:r}of t.ranges){if(e<=r){i+=e-n;break}i+=r-n}return i/t.total}const Ur={toDOM:t=>t,fromDOM:t=>t,scale:1,eq(t){return t==this}};function Hr(t){let e=t.facet(Ii).filter(t=>\"function\"!=typeof t),i=t.facet(Ni).filter(t=>\"function\"!=typeof t);return i.length&&e.push(_t.join(i)),e}class Fr{constructor(t,e,i){let n=0,r=0,s=0;this.viewports=i.map(({from:i,to:r})=>{let s=e.lineAt(i,Cr.ByPos,t,0,0).top,o=e.lineAt(r,Cr.ByPos,t,0,0).bottom;return n+=o-s,{from:i,to:r,top:s,bottom:o,domTop:0,domBottom:0}}),this.scale=(7e6-n)/(e.height-n);for(let o of this.viewports)o.domTop=s+(o.top-r)*this.scale,s=o.domBottom=o.domTop+(o.bottom-o.top),r=o.bottom}toDOM(t){for(let e=0,i=0,n=0;;e++){let r=e<this.viewports.length?this.viewports[e]:null;if(!r||t<r.top)return n+(t-i)*this.scale;if(t<=r.bottom)return r.domTop+(t-r.top);i=r.bottom,n=r.domBottom}}fromDOM(t){for(let e=0,i=0,n=0;;e++){let r=e<this.viewports.length?this.viewports[e]:null;if(!r||t<r.domTop)return i+(t-n)/this.scale;if(t<=r.domBottom)return r.top+(t-r.domTop);i=r.bottom,n=r.domBottom}}eq(t){return t instanceof Fr&&(this.scale==t.scale&&this.viewports.length==t.viewports.length&&this.viewports.every((e,i)=>e.from==t.viewports[i].from&&e.to==t.viewports[i].to))}}function Kr(t,e){if(1==e.scale)return t;let i=e.toDOM(t.top),n=e.toDOM(t.bottom);return new Tr(t.from,t.length,i,n-i,Array.isArray(t._content)?t._content.map(t=>Kr(t,e)):t._content)}const Jr=V.define({combine:t=>t.join(\" \")}),ts=V.define({combine:t=>t.indexOf(!0)>-1}),es=Jt.newName(),is=Jt.newName(),ns=Jt.newName(),rs={\"&light\":\".\"+is,\"&dark\":\".\"+ns};function ss(t,e,i){return new Jt(e,{finish:e=>/&/.test(e)?e.replace(/&\\w*/,e=>{if(\"&\"==e)return t;if(!i||!i[e])throw new RangeError(`Unsupported selector: ${e}`);return i[e]}):t+\" \"+e})}const os=ss(\".\"+es,{\"&\":{position:\"relative !important\",boxSizing:\"border-box\",\"&.cm-focused\":{outline:\"1px dotted #212121\"},display:\"flex !important\",flexDirection:\"column\"},\".cm-scroller\":{display:\"flex !important\",alignItems:\"flex-start !important\",fontFamily:\"monospace\",lineHeight:1.4,height:\"100%\",overflowX:\"auto\",position:\"relative\",zIndex:0,overflowAnchor:\"none\"},\".cm-content\":{margin:0,flexGrow:2,flexShrink:0,display:\"block\",whiteSpace:\"pre\",wordWrap:\"normal\",boxSizing:\"border-box\",minHeight:\"100%\",padding:\"4px 0\",outline:\"none\",\"&[contenteditable=true]\":{WebkitUserModify:\"read-write-plaintext-only\"}},\".cm-lineWrapping\":{whiteSpace_fallback:\"pre-wrap\",whiteSpace:\"break-spaces\",wordBreak:\"break-word\",overflowWrap:\"anywhere\",flexShrink:1},\"&light .cm-content\":{caretColor:\"black\"},\"&dark .cm-content\":{caretColor:\"white\"},\".cm-line\":{display:\"block\",padding:\"0 2px 0 6px\"},\".cm-layer\":{position:\"absolute\",left:0,top:0,contain:\"size style\",\"& > *\":{position:\"absolute\"}},\"&light .cm-selectionBackground\":{background:\"#d9d9d9\"},\"&dark .cm-selectionBackground\":{background:\"#222\"},\"&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground\":{background:\"#d7d4f0\"},\"&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground\":{background:\"#233\"},\".cm-cursorLayer\":{pointerEvents:\"none\"},\"&.cm-focused > .cm-scroller > .cm-cursorLayer\":{animation:\"steps(1) cm-blink 1.2s infinite\"},\"@keyframes cm-blink\":{\"0%\":{},\"50%\":{opacity:0},\"100%\":{}},\"@keyframes cm-blink2\":{\"0%\":{},\"50%\":{opacity:0},\"100%\":{}},\".cm-cursor, .cm-dropCursor\":{borderLeft:\"1.2px solid black\",marginLeft:\"-0.6px\",pointerEvents:\"none\"},\".cm-cursor\":{display:\"none\"},\"&dark .cm-cursor\":{borderLeftColor:\"#ddd\"},\".cm-dropCursor\":{position:\"absolute\"},\"&.cm-focused > .cm-scroller > .cm-cursorLayer .cm-cursor\":{display:\"block\"},\".cm-iso\":{unicodeBidi:\"isolate\"},\".cm-announced\":{position:\"fixed\",top:\"-10000px\"},\"@media print\":{\".cm-announced\":{display:\"none\"}},\"&light .cm-activeLine\":{backgroundColor:\"#cceeff44\"},\"&dark .cm-activeLine\":{backgroundColor:\"#99eeff33\"},\"&light .cm-specialChar\":{color:\"red\"},\"&dark .cm-specialChar\":{color:\"#f78\"},\".cm-gutters\":{flexShrink:0,display:\"flex\",height:\"100%\",boxSizing:\"border-box\",zIndex:200},\".cm-gutters-before\":{insetInlineStart:0},\".cm-gutters-after\":{insetInlineEnd:0},\"&light .cm-gutters\":{backgroundColor:\"#f5f5f5\",color:\"#6c6c6c\",border:\"0px solid #ddd\",\"&.cm-gutters-before\":{borderRightWidth:\"1px\"},\"&.cm-gutters-after\":{borderLeftWidth:\"1px\"}},\"&dark .cm-gutters\":{backgroundColor:\"#333338\",color:\"#ccc\"},\".cm-gutter\":{display:\"flex !important\",flexDirection:\"column\",flexShrink:0,boxSizing:\"border-box\",minHeight:\"100%\",overflow:\"hidden\"},\".cm-gutterElement\":{boxSizing:\"border-box\"},\".cm-lineNumbers .cm-gutterElement\":{padding:\"0 3px 0 5px\",minWidth:\"20px\",textAlign:\"right\",whiteSpace:\"nowrap\"},\"&light .cm-activeLineGutter\":{backgroundColor:\"#e2f2ff\"},\"&dark .cm-activeLineGutter\":{backgroundColor:\"#222227\"},\".cm-panels\":{boxSizing:\"border-box\",position:\"sticky\",left:0,right:0,zIndex:300},\"&light .cm-panels\":{backgroundColor:\"#f5f5f5\",color:\"black\"},\"&light .cm-panels-top\":{borderBottom:\"1px solid #ddd\"},\"&light .cm-panels-bottom\":{borderTop:\"1px solid #ddd\"},\"&dark .cm-panels\":{backgroundColor:\"#333338\",color:\"white\"},\".cm-dialog\":{padding:\"2px 19px 4px 6px\",position:\"relative\",\"& label\":{fontSize:\"80%\"}},\".cm-dialog-close\":{position:\"absolute\",top:\"3px\",right:\"4px\",backgroundColor:\"inherit\",border:\"none\",font:\"inherit\",fontSize:\"14px\",padding:\"0\"},\".cm-tab\":{display:\"inline-block\",overflow:\"hidden\",verticalAlign:\"bottom\"},\".cm-widgetBuffer\":{verticalAlign:\"text-top\",height:\"1em\",width:0,display:\"inline\"},\".cm-placeholder\":{color:\"#888\",display:\"inline-block\",verticalAlign:\"top\",userSelect:\"none\"},\".cm-highlightSpace\":{backgroundImage:\"radial-gradient(circle at 50% 55%, #aaa 20%, transparent 5%)\",backgroundPosition:\"center\"},\".cm-highlightTab\":{backgroundImage:'url(\\'data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"200\" height=\"20\"><path stroke=\"%23888\" stroke-width=\"1\" fill=\"none\" d=\"M1 10H196L190 5M190 15L196 10M197 4L197 16\"/></svg>\\')',backgroundSize:\"auto 100%\",backgroundPosition:\"right 90%\",backgroundRepeat:\"no-repeat\"},\".cm-trailingSpace\":{backgroundColor:\"#ff332255\"},\".cm-button\":{verticalAlign:\"middle\",color:\"inherit\",fontSize:\"70%\",padding:\".2em 1em\",borderRadius:\"1px\"},\"&light .cm-button\":{backgroundImage:\"linear-gradient(#eff1f5, #d9d9df)\",border:\"1px solid #888\",\"&:active\":{backgroundImage:\"linear-gradient(#b4b4b4, #d0d3d6)\"}},\"&dark .cm-button\":{backgroundImage:\"linear-gradient(#393939, #111)\",border:\"1px solid #888\",\"&:active\":{backgroundImage:\"linear-gradient(#111, #333)\"}},\".cm-textfield\":{verticalAlign:\"middle\",color:\"inherit\",fontSize:\"70%\",border:\"1px solid silver\",padding:\".2em .5em\"},\"&light .cm-textfield\":{backgroundColor:\"white\"},\"&dark .cm-textfield\":{border:\"1px solid #555\",backgroundColor:\"inherit\"}},rs),as={childList:!0,characterData:!0,subtree:!0,attributes:!0,characterDataOldValue:!0},ls=we.ie&&we.ie_version<=11;class hs{constructor(t){this.view=t,this.active=!1,this.editContext=null,this.selectionRange=new Ne,this.selectionChanged=!1,this.delayedFlush=-1,this.resizeTimeout=-1,this.queue=[],this.delayedAndroidKey=null,this.flushingAndroidKey=-1,this.lastChange=0,this.scrollTargets=[],this.intersection=null,this.resizeScroll=null,this.intersecting=!1,this.gapIntersection=null,this.gaps=[],this.printQuery=null,this.parentCheck=-1,this.dom=t.contentDOM,this.observer=new MutationObserver(e=>{for(let t of e)this.queue.push(t);(we.ie&&we.ie_version<=11||we.ios&&t.composing)&&e.some(t=>\"childList\"==t.type&&t.removedNodes.length||\"characterData\"==t.type&&t.oldValue.length>t.target.nodeValue.length)?this.flushSoon():this.flush()}),!window.EditContext||!we.android||!1===t.constructor.EDIT_CONTEXT||we.chrome&&we.chrome_version<126||(this.editContext=new ds(t),t.state.facet(Li)&&(t.contentDOM.editContext=this.editContext.editContext)),ls&&(this.onCharData=t=>{this.queue.push({target:t.target,type:\"characterData\",oldValue:t.prevValue}),this.flushSoon()}),this.onSelectionChange=this.onSelectionChange.bind(this),this.onResize=this.onResize.bind(this),this.onPrint=this.onPrint.bind(this),this.onScroll=this.onScroll.bind(this),window.matchMedia&&(this.printQuery=window.matchMedia(\"print\")),\"function\"==typeof ResizeObserver&&(this.resizeScroll=new ResizeObserver(()=>{var t;(null===(t=this.view.docView)||void 0===t?void 0:t.lastUpdate)<Date.now()-75&&this.onResize()}),this.resizeScroll.observe(t.scrollDOM)),this.addWindowListeners(this.win=t.win),this.start(),\"function\"==typeof IntersectionObserver&&(this.intersection=new IntersectionObserver(t=>{this.parentCheck<0&&(this.parentCheck=setTimeout(this.listenForScroll.bind(this),1e3)),t.length>0&&t[t.length-1].intersectionRatio>0!=this.intersecting&&(this.intersecting=!this.intersecting,this.intersecting!=this.view.inView&&this.onScrollChanged(document.createEvent(\"Event\")))},{threshold:[0,.001]}),this.intersection.observe(this.dom),this.gapIntersection=new IntersectionObserver(t=>{t.length>0&&t[t.length-1].intersectionRatio>0&&this.onScrollChanged(document.createEvent(\"Event\"))},{})),this.listenForScroll(),this.readSelectionRange()}onScrollChanged(t){this.view.inputState.runHandlers(\"scroll\",t),this.intersecting&&this.view.measure()}onScroll(t){this.intersecting&&this.flush(!1),this.editContext&&this.view.requestMeasure(this.editContext.measureReq),this.onScrollChanged(t)}onResize(){this.resizeTimeout<0&&(this.resizeTimeout=setTimeout(()=>{this.resizeTimeout=-1,this.view.requestMeasure()},50))}onPrint(t){(\"change\"!=t.type&&t.type||t.matches)&&(this.view.viewState.printing=!0,this.view.measure(),setTimeout(()=>{this.view.viewState.printing=!1,this.view.requestMeasure()},500))}updateGaps(t){if(this.gapIntersection&&(t.length!=this.gaps.length||this.gaps.some((e,i)=>e!=t[i]))){this.gapIntersection.disconnect();for(let e of t)this.gapIntersection.observe(e);this.gaps=t}}onSelectionChange(t){let e=this.selectionChanged;if(!this.readSelectionRange()||this.delayedAndroidKey)return;let{view:i}=this,n=this.selectionRange;if(i.state.facet(Li)?i.root.activeElement!=this.dom:!Ye(this.dom,n))return;let r=n.anchorNode&&i.docView.tile.nearest(n.anchorNode);r&&r.isWidget()&&r.widget.ignoreEvent(t)?e||(this.selectionChanged=!1):(we.ie&&we.ie_version<=11||we.android&&we.chrome)&&!i.state.selection.main.empty&&n.focusNode&&qe(n.focusNode,n.focusOffset,n.anchorNode,n.anchorOffset)?this.flushSoon():this.flush(!1)}readSelectionRange(){let{view:t}=this,e=_e(t.root);if(!e)return!1;let i=we.safari&&11==t.root.nodeType&&t.root.activeElement==this.dom&&function(t,e){if(e.getComposedRanges){let i=e.getComposedRanges(t.root)[0];if(i)return us(t,i)}let i=null;function n(t){t.preventDefault(),t.stopImmediatePropagation(),i=t.getTargetRanges()[0]}return t.contentDOM.addEventListener(\"beforeinput\",n,!0),t.dom.ownerDocument.execCommand(\"indent\"),t.contentDOM.removeEventListener(\"beforeinput\",n,!0),i?us(t,i):null}(this.view,e)||e;if(!i||this.selectionRange.eq(i))return!1;let n=Ye(this.dom,i);return n&&!this.selectionChanged&&t.inputState.lastFocusTime>Date.now()-200&&t.inputState.lastTouchTime<Date.now()-300&&function(t,e){let i=e.focusNode,n=e.focusOffset;if(!i||e.anchorNode!=i||e.anchorOffset!=n)return!1;for(n=Math.min(n,Be(i));;)if(n){if(1!=i.nodeType)return!1;let t=i.childNodes[n-1];\"false\"==t.contentEditable?n--:(i=t,n=Be(i))}else{if(i==t)return!0;n=Ve(i),i=i.parentNode}}(this.dom,i)?(this.view.inputState.lastFocusTime=0,t.docView.updateSelection(),!1):(this.selectionRange.setRange(i),n&&(this.selectionChanged=!0),!0)}setSelectionRange(t,e){this.selectionRange.set(t.node,t.offset,e.node,e.offset),this.selectionChanged=!1}clearSelectionRange(){this.selectionRange.set(null,0,null,0)}listenForScroll(){this.parentCheck=-1;let t=0,e=null;for(let i=this.dom;i;)if(1==i.nodeType)!e&&t<this.scrollTargets.length&&this.scrollTargets[t]==i?t++:e||(e=this.scrollTargets.slice(0,t)),e&&e.push(i),i=i.assignedSlot||i.parentNode;else{if(11!=i.nodeType)break;i=i.host}if(t<this.scrollTargets.length&&!e&&(e=this.scrollTargets.slice(0,t)),e){for(let t of this.scrollTargets)t.removeEventListener(\"scroll\",this.onScroll);for(let t of this.scrollTargets=e)t.addEventListener(\"scroll\",this.onScroll)}}ignore(t){if(!this.active)return t();try{return this.stop(),t()}finally{this.start(),this.clear()}}start(){this.active||(this.observer.observe(this.dom,as),ls&&this.dom.addEventListener(\"DOMCharacterDataModified\",this.onCharData),this.active=!0)}stop(){this.active&&(this.active=!1,this.observer.disconnect(),ls&&this.dom.removeEventListener(\"DOMCharacterDataModified\",this.onCharData))}clear(){this.processRecords(),this.queue.length=0,this.selectionChanged=!1}delayAndroidKey(t,e){var i;if(!this.delayedAndroidKey){let t=()=>{let t=this.delayedAndroidKey;if(t){this.clearDelayedAndroidKey(),this.view.inputState.lastKeyCode=t.keyCode,this.view.inputState.lastKeyTime=Date.now(),!this.flush()&&t.force&&Je(this.dom,t.key,t.keyCode)}};this.flushingAndroidKey=this.view.win.requestAnimationFrame(t)}this.delayedAndroidKey&&\"Enter\"!=t||(this.delayedAndroidKey={key:t,keyCode:e,force:this.lastChange<Date.now()-50||!!(null===(i=this.delayedAndroidKey)||void 0===i?void 0:i.force)})}clearDelayedAndroidKey(){this.win.cancelAnimationFrame(this.flushingAndroidKey),this.delayedAndroidKey=null,this.flushingAndroidKey=-1}flushSoon(){this.delayedFlush<0&&(this.delayedFlush=this.view.win.requestAnimationFrame(()=>{this.delayedFlush=-1,this.flush()}))}forceFlush(){this.delayedFlush>=0&&(this.view.win.cancelAnimationFrame(this.delayedFlush),this.delayedFlush=-1),this.flush()}pendingRecords(){for(let t of this.observer.takeRecords())this.queue.push(t);return this.queue}processRecords(){let t=this.pendingRecords();t.length&&(this.queue=[]);let e=-1,i=-1,n=!1;for(let r of t){let t=this.readMutation(r);t&&(t.typeOver&&(n=!0),-1==e?({from:e,to:i}=t):(e=Math.min(t.from,e),i=Math.max(t.to,i)))}return{from:e,to:i,typeOver:n}}readChange(){let{from:t,to:e,typeOver:i}=this.processRecords(),n=this.selectionChanged&&Ye(this.dom,this.selectionRange);if(t<0&&!n)return null;t>-1&&(this.lastChange=Date.now()),this.view.inputState.lastFocusTime=0,this.selectionChanged=!1;let r=new Nn(this.view,t,e,i);return this.view.docView.domChanged={newSel:r.newSel?r.newSel.main:null},r}flush(t=!0){if(this.delayedFlush>=0||this.delayedAndroidKey)return!1;t&&this.readSelectionRange();let e=this.readChange();if(!e)return this.view.requestMeasure(),!1;let i=this.view.state,n=Hn(this.view,e);return this.view.state==i&&(e.domChanged||e.newSel&&!e.newSel.main.eq(this.view.state.selection.main))&&this.view.update([]),n}readMutation(t){let e=this.view.docView.tile.nearest(t.target);if(!e||e.isWidget())return null;if(e.markDirty(\"attributes\"==t.type),\"childList\"==t.type){let i=cs(e,t.previousSibling||t.target.previousSibling,-1),n=cs(e,t.nextSibling||t.target.nextSibling,1);return{from:i?e.posAfter(i):e.posAtStart,to:n?e.posBefore(n):e.posAtEnd,typeOver:!1}}return\"characterData\"==t.type?{from:e.posAtStart,to:e.posAtEnd,typeOver:t.target.nodeValue==t.oldValue}:null}setWindow(t){t!=this.win&&(this.removeWindowListeners(this.win),this.win=t,this.addWindowListeners(this.win))}addWindowListeners(t){t.addEventListener(\"resize\",this.onResize),this.printQuery?this.printQuery.addEventListener?this.printQuery.addEventListener(\"change\",this.onPrint):this.printQuery.addListener(this.onPrint):t.addEventListener(\"beforeprint\",this.onPrint),t.addEventListener(\"scroll\",this.onScroll),t.document.addEventListener(\"selectionchange\",this.onSelectionChange)}removeWindowListeners(t){t.removeEventListener(\"scroll\",this.onScroll),t.removeEventListener(\"resize\",this.onResize),this.printQuery?this.printQuery.removeEventListener?this.printQuery.removeEventListener(\"change\",this.onPrint):this.printQuery.removeListener(this.onPrint):t.removeEventListener(\"beforeprint\",this.onPrint),t.document.removeEventListener(\"selectionchange\",this.onSelectionChange)}update(t){this.editContext&&(this.editContext.update(t),t.startState.facet(Li)!=t.state.facet(Li)&&(t.view.contentDOM.editContext=t.state.facet(Li)?this.editContext.editContext:null))}destroy(){var t,e,i;this.stop(),null===(t=this.intersection)||void 0===t||t.disconnect(),null===(e=this.gapIntersection)||void 0===e||e.disconnect(),null===(i=this.resizeScroll)||void 0===i||i.disconnect();for(let n of this.scrollTargets)n.removeEventListener(\"scroll\",this.onScroll);this.removeWindowListeners(this.win),clearTimeout(this.parentCheck),clearTimeout(this.resizeTimeout),this.win.cancelAnimationFrame(this.delayedFlush),this.win.cancelAnimationFrame(this.flushingAndroidKey),this.editContext&&(this.view.contentDOM.editContext=null,this.editContext.destroy())}}function cs(t,e,i){for(;e;){let n=sn.get(e);if(n&&n.parent==t)return n;let r=e.parentNode;e=r!=t.dom?r:i>0?e.nextSibling:e.previousSibling}return null}function us(t,e){let i=e.startContainer,n=e.startOffset,r=e.endContainer,s=e.endOffset,o=t.docView.domAtPos(t.state.selection.main.anchor,1);return qe(o.node,o.offset,r,s)&&([i,n,r,s]=[r,s,i,n]),{anchorNode:i,anchorOffset:n,focusNode:r,focusOffset:s}}class ds{constructor(t){this.from=0,this.to=0,this.pendingContextChange=null,this.handlers=Object.create(null),this.composing=null,this.resetRange(t.state);let e=this.editContext=new window.EditContext({text:t.state.doc.sliceString(this.from,this.to),selectionStart:this.toContextPos(Math.max(this.from,Math.min(this.to,t.state.selection.main.anchor))),selectionEnd:this.toContextPos(t.state.selection.main.head)});this.handlers.textupdate=i=>{let n=t.state.selection.main,{anchor:r,head:s}=n,o=this.toEditorPos(i.updateRangeStart),a=this.toEditorPos(i.updateRangeEnd);t.inputState.composing>=0&&!this.composing&&(this.composing={contextBase:i.updateRangeStart,editorBase:o,drifted:!1});let l=a-o>i.text.length;o==this.from&&r<this.from?o=r:a==this.to&&r>this.to&&(a=r);let h=Kn(t.state.sliceDoc(o,a),i.text,(l?n.from:n.to)-o,l?\"end\":null);if(!h){let e=Y.single(this.toEditorPos(i.selectionStart),this.toEditorPos(i.selectionEnd));return void(e.main.eq(n)||t.dispatch({selection:e,userEvent:\"select\"}))}let c={from:h.from+o,to:h.toA+o,insert:f.of(i.text.slice(h.from,h.toB).split(\"\\n\"))};if((we.mac||we.android)&&c.from==s-1&&/^\\. ?$/.test(i.text)&&\"off\"==t.contentDOM.getAttribute(\"autocorrect\")&&(c={from:o,to:a,insert:f.of([i.text.replace(\".\",\" \")])}),this.pendingContextChange=c,!t.state.readOnly){let e=this.to-this.from+(c.to-c.from+c.insert.length);Fn(t,c,Y.single(this.toEditorPos(i.selectionStart,e),this.toEditorPos(i.selectionEnd,e)))}this.pendingContextChange&&(this.revertPending(t.state),this.setSelection(t.state)),c.from<c.to&&!c.insert.length&&t.inputState.composing>=0&&!/[\\\\p{Alphabetic}\\\\p{Number}_]/.test(e.text.slice(Math.max(0,i.updateRangeStart-1),Math.min(e.text.length,i.updateRangeStart+1)))&&this.handlers.compositionend(i)},this.handlers.characterboundsupdate=i=>{let n=[],r=null;for(let e=this.toEditorPos(i.rangeStart),s=this.toEditorPos(i.rangeEnd);e<s;e++){let i=t.coordsForChar(e);r=i&&new DOMRect(i.left,i.top,i.right-i.left,i.bottom-i.top)||r||new DOMRect,n.push(r)}e.updateCharacterBounds(i.rangeStart,n)},this.handlers.textformatupdate=e=>{let i=[];for(let t of e.getTextFormats()){let e=t.underlineStyle,n=t.underlineThickness;if(!/none/i.test(e)&&!/none/i.test(n)){let r=this.toEditorPos(t.rangeStart),s=this.toEditorPos(t.rangeEnd);if(r<s){let t=`text-decoration: underline ${/^[a-z]/.test(e)?e+\" \":\"Dashed\"==e?\"dashed \":\"Squiggle\"==e?\"wavy \":\"\"}${/thin/i.test(n)?1:2}px`;i.push(Ce.mark({attributes:{style:t}}).range(r,s))}}}t.dispatch({effects:Ei.of(Ce.set(i))})},this.handlers.compositionstart=()=>{t.inputState.composing<0&&(t.inputState.composing=0,t.inputState.compositionFirstChange=!0)},this.handlers.compositionend=()=>{if(t.inputState.composing=-1,t.inputState.compositionFirstChange=null,this.composing){let{drifted:e}=this.composing;this.composing=null,e&&this.reset(t.state)}};for(let i in this.handlers)e.addEventListener(i,this.handlers[i]);this.measureReq={read:t=>{this.editContext.updateControlBounds(t.contentDOM.getBoundingClientRect());let e=_e(t.root);e&&e.rangeCount&&this.editContext.updateSelectionBounds(e.getRangeAt(0).getBoundingClientRect())}}}applyEdits(t){let e=0,i=!1,n=this.pendingContextChange;return t.changes.iterChanges((r,s,o,a,l)=>{if(i)return;let h=l.length-(s-r);if(n&&s>=n.to){if(n.from==r&&n.to==s&&n.insert.eq(l))return n=this.pendingContextChange=null,e+=h,void(this.to+=h);n=null,this.revertPending(t.state)}if(r+=e,(s+=e)<=this.from)this.from+=h,this.to+=h;else if(r<this.to){if(r<this.from||s>this.to||this.to-this.from+l.length>3e4)return void(i=!0);this.editContext.updateText(this.toContextPos(r),this.toContextPos(s),l.toString()),this.to+=h}e+=h}),n&&!i&&this.revertPending(t.state),!i}update(t){let e=this.pendingContextChange,i=t.startState.selection.main;this.composing&&(this.composing.drifted||!t.changes.touchesRange(i.from,i.to)&&t.transactions.some(t=>!t.isUserEvent(\"input.type\")&&t.changes.touchesRange(this.from,this.to)))?(this.composing.drifted=!0,this.composing.editorBase=t.changes.mapPos(this.composing.editorBase)):this.applyEdits(t)&&this.rangeIsValid(t.state)?(t.docChanged||t.selectionSet||e)&&this.setSelection(t.state):(this.pendingContextChange=null,this.reset(t.state)),(t.geometryChanged||t.docChanged||t.selectionSet)&&t.view.requestMeasure(this.measureReq)}resetRange(t){let{head:e}=t.selection.main;this.from=Math.max(0,e-1e4),this.to=Math.min(t.doc.length,e+1e4)}reset(t){this.resetRange(t),this.editContext.updateText(0,this.editContext.text.length,t.doc.sliceString(this.from,this.to)),this.setSelection(t)}revertPending(t){let e=this.pendingContextChange;this.pendingContextChange=null,this.editContext.updateText(this.toContextPos(e.from),this.toContextPos(e.from+e.insert.length),t.doc.sliceString(e.from,e.to))}setSelection(t){let{main:e}=t.selection,i=this.toContextPos(Math.max(this.from,Math.min(this.to,e.anchor))),n=this.toContextPos(e.head);this.editContext.selectionStart==i&&this.editContext.selectionEnd==n||this.editContext.updateSelection(i,n)}rangeIsValid(t){let{head:e}=t.selection.main;return!(this.from>0&&e-this.from<500||this.to<t.doc.length&&this.to-e<500||this.to-this.from>3e4)}toEditorPos(t,e=this.to-this.from){t=Math.min(t,e);let i=this.composing;return i&&i.drifted?i.editorBase+(t-i.contextBase):t+this.from}toContextPos(t){let e=this.composing;return e&&e.drifted?e.contextBase+(t-e.editorBase):t-this.from}destroy(){for(let t in this.handlers)this.editContext.removeEventListener(t,this.handlers[t])}}class fs{get state(){return this.viewState.state}get viewport(){return this.viewState.viewport}get visibleRanges(){return this.viewState.visibleRanges}get inView(){return this.viewState.inView}get composing(){return!!this.inputState&&this.inputState.composing>0}get compositionStarted(){return!!this.inputState&&this.inputState.composing>=0}get root(){return this._root}get win(){return this.dom.ownerDocument.defaultView||window}constructor(t={}){var e;this.plugins=[],this.pluginMap=new Map,this.editorAttrs={},this.contentAttrs={},this.bidiCache=[],this.destroyed=!1,this.updateState=2,this.measureScheduled=-1,this.measureRequests=[],this.contentDOM=document.createElement(\"div\"),this.scrollDOM=document.createElement(\"div\"),this.scrollDOM.tabIndex=-1,this.scrollDOM.className=\"cm-scroller\",this.scrollDOM.appendChild(this.contentDOM),this.announceDOM=document.createElement(\"div\"),this.announceDOM.className=\"cm-announced\",this.announceDOM.setAttribute(\"aria-live\",\"polite\"),this.dom=document.createElement(\"div\"),this.dom.appendChild(this.announceDOM),this.dom.appendChild(this.scrollDOM),t.parent&&t.parent.appendChild(this.dom);let{dispatch:i}=t;this.dispatchTransactions=t.dispatchTransactions||i&&(t=>t.forEach(t=>i(t,this)))||(t=>this.update(t)),this.dispatch=this.dispatch.bind(this),this._root=t.root||function(t){for(;t;){if(t&&(9==t.nodeType||11==t.nodeType&&t.host))return t;t=t.assignedSlot||t.parentNode}return null}(t.parent)||document,this.viewState=new jr(t.state||Ct.create(t)),t.scrollTo&&t.scrollTo.is(_i)&&(this.viewState.scrollTarget=t.scrollTo.value.clip(this.viewState.state)),this.plugins=this.state.facet(Vi).map(t=>new Di(t));for(let n of this.plugins)n.update(this);this.observer=new hs(this),this.inputState=new Jn(this),this.inputState.ensureHandlers(this.plugins),this.docView=new Tn(this),this.mountStyles(),this.updateAttrs(),this.updateState=0,this.requestMeasure(),(null===(e=document.fonts)||void 0===e?void 0:e.ready)&&document.fonts.ready.then(()=>this.requestMeasure())}dispatch(...t){let e=1==t.length&&t[0]instanceof Qt?t:1==t.length&&Array.isArray(t[0])?t[0]:[this.state.update(...t)];this.dispatchTransactions(e,this)}update(t){if(0!=this.updateState)throw new Error(\"Calls to EditorView.update are not allowed while an update is in progress\");let e,i=!1,n=!1,r=this.state;for(let d of t){if(d.startState!=r)throw new RangeError(\"Trying to update state with a transaction that doesn't start from the previous state.\");r=d.state}if(this.destroyed)return void(this.viewState.state=r);let s=this.hasFocus,o=0,a=null;t.some(t=>t.annotation(br))?(this.inputState.notifiedFocused=s,o=1):s!=this.inputState.notifiedFocused&&(this.inputState.notifiedFocused=s,a=vr(r,s),a||(o=1));let l=this.observer.delayedAndroidKey,h=null;if(l?(this.observer.clearDelayedAndroidKey(),h=this.observer.readChange(),(h&&!this.state.doc.eq(r.doc)||!this.state.selection.eq(r.selection))&&(h=null)):this.observer.clear(),r.facet(Ct.phrases)!=this.state.facet(Ct.phrases))return this.setState(r);e=nn.create(this,r,t),e.flags|=o;let c=this.viewState.scrollTarget;try{this.updateState=2;for(let e of t){if(c&&(c=c.map(e.changes)),e.scrollIntoView){let{main:t}=e.state.selection;c=new zi(t.empty?t:Y.cursor(t.head,t.head>t.anchor?-1:1))}for(let t of e.effects)t.is(_i)&&(c=t.value.clip(this.state))}this.viewState.update(e,c),this.bidiCache=ms.update(this.bidiCache,e.changes),e.empty||(this.updatePlugins(e),this.inputState.update(e)),i=this.docView.update(e),this.state.facet(tn)!=this.styleModules&&this.mountStyles(),n=this.updateAttrs(),this.showAnnouncements(t),this.docView.updateSelection(i,t.some(t=>t.isUserEvent(\"select.pointer\")))}finally{this.updateState=0}if(e.startState.facet(Jr)!=e.state.facet(Jr)&&(this.viewState.mustMeasureContent=!0),(i||n||c||this.viewState.mustEnforceCursorAssoc||this.viewState.mustMeasureContent)&&this.requestMeasure(),i&&this.docViewUpdate(),!e.empty)for(let d of this.state.facet(Pi))try{d(e)}catch(u){Yi(this.state,u,\"update listener\")}(a||h)&&Promise.resolve().then(()=>{a&&this.state==a.startState&&this.dispatch(a),h&&!Hn(this,h)&&l.force&&Je(this.contentDOM,l.key,l.keyCode)})}setState(t){if(0!=this.updateState)throw new Error(\"Calls to EditorView.setState are not allowed while an update is in progress\");if(this.destroyed)return void(this.viewState.state=t);this.updateState=2;let e=this.hasFocus;try{for(let t of this.plugins)t.destroy(this);this.viewState=new jr(t),this.plugins=t.facet(Vi).map(t=>new Di(t)),this.pluginMap.clear();for(let t of this.plugins)t.update(this);this.docView.destroy(),this.docView=new Tn(this),this.inputState.ensureHandlers(this.plugins),this.mountStyles(),this.updateAttrs(),this.bidiCache=[]}finally{this.updateState=0}e&&this.focus(),this.requestMeasure()}updatePlugins(t){let e=t.startState.facet(Vi),i=t.state.facet(Vi);if(e!=i){let n=[];for(let r of i){let i=e.indexOf(r);if(i<0)n.push(new Di(r));else{let e=this.plugins[i];e.mustUpdate=t,n.push(e)}}for(let e of this.plugins)e.mustUpdate!=t&&e.destroy(this);this.plugins=n,this.pluginMap.clear()}else for(let n of this.plugins)n.mustUpdate=t;for(let n=0;n<this.plugins.length;n++)this.plugins[n].update(this);e!=i&&this.inputState.ensureHandlers(this.plugins)}docViewUpdate(){for(let e of this.plugins){let i=e.value;if(i&&i.docViewUpdate)try{i.docViewUpdate(this)}catch(t){Yi(this.state,t,\"doc view update listener\")}}}measure(t=!0){if(this.destroyed)return;if(this.measureScheduled>-1&&this.win.cancelAnimationFrame(this.measureScheduled),this.observer.delayedAndroidKey)return this.measureScheduled=-1,void this.requestMeasure();this.measureScheduled=0,t&&this.observer.forceFlush();let e=null,i=this.scrollDOM,n=i.scrollTop*this.scaleY,{scrollAnchorPos:r,scrollAnchorHeight:s}=this.viewState;Math.abs(n-this.viewState.scrollTop)>1&&(s=-1),this.viewState.scrollAnchorHeight=-1;try{for(let t=0;;t++){if(s<0)if(ti(i))r=-1,s=this.viewState.heightMap.height;else{let t=this.viewState.scrollAnchorAt(n);r=t.from,s=t.top}this.updateState=1;let a=this.viewState.measure(this);if(!a&&!this.measureRequests.length&&null==this.viewState.scrollTarget)break;if(t>5)break;let l=[];4&a||([this.measureRequests,l]=[l,this.measureRequests]);let h=l.map(t=>{try{return t.read(this)}catch(e){return Yi(this.state,e),ps}}),c=nn.create(this,this.state,[]),u=!1;c.flags|=a,e?e.flags|=a:e=c,this.updateState=2,c.empty||(this.updatePlugins(c),this.inputState.update(c),this.updateAttrs(),u=this.docView.update(c),u&&this.docViewUpdate());for(let t=0;t<l.length;t++)if(h[t]!=ps)try{let e=l[t];e.write&&e.write(h[t],this)}catch(o){Yi(this.state,o)}if(u&&this.docView.updateSelection(!0),!c.viewportChanged&&0==this.measureRequests.length){if(this.viewState.editorHeight){if(this.viewState.scrollTarget){this.docView.scrollIntoView(this.viewState.scrollTarget),this.viewState.scrollTarget=null,s=-1;continue}{let t=(r<0?this.viewState.heightMap.height:this.viewState.lineBlockAt(r).top)-s;if(t>1||t<-1){n+=t,i.scrollTop=n/this.scaleY,s=-1;continue}}}break}}}finally{this.updateState=0,this.measureScheduled=-1}if(e&&!e.empty)for(let a of this.state.facet(Pi))a(e)}get themeClasses(){return es+\" \"+(this.state.facet(ts)?ns:is)+\" \"+this.state.facet(Jr)}updateAttrs(){let t=gs(this,Bi,{class:\"cm-editor\"+(this.hasFocus?\" cm-focused \":\" \")+this.themeClasses}),e={spellcheck:\"false\",autocorrect:\"off\",autocapitalize:\"off\",writingsuggestions:\"false\",translate:\"no\",contenteditable:this.state.facet(Li)?\"true\":\"false\",class:\"cm-content\",style:`${we.tabSize}: ${this.state.tabSize}`,role:\"textbox\",\"aria-multiline\":\"true\"};this.state.readOnly&&(e[\"aria-readonly\"]=\"true\"),gs(this,ji,e);let i=this.observer.ignore(()=>{let i=ke(this.contentDOM,this.contentAttrs,e),n=ke(this.dom,this.editorAttrs,t);return i||n});return this.editorAttrs=t,this.contentAttrs=e,i}showAnnouncements(t){let e=!0;for(let i of t)for(let t of i.effects)if(t.is(fs.announce)){e&&(this.announceDOM.textContent=\"\"),e=!1,this.announceDOM.appendChild(document.createElement(\"div\")).textContent=t.value}}mountStyles(){this.styleModules=this.state.facet(tn);let t=this.state.facet(fs.cspNonce);Jt.mount(this.root,this.styleModules.concat(os).reverse(),t?{nonce:t}:void 0)}readMeasured(){if(2==this.updateState)throw new Error(\"Reading the editor layout isn't allowed during an update\");0==this.updateState&&this.measureScheduled>-1&&this.measure(!1)}requestMeasure(t){if(this.measureScheduled<0&&(this.measureScheduled=this.win.requestAnimationFrame(()=>this.measure())),t){if(this.measureRequests.indexOf(t)>-1)return;if(null!=t.key)for(let e=0;e<this.measureRequests.length;e++)if(this.measureRequests[e].key===t.key)return void(this.measureRequests[e]=t);this.measureRequests.push(t)}}plugin(t){let e=this.pluginMap.get(t);return(void 0===e||e&&e.plugin!=t)&&this.pluginMap.set(t,e=this.plugins.find(e=>e.plugin==t)||null),e&&e.update(this).value}get documentTop(){return this.contentDOM.getBoundingClientRect().top+this.viewState.paddingTop}get documentPadding(){return{top:this.viewState.paddingTop,bottom:this.viewState.paddingBottom}}get scaleX(){return this.viewState.scaleX}get scaleY(){return this.viewState.scaleY}elementAtHeight(t){return this.readMeasured(),this.viewState.elementAtHeight(t)}lineBlockAtHeight(t){return this.readMeasured(),this.viewState.lineBlockAtHeight(t)}get viewportLineBlocks(){return this.viewState.viewportLines}lineBlockAt(t){return this.viewState.lineBlockAt(t)}get contentHeight(){return this.viewState.contentHeight}moveByChar(t,e,i){return Yn(this,t,zn(this,t,e,i))}moveByGroup(t,e){return Yn(this,t,zn(this,t,e,e=>function(t,e,i){let n=t.state.charCategorizer(e),r=n(i);return t=>{let e=n(t);return r==kt.Space&&(r=e),r==e}}(this,t.head,e)))}visualLineSide(t,e){let i=this.bidiSpans(t),n=this.textDirectionAt(t.from),r=i[e?i.length-1:0];return Y.cursor(r.side(e,n)+t.from,r.forward(!e,n)?1:-1)}moveToLineBoundary(t,e,i=!0){return function(t,e,i,n){let r=Rn(t,e.head,e.assoc||-1),s=n&&r.type==Te.Text&&(t.lineWrapping||r.widgetLineBreaks)?t.coordsAtPos(e.assoc<0&&e.head>r.from?e.head-1:e.head):null;if(s){let e=t.dom.getBoundingClientRect(),n=t.textDirectionAt(r.from),o=t.posAtCoords({x:i==(n==ri.LTR)?e.right-1:e.left+1,y:(s.top+s.bottom)/2});if(null!=o)return Y.cursor(o,i?-1:1)}return Y.cursor(i?r.to:r.from,i?-1:1)}(this,t,e,i)}moveVertically(t,e,i){return Yn(this,t,function(t,e,i,n){let r=e.head,s=i?1:-1;if(r==(i?t.state.doc.length:0))return Y.cursor(r,e.assoc);let o,a=e.goalColumn,l=t.contentDOM.getBoundingClientRect(),h=t.coordsAtPos(r,e.assoc||-1),c=t.documentTop;if(h)null==a&&(a=h.left-l.left),o=s<0?h.top:h.bottom;else{let e=t.viewState.lineBlockAt(r);null==a&&(a=Math.min(l.right-l.left,t.defaultCharacterWidth*(r-e.from))),o=(s<0?e.top:e.bottom)+c}let u=l.left+a,d=null!=n?n:t.viewState.heightOracle.textHeight>>1;for(let f=0;;f+=10){let e=qn(t,{x:u,y:o+(d+f)*s},!1,s);return Y.cursor(e.pos,e.assoc,void 0,a)}}(this,t,e,i))}domAtPos(t,e=1){return this.docView.domAtPos(t,e)}posAtDOM(t,e=0){return this.docView.posFromDOM(t,e)}posAtCoords(t,e=!0){this.readMeasured();let i=qn(this,t,e);return i&&i.pos}posAndSideAtCoords(t,e=!0){return this.readMeasured(),qn(this,t,e)}coordsAtPos(t,e=1){this.readMeasured();let i=this.docView.coordsAt(t,e);if(!i||i.left==i.right)return i;let n=this.state.doc.lineAt(t),r=this.bidiSpans(n);return je(i,r[Oi.find(r,t-n.from,-1,e)].dir==ri.LTR==e>0)}coordsForChar(t){return this.readMeasured(),this.docView.coordsForChar(t)}get defaultCharacterWidth(){return this.viewState.heightOracle.charWidth}get defaultLineHeight(){return this.viewState.heightOracle.lineHeight}get textDirection(){return this.viewState.defaultTextDirection}textDirectionAt(t){return!this.state.facet(Ai)||t<this.viewport.from||t>this.viewport.to?this.textDirection:(this.readMeasured(),this.docView.textDirectionAt(t))}get lineWrapping(){return this.viewState.heightOracle.lineWrapping}bidiSpans(t){if(t.length>Os)return bi(t.length);let e,i=this.textDirectionAt(t.from);for(let r of this.bidiCache)if(r.from==t.from&&r.dir==i&&(r.fresh||pi(r.isolates,e=Fi(this,t))))return r.order;e||(e=Fi(this,t));let n=function(t,e,i){if(!t)return[new Oi(0,0,e==oi?1:0)];if(e==si&&!i.length&&!fi.test(t))return bi(t.length);if(i.length)for(;t.length>mi.length;)mi[mi.length]=256;let n=[],r=e==si?0:1;return Qi(t,r,r,i,0,t.length,n),n}(t.text,i,e);return this.bidiCache.push(new ms(t.from,t.to,i,e,!0,n)),n}get hasFocus(){var t;return(this.dom.ownerDocument.hasFocus()||we.safari&&(null===(t=this.inputState)||void 0===t?void 0:t.lastContextMenu)>Date.now()-3e4)&&this.root.activeElement==this.contentDOM}focus(){this.observer.ignore(()=>{Fe(this.contentDOM),this.docView.updateSelection()})}setRoot(t){this._root!=t&&(this._root=t,this.observer.setWindow((9==t.nodeType?t:t.ownerDocument).defaultView||window),this.mountStyles())}destroy(){this.root.activeElement==this.contentDOM&&this.contentDOM.blur();for(let t of this.plugins)t.destroy(this);this.plugins=[],this.inputState.destroy(),this.docView.destroy(),this.dom.remove(),this.observer.destroy(),this.measureScheduled>-1&&this.win.cancelAnimationFrame(this.measureScheduled),this.destroyed=!0}static scrollIntoView(t,e={}){return _i.of(new zi(\"number\"==typeof t?Y.cursor(t):t,e.y,e.x,e.yMargin,e.xMargin))}scrollSnapshot(){let{scrollTop:t,scrollLeft:e}=this.scrollDOM,i=this.viewState.scrollAnchorAt(t);return _i.of(new zi(Y.cursor(i.from),\"start\",\"start\",i.top-t,e,!0))}setTabFocusMode(t){null==t?this.inputState.tabFocusMode=this.inputState.tabFocusMode<0?0:-1:\"boolean\"==typeof t?this.inputState.tabFocusMode=t?0:-1:0!=this.inputState.tabFocusMode&&(this.inputState.tabFocusMode=Date.now()+t)}static domEventHandlers(t){return Wi.define(()=>({}),{eventHandlers:t})}static domEventObservers(t){return Wi.define(()=>({}),{eventObservers:t})}static theme(t,e){let i=Jt.newName(),n=[Jr.of(i),tn.of(ss(`.${i}`,t))];return e&&e.dark&&n.push(ts.of(!0)),n}static baseTheme(t){return tt.lowest(tn.of(ss(\".\"+es,t,rs)))}static findFromDOM(t){var e;let i=t.querySelector(\".cm-content\"),n=i&&sn.get(i)||sn.get(t);return(null===(e=null==n?void 0:n.root)||void 0===e?void 0:e.view)||null}}fs.styleModule=tn,fs.inputHandler=Ti,fs.clipboardInputFilter=Zi,fs.clipboardOutputFilter=Xi,fs.scrollHandler=Ri,fs.focusChangeEffect=Ci,fs.perLineTextDirection=Ai,fs.exceptionSink=$i,fs.updateListener=Pi,fs.editable=Li,fs.mouseSelectionStyle=ki,fs.dragMovesSelection=yi,fs.clickAddsSelectionRange=Si,fs.decorations=Ii,fs.blockWrappers=Gi,fs.outerDecorations=Ni,fs.atomicRanges=Ui,fs.bidiIsolatedRanges=Hi,fs.scrollMargins=Ki,fs.darkTheme=ts,fs.cspNonce=V.define({combine:t=>t.length?t[0]:\"\"}),fs.contentAttributes=ji,fs.editorAttributes=Bi,fs.lineWrapping=fs.contentAttributes.of({class:\"cm-lineWrapping\"}),fs.announce=gt.define();const Os=4096,ps={};class ms{constructor(t,e,i,n,r,s){this.from=t,this.to=e,this.dir=i,this.isolates=n,this.fresh=r,this.order=s}static update(t,e){if(e.empty&&!t.some(t=>t.fresh))return t;let i=[],n=t.length?t[t.length-1].dir:ri.LTR;for(let r=Math.max(0,t.length-10);r<t.length;r++){let s=t[r];s.dir!=n||e.touchesRange(s.from,s.to)||i.push(new ms(e.mapPos(s.from,1),e.mapPos(s.to,-1),s.dir,s.isolates,!1,s.order))}return i}}function gs(t,e,i){for(let n=t.state.facet(e),r=n.length-1;r>=0;r--){let e=n[r],s=\"function\"==typeof e?e(t):e;s&&xe(s,i)}return i}const Qs=we.mac?\"mac\":we.windows?\"win\":we.linux?\"linux\":\"key\";function bs(t,e,i){return e.altKey&&(t=\"Alt-\"+t),e.ctrlKey&&(t=\"Ctrl-\"+t),e.metaKey&&(t=\"Meta-\"+t),!1!==i&&e.shiftKey&&(t=\"Shift-\"+t),t}const vs=tt.default(fs.domEventHandlers({keydown:(t,e)=>Ps(Ss(e.state),t,e,\"editor\")})),ws=V.define({enables:vs}),xs=new WeakMap;function Ss(t){let e=t.facet(ws),i=xs.get(e);return i||xs.set(e,i=function(t,e=Qs){let i=Object.create(null),n=Object.create(null),r=(t,e)=>{let i=n[t];if(null==i)n[t]=e;else if(i!=e)throw new Error(\"Key binding \"+t+\" is used both as a regular binding and as a multi-stroke prefix\")},s=(t,n,s,o,a)=>{var l,h;let c=i[t]||(i[t]=Object.create(null)),u=n.split(/ (?!$)/).map(t=>function(t,e){const i=t.split(/-(?!$)/);let n,r,s,o,a=i[i.length-1];\"Space\"==a&&(a=\" \");for(let l=0;l<i.length-1;++l){const t=i[l];if(/^(cmd|meta|m)$/i.test(t))o=!0;else if(/^a(lt)?$/i.test(t))n=!0;else if(/^(c|ctrl|control)$/i.test(t))r=!0;else if(/^s(hift)?$/i.test(t))s=!0;else{if(!/^mod$/i.test(t))throw new Error(\"Unrecognized modifier name: \"+t);\"mac\"==e?o=!0:r=!0}}return n&&(a=\"Alt-\"+a),r&&(a=\"Ctrl-\"+a),o&&(a=\"Meta-\"+a),s&&(a=\"Shift-\"+a),a}(t,e));for(let e=1;e<u.length;e++){let i=u.slice(0,e).join(\" \");r(i,!0),c[i]||(c[i]={preventDefault:!0,stopPropagation:!1,run:[e=>{let n=ys={view:e,prefix:i,scope:t};return setTimeout(()=>{ys==n&&(ys=null)},ks),!0}]})}let d=u.join(\" \");r(d,!1);let f=c[d]||(c[d]={preventDefault:!1,stopPropagation:!1,run:(null===(h=null===(l=c._any)||void 0===l?void 0:l.run)||void 0===h?void 0:h.slice())||[]});s&&f.run.push(s),o&&(f.preventDefault=!0),a&&(f.stopPropagation=!0)};for(let o of t){let t=o.scope?o.scope.split(\" \"):[\"editor\"];if(o.any)for(let e of t){let t=i[e]||(i[e]=Object.create(null));t._any||(t._any={preventDefault:!1,stopPropagation:!1,run:[]});let{any:n}=o;for(let e in t)t[e].run.push(t=>n(t,$s))}let n=o[e]||o.key;if(n)for(let e of t)s(e,n,o.run,o.preventDefault,o.stopPropagation),o.shift&&s(e,\"Shift-\"+n,o.shift,o.preventDefault,o.stopPropagation)}return i}(e.reduce((t,e)=>t.concat(e),[]))),i}let ys=null;const ks=4e3;let $s=null;function Ps(t,e,i,n){$s=e;let r=function(t){var e=!(re&&t.metaKey&&t.shiftKey&&!t.ctrlKey&&!t.altKey||se&&t.shiftKey&&t.key&&1==t.key.length||\"Unidentified\"==t.key)&&t.key||(t.shiftKey?ne:ie)[t.keyCode]||t.key||\"Unidentified\";return\"Esc\"==e&&(e=\"Escape\"),\"Del\"==e&&(e=\"Delete\"),\"Left\"==e&&(e=\"ArrowLeft\"),\"Up\"==e&&(e=\"ArrowUp\"),\"Right\"==e&&(e=\"ArrowRight\"),\"Down\"==e&&(e=\"ArrowDown\"),e}(e),s=$(y(r,0))==r.length&&\" \"!=r,o=\"\",a=!1,l=!1,h=!1;ys&&ys.view==i&&ys.scope==n&&(o=ys.prefix+\" \",nr.indexOf(e.keyCode)<0&&(l=!0,ys=null));let c,u,d=new Set,f=t=>{if(t){for(let e of t.run)if(!d.has(e)&&(d.add(e),e(i)))return t.stopPropagation&&(h=!0),!0;t.preventDefault&&(t.stopPropagation&&(h=!0),l=!0)}return!1},O=t[n];return O&&(f(O[o+bs(r,e,!s)])?a=!0:!s||!(e.altKey||e.metaKey||e.ctrlKey)||we.windows&&e.ctrlKey&&e.altKey||we.mac&&e.altKey&&!e.ctrlKey&&!e.metaKey||!(c=ie[e.keyCode])||c==r?s&&e.shiftKey&&f(O[o+bs(r,e,!0)])&&(a=!0):(f(O[o+bs(c,e,!0)])||e.shiftKey&&(u=ne[e.keyCode])!=r&&u!=c&&f(O[o+bs(u,e,!1)]))&&(a=!0),!a&&f(O._any)&&(a=!0)),l&&(a=!0),a&&h&&e.stopPropagation(),$s=null,a}class Ts{constructor(t,e,i,n,r){this.className=t,this.left=e,this.top=i,this.width=n,this.height=r}draw(){let t=document.createElement(\"div\");return t.className=this.className,this.adjust(t),t}update(t,e){return e.className==this.className&&(this.adjust(t),!0)}adjust(t){t.style.left=this.left+\"px\",t.style.top=this.top+\"px\",null!=this.width&&(t.style.width=this.width+\"px\"),t.style.height=this.height+\"px\"}eq(t){return this.left==t.left&&this.top==t.top&&this.width==t.width&&this.height==t.height&&this.className==t.className}static forRange(t,e,i){if(i.empty){let n=t.coordsAtPos(i.head,i.assoc||1);if(!n)return[];let r=Cs(t);return[new Ts(e,n.left-r.left,n.top-r.top,null,n.bottom-n.top)]}return function(t,e,i){if(i.to<=t.viewport.from||i.from>=t.viewport.to)return[];let n=Math.max(i.from,t.viewport.from),r=Math.min(i.to,t.viewport.to),s=t.textDirection==ri.LTR,o=t.contentDOM,a=o.getBoundingClientRect(),l=Cs(t),h=o.querySelector(\".cm-line\"),c=h&&window.getComputedStyle(h),u=a.left+(c?parseInt(c.paddingLeft)+Math.min(0,parseInt(c.textIndent)):0),d=a.right-(c?parseInt(c.paddingRight):0),f=Rn(t,n,1),O=Rn(t,r,-1),p=f.type==Te.Text?f:null,m=O.type==Te.Text?O:null;p&&(t.lineWrapping||f.widgetLineBreaks)&&(p=Zs(t,n,1,p));m&&(t.lineWrapping||O.widgetLineBreaks)&&(m=Zs(t,r,-1,m));if(p&&m&&p.from==m.from&&p.to==m.to)return Q(b(i.from,i.to,p));{let e=p?b(i.from,null,p):v(f,!1),n=m?b(null,i.to,m):v(O,!0),r=[];return(p||f).to<(m||O).from-(p&&m?1:0)||f.widgetLineBreaks>1&&e.bottom+t.defaultLineHeight/2<n.top?r.push(g(u,e.bottom,d,n.top)):e.bottom<n.top&&t.elementAtHeight((e.bottom+n.top)/2).type==Te.Text&&(e.bottom=n.top=(e.bottom+n.top)/2),Q(e).concat(r).concat(Q(n))}function g(t,i,n,r){return new Ts(e,t-l.left,i-l.top,n-t,r-i)}function Q({top:t,bottom:e,horizontal:i}){let n=[];for(let r=0;r<i.length;r+=2)n.push(g(i[r],t,i[r+1],e));return n}function b(e,i,n){let r=1e9,o=-1e9,a=[];function l(e,i,l,h,c){let f=t.coordsAtPos(e,e==n.to?-2:2),O=t.coordsAtPos(l,l==n.from?2:-2);f&&O&&(r=Math.min(f.top,O.top,r),o=Math.max(f.bottom,O.bottom,o),c==ri.LTR?a.push(s&&i?u:f.left,s&&h?d:O.right):a.push(!s&&h?u:O.left,!s&&i?d:f.right))}let h=null!=e?e:n.from,c=null!=i?i:n.to;for(let s of t.visibleRanges)if(s.to>h&&s.from<c)for(let n=Math.max(s.from,h),r=Math.min(s.to,c);;){let s=t.state.doc.lineAt(n);for(let o of t.bidiSpans(s)){let t=o.from+s.from,a=o.to+s.from;if(t>=r)break;a>n&&l(Math.max(t,n),null==e&&t<=h,Math.min(a,r),null==i&&a>=c,o.dir)}if(n=s.to+1,n>=r)break}return 0==a.length&&l(h,null==e,c,null==i,t.textDirection),{top:r,bottom:o,horizontal:a}}function v(t,e){let i=a.top+(e?t.top:t.bottom);return{top:i,bottom:i,horizontal:[]}}}(t,e,i)}}function Cs(t){let e=t.scrollDOM.getBoundingClientRect();return{left:(t.textDirection==ri.LTR?e.left:e.right-t.scrollDOM.clientWidth*t.scaleX)-t.scrollDOM.scrollLeft*t.scaleX,top:e.top-t.scrollDOM.scrollTop*t.scaleY}}function Zs(t,e,i,n){let r=t.coordsAtPos(e,2*i);if(!r)return n;let s=t.dom.getBoundingClientRect(),o=(r.top+r.bottom)/2,a=t.posAtCoords({x:s.left+1,y:o}),l=t.posAtCoords({x:s.right-1,y:o});return null==a||null==l?n:{from:Math.max(n.from,Math.min(a,l)),to:Math.min(n.to,Math.max(a,l))}}class Xs{constructor(t,e){this.view=t,this.layer=e,this.drawn=[],this.scaleX=1,this.scaleY=1,this.measureReq={read:this.measure.bind(this),write:this.draw.bind(this)},this.dom=t.scrollDOM.appendChild(document.createElement(\"div\")),this.dom.classList.add(\"cm-layer\"),e.above&&this.dom.classList.add(\"cm-layer-above\"),e.class&&this.dom.classList.add(e.class),this.scale(),this.dom.setAttribute(\"aria-hidden\",\"true\"),this.setOrder(t.state),t.requestMeasure(this.measureReq),e.mount&&e.mount(this.dom,t)}update(t){t.startState.facet(As)!=t.state.facet(As)&&this.setOrder(t.state),(this.layer.update(t,this.dom)||t.geometryChanged)&&(this.scale(),t.view.requestMeasure(this.measureReq))}docViewUpdate(t){!1!==this.layer.updateOnDocViewUpdate&&t.requestMeasure(this.measureReq)}setOrder(t){let e=0,i=t.facet(As);for(;e<i.length&&i[e]!=this.layer;)e++;this.dom.style.zIndex=String((this.layer.above?150:-1)-e)}measure(){return this.layer.markers(this.view)}scale(){let{scaleX:t,scaleY:e}=this.view;t==this.scaleX&&e==this.scaleY||(this.scaleX=t,this.scaleY=e,this.dom.style.transform=`scale(${1/t}, ${1/e})`)}draw(t){if(t.length!=this.drawn.length||t.some((t,e)=>{return i=t,n=this.drawn[e],!(i.constructor==n.constructor&&i.eq(n));var i,n})){let e=this.dom.firstChild,i=0;for(let n of t)n.update&&e&&n.constructor&&this.drawn[i].constructor&&n.update(e,this.drawn[i])?(e=e.nextSibling,i++):this.dom.insertBefore(n.draw(),e);for(;e;){let t=e.nextSibling;e.remove(),e=t}this.drawn=t,we.safari&&we.safari_version>=26&&(this.dom.style.display=this.dom.firstChild?\"\":\"none\")}}destroy(){this.layer.destroy&&this.layer.destroy(this.dom,this.view),this.dom.remove()}}const As=V.define();function Ms(t){return[Wi.define(e=>new Xs(e,t)),As.of(t)]}const Rs=V.define({combine:t=>Zt(t,{cursorBlinkRate:1200,drawRangeCursor:!0},{cursorBlinkRate:(t,e)=>Math.min(t,e),drawRangeCursor:(t,e)=>t||e})});function zs(t){return t.startState.facet(Rs)!=t.state.facet(Rs)}const _s=Ms({above:!0,markers(t){let{state:e}=t,i=e.facet(Rs),n=[];for(let r of e.selection.ranges){let s=r==e.selection.main;if(r.empty||i.drawRangeCursor){let e=s?\"cm-cursor cm-cursor-primary\":\"cm-cursor cm-cursor-secondary\",i=r.empty?r:Y.cursor(r.head,r.head>r.anchor?-1:1);for(let r of Ts.forRange(t,e,i))n.push(r)}}return n},update(t,e){t.transactions.some(t=>t.selection)&&(e.style.animationName=\"cm-blink\"==e.style.animationName?\"cm-blink2\":\"cm-blink\");let i=zs(t);return i&&Es(t.state,e),t.docChanged||t.selectionSet||i},mount(t,e){Es(e.state,t)},class:\"cm-cursorLayer\"});function Es(t,e){e.style.animationDuration=t.facet(Rs).cursorBlinkRate+\"ms\"}const Ys=Ms({above:!1,markers:t=>t.state.selection.ranges.map(e=>e.empty?[]:Ts.forRange(t,\"cm-selectionBackground\",e)).reduce((t,e)=>t.concat(e)),update:(t,e)=>t.docChanged||t.selectionSet||t.viewportChanged||zs(t),class:\"cm-selectionLayer\"}),Ls=tt.highest(fs.theme({\".cm-line\":{\"& ::selection, &::selection\":{backgroundColor:\"transparent !important\"},caretColor:\"transparent !important\"},\".cm-content\":{caretColor:\"transparent !important\",\"& :focus\":{caretColor:\"initial !important\",\"&::selection, & ::selection\":{backgroundColor:\"Highlight !important\"}}}})),qs=gt.define({map:(t,e)=>null==t?null:e.mapPos(t)}),Vs=N.define({create:()=>null,update:(t,e)=>(null!=t&&(t=e.changes.mapPos(t)),e.effects.reduce((t,e)=>e.is(qs)?e.value:t,t))}),Ws=Wi.fromClass(class{constructor(t){this.view=t,this.cursor=null,this.measureReq={read:this.readPos.bind(this),write:this.drawCursor.bind(this)}}update(t){var e;let i=t.state.field(Vs);null==i?null!=this.cursor&&(null===(e=this.cursor)||void 0===e||e.remove(),this.cursor=null):(this.cursor||(this.cursor=this.view.scrollDOM.appendChild(document.createElement(\"div\")),this.cursor.className=\"cm-dropCursor\"),(t.startState.field(Vs)!=i||t.docChanged||t.geometryChanged)&&this.view.requestMeasure(this.measureReq))}readPos(){let{view:t}=this,e=t.state.field(Vs),i=null!=e&&t.coordsAtPos(e);if(!i)return null;let n=t.scrollDOM.getBoundingClientRect();return{left:i.left-n.left+t.scrollDOM.scrollLeft*t.scaleX,top:i.top-n.top+t.scrollDOM.scrollTop*t.scaleY,height:i.bottom-i.top}}drawCursor(t){if(this.cursor){let{scaleX:e,scaleY:i}=this.view;t?(this.cursor.style.left=t.left/e+\"px\",this.cursor.style.top=t.top/i+\"px\",this.cursor.style.height=t.height/i+\"px\"):this.cursor.style.left=\"-100000px\"}}destroy(){this.cursor&&this.cursor.remove()}setDropPos(t){this.view.state.field(Vs)!=t&&this.view.dispatch({effects:qs.of(t)})}},{eventObservers:{dragover(t){this.setDropPos(this.view.posAtCoords({x:t.clientX,y:t.clientY}))},dragleave(t){t.target!=this.view.contentDOM&&this.view.contentDOM.contains(t.relatedTarget)||this.setDropPos(null)},dragend(){this.setDropPos(null)},drop(){this.setDropPos(null)}}});function Ds(t,e,i,n,r){e.lastIndex=0;for(let s,o=t.iterRange(i,n),a=i;!o.next().done;a+=o.value.length)if(!o.lineBreak)for(;s=e.exec(o.value);)r(a+s.index,s)}class Bs{constructor(t){const{regexp:e,decoration:i,decorate:n,boundary:r,maxLength:s=1e3}=t;if(!e.global)throw new RangeError(\"The regular expression given to MatchDecorator should have its 'g' flag set\");if(this.regexp=e,n)this.addMatch=(t,e,i,r)=>n(r,i,i+t[0].length,t,e);else if(\"function\"==typeof i)this.addMatch=(t,e,n,r)=>{let s=i(t,e,n);s&&r(n,n+t[0].length,s)};else{if(!i)throw new RangeError(\"Either 'decorate' or 'decoration' should be provided to MatchDecorator\");this.addMatch=(t,e,n,r)=>r(n,n+t[0].length,i)}this.boundary=r,this.maxLength=s}createDeco(t){let e=new Et,i=e.add.bind(e);for(let{from:n,to:r}of function(t,e){let i=t.visibleRanges;if(1==i.length&&i[0].from==t.viewport.from&&i[0].to==t.viewport.to)return i;let n=[];for(let{from:r,to:s}of i)r=Math.max(t.state.doc.lineAt(r).from,r-e),s=Math.min(t.state.doc.lineAt(s).to,s+e),n.length&&n[n.length-1].to>=r?n[n.length-1].to=s:n.push({from:r,to:s});return n}(t,this.maxLength))Ds(t.state.doc,this.regexp,n,r,(e,n)=>this.addMatch(n,t,e,i));return e.finish()}updateDeco(t,e){let i=1e9,n=-1;return t.docChanged&&t.changes.iterChanges((e,r,s,o)=>{o>=t.view.viewport.from&&s<=t.view.viewport.to&&(i=Math.min(s,i),n=Math.max(o,n))}),t.viewportMoved||n-i>1e3?this.createDeco(t.view):n>-1?this.updateRange(t.view,e.map(t.changes),i,n):e}updateRange(t,e,i,n){for(let r of t.visibleRanges){let s=Math.max(r.from,i),o=Math.min(r.to,n);if(o>=s){let i=t.state.doc.lineAt(s),n=i.to<o?t.state.doc.lineAt(o):i,a=Math.max(r.from,i.from),l=Math.min(r.to,n.to);if(this.boundary){for(;s>i.from;s--)if(this.boundary.test(i.text[s-1-i.from])){a=s;break}for(;o<n.to;o++)if(this.boundary.test(n.text[o-n.from])){l=o;break}}let h,c=[],u=(t,e,i)=>c.push(i.range(t,e));if(i==n)for(this.regexp.lastIndex=a-i.from;(h=this.regexp.exec(i.text))&&h.index<l-i.from;)this.addMatch(h,t,h.index+i.from,u);else Ds(t.state.doc,this.regexp,a,l,(e,i)=>this.addMatch(i,t,e,u));e=e.update({filterFrom:a,filterTo:l,filter:(t,e)=>t<a||e>l,add:c})}}return e}}const js=null!=/x/.unicode?\"gu\":\"g\",Is=new RegExp(\"[\\0-\\b\\n-\u001f-­؜​‎‏\\u2028\\u2029‭‮⁦⁧⁩\\ufeff￹-￼]\",js),Gs={0:\"null\",7:\"bell\",8:\"backspace\",10:\"newline\",11:\"vertical tab\",13:\"carriage return\",27:\"escape\",8203:\"zero width space\",8204:\"zero width non-joiner\",8205:\"zero width joiner\",8206:\"left-to-right mark\",8207:\"right-to-left mark\",8232:\"line separator\",8237:\"left-to-right override\",8238:\"right-to-left override\",8294:\"left-to-right isolate\",8295:\"right-to-left isolate\",8297:\"pop directional isolate\",8233:\"paragraph separator\",65279:\"zero width no-break space\",65532:\"object replacement\"};let Ns=null;const Us=V.define({combine(t){let e=Zt(t,{render:null,specialChars:Is,addSpecialChars:null});return(e.replaceTabs=!function(){var t;if(null==Ns&&\"undefined\"!=typeof document&&document.body){let e=document.body.style;Ns=null!=(null!==(t=e.tabSize)&&void 0!==t?t:e.MozTabSize)}return Ns||!1}())&&(e.specialChars=new RegExp(\"\\t|\"+e.specialChars.source,js)),e.addSpecialChars&&(e.specialChars=new RegExp(e.specialChars.source+\"|\"+e.addSpecialChars.source,js)),e}});let Hs=null;class Fs extends Pe{constructor(t,e){super(),this.options=t,this.code=e}eq(t){return t.code==this.code}toDOM(t){let e=function(t){return t>=32?\"•\":10==t?\"␤\":String.fromCharCode(9216+t)}(this.code),i=t.state.phrase(\"Control character\")+\" \"+(Gs[this.code]||\"0x\"+this.code.toString(16)),n=this.options.render&&this.options.render(this.code,i,e);if(n)return n;let r=document.createElement(\"span\");return r.textContent=e,r.title=i,r.setAttribute(\"aria-label\",i),r.className=\"cm-specialChar\",r}ignoreEvent(){return!1}}class Ks extends Pe{constructor(t){super(),this.width=t}eq(t){return t.width==this.width}toDOM(){let t=document.createElement(\"span\");return t.textContent=\"\\t\",t.className=\"cm-tab\",t.style.width=this.width+\"px\",t}ignoreEvent(){return!1}}const Js=Ce.line({class:\"cm-activeLine\"}),to=Wi.fromClass(class{constructor(t){this.decorations=this.getDeco(t)}update(t){(t.docChanged||t.selectionSet)&&(this.decorations=this.getDeco(t.view))}getDeco(t){let e=-1,i=[];for(let n of t.state.selection.ranges){let r=t.lineBlockAt(n.head);r.from>e&&(i.push(Js.range(r.from)),e=r.from)}return Ce.set(i)}},{decorations:t=>t.decorations});class eo extends Pe{constructor(t){super(),this.content=t}toDOM(t){let e=document.createElement(\"span\");return e.className=\"cm-placeholder\",e.style.pointerEvents=\"none\",e.appendChild(\"string\"==typeof this.content?document.createTextNode(this.content):\"function\"==typeof this.content?this.content(t):this.content.cloneNode(!0)),e.setAttribute(\"aria-hidden\",\"true\"),e}coordsAt(t){let e=t.firstChild?Le(t.firstChild):[];if(!e.length)return null;let i=window.getComputedStyle(t.parentNode),n=je(e[0],\"rtl\"!=i.direction),r=parseInt(i.lineHeight);return n.bottom-n.top>1.5*r?{left:n.left,right:n.right,top:n.top,bottom:n.top+r}:n}ignoreEvent(){return!1}}function io(t){let e=Wi.fromClass(class{constructor(e){this.view=e,this.placeholder=t?Ce.set([Ce.widget({widget:new eo(t),side:1}).range(0)]):Ce.none}get decorations(){return this.view.state.doc.length?Ce.none:this.placeholder}},{decorations:t=>t.decorations});return\"string\"==typeof t?[e,fs.contentAttributes.of({\"aria-placeholder\":t})]:e}const no=2e3;function ro(t,e){let i=t.posAtCoords({x:e.clientX,y:e.clientY},!1),n=t.state.doc.lineAt(i),r=i-n.from,s=r>no?-1:r==n.length?function(t,e){let i=t.coordsAtPos(t.viewport.from);return i?Math.round(Math.abs((i.left-e)/t.defaultCharacterWidth)):-1}(t,e.clientX):Nt(n.text,t.state.tabSize,i-n.from);return{line:n.number,col:s,off:r}}function so(t,e){let i=ro(t,e),n=t.state.selection;return i?{update(t){if(t.docChanged){let e=t.changes.mapPos(t.startState.doc.line(i.line).from),r=t.state.doc.lineAt(e);i={line:r.number,col:i.col,off:Math.min(i.off,r.length)},n=n.map(t.changes)}},get(e,r,s){let o=ro(t,e);if(!o)return n;let a=function(t,e,i){let n=Math.min(e.line,i.line),r=Math.max(e.line,i.line),s=[];if(e.off>no||i.off>no||e.col<0||i.col<0){let o=Math.min(e.off,i.off),a=Math.max(e.off,i.off);for(let e=n;e<=r;e++){let i=t.doc.line(e);i.length<=a&&s.push(Y.range(i.from+o,i.to+a))}}else{let o=Math.min(e.col,i.col),a=Math.max(e.col,i.col);for(let e=n;e<=r;e++){let i=t.doc.line(e),n=Ut(i.text,o,t.tabSize,!0);if(n<0)s.push(Y.cursor(i.to));else{let e=Ut(i.text,a,t.tabSize);s.push(Y.range(i.from+n,i.from+e))}}}return s}(t.state,i,o);return a.length?s?Y.create(a.concat(n.ranges)):Y.create(a):n}}:null}const oo={Alt:[18,t=>!!t.altKey],Control:[17,t=>!!t.ctrlKey],Shift:[16,t=>!!t.shiftKey],Meta:[91,t=>!!t.metaKey]},ao={style:\"cursor: crosshair\"};const lo=\"-10000px\";class ho{constructor(t,e,i,n){this.facet=e,this.createTooltipView=i,this.removeTooltipView=n,this.input=t.state.facet(e),this.tooltips=this.input.filter(t=>t);let r=null;this.tooltipViews=this.tooltips.map(t=>r=i(t,r))}update(t,e){var i;let n=t.state.facet(this.facet),r=n.filter(t=>t);if(n===this.input){for(let e of this.tooltipViews)e.update&&e.update(t);return!1}let s=[],o=e?[]:null;for(let a=0;a<r.length;a++){let i=r[a],n=-1;if(i){for(let t=0;t<this.tooltips.length;t++){let e=this.tooltips[t];e&&e.create==i.create&&(n=t)}if(n<0)s[a]=this.createTooltipView(i,a?s[a-1]:null),o&&(o[a]=!!i.above);else{let i=s[a]=this.tooltipViews[n];o&&(o[a]=e[n]),i.update&&i.update(t)}}}for(let a of this.tooltipViews)s.indexOf(a)<0&&(this.removeTooltipView(a),null===(i=a.destroy)||void 0===i||i.call(a));return e&&(o.forEach((t,i)=>e[i]=t),e.length=o.length),this.input=n,this.tooltips=r,this.tooltipViews=s,!0}}function co(t){let e=t.dom.ownerDocument.documentElement;return{top:0,left:0,bottom:e.clientHeight,right:e.clientWidth}}const uo=V.define({combine:t=>{var e,i,n;return{position:we.ios?\"absolute\":(null===(e=t.find(t=>t.position))||void 0===e?void 0:e.position)||\"fixed\",parent:(null===(i=t.find(t=>t.parent))||void 0===i?void 0:i.parent)||null,tooltipSpace:(null===(n=t.find(t=>t.tooltipSpace))||void 0===n?void 0:n.tooltipSpace)||co}}}),fo=new WeakMap,Oo=Wi.fromClass(class{constructor(t){this.view=t,this.above=[],this.inView=!0,this.madeAbsolute=!1,this.lastTransaction=0,this.measureTimeout=-1;let e=t.state.facet(uo);this.position=e.position,this.parent=e.parent,this.classes=t.themeClasses,this.createContainer(),this.measureReq={read:this.readMeasure.bind(this),write:this.writeMeasure.bind(this),key:this},this.resizeObserver=\"function\"==typeof ResizeObserver?new ResizeObserver(()=>this.measureSoon()):null,this.manager=new ho(t,Qo,(t,e)=>this.createTooltip(t,e),t=>{this.resizeObserver&&this.resizeObserver.unobserve(t.dom),t.dom.remove()}),this.above=this.manager.tooltips.map(t=>!!t.above),this.intersectionObserver=\"function\"==typeof IntersectionObserver?new IntersectionObserver(t=>{Date.now()>this.lastTransaction-50&&t.length>0&&t[t.length-1].intersectionRatio<1&&this.measureSoon()},{threshold:[1]}):null,this.observeIntersection(),t.win.addEventListener(\"resize\",this.measureSoon=this.measureSoon.bind(this)),this.maybeMeasure()}createContainer(){this.parent?(this.container=document.createElement(\"div\"),this.container.style.position=\"relative\",this.container.className=this.view.themeClasses,this.parent.appendChild(this.container)):this.container=this.view.dom}observeIntersection(){if(this.intersectionObserver){this.intersectionObserver.disconnect();for(let t of this.manager.tooltipViews)this.intersectionObserver.observe(t.dom)}}measureSoon(){this.measureTimeout<0&&(this.measureTimeout=setTimeout(()=>{this.measureTimeout=-1,this.maybeMeasure()},50))}update(t){t.transactions.length&&(this.lastTransaction=Date.now());let e=this.manager.update(t,this.above);e&&this.observeIntersection();let i=e||t.geometryChanged,n=t.state.facet(uo);if(n.position!=this.position&&!this.madeAbsolute){this.position=n.position;for(let t of this.manager.tooltipViews)t.dom.style.position=this.position;i=!0}if(n.parent!=this.parent){this.parent&&this.container.remove(),this.parent=n.parent,this.createContainer();for(let t of this.manager.tooltipViews)this.container.appendChild(t.dom);i=!0}else this.parent&&this.view.themeClasses!=this.classes&&(this.classes=this.container.className=this.view.themeClasses);i&&this.maybeMeasure()}createTooltip(t,e){let i=t.create(this.view),n=e?e.dom:null;if(i.dom.classList.add(\"cm-tooltip\"),t.arrow&&!i.dom.querySelector(\".cm-tooltip > .cm-tooltip-arrow\")){let t=document.createElement(\"div\");t.className=\"cm-tooltip-arrow\",i.dom.appendChild(t)}return i.dom.style.position=this.position,i.dom.style.top=lo,i.dom.style.left=\"0px\",this.container.insertBefore(i.dom,n),i.mount&&i.mount(this.view),this.resizeObserver&&this.resizeObserver.observe(i.dom),i}destroy(){var t,e,i;this.view.win.removeEventListener(\"resize\",this.measureSoon);for(let n of this.manager.tooltipViews)n.dom.remove(),null===(t=n.destroy)||void 0===t||t.call(n);this.parent&&this.container.remove(),null===(e=this.resizeObserver)||void 0===e||e.disconnect(),null===(i=this.intersectionObserver)||void 0===i||i.disconnect(),clearTimeout(this.measureTimeout)}readMeasure(){let t=1,e=1,i=!1;if(\"fixed\"==this.position&&this.manager.tooltipViews.length){let{dom:t}=this.manager.tooltipViews[0];if(we.safari){let e=t.getBoundingClientRect();i=Math.abs(e.top+1e4)>1||Math.abs(e.left)>1}else i=!!t.offsetParent&&t.offsetParent!=this.container.ownerDocument.body}if(i||\"absolute\"==this.position)if(this.parent){let i=this.parent.getBoundingClientRect();i.width&&i.height&&(t=i.width/this.parent.offsetWidth,e=i.height/this.parent.offsetHeight)}else({scaleX:t,scaleY:e}=this.view.viewState);let n=this.view.scrollDOM.getBoundingClientRect(),r=Ji(this.view);return{visible:{left:n.left+r.left,top:n.top+r.top,right:n.right-r.right,bottom:n.bottom-r.bottom},parent:this.parent?this.container.getBoundingClientRect():this.view.dom.getBoundingClientRect(),pos:this.manager.tooltips.map((t,e)=>{let i=this.manager.tooltipViews[e];return i.getCoords?i.getCoords(t.pos):this.view.coordsAtPos(t.pos)}),size:this.manager.tooltipViews.map(({dom:t})=>t.getBoundingClientRect()),space:this.view.state.facet(uo).tooltipSpace(this.view),scaleX:t,scaleY:e,makeAbsolute:i}}writeMeasure(t){var e;if(t.makeAbsolute){this.madeAbsolute=!0,this.position=\"absolute\";for(let t of this.manager.tooltipViews)t.dom.style.position=\"absolute\"}let{visible:i,space:n,scaleX:r,scaleY:s}=t,o=[];for(let a=0;a<this.manager.tooltips.length;a++){let l=this.manager.tooltips[a],h=this.manager.tooltipViews[a],{dom:c}=h,u=t.pos[a],d=t.size[a];if(!u||!1!==l.clip&&(u.bottom<=Math.max(i.top,n.top)||u.top>=Math.min(i.bottom,n.bottom)||u.right<Math.max(i.left,n.left)-.1||u.left>Math.min(i.right,n.right)+.1)){c.style.top=lo;continue}let f=l.arrow?h.dom.querySelector(\".cm-tooltip-arrow\"):null,O=f?7:0,p=d.right-d.left,m=null!==(e=fo.get(h))&&void 0!==e?e:d.bottom-d.top,g=h.offset||go,Q=this.view.textDirection==ri.LTR,b=d.width>n.right-n.left?Q?n.left:n.right-d.width:Q?Math.max(n.left,Math.min(u.left-(f?14:0)+g.x,n.right-p)):Math.min(Math.max(n.left,u.left-p+(f?14:0)-g.x),n.right-p),v=this.above[a];!l.strictSide&&(v?u.top-m-O-g.y<n.top:u.bottom+m+O+g.y>n.bottom)&&v==n.bottom-u.bottom>u.top-n.top&&(v=this.above[a]=!v);let w=(v?u.top-n.top:n.bottom-u.bottom)-O;if(w<m&&!1!==h.resize){if(w<this.view.defaultLineHeight){c.style.top=lo;continue}fo.set(h,m),c.style.height=(m=w)/s+\"px\"}else c.style.height&&(c.style.height=\"\");let x=v?u.top-m-O-g.y:u.bottom+O+g.y,S=b+p;if(!0!==h.overlap)for(let t of o)t.left<S&&t.right>b&&t.top<x+m&&t.bottom>x&&(x=v?t.top-m-2-O:t.bottom+O+2);if(\"absolute\"==this.position?(c.style.top=(x-t.parent.top)/s+\"px\",po(c,(b-t.parent.left)/r)):(c.style.top=x/s+\"px\",po(c,b/r)),f){let t=u.left+(Q?g.x:-g.x)-(b+14-7);f.style.left=t/r+\"px\"}!0!==h.overlap&&o.push({left:b,top:x,right:S,bottom:x+m}),c.classList.toggle(\"cm-tooltip-above\",v),c.classList.toggle(\"cm-tooltip-below\",!v),h.positioned&&h.positioned(t.space)}}maybeMeasure(){if(this.manager.tooltips.length&&(this.view.inView&&this.view.requestMeasure(this.measureReq),this.inView!=this.view.inView&&(this.inView=this.view.inView,!this.inView)))for(let t of this.manager.tooltipViews)t.dom.style.top=lo}},{eventObservers:{scroll(){this.maybeMeasure()}}});function po(t,e){let i=parseInt(t.style.left,10);(isNaN(i)||Math.abs(e-i)>1)&&(t.style.left=e+\"px\")}const mo=fs.baseTheme({\".cm-tooltip\":{zIndex:500,boxSizing:\"border-box\"},\"&light .cm-tooltip\":{border:\"1px solid #bbb\",backgroundColor:\"#f5f5f5\"},\"&light .cm-tooltip-section:not(:first-child)\":{borderTop:\"1px solid #bbb\"},\"&dark .cm-tooltip\":{backgroundColor:\"#333338\",color:\"white\"},\".cm-tooltip-arrow\":{height:\"7px\",width:\"14px\",position:\"absolute\",zIndex:-1,overflow:\"hidden\",\"&:before, &:after\":{content:\"''\",position:\"absolute\",width:0,height:0,borderLeft:\"7px solid transparent\",borderRight:\"7px solid transparent\"},\".cm-tooltip-above &\":{bottom:\"-7px\",\"&:before\":{borderTop:\"7px solid #bbb\"},\"&:after\":{borderTop:\"7px solid #f5f5f5\",bottom:\"1px\"}},\".cm-tooltip-below &\":{top:\"-7px\",\"&:before\":{borderBottom:\"7px solid #bbb\"},\"&:after\":{borderBottom:\"7px solid #f5f5f5\",top:\"1px\"}}},\"&dark .cm-tooltip .cm-tooltip-arrow\":{\"&:before\":{borderTopColor:\"#333338\",borderBottomColor:\"#333338\"},\"&:after\":{borderTopColor:\"transparent\",borderBottomColor:\"transparent\"}}}),go={x:0,y:0},Qo=V.define({enables:[Oo,mo]}),bo=V.define({combine:t=>t.reduce((t,e)=>t.concat(e),[])});class vo{static create(t){return new vo(t)}constructor(t){this.view=t,this.mounted=!1,this.dom=document.createElement(\"div\"),this.dom.classList.add(\"cm-tooltip-hover\"),this.manager=new ho(t,bo,(t,e)=>this.createHostedView(t,e),t=>t.dom.remove())}createHostedView(t,e){let i=t.create(this.view);return i.dom.classList.add(\"cm-tooltip-section\"),this.dom.insertBefore(i.dom,e?e.dom.nextSibling:this.dom.firstChild),this.mounted&&i.mount&&i.mount(this.view),i}mount(t){for(let e of this.manager.tooltipViews)e.mount&&e.mount(t);this.mounted=!0}positioned(t){for(let e of this.manager.tooltipViews)e.positioned&&e.positioned(t)}update(t){this.manager.update(t)}destroy(){var t;for(let e of this.manager.tooltipViews)null===(t=e.destroy)||void 0===t||t.call(e)}passProp(t){let e;for(let i of this.manager.tooltipViews){let n=i[t];if(void 0!==n)if(void 0===e)e=n;else if(e!==n)return}return e}get offset(){return this.passProp(\"offset\")}get getCoords(){return this.passProp(\"getCoords\")}get overlap(){return this.passProp(\"overlap\")}get resize(){return this.passProp(\"resize\")}}const wo=Qo.compute([bo],t=>{let e=t.facet(bo);return 0===e.length?null:{pos:Math.min(...e.map(t=>t.pos)),end:Math.max(...e.map(t=>{var e;return null!==(e=t.end)&&void 0!==e?e:t.pos})),create:vo.create,above:e[0].above,arrow:e.some(t=>t.arrow)}});class xo{constructor(t,e,i,n,r){this.view=t,this.source=e,this.field=i,this.setHover=n,this.hoverTime=r,this.hoverTimeout=-1,this.restartTimeout=-1,this.pending=null,this.lastMove={x:0,y:0,target:t.dom,time:0},this.checkHover=this.checkHover.bind(this),t.dom.addEventListener(\"mouseleave\",this.mouseleave=this.mouseleave.bind(this)),t.dom.addEventListener(\"mousemove\",this.mousemove=this.mousemove.bind(this))}update(){this.pending&&(this.pending=null,clearTimeout(this.restartTimeout),this.restartTimeout=setTimeout(()=>this.startHover(),20))}get active(){return this.view.state.field(this.field)}checkHover(){if(this.hoverTimeout=-1,this.active.length)return;let t=Date.now()-this.lastMove.time;t<this.hoverTime?this.hoverTimeout=setTimeout(this.checkHover,this.hoverTime-t):this.startHover()}startHover(){clearTimeout(this.restartTimeout);let{view:t,lastMove:e}=this,i=t.docView.tile.nearest(e.target);if(!i)return;let n,r=1;if(i.isWidget())n=i.posAtStart;else{if(n=t.posAtCoords(e),null==n)return;let i=t.coordsAtPos(n);if(!i||e.y<i.top||e.y>i.bottom||e.x<i.left-t.defaultCharacterWidth||e.x>i.right+t.defaultCharacterWidth)return;let s=t.bidiSpans(t.state.doc.lineAt(n)).find(t=>t.from<=n&&t.to>=n),o=s&&s.dir==ri.RTL?-1:1;r=e.x<i.left?-o:o}let s=this.source(t,n,r);if(null==s?void 0:s.then){let e=this.pending={pos:n};s.then(i=>{this.pending==e&&(this.pending=null,!i||Array.isArray(i)&&!i.length||t.dispatch({effects:this.setHover.of(Array.isArray(i)?i:[i])}))},e=>Yi(t.state,e,\"hover tooltip\"))}else!s||Array.isArray(s)&&!s.length||t.dispatch({effects:this.setHover.of(Array.isArray(s)?s:[s])})}get tooltip(){let t=this.view.plugin(Oo),e=t?t.manager.tooltips.findIndex(t=>t.create==vo.create):-1;return e>-1?t.manager.tooltipViews[e]:null}mousemove(t){var e,i;this.lastMove={x:t.clientX,y:t.clientY,target:t.target,time:Date.now()},this.hoverTimeout<0&&(this.hoverTimeout=setTimeout(this.checkHover,this.hoverTime));let{active:n,tooltip:r}=this;if(n.length&&r&&!function(t,e){let i,{left:n,right:r,top:s,bottom:o}=t.getBoundingClientRect();if(i=t.querySelector(\".cm-tooltip-arrow\")){let t=i.getBoundingClientRect();s=Math.min(t.top,s),o=Math.max(t.bottom,o)}return e.clientX>=n-So&&e.clientX<=r+So&&e.clientY>=s-So&&e.clientY<=o+So}(r.dom,t)||this.pending){let{pos:r}=n[0]||this.pending,s=null!==(i=null===(e=n[0])||void 0===e?void 0:e.end)&&void 0!==i?i:r;(r==s?this.view.posAtCoords(this.lastMove)==r:function(t,e,i,n,r){let s=t.scrollDOM.getBoundingClientRect(),o=t.documentTop+t.documentPadding.top+t.contentHeight;if(s.left>n||s.right<n||s.top>r||Math.min(s.bottom,o)<r)return!1;let a=t.posAtCoords({x:n,y:r},!1);return a>=e&&a<=i}(this.view,r,s,t.clientX,t.clientY))||(this.view.dispatch({effects:this.setHover.of([])}),this.pending=null)}}mouseleave(t){clearTimeout(this.hoverTimeout),this.hoverTimeout=-1;let{active:e}=this;if(e.length){let{tooltip:e}=this;e&&e.dom.contains(t.relatedTarget)?this.watchTooltipLeave(e.dom):this.view.dispatch({effects:this.setHover.of([])})}}watchTooltipLeave(t){let e=i=>{t.removeEventListener(\"mouseleave\",e),this.active.length&&!this.view.dom.contains(i.relatedTarget)&&this.view.dispatch({effects:this.setHover.of([])})};t.addEventListener(\"mouseleave\",e)}destroy(){clearTimeout(this.hoverTimeout),clearTimeout(this.restartTimeout),this.view.dom.removeEventListener(\"mouseleave\",this.mouseleave),this.view.dom.removeEventListener(\"mousemove\",this.mousemove)}}const So=4;function yo(t,e={}){let i=gt.define(),n=N.define({create:()=>[],update(t,n){if(t.length&&(e.hideOnChange&&(n.docChanged||n.selection)?t=[]:e.hideOn&&(t=t.filter(t=>!e.hideOn(n,t))),n.docChanged)){let e=[];for(let i of t){let t=n.changes.mapPos(i.pos,-1,T.TrackDel);if(null!=t){let r=Object.assign(Object.create(null),i);r.pos=t,null!=r.end&&(r.end=n.changes.mapPos(r.end)),e.push(r)}}t=e}for(let e of n.effects)e.is(i)&&(t=e.value),e.is($o)&&(t=[]);return t},provide:t=>bo.from(t)});return{active:n,extension:[n,Wi.define(r=>new xo(r,t,n,i,e.hoverTime||300)),wo]}}function ko(t,e){let i=t.plugin(Oo);if(!i)return null;let n=i.manager.tooltips.indexOf(e);return n<0?null:i.manager.tooltipViews[n]}const $o=gt.define(),Po=V.define({combine(t){let e,i;for(let n of t)e=e||n.topContainer,i=i||n.bottomContainer;return{topContainer:e,bottomContainer:i}}});function To(t,e){let i=t.plugin(Co),n=i?i.specs.indexOf(e):-1;return n>-1?i.panels[n]:null}const Co=Wi.fromClass(class{constructor(t){this.input=t.state.facet(Ao),this.specs=this.input.filter(t=>t),this.panels=this.specs.map(e=>e(t));let e=t.state.facet(Po);this.top=new Zo(t,!0,e.topContainer),this.bottom=new Zo(t,!1,e.bottomContainer),this.top.sync(this.panels.filter(t=>t.top)),this.bottom.sync(this.panels.filter(t=>!t.top));for(let i of this.panels)i.dom.classList.add(\"cm-panel\"),i.mount&&i.mount()}update(t){let e=t.state.facet(Po);this.top.container!=e.topContainer&&(this.top.sync([]),this.top=new Zo(t.view,!0,e.topContainer)),this.bottom.container!=e.bottomContainer&&(this.bottom.sync([]),this.bottom=new Zo(t.view,!1,e.bottomContainer)),this.top.syncClasses(),this.bottom.syncClasses();let i=t.state.facet(Ao);if(i!=this.input){let e=i.filter(t=>t),n=[],r=[],s=[],o=[];for(let i of e){let e,a=this.specs.indexOf(i);a<0?(e=i(t.view),o.push(e)):(e=this.panels[a],e.update&&e.update(t)),n.push(e),(e.top?r:s).push(e)}this.specs=e,this.panels=n,this.top.sync(r),this.bottom.sync(s);for(let t of o)t.dom.classList.add(\"cm-panel\"),t.mount&&t.mount()}else for(let n of this.panels)n.update&&n.update(t)}destroy(){this.top.sync([]),this.bottom.sync([])}},{provide:t=>fs.scrollMargins.of(e=>{let i=e.plugin(t);return i&&{top:i.top.scrollMargin(),bottom:i.bottom.scrollMargin()}})});class Zo{constructor(t,e,i){this.view=t,this.top=e,this.container=i,this.dom=void 0,this.classes=\"\",this.panels=[],this.syncClasses()}sync(t){for(let e of this.panels)e.destroy&&t.indexOf(e)<0&&e.destroy();this.panels=t,this.syncDOM()}syncDOM(){if(0==this.panels.length)return void(this.dom&&(this.dom.remove(),this.dom=void 0));if(!this.dom){this.dom=document.createElement(\"div\"),this.dom.className=this.top?\"cm-panels cm-panels-top\":\"cm-panels cm-panels-bottom\",this.dom.style[this.top?\"top\":\"bottom\"]=\"0\";let t=this.container||this.view.dom;t.insertBefore(this.dom,this.top?t.firstChild:null)}let t=this.dom.firstChild;for(let e of this.panels)if(e.dom.parentNode==this.dom){for(;t!=e.dom;)t=Xo(t);t=t.nextSibling}else this.dom.insertBefore(e.dom,t);for(;t;)t=Xo(t)}scrollMargin(){return!this.dom||this.container?0:Math.max(0,this.top?this.dom.getBoundingClientRect().bottom-Math.max(0,this.view.scrollDOM.getBoundingClientRect().top):Math.min(innerHeight,this.view.scrollDOM.getBoundingClientRect().bottom)-this.dom.getBoundingClientRect().top)}syncClasses(){if(this.container&&this.classes!=this.view.themeClasses){for(let t of this.classes.split(\" \"))t&&this.container.classList.remove(t);for(let t of(this.classes=this.view.themeClasses).split(\" \"))t&&this.container.classList.add(t)}}}function Xo(t){let e=t.nextSibling;return t.remove(),e}const Ao=V.define({enables:Co});class Mo extends Xt{compare(t){return this==t||this.constructor==t.constructor&&this.eq(t)}eq(t){return!1}destroy(t){}}Mo.prototype.elementClass=\"\",Mo.prototype.toDOM=void 0,Mo.prototype.mapMode=T.TrackBefore,Mo.prototype.startSide=Mo.prototype.endSide=-1,Mo.prototype.point=!0;const Ro=V.define(),zo=V.define(),_o={class:\"\",renderEmptyElements:!1,elementStyle:\"\",markers:()=>_t.empty,lineMarker:()=>null,widgetMarker:()=>null,lineMarkerChange:null,initialSpacer:null,updateSpacer:null,domEventHandlers:{},side:\"before\"},Eo=V.define();function Yo(t){return[qo(),Eo.of({..._o,...t})]}const Lo=V.define({combine:t=>t.some(t=>t)});function qo(t){return[Vo]}const Vo=Wi.fromClass(class{constructor(t){this.view=t,this.domAfter=null,this.prevViewport=t.viewport,this.dom=document.createElement(\"div\"),this.dom.className=\"cm-gutters cm-gutters-before\",this.dom.setAttribute(\"aria-hidden\",\"true\"),this.dom.style.minHeight=this.view.contentHeight/this.view.scaleY+\"px\",this.gutters=t.state.facet(Eo).map(e=>new jo(t,e)),this.fixed=!t.state.facet(Lo);for(let e of this.gutters)\"after\"==e.config.side?this.getDOMAfter().appendChild(e.dom):this.dom.appendChild(e.dom);this.fixed&&(this.dom.style.position=\"sticky\"),this.syncGutters(!1),t.scrollDOM.insertBefore(this.dom,t.contentDOM)}getDOMAfter(){return this.domAfter||(this.domAfter=document.createElement(\"div\"),this.domAfter.className=\"cm-gutters cm-gutters-after\",this.domAfter.setAttribute(\"aria-hidden\",\"true\"),this.domAfter.style.minHeight=this.view.contentHeight/this.view.scaleY+\"px\",this.domAfter.style.position=this.fixed?\"sticky\":\"\",this.view.scrollDOM.appendChild(this.domAfter)),this.domAfter}update(t){if(this.updateGutters(t)){let e=this.prevViewport,i=t.view.viewport,n=Math.min(e.to,i.to)-Math.max(e.from,i.from);this.syncGutters(n<.8*(i.to-i.from))}if(t.geometryChanged){let t=this.view.contentHeight/this.view.scaleY+\"px\";this.dom.style.minHeight=t,this.domAfter&&(this.domAfter.style.minHeight=t)}this.view.state.facet(Lo)!=!this.fixed&&(this.fixed=!this.fixed,this.dom.style.position=this.fixed?\"sticky\":\"\",this.domAfter&&(this.domAfter.style.position=this.fixed?\"sticky\":\"\")),this.prevViewport=t.view.viewport}syncGutters(t){let e=this.dom.nextSibling;t&&(this.dom.remove(),this.domAfter&&this.domAfter.remove());let i=_t.iter(this.view.state.facet(Ro),this.view.viewport.from),n=[],r=this.gutters.map(t=>new Bo(t,this.view.viewport,-this.view.documentPadding.top));for(let s of this.view.viewportLineBlocks)if(n.length&&(n=[]),Array.isArray(s.type)){let t=!0;for(let e of s.type)if(e.type==Te.Text&&t){Do(i,n,e.from);for(let t of r)t.line(this.view,e,n);t=!1}else if(e.widget)for(let t of r)t.widget(this.view,e)}else if(s.type==Te.Text){Do(i,n,s.from);for(let t of r)t.line(this.view,s,n)}else if(s.widget)for(let t of r)t.widget(this.view,s);for(let s of r)s.finish();t&&(this.view.scrollDOM.insertBefore(this.dom,e),this.domAfter&&this.view.scrollDOM.appendChild(this.domAfter))}updateGutters(t){let e=t.startState.facet(Eo),i=t.state.facet(Eo),n=t.docChanged||t.heightChanged||t.viewportChanged||!_t.eq(t.startState.facet(Ro),t.state.facet(Ro),t.view.viewport.from,t.view.viewport.to);if(e==i)for(let r of this.gutters)r.update(t)&&(n=!0);else{n=!0;let r=[];for(let n of i){let i=e.indexOf(n);i<0?r.push(new jo(this.view,n)):(this.gutters[i].update(t),r.push(this.gutters[i]))}for(let t of this.gutters)t.dom.remove(),r.indexOf(t)<0&&t.destroy();for(let t of r)\"after\"==t.config.side?this.getDOMAfter().appendChild(t.dom):this.dom.appendChild(t.dom);this.gutters=r}return n}destroy(){for(let t of this.gutters)t.destroy();this.dom.remove(),this.domAfter&&this.domAfter.remove()}},{provide:t=>fs.scrollMargins.of(e=>{let i=e.plugin(t);if(!i||0==i.gutters.length||!i.fixed)return null;let n=i.dom.offsetWidth*e.scaleX,r=i.domAfter?i.domAfter.offsetWidth*e.scaleX:0;return e.textDirection==ri.LTR?{left:n,right:r}:{right:n,left:r}})});function Wo(t){return Array.isArray(t)?t:[t]}function Do(t,e,i){for(;t.value&&t.from<=i;)t.from==i&&e.push(t.value),t.next()}class Bo{constructor(t,e,i){this.gutter=t,this.height=i,this.i=0,this.cursor=_t.iter(t.markers,e.from)}addElement(t,e,i){let{gutter:n}=this,r=(e.top-this.height)/t.scaleY,s=e.height/t.scaleY;if(this.i==n.elements.length){let e=new Io(t,s,r,i);n.elements.push(e),n.dom.appendChild(e.dom)}else n.elements[this.i].update(t,s,r,i);this.height=e.bottom,this.i++}line(t,e,i){let n=[];Do(this.cursor,n,e.from),i.length&&(n=n.concat(i));let r=this.gutter.config.lineMarker(t,e,n);r&&n.unshift(r);let s=this.gutter;(0!=n.length||s.config.renderEmptyElements)&&this.addElement(t,e,n)}widget(t,e){let i=this.gutter.config.widgetMarker(t,e.widget,e),n=i?[i]:null;for(let r of t.state.facet(zo)){let i=r(t,e.widget,e);i&&(n||(n=[])).push(i)}n&&this.addElement(t,e,n)}finish(){let t=this.gutter;for(;t.elements.length>this.i;){let e=t.elements.pop();t.dom.removeChild(e.dom),e.destroy()}}}class jo{constructor(t,e){this.view=t,this.config=e,this.elements=[],this.spacer=null,this.dom=document.createElement(\"div\"),this.dom.className=\"cm-gutter\"+(this.config.class?\" \"+this.config.class:\"\");for(let i in e.domEventHandlers)this.dom.addEventListener(i,n=>{let r,s=n.target;if(s!=this.dom&&this.dom.contains(s)){for(;s.parentNode!=this.dom;)s=s.parentNode;let t=s.getBoundingClientRect();r=(t.top+t.bottom)/2}else r=n.clientY;let o=t.lineBlockAtHeight(r-t.documentTop);e.domEventHandlers[i](t,o,n)&&n.preventDefault()});this.markers=Wo(e.markers(t)),e.initialSpacer&&(this.spacer=new Io(t,0,0,[e.initialSpacer(t)]),this.dom.appendChild(this.spacer.dom),this.spacer.dom.style.cssText+=\"visibility: hidden; pointer-events: none\")}update(t){let e=this.markers;if(this.markers=Wo(this.config.markers(t.view)),this.spacer&&this.config.updateSpacer){let e=this.config.updateSpacer(this.spacer.markers[0],t);e!=this.spacer.markers[0]&&this.spacer.update(t.view,0,0,[e])}let i=t.view.viewport;return!_t.eq(this.markers,e,i.from,i.to)||!!this.config.lineMarkerChange&&this.config.lineMarkerChange(t)}destroy(){for(let t of this.elements)t.destroy()}}class Io{constructor(t,e,i,n){this.height=-1,this.above=0,this.markers=[],this.dom=document.createElement(\"div\"),this.dom.className=\"cm-gutterElement\",this.update(t,e,i,n)}update(t,e,i,n){this.height!=e&&(this.height=e,this.dom.style.height=e+\"px\"),this.above!=i&&(this.dom.style.marginTop=(this.above=i)?i+\"px\":\"\"),function(t,e){if(t.length!=e.length)return!1;for(let i=0;i<t.length;i++)if(!t[i].compare(e[i]))return!1;return!0}(this.markers,n)||this.setMarkers(t,n)}setMarkers(t,e){let i=\"cm-gutterElement\",n=this.dom.firstChild;for(let r=0,s=0;;){let o=s,a=r<e.length?e[r++]:null,l=!1;if(a){let t=a.elementClass;t&&(i+=\" \"+t);for(let e=s;e<this.markers.length;e++)if(this.markers[e].compare(a)){o=e,l=!0;break}}else o=this.markers.length;for(;s<o;){let t=this.markers[s++];if(t.toDOM){t.destroy(n);let e=n.nextSibling;n.remove(),n=e}}if(!a)break;a.toDOM&&(l?n=n.nextSibling:this.dom.insertBefore(a.toDOM(t),n)),l&&s++}this.dom.className=i,this.markers=e}destroy(){this.setMarkers(null,[])}}const Go=V.define(),No=V.define(),Uo=V.define({combine:t=>Zt(t,{formatNumber:String,domEventHandlers:{}},{domEventHandlers(t,e){let i=Object.assign({},t);for(let n in e){let t=i[n],r=e[n];i[n]=t?(e,i,n)=>t(e,i,n)||r(e,i,n):r}return i}})});class Ho extends Mo{constructor(t){super(),this.number=t}eq(t){return this.number==t.number}toDOM(){return document.createTextNode(this.number)}}function Fo(t,e){return t.state.facet(Uo).formatNumber(e,t.state)}const Ko=Eo.compute([Uo],t=>({class:\"cm-lineNumbers\",renderEmptyElements:!1,markers:t=>t.state.facet(Go),lineMarker:(t,e,i)=>i.some(t=>t.toDOM)?null:new Ho(Fo(t,t.state.doc.lineAt(e.from).number)),widgetMarker:(t,e,i)=>{for(let n of t.state.facet(No)){let r=n(t,e,i);if(r)return r}return null},lineMarkerChange:t=>t.startState.facet(Uo)!=t.state.facet(Uo),initialSpacer:t=>new Ho(Fo(t,Jo(t.state.doc.lines))),updateSpacer(t,e){let i=Fo(e.view,Jo(e.view.state.doc.lines));return i==t.number?t:new Ho(i)},domEventHandlers:t.facet(Uo).domEventHandlers,side:\"before\"}));function Jo(t){let e=9;for(;e<t;)e=10*e+9;return e}const ta=new class extends Mo{constructor(){super(...arguments),this.elementClass=\"cm-activeLineGutter\"}},ea=Ro.compute([\"selection\"],t=>{let e=[],i=-1;for(let n of t.selection.ranges){let r=t.doc.lineAt(n.head).from;r>i&&(i=r,e.push(ta.range(r)))}return _t.of(e)});const ia=1024;let na=0;class ra{constructor(t,e){this.from=t,this.to=e}}class sa{constructor(t={}){this.id=na++,this.perNode=!!t.perNode,this.deserialize=t.deserialize||(()=>{throw new Error(\"This node type doesn't define a deserialize function\")}),this.combine=t.combine||null}add(t){if(this.perNode)throw new RangeError(\"Can't add per-node props to node types\");return\"function\"!=typeof t&&(t=la.match(t)),e=>{let i=t(e);return void 0===i?null:[this,i]}}}sa.closedBy=new sa({deserialize:t=>t.split(\" \")}),sa.openedBy=new sa({deserialize:t=>t.split(\" \")}),sa.group=new sa({deserialize:t=>t.split(\" \")}),sa.isolate=new sa({deserialize:t=>{if(t&&\"rtl\"!=t&&\"ltr\"!=t&&\"auto\"!=t)throw new RangeError(\"Invalid value for isolate: \"+t);return t||\"auto\"}}),sa.contextHash=new sa({perNode:!0}),sa.lookAhead=new sa({perNode:!0}),sa.mounted=new sa({perNode:!0});class oa{constructor(t,e,i,n=!1){this.tree=t,this.overlay=e,this.parser=i,this.bracketed=n}static get(t){return t&&t.props&&t.props[sa.mounted.id]}}const aa=Object.create(null);class la{constructor(t,e,i,n=0){this.name=t,this.props=e,this.id=i,this.flags=n}static define(t){let e=t.props&&t.props.length?Object.create(null):aa,i=(t.top?1:0)|(t.skipped?2:0)|(t.error?4:0)|(null==t.name?8:0),n=new la(t.name||\"\",e,t.id,i);if(t.props)for(let r of t.props)if(Array.isArray(r)||(r=r(n)),r){if(r[0].perNode)throw new RangeError(\"Can't store a per-node prop on a node type\");e[r[0].id]=r[1]}return n}prop(t){return this.props[t.id]}get isTop(){return(1&this.flags)>0}get isSkipped(){return(2&this.flags)>0}get isError(){return(4&this.flags)>0}get isAnonymous(){return(8&this.flags)>0}is(t){if(\"string\"==typeof t){if(this.name==t)return!0;let e=this.prop(sa.group);return!!e&&e.indexOf(t)>-1}return this.id==t}static match(t){let e=Object.create(null);for(let i in t)for(let n of i.split(\" \"))e[n]=t[i];return t=>{for(let i=t.prop(sa.group),n=-1;n<(i?i.length:0);n++){let r=e[n<0?t.name:i[n]];if(r)return r}}}}la.none=new la(\"\",Object.create(null),0,8);class ha{constructor(t){this.types=t;for(let e=0;e<t.length;e++)if(t[e].id!=e)throw new RangeError(\"Node type ids should correspond to array positions when creating a node set\")}extend(...t){let e=[];for(let i of this.types){let n=null;for(let e of t){let t=e(i);if(t){n||(n=Object.assign({},i.props));let e=t[1],r=t[0];r.combine&&r.id in n&&(e=r.combine(n[r.id],e)),n[r.id]=e}}e.push(n?new la(i.name,n,i.id,i.flags):i)}return new ha(e)}}const ca=new WeakMap,ua=new WeakMap;var da,fa;(fa=da||(da={}))[fa.ExcludeBuffers=1]=\"ExcludeBuffers\",fa[fa.IncludeAnonymous=2]=\"IncludeAnonymous\",fa[fa.IgnoreMounts=4]=\"IgnoreMounts\",fa[fa.IgnoreOverlays=8]=\"IgnoreOverlays\",fa[fa.EnterBracketed=16]=\"EnterBracketed\";class Oa{constructor(t,e,i,n,r){if(this.type=t,this.children=e,this.positions=i,this.length=n,this.props=null,r&&r.length){this.props=Object.create(null);for(let[t,e]of r)this.props[\"number\"==typeof t?t:t.id]=e}}toString(){let t=oa.get(this);if(t&&!t.overlay)return t.tree.toString();let e=\"\";for(let i of this.children){let t=i.toString();t&&(e&&(e+=\",\"),e+=t)}return this.type.name?(/\\W/.test(this.type.name)&&!this.type.isError?JSON.stringify(this.type.name):this.type.name)+(e.length?\"(\"+e+\")\":\"\"):e}cursor(t=0){return new Pa(this.topNode,t)}cursorAt(t,e=0,i=0){let n=ca.get(this)||this.topNode,r=new Pa(n);return r.moveTo(t,e),ca.set(this,r._tree),r}get topNode(){return new va(this,0,0,null)}resolve(t,e=0){let i=Qa(ca.get(this)||this.topNode,t,e,!1);return ca.set(this,i),i}resolveInner(t,e=0){let i=Qa(ua.get(this)||this.topNode,t,e,!0);return ua.set(this,i),i}resolveStack(t,e=0){return function(t,e,i){let n=t.resolveInner(e,i),r=null;for(let s=n instanceof va?n:n.context.parent;s;s=s.parent)if(s.index<0){let t=s.parent;(r||(r=[n])).push(t.resolve(e,i)),s=t}else{let t=oa.get(s.tree);if(t&&t.overlay&&t.overlay[0].from<=e&&t.overlay[t.overlay.length-1].to>=e){let o=new va(t.tree,t.overlay[0].from+s.from,-1,s);(r||(r=[n])).push(Qa(o,e,i,!1))}}return r?ka(r):n}(this,t,e)}iterate(t){let{enter:e,leave:i,from:n=0,to:r=this.length}=t,s=t.mode||0,o=(s&da.IncludeAnonymous)>0;for(let a=this.cursor(s|da.IncludeAnonymous);;){let t=!1;if(a.from<=r&&a.to>=n&&(!o&&a.type.isAnonymous||!1!==e(a))){if(a.firstChild())continue;t=!0}for(;t&&i&&(o||!a.type.isAnonymous)&&i(a),!a.nextSibling();){if(!a.parent())return;t=!0}}}prop(t){return t.perNode?this.props?this.props[t.id]:void 0:this.type.prop(t)}get propValues(){let t=[];if(this.props)for(let e in this.props)t.push([+e,this.props[e]]);return t}balance(t={}){return this.children.length<=8?this:Xa(la.none,this.children,this.positions,0,this.children.length,0,this.length,(t,e,i)=>new Oa(this.type,t,e,i,this.propValues),t.makeTree||((t,e,i)=>new Oa(la.none,t,e,i)))}static build(t){return function(t){var e;let{buffer:i,nodeSet:n,maxBufferLength:r=ia,reused:s=[],minRepeatType:o=n.types.length}=t,a=Array.isArray(i)?new pa(i,i.length):i,l=n.types,h=0,c=0;function u(t,e,i,Q,b,v){let{id:w,start:x,end:S,size:y}=a,k=c,$=h;if(y<0){if(a.next(),-1==y){let e=s[w];return i.push(e),void Q.push(x-t)}if(-3==y)return void(h=w);if(-4==y)return void(c=w);throw new RangeError(`Unrecognized record size: ${y}`)}let P,T,C=l[w],Z=x-t;if(S-x<=r&&(T=m(a.pos-e,b))){let e=new Uint16Array(T.size-T.skip),i=a.pos-T.size,r=e.length;for(;a.pos>i;)r=g(T.start,e,r);P=new ma(e,S-T.start,n),Z=T.start-t}else{let t=a.pos-y;a.next();let e=[],i=[],n=w>=o?w:-1,s=0,l=S;for(;a.pos>t;)n>=0&&a.id==n&&a.size>=0?(a.end<=l-r&&(O(e,i,x,s,a.end,l,n,k,$),s=e.length,l=a.end),a.next()):v>2500?d(x,t,e,i):u(x,t,e,i,n,v+1);if(n>=0&&s>0&&s<e.length&&O(e,i,x,s,x,l,n,k,$),e.reverse(),i.reverse(),n>-1&&s>0){let t=f(C,$);P=Xa(C,e,i,0,e.length,0,S-x,t,t)}else P=p(C,e,i,S-x,k-S,$)}i.push(P),Q.push(Z)}function d(t,e,i,s){let o=[],l=0,h=-1;for(;a.pos>e;){let{id:t,start:e,end:i,size:n}=a;if(n>4)a.next();else{if(h>-1&&e<h)break;h<0&&(h=i-r),o.push(t,e,i),l++,a.next()}}if(l){let e=new Uint16Array(4*l),r=o[o.length-2];for(let t=o.length-3,i=0;t>=0;t-=3)e[i++]=o[t],e[i++]=o[t+1]-r,e[i++]=o[t+2]-r,e[i++]=i;i.push(new ma(e,o[2]-r,n)),s.push(r-t)}}function f(t,e){return(i,n,r)=>{let s,o,a=0,l=i.length-1;if(l>=0&&(s=i[l])instanceof Oa){if(!l&&s.type==t&&s.length==r)return s;(o=s.prop(sa.lookAhead))&&(a=n[l]+s.length+o)}return p(t,i,n,r,a,e)}}function O(t,e,i,r,s,o,a,l,h){let c=[],u=[];for(;t.length>r;)c.push(t.pop()),u.push(e.pop()+i-s);t.push(p(n.types[a],c,u,o-s,l-o,h)),e.push(s-i)}function p(t,e,i,n,r,s,o){if(s){let t=[sa.contextHash,s];o=o?[t].concat(o):[t]}if(r>25){let t=[sa.lookAhead,r];o=o?[t].concat(o):[t]}return new Oa(t,e,i,n,o)}function m(t,e){let i=a.fork(),n=0,s=0,l=0,h=i.end-r,c={size:0,start:0,skip:0};t:for(let r=i.pos-t;i.pos>r;){let t=i.size;if(i.id==e&&t>=0){c.size=n,c.start=s,c.skip=l,l+=4,n+=4,i.next();continue}let a=i.pos-t;if(t<0||a<r||i.start<h)break;let u=i.id>=o?4:0,d=i.start;for(i.next();i.pos>a;){if(i.size<0){if(-3!=i.size&&-4!=i.size)break t;u+=4}else i.id>=o&&(u+=4);i.next()}s=d,n+=t,l+=u}return(e<0||n==t)&&(c.size=n,c.start=s,c.skip=l),c.size>4?c:void 0}function g(t,e,i){let{id:n,start:r,end:s,size:l}=a;if(a.next(),l>=0&&n<o){let o=i;if(l>4){let n=a.pos-(l-4);for(;a.pos>n;)i=g(t,e,i)}e[--i]=o,e[--i]=s-t,e[--i]=r-t,e[--i]=n}else-3==l?h=n:-4==l&&(c=n);return i}let Q=[],b=[];for(;a.pos>0;)u(t.start||0,t.bufferStart||0,Q,b,-1,0);let v=null!==(e=t.length)&&void 0!==e?e:Q.length?b[0]+Q[0].length:0;return new Oa(l[t.topID],Q.reverse(),b.reverse(),v)}(t)}}Oa.empty=new Oa(la.none,[],[],0);class pa{constructor(t,e){this.buffer=t,this.index=e}get id(){return this.buffer[this.index-4]}get start(){return this.buffer[this.index-3]}get end(){return this.buffer[this.index-2]}get size(){return this.buffer[this.index-1]}get pos(){return this.index}next(){this.index-=4}fork(){return new pa(this.buffer,this.index)}}class ma{constructor(t,e,i){this.buffer=t,this.length=e,this.set=i}get type(){return la.none}toString(){let t=[];for(let e=0;e<this.buffer.length;)t.push(this.childString(e)),e=this.buffer[e+3];return t.join(\",\")}childString(t){let e=this.buffer[t],i=this.buffer[t+3],n=this.set.types[e],r=n.name;if(/\\W/.test(r)&&!n.isError&&(r=JSON.stringify(r)),i==(t+=4))return r;let s=[];for(;t<i;)s.push(this.childString(t)),t=this.buffer[t+3];return r+\"(\"+s.join(\",\")+\")\"}findChild(t,e,i,n,r){let{buffer:s}=this,o=-1;for(let a=t;a!=e&&!(ga(r,n,s[a+1],s[a+2])&&(o=a,i>0));a=s[a+3]);return o}slice(t,e,i){let n=this.buffer,r=new Uint16Array(e-t),s=0;for(let o=t,a=0;o<e;){r[a++]=n[o++],r[a++]=n[o++]-i;let e=r[a++]=n[o++]-i;r[a++]=n[o++]-t,s=Math.max(s,e)}return new ma(r,s,this.set)}}function ga(t,e,i,n){switch(t){case-2:return i<e;case-1:return n>=e&&i<e;case 0:return i<e&&n>e;case 1:return i<=e&&n>e;case 2:return n>e;case 4:return!0}}function Qa(t,e,i,n){for(var r;t.from==t.to||(i<1?t.from>=e:t.from>e)||(i>-1?t.to<=e:t.to<e);){let e=!n&&t instanceof va&&t.index<0?null:t.parent;if(!e)return t;t=e}let s=n?0:da.IgnoreOverlays;if(n)for(let o=t,a=o.parent;a;o=a,a=o.parent)o instanceof va&&o.index<0&&(null===(r=a.enter(e,i,s))||void 0===r?void 0:r.from)!=o.from&&(t=a);for(;;){let n=t.enter(e,i,s);if(!n)return t;t=n}}class ba{cursor(t=0){return new Pa(this,t)}getChild(t,e=null,i=null){let n=wa(this,t,e,i);return n.length?n[0]:null}getChildren(t,e=null,i=null){return wa(this,t,e,i)}resolve(t,e=0){return Qa(this,t,e,!1)}resolveInner(t,e=0){return Qa(this,t,e,!0)}matchContext(t){return xa(this.parent,t)}enterUnfinishedNodesBefore(t){let e=this.childBefore(t),i=this;for(;e;){let t=e.lastChild;if(!t||t.to!=e.to)break;t.type.isError&&t.from==t.to?(i=e,e=t.prevSibling):e=t}return i}get node(){return this}get next(){return this.parent}}class va extends ba{constructor(t,e,i,n){super(),this._tree=t,this.from=e,this.index=i,this._parent=n}get type(){return this._tree.type}get name(){return this._tree.type.name}get to(){return this.from+this._tree.length}nextChild(t,e,i,n,r=0){var s;for(let o=this;;){for(let{children:a,positions:l}=o._tree,h=e>0?a.length:-1;t!=h;t+=e){let h=a[t],c=l[t]+o.from;if(r&da.EnterBracketed&&h instanceof Oa&&null===(null===(s=oa.get(h))||void 0===s?void 0:s.overlay)&&(c>=i||c+h.length<=i)||ga(n,i,c,c+h.length))if(h instanceof ma){if(r&da.ExcludeBuffers)continue;let s=h.findChild(0,h.buffer.length,e,i-c,n);if(s>-1)return new ya(new Sa(o,h,t,c),null,s)}else if(r&da.IncludeAnonymous||!h.type.isAnonymous||Ta(h)){let s;if(!(r&da.IgnoreMounts)&&(s=oa.get(h))&&!s.overlay)return new va(s.tree,c,t,o);let a=new va(h,c,t,o);return r&da.IncludeAnonymous||!a.type.isAnonymous?a:a.nextChild(e<0?h.children.length-1:0,e,i,n,r)}}if(r&da.IncludeAnonymous||!o.type.isAnonymous)return null;if(t=o.index>=0?o.index+e:e<0?-1:o._parent._tree.children.length,o=o._parent,!o)return null}}get firstChild(){return this.nextChild(0,1,0,4)}get lastChild(){return this.nextChild(this._tree.children.length-1,-1,0,4)}childAfter(t){return this.nextChild(0,1,t,2)}childBefore(t){return this.nextChild(this._tree.children.length-1,-1,t,-2)}prop(t){return this._tree.prop(t)}enter(t,e,i=0){let n;if(!(i&da.IgnoreOverlays)&&(n=oa.get(this._tree))&&n.overlay){let r=t-this.from,s=i&da.EnterBracketed&&n.bracketed;for(let{from:t,to:i}of n.overlay)if((e>0||s?t<=r:t<r)&&(e<0||s?i>=r:i>r))return new va(n.tree,n.overlay[0].from+this.from,-1,this)}return this.nextChild(0,1,t,e,i)}nextSignificantParent(){let t=this;for(;t.type.isAnonymous&&t._parent;)t=t._parent;return t}get parent(){return this._parent?this._parent.nextSignificantParent():null}get nextSibling(){return this._parent&&this.index>=0?this._parent.nextChild(this.index+1,1,0,4):null}get prevSibling(){return this._parent&&this.index>=0?this._parent.nextChild(this.index-1,-1,0,4):null}get tree(){return this._tree}toTree(){return this._tree}toString(){return this._tree.toString()}}function wa(t,e,i,n){let r=t.cursor(),s=[];if(!r.firstChild())return s;if(null!=i)for(let o=!1;!o;)if(o=r.type.is(i),!r.nextSibling())return s;for(;;){if(null!=n&&r.type.is(n))return s;if(r.type.is(e)&&s.push(r.node),!r.nextSibling())return null==n?s:[]}}function xa(t,e,i=e.length-1){for(let n=t;i>=0;n=n.parent){if(!n)return!1;if(!n.type.isAnonymous){if(e[i]&&e[i]!=n.name)return!1;i--}}return!0}class Sa{constructor(t,e,i,n){this.parent=t,this.buffer=e,this.index=i,this.start=n}}class ya extends ba{get name(){return this.type.name}get from(){return this.context.start+this.context.buffer.buffer[this.index+1]}get to(){return this.context.start+this.context.buffer.buffer[this.index+2]}constructor(t,e,i){super(),this.context=t,this._parent=e,this.index=i,this.type=t.buffer.set.types[t.buffer.buffer[i]]}child(t,e,i){let{buffer:n}=this.context,r=n.findChild(this.index+4,n.buffer[this.index+3],t,e-this.context.start,i);return r<0?null:new ya(this.context,this,r)}get firstChild(){return this.child(1,0,4)}get lastChild(){return this.child(-1,0,4)}childAfter(t){return this.child(1,t,2)}childBefore(t){return this.child(-1,t,-2)}prop(t){return this.type.prop(t)}enter(t,e,i=0){if(i&da.ExcludeBuffers)return null;let{buffer:n}=this.context,r=n.findChild(this.index+4,n.buffer[this.index+3],e>0?1:-1,t-this.context.start,e);return r<0?null:new ya(this.context,this,r)}get parent(){return this._parent||this.context.parent.nextSignificantParent()}externalSibling(t){return this._parent?null:this.context.parent.nextChild(this.context.index+t,t,0,4)}get nextSibling(){let{buffer:t}=this.context,e=t.buffer[this.index+3];return e<(this._parent?t.buffer[this._parent.index+3]:t.buffer.length)?new ya(this.context,this._parent,e):this.externalSibling(1)}get prevSibling(){let{buffer:t}=this.context,e=this._parent?this._parent.index+4:0;return this.index==e?this.externalSibling(-1):new ya(this.context,this._parent,t.findChild(e,this.index,-1,0,4))}get tree(){return null}toTree(){let t=[],e=[],{buffer:i}=this.context,n=this.index+4,r=i.buffer[this.index+3];if(r>n){let s=i.buffer[this.index+1];t.push(i.slice(n,r,s)),e.push(0)}return new Oa(this.type,t,e,this.to-this.from)}toString(){return this.context.buffer.childString(this.index)}}function ka(t){if(!t.length)return null;let e=0,i=t[0];for(let s=1;s<t.length;s++){let n=t[s];(n.from>i.from||n.to<i.to)&&(i=n,e=s)}let n=i instanceof va&&i.index<0?null:i.parent,r=t.slice();return n?r[e]=n:r.splice(e,1),new $a(r,i)}class $a{constructor(t,e){this.heads=t,this.node=e}get next(){return ka(this.heads)}}class Pa{get name(){return this.type.name}constructor(t,e=0){if(this.buffer=null,this.stack=[],this.index=0,this.bufferNode=null,this.mode=e&~da.EnterBracketed,t instanceof va)this.yieldNode(t);else{this._tree=t.context.parent,this.buffer=t.context;for(let e=t._parent;e;e=e._parent)this.stack.unshift(e.index);this.bufferNode=t,this.yieldBuf(t.index)}}yieldNode(t){return!!t&&(this._tree=t,this.type=t.type,this.from=t.from,this.to=t.to,!0)}yieldBuf(t,e){this.index=t;let{start:i,buffer:n}=this.buffer;return this.type=e||n.set.types[n.buffer[t]],this.from=i+n.buffer[t+1],this.to=i+n.buffer[t+2],!0}yield(t){return!!t&&(t instanceof va?(this.buffer=null,this.yieldNode(t)):(this.buffer=t.context,this.yieldBuf(t.index,t.type)))}toString(){return this.buffer?this.buffer.buffer.childString(this.index):this._tree.toString()}enterChild(t,e,i){if(!this.buffer)return this.yield(this._tree.nextChild(t<0?this._tree._tree.children.length-1:0,t,e,i,this.mode));let{buffer:n}=this.buffer,r=n.findChild(this.index+4,n.buffer[this.index+3],t,e-this.buffer.start,i);return!(r<0)&&(this.stack.push(this.index),this.yieldBuf(r))}firstChild(){return this.enterChild(1,0,4)}lastChild(){return this.enterChild(-1,0,4)}childAfter(t){return this.enterChild(1,t,2)}childBefore(t){return this.enterChild(-1,t,-2)}enter(t,e,i=this.mode){return this.buffer?!(i&da.ExcludeBuffers)&&this.enterChild(1,t,e):this.yield(this._tree.enter(t,e,i))}parent(){if(!this.buffer)return this.yieldNode(this.mode&da.IncludeAnonymous?this._tree._parent:this._tree.parent);if(this.stack.length)return this.yieldBuf(this.stack.pop());let t=this.mode&da.IncludeAnonymous?this.buffer.parent:this.buffer.parent.nextSignificantParent();return this.buffer=null,this.yieldNode(t)}sibling(t){if(!this.buffer)return!!this._tree._parent&&this.yield(this._tree.index<0?null:this._tree._parent.nextChild(this._tree.index+t,t,0,4,this.mode));let{buffer:e}=this.buffer,i=this.stack.length-1;if(t<0){let t=i<0?0:this.stack[i]+4;if(this.index!=t)return this.yieldBuf(e.findChild(t,this.index,-1,0,4))}else{let t=e.buffer[this.index+3];if(t<(i<0?e.buffer.length:e.buffer[this.stack[i]+3]))return this.yieldBuf(t)}return i<0&&this.yield(this.buffer.parent.nextChild(this.buffer.index+t,t,0,4,this.mode))}nextSibling(){return this.sibling(1)}prevSibling(){return this.sibling(-1)}atLastNode(t){let e,i,{buffer:n}=this;if(n){if(t>0){if(this.index<n.buffer.buffer.length)return!1}else for(let t=0;t<this.index;t++)if(n.buffer.buffer[t+3]<this.index)return!1;({index:e,parent:i}=n)}else({index:e,_parent:i}=this._tree);for(;i;({index:e,_parent:i}=i))if(e>-1)for(let n=e+t,r=t<0?-1:i._tree.children.length;n!=r;n+=t){let t=i._tree.children[n];if(this.mode&da.IncludeAnonymous||t instanceof ma||!t.type.isAnonymous||Ta(t))return!1}return!0}move(t,e){if(e&&this.enterChild(t,0,4))return!0;for(;;){if(this.sibling(t))return!0;if(this.atLastNode(t)||!this.parent())return!1}}next(t=!0){return this.move(1,t)}prev(t=!0){return this.move(-1,t)}moveTo(t,e=0){for(;(this.from==this.to||(e<1?this.from>=t:this.from>t)||(e>-1?this.to<=t:this.to<t))&&this.parent(););for(;this.enterChild(1,t,e););return this}get node(){if(!this.buffer)return this._tree;let t=this.bufferNode,e=null,i=0;if(t&&t.context==this.buffer)t:for(let n=this.index,r=this.stack.length;r>=0;){for(let s=t;s;s=s._parent)if(s.index==n){if(n==this.index)return s;e=s,i=r+1;break t}n=this.stack[--r]}for(let n=i;n<this.stack.length;n++)e=new ya(this.buffer,e,this.stack[n]);return this.bufferNode=new ya(this.buffer,e,this.index)}get tree(){return this.buffer?null:this._tree._tree}iterate(t,e){for(let i=0;;){let n=!1;if(this.type.isAnonymous||!1!==t(this)){if(this.firstChild()){i++;continue}this.type.isAnonymous||(n=!0)}for(;;){if(n&&e&&e(this),n=this.type.isAnonymous,!i)return;if(this.nextSibling())break;this.parent(),i--,n=!0}}}matchContext(t){if(!this.buffer)return xa(this.node.parent,t);let{buffer:e}=this.buffer,{types:i}=e.set;for(let n=t.length-1,r=this.stack.length-1;n>=0;r--){if(r<0)return xa(this._tree,t,n);let s=i[e.buffer[this.stack[r]]];if(!s.isAnonymous){if(t[n]&&t[n]!=s.name)return!1;n--}}return!0}}function Ta(t){return t.children.some(t=>t instanceof ma||!t.type.isAnonymous||Ta(t))}const Ca=new WeakMap;function Za(t,e){if(!t.isAnonymous||e instanceof ma||e.type!=t)return 1;let i=Ca.get(e);if(null==i){i=1;for(let n of e.children){if(n.type!=t||!(n instanceof Oa)){i=1;break}i+=Za(t,n)}Ca.set(e,i)}return i}function Xa(t,e,i,n,r,s,o,a,l){let h=0;for(let f=n;f<r;f++)h+=Za(t,e[f]);let c=Math.ceil(1.5*h/8),u=[],d=[];return function e(i,n,r,o,a){for(let h=r;h<o;){let r=h,f=n[h],O=Za(t,i[h]);for(h++;h<o;h++){let e=Za(t,i[h]);if(O+e>=c)break;O+=e}if(h==r+1){if(O>c){let t=i[r];e(t.children,t.positions,0,t.children.length,n[r]+a);continue}u.push(i[r])}else{let e=n[h-1]+i[h-1].length-f;u.push(Xa(t,i,n,r,h,f,e,null,l))}d.push(f+a-s)}}(e,i,n,r,0),(a||l)(u,d,o)}class Aa{constructor(){this.map=new WeakMap}setBuffer(t,e,i){let n=this.map.get(t);n||this.map.set(t,n=new Map),n.set(e,i)}getBuffer(t,e){let i=this.map.get(t);return i&&i.get(e)}set(t,e){t instanceof ya?this.setBuffer(t.context.buffer,t.index,e):t instanceof va&&this.map.set(t.tree,e)}get(t){return t instanceof ya?this.getBuffer(t.context.buffer,t.index):t instanceof va?this.map.get(t.tree):void 0}cursorSet(t,e){t.buffer?this.setBuffer(t.buffer.buffer,t.index,e):this.map.set(t.tree,e)}cursorGet(t){return t.buffer?this.getBuffer(t.buffer.buffer,t.index):this.map.get(t.tree)}}class Ma{constructor(t,e,i,n,r=!1,s=!1){this.from=t,this.to=e,this.tree=i,this.offset=n,this.open=(r?1:0)|(s?2:0)}get openStart(){return(1&this.open)>0}get openEnd(){return(2&this.open)>0}static addTree(t,e=[],i=!1){let n=[new Ma(0,t.length,t,0,!1,i)];for(let r of e)r.to>t.length&&n.push(r);return n}static applyChanges(t,e,i=128){if(!e.length)return t;let n=[],r=1,s=t.length?t[0]:null;for(let o=0,a=0,l=0;;o++){let h=o<e.length?e[o]:null,c=h?h.fromA:1e9;if(c-a>=i)for(;s&&s.from<c;){let e=s;if(a>=e.from||c<=e.to||l){let t=Math.max(e.from,a)-l,i=Math.min(e.to,c)-l;e=t>=i?null:new Ma(t,i,e.tree,e.offset+l,o>0,!!h)}if(e&&n.push(e),s.to>c)break;s=r<t.length?t[r++]:null}if(!h)break;a=h.toA,l=h.toA-h.toB}return n}}class Ra{startParse(t,e,i){return\"string\"==typeof t&&(t=new za(t)),i=i?i.length?i.map(t=>new ra(t.from,t.to)):[new ra(0,0)]:[new ra(0,t.length)],this.createParse(t,e||[],i)}parse(t,e,i){let n=this.startParse(t,e,i);for(;;){let t=n.advance();if(t)return t}}}class za{constructor(t){this.string=t}get length(){return this.string.length}chunk(t){return this.string.slice(t)}get lineChunks(){return!1}read(t,e){return this.string.slice(t,e)}}function _a(t){return(e,i,n,r)=>new Va(e,t,i,n,r)}class Ea{constructor(t,e,i,n,r,s){this.parser=t,this.parse=e,this.overlay=i,this.bracketed=n,this.target=r,this.from=s}}function Ya(t){if(!t.length||t.some(t=>t.from>=t.to))throw new RangeError(\"Invalid inner parse ranges given: \"+JSON.stringify(t))}class La{constructor(t,e,i,n,r,s,o,a){this.parser=t,this.predicate=e,this.mounts=i,this.index=n,this.start=r,this.bracketed=s,this.target=o,this.prev=a,this.depth=0,this.ranges=[]}}const qa=new sa({perNode:!0});class Va{constructor(t,e,i,n,r){this.nest=e,this.input=i,this.fragments=n,this.ranges=r,this.inner=[],this.innerDone=0,this.baseTree=null,this.stoppedAt=null,this.baseParse=t}advance(){if(this.baseParse){let t=this.baseParse.advance();if(!t)return null;if(this.baseParse=null,this.baseTree=t,this.startInner(),null!=this.stoppedAt)for(let e of this.inner)e.parse.stopAt(this.stoppedAt)}if(this.innerDone==this.inner.length){let t=this.baseTree;return null!=this.stoppedAt&&(t=new Oa(t.type,t.children,t.positions,t.length,t.propValues.concat([[qa,this.stoppedAt]]))),t}let t=this.inner[this.innerDone],e=t.parse.advance();if(e){this.innerDone++;let i=Object.assign(Object.create(null),t.target.props);i[sa.mounted.id]=new oa(e,t.overlay,t.parser,t.bracketed),t.target.props=i}return null}get parsedPos(){if(this.baseParse)return 0;let t=this.input.length;for(let e=this.innerDone;e<this.inner.length;e++)this.inner[e].from<t&&(t=Math.min(t,this.inner[e].parse.parsedPos));return t}stopAt(t){if(this.stoppedAt=t,this.baseParse)this.baseParse.stopAt(t);else for(let e=this.innerDone;e<this.inner.length;e++)this.inner[e].parse.stopAt(t)}startInner(){let t=new Ia(this.fragments),e=null,i=null,n=new Pa(new va(this.baseTree,this.ranges[0].from,0,null),da.IncludeAnonymous|da.IgnoreMounts);t:for(let r,s;;){let o,a=!0;if(null!=this.stoppedAt&&n.from>=this.stoppedAt)a=!1;else if(t.hasNode(n)){if(e){let t=e.mounts.find(t=>t.frag.from<=n.from&&t.frag.to>=n.to&&t.mount.overlay);if(t)for(let i of t.mount.overlay){let r=i.from+t.pos,s=i.to+t.pos;r>=n.from&&s<=n.to&&!e.ranges.some(t=>t.from<s&&t.to>r)&&e.ranges.push({from:r,to:s})}}a=!1}else if(i&&(s=Wa(i.ranges,n.from,n.to)))a=2!=s;else if(!n.type.isAnonymous&&(r=this.nest(n,this.input))&&(n.from<n.to||!r.overlay)){n.tree||(Ba(n),e&&e.depth++,i&&i.depth++);let s=t.findMounts(n.from,r.parser);if(\"function\"==typeof r.overlay)e=new La(r.parser,r.overlay,s,this.inner.length,n.from,!!r.bracketed,n.tree,e);else{let t=Ga(this.ranges,r.overlay||(n.from<n.to?[new ra(n.from,n.to)]:[]));t.length&&Ya(t),!t.length&&r.overlay||this.inner.push(new Ea(r.parser,t.length?r.parser.startParse(this.input,Ua(s,t),t):r.parser.startParse(\"\"),r.overlay?r.overlay.map(t=>new ra(t.from-n.from,t.to-n.from)):null,!!r.bracketed,n.tree,t.length?t[0].from:n.from)),r.overlay?t.length&&(i={ranges:t,depth:0,prev:i}):a=!1}}else if(e&&(o=e.predicate(n))&&(!0===o&&(o=new ra(n.from,n.to)),o.from<o.to)){let t=e.ranges.length-1;t>=0&&e.ranges[t].to==o.from?e.ranges[t]={from:e.ranges[t].from,to:o.to}:e.ranges.push(o)}if(a&&n.firstChild())e&&e.depth++,i&&i.depth++;else for(;!n.nextSibling();){if(!n.parent())break t;if(e&&! --e.depth){let t=Ga(this.ranges,e.ranges);t.length&&(Ya(t),this.inner.splice(e.index,0,new Ea(e.parser,e.parser.startParse(this.input,Ua(e.mounts,t),t),e.ranges.map(t=>new ra(t.from-e.start,t.to-e.start)),e.bracketed,e.target,t[0].from))),e=e.prev}i&&! --i.depth&&(i=i.prev)}}}}function Wa(t,e,i){for(let n of t){if(n.from>=i)break;if(n.to>e)return n.from<=e&&n.to>=i?2:1}return 0}function Da(t,e,i,n,r,s){if(e<i){let o=t.buffer[e+1];n.push(t.slice(e,i,o)),r.push(o-s)}}function Ba(t){let{node:e}=t,i=[],n=e.context.buffer;do{i.push(t.index),t.parent()}while(!t.tree);let r=t.tree,s=r.children.indexOf(n),o=r.children[s],a=o.buffer,l=[s];r.children[s]=function t(n,r,s,h,c,u){let d=i[u],f=[],O=[];Da(o,n,d,f,O,h);let p=a[d+1],m=a[d+2];l.push(f.length);let g=u?t(d+4,a[d+3],o.set.types[a[d]],p,m-p,u-1):e.toTree();return f.push(g),O.push(p-h),Da(o,a[d+3],r,f,O,h),new Oa(s,f,O,c)}(0,a.length,la.none,0,o.length,i.length-1);for(let h of l){let e=t.tree.children[h],i=t.tree.positions[h];t.yield(new va(e,i+t.from,h,t._tree))}}class ja{constructor(t,e){this.offset=e,this.done=!1,this.cursor=t.cursor(da.IncludeAnonymous|da.IgnoreMounts)}moveTo(t){let{cursor:e}=this,i=t-this.offset;for(;!this.done&&e.from<i;)e.to>=t&&e.enter(i,1,da.IgnoreOverlays|da.ExcludeBuffers)||e.next(!1)||(this.done=!0)}hasNode(t){if(this.moveTo(t.from),!this.done&&this.cursor.from+this.offset==t.from&&this.cursor.tree)for(let e=this.cursor.tree;;){if(e==t.tree)return!0;if(!(e.children.length&&0==e.positions[0]&&e.children[0]instanceof Oa))break;e=e.children[0]}return!1}}let Ia=class{constructor(t){var e;if(this.fragments=t,this.curTo=0,this.fragI=0,t.length){let i=this.curFrag=t[0];this.curTo=null!==(e=i.tree.prop(qa))&&void 0!==e?e:i.to,this.inner=new ja(i.tree,-i.offset)}else this.curFrag=this.inner=null}hasNode(t){for(;this.curFrag&&t.from>=this.curTo;)this.nextFrag();return this.curFrag&&this.curFrag.from<=t.from&&this.curTo>=t.to&&this.inner.hasNode(t)}nextFrag(){var t;if(this.fragI++,this.fragI==this.fragments.length)this.curFrag=this.inner=null;else{let e=this.curFrag=this.fragments[this.fragI];this.curTo=null!==(t=e.tree.prop(qa))&&void 0!==t?t:e.to,this.inner=new ja(e.tree,-e.offset)}}findMounts(t,e){var i;let n=[];if(this.inner){this.inner.cursor.moveTo(t,1);for(let t=this.inner.cursor.node;t;t=t.parent){let r=null===(i=t.tree)||void 0===i?void 0:i.prop(sa.mounted);if(r&&r.parser==e)for(let e=this.fragI;e<this.fragments.length;e++){let i=this.fragments[e];if(i.from>=t.to)break;i.tree==this.curFrag.tree&&n.push({frag:i,pos:t.from-i.offset,mount:r})}}}return n}};function Ga(t,e){let i=null,n=e;for(let r=1,s=0;r<t.length;r++){let o=t[r-1].to,a=t[r].from;for(;s<n.length;s++){let t=n[s];if(t.from>=a)break;t.to<=o||(i||(n=i=e.slice()),t.from<o?(i[s]=new ra(t.from,o),t.to>a&&i.splice(s+1,0,new ra(a,t.to))):t.to>a?i[s--]=new ra(a,t.to):i.splice(s--,1))}}return n}function Na(t,e,i,n){let r=0,s=0,o=!1,a=!1,l=-1e9,h=[];for(;;){let c=r==t.length?1e9:o?t[r].to:t[r].from,u=s==e.length?1e9:a?e[s].to:e[s].from;if(o!=a){let t=Math.max(l,i),e=Math.min(c,u,n);t<e&&h.push(new ra(t,e))}if(l=Math.min(c,u),1e9==l)break;c==l&&(o?(o=!1,r++):o=!0),u==l&&(a?(a=!1,s++):a=!0)}return h}function Ua(t,e){let i=[];for(let{pos:n,mount:r,frag:s}of t){let t=n+(r.overlay?r.overlay[0].from:0),o=t+r.tree.length,a=Math.max(s.from,t),l=Math.min(s.to,o);if(r.overlay){let o=Na(e,r.overlay.map(t=>new ra(t.from+n,t.to+n)),a,l);for(let e=0,n=a;;e++){let a=e==o.length,h=a?l:o[e].from;if(h>n&&i.push(new Ma(n,h,r.tree,-t,s.from>=n||s.openStart,s.to<=h||s.openEnd)),a)break;n=o[e].to}}else i.push(new Ma(a,l,r.tree,-t,s.from>=t||s.openStart,s.to<=o||s.openEnd))}return i}let Ha=0;class Fa{constructor(t,e,i,n){this.name=t,this.set=e,this.base=i,this.modified=n,this.id=Ha++}toString(){let{name:t}=this;for(let e of this.modified)e.name&&(t=`${e.name}(${t})`);return t}static define(t,e){let i=\"string\"==typeof t?t:\"?\";if(t instanceof Fa&&(e=t),null==e?void 0:e.base)throw new Error(\"Can not derive from a modified tag\");let n=new Fa(i,[],null,[]);if(n.set.push(n),e)for(let r of e.set)n.set.push(r);return n}static defineModifier(t){let e=new Ja(t);return t=>t.modified.indexOf(e)>-1?t:Ja.get(t.base||t,t.modified.concat(e).sort((t,e)=>t.id-e.id))}}let Ka=0;class Ja{constructor(t){this.name=t,this.instances=[],this.id=Ka++}static get(t,e){if(!e.length)return t;let i=e[0].instances.find(i=>{return i.base==t&&(n=e,r=i.modified,n.length==r.length&&n.every((t,e)=>t==r[e]));var n,r});if(i)return i;let n=[],r=new Fa(t.name,n,t,e);for(let o of e)o.instances.push(r);let s=function(t){let e=[[]];for(let i=0;i<t.length;i++)for(let n=0,r=e.length;n<r;n++)e.push(e[n].concat(t[i]));return e.sort((t,e)=>e.length-t.length)}(e);for(let o of t.set)if(!o.modified.length)for(let t of s)n.push(Ja.get(o,t));return r}}function tl(t){let e=Object.create(null);for(let i in t){let n=t[i];Array.isArray(n)||(n=[n]);for(let t of i.split(\" \"))if(t){let i=[],r=2,s=t;for(let e=0;;){if(\"...\"==s&&e>0&&e+3==t.length){r=1;break}let n=/^\"(?:[^\"\\\\]|\\\\.)*?\"|[^\\/!]+/.exec(s);if(!n)throw new RangeError(\"Invalid path: \"+t);if(i.push(\"*\"==n[0]?\"\":'\"'==n[0][0]?JSON.parse(n[0]):n[0]),e+=n[0].length,e==t.length)break;let o=t[e++];if(e==t.length&&\"!\"==o){r=0;break}if(\"/\"!=o)throw new RangeError(\"Invalid path: \"+t);s=t.slice(e)}let o=i.length-1,a=i[o];if(!a)throw new RangeError(\"Invalid path: \"+t);let l=new il(n,r,o>0?i.slice(0,o):null);e[a]=l.sort(e[a])}}return el.add(e)}const el=new sa({combine(t,e){let i,n,r;for(;t||e;){if(!t||e&&t.depth>=e.depth?(r=e,e=e.next):(r=t,t=t.next),i&&i.mode==r.mode&&!r.context&&!i.context)continue;let s=new il(r.tags,r.mode,r.context);i?i.next=s:n=s,i=s}return n}});class il{constructor(t,e,i,n){this.tags=t,this.mode=e,this.context=i,this.next=n}get opaque(){return 0==this.mode}get inherit(){return 1==this.mode}sort(t){return!t||t.depth<this.depth?(this.next=t,this):(t.next=this.sort(t.next),t)}get depth(){return this.context?this.context.length:0}}function nl(t,e){let i=Object.create(null);for(let s of t)if(Array.isArray(s.tag))for(let t of s.tag)i[t.id]=s.class;else i[s.tag.id]=s.class;let{scope:n,all:r=null}=e||{};return{style:t=>{let e=r;for(let n of t)for(let t of n.set){let n=i[t.id];if(n){e=e?e+\" \"+n:n;break}}return e},scope:n}}function rl(t,e,i,n=0,r=t.length){let s=new sl(n,Array.isArray(e)?e:[e],i);s.highlightRange(t.cursor(),n,r,\"\",s.highlighters),s.flush(r)}il.empty=new il([],2,null);class sl{constructor(t,e,i){this.at=t,this.highlighters=e,this.span=i,this.class=\"\"}startSpan(t,e){e!=this.class&&(this.flush(t),t>this.at&&(this.at=t),this.class=e)}flush(t){t>this.at&&this.class&&this.span(this.at,t,this.class)}highlightRange(t,e,i,n,r){let{type:s,from:o,to:a}=t;if(o>=i||a<=e)return;s.isTop&&(r=this.highlighters.filter(t=>!t.scope||t.scope(s)));let l=n,h=function(t){let e=t.type.prop(el);for(;e&&e.context&&!t.matchContext(e.context);)e=e.next;return e||null}(t)||il.empty,c=function(t,e){let i=null;for(let n of t){let t=n.style(e);t&&(i=i?i+\" \"+t:t)}return i}(r,h.tags);if(c&&(l&&(l+=\" \"),l+=c,1==h.mode&&(n+=(n?\" \":\"\")+c)),this.startSpan(Math.max(e,o),l),h.opaque)return;let u=t.tree&&t.tree.prop(sa.mounted);if(u&&u.overlay){let s=t.node.enter(u.overlay[0].from+o,1),h=this.highlighters.filter(t=>!t.scope||t.scope(u.tree.type)),c=t.firstChild();for(let d=0,f=o;;d++){let O=d<u.overlay.length?u.overlay[d]:null,p=O?O.from+o:a,m=Math.max(e,f),g=Math.min(i,p);if(m<g&&c)for(;t.from<g&&(this.highlightRange(t,m,g,n,r),this.startSpan(Math.min(g,t.to),l),!(t.to>=p)&&t.nextSibling()););if(!O||p>i)break;f=O.to+o,f>e&&(this.highlightRange(s.cursor(),Math.max(e,O.from+o),Math.min(i,f),\"\",h),this.startSpan(Math.min(i,f),l))}c&&t.parent()}else if(t.firstChild()){u&&(n=\"\");do{if(!(t.to<=e)){if(t.from>=i)break;this.highlightRange(t,e,i,n,r),this.startSpan(Math.min(i,t.to),l)}}while(t.nextSibling());t.parent()}}}const ol=Fa.define,al=ol(),ll=ol(),hl=ol(ll),cl=ol(ll),ul=ol(),dl=ol(ul),fl=ol(ul),Ol=ol(),pl=ol(Ol),ml=ol(),gl=ol(),Ql=ol(),bl=ol(Ql),vl=ol(),wl={comment:al,lineComment:ol(al),blockComment:ol(al),docComment:ol(al),name:ll,variableName:ol(ll),typeName:hl,tagName:ol(hl),propertyName:cl,attributeName:ol(cl),className:ol(ll),labelName:ol(ll),namespace:ol(ll),macroName:ol(ll),literal:ul,string:dl,docString:ol(dl),character:ol(dl),attributeValue:ol(dl),number:fl,integer:ol(fl),float:ol(fl),bool:ol(ul),regexp:ol(ul),escape:ol(ul),color:ol(ul),url:ol(ul),keyword:ml,self:ol(ml),null:ol(ml),atom:ol(ml),unit:ol(ml),modifier:ol(ml),operatorKeyword:ol(ml),controlKeyword:ol(ml),definitionKeyword:ol(ml),moduleKeyword:ol(ml),operator:gl,derefOperator:ol(gl),arithmeticOperator:ol(gl),logicOperator:ol(gl),bitwiseOperator:ol(gl),compareOperator:ol(gl),updateOperator:ol(gl),definitionOperator:ol(gl),typeOperator:ol(gl),controlOperator:ol(gl),punctuation:Ql,separator:ol(Ql),bracket:bl,angleBracket:ol(bl),squareBracket:ol(bl),paren:ol(bl),brace:ol(bl),content:Ol,heading:pl,heading1:ol(pl),heading2:ol(pl),heading3:ol(pl),heading4:ol(pl),heading5:ol(pl),heading6:ol(pl),contentSeparator:ol(Ol),list:ol(Ol),quote:ol(Ol),emphasis:ol(Ol),strong:ol(Ol),link:ol(Ol),monospace:ol(Ol),strikethrough:ol(Ol),inserted:ol(),deleted:ol(),changed:ol(),invalid:ol(),meta:vl,documentMeta:ol(vl),annotation:ol(vl),processingInstruction:ol(vl),definition:Fa.defineModifier(\"definition\"),constant:Fa.defineModifier(\"constant\"),function:Fa.defineModifier(\"function\"),standard:Fa.defineModifier(\"standard\"),local:Fa.defineModifier(\"local\"),special:Fa.defineModifier(\"special\")};for(let Mb in wl){let t=wl[Mb];t instanceof Fa&&(t.name=Mb)}var xl;nl([{tag:wl.link,class:\"tok-link\"},{tag:wl.heading,class:\"tok-heading\"},{tag:wl.emphasis,class:\"tok-emphasis\"},{tag:wl.strong,class:\"tok-strong\"},{tag:wl.keyword,class:\"tok-keyword\"},{tag:wl.atom,class:\"tok-atom\"},{tag:wl.bool,class:\"tok-bool\"},{tag:wl.url,class:\"tok-url\"},{tag:wl.labelName,class:\"tok-labelName\"},{tag:wl.inserted,class:\"tok-inserted\"},{tag:wl.deleted,class:\"tok-deleted\"},{tag:wl.literal,class:\"tok-literal\"},{tag:wl.string,class:\"tok-string\"},{tag:wl.number,class:\"tok-number\"},{tag:[wl.regexp,wl.escape,wl.special(wl.string)],class:\"tok-string2\"},{tag:wl.variableName,class:\"tok-variableName\"},{tag:wl.local(wl.variableName),class:\"tok-variableName tok-local\"},{tag:wl.definition(wl.variableName),class:\"tok-variableName tok-definition\"},{tag:wl.special(wl.variableName),class:\"tok-variableName2\"},{tag:wl.definition(wl.propertyName),class:\"tok-propertyName tok-definition\"},{tag:wl.typeName,class:\"tok-typeName\"},{tag:wl.namespace,class:\"tok-namespace\"},{tag:wl.className,class:\"tok-className\"},{tag:wl.macroName,class:\"tok-macroName\"},{tag:wl.propertyName,class:\"tok-propertyName\"},{tag:wl.operator,class:\"tok-operator\"},{tag:wl.comment,class:\"tok-comment\"},{tag:wl.meta,class:\"tok-meta\"},{tag:wl.invalid,class:\"tok-invalid\"},{tag:wl.punctuation,class:\"tok-punctuation\"}]);const Sl=new sa;function yl(t){return V.define({combine:t?e=>e.concat(t):void 0})}const kl=new sa;class $l{constructor(t,e,i=[],n=\"\"){this.data=t,this.name=n,Ct.prototype.hasOwnProperty(\"tree\")||Object.defineProperty(Ct.prototype,\"tree\",{get(){return Cl(this)}}),this.parser=e,this.extension=[Yl.of(this),Ct.languageData.of((t,e,i)=>{let n=Pl(t,e,i),r=n.type.prop(Sl);if(!r)return[];let s=t.facet(r),o=n.type.prop(kl);if(o){let r=n.resolve(e-n.from,i);for(let e of o)if(e.test(r,t)){let i=t.facet(e.facet);return\"replace\"==e.type?i:i.concat(s)}}return s})].concat(i)}isActiveAt(t,e,i=-1){return Pl(t,e,i).type.prop(Sl)==this.data}findRegions(t){let e=t.facet(Yl);if((null==e?void 0:e.data)==this.data)return[{from:0,to:t.doc.length}];if(!e||!e.allowsNesting)return[];let i=[],n=(t,e)=>{if(t.prop(Sl)==this.data)return void i.push({from:e,to:e+t.length});let r=t.prop(sa.mounted);if(r){if(r.tree.prop(Sl)==this.data){if(r.overlay)for(let t of r.overlay)i.push({from:t.from+e,to:t.to+e});else i.push({from:e,to:e+t.length});return}if(r.overlay){let t=i.length;if(n(r.tree,r.overlay[0].from+e),i.length>t)return}}for(let i=0;i<t.children.length;i++){let r=t.children[i];r instanceof Oa&&n(r,t.positions[i]+e)}};return n(Cl(t),0),i}get allowsNesting(){return!0}}function Pl(t,e,i){let n=t.facet(Yl),r=Cl(t).topNode;if(!n||n.allowsNesting)for(let s=r;s;s=s.enter(e,i,da.ExcludeBuffers|da.EnterBracketed))s.type.isTop&&(r=s);return r}$l.setState=gt.define();class Tl extends $l{constructor(t,e,i){super(t,e,[],i),this.parser=e}static define(t){let e=yl(t.languageData);return new Tl(e,t.parser.configure({props:[Sl.add(t=>t.isTop?e:void 0)]}),t.name)}configure(t,e){return new Tl(this.data,this.parser.configure(t),e||this.name)}get allowsNesting(){return this.parser.hasWrappers()}}function Cl(t){let e=t.field($l.state,!1);return e?e.tree:Oa.empty}class Zl{constructor(t){this.doc=t,this.cursorPos=0,this.string=\"\",this.cursor=t.iter()}get length(){return this.doc.length}syncTo(t){return this.string=this.cursor.next(t-this.cursorPos).value,this.cursorPos=t+this.string.length,this.cursorPos-this.string.length}chunk(t){return this.syncTo(t),this.string}get lineChunks(){return!0}read(t,e){let i=this.cursorPos-this.string.length;return t<i||e>=this.cursorPos?this.doc.sliceString(t,e):this.string.slice(t-i,e-i)}}let Xl=null;class Al{constructor(t,e,i=[],n,r,s,o,a){this.parser=t,this.state=e,this.fragments=i,this.tree=n,this.treeLen=r,this.viewport=s,this.skipped=o,this.scheduleOn=a,this.parse=null,this.tempSkipped=[]}static create(t,e,i){return new Al(t,e,[],Oa.empty,0,i,[],null)}startParse(){return this.parser.startParse(new Zl(this.state.doc),this.fragments)}work(t,e){return null!=e&&e>=this.state.doc.length&&(e=void 0),this.tree!=Oa.empty&&this.isDone(null!=e?e:this.state.doc.length)?(this.takeTree(),!0):this.withContext(()=>{var i;if(\"number\"==typeof t){let e=Date.now()+t;t=()=>Date.now()>e}for(this.parse||(this.parse=this.startParse()),null!=e&&(null==this.parse.stoppedAt||this.parse.stoppedAt>e)&&e<this.state.doc.length&&this.parse.stopAt(e);;){let n=this.parse.advance();if(n){if(this.fragments=this.withoutTempSkipped(Ma.addTree(n,this.fragments,null!=this.parse.stoppedAt)),this.treeLen=null!==(i=this.parse.stoppedAt)&&void 0!==i?i:this.state.doc.length,this.tree=n,this.parse=null,!(this.treeLen<(null!=e?e:this.state.doc.length)))return!0;this.parse=this.startParse()}if(t())return!1}})}takeTree(){let t,e;this.parse&&(t=this.parse.parsedPos)>=this.treeLen&&((null==this.parse.stoppedAt||this.parse.stoppedAt>t)&&this.parse.stopAt(t),this.withContext(()=>{for(;!(e=this.parse.advance()););}),this.treeLen=t,this.tree=e,this.fragments=this.withoutTempSkipped(Ma.addTree(this.tree,this.fragments,!0)),this.parse=null)}withContext(t){let e=Xl;Xl=this;try{return t()}finally{Xl=e}}withoutTempSkipped(t){for(let e;e=this.tempSkipped.pop();)t=Ml(t,e.from,e.to);return t}changes(t,e){let{fragments:i,tree:n,treeLen:r,viewport:s,skipped:o}=this;if(this.takeTree(),!t.empty){let e=[];if(t.iterChangedRanges((t,i,n,r)=>e.push({fromA:t,toA:i,fromB:n,toB:r})),i=Ma.applyChanges(i,e),n=Oa.empty,r=0,s={from:t.mapPos(s.from,-1),to:t.mapPos(s.to,1)},this.skipped.length){o=[];for(let e of this.skipped){let i=t.mapPos(e.from,1),n=t.mapPos(e.to,-1);i<n&&o.push({from:i,to:n})}}}return new Al(this.parser,e,i,n,r,s,o,this.scheduleOn)}updateViewport(t){if(this.viewport.from==t.from&&this.viewport.to==t.to)return!1;this.viewport=t;let e=this.skipped.length;for(let i=0;i<this.skipped.length;i++){let{from:e,to:n}=this.skipped[i];e<t.to&&n>t.from&&(this.fragments=Ml(this.fragments,e,n),this.skipped.splice(i--,1))}return!(this.skipped.length>=e)&&(this.reset(),!0)}reset(){this.parse&&(this.takeTree(),this.parse=null)}skipUntilInView(t,e){this.skipped.push({from:t,to:e})}static getSkippingParser(t){return new class extends Ra{createParse(e,i,n){let r=n[0].from,s=n[n.length-1].to;return{parsedPos:r,advance(){let e=Xl;if(e){for(let t of n)e.tempSkipped.push(t);t&&(e.scheduleOn=e.scheduleOn?Promise.all([e.scheduleOn,t]):t)}return this.parsedPos=s,new Oa(la.none,[],[],s-r)},stoppedAt:null,stopAt(){}}}}}isDone(t){t=Math.min(t,this.state.doc.length);let e=this.fragments;return this.treeLen>=t&&e.length&&0==e[0].from&&e[0].to>=t}static get(){return Xl}}function Ml(t,e,i){return Ma.applyChanges(t,[{fromA:e,toA:i,fromB:e,toB:i}])}class Rl{constructor(t){this.context=t,this.tree=t.tree}apply(t){if(!t.docChanged&&this.tree==this.context.tree)return this;let e=this.context.changes(t.changes,t.state),i=this.context.treeLen==t.startState.doc.length?void 0:Math.max(t.changes.mapPos(this.context.treeLen),e.viewport.to);return e.work(20,i)||e.takeTree(),new Rl(e)}static init(t){let e=Math.min(3e3,t.doc.length),i=Al.create(t.facet(Yl).parser,t,{from:0,to:e});return i.work(20,e)||i.takeTree(),new Rl(i)}}$l.state=N.define({create:Rl.init,update(t,e){for(let i of e.effects)if(i.is($l.setState))return i.value;return e.startState.facet(Yl)!=e.state.facet(Yl)?Rl.init(e.state):t.apply(e)}});let zl=t=>{let e=setTimeout(()=>t(),500);return()=>clearTimeout(e)};\"undefined\"!=typeof requestIdleCallback&&(zl=t=>{let e=-1,i=setTimeout(()=>{e=requestIdleCallback(t,{timeout:400})},100);return()=>e<0?clearTimeout(i):cancelIdleCallback(e)});const _l=\"undefined\"!=typeof navigator&&(null===(xl=navigator.scheduling)||void 0===xl?void 0:xl.isInputPending)?()=>navigator.scheduling.isInputPending():null,El=Wi.fromClass(class{constructor(t){this.view=t,this.working=null,this.workScheduled=0,this.chunkEnd=-1,this.chunkBudget=-1,this.work=this.work.bind(this),this.scheduleWork()}update(t){let e=this.view.state.field($l.state).context;(e.updateViewport(t.view.viewport)||this.view.viewport.to>e.treeLen)&&this.scheduleWork(),(t.docChanged||t.selectionSet)&&(this.view.hasFocus&&(this.chunkBudget+=50),this.scheduleWork()),this.checkAsyncSchedule(e)}scheduleWork(){if(this.working)return;let{state:t}=this.view,e=t.field($l.state);e.tree==e.context.tree&&e.context.isDone(t.doc.length)||(this.working=zl(this.work))}work(t){this.working=null;let e=Date.now();if(this.chunkEnd<e&&(this.chunkEnd<0||this.view.hasFocus)&&(this.chunkEnd=e+3e4,this.chunkBudget=3e3),this.chunkBudget<=0)return;let{state:i,viewport:{to:n}}=this.view,r=i.field($l.state);if(r.tree==r.context.tree&&r.context.isDone(n+1e5))return;let s=Date.now()+Math.min(this.chunkBudget,100,t&&!_l?Math.max(25,t.timeRemaining()-5):1e9),o=r.context.treeLen<n&&i.doc.length>n+1e3,a=r.context.work(()=>_l&&_l()||Date.now()>s,n+(o?0:1e5));this.chunkBudget-=Date.now()-e,(a||this.chunkBudget<=0)&&(r.context.takeTree(),this.view.dispatch({effects:$l.setState.of(new Rl(r.context))})),this.chunkBudget>0&&(!a||o)&&this.scheduleWork(),this.checkAsyncSchedule(r.context)}checkAsyncSchedule(t){t.scheduleOn&&(this.workScheduled++,t.scheduleOn.then(()=>this.scheduleWork()).catch(t=>Yi(this.view.state,t)).then(()=>this.workScheduled--),t.scheduleOn=null)}destroy(){this.working&&this.working()}isWorking(){return!!(this.working||this.workScheduled>0)}},{eventHandlers:{focus(){this.scheduleWork()}}}),Yl=V.define({combine:t=>t.length?t[0]:null,enables:t=>[$l.state,El,fs.contentAttributes.compute([t],e=>{let i=e.facet(t);return i&&i.name?{\"data-language\":i.name}:{}})]});class Ll{constructor(t,e=[]){this.language=t,this.support=e,this.extension=[t,e]}}class ql{constructor(t,e,i,n,r,s=void 0){this.name=t,this.alias=e,this.extensions=i,this.filename=n,this.loadFunc=r,this.support=s,this.loading=null}load(){return this.loading||(this.loading=this.loadFunc().then(t=>this.support=t,t=>{throw this.loading=null,t}))}static of(t){let{load:e,support:i}=t;if(!e){if(!i)throw new RangeError(\"Must pass either 'load' or 'support' to LanguageDescription.of\");e=()=>Promise.resolve(i)}return new ql(t.name,(t.alias||[]).concat(t.name).map(t=>t.toLowerCase()),t.extensions||[],t.filename,e,i)}static matchFilename(t,e){for(let n of t)if(n.filename&&n.filename.test(e))return n;let i=/\\.([^.]+)$/.exec(e);if(i)for(let n of t)if(n.extensions.indexOf(i[1])>-1)return n;return null}static matchLanguageName(t,e,i=!0){e=e.toLowerCase();for(let n of t)if(n.alias.some(t=>t==e))return n;if(i)for(let n of t)for(let t of n.alias){let i=e.indexOf(t);if(i>-1&&(t.length>2||!/\\w/.test(e[i-1])&&!/\\w/.test(e[i+t.length])))return n}return null}}const Vl=V.define(),Wl=V.define({combine:t=>{if(!t.length)return\"  \";let e=t[0];if(!e||/\\S/.test(e)||Array.from(e).some(t=>t!=e[0]))throw new Error(\"Invalid indent unit: \"+JSON.stringify(t[0]));return e}});function Dl(t){let e=t.facet(Wl);return 9==e.charCodeAt(0)?t.tabSize*e.length:e.length}function Bl(t,e){let i=\"\",n=t.tabSize,r=t.facet(Wl)[0];if(\"\\t\"==r){for(;e>=n;)i+=\"\\t\",e-=n;r=\" \"}for(let s=0;s<e;s++)i+=r;return i}function jl(t,e){t instanceof Ct&&(t=new Il(t));for(let n of t.state.facet(Vl)){let i=n(t,e);if(void 0!==i)return i}let i=Cl(t.state);return i.length>=e?function(t,e,i){let n=e.resolveStack(i),r=e.resolveInner(i,-1).resolve(i,0).enterUnfinishedNodesBefore(i);if(r!=n.node){let t=[];for(let e=r;e&&!(e.from<n.node.from||e.to>n.node.to||e.from==n.node.from&&e.type==n.node.type);e=e.parent)t.push(e);for(let e=t.length-1;e>=0;e--)n={node:t[e],next:n}}return Nl(n,t,i)}(t,i,e):null}class Il{constructor(t,e={}){this.state=t,this.options=e,this.unit=Dl(t)}lineAt(t,e=1){let i=this.state.doc.lineAt(t),{simulateBreak:n,simulateDoubleBreak:r}=this.options;return null!=n&&n>=i.from&&n<=i.to?r&&n==t?{text:\"\",from:t}:(e<0?n<t:n<=t)?{text:i.text.slice(n-i.from),from:n}:{text:i.text.slice(0,n-i.from),from:i.from}:i}textAfterPos(t,e=1){if(this.options.simulateDoubleBreak&&t==this.options.simulateBreak)return\"\";let{text:i,from:n}=this.lineAt(t,e);return i.slice(t-n,Math.min(i.length,t+100-n))}column(t,e=1){let{text:i,from:n}=this.lineAt(t,e),r=this.countColumn(i,t-n),s=this.options.overrideIndentation?this.options.overrideIndentation(n):-1;return s>-1&&(r+=s-this.countColumn(i,i.search(/\\S|$/))),r}countColumn(t,e=t.length){return Nt(t,this.state.tabSize,e)}lineIndent(t,e=1){let{text:i,from:n}=this.lineAt(t,e),r=this.options.overrideIndentation;if(r){let t=r(n);if(t>-1)return t}return this.countColumn(i,i.search(/\\S|$/))}get simulatedBreak(){return this.options.simulateBreak||null}}const Gl=new sa;function Nl(t,e,i){for(let n=t;n;n=n.next){let t=Ul(n.node);if(t)return t(Fl.create(e,i,n))}return 0}function Ul(t){let e=t.type.prop(Gl);if(e)return e;let i,n=t.firstChild;if(n&&(i=n.type.prop(sa.closedBy))){let e=t.lastChild,n=e&&i.indexOf(e.name)>-1;return t=>th(t,!0,1,void 0,n&&!function(t){return t.pos==t.options.simulateBreak&&t.options.simulateDoubleBreak}(t)?e.from:void 0)}return null==t.parent?Hl:null}function Hl(){return 0}class Fl extends Il{constructor(t,e,i){super(t.state,t.options),this.base=t,this.pos=e,this.context=i}get node(){return this.context.node}static create(t,e,i){return new Fl(t,e,i)}get textAfter(){return this.textAfterPos(this.pos)}get baseIndent(){return this.baseIndentFor(this.node)}baseIndentFor(t){let e=this.state.doc.lineAt(t.from);for(;;){let i=t.resolve(e.from);for(;i.parent&&i.parent.from==i.from;)i=i.parent;if(Kl(i,t))break;e=this.state.doc.lineAt(i.from)}return this.lineIndent(e.from)}continue(){return Nl(this.context.next,this.base,this.pos)}}function Kl(t,e){for(let i=e;i;i=i.parent)if(t==i)return!0;return!1}function Jl({closing:t,align:e=!0,units:i=1}){return n=>th(n,e,i,t)}function th(t,e,i,n,r){let s=t.textAfter,o=s.match(/^\\s*/)[0].length,a=n&&s.slice(o,o+n.length)==n||r==t.pos+o,l=e?function(t){let e=t.node,i=e.childAfter(e.from),n=e.lastChild;if(!i)return null;let r=t.options.simulateBreak,s=t.state.doc.lineAt(i.from),o=null==r||r<=s.from?s.to:Math.min(s.to,r);for(let a=i.to;;){let t=e.childAfter(a);if(!t||t==n)return null;if(!t.type.isSkipped){if(t.from>=o)return null;let e=/^ */.exec(s.text.slice(i.to-s.from))[0].length;return{from:i.from,to:i.to+e}}a=t.to}}(t):null;return l?a?t.column(l.from):t.column(l.to):t.baseIndent+(a?0:t.unit*i)}function eh({except:t,units:e=1}={}){return i=>{let n=t&&t.test(i.textAfter);return i.baseIndent+(n?0:e*i.unit)}}const ih=V.define(),nh=new sa;function rh(t){let e=t.firstChild,i=t.lastChild;return e&&e.to<i.from?{from:e.to,to:i.type.isError?t.to:i.from}:null}function sh(t){let e=t.lastChild;return e&&e.to==t.to&&e.type.isError}function oh(t,e,i){for(let n of t.facet(ih)){let r=n(t,e,i);if(r)return r}return function(t,e,i){let n=Cl(t);if(n.length<i)return null;let r=null;for(let s=n.resolveStack(i,1);s;s=s.next){let o=s.node;if(o.to<=i||o.from>i)continue;if(r&&o.from<e)break;let a=o.type.prop(nh);if(a&&(o.to<n.length-50||n.length==t.doc.length||!sh(o))){let n=a(o,t);n&&n.from<=i&&n.from>=e&&n.to>i&&(r=n)}}return r}(t,e,i)}function ah(t,e){let i=e.mapPos(t.from,1),n=e.mapPos(t.to,-1);return i>=n?void 0:{from:i,to:n}}const lh=gt.define({map:ah}),hh=gt.define({map:ah});function ch(t){let e=[];for(let{head:i}of t.state.selection.ranges)e.some(t=>t.from<=i&&t.to>=i)||e.push(t.lineBlockAt(i));return e}const uh=N.define({create:()=>Ce.none,update(t,e){e.isUserEvent(\"delete\")&&e.changes.iterChangedRanges((e,i)=>t=dh(t,e,i)),t=t.map(e.changes);for(let i of e.effects)if(i.is(lh)&&!Oh(t,i.value.from,i.value.to)){let{preparePlaceholder:n}=e.state.facet(bh),r=n?Ce.replace({widget:new Sh(n(e.state,i.value))}):xh;t=t.update({add:[r.range(i.value.from,i.value.to)]})}else i.is(hh)&&(t=t.update({filter:(t,e)=>i.value.from!=t||i.value.to!=e,filterFrom:i.value.from,filterTo:i.value.to}));return e.selection&&(t=dh(t,e.selection.main.head)),t},provide:t=>fs.decorations.from(t),toJSON(t,e){let i=[];return t.between(0,e.doc.length,(t,e)=>{i.push(t,e)}),i},fromJSON(t){if(!Array.isArray(t)||t.length%2)throw new RangeError(\"Invalid JSON for fold state\");let e=[];for(let i=0;i<t.length;){let n=t[i++],r=t[i++];if(\"number\"!=typeof n||\"number\"!=typeof r)throw new RangeError(\"Invalid JSON for fold state\");e.push(xh.range(n,r))}return Ce.set(e,!0)}});function dh(t,e,i=e){let n=!1;return t.between(e,i,(t,r)=>{t<i&&r>e&&(n=!0)}),n?t.update({filterFrom:e,filterTo:i,filter:(t,n)=>t>=i||n<=e}):t}function fh(t,e,i){var n;let r=null;return null===(n=t.field(uh,!1))||void 0===n||n.between(e,i,(t,e)=>{(!r||r.from>t)&&(r={from:t,to:e})}),r}function Oh(t,e,i){let n=!1;return t.between(e,e,(t,r)=>{t==e&&r==i&&(n=!0)}),n}function ph(t,e){return t.field(uh,!1)?e:e.concat(gt.appendConfig.of(vh()))}function mh(t,e,i=!0){let n=t.state.doc.lineAt(e.from).number,r=t.state.doc.lineAt(e.to).number;return fs.announce.of(`${t.state.phrase(i?\"Folded lines\":\"Unfolded lines\")} ${n} ${t.state.phrase(\"to\")} ${r}.`)}const gh=[{key:\"Ctrl-Shift-[\",mac:\"Cmd-Alt-[\",run:t=>{for(let e of ch(t)){let i=oh(t.state,e.from,e.to);if(i)return t.dispatch({effects:ph(t.state,[lh.of(i),mh(t,i)])}),!0}return!1}},{key:\"Ctrl-Shift-]\",mac:\"Cmd-Alt-]\",run:t=>{if(!t.state.field(uh,!1))return!1;let e=[];for(let i of ch(t)){let n=fh(t.state,i.from,i.to);n&&e.push(hh.of(n),mh(t,n,!1))}return e.length&&t.dispatch({effects:e}),e.length>0}},{key:\"Ctrl-Alt-[\",run:t=>{let{state:e}=t,i=[];for(let n=0;n<e.doc.length;){let r=t.lineBlockAt(n),s=oh(e,r.from,r.to);s&&i.push(lh.of(s)),n=(s?t.lineBlockAt(s.to):r).to+1}return i.length&&t.dispatch({effects:ph(t.state,i)}),!!i.length}},{key:\"Ctrl-Alt-]\",run:t=>{let e=t.state.field(uh,!1);if(!e||!e.size)return!1;let i=[];return e.between(0,t.state.doc.length,(t,e)=>{i.push(hh.of({from:t,to:e}))}),t.dispatch({effects:i}),!0}}],Qh={placeholderDOM:null,preparePlaceholder:null,placeholderText:\"…\"},bh=V.define({combine:t=>Zt(t,Qh)});function vh(t){return[uh,$h]}function wh(t,e){let{state:i}=t,n=i.facet(bh),r=e=>{let i=t.lineBlockAt(t.posAtDOM(e.target)),n=fh(t.state,i.from,i.to);n&&t.dispatch({effects:hh.of(n)}),e.preventDefault()};if(n.placeholderDOM)return n.placeholderDOM(t,r,e);let s=document.createElement(\"span\");return s.textContent=n.placeholderText,s.setAttribute(\"aria-label\",i.phrase(\"folded code\")),s.title=i.phrase(\"unfold\"),s.className=\"cm-foldPlaceholder\",s.onclick=r,s}const xh=Ce.replace({widget:new class extends Pe{toDOM(t){return wh(t,null)}}});class Sh extends Pe{constructor(t){super(),this.value=t}eq(t){return this.value==t.value}toDOM(t){return wh(t,this.value)}}const yh={openText:\"⌄\",closedText:\"›\",markerDOM:null,domEventHandlers:{},foldingChanged:()=>!1};class kh extends Mo{constructor(t,e){super(),this.config=t,this.open=e}eq(t){return this.config==t.config&&this.open==t.open}toDOM(t){if(this.config.markerDOM)return this.config.markerDOM(this.open);let e=document.createElement(\"span\");return e.textContent=this.open?this.config.openText:this.config.closedText,e.title=t.state.phrase(this.open?\"Fold line\":\"Unfold line\"),e}}const $h=fs.baseTheme({\".cm-foldPlaceholder\":{backgroundColor:\"#eee\",border:\"1px solid #ddd\",color:\"#888\",borderRadius:\".2em\",margin:\"0 1px\",padding:\"0 1px\",cursor:\"pointer\"},\".cm-foldGutter span\":{padding:\"0 1px\",cursor:\"pointer\"}});class Ph{constructor(t,e){let i;function n(t){let e=Jt.newName();return(i||(i=Object.create(null)))[\".\"+e]=t,e}this.specs=t;const r=\"string\"==typeof e.all?e.all:e.all?n(e.all):void 0,s=e.scope;this.scope=s instanceof $l?t=>t.prop(Sl)==s.data:s?t=>t==s:void 0,this.style=nl(t.map(t=>({tag:t.tag,class:t.class||n(Object.assign({},t,{tag:null}))})),{all:r}).style,this.module=i?new Jt(i):null,this.themeType=e.themeType}static define(t,e){return new Ph(t,e||{})}}const Th=V.define(),Ch=V.define({combine:t=>t.length?[t[0]]:null});function Zh(t){let e=t.facet(Th);return e.length?e:t.facet(Ch)}function Xh(t,e){let i,n=[Mh];return t instanceof Ph&&(t.module&&n.push(fs.styleModule.of(t.module)),i=t.themeType),(null==e?void 0:e.fallback)?n.push(Ch.of(t)):i?n.push(Th.computeN([fs.darkTheme],e=>e.facet(fs.darkTheme)==(\"dark\"==i)?[t]:[])):n.push(Th.of(t)),n}class Ah{constructor(t){this.markCache=Object.create(null),this.tree=Cl(t.state),this.decorations=this.buildDeco(t,Zh(t.state)),this.decoratedTo=t.viewport.to}update(t){let e=Cl(t.state),i=Zh(t.state),n=i!=Zh(t.startState),{viewport:r}=t.view,s=t.changes.mapPos(this.decoratedTo,1);e.length<r.to&&!n&&e.type==this.tree.type&&s>=r.to?(this.decorations=this.decorations.map(t.changes),this.decoratedTo=s):(e!=this.tree||t.viewportChanged||n)&&(this.tree=e,this.decorations=this.buildDeco(t.view,i),this.decoratedTo=r.to)}buildDeco(t,e){if(!e||!this.tree.length)return Ce.none;let i=new Et;for(let{from:n,to:r}of t.visibleRanges)rl(this.tree,e,(t,e,n)=>{i.add(t,e,this.markCache[n]||(this.markCache[n]=Ce.mark({class:n})))},n,r);return i.finish()}}const Mh=tt.high(Wi.fromClass(Ah,{decorations:t=>t.decorations})),Rh=Ph.define([{tag:wl.meta,color:\"#404740\"},{tag:wl.link,textDecoration:\"underline\"},{tag:wl.heading,textDecoration:\"underline\",fontWeight:\"bold\"},{tag:wl.emphasis,fontStyle:\"italic\"},{tag:wl.strong,fontWeight:\"bold\"},{tag:wl.strikethrough,textDecoration:\"line-through\"},{tag:wl.keyword,color:\"#708\"},{tag:[wl.atom,wl.bool,wl.url,wl.contentSeparator,wl.labelName],color:\"#219\"},{tag:[wl.literal,wl.inserted],color:\"#164\"},{tag:[wl.string,wl.deleted],color:\"#a11\"},{tag:[wl.regexp,wl.escape,wl.special(wl.string)],color:\"#e40\"},{tag:wl.definition(wl.variableName),color:\"#00f\"},{tag:wl.local(wl.variableName),color:\"#30a\"},{tag:[wl.typeName,wl.namespace],color:\"#085\"},{tag:wl.className,color:\"#167\"},{tag:[wl.special(wl.variableName),wl.macroName],color:\"#256\"},{tag:wl.definition(wl.propertyName),color:\"#00c\"},{tag:wl.comment,color:\"#940\"},{tag:wl.invalid,color:\"#f00\"}]),zh=fs.baseTheme({\"&.cm-focused .cm-matchingBracket\":{backgroundColor:\"#328c8252\"},\"&.cm-focused .cm-nonmatchingBracket\":{backgroundColor:\"#bb555544\"}}),_h=\"()[]{}\",Eh=V.define({combine:t=>Zt(t,{afterCursor:!0,brackets:_h,maxScanDistance:1e4,renderMatch:qh})}),Yh=Ce.mark({class:\"cm-matchingBracket\"}),Lh=Ce.mark({class:\"cm-nonmatchingBracket\"});function qh(t){let e=[],i=t.matched?Yh:Lh;return e.push(i.range(t.start.from,t.start.to)),t.end&&e.push(i.range(t.end.from,t.end.to)),e}const Vh=[N.define({create:()=>Ce.none,update(t,e){if(!e.docChanged&&!e.selection)return t;let i=[],n=e.state.facet(Eh);for(let r of e.state.selection.ranges){if(!r.empty)continue;let t=jh(e.state,r.head,-1,n)||r.head>0&&jh(e.state,r.head-1,1,n)||n.afterCursor&&(jh(e.state,r.head,1,n)||r.head<e.state.doc.length&&jh(e.state,r.head+1,-1,n));t&&(i=i.concat(n.renderMatch(t,e.state)))}return Ce.set(i,!0)},provide:t=>fs.decorations.from(t)}),zh];const Wh=new sa;function Dh(t,e,i){let n=t.prop(e<0?sa.openedBy:sa.closedBy);if(n)return n;if(1==t.name.length){let n=i.indexOf(t.name);if(n>-1&&n%2==(e<0?1:0))return[i[n+e]]}return null}function Bh(t){let e=t.type.prop(Wh);return e?e(t.node):t}function jh(t,e,i,n={}){let r=n.maxScanDistance||1e4,s=n.brackets||_h,o=Cl(t),a=o.resolveInner(e,i);for(let l=a;l;l=l.parent){let n=Dh(l.type,i,s);if(n&&l.from<l.to){let r=Bh(l);if(r&&(i>0?e>=r.from&&e<r.to:e>r.from&&e<=r.to))return Ih(t,e,i,l,r,n,s)}}return function(t,e,i,n,r,s,o){let a=i<0?t.sliceDoc(e-1,e):t.sliceDoc(e,e+1),l=o.indexOf(a);if(l<0||l%2==0!=i>0)return null;let h={from:i<0?e-1:e,to:i>0?e+1:e},c=t.doc.iterRange(e,i>0?t.doc.length:0),u=0;for(let d=0;!c.next().done&&d<=s;){let t=c.value;i<0&&(d+=t.length);let s=e+d*i;for(let e=i>0?0:t.length-1,a=i>0?t.length:-1;e!=a;e+=i){let a=o.indexOf(t[e]);if(!(a<0||n.resolveInner(s+e,1).type!=r))if(a%2==0==i>0)u++;else{if(1==u)return{start:h,end:{from:s+e,to:s+e+1},matched:a>>1==l>>1};u--}}i>0&&(d+=t.length)}return c.done?{start:h,matched:!1}:null}(t,e,i,o,a.type,r,s)}function Ih(t,e,i,n,r,s,o){let a=n.parent,l={from:r.from,to:r.to},h=0,c=null==a?void 0:a.cursor();if(c&&(i<0?c.childBefore(n.from):c.childAfter(n.to)))do{if(i<0?c.to<=n.from:c.from>=n.to){if(0==h&&s.indexOf(c.type.name)>-1&&c.from<c.to){let t=Bh(c);return{start:l,end:t?{from:t.from,to:t.to}:void 0,matched:!0}}if(Dh(c.type,i,o))h++;else if(Dh(c.type,-i,o)){if(0==h){let t=Bh(c);return{start:l,end:t&&t.from<t.to?{from:t.from,to:t.to}:void 0,matched:!1}}h--}}}while(i<0?c.prevSibling():c.nextSibling());return{start:l,matched:!1}}const Gh=Object.create(null),Nh=[la.none],Uh=[],Hh=Object.create(null),Fh=Object.create(null);for(let[Mb,Rb]of[[\"variable\",\"variableName\"],[\"variable-2\",\"variableName.special\"],[\"string-2\",\"string.special\"],[\"def\",\"variableName.definition\"],[\"tag\",\"tagName\"],[\"attribute\",\"attributeName\"],[\"type\",\"typeName\"],[\"builtin\",\"variableName.standard\"],[\"qualifier\",\"modifier\"],[\"error\",\"invalid\"],[\"header\",\"heading\"],[\"property\",\"propertyName\"]])Fh[Mb]=Jh(Gh,Rb);function Kh(t,e){Uh.indexOf(t)>-1||Uh.push(t)}function Jh(t,e){let i=[];for(let a of e.split(\" \")){let e=[];for(let i of a.split(\".\")){let n=t[i]||wl[i];n?\"function\"==typeof n?e.length?e=e.map(n):Kh(i):e.length?Kh(i):e=Array.isArray(n)?n:[n]:Kh(i)}for(let t of e)i.push(t)}if(!i.length)return 0;let n=e.replace(/ /g,\"_\"),r=n+\" \"+i.map(t=>t.id),s=Hh[r];if(s)return s.id;let o=Hh[r]=la.define({id:Nh.length,name:n,props:[tl({[n]:i})]});return Nh.push(o),o.id}ri.RTL,ri.LTR;class tc{constructor(t,e,i,n){this.state=t,this.pos=e,this.explicit=i,this.view=n,this.abortListeners=[],this.abortOnDocChange=!1}tokenBefore(t){let e=Cl(this.state).resolveInner(this.pos,-1);for(;e&&t.indexOf(e.name)<0;)e=e.parent;return e?{from:e.from,to:this.pos,text:this.state.sliceDoc(e.from,this.pos),type:e.type}:null}matchBefore(t){let e=this.state.doc.lineAt(this.pos),i=Math.max(e.from,this.pos-250),n=e.text.slice(i-e.from,this.pos-e.from),r=n.search(sc(t,!1));return r<0?null:{from:i+r,to:this.pos,text:n.slice(r)}}get aborted(){return null==this.abortListeners}addEventListener(t,e,i){\"abort\"==t&&this.abortListeners&&(this.abortListeners.push(e),i&&i.onDocChange&&(this.abortOnDocChange=!0))}}function ec(t){let e=Object.keys(t).join(\"\"),i=/\\w/.test(e);return i&&(e=e.replace(/\\w/g,\"\")),`[${i?\"\\\\w\":\"\"}${e.replace(/[^\\w\\s]/g,\"\\\\$&\")}]`}function ic(t){let e=t.map(t=>\"string\"==typeof t?{label:t}:t),[i,n]=e.every(t=>/^\\w+$/.test(t.label))?[/\\w*$/,/\\w+$/]:function(t){let e=Object.create(null),i=Object.create(null);for(let{label:r}of t){e[r[0]]=!0;for(let t=1;t<r.length;t++)i[r[t]]=!0}let n=ec(e)+ec(i)+\"*$\";return[new RegExp(\"^\"+n),new RegExp(n)]}(e);return t=>{let r=t.matchBefore(n);return r||t.explicit?{from:r?r.from:t.pos,options:e,validFor:i}:null}}class nc{constructor(t,e,i,n){this.completion=t,this.source=e,this.match=i,this.score=n}}function rc(t){return t.selection.main.from}function sc(t,e){var i;let{source:n}=t,r=e&&\"^\"!=n[0],s=\"$\"!=n[n.length-1];return r||s?new RegExp(`${r?\"^\":\"\"}(?:${n})${s?\"$\":\"\"}`,null!==(i=t.flags)&&void 0!==i?i:t.ignoreCase?\"i\":\"\"):t}const oc=Ot.define();function ac(t,e,i,n){let{main:r}=t.selection,s=i-r.from,o=n-r.from;return{...t.changeByRange(a=>{if(a!=r&&i!=n&&t.sliceDoc(a.from+s,a.from+o)!=t.sliceDoc(i,n))return{range:a};let l=t.toText(e);return{changes:{from:a.from+s,to:n==r.from?a.to:a.from+o,insert:l},range:Y.cursor(a.from+s+l.length)}}),scrollIntoView:!0,userEvent:\"input.complete\"}}const lc=new WeakMap;function hc(t){if(!Array.isArray(t))return t;let e=lc.get(t);return e||lc.set(t,e=ic(t)),e}const cc=gt.define(),uc=gt.define();class dc{constructor(t){this.pattern=t,this.chars=[],this.folded=[],this.any=[],this.precise=[],this.byWord=[],this.score=0,this.matched=[];for(let e=0;e<t.length;){let i=y(t,e),n=$(i);this.chars.push(i);let r=t.slice(e,e+n),s=r.toUpperCase();this.folded.push(y(s==r?r.toLowerCase():s,0)),e+=n}this.astral=t.length!=this.chars.length}ret(t,e){return this.score=t,this.matched=e,this}match(t){if(0==this.pattern.length)return this.ret(-100,[]);if(t.length<this.pattern.length)return null;let{chars:e,folded:i,any:n,precise:r,byWord:s}=this;if(1==e.length){let n=y(t,0),r=$(n),s=r==t.length?0:-100;if(n==e[0]);else{if(n!=i[0])return null;s+=-200}return this.ret(s,[0,r])}let o=t.indexOf(this.pattern);if(0==o)return this.ret(t.length==this.pattern.length?0:-100,[0,this.pattern.length]);let a=e.length,l=0;if(o<0){for(let r=0,s=Math.min(t.length,200);r<s&&l<a;){let s=y(t,r);s!=e[l]&&s!=i[l]||(n[l++]=r),r+=$(s)}if(l<a)return null}let h=0,c=0,u=!1,d=0,f=-1,O=-1,p=/[a-z]/.test(t),m=!0;for(let g=0,Q=Math.min(t.length,200),b=0;g<Q&&c<a;){let n=y(t,g);o<0&&(h<a&&n==e[h]&&(r[h++]=g),d<a&&(n==e[d]||n==i[d]?(0==d&&(f=g),O=g+1,d++):d=0));let l,Q=n<255?n>=48&&n<=57||n>=97&&n<=122?2:n>=65&&n<=90?1:0:(l=k(n))!=l.toLowerCase()?1:l!=l.toUpperCase()?2:0;(!g||1==Q&&p||0==b&&0!=Q)&&(e[c]==n||i[c]==n&&(u=!0)?s[c++]=g:s.length&&(m=!1)),b=Q,g+=$(n)}return c==a&&0==s[0]&&m?this.result((u?-200:0)-100,s,t):d==a&&0==f?this.ret(-200-t.length+(O==t.length?0:-100),[0,O]):o>-1?this.ret(-700-t.length,[o,o+this.pattern.length]):d==a?this.ret(-900-t.length,[f,O]):c==a?this.result((u?-200:0)-100-700+(m?0:-1100),s,t):2==e.length?null:this.result((n[0]?-700:0)-200-1100,n,t)}result(t,e,i){let n=[],r=0;for(let s of e){let t=s+(this.astral?$(y(i,s)):1);r&&n[r-1]==s?n[r-1]=t:(n[r++]=s,n[r++]=t)}return this.ret(t-i.length,n)}}class fc{constructor(t){this.pattern=t,this.matched=[],this.score=0,this.folded=t.toLowerCase()}match(t){if(t.length<this.pattern.length)return null;let e=t.slice(0,this.pattern.length),i=e==this.pattern?0:e.toLowerCase()==this.folded?-200:null;return null==i?null:(this.matched=[0,e.length],this.score=i+(t.length==this.pattern.length?0:-100),this)}}const Oc=V.define({combine:t=>Zt(t,{activateOnTyping:!0,activateOnCompletion:()=>!1,activateOnTypingDelay:100,selectOnOpen:!0,override:null,closeOnBlur:!0,maxRenderedOptions:100,defaultKeymap:!0,tooltipClass:()=>\"\",optionClass:()=>\"\",aboveCursor:!1,icons:!0,addToOptions:[],positionInfo:mc,filterStrict:!1,compareCompletions:(t,e)=>(t.sortText||t.label).localeCompare(e.sortText||e.label),interactionDelay:75,updateSyncTime:100},{defaultKeymap:(t,e)=>t&&e,closeOnBlur:(t,e)=>t&&e,icons:(t,e)=>t&&e,tooltipClass:(t,e)=>i=>pc(t(i),e(i)),optionClass:(t,e)=>i=>pc(t(i),e(i)),addToOptions:(t,e)=>t.concat(e),filterStrict:(t,e)=>t||e})});function pc(t,e){return t?e?t+\" \"+e:t:e}function mc(t,e,i,n,r,s){let o,a,l=t.textDirection==ri.RTL,h=l,c=!1,u=\"top\",d=e.left-r.left,f=r.right-e.right,O=n.right-n.left,p=n.bottom-n.top;if(h&&d<Math.min(O,f)?h=!1:!h&&f<Math.min(O,d)&&(h=!0),O<=(h?d:f))o=Math.max(r.top,Math.min(i.top,r.bottom-p))-e.top,a=Math.min(400,h?d:f);else{c=!0,a=Math.min(400,(l?e.right:r.right-e.left)-30);let t=r.bottom-e.bottom;t>=p||t>e.top?o=i.bottom-e.top:(u=\"bottom\",o=e.bottom-i.top)}return{style:`${u}: ${o/((e.bottom-e.top)/s.offsetHeight)}px; max-width: ${a/((e.right-e.left)/s.offsetWidth)}px`,class:\"cm-completionInfo-\"+(c?l?\"left-narrow\":\"right-narrow\":h?\"left\":\"right\")}}function gc(t,e,i){if(t<=i)return{from:0,to:t};if(e<0&&(e=0),e<=t>>1){let t=Math.floor(e/i);return{from:t*i,to:(t+1)*i}}let n=Math.floor((t-e)/i);return{from:t-(n+1)*i,to:t-n*i}}class Qc{constructor(t,e,i){this.view=t,this.stateField=e,this.applyCompletion=i,this.info=null,this.infoDestroy=null,this.placeInfoReq={read:()=>this.measureInfo(),write:t=>this.placeInfo(t),key:this},this.space=null,this.currentClass=\"\";let n=t.state.field(e),{options:r,selected:s}=n.open,o=t.state.facet(Oc);this.optionContent=function(t){let e=t.addToOptions.slice();return t.icons&&e.push({render(t){let e=document.createElement(\"div\");return e.classList.add(\"cm-completionIcon\"),t.type&&e.classList.add(...t.type.split(/\\s+/g).map(t=>\"cm-completionIcon-\"+t)),e.setAttribute(\"aria-hidden\",\"true\"),e},position:20}),e.push({render(t,e,i,n){let r=document.createElement(\"span\");r.className=\"cm-completionLabel\";let s=t.displayLabel||t.label,o=0;for(let a=0;a<n.length;){let t=n[a++],e=n[a++];t>o&&r.appendChild(document.createTextNode(s.slice(o,t)));let i=r.appendChild(document.createElement(\"span\"));i.appendChild(document.createTextNode(s.slice(t,e))),i.className=\"cm-completionMatchedText\",o=e}return o<s.length&&r.appendChild(document.createTextNode(s.slice(o))),r},position:50},{render(t){if(!t.detail)return null;let e=document.createElement(\"span\");return e.className=\"cm-completionDetail\",e.textContent=t.detail,e},position:80}),e.sort((t,e)=>t.position-e.position).map(t=>t.render)}(o),this.optionClass=o.optionClass,this.tooltipClass=o.tooltipClass,this.range=gc(r.length,s,o.maxRenderedOptions),this.dom=document.createElement(\"div\"),this.dom.className=\"cm-tooltip-autocomplete\",this.updateTooltipClass(t.state),this.dom.addEventListener(\"mousedown\",i=>{let{options:n}=t.state.field(e).open;for(let e,r=i.target;r&&r!=this.dom;r=r.parentNode)if(\"LI\"==r.nodeName&&(e=/-(\\d+)$/.exec(r.id))&&+e[1]<n.length)return this.applyCompletion(t,n[+e[1]]),void i.preventDefault()}),this.dom.addEventListener(\"focusout\",e=>{let i=t.state.field(this.stateField,!1);i&&i.tooltip&&t.state.facet(Oc).closeOnBlur&&e.relatedTarget!=t.contentDOM&&t.dispatch({effects:uc.of(null)})}),this.showOptions(r,n.id)}mount(){this.updateSel()}showOptions(t,e){this.list&&this.list.remove(),this.list=this.dom.appendChild(this.createListBox(t,e,this.range)),this.list.addEventListener(\"scroll\",()=>{this.info&&this.view.requestMeasure(this.placeInfoReq)})}update(t){var e;let i=t.state.field(this.stateField),n=t.startState.field(this.stateField);if(this.updateTooltipClass(t.state),i!=n){let{options:r,selected:s,disabled:o}=i.open;n.open&&n.open.options==r||(this.range=gc(r.length,s,t.state.facet(Oc).maxRenderedOptions),this.showOptions(r,i.id)),this.updateSel(),o!=(null===(e=n.open)||void 0===e?void 0:e.disabled)&&this.dom.classList.toggle(\"cm-tooltip-autocomplete-disabled\",!!o)}}updateTooltipClass(t){let e=this.tooltipClass(t);if(e!=this.currentClass){for(let t of this.currentClass.split(\" \"))t&&this.dom.classList.remove(t);for(let t of e.split(\" \"))t&&this.dom.classList.add(t);this.currentClass=e}}positioned(t){this.space=t,this.info&&this.view.requestMeasure(this.placeInfoReq)}updateSel(){let t=this.view.state.field(this.stateField),e=t.open;(e.selected>-1&&e.selected<this.range.from||e.selected>=this.range.to)&&(this.range=gc(e.options.length,e.selected,this.view.state.facet(Oc).maxRenderedOptions),this.showOptions(e.options,t.id));let i=this.updateSelectedOption(e.selected);if(i){this.destroyInfo();let{completion:n}=e.options[e.selected],{info:r}=n;if(!r)return;let s=\"string\"==typeof r?document.createTextNode(r):r(n);if(!s)return;\"then\"in s?s.then(e=>{e&&this.view.state.field(this.stateField,!1)==t&&this.addInfoPane(e,n)}).catch(t=>Yi(this.view.state,t,\"completion info\")):(this.addInfoPane(s,n),i.setAttribute(\"aria-describedby\",this.info.id))}}addInfoPane(t,e){this.destroyInfo();let i=this.info=document.createElement(\"div\");if(i.className=\"cm-tooltip cm-completionInfo\",i.id=\"cm-completionInfo-\"+Math.floor(65535*Math.random()).toString(16),null!=t.nodeType)i.appendChild(t),this.infoDestroy=null;else{let{dom:e,destroy:n}=t;i.appendChild(e),this.infoDestroy=n||null}this.dom.appendChild(i),this.view.requestMeasure(this.placeInfoReq)}updateSelectedOption(t){let e=null;for(let i=this.list.firstChild,n=this.range.from;i;i=i.nextSibling,n++)\"LI\"==i.nodeName&&i.id?n==t?i.hasAttribute(\"aria-selected\")||(i.setAttribute(\"aria-selected\",\"true\"),e=i):i.hasAttribute(\"aria-selected\")&&(i.removeAttribute(\"aria-selected\"),i.removeAttribute(\"aria-describedby\")):n--;return e&&function(t,e){let i=t.getBoundingClientRect(),n=e.getBoundingClientRect(),r=i.height/t.offsetHeight;n.top<i.top?t.scrollTop-=(i.top-n.top)/r:n.bottom>i.bottom&&(t.scrollTop+=(n.bottom-i.bottom)/r)}(this.list,e),e}measureInfo(){let t=this.dom.querySelector(\"[aria-selected]\");if(!t||!this.info)return null;let e=this.dom.getBoundingClientRect(),i=this.info.getBoundingClientRect(),n=t.getBoundingClientRect(),r=this.space;if(!r){let t=this.dom.ownerDocument.documentElement;r={left:0,top:0,right:t.clientWidth,bottom:t.clientHeight}}return n.top>Math.min(r.bottom,e.bottom)-10||n.bottom<Math.max(r.top,e.top)+10?null:this.view.state.facet(Oc).positionInfo(this.view,e,n,i,r,this.dom)}placeInfo(t){this.info&&(t?(t.style&&(this.info.style.cssText=t.style),this.info.className=\"cm-tooltip cm-completionInfo \"+(t.class||\"\")):this.info.style.cssText=\"top: -1e6px\")}createListBox(t,e,i){const n=document.createElement(\"ul\");n.id=e,n.setAttribute(\"role\",\"listbox\"),n.setAttribute(\"aria-expanded\",\"true\"),n.setAttribute(\"aria-label\",this.view.state.phrase(\"Completions\")),n.addEventListener(\"mousedown\",t=>{t.target==n&&t.preventDefault()});let r=null;for(let s=i.from;s<i.to;s++){let{completion:o,match:a}=t[s],{section:l}=o;if(l){let t=\"string\"==typeof l?l:l.name;if(t!=r&&(s>i.from||0==i.from))if(r=t,\"string\"!=typeof l&&l.header)n.appendChild(l.header(l));else{n.appendChild(document.createElement(\"completion-section\")).textContent=t}}const h=n.appendChild(document.createElement(\"li\"));h.id=e+\"-\"+s,h.setAttribute(\"role\",\"option\");let c=this.optionClass(o);c&&(h.className=c);for(let t of this.optionContent){let e=t(o,this.view.state,this.view,a);e&&h.appendChild(e)}}return i.from&&n.classList.add(\"cm-completionListIncompleteTop\"),i.to<t.length&&n.classList.add(\"cm-completionListIncompleteBottom\"),n}destroyInfo(){this.info&&(this.infoDestroy&&this.infoDestroy(),this.info.remove(),this.info=null)}destroy(){this.destroyInfo()}}function bc(t,e){return i=>new Qc(i,t,e)}function vc(t){return 100*(t.boost||0)+(t.apply?10:0)+(t.info?5:0)+(t.type?1:0)}class wc{constructor(t,e,i,n,r,s){this.options=t,this.attrs=e,this.tooltip=i,this.timestamp=n,this.selected=r,this.disabled=s}setSelected(t,e){return t==this.selected||t>=this.options.length?this:new wc(this.options,kc(e,t),this.tooltip,this.timestamp,t,this.disabled)}static build(t,e,i,n,r,s){if(n&&!s&&t.some(t=>t.isPending))return n.setDisabled();let o=function(t,e){let i=[],n=null,r=null,s=t=>{i.push(t);let{section:e}=t.completion;if(e){n||(n=[]);let t=\"string\"==typeof e?e:e.name;n.some(e=>e.name==t)||n.push(\"string\"==typeof e?{name:t}:e)}},o=e.facet(Oc);for(let c of t)if(c.hasResult()){let t=c.result.getMatch;if(!1===c.result.filter)for(let e of c.result.options)s(new nc(e,c.source,t?t(e):[],1e9-i.length));else{let i,n=e.sliceDoc(c.from,c.to),a=o.filterStrict?new fc(n):new dc(n);for(let e of c.result.options)if(i=a.match(e.label)){let n=e.displayLabel?t?t(e,i.matched):[]:i.matched,o=i.score+(e.boost||0);if(s(new nc(e,c.source,n,o)),\"object\"==typeof e.section&&\"dynamic\"===e.section.rank){let{name:t}=e.section;r||(r=Object.create(null)),r[t]=Math.max(o,r[t]||-1e9)}}}}if(n){let t=Object.create(null),e=0,s=(t,e)=>(\"dynamic\"===t.rank&&\"dynamic\"===e.rank?r[e.name]-r[t.name]:0)||(\"number\"==typeof t.rank?t.rank:1e9)-(\"number\"==typeof e.rank?e.rank:1e9)||(t.name<e.name?-1:1);for(let i of n.sort(s))e-=1e5,t[i.name]=e;for(let n of i){let{section:e}=n.completion;e&&(n.score+=t[\"string\"==typeof e?e:e.name])}}let a=[],l=null,h=o.compareCompletions;for(let c of i.sort((t,e)=>e.score-t.score||h(t.completion,e.completion))){let t=c.completion;!l||l.label!=t.label||l.detail!=t.detail||null!=l.type&&null!=t.type&&l.type!=t.type||l.apply!=t.apply||l.boost!=t.boost?a.push(c):vc(c.completion)>vc(l)&&(a[a.length-1]=c),l=c.completion}return a}(t,e);if(!o.length)return n&&t.some(t=>t.isPending)?n.setDisabled():null;let a=e.facet(Oc).selectOnOpen?0:-1;if(n&&n.selected!=a&&-1!=n.selected){let t=n.options[n.selected].completion;for(let e=0;e<o.length;e++)if(o[e].completion==t){a=e;break}}return new wc(o,kc(i,a),{pos:t.reduce((t,e)=>e.hasResult()?Math.min(t,e.from):t,1e8),create:Rc,above:r.aboveCursor},n?n.timestamp:Date.now(),a,!1)}map(t){return new wc(this.options,this.attrs,{...this.tooltip,pos:t.mapPos(this.tooltip.pos)},this.timestamp,this.selected,this.disabled)}setDisabled(){return new wc(this.options,this.attrs,this.tooltip,this.timestamp,this.selected,!0)}}class xc{constructor(t,e,i){this.active=t,this.id=e,this.open=i}static start(){return new xc($c,\"cm-ac-\"+Math.floor(2e6*Math.random()).toString(36),null)}update(t){let{state:e}=t,i=e.facet(Oc),n=(i.override||e.languageDataAt(\"autocomplete\",rc(e)).map(hc)).map(e=>(this.active.find(t=>t.source==e)||new Tc(e,this.active.some(t=>0!=t.state)?1:0)).update(t,i));n.length==this.active.length&&n.every((t,e)=>t==this.active[e])&&(n=this.active);let r=this.open,s=t.effects.some(t=>t.is(Zc));r&&t.docChanged&&(r=r.map(t.changes)),t.selection||n.some(e=>e.hasResult()&&t.changes.touchesRange(e.from,e.to))||!function(t,e){if(t==e)return!0;for(let i=0,n=0;;){for(;i<t.length&&!t[i].hasResult();)i++;for(;n<e.length&&!e[n].hasResult();)n++;let r=i==t.length,s=n==e.length;if(r||s)return r==s;if(t[i++].result!=e[n++].result)return!1}}(n,this.active)||s?r=wc.build(n,e,this.id,r,i,s):r&&r.disabled&&!n.some(t=>t.isPending)&&(r=null),!r&&n.every(t=>!t.isPending)&&n.some(t=>t.hasResult())&&(n=n.map(t=>t.hasResult()?new Tc(t.source,0):t));for(let o of t.effects)o.is(Xc)&&(r=r&&r.setSelected(o.value,this.id));return n==this.active&&r==this.open?this:new xc(n,this.id,r)}get tooltip(){return this.open?this.open.tooltip:null}get attrs(){return this.open?this.open.attrs:this.active.length?Sc:yc}}const Sc={\"aria-autocomplete\":\"list\"},yc={};function kc(t,e){let i={\"aria-autocomplete\":\"list\",\"aria-haspopup\":\"listbox\",\"aria-controls\":t};return e>-1&&(i[\"aria-activedescendant\"]=t+\"-\"+e),i}const $c=[];function Pc(t,e){if(t.isUserEvent(\"input.complete\")){let i=t.annotation(oc);if(i&&e.activateOnCompletion(i))return 12}let i=t.isUserEvent(\"input.type\");return i&&e.activateOnTyping?5:i?1:t.isUserEvent(\"delete.backward\")?2:t.selection?8:t.docChanged?16:0}class Tc{constructor(t,e,i=!1){this.source=t,this.state=e,this.explicit=i}hasResult(){return!1}get isPending(){return 1==this.state}update(t,e){let i=Pc(t,e),n=this;(8&i||16&i&&this.touches(t))&&(n=new Tc(n.source,0)),4&i&&0==n.state&&(n=new Tc(this.source,1)),n=n.updateFor(t,i);for(let r of t.effects)if(r.is(cc))n=new Tc(n.source,1,r.value);else if(r.is(uc))n=new Tc(n.source,0);else if(r.is(Zc))for(let t of r.value)t.source==n.source&&(n=t);return n}updateFor(t,e){return this.map(t.changes)}map(t){return this}touches(t){return t.changes.touchesRange(rc(t.state))}}class Cc extends Tc{constructor(t,e,i,n,r,s){super(t,3,e),this.limit=i,this.result=n,this.from=r,this.to=s}hasResult(){return!0}updateFor(t,e){var i;if(!(3&e))return this.map(t.changes);let n=this.result;n.map&&!t.changes.empty&&(n=n.map(n,t.changes));let r=t.changes.mapPos(this.from),s=t.changes.mapPos(this.to,1),o=rc(t.state);if(o>s||!n||2&e&&(rc(t.startState)==this.from||o<this.limit))return new Tc(this.source,4&e?1:0);let a=t.changes.mapPos(this.limit);return function(t,e,i,n){if(!t)return!1;let r=e.sliceDoc(i,n);return\"function\"==typeof t?t(r,i,n,e):sc(t,!0).test(r)}(n.validFor,t.state,r,s)?new Cc(this.source,this.explicit,a,n,r,s):n.update&&(n=n.update(n,r,s,new tc(t.state,o,!1)))?new Cc(this.source,this.explicit,a,n,n.from,null!==(i=n.to)&&void 0!==i?i:rc(t.state)):new Tc(this.source,1,this.explicit)}map(t){if(t.empty)return this;return(this.result.map?this.result.map(this.result,t):this.result)?new Cc(this.source,this.explicit,t.mapPos(this.limit),this.result,t.mapPos(this.from),t.mapPos(this.to,1)):new Tc(this.source,0)}touches(t){return t.changes.touchesRange(this.from,this.to)}}const Zc=gt.define({map:(t,e)=>t.map(t=>t.map(e))}),Xc=gt.define(),Ac=N.define({create:()=>xc.start(),update:(t,e)=>t.update(e),provide:t=>[Qo.from(t,t=>t.tooltip),fs.contentAttributes.from(t,t=>t.attrs)]});function Mc(t,e){const i=e.completion.apply||e.completion.label;let n=t.state.field(Ac).active.find(t=>t.source==e.source);return n instanceof Cc&&(\"string\"==typeof i?t.dispatch({...ac(t.state,i,n.from,n.to),annotations:oc.of(e.completion)}):i(t,e.completion,n.from,n.to),!0)}const Rc=bc(Ac,Mc);function zc(t,e=\"option\"){return i=>{let n=i.state.field(Ac,!1);if(!n||!n.open||n.open.disabled||Date.now()-n.open.timestamp<i.state.facet(Oc).interactionDelay)return!1;let r,s=1;\"page\"==e&&(r=ko(i,n.open.tooltip))&&(s=Math.max(2,Math.floor(r.dom.offsetHeight/r.dom.querySelector(\"li\").offsetHeight)-1));let{length:o}=n.open.options,a=n.open.selected>-1?n.open.selected+s*(t?1:-1):t?0:o-1;return a<0?a=\"page\"==e?0:o-1:a>=o&&(a=\"page\"==e?o-1:0),i.dispatch({effects:Xc.of(a)}),!0}}const _c=t=>!!t.state.field(Ac,!1)&&(t.dispatch({effects:cc.of(!0)}),!0);class Ec{constructor(t,e){this.active=t,this.context=e,this.time=Date.now(),this.updates=[],this.done=void 0}}const Yc=Wi.fromClass(class{constructor(t){this.view=t,this.debounceUpdate=-1,this.running=[],this.debounceAccept=-1,this.pendingStart=!1,this.composing=0;for(let e of t.state.field(Ac).active)e.isPending&&this.startQuery(e)}update(t){let e=t.state.field(Ac),i=t.state.facet(Oc);if(!t.selectionSet&&!t.docChanged&&t.startState.field(Ac)==e)return;let n=t.transactions.some(t=>{let e=Pc(t,i);return 8&e||(t.selection||t.docChanged)&&!(3&e)});for(let o=0;o<this.running.length;o++){let e=this.running[o];if(n||e.context.abortOnDocChange&&t.docChanged||e.updates.length+t.transactions.length>50&&Date.now()-e.time>1e3){for(let t of e.context.abortListeners)try{t()}catch(s){Yi(this.view.state,s)}e.context.abortListeners=null,this.running.splice(o--,1)}else e.updates.push(...t.transactions)}this.debounceUpdate>-1&&clearTimeout(this.debounceUpdate),t.transactions.some(t=>t.effects.some(t=>t.is(cc)))&&(this.pendingStart=!0);let r=this.pendingStart?50:i.activateOnTypingDelay;if(this.debounceUpdate=e.active.some(t=>t.isPending&&!this.running.some(e=>e.active.source==t.source))?setTimeout(()=>this.startUpdate(),r):-1,0!=this.composing)for(let o of t.transactions)o.isUserEvent(\"input.type\")?this.composing=2:2==this.composing&&o.selection&&(this.composing=3)}startUpdate(){this.debounceUpdate=-1,this.pendingStart=!1;let{state:t}=this.view,e=t.field(Ac);for(let i of e.active)i.isPending&&!this.running.some(t=>t.active.source==i.source)&&this.startQuery(i);this.running.length&&e.open&&e.open.disabled&&(this.debounceAccept=setTimeout(()=>this.accept(),this.view.state.facet(Oc).updateSyncTime))}startQuery(t){let{state:e}=this.view,i=rc(e),n=new tc(e,i,t.explicit,this.view),r=new Ec(t,n);this.running.push(r),Promise.resolve(t.source(n)).then(t=>{r.context.aborted||(r.done=t||null,this.scheduleAccept())},t=>{this.view.dispatch({effects:uc.of(null)}),Yi(this.view.state,t)})}scheduleAccept(){this.running.every(t=>void 0!==t.done)?this.accept():this.debounceAccept<0&&(this.debounceAccept=setTimeout(()=>this.accept(),this.view.state.facet(Oc).updateSyncTime))}accept(){var t;this.debounceAccept>-1&&clearTimeout(this.debounceAccept),this.debounceAccept=-1;let e=[],i=this.view.state.facet(Oc),n=this.view.state.field(Ac);for(let r=0;r<this.running.length;r++){let s=this.running[r];if(void 0===s.done)continue;if(this.running.splice(r--,1),s.done){let n=rc(s.updates.length?s.updates[0].startState:this.view.state),r=Math.min(n,s.done.from+(s.active.explicit?0:1)),o=new Cc(s.active.source,s.active.explicit,r,s.done,s.done.from,null!==(t=s.done.to)&&void 0!==t?t:n);for(let t of s.updates)o=o.update(t,i);if(o.hasResult()){e.push(o);continue}}let o=n.active.find(t=>t.source==s.active.source);if(o&&o.isPending)if(null==s.done){let t=new Tc(s.active.source,0);for(let e of s.updates)t=t.update(e,i);t.isPending||e.push(t)}else this.startQuery(o)}(e.length||n.open&&n.open.disabled)&&this.view.dispatch({effects:Zc.of(e)})}},{eventHandlers:{blur(t){let e=this.view.state.field(Ac,!1);if(e&&e.tooltip&&this.view.state.facet(Oc).closeOnBlur){let i=e.open&&ko(this.view,e.open.tooltip);i&&i.dom.contains(t.relatedTarget)||setTimeout(()=>this.view.dispatch({effects:uc.of(null)}),10)}},compositionstart(){this.composing=1},compositionend(){3==this.composing&&setTimeout(()=>this.view.dispatch({effects:cc.of(!1)}),20),this.composing=0}}}),Lc=\"object\"==typeof navigator&&/Win/.test(navigator.platform),qc=tt.highest(fs.domEventHandlers({keydown(t,e){let i=e.state.field(Ac,!1);if(!i||!i.open||i.open.disabled||i.open.selected<0||t.key.length>1||t.ctrlKey&&(!Lc||!t.altKey)||t.metaKey)return!1;let n=i.open.options[i.open.selected],r=i.active.find(t=>t.source==n.source),s=n.completion.commitCharacters||r.result.commitCharacters;return s&&s.indexOf(t.key)>-1&&Mc(e,n),!1}})),Vc=fs.baseTheme({\".cm-tooltip.cm-tooltip-autocomplete\":{\"& > ul\":{fontFamily:\"monospace\",whiteSpace:\"nowrap\",overflow:\"hidden auto\",maxWidth_fallback:\"700px\",maxWidth:\"min(700px, 95vw)\",minWidth:\"250px\",maxHeight:\"10em\",height:\"100%\",listStyle:\"none\",margin:0,padding:0,\"& > li, & > completion-section\":{padding:\"1px 3px\",lineHeight:1.2},\"& > li\":{overflowX:\"hidden\",textOverflow:\"ellipsis\",cursor:\"pointer\"},\"& > completion-section\":{display:\"list-item\",borderBottom:\"1px solid silver\",paddingLeft:\"0.5em\",opacity:.7}}},\"&light .cm-tooltip-autocomplete ul li[aria-selected]\":{background:\"#17c\",color:\"white\"},\"&light .cm-tooltip-autocomplete-disabled ul li[aria-selected]\":{background:\"#777\"},\"&dark .cm-tooltip-autocomplete ul li[aria-selected]\":{background:\"#347\",color:\"white\"},\"&dark .cm-tooltip-autocomplete-disabled ul li[aria-selected]\":{background:\"#444\"},\".cm-completionListIncompleteTop:before, .cm-completionListIncompleteBottom:after\":{content:'\"···\"',opacity:.5,display:\"block\",textAlign:\"center\"},\".cm-tooltip.cm-completionInfo\":{position:\"absolute\",padding:\"3px 9px\",width:\"max-content\",maxWidth:\"400px\",boxSizing:\"border-box\",whiteSpace:\"pre-line\"},\".cm-completionInfo.cm-completionInfo-left\":{right:\"100%\"},\".cm-completionInfo.cm-completionInfo-right\":{left:\"100%\"},\".cm-completionInfo.cm-completionInfo-left-narrow\":{right:\"30px\"},\".cm-completionInfo.cm-completionInfo-right-narrow\":{left:\"30px\"},\"&light .cm-snippetField\":{backgroundColor:\"#00000022\"},\"&dark .cm-snippetField\":{backgroundColor:\"#ffffff22\"},\".cm-snippetFieldPosition\":{verticalAlign:\"text-top\",width:0,height:\"1.15em\",display:\"inline-block\",margin:\"0 -0.7px -.7em\",borderLeft:\"1.4px dotted #888\"},\".cm-completionMatchedText\":{textDecoration:\"underline\"},\".cm-completionDetail\":{marginLeft:\"0.5em\",fontStyle:\"italic\"},\".cm-completionIcon\":{fontSize:\"90%\",width:\".8em\",display:\"inline-block\",textAlign:\"center\",paddingRight:\".6em\",opacity:\"0.6\",boxSizing:\"content-box\"},\".cm-completionIcon-function, .cm-completionIcon-method\":{\"&:after\":{content:\"'ƒ'\"}},\".cm-completionIcon-class\":{\"&:after\":{content:\"'○'\"}},\".cm-completionIcon-interface\":{\"&:after\":{content:\"'◌'\"}},\".cm-completionIcon-variable\":{\"&:after\":{content:\"'𝑥'\"}},\".cm-completionIcon-constant\":{\"&:after\":{content:\"'𝐶'\"}},\".cm-completionIcon-type\":{\"&:after\":{content:\"'𝑡'\"}},\".cm-completionIcon-enum\":{\"&:after\":{content:\"'∪'\"}},\".cm-completionIcon-property\":{\"&:after\":{content:\"'□'\"}},\".cm-completionIcon-keyword\":{\"&:after\":{content:\"'🔑︎'\"}},\".cm-completionIcon-namespace\":{\"&:after\":{content:\"'▢'\"}},\".cm-completionIcon-text\":{\"&:after\":{content:\"'abc'\",fontSize:\"50%\",verticalAlign:\"middle\"}}});class Wc{constructor(t,e,i,n){this.field=t,this.line=e,this.from=i,this.to=n}}class Dc{constructor(t,e,i){this.field=t,this.from=e,this.to=i}map(t){let e=t.mapPos(this.from,-1,T.TrackDel),i=t.mapPos(this.to,1,T.TrackDel);return null==e||null==i?null:new Dc(this.field,e,i)}}class Bc{constructor(t,e){this.lines=t,this.fieldPositions=e}instantiate(t,e){let i=[],n=[e],r=t.doc.lineAt(e),s=/^\\s*/.exec(r.text)[0];for(let o of this.lines){if(i.length){let i=s,r=/^\\t*/.exec(o)[0].length;for(let e=0;e<r;e++)i+=t.facet(Wl);n.push(e+i.length-r),o=i+o.slice(r)}i.push(o),e+=o.length+1}return{text:i,ranges:this.fieldPositions.map(t=>new Dc(t.field,n[t.line]+t.from,n[t.line]+t.to))}}static parse(t){let e,i=[],n=[],r=[];for(let s of t.split(/\\r\\n?|\\n/)){for(;e=/[#$]\\{(?:(\\d+)(?::([^{}]*))?|((?:\\\\[{}]|[^{}])*))\\}/.exec(s);){let t=e[1]?+e[1]:null,o=e[2]||e[3]||\"\",a=-1,l=o.replace(/\\\\[{}]/g,t=>t[1]);for(let e=0;e<i.length;e++)(null!=t?i[e].seq==t:l&&i[e].name==l)&&(a=e);if(a<0){let e=0;for(;e<i.length&&(null==t||null!=i[e].seq&&i[e].seq<t);)e++;i.splice(e,0,{seq:t,name:l}),a=e;for(let t of r)t.field>=a&&t.field++}for(let i of r)if(i.line==n.length&&i.from>e.index){let t=e[2]?3+(e[1]||\"\").length:2;i.from-=t,i.to-=t}r.push(new Wc(a,n.length,e.index,e.index+l.length)),s=s.slice(0,e.index)+o+s.slice(e.index+e[0].length)}s=s.replace(/\\\\([{}])/g,(t,e,i)=>{for(let s of r)s.line==n.length&&s.from>i&&(s.from--,s.to--);return e}),n.push(s)}return new Bc(n,r)}}let jc=Ce.widget({widget:new class extends Pe{toDOM(){let t=document.createElement(\"span\");return t.className=\"cm-snippetFieldPosition\",t}ignoreEvent(){return!1}}}),Ic=Ce.mark({class:\"cm-snippetField\"});class Gc{constructor(t,e){this.ranges=t,this.active=e,this.deco=Ce.set(t.map(t=>(t.from==t.to?jc:Ic).range(t.from,t.to)),!0)}map(t){let e=[];for(let i of this.ranges){let n=i.map(t);if(!n)return null;e.push(n)}return new Gc(e,this.active)}selectionInsideField(t){return t.ranges.every(t=>this.ranges.some(e=>e.field==this.active&&e.from<=t.from&&e.to>=t.to))}}const Nc=gt.define({map:(t,e)=>t&&t.map(e)}),Uc=gt.define(),Hc=N.define({create:()=>null,update(t,e){for(let i of e.effects){if(i.is(Nc))return i.value;if(i.is(Uc)&&t)return new Gc(t.ranges,i.value)}return t&&e.docChanged&&(t=t.map(e.changes)),t&&e.selection&&!t.selectionInsideField(e.selection)&&(t=null),t},provide:t=>fs.decorations.from(t,t=>t?t.deco:Ce.none)});function Fc(t,e){return Y.create(t.filter(t=>t.field==e).map(t=>Y.range(t.from,t.to)))}function Kc(t){let e=Bc.parse(t);return(t,i,n,r)=>{let{text:s,ranges:o}=e.instantiate(t.state,n),{main:a}=t.state.selection,l={changes:{from:n,to:r==a.from?a.to:r,insert:f.of(s)},scrollIntoView:!0,annotations:i?[oc.of(i),Qt.userEvent.of(\"input.complete\")]:void 0};if(o.length&&(l.selection=Fc(o,0)),o.some(t=>t.field>0)){let e=new Gc(o,0),i=l.effects=[Nc.of(e)];void 0===t.state.field(Hc,!1)&&i.push(gt.appendConfig.of([Hc,iu,ru,Vc]))}t.dispatch(t.state.update(l))}}function Jc(t){return({state:e,dispatch:i})=>{let n=e.field(Hc,!1);if(!n||t<0&&0==n.active)return!1;let r=n.active+t,s=t>0&&!n.ranges.some(e=>e.field==r+t);return i(e.update({selection:Fc(n.ranges,r),effects:Nc.of(s?null:new Gc(n.ranges,r)),scrollIntoView:!0})),!0}}const tu=[{key:\"Tab\",run:Jc(1),shift:Jc(-1)},{key:\"Escape\",run:({state:t,dispatch:e})=>!!t.field(Hc,!1)&&(e(t.update({effects:Nc.of(null)})),!0)}],eu=V.define({combine:t=>t.length?t[0]:tu}),iu=tt.highest(ws.compute([eu],t=>t.facet(eu)));function nu(t,e){return{...e,apply:Kc(t)}}const ru=fs.domEventHandlers({mousedown(t,e){let i,n=e.state.field(Hc,!1);if(!n||null==(i=e.posAtCoords({x:t.clientX,y:t.clientY})))return!1;let r=n.ranges.find(t=>t.from<=i&&t.to>=i);return!(!r||r.field==n.active)&&(e.dispatch({selection:Fc(n.ranges,r.field),effects:Nc.of(n.ranges.some(t=>t.field>r.field)?new Gc(n.ranges,r.field):null),scrollIntoView:!0}),!0)}}),su={brackets:[\"(\",\"[\",\"{\",\"'\",'\"'],before:\")]}:;>\",stringPrefixes:[]},ou=gt.define({map(t,e){let i=e.mapPos(t,-1,T.TrackAfter);return null==i?void 0:i}}),au=new class extends Xt{};au.startSide=1,au.endSide=-1;const lu=N.define({create:()=>_t.empty,update(t,e){if(t=t.map(e.changes),e.selection){let i=e.state.doc.lineAt(e.selection.main.head);t=t.update({filter:t=>t>=i.from&&t<=i.to})}for(let i of e.effects)i.is(ou)&&(t=t.update({add:[au.range(i.value,i.value+1)]}));return t}});const hu=\"()[]{}<>«»»«［］｛｝\";function cu(t){for(let e=0;e<16;e+=2)if(hu.charCodeAt(e)==t)return hu.charAt(e+1);return k(t<128?t:t+1)}function uu(t,e){return t.languageDataAt(\"closeBrackets\",e)[0]||su}const du=\"object\"==typeof navigator&&/Android\\b/.test(navigator.userAgent),fu=fs.inputHandler.of((t,e,i,n)=>{if((du?t.composing:t.compositionStarted)||t.state.readOnly)return!1;let r=t.state.selection.main;if(n.length>2||2==n.length&&1==$(y(n,0))||e!=r.from||i!=r.to)return!1;let s=function(t,e){let i=uu(t,t.selection.main.head),n=i.brackets||su.brackets;for(let r of n){let s=cu(y(r,0));if(e==r)return s==r?bu(t,r,n.indexOf(r+r+r)>-1,i):gu(t,r,s,i.before||su.before);if(e==s&&pu(t,t.selection.main.from))return Qu(t,r,s)}return null}(t.state,n);return!!s&&(t.dispatch(s),!0)}),Ou=[{key:\"Backspace\",run:({state:t,dispatch:e})=>{if(t.readOnly)return!1;let i=uu(t,t.selection.main.head).brackets||su.brackets,n=null,r=t.changeByRange(e=>{if(e.empty){let n=function(t,e){let i=t.sliceString(e-2,e);return $(y(i,0))==i.length?i:i.slice(1)}(t.doc,e.head);for(let r of i)if(r==n&&mu(t.doc,e.head)==cu(y(r,0)))return{changes:{from:e.head-r.length,to:e.head+r.length},range:Y.cursor(e.head-r.length)}}return{range:n=e}});return n||e(t.update(r,{scrollIntoView:!0,userEvent:\"delete.backward\"})),!n}}];function pu(t,e){let i=!1;return t.field(lu).between(0,t.doc.length,t=>{t==e&&(i=!0)}),i}function mu(t,e){let i=t.sliceString(e,e+2);return i.slice(0,$(y(i,0)))}function gu(t,e,i,n){let r=null,s=t.changeByRange(s=>{if(!s.empty)return{changes:[{insert:e,from:s.from},{insert:i,from:s.to}],effects:ou.of(s.to+e.length),range:Y.range(s.anchor+e.length,s.head+e.length)};let o=mu(t.doc,s.head);return!o||/\\s/.test(o)||n.indexOf(o)>-1?{changes:{insert:e+i,from:s.head},effects:ou.of(s.head+e.length),range:Y.cursor(s.head+e.length)}:{range:r=s}});return r?null:t.update(s,{scrollIntoView:!0,userEvent:\"input.type\"})}function Qu(t,e,i){let n=null,r=t.changeByRange(e=>e.empty&&mu(t.doc,e.head)==i?{changes:{from:e.head,to:e.head+i.length,insert:i},range:Y.cursor(e.head+i.length)}:n={range:e});return n?null:t.update(r,{scrollIntoView:!0,userEvent:\"input.type\"})}function bu(t,e,i,n){let r=n.stringPrefixes||su.stringPrefixes,s=null,o=t.changeByRange(n=>{if(!n.empty)return{changes:[{insert:e,from:n.from},{insert:e,from:n.to}],effects:ou.of(n.to+e.length),range:Y.range(n.anchor+e.length,n.head+e.length)};let o,a=n.head,l=mu(t.doc,a);if(l==e){if(vu(t,a))return{changes:{insert:e+e,from:a},effects:ou.of(a+e.length),range:Y.cursor(a+e.length)};if(pu(t,a)){let n=i&&t.sliceDoc(a,a+3*e.length)==e+e+e?e+e+e:e;return{changes:{from:a,to:a+n.length,insert:n},range:Y.cursor(a+n.length)}}}else{if(i&&t.sliceDoc(a-2*e.length,a)==e+e&&(o=wu(t,a-2*e.length,r))>-1&&vu(t,o))return{changes:{insert:e+e+e+e,from:a},effects:ou.of(a+e.length),range:Y.cursor(a+e.length)};if(t.charCategorizer(a)(l)!=kt.Word&&wu(t,a,r)>-1&&!function(t,e,i,n){let r=Cl(t).resolveInner(e,-1),s=n.reduce((t,e)=>Math.max(t,e.length),0);for(let o=0;o<5;o++){let o=t.sliceDoc(r.from,Math.min(r.to,r.from+i.length+s)),a=o.indexOf(i);if(!a||a>-1&&n.indexOf(o.slice(0,a))>-1){let e=r.firstChild;for(;e&&e.from==r.from&&e.to-e.from>i.length+a;){if(t.sliceDoc(e.to-i.length,e.to)==i)return!1;e=e.firstChild}return!0}let l=r.to==e&&r.parent;if(!l)break;r=l}return!1}(t,a,e,r))return{changes:{insert:e+e,from:a},effects:ou.of(a+e.length),range:Y.cursor(a+e.length)}}return{range:s=n}});return s?null:t.update(o,{scrollIntoView:!0,userEvent:\"input.type\"})}function vu(t,e){let i=Cl(t).resolveInner(e+1);return i.parent&&i.from==e}function wu(t,e,i){let n=t.charCategorizer(e);if(n(t.sliceDoc(e-1,e))!=kt.Word)return e;for(let r of i){let i=e-r.length;if(t.sliceDoc(i,e)==r&&n(t.sliceDoc(i-1,i))!=kt.Word)return i}return-1}const xu=[{key:\"Ctrl-Space\",run:_c},{mac:\"Alt-`\",run:_c},{mac:\"Alt-i\",run:_c},{key:\"Escape\",run:t=>{let e=t.state.field(Ac,!1);return!(!e||!e.active.some(t=>0!=t.state))&&(t.dispatch({effects:uc.of(null)}),!0)}},{key:\"ArrowDown\",run:zc(!0)},{key:\"ArrowUp\",run:zc(!1)},{key:\"PageDown\",run:zc(!0,\"page\")},{key:\"PageUp\",run:zc(!1,\"page\")},{key:\"Enter\",run:t=>{let e=t.state.field(Ac,!1);return!(t.state.readOnly||!e||!e.open||e.open.selected<0||e.open.disabled||Date.now()-e.open.timestamp<t.state.facet(Oc).interactionDelay)&&Mc(t,e.open.options[e.open.selected])}}],Su=tt.highest(ws.computeN([Oc],t=>t.facet(Oc).defaultKeymap?[xu]:[]));class yu{static create(t,e,i,n,r){return new yu(t,e,i,n+(n<<8)+t+(e<<4)|0,r,[],[])}constructor(t,e,i,n,r,s,o){this.type=t,this.value=e,this.from=i,this.hash=n,this.end=r,this.children=s,this.positions=o,this.hashProp=[[sa.contextHash,n]]}addChild(t,e){t.prop(sa.contextHash)!=this.hash&&(t=new Oa(t.type,t.children,t.positions,t.length,this.hashProp)),this.children.push(t),this.positions.push(e)}toTree(t,e=this.end){let i=this.children.length-1;return i>=0&&(e=Math.max(e,this.positions[i]+this.children[i].length+this.from)),new Oa(t.types[this.type],this.children,this.positions,e-this.from).balance({makeTree:(t,e,i)=>new Oa(la.none,t,e,i,this.hashProp)})}}var ku,$u;($u=ku||(ku={}))[$u.Document=1]=\"Document\",$u[$u.CodeBlock=2]=\"CodeBlock\",$u[$u.FencedCode=3]=\"FencedCode\",$u[$u.Blockquote=4]=\"Blockquote\",$u[$u.HorizontalRule=5]=\"HorizontalRule\",$u[$u.BulletList=6]=\"BulletList\",$u[$u.OrderedList=7]=\"OrderedList\",$u[$u.ListItem=8]=\"ListItem\",$u[$u.ATXHeading1=9]=\"ATXHeading1\",$u[$u.ATXHeading2=10]=\"ATXHeading2\",$u[$u.ATXHeading3=11]=\"ATXHeading3\",$u[$u.ATXHeading4=12]=\"ATXHeading4\",$u[$u.ATXHeading5=13]=\"ATXHeading5\",$u[$u.ATXHeading6=14]=\"ATXHeading6\",$u[$u.SetextHeading1=15]=\"SetextHeading1\",$u[$u.SetextHeading2=16]=\"SetextHeading2\",$u[$u.HTMLBlock=17]=\"HTMLBlock\",$u[$u.LinkReference=18]=\"LinkReference\",$u[$u.Paragraph=19]=\"Paragraph\",$u[$u.CommentBlock=20]=\"CommentBlock\",$u[$u.ProcessingInstructionBlock=21]=\"ProcessingInstructionBlock\",$u[$u.Escape=22]=\"Escape\",$u[$u.Entity=23]=\"Entity\",$u[$u.HardBreak=24]=\"HardBreak\",$u[$u.Emphasis=25]=\"Emphasis\",$u[$u.StrongEmphasis=26]=\"StrongEmphasis\",$u[$u.Link=27]=\"Link\",$u[$u.Image=28]=\"Image\",$u[$u.InlineCode=29]=\"InlineCode\",$u[$u.HTMLTag=30]=\"HTMLTag\",$u[$u.Comment=31]=\"Comment\",$u[$u.ProcessingInstruction=32]=\"ProcessingInstruction\",$u[$u.Autolink=33]=\"Autolink\",$u[$u.HeaderMark=34]=\"HeaderMark\",$u[$u.QuoteMark=35]=\"QuoteMark\",$u[$u.ListMark=36]=\"ListMark\",$u[$u.LinkMark=37]=\"LinkMark\",$u[$u.EmphasisMark=38]=\"EmphasisMark\",$u[$u.CodeMark=39]=\"CodeMark\",$u[$u.CodeText=40]=\"CodeText\",$u[$u.CodeInfo=41]=\"CodeInfo\",$u[$u.LinkTitle=42]=\"LinkTitle\",$u[$u.LinkLabel=43]=\"LinkLabel\",$u[$u.URL=44]=\"URL\";class Pu{constructor(t,e){this.start=t,this.content=e,this.marks=[],this.parsers=[]}}class Tu{constructor(){this.text=\"\",this.baseIndent=0,this.basePos=0,this.depth=0,this.markers=[],this.pos=0,this.indent=0,this.next=-1}forward(){this.basePos>this.pos&&this.forwardInner()}forwardInner(){let t=this.skipSpace(this.basePos);this.indent=this.countIndent(t,this.pos,this.indent),this.pos=t,this.next=t==this.text.length?-1:this.text.charCodeAt(t)}skipSpace(t){return Au(this.text,t)}reset(t){for(this.text=t,this.baseIndent=this.basePos=this.pos=this.indent=0,this.forwardInner(),this.depth=1;this.markers.length;)this.markers.pop()}moveBase(t){this.basePos=t,this.baseIndent=this.countIndent(t,this.pos,this.indent)}moveBaseColumn(t){this.baseIndent=t,this.basePos=this.findColumn(t)}addMarker(t){this.markers.push(t)}countIndent(t,e=0,i=0){for(let n=e;n<t;n++)i+=9==this.text.charCodeAt(n)?4-i%4:1;return i}findColumn(t){let e=0;for(let i=0;e<this.text.length&&i<t;e++)i+=9==this.text.charCodeAt(e)?4-i%4:1;return e}scrub(){if(!this.baseIndent)return this.text;let t=\"\";for(let e=0;e<this.basePos;e++)t+=\" \";return t+this.text.slice(this.basePos)}}function Cu(t,e,i){if(i.pos==i.text.length||t!=e.block&&i.indent>=e.stack[i.depth+1].value+i.baseIndent)return!0;if(i.indent>=i.baseIndent+4)return!1;let n=(t.type==ku.OrderedList?Lu:Yu)(i,e,!1);return n>0&&(t.type!=ku.BulletList||_u(i,e,!1)<0)&&i.text.charCodeAt(i.pos+n-1)==t.value}const Zu={[ku.Blockquote]:(t,e,i)=>62==i.next&&(i.markers.push(fd(ku.QuoteMark,e.lineStart+i.pos,e.lineStart+i.pos+1)),i.moveBase(i.pos+(Xu(i.text.charCodeAt(i.pos+1))?2:1)),t.end=e.lineStart+i.text.length,!0),[ku.ListItem]:(t,e,i)=>!(i.indent<i.baseIndent+t.value&&i.next>-1)&&(i.moveBaseColumn(i.baseIndent+t.value),!0),[ku.OrderedList]:Cu,[ku.BulletList]:Cu,[ku.Document]:()=>!0};function Xu(t){return 32==t||9==t||10==t||13==t}function Au(t,e=0){for(;e<t.length&&Xu(t.charCodeAt(e));)e++;return e}function Mu(t,e,i){for(;e>i&&Xu(t.charCodeAt(e-1));)e--;return e}function Ru(t){if(96!=t.next&&126!=t.next)return-1;let e=t.pos+1;for(;e<t.text.length&&t.text.charCodeAt(e)==t.next;)e++;if(e<t.pos+3)return-1;if(96==t.next)for(let i=e;i<t.text.length;i++)if(96==t.text.charCodeAt(i))return-1;return e}function zu(t){return 62!=t.next?-1:32==t.text.charCodeAt(t.pos+1)?2:1}function _u(t,e,i){if(42!=t.next&&45!=t.next&&95!=t.next)return-1;let n=1;for(let r=t.pos+1;r<t.text.length;r++){let e=t.text.charCodeAt(r);if(e==t.next)n++;else if(!Xu(e))return-1}return i&&45==t.next&&Vu(t)>-1&&t.depth==e.stack.length&&e.parser.leafBlockParsers.indexOf(Ju.SetextHeading)>-1||n<3?-1:1}function Eu(t,e){for(let i=t.stack.length-1;i>=0;i--)if(t.stack[i].type==e)return!0;return!1}function Yu(t,e,i){return 45!=t.next&&43!=t.next&&42!=t.next||t.pos!=t.text.length-1&&!Xu(t.text.charCodeAt(t.pos+1))||!(!i||Eu(e,ku.BulletList)||t.skipSpace(t.pos+2)<t.text.length)?-1:1}function Lu(t,e,i){let n=t.pos,r=t.next;for(;r>=48&&r<=57;){if(n++,n==t.text.length)return-1;r=t.text.charCodeAt(n)}return n==t.pos||n>t.pos+9||46!=r&&41!=r||n<t.text.length-1&&!Xu(t.text.charCodeAt(n+1))||i&&!Eu(e,ku.OrderedList)&&(t.skipSpace(n+1)==t.text.length||n>t.pos+1||49!=t.next)?-1:n+1-t.pos}function qu(t){if(35!=t.next)return-1;let e=t.pos+1;for(;e<t.text.length&&35==t.text.charCodeAt(e);)e++;if(e<t.text.length&&32!=t.text.charCodeAt(e))return-1;let i=e-t.pos;return i>6?-1:i}function Vu(t){if(45!=t.next&&61!=t.next||t.indent>=t.baseIndent+4)return-1;let e=t.pos+1;for(;e<t.text.length&&t.text.charCodeAt(e)==t.next;)e++;let i=e;for(;e<t.text.length&&Xu(t.text.charCodeAt(e));)e++;return e==t.text.length?i:-1}const Wu=/^[ \\t]*$/,Du=/-->/,Bu=/\\?>/,ju=[[/^<(?:script|pre|style)(?:\\s|>|$)/i,/<\\/(?:script|pre|style)>/i],[/^\\s*<!--/,Du],[/^\\s*<\\?/,Bu],[/^\\s*<![A-Z]/,/>/],[/^\\s*<!\\[CDATA\\[/,/\\]\\]>/],[/^\\s*<\\/?(?:address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h1|h2|h3|h4|h5|h6|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)(?:\\s|\\/?>|$)/i,Wu],[/^\\s*(?:<\\/[a-z][\\w-]*\\s*>|<[a-z][\\w-]*(\\s+[a-z:_][\\w-.]*(?:\\s*=\\s*(?:[^\\s\"'=<>`]+|'[^']*'|\"[^\"]*\"))?)*\\s*>)\\s*$/i,Wu]];function Iu(t,e,i){if(60!=t.next)return-1;let n=t.text.slice(t.pos);for(let r=0,s=ju.length-(i?1:0);r<s;r++)if(ju[r][0].test(n))return r;return-1}function Gu(t,e){let i=t.countIndent(e,t.pos,t.indent),n=t.countIndent(t.skipSpace(e),e,i);return n>=i+5?i+1:n}function Nu(t,e,i){let n=t.length-1;n>=0&&t[n].to==e&&t[n].type==ku.CodeText?t[n].to=i:t.push(fd(ku.CodeText,e,i))}const Uu={LinkReference:void 0,IndentedCode(t,e){let i=e.baseIndent+4;if(e.indent<i)return!1;let n=e.findColumn(i),r=t.lineStart+n,s=t.lineStart+e.text.length,o=[],a=[];for(Nu(o,r,s);t.nextLine()&&e.depth>=t.stack.length;)if(e.pos==e.text.length){Nu(a,t.lineStart-1,t.lineStart);for(let t of e.markers)a.push(t)}else{if(e.indent<i)break;{if(a.length){for(let t of a)t.type==ku.CodeText?Nu(o,t.from,t.to):o.push(t);a=[]}Nu(o,t.lineStart-1,t.lineStart);for(let t of e.markers)o.push(t);s=t.lineStart+e.text.length;let i=t.lineStart+e.findColumn(e.baseIndent+4);i<s&&Nu(o,i,s)}}return a.length&&(a=a.filter(t=>t.type!=ku.CodeText),a.length&&(e.markers=a.concat(e.markers))),t.addNode(t.buffer.writeElements(o,-r).finish(ku.CodeBlock,s-r),r),!0},FencedCode(t,e){let i=Ru(e);if(i<0)return!1;let n=t.lineStart+e.pos,r=e.next,s=i-e.pos,o=e.skipSpace(i),a=Mu(e.text,e.text.length,o),l=[fd(ku.CodeMark,n,n+s)];o<a&&l.push(fd(ku.CodeInfo,t.lineStart+o,t.lineStart+a));for(let h=!0,c=!0,u=!1;t.nextLine()&&e.depth>=t.stack.length;h=!1){let i=e.pos;if(e.indent-e.baseIndent<4)for(;i<e.text.length&&e.text.charCodeAt(i)==r;)i++;if(i-e.pos>=s&&e.skipSpace(i)==e.text.length){for(let t of e.markers)l.push(t);c&&u&&Nu(l,t.lineStart-1,t.lineStart),l.push(fd(ku.CodeMark,t.lineStart+e.pos,t.lineStart+i)),t.nextLine();break}{u=!0,h||(Nu(l,t.lineStart-1,t.lineStart),c=!1);for(let t of e.markers)l.push(t);let i=t.lineStart+e.basePos,n=t.lineStart+e.text.length;i<n&&(Nu(l,i,n),c=!1)}}return t.addNode(t.buffer.writeElements(l,-n).finish(ku.FencedCode,t.prevLineEnd()-n),n),!0},Blockquote(t,e){let i=zu(e);return!(i<0)&&(t.startContext(ku.Blockquote,e.pos),t.addNode(ku.QuoteMark,t.lineStart+e.pos,t.lineStart+e.pos+1),e.moveBase(e.pos+i),null)},HorizontalRule(t,e){if(_u(e,t,!1)<0)return!1;let i=t.lineStart+e.pos;return t.nextLine(),t.addNode(ku.HorizontalRule,i),!0},BulletList(t,e){let i=Yu(e,t,!1);if(i<0)return!1;t.block.type!=ku.BulletList&&t.startContext(ku.BulletList,e.basePos,e.next);let n=Gu(e,e.pos+1);return t.startContext(ku.ListItem,e.basePos,n-e.baseIndent),t.addNode(ku.ListMark,t.lineStart+e.pos,t.lineStart+e.pos+i),e.moveBaseColumn(n),null},OrderedList(t,e){let i=Lu(e,t,!1);if(i<0)return!1;t.block.type!=ku.OrderedList&&t.startContext(ku.OrderedList,e.basePos,e.text.charCodeAt(e.pos+i-1));let n=Gu(e,e.pos+i);return t.startContext(ku.ListItem,e.basePos,n-e.baseIndent),t.addNode(ku.ListMark,t.lineStart+e.pos,t.lineStart+e.pos+i),e.moveBaseColumn(n),null},ATXHeading(t,e){let i=qu(e);if(i<0)return!1;let n=e.pos,r=t.lineStart+n,s=Mu(e.text,e.text.length,n),o=s;for(;o>n&&e.text.charCodeAt(o-1)==e.next;)o--;o!=s&&o!=n&&Xu(e.text.charCodeAt(o-1))||(o=e.text.length);let a=t.buffer.write(ku.HeaderMark,0,i).writeElements(t.parser.parseInline(e.text.slice(n+i+1,o),r+i+1),-r);o<e.text.length&&a.write(ku.HeaderMark,o-n,s-n);let l=a.finish(ku.ATXHeading1-1+i,e.text.length-n);return t.nextLine(),t.addNode(l,r),!0},HTMLBlock(t,e){let i=Iu(e,0,!1);if(i<0)return!1;let n=t.lineStart+e.pos,r=ju[i][1],s=[],o=r!=Wu;for(;!r.test(e.text)&&t.nextLine();){if(e.depth<t.stack.length){o=!1;break}for(let t of e.markers)s.push(t)}o&&t.nextLine();let a=r==Du?ku.CommentBlock:r==Bu?ku.ProcessingInstructionBlock:ku.HTMLBlock,l=t.prevLineEnd();return t.addNode(t.buffer.writeElements(s,-n).finish(a,l-n),n),!0},SetextHeading:void 0};class Hu{constructor(t){this.stage=0,this.elts=[],this.pos=0,this.start=t.start,this.advance(t.content)}nextLine(t,e,i){if(-1==this.stage)return!1;let n=i.content+\"\\n\"+e.scrub(),r=this.advance(n);return r>-1&&r<n.length&&this.complete(t,i,r)}finish(t,e){return(2==this.stage||3==this.stage)&&Au(e.content,this.pos)==e.content.length&&this.complete(t,e,e.content.length)}complete(t,e,i){return t.addLeafElement(e,fd(ku.LinkReference,this.start,this.start+i,this.elts)),!0}nextStage(t){return t?(this.pos=t.to-this.start,this.elts.push(t),this.stage++,!0):(!1===t&&(this.stage=-1),!1)}advance(t){for(;;){if(-1==this.stage)return-1;if(0==this.stage){if(!this.nextStage(kd(t,this.pos,this.start,!0)))return-1;if(58!=t.charCodeAt(this.pos))return this.stage=-1;this.elts.push(fd(ku.LinkMark,this.pos+this.start,this.pos+this.start+1)),this.pos++}else{if(1!=this.stage){if(2==this.stage){let e=Au(t,this.pos),i=0;if(e>this.pos){let n=yd(t,e,this.start);if(n){let e=Fu(t,n.to-this.start);e>0&&(this.nextStage(n),i=e)}}return i||(i=Fu(t,this.pos)),i>0&&i<t.length?i:-1}return Fu(t,this.pos)}if(!this.nextStage(Sd(t,Au(t,this.pos),this.start)))return-1}}}}function Fu(t,e){for(;e<t.length;e++){let i=t.charCodeAt(e);if(10==i)break;if(!Xu(i))return-1}return e}class Ku{nextLine(t,e,i){let n=e.depth<t.stack.length?-1:Vu(e),r=e.next;if(n<0)return!1;let s=fd(ku.HeaderMark,t.lineStart+e.pos,t.lineStart+n);return t.nextLine(),t.addLeafElement(i,fd(61==r?ku.SetextHeading1:ku.SetextHeading2,i.start,t.prevLineEnd(),[...t.parser.parseInline(i.content,i.start),s])),!0}finish(){return!1}}const Ju={LinkReference:(t,e)=>91==e.content.charCodeAt(0)?new Hu(e):null,SetextHeading:()=>new Ku},td=[(t,e)=>qu(e)>=0,(t,e)=>Ru(e)>=0,(t,e)=>zu(e)>=0,(t,e)=>Yu(e,t,!0)>=0,(t,e)=>Lu(e,t,!0)>=0,(t,e)=>_u(e,t,!0)>=0,(t,e)=>Iu(e,0,!0)>=0],ed={text:\"\",end:0};class id{constructor(t,e,i,n){this.parser=t,this.input=e,this.ranges=n,this.line=new Tu,this.atEnd=!1,this.reusePlaceholders=new Map,this.stoppedAt=null,this.rangeI=0,this.to=n[n.length-1].to,this.lineStart=this.absoluteLineStart=this.absoluteLineEnd=n[0].from,this.block=yu.create(ku.Document,0,this.lineStart,0,0),this.stack=[this.block],this.fragments=i.length?new Cd(i,e):null,this.readLine()}get parsedPos(){return this.absoluteLineStart}advance(){if(null!=this.stoppedAt&&this.absoluteLineStart>this.stoppedAt)return this.finish();let{line:t}=this;for(;;){for(let e=0;;){let i=t.depth<this.stack.length?this.stack[this.stack.length-1]:null;for(;e<t.markers.length&&(!i||t.markers[e].from<i.end);){let i=t.markers[e++];this.addNode(i.type,i.from,i.to)}if(!i)break;this.finishContext()}if(t.pos<t.text.length)break;if(!this.nextLine())return this.finish()}if(this.fragments&&this.reuseFragment(t.basePos))return null;t:for(;;){for(let e of this.parser.blockParsers)if(e){let i=e(this,t);if(0!=i){if(1==i)return null;t.forward();continue t}}break}let e=new Pu(this.lineStart+t.pos,t.text.slice(t.pos));for(let i of this.parser.leafBlockParsers)if(i){let t=i(this,e);t&&e.parsers.push(t)}t:for(;this.nextLine()&&t.pos!=t.text.length;){if(t.indent<t.baseIndent+4)for(let i of this.parser.endLeafBlock)if(i(this,t,e))break t;for(let i of e.parsers)if(i.nextLine(this,t,e))return null;e.content+=\"\\n\"+t.scrub();for(let i of t.markers)e.marks.push(i)}return this.finishLeaf(e),null}stopAt(t){if(null!=this.stoppedAt&&this.stoppedAt<t)throw new RangeError(\"Can't move stoppedAt forward\");this.stoppedAt=t}reuseFragment(t){if(!this.fragments.moveTo(this.absoluteLineStart+t,this.absoluteLineStart)||!this.fragments.matches(this.block.hash))return!1;let e=this.fragments.takeNodes(this);return!!e&&(this.absoluteLineStart+=e,this.lineStart=Zd(this.absoluteLineStart,this.ranges),this.moveRangeI(),this.absoluteLineStart<this.to?(this.lineStart++,this.absoluteLineStart++,this.readLine()):(this.atEnd=!0,this.readLine()),!0)}get depth(){return this.stack.length}parentType(t=this.depth-1){return this.parser.nodeSet.types[this.stack[t].type]}nextLine(){return this.lineStart+=this.line.text.length,this.absoluteLineEnd>=this.to?(this.absoluteLineStart=this.absoluteLineEnd,this.atEnd=!0,this.readLine(),!1):(this.lineStart++,this.absoluteLineStart=this.absoluteLineEnd+1,this.moveRangeI(),this.readLine(),!0)}peekLine(){return this.scanLine(this.absoluteLineEnd+1).text}moveRangeI(){for(;this.rangeI<this.ranges.length-1&&this.absoluteLineStart>=this.ranges[this.rangeI].to;)this.rangeI++,this.absoluteLineStart=Math.max(this.absoluteLineStart,this.ranges[this.rangeI].from)}scanLine(t){let e=ed;if(e.end=t,t>=this.to)e.text=\"\";else if(e.text=this.lineChunkAt(t),e.end+=e.text.length,this.ranges.length>1){let t=this.absoluteLineStart,i=this.rangeI;for(;this.ranges[i].to<e.end;){i++;let n=this.ranges[i].from,r=this.lineChunkAt(n);e.end=n+r.length,e.text=e.text.slice(0,this.ranges[i-1].to-t)+r,t=e.end-e.text.length}}return e}readLine(){let{line:t}=this,{text:e,end:i}=this.scanLine(this.absoluteLineStart);for(this.absoluteLineEnd=i,t.reset(e);t.depth<this.stack.length;t.depth++){let e=this.stack[t.depth],i=this.parser.skipContextMarkup[e.type];if(!i)throw new Error(\"Unhandled block context \"+ku[e.type]);let n=this.line.markers.length;if(!i(e,this,t)){this.line.markers.length>n&&(e.end=this.line.markers[this.line.markers.length-1].to),t.forward();break}t.forward()}}lineChunkAt(t){let e,i=this.input.chunk(t);if(this.input.lineChunks)e=\"\\n\"==i?\"\":i;else{let t=i.indexOf(\"\\n\");e=t<0?i:i.slice(0,t)}return t+e.length>this.to?e.slice(0,this.to-t):e}prevLineEnd(){return this.atEnd?this.lineStart:this.lineStart-1}startContext(t,e,i=0){this.block=yu.create(t,i,this.lineStart+e,this.block.hash,this.lineStart+this.line.text.length),this.stack.push(this.block)}startComposite(t,e,i=0){this.startContext(this.parser.getNodeType(t),e,i)}addNode(t,e,i){\"number\"==typeof t&&(t=new Oa(this.parser.nodeSet.types[t],hd,hd,(null!=i?i:this.prevLineEnd())-e)),this.block.addChild(t,e-this.block.from)}addElement(t){this.block.addChild(t.toTree(this.parser.nodeSet),t.from-this.block.from)}addLeafElement(t,e){this.addNode(this.buffer.writeElements(Pd(e.children,t.marks),-e.from).finish(e.type,e.to-e.from),e.from)}finishContext(){let t=this.stack.pop(),e=this.stack[this.stack.length-1];e.addChild(t.toTree(this.parser.nodeSet),t.from-e.from),this.block=e}finish(){for(;this.stack.length>1;)this.finishContext();return this.addGaps(this.block.toTree(this.parser.nodeSet,this.lineStart))}addGaps(t){return this.ranges.length>1?nd(this.ranges,0,t.topNode,this.ranges[0].from,this.reusePlaceholders):t}finishLeaf(t){for(let i of t.parsers)if(i.finish(this,t))return;let e=Pd(this.parser.parseInline(t.content,t.start),t.marks);this.addNode(this.buffer.writeElements(e,-t.start).finish(ku.Paragraph,t.content.length),t.start)}elt(t,e,i,n){return\"string\"==typeof t?fd(this.parser.getNodeType(t),e,i,n):new dd(t,e)}get buffer(){return new cd(this.parser.nodeSet)}}function nd(t,e,i,n,r){let s=t[e].to,o=[],a=[],l=i.from+n;function h(i,r){for(;r?i>=s:i>s;){let r=t[e+1].from-s;n+=r,i+=r,e++,s=t[e].to}}for(let c=i.firstChild;c;c=c.nextSibling){h(c.from+n,!0);let i,u=c.from+n,d=r.get(c.tree);d?i=d:c.to+n>s?(i=nd(t,e,c,n,r),h(c.to+n,!1)):i=c.toTree(),o.push(i),a.push(u-l)}return h(i.to+n,!1),new Oa(i.type,o,a,i.to+n-l,i.tree?i.tree.propValues:void 0)}class rd extends Ra{constructor(t,e,i,n,r,s,o,a,l){super(),this.nodeSet=t,this.blockParsers=e,this.leafBlockParsers=i,this.blockNames=n,this.endLeafBlock=r,this.skipContextMarkup=s,this.inlineParsers=o,this.inlineNames=a,this.wrappers=l,this.nodeTypes=Object.create(null);for(let h of t.types)this.nodeTypes[h.name]=h.id}createParse(t,e,i){let n=new id(this,t,e,i);for(let r of this.wrappers)n=r(n,t,e,i);return n}configure(t){let e=od(t);if(!e)return this;let{nodeSet:i,skipContextMarkup:n}=this,r=this.blockParsers.slice(),s=this.leafBlockParsers.slice(),o=this.blockNames.slice(),a=this.inlineParsers.slice(),l=this.inlineNames.slice(),h=this.endLeafBlock.slice(),c=this.wrappers;if(sd(e.defineNodes)){n=Object.assign({},n);let t,r=i.types.slice();for(let i of e.defineNodes){let{name:e,block:s,composite:o,style:a}=\"string\"==typeof i?{name:i}:i;if(r.some(t=>t.name==e))continue;o&&(n[r.length]=(t,e,i)=>o(e,i,t.value));let l=r.length,h=o?[\"Block\",\"BlockContext\"]:s?l>=ku.ATXHeading1&&l<=ku.SetextHeading2?[\"Block\",\"LeafBlock\",\"Heading\"]:[\"Block\",\"LeafBlock\"]:void 0;r.push(la.define({id:l,name:e,props:h&&[[sa.group,h]]})),a&&(t||(t={}),Array.isArray(a)||a instanceof Fa?t[e]=a:Object.assign(t,a))}i=new ha(r),t&&(i=i.extend(tl(t)))}if(sd(e.props)&&(i=i.extend(...e.props)),sd(e.remove))for(let u of e.remove){let t=this.blockNames.indexOf(u),e=this.inlineNames.indexOf(u);t>-1&&(r[t]=s[t]=void 0),e>-1&&(a[e]=void 0)}if(sd(e.parseBlock))for(let u of e.parseBlock){let t=o.indexOf(u.name);if(t>-1)r[t]=u.parse,s[t]=u.leaf;else{let t=u.before?ad(o,u.before):u.after?ad(o,u.after)+1:o.length-1;r.splice(t,0,u.parse),s.splice(t,0,u.leaf),o.splice(t,0,u.name)}u.endLeaf&&h.push(u.endLeaf)}if(sd(e.parseInline))for(let u of e.parseInline){let t=l.indexOf(u.name);if(t>-1)a[t]=u.parse;else{let t=u.before?ad(l,u.before):u.after?ad(l,u.after)+1:l.length-1;a.splice(t,0,u.parse),l.splice(t,0,u.name)}}return e.wrap&&(c=c.concat(e.wrap)),new rd(i,r,s,o,h,n,a,l,c)}getNodeType(t){let e=this.nodeTypes[t];if(null==e)throw new RangeError(`Unknown node type '${t}'`);return e}parseInline(t,e){let i=new $d(this,t,e);t:for(let n=e;n<i.end;){let t=i.char(n);for(let e of this.inlineParsers)if(e){let r=e(i,t,n);if(r>=0){n=r;continue t}}n++}return i.resolveMarkers(0)}}function sd(t){return null!=t&&t.length>0}function od(t){if(!Array.isArray(t))return t;if(0==t.length)return null;let e=od(t[0]);if(1==t.length)return e;let i=od(t.slice(1));if(!i||!e)return e||i;let n=(t,e)=>(t||hd).concat(e||hd),r=e.wrap,s=i.wrap;return{props:n(e.props,i.props),defineNodes:n(e.defineNodes,i.defineNodes),parseBlock:n(e.parseBlock,i.parseBlock),parseInline:n(e.parseInline,i.parseInline),remove:n(e.remove,i.remove),wrap:r?s?(t,e,i,n)=>r(s(t,e,i,n),e,i,n):r:s}}function ad(t,e){let i=t.indexOf(e);if(i<0)throw new RangeError(`Position specified relative to unknown parser ${e}`);return i}let ld=[la.none];for(let Mb,Rb=1;Mb=ku[Rb];Rb++)ld[Rb]=la.define({id:Rb,name:Mb,props:Rb>=ku.Escape?[]:[[sa.group,Rb in Zu?[\"Block\",\"BlockContext\"]:[\"Block\",\"LeafBlock\"]]],top:\"Document\"==Mb});const hd=[];class cd{constructor(t){this.nodeSet=t,this.content=[],this.nodes=[]}write(t,e,i,n=0){return this.content.push(t,e,i,4+4*n),this}writeElements(t,e=0){for(let i of t)i.writeTo(this,e);return this}finish(t,e){return Oa.build({buffer:this.content,nodeSet:this.nodeSet,reused:this.nodes,topID:t,length:e})}}let ud=class{constructor(t,e,i,n=hd){this.type=t,this.from=e,this.to=i,this.children=n}writeTo(t,e){let i=t.content.length;t.writeElements(this.children,e),t.content.push(this.type,this.from+e,this.to+e,t.content.length+4-i)}toTree(t){return new cd(t).writeElements(this.children,-this.from).finish(this.type,this.to-this.from)}};class dd{constructor(t,e){this.tree=t,this.from=e}get to(){return this.from+this.tree.length}get type(){return this.tree.type.id}get children(){return hd}writeTo(t,e){t.nodes.push(this.tree),t.content.push(t.nodes.length-1,this.from+e,this.to+e,-1)}toTree(){return this.tree}}function fd(t,e,i,n){return new ud(t,e,i,n)}const Od={resolve:\"Emphasis\",mark:\"EmphasisMark\"},pd={resolve:\"Emphasis\",mark:\"EmphasisMark\"},md={},gd={};class Qd{constructor(t,e,i,n){this.type=t,this.from=e,this.to=i,this.side=n}}const bd=\"!\\\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~\";let vd=/[!\"#$%&'()*+,\\-.\\/:;<=>?@\\[\\\\\\]^_`{|}~\\xA1\\u2010-\\u2027]/;try{vd=new RegExp(\"[\\\\p{S}|\\\\p{P}]\",\"u\")}catch(Ab){}const wd={Escape(t,e,i){if(92!=e||i==t.end-1)return-1;let n=t.char(i+1);for(let r=0;r<32;r++)if(bd.charCodeAt(r)==n)return t.append(fd(ku.Escape,i,i+2));return-1},Entity(t,e,i){if(38!=e)return-1;let n=/^(?:#\\d+|#x[a-f\\d]+|\\w+);/i.exec(t.slice(i+1,i+31));return n?t.append(fd(ku.Entity,i,i+1+n[0].length)):-1},InlineCode(t,e,i){if(96!=e||i&&96==t.char(i-1))return-1;let n=i+1;for(;n<t.end&&96==t.char(n);)n++;let r=n-i,s=0;for(;n<t.end;n++)if(96==t.char(n)){if(s++,s==r&&96!=t.char(n+1))return t.append(fd(ku.InlineCode,i,n+1,[fd(ku.CodeMark,i,i+r),fd(ku.CodeMark,n+1-r,n+1)]))}else s=0;return-1},HTMLTag(t,e,i){if(60!=e||i==t.end-1)return-1;let n=t.slice(i+1,t.end),r=/^(?:[a-z][-\\w+.]+:[^\\s>]+|[a-z\\d.!#$%&'*+/=?^_`{|}~-]+@[a-z\\d](?:[a-z\\d-]{0,61}[a-z\\d])?(?:\\.[a-z\\d](?:[a-z\\d-]{0,61}[a-z\\d])?)*)>/i.exec(n);if(r)return t.append(fd(ku.Autolink,i,i+1+r[0].length,[fd(ku.LinkMark,i,i+1),fd(ku.URL,i+1,i+r[0].length),fd(ku.LinkMark,i+r[0].length,i+1+r[0].length)]));let s=/^!--[^>](?:-[^-]|[^-])*?-->/i.exec(n);if(s)return t.append(fd(ku.Comment,i,i+1+s[0].length));let o=/^\\?[^]*?\\?>/.exec(n);if(o)return t.append(fd(ku.ProcessingInstruction,i,i+1+o[0].length));let a=/^(?:![A-Z][^]*?>|!\\[CDATA\\[[^]*?\\]\\]>|\\/\\s*[a-zA-Z][\\w-]*\\s*>|\\s*[a-zA-Z][\\w-]*(\\s+[a-zA-Z:_][\\w-.:]*(?:\\s*=\\s*(?:[^\\s\"'=<>`]+|'[^']*'|\"[^\"]*\"))?)*\\s*(\\/\\s*)?>)/.exec(n);return a?t.append(fd(ku.HTMLTag,i,i+1+a[0].length)):-1},Emphasis(t,e,i){if(95!=e&&42!=e)return-1;let n=i+1;for(;t.char(n)==e;)n++;let r=t.slice(i-1,i),s=t.slice(n,n+1),o=vd.test(r),a=vd.test(s),l=/\\s|^$/.test(r),h=/\\s|^$/.test(s),c=!h&&(!a||l||o),u=!l&&(!o||h||a),d=c&&(42==e||!u||o),f=u&&(42==e||!c||a);return t.append(new Qd(95==e?Od:pd,i,n,(d?1:0)|(f?2:0)))},HardBreak(t,e,i){if(92==e&&10==t.char(i+1))return t.append(fd(ku.HardBreak,i,i+2));if(32==e){let e=i+1;for(;32==t.char(e);)e++;if(10==t.char(e)&&e>=i+2)return t.append(fd(ku.HardBreak,i,e+1))}return-1},Link:(t,e,i)=>91==e?t.append(new Qd(md,i,i+1,1)):-1,Image:(t,e,i)=>33==e&&91==t.char(i+1)?t.append(new Qd(gd,i,i+2,1)):-1,LinkEnd(t,e,i){if(93!=e)return-1;for(let n=t.parts.length-1;n>=0;n--){let e=t.parts[n];if(e instanceof Qd&&(e.type==md||e.type==gd)){if(!e.side||t.skipSpace(e.to)==i&&!/[(\\[]/.test(t.slice(i+1,i+2)))return t.parts[n]=null,-1;let r=t.takeContent(n),s=t.parts[n]=xd(t,r,e.type==md?ku.Link:ku.Image,e.from,i+1);if(e.type==md)for(let e=0;e<n;e++){let i=t.parts[e];i instanceof Qd&&i.type==md&&(i.side=0)}return s.to}}return-1}};function xd(t,e,i,n,r){let{text:s}=t,o=t.char(r),a=r;if(e.unshift(fd(ku.LinkMark,n,n+(i==ku.Image?2:1))),e.push(fd(ku.LinkMark,r-1,r)),40==o){let i,n=t.skipSpace(r+1),o=Sd(s,n-t.offset,t.offset);o&&(n=t.skipSpace(o.to),n!=o.to&&(i=yd(s,n-t.offset,t.offset),i&&(n=t.skipSpace(i.to)))),41==t.char(n)&&(e.push(fd(ku.LinkMark,r,r+1)),a=n+1,o&&e.push(o),i&&e.push(i),e.push(fd(ku.LinkMark,n,a)))}else if(91==o){let i=kd(s,r-t.offset,t.offset,!1);i&&(e.push(i),a=i.to)}return fd(i,n,a,e)}function Sd(t,e,i){if(60==t.charCodeAt(e)){for(let n=e+1;n<t.length;n++){let r=t.charCodeAt(n);if(62==r)return fd(ku.URL,e+i,n+1+i);if(60==r||10==r)return!1}return null}{let n=0,r=e;for(let e=!1;r<t.length;r++){let i=t.charCodeAt(r);if(Xu(i))break;if(e)e=!1;else if(40==i)n++;else if(41==i){if(!n)break;n--}else 92==i&&(e=!0)}return r>e?fd(ku.URL,e+i,r+i):r==t.length&&null}}function yd(t,e,i){let n=t.charCodeAt(e);if(39!=n&&34!=n&&40!=n)return!1;let r=40==n?41:n;for(let s=e+1,o=!1;s<t.length;s++){let n=t.charCodeAt(s);if(o)o=!1;else{if(n==r)return fd(ku.LinkTitle,e+i,s+1+i);92==n&&(o=!0)}}return null}function kd(t,e,i,n){for(let r=!1,s=e+1,o=Math.min(t.length,s+999);s<o;s++){let o=t.charCodeAt(s);if(r)r=!1;else{if(93==o)return!n&&fd(ku.LinkLabel,e+i,s+1+i);if(n&&!Xu(o)&&(n=!1),91==o)return!1;92==o&&(r=!0)}}return null}class $d{constructor(t,e,i){this.parser=t,this.text=e,this.offset=i,this.parts=[]}char(t){return t>=this.end?-1:this.text.charCodeAt(t-this.offset)}get end(){return this.offset+this.text.length}slice(t,e){return this.text.slice(t-this.offset,e-this.offset)}append(t){return this.parts.push(t),t.to}addDelimiter(t,e,i,n,r){return this.append(new Qd(t,e,i,(n?1:0)|(r?2:0)))}get hasOpenLink(){for(let t=this.parts.length-1;t>=0;t--){let e=this.parts[t];if(e instanceof Qd&&(e.type==md||e.type==gd))return!0}return!1}addElement(t){return this.append(t)}resolveMarkers(t){for(let i=t;i<this.parts.length;i++){let e=this.parts[i];if(!(e instanceof Qd&&e.type.resolve&&2&e.side))continue;let n,r=e.type==Od||e.type==pd,s=e.to-e.from,o=i-1;for(;o>=t;o--){let t=this.parts[o];if(t instanceof Qd&&1&t.side&&t.type==e.type&&!(r&&(1&e.side||2&t.side)&&(t.to-t.from+s)%3==0&&((t.to-t.from)%3||s%3))){n=t;break}}if(!n)continue;let a=e.type.resolve,l=[],h=n.from,c=e.to;if(r){let t=Math.min(2,n.to-n.from,s);h=n.to-t,c=e.from+t,a=1==t?\"Emphasis\":\"StrongEmphasis\"}n.type.mark&&l.push(this.elt(n.type.mark,h,n.to));for(let t=o+1;t<i;t++)this.parts[t]instanceof ud&&l.push(this.parts[t]),this.parts[t]=null;e.type.mark&&l.push(this.elt(e.type.mark,e.from,c));let u=this.elt(a,h,c,l);this.parts[o]=r&&n.from!=h?new Qd(n.type,n.from,h,n.side):null,(this.parts[i]=r&&e.to!=c?new Qd(e.type,c,e.to,e.side):null)?this.parts.splice(i,0,u):this.parts[i]=u}let e=[];for(let i=t;i<this.parts.length;i++){let t=this.parts[i];t instanceof ud&&e.push(t)}return e}findOpeningDelimiter(t){for(let e=this.parts.length-1;e>=0;e--){let i=this.parts[e];if(i instanceof Qd&&i.type==t&&1&i.side)return e}return null}takeContent(t){let e=this.resolveMarkers(t);return this.parts.length=t,e}getDelimiterAt(t){let e=this.parts[t];return e instanceof Qd?e:null}skipSpace(t){return Au(this.text,t-this.offset)+this.offset}elt(t,e,i,n){return\"string\"==typeof t?fd(this.parser.getNodeType(t),e,i,n):new dd(t,e)}}function Pd(t,e){if(!e.length)return t;if(!t.length)return e;let i=t.slice(),n=0;for(let r of e){for(;n<i.length&&i[n].to<r.to;)n++;if(n<i.length&&i[n].from<r.from){let t=i[n];t instanceof ud&&(i[n]=new ud(t.type,t.from,t.to,Pd(t.children,[r])))}else i.splice(n++,0,r)}return i}$d.linkStart=md,$d.imageStart=gd;const Td=[ku.CodeBlock,ku.ListItem,ku.OrderedList,ku.BulletList];let Cd=class{constructor(t,e){this.fragments=t,this.input=e,this.i=0,this.fragment=null,this.fragmentEnd=-1,this.cursor=null,t.length&&(this.fragment=t[this.i++])}nextFragment(){this.fragment=this.i<this.fragments.length?this.fragments[this.i++]:null,this.cursor=null,this.fragmentEnd=-1}moveTo(t,e){for(;this.fragment&&this.fragment.to<=t;)this.nextFragment();if(!this.fragment||this.fragment.from>(t?t-1:0))return!1;if(this.fragmentEnd<0){let t=this.fragment.to;for(;t>0&&\"\\n\"!=this.input.read(t-1,t);)t--;this.fragmentEnd=t?t-1:0}let i=this.cursor;i||(i=this.cursor=this.fragment.tree.cursor(),i.firstChild());let n=t+this.fragment.offset;for(;i.to<=n;)if(!i.parent())return!1;for(;;){if(i.from>=n)return this.fragment.from<=e;if(!i.childAfter(n))return!1}}matches(t){let e=this.cursor.tree;return e&&e.prop(sa.contextHash)==t}takeNodes(t){let e=this.cursor,i=this.fragment.offset,n=this.fragmentEnd-(this.fragment.openEnd?1:0),r=t.absoluteLineStart,s=r,o=t.block.children.length,a=s,l=o;for(;;){if(e.to-i>n){if(e.type.isAnonymous&&e.firstChild())continue;break}let r=Zd(e.from-i,t.ranges);if(e.to-i<=t.ranges[t.rangeI].to)t.addNode(e.tree,r);else{let i=new Oa(t.parser.nodeSet.types[ku.Paragraph],[],[],0,t.block.hashProp);t.reusePlaceholders.set(i,e.tree),t.addNode(i,r)}if(e.type.is(\"Block\")&&(Td.indexOf(e.type.id)<0?(s=e.to-i,o=t.block.children.length):(s=a,o=l),a=e.to-i,l=t.block.children.length),!e.nextSibling())break}for(;t.block.children.length>o;)t.block.children.pop(),t.block.positions.pop();return s-r}};function Zd(t,e){let i=t;for(let n=1;n<e.length;n++){let r=e[n-1].to,s=e[n].from;r<t&&(i-=s-r)}return i}const Xd=tl({\"Blockquote/...\":wl.quote,HorizontalRule:wl.contentSeparator,\"ATXHeading1/... SetextHeading1/...\":wl.heading1,\"ATXHeading2/... SetextHeading2/...\":wl.heading2,\"ATXHeading3/...\":wl.heading3,\"ATXHeading4/...\":wl.heading4,\"ATXHeading5/...\":wl.heading5,\"ATXHeading6/...\":wl.heading6,\"Comment CommentBlock\":wl.comment,Escape:wl.escape,Entity:wl.character,\"Emphasis/...\":wl.emphasis,\"StrongEmphasis/...\":wl.strong,\"Link/... Image/...\":wl.link,\"OrderedList/... BulletList/...\":wl.list,\"BlockQuote/...\":wl.quote,\"InlineCode CodeText\":wl.monospace,\"URL Autolink\":wl.url,\"HeaderMark HardBreak QuoteMark ListMark LinkMark EmphasisMark CodeMark\":wl.processingInstruction,\"CodeInfo LinkLabel\":wl.labelName,LinkTitle:wl.string,Paragraph:wl.content}),Ad=new rd(new ha(ld).extend(Xd),Object.keys(Uu).map(t=>Uu[t]),Object.keys(Uu).map(t=>Ju[t]),Object.keys(Uu),td,Zu,Object.keys(wd).map(t=>wd[t]),Object.keys(wd),[]);function Md(t,e,i){let n=[];for(let r=t.firstChild,s=e;;r=r.nextSibling){let t=r?r.from:i;if(t>s&&n.push({from:s,to:t}),!r)break;s=r.to}return n}const Rd={resolve:\"Strikethrough\",mark:\"StrikethroughMark\"},zd={defineNodes:[{name:\"Strikethrough\",style:{\"Strikethrough/...\":wl.strikethrough}},{name:\"StrikethroughMark\",style:wl.processingInstruction}],parseInline:[{name:\"Strikethrough\",parse(t,e,i){if(126!=e||126!=t.char(i+1)||126==t.char(i+2))return-1;let n=t.slice(i-1,i),r=t.slice(i+2,i+3),s=/\\s|^$/.test(n),o=/\\s|^$/.test(r),a=vd.test(n),l=vd.test(r);return t.addDelimiter(Rd,i,i+2,!o&&(!l||s||a),!s&&(!a||o||l))},after:\"Emphasis\"}]};function _d(t,e,i=0,n,r=0){let s=0,o=!0,a=-1,l=-1,h=!1,c=()=>{n.push(t.elt(\"TableCell\",r+a,r+l,t.parser.parseInline(e.slice(a,l),r+a)))};for(let u=i;u<e.length;u++){let i=e.charCodeAt(u);124!=i||h?(h||32!=i&&9!=i)&&(a<0&&(a=u),l=u+1):((!o||a>-1)&&s++,o=!1,n&&(a>-1&&c(),n.push(t.elt(\"TableDelimiter\",u+r,u+r+1))),a=l=-1),h=!h&&92==i}return a>-1&&(s++,n&&c()),s}function Ed(t,e){for(let i=e;i<t.length;i++){let e=t.charCodeAt(i);if(124==e)return!0;92==e&&i++}return!1}const Yd=/^\\|?(\\s*:?-+:?\\s*\\|)+(\\s*:?-+:?\\s*)?$/;class Ld{constructor(){this.rows=null}nextLine(t,e,i){if(null==this.rows){let n;if(this.rows=!1,(45==e.next||58==e.next||124==e.next)&&Yd.test(n=e.text.slice(e.pos))){let r=[];_d(t,i.content,0,r,i.start)==_d(t,n,e.pos)&&(this.rows=[t.elt(\"TableHeader\",i.start,i.start+i.content.length,r),t.elt(\"TableDelimiter\",t.lineStart+e.pos,t.lineStart+e.text.length)])}}else if(this.rows){let i=[];_d(t,e.text,e.pos,i,t.lineStart),this.rows.push(t.elt(\"TableRow\",t.lineStart+e.pos,t.lineStart+e.text.length,i))}return!1}finish(t,e){return!!this.rows&&(t.addLeafElement(e,t.elt(\"Table\",e.start,e.start+e.content.length,this.rows)),!0)}}const qd={defineNodes:[{name:\"Table\",block:!0},{name:\"TableHeader\",style:{\"TableHeader/...\":wl.heading}},\"TableRow\",{name:\"TableCell\",style:wl.content},{name:\"TableDelimiter\",style:wl.processingInstruction}],parseBlock:[{name:\"Table\",leaf:(t,e)=>Ed(e.content,0)?new Ld:null,endLeaf(t,e,i){if(i.parsers.some(t=>t instanceof Ld)||!Ed(e.text,e.basePos))return!1;let n=t.peekLine();return Yd.test(n)&&_d(t,e.text,e.basePos)==_d(t,n,e.basePos)},before:\"SetextHeading\"}]};class Vd{nextLine(){return!1}finish(t,e){return t.addLeafElement(e,t.elt(\"Task\",e.start,e.start+e.content.length,[t.elt(\"TaskMarker\",e.start,e.start+3),...t.parser.parseInline(e.content.slice(3),e.start+3)])),!0}}const Wd={defineNodes:[{name:\"Task\",block:!0,style:wl.list},{name:\"TaskMarker\",style:wl.atom}],parseBlock:[{name:\"TaskList\",leaf:(t,e)=>/^\\[[ xX]\\][ \\t]/.test(e.content)&&\"ListItem\"==t.parentType().name?new Vd:null,after:\"SetextHeading\"}]},Dd=/(www\\.)|(https?:\\/\\/)|([\\w.+-]{1,100}@)|(mailto:|xmpp:)/gy,Bd=/[\\w-]+(\\.[\\w-]+)+(\\/[^\\s<]*)?/gy,jd=/[\\w-]+\\.[\\w-]+($|\\/)/,Id=/[\\w.+-]+@[\\w-]+(\\.[\\w.-]+)+/gy,Gd=/\\/[a-zA-Z\\d@.]+/gy;function Nd(t,e,i,n){let r=0;for(let s=e;s<i;s++)t[s]==n&&r++;return r}function Ud(t,e){Id.lastIndex=e;let i=Id.exec(t);if(!i)return-1;let n=i[0][i[0].length-1];return\"_\"==n||\"-\"==n?-1:e+i[0].length-(\".\"==n?1:0)}const Hd=[qd,Wd,zd,{parseInline:[{name:\"Autolink\",parse(t,e,i){let n=i-t.offset;if(n&&/\\w/.test(t.text[n-1]))return-1;Dd.lastIndex=n;let r=Dd.exec(t.text),s=-1;if(!r)return-1;if(r[1]||r[2]){if(s=function(t,e){Bd.lastIndex=e;let i=Bd.exec(t);if(!i||jd.exec(i[0])[0].indexOf(\"_\")>-1)return-1;let n=e+i[0].length;for(;;){let i,r=t[n-1];if(/[?!.,:*_~]/.test(r)||\")\"==r&&Nd(t,e,n,\")\")>Nd(t,e,n,\"(\"))n--;else{if(\";\"!=r||!(i=/&(?:#\\d+|#x[a-f\\d]+|\\w+);$/.exec(t.slice(e,n))))break;n=e+i.index}}return n}(t.text,n+r[0].length),s>-1&&t.hasOpenLink){s=n+/([^\\[\\]]|\\[[^\\]]*\\])*/.exec(t.text.slice(n,s))[0].length}}else r[3]?s=Ud(t.text,n):(s=Ud(t.text,n+r[0].length),s>-1&&\"xmpp:\"==r[0]&&(Gd.lastIndex=s,r=Gd.exec(t.text),r&&(s=r.index+r[0].length)));return s<0?-1:(t.addElement(t.elt(\"URL\",i,s+t.offset)),s+t.offset)}}]}];function Fd(t,e,i){return(n,r,s)=>{if(r!=t||n.char(s+1)==t)return-1;let o=[n.elt(i,s,s+1)];for(let a=s+1;a<n.end;a++){let r=n.char(a);if(r==t)return n.addElement(n.elt(e,s,a+1,o.concat(n.elt(i,a,a+1))));if(92==r&&o.push(n.elt(\"Escape\",a,2+a++)),Xu(r))break}return-1}}const Kd={defineNodes:[{name:\"Superscript\",style:wl.special(wl.content)},{name:\"SuperscriptMark\",style:wl.processingInstruction}],parseInline:[{name:\"Superscript\",parse:Fd(94,\"Superscript\",\"SuperscriptMark\")}]},Jd={defineNodes:[{name:\"Subscript\",style:wl.special(wl.content)},{name:\"SubscriptMark\",style:wl.processingInstruction}],parseInline:[{name:\"Subscript\",parse:Fd(126,\"Subscript\",\"SubscriptMark\")}]},tf={defineNodes:[{name:\"Emoji\",style:wl.character}],parseInline:[{name:\"Emoji\",parse(t,e,i){let n;return 58==e&&(n=/^[a-zA-Z_0-9]+:/.exec(t.slice(i+1,t.end)))?t.addElement(t.elt(\"Emoji\",i,i+1+n[0].length)):-1}}]};var ef={};class nf{constructor(t,e,i,n,r,s,o,a,l,h=0,c){this.p=t,this.stack=e,this.state=i,this.reducePos=n,this.pos=r,this.score=s,this.buffer=o,this.bufferBase=a,this.curContext=l,this.lookAhead=h,this.parent=c}toString(){return`[${this.stack.filter((t,e)=>e%3==0).concat(this.state)}]@${this.pos}${this.score?\"!\"+this.score:\"\"}`}static start(t,e,i=0){let n=t.parser.context;return new nf(t,[],e,i,i,0,[],0,n?new rf(n,n.start):null,0,null)}get context(){return this.curContext?this.curContext.context:null}pushState(t,e){this.stack.push(this.state,e,this.bufferBase+this.buffer.length),this.state=t}reduce(t){var e;let i=t>>19,n=65535&t,{parser:r}=this.p,s=this.reducePos<this.pos-25&&this.setLookAhead(this.pos),o=r.dynamicPrecedence(n);if(o&&(this.score+=o),0==i)return this.pushState(r.getGoto(this.state,n,!0),this.reducePos),n<r.minRepeatTerm&&this.storeNode(n,this.reducePos,this.reducePos,s?8:4,!0),void this.reduceContext(n,this.reducePos);let a=this.stack.length-3*(i-1)-(262144&t?6:0),l=a?this.stack[a-2]:this.p.ranges[0].from,h=this.reducePos-l;h>=2e3&&!(null===(e=this.p.parser.nodeSet.types[n])||void 0===e?void 0:e.isAnonymous)&&(l==this.p.lastBigReductionStart?(this.p.bigReductionCount++,this.p.lastBigReductionSize=h):this.p.lastBigReductionSize<h&&(this.p.bigReductionCount=1,this.p.lastBigReductionStart=l,this.p.lastBigReductionSize=h));let c=a?this.stack[a-1]:0,u=this.bufferBase+this.buffer.length-c;if(n<r.minRepeatTerm||131072&t){let t=r.stateFlag(this.state,1)?this.pos:this.reducePos;this.storeNode(n,l,t,u+4,!0)}if(262144&t)this.state=this.stack[a];else{let t=this.stack[a-3];this.state=r.getGoto(t,n,!0)}for(;this.stack.length>a;)this.stack.pop();this.reduceContext(n,l)}storeNode(t,e,i,n=4,r=!1){if(0==t&&(!this.stack.length||this.stack[this.stack.length-1]<this.buffer.length+this.bufferBase)){let t=this,n=this.buffer.length;if(0==n&&t.parent&&(n=t.bufferBase-t.parent.bufferBase,t=t.parent),n>0&&0==t.buffer[n-4]&&t.buffer[n-1]>-1){if(e==i)return;if(t.buffer[n-2]>=e)return void(t.buffer[n-2]=i)}}if(r&&this.pos!=i){let r=this.buffer.length;if(r>0&&(0!=this.buffer[r-4]||this.buffer[r-1]<0)){let t=!1;for(let e=r;e>0&&this.buffer[e-2]>i;e-=4)if(this.buffer[e-1]>=0){t=!0;break}if(t)for(;r>0&&this.buffer[r-2]>i;)this.buffer[r]=this.buffer[r-4],this.buffer[r+1]=this.buffer[r-3],this.buffer[r+2]=this.buffer[r-2],this.buffer[r+3]=this.buffer[r-1],r-=4,n>4&&(n-=4)}this.buffer[r]=t,this.buffer[r+1]=e,this.buffer[r+2]=i,this.buffer[r+3]=n}else this.buffer.push(t,e,i,n)}shift(t,e,i,n){if(131072&t)this.pushState(65535&t,this.pos);else if(262144&t)this.pos=n,this.shiftContext(e,i),e<=this.p.parser.maxNode&&this.buffer.push(e,i,n,4);else{let r=t,{parser:s}=this.p;this.pos=n,!s.stateFlag(r,1)&&(n>i||e<=s.maxNode)&&(this.reducePos=n),this.pushState(r,Math.min(i,this.reducePos)),this.shiftContext(e,i),e<=s.maxNode&&this.buffer.push(e,i,n,4)}}apply(t,e,i,n){65536&t?this.reduce(t):this.shift(t,e,i,n)}useNode(t,e){let i=this.p.reused.length-1;(i<0||this.p.reused[i]!=t)&&(this.p.reused.push(t),i++);let n=this.pos;this.reducePos=this.pos=n+t.length,this.pushState(e,n),this.buffer.push(i,n,this.reducePos,-1),this.curContext&&this.updateContext(this.curContext.tracker.reuse(this.curContext.context,t,this,this.p.stream.reset(this.pos-t.length)))}split(){let t=this,e=t.buffer.length;for(;e>0&&t.buffer[e-2]>t.reducePos;)e-=4;let i=t.buffer.slice(e),n=t.bufferBase+e;for(;t&&n==t.bufferBase;)t=t.parent;return new nf(this.p,this.stack.slice(),this.state,this.reducePos,this.pos,this.score,i,n,this.curContext,this.lookAhead,t)}recoverByDelete(t,e){let i=t<=this.p.parser.maxNode;i&&this.storeNode(t,this.pos,e,4),this.storeNode(0,this.pos,e,i?8:4),this.pos=this.reducePos=e,this.score-=190}canShift(t){for(let e=new sf(this);;){let i=this.p.parser.stateSlot(e.state,4)||this.p.parser.hasAction(e.state,t);if(0==i)return!1;if(!(65536&i))return!0;e.reduce(i)}}recoverByInsert(t){if(this.stack.length>=300)return[];let e=this.p.parser.nextStates(this.state);if(e.length>8||this.stack.length>=120){let i=[];for(let n,r=0;r<e.length;r+=2)(n=e[r+1])!=this.state&&this.p.parser.hasAction(n,t)&&i.push(e[r],n);if(this.stack.length<120)for(let t=0;i.length<8&&t<e.length;t+=2){let n=e[t+1];i.some((t,e)=>1&e&&t==n)||i.push(e[t],n)}e=i}let i=[];for(let n=0;n<e.length&&i.length<4;n+=2){let t=e[n+1];if(t==this.state)continue;let r=this.split();r.pushState(t,this.pos),r.storeNode(0,r.pos,r.pos,4,!0),r.shiftContext(e[n],this.pos),r.reducePos=this.pos,r.score-=200,i.push(r)}return i}forceReduce(){let{parser:t}=this.p,e=t.stateSlot(this.state,5);if(!(65536&e))return!1;if(!t.validAction(this.state,e)){let i=e>>19,n=65535&e,r=this.stack.length-3*i;if(r<0||t.getGoto(this.stack[r],n,!1)<0){let t=this.findForcedReduction();if(null==t)return!1;e=t}this.storeNode(0,this.pos,this.pos,4,!0),this.score-=100}return this.reducePos=this.pos,this.reduce(e),!0}findForcedReduction(){let{parser:t}=this.p,e=[],i=(n,r)=>{if(!e.includes(n))return e.push(n),t.allActions(n,e=>{if(393216&e);else if(65536&e){let i=(e>>19)-r;if(i>1){let n=65535&e,r=this.stack.length-3*i;if(r>=0&&t.getGoto(this.stack[r],n,!1)>=0)return i<<19|65536|n}}else{let t=i(e,r+1);if(null!=t)return t}})};return i(this.state,0)}forceAll(){for(;!this.p.parser.stateFlag(this.state,2);)if(!this.forceReduce()){this.storeNode(0,this.pos,this.pos,4,!0);break}return this}get deadEnd(){if(3!=this.stack.length)return!1;let{parser:t}=this.p;return 65535==t.data[t.stateSlot(this.state,1)]&&!t.stateSlot(this.state,4)}restart(){this.storeNode(0,this.pos,this.pos,4,!0),this.state=this.stack[0],this.stack.length=0}sameState(t){if(this.state!=t.state||this.stack.length!=t.stack.length)return!1;for(let e=0;e<this.stack.length;e+=3)if(this.stack[e]!=t.stack[e])return!1;return!0}get parser(){return this.p.parser}dialectEnabled(t){return this.p.parser.dialect.flags[t]}shiftContext(t,e){this.curContext&&this.updateContext(this.curContext.tracker.shift(this.curContext.context,t,this,this.p.stream.reset(e)))}reduceContext(t,e){this.curContext&&this.updateContext(this.curContext.tracker.reduce(this.curContext.context,t,this,this.p.stream.reset(e)))}emitContext(){let t=this.buffer.length-1;(t<0||-3!=this.buffer[t])&&this.buffer.push(this.curContext.hash,this.pos,this.pos,-3)}emitLookAhead(){let t=this.buffer.length-1;(t<0||-4!=this.buffer[t])&&this.buffer.push(this.lookAhead,this.pos,this.pos,-4)}updateContext(t){if(t!=this.curContext.context){let e=new rf(this.curContext.tracker,t);e.hash!=this.curContext.hash&&this.emitContext(),this.curContext=e}}setLookAhead(t){return!(t<=this.lookAhead)&&(this.emitLookAhead(),this.lookAhead=t,!0)}close(){this.curContext&&this.curContext.tracker.strict&&this.emitContext(),this.lookAhead>0&&this.emitLookAhead()}}class rf{constructor(t,e){this.tracker=t,this.context=e,this.hash=t.strict?t.hash(e):0}}class sf{constructor(t){this.start=t,this.state=t.state,this.stack=t.stack,this.base=this.stack.length}reduce(t){let e=65535&t,i=t>>19;0==i?(this.stack==this.start.stack&&(this.stack=this.stack.slice()),this.stack.push(this.state,0,0),this.base+=3):this.base-=3*(i-1);let n=this.start.p.parser.getGoto(this.stack[this.base-3],e,!0);this.state=n}}class of{constructor(t,e,i){this.stack=t,this.pos=e,this.index=i,this.buffer=t.buffer,0==this.index&&this.maybeNext()}static create(t,e=t.bufferBase+t.buffer.length){return new of(t,e,e-t.bufferBase)}maybeNext(){let t=this.stack.parent;null!=t&&(this.index=this.stack.bufferBase-t.bufferBase,this.stack=t,this.buffer=t.buffer)}get id(){return this.buffer[this.index-4]}get start(){return this.buffer[this.index-3]}get end(){return this.buffer[this.index-2]}get size(){return this.buffer[this.index-1]}next(){this.index-=4,this.pos-=4,0==this.index&&this.maybeNext()}fork(){return new of(this.stack,this.pos,this.index)}}function af(t,e=Uint16Array){if(\"string\"!=typeof t)return t;let i=null;for(let n=0,r=0;n<t.length;){let s=0;for(;;){let e=t.charCodeAt(n++),i=!1;if(126==e){s=65535;break}e>=92&&e--,e>=34&&e--;let r=e-32;if(r>=46&&(r-=46,i=!0),s+=r,i)break;s*=46}i?i[r++]=s:i=new e(s)}return i}class lf{constructor(){this.start=-1,this.value=-1,this.end=-1,this.extended=-1,this.lookAhead=0,this.mask=0,this.context=0}}const hf=new lf;class cf{constructor(t,e){this.input=t,this.ranges=e,this.chunk=\"\",this.chunkOff=0,this.chunk2=\"\",this.chunk2Pos=0,this.next=-1,this.token=hf,this.rangeIndex=0,this.pos=this.chunkPos=e[0].from,this.range=e[0],this.end=e[e.length-1].to,this.readNext()}resolveOffset(t,e){let i=this.range,n=this.rangeIndex,r=this.pos+t;for(;r<i.from;){if(!n)return null;let t=this.ranges[--n];r-=i.from-t.to,i=t}for(;e<0?r>i.to:r>=i.to;){if(n==this.ranges.length-1)return null;let t=this.ranges[++n];r+=t.from-i.to,i=t}return r}clipPos(t){if(t>=this.range.from&&t<this.range.to)return t;for(let e of this.ranges)if(e.to>t)return Math.max(t,e.from);return this.end}peek(t){let e,i,n=this.chunkOff+t;if(n>=0&&n<this.chunk.length)e=this.pos+t,i=this.chunk.charCodeAt(n);else{let n=this.resolveOffset(t,1);if(null==n)return-1;if(e=n,e>=this.chunk2Pos&&e<this.chunk2Pos+this.chunk2.length)i=this.chunk2.charCodeAt(e-this.chunk2Pos);else{let t=this.rangeIndex,n=this.range;for(;n.to<=e;)n=this.ranges[++t];this.chunk2=this.input.chunk(this.chunk2Pos=e),e+this.chunk2.length>n.to&&(this.chunk2=this.chunk2.slice(0,n.to-e)),i=this.chunk2.charCodeAt(0)}}return e>=this.token.lookAhead&&(this.token.lookAhead=e+1),i}acceptToken(t,e=0){let i=e?this.resolveOffset(e,-1):this.pos;if(null==i||i<this.token.start)throw new RangeError(\"Token end out of bounds\");this.token.value=t,this.token.end=i}acceptTokenTo(t,e){this.token.value=t,this.token.end=e}getChunk(){if(this.pos>=this.chunk2Pos&&this.pos<this.chunk2Pos+this.chunk2.length){let{chunk:t,chunkPos:e}=this;this.chunk=this.chunk2,this.chunkPos=this.chunk2Pos,this.chunk2=t,this.chunk2Pos=e,this.chunkOff=this.pos-this.chunkPos}else{this.chunk2=this.chunk,this.chunk2Pos=this.chunkPos;let t=this.input.chunk(this.pos),e=this.pos+t.length;this.chunk=e>this.range.to?t.slice(0,this.range.to-this.pos):t,this.chunkPos=this.pos,this.chunkOff=0}}readNext(){return this.chunkOff>=this.chunk.length&&(this.getChunk(),this.chunkOff==this.chunk.length)?this.next=-1:this.next=this.chunk.charCodeAt(this.chunkOff)}advance(t=1){for(this.chunkOff+=t;this.pos+t>=this.range.to;){if(this.rangeIndex==this.ranges.length-1)return this.setDone();t-=this.range.to-this.pos,this.range=this.ranges[++this.rangeIndex],this.pos=this.range.from}return this.pos+=t,this.pos>=this.token.lookAhead&&(this.token.lookAhead=this.pos+1),this.readNext()}setDone(){return this.pos=this.chunkPos=this.end,this.range=this.ranges[this.rangeIndex=this.ranges.length-1],this.chunk=\"\",this.next=-1}reset(t,e){if(e?(this.token=e,e.start=t,e.lookAhead=t+1,e.value=e.extended=-1):this.token=hf,this.pos!=t){if(this.pos=t,t==this.end)return this.setDone(),this;for(;t<this.range.from;)this.range=this.ranges[--this.rangeIndex];for(;t>=this.range.to;)this.range=this.ranges[++this.rangeIndex];t>=this.chunkPos&&t<this.chunkPos+this.chunk.length?this.chunkOff=t-this.chunkPos:(this.chunk=\"\",this.chunkOff=0),this.readNext()}return this}read(t,e){if(t>=this.chunkPos&&e<=this.chunkPos+this.chunk.length)return this.chunk.slice(t-this.chunkPos,e-this.chunkPos);if(t>=this.chunk2Pos&&e<=this.chunk2Pos+this.chunk2.length)return this.chunk2.slice(t-this.chunk2Pos,e-this.chunk2Pos);if(t>=this.range.from&&e<=this.range.to)return this.input.read(t,e);let i=\"\";for(let n of this.ranges){if(n.from>=e)break;n.to>t&&(i+=this.input.read(Math.max(n.from,t),Math.min(n.to,e)))}return i}}class uf{constructor(t,e){this.data=t,this.id=e}token(t,e){let{parser:i}=e.p;Of(this.data,t,e,this.id,i.data,i.tokenPrecTable)}}uf.prototype.contextual=uf.prototype.fallback=uf.prototype.extend=!1;class df{constructor(t,e,i){this.precTable=e,this.elseToken=i,this.data=\"string\"==typeof t?af(t):t}token(t,e){let i=t.pos,n=0;for(;;){let i=t.next<0,r=t.resolveOffset(1,1);if(Of(this.data,t,e,0,this.data,this.precTable),t.token.value>-1)break;if(null==this.elseToken)return;if(i||n++,null==r)break;t.reset(r,t.token)}n&&(t.reset(i,t.token),t.acceptToken(this.elseToken,n))}}df.prototype.contextual=uf.prototype.fallback=uf.prototype.extend=!1;class ff{constructor(t,e={}){this.token=t,this.contextual=!!e.contextual,this.fallback=!!e.fallback,this.extend=!!e.extend}}function Of(t,e,i,n,r,s){let o=0,a=1<<n,{dialect:l}=i.p.parser;t:for(;0!=(a&t[o]);){let i=t[o+1];for(let u=o+3;u<i;u+=2)if((t[u+1]&a)>0){let i=t[u];if(l.allows(i)&&(-1==e.token.value||e.token.value==i||mf(i,e.token.value,r,s))){e.acceptToken(i);break}}let n=e.next,h=0,c=t[o+2];if(!(e.next<0&&c>h&&65535==t[i+3*c-3])){for(;h<c;){let r=h+c>>1,s=i+r+(r<<1),a=t[s],l=t[s+1]||65536;if(n<a)c=r;else{if(!(n>=l)){o=t[s+2],e.advance();continue t}h=r+1}}break}o=t[i+3*c-1]}}function pf(t,e,i){for(let n,r=e;65535!=(n=t[r]);r++)if(n==i)return r-e;return-1}function mf(t,e,i,n){let r=pf(i,n,e);return r<0||pf(i,n,t)<r}const gf=\"undefined\"!=typeof process&&ef&&/\\bparse\\b/.test(ef.LOG);let Qf=null;function bf(t,e,i){let n=t.cursor(da.IncludeAnonymous);for(n.moveTo(e);;)if(!(i<0?n.childBefore(e):n.childAfter(e)))for(;;){if((i<0?n.to<e:n.from>e)&&!n.type.isError)return i<0?Math.max(0,Math.min(n.to-1,e-25)):Math.min(t.length,Math.max(n.from+1,e+25));if(i<0?n.prevSibling():n.nextSibling())break;if(!n.parent())return i<0?0:t.length}}class vf{constructor(t,e){this.fragments=t,this.nodeSet=e,this.i=0,this.fragment=null,this.safeFrom=-1,this.safeTo=-1,this.trees=[],this.start=[],this.index=[],this.nextFragment()}nextFragment(){let t=this.fragment=this.i==this.fragments.length?null:this.fragments[this.i++];if(t){for(this.safeFrom=t.openStart?bf(t.tree,t.from+t.offset,1)-t.offset:t.from,this.safeTo=t.openEnd?bf(t.tree,t.to+t.offset,-1)-t.offset:t.to;this.trees.length;)this.trees.pop(),this.start.pop(),this.index.pop();this.trees.push(t.tree),this.start.push(-t.offset),this.index.push(0),this.nextStart=this.safeFrom}else this.nextStart=1e9}nodeAt(t){if(t<this.nextStart)return null;for(;this.fragment&&this.safeTo<=t;)this.nextFragment();if(!this.fragment)return null;for(;;){let e=this.trees.length-1;if(e<0)return this.nextFragment(),null;let i=this.trees[e],n=this.index[e];if(n==i.children.length){this.trees.pop(),this.start.pop(),this.index.pop();continue}let r=i.children[n],s=this.start[e]+i.positions[n];if(s>t)return this.nextStart=s,null;if(r instanceof Oa){if(s==t){if(s<this.safeFrom)return null;let t=s+r.length;if(t<=this.safeTo){let e=r.prop(sa.lookAhead);if(!e||t+e<this.fragment.to)return r}}this.index[e]++,s+r.length>=Math.max(this.safeFrom,t)&&(this.trees.push(r),this.start.push(s),this.index.push(0))}else this.index[e]++,this.nextStart=s+r.length}}}class wf{constructor(t,e){this.stream=e,this.tokens=[],this.mainToken=null,this.actions=[],this.tokens=t.tokenizers.map(t=>new lf)}getActions(t){let e=0,i=null,{parser:n}=t.p,{tokenizers:r}=n,s=n.stateSlot(t.state,3),o=t.curContext?t.curContext.hash:0,a=0;for(let l=0;l<r.length;l++){if(!(1<<l&s))continue;let n=r[l],h=this.tokens[l];if((!i||n.fallback)&&((n.contextual||h.start!=t.pos||h.mask!=s||h.context!=o)&&(this.updateCachedToken(h,n,t),h.mask=s,h.context=o),h.lookAhead>h.end+25&&(a=Math.max(h.lookAhead,a)),0!=h.value)){let r=e;if(h.extended>-1&&(e=this.addActions(t,h.extended,h.end,e)),e=this.addActions(t,h.value,h.end,e),!n.extend&&(i=h,e>r))break}}for(;this.actions.length>e;)this.actions.pop();return a&&t.setLookAhead(a),i||t.pos!=this.stream.end||(i=new lf,i.value=t.p.parser.eofTerm,i.start=i.end=t.pos,e=this.addActions(t,i.value,i.end,e)),this.mainToken=i,this.actions}getMainToken(t){if(this.mainToken)return this.mainToken;let e=new lf,{pos:i,p:n}=t;return e.start=i,e.end=Math.min(i+1,n.stream.end),e.value=i==n.stream.end?n.parser.eofTerm:0,e}updateCachedToken(t,e,i){let n=this.stream.clipPos(i.pos);if(e.token(this.stream.reset(n,t),i),t.value>-1){let{parser:e}=i.p;for(let n=0;n<e.specialized.length;n++)if(e.specialized[n]==t.value){let r=e.specializers[n](this.stream.read(t.start,t.end),i);if(r>=0&&i.p.parser.dialect.allows(r>>1)){1&r?t.extended=r>>1:t.value=r>>1;break}}}else t.value=0,t.end=this.stream.clipPos(n+1)}putAction(t,e,i,n){for(let r=0;r<n;r+=3)if(this.actions[r]==t)return n;return this.actions[n++]=t,this.actions[n++]=e,this.actions[n++]=i,n}addActions(t,e,i,n){let{state:r}=t,{parser:s}=t.p,{data:o}=s;for(let a=0;a<2;a++)for(let t=s.stateSlot(r,a?2:1);;t+=3){if(65535==o[t]){if(1!=o[t+1]){0==n&&2==o[t+1]&&(n=this.putAction(Tf(o,t+2),e,i,n));break}t=Tf(o,t+2)}o[t]==e&&(n=this.putAction(Tf(o,t+1),e,i,n))}return n}}class xf{constructor(t,e,i,n){this.parser=t,this.input=e,this.ranges=n,this.recovering=0,this.nextStackID=9812,this.minStackPos=0,this.reused=[],this.stoppedAt=null,this.lastBigReductionStart=-1,this.lastBigReductionSize=0,this.bigReductionCount=0,this.stream=new cf(e,n),this.tokens=new wf(t,this.stream),this.topTerm=t.top[1];let{from:r}=n[0];this.stacks=[nf.start(this,t.top[0],r)],this.fragments=i.length&&this.stream.end-r>4*t.bufferLength?new vf(i,t.nodeSet):null}get parsedPos(){return this.minStackPos}advance(){let t,e,i=this.stacks,n=this.minStackPos,r=this.stacks=[];if(this.bigReductionCount>300&&1==i.length){let[t]=i;for(;t.forceReduce()&&t.stack.length&&t.stack[t.stack.length-2]>=this.lastBigReductionStart;);this.bigReductionCount=this.lastBigReductionSize=0}for(let s=0;s<i.length;s++){let o=i[s];for(;;){if(this.tokens.mainToken=null,o.pos>n)r.push(o);else{if(this.advanceStack(o,r,i))continue;{t||(t=[],e=[]),t.push(o);let i=this.tokens.getMainToken(o);e.push(i.value,i.end)}}break}}if(!r.length){let e=t&&function(t){let e=null;for(let i of t){let t=i.p.stoppedAt;(i.pos==i.p.stream.end||null!=t&&i.pos>t)&&i.p.parser.stateFlag(i.state,2)&&(!e||e.score<i.score)&&(e=i)}return e}(t);if(e)return this.stackToTree(e);if(this.parser.strict)throw new SyntaxError(\"No parse at \"+n);this.recovering||(this.recovering=5)}if(this.recovering&&t){let i=null!=this.stoppedAt&&t[0].pos>this.stoppedAt?t[0]:this.runRecovery(t,e,r);if(i)return this.stackToTree(i.forceAll())}if(this.recovering){let t=1==this.recovering?1:3*this.recovering;if(r.length>t)for(r.sort((t,e)=>e.score-t.score);r.length>t;)r.pop();r.some(t=>t.reducePos>n)&&this.recovering--}else if(r.length>1){t:for(let t=0;t<r.length-1;t++){let e=r[t];for(let i=t+1;i<r.length;i++){let n=r[i];if(e.sameState(n)||e.buffer.length>500&&n.buffer.length>500){if(!((e.score-n.score||e.buffer.length-n.buffer.length)>0)){r.splice(t--,1);continue t}r.splice(i--,1)}}}r.length>12&&(r.sort((t,e)=>e.score-t.score),r.splice(12,r.length-12))}this.minStackPos=r[0].pos;for(let s=1;s<r.length;s++)r[s].pos<this.minStackPos&&(this.minStackPos=r[s].pos);return null}stopAt(t){if(null!=this.stoppedAt&&this.stoppedAt<t)throw new RangeError(\"Can't move stoppedAt forward\");this.stoppedAt=t}advanceStack(t,e,i){let n=t.pos,{parser:r}=this;gf&&this.stackID(t);if(null!=this.stoppedAt&&n>this.stoppedAt)return t.forceReduce()?t:null;if(this.fragments){let e=t.curContext&&t.curContext.tracker.strict,i=e?t.curContext.hash:0;for(let s=this.fragments.nodeAt(n);s;){let n=this.parser.nodeSet.types[s.type.id]==s.type?r.getGoto(t.state,s.type.id):-1;if(n>-1&&s.length&&(!e||(s.prop(sa.contextHash)||0)==i))return t.useNode(s,n),!0;if(!(s instanceof Oa)||0==s.children.length||s.positions[0]>0)break;let o=s.children[0];if(!(o instanceof Oa&&0==s.positions[0]))break;s=o}}let s=r.stateSlot(t.state,4);if(s>0)return t.reduce(s),!0;if(t.stack.length>=8400)for(;t.stack.length>6e3&&t.forceReduce(););let o=this.tokens.getActions(t);for(let a=0;a<o.length;){let r=o[a++],s=o[a++],l=o[a++],h=a==o.length||!i,c=h?t:t.split(),u=this.tokens.mainToken;if(c.apply(r,s,u?u.start:c.pos,l),h)return!0;c.pos>n?e.push(c):i.push(c)}return!1}advanceFully(t,e){let i=t.pos;for(;;){if(!this.advanceStack(t,null,null))return!1;if(t.pos>i)return Sf(t,e),!0}}runRecovery(t,e,i){let n=null,r=!1;for(let s=0;s<t.length;s++){let o=t[s],a=e[s<<1],l=e[1+(s<<1)],h=gf?this.stackID(o)+\" -> \":\"\";if(o.deadEnd){if(r)continue;if(r=!0,o.restart(),this.advanceFully(o,i))continue}let c=o.split(),u=h;for(let t=0;t<10&&c.forceReduce();t++){if(this.advanceFully(c,i))break;gf&&(u=this.stackID(c)+\" -> \")}for(let t of o.recoverByInsert(a))this.advanceFully(t,i);this.stream.end>o.pos?(l==o.pos&&(l++,a=0),o.recoverByDelete(a,l),Sf(o,i)):(!n||n.score<c.score)&&(n=c)}return n}stackToTree(t){return t.close(),Oa.build({buffer:of.create(t),nodeSet:this.parser.nodeSet,topID:this.topTerm,maxBufferLength:this.parser.bufferLength,reused:this.reused,start:this.ranges[0].from,length:t.pos-this.ranges[0].from,minRepeatType:this.parser.minRepeatTerm})}stackID(t){let e=(Qf||(Qf=new WeakMap)).get(t);return e||Qf.set(t,e=String.fromCodePoint(this.nextStackID++)),e+t}}function Sf(t,e){for(let i=0;i<e.length;i++){let n=e[i];if(n.pos==t.pos&&n.sameState(t))return void(e[i].score<t.score&&(e[i]=t))}e.push(t)}class yf{constructor(t,e,i){this.source=t,this.flags=e,this.disabled=i}allows(t){return!this.disabled||0==this.disabled[t]}}const kf=t=>t;class $f{constructor(t){this.start=t.start,this.shift=t.shift||kf,this.reduce=t.reduce||kf,this.reuse=t.reuse||kf,this.hash=t.hash||(()=>0),this.strict=!1!==t.strict}}class Pf extends Ra{constructor(t){if(super(),this.wrappers=[],14!=t.version)throw new RangeError(`Parser version (${t.version}) doesn't match runtime version (14)`);let e=t.nodeNames.split(\" \");this.minRepeatTerm=e.length;for(let o=0;o<t.repeatNodeCount;o++)e.push(\"\");let i=Object.keys(t.topRules).map(e=>t.topRules[e][1]),n=[];for(let o=0;o<e.length;o++)n.push([]);function r(t,e,i){n[t].push([e,e.deserialize(String(i))])}if(t.nodeProps)for(let o of t.nodeProps){let t=o[0];\"string\"==typeof t&&(t=sa[t]);for(let e=1;e<o.length;){let i=o[e++];if(i>=0)r(i,t,o[e++]);else{let n=o[e+-i];for(let s=-i;s>0;s--)r(o[e++],t,n);e++}}}this.nodeSet=new ha(e.map((e,r)=>la.define({name:r>=this.minRepeatTerm?void 0:e,id:r,props:n[r],top:i.indexOf(r)>-1,error:0==r,skipped:t.skippedNodes&&t.skippedNodes.indexOf(r)>-1}))),t.propSources&&(this.nodeSet=this.nodeSet.extend(...t.propSources)),this.strict=!1,this.bufferLength=ia;let s=af(t.tokenData);this.context=t.context,this.specializerSpecs=t.specialized||[],this.specialized=new Uint16Array(this.specializerSpecs.length);for(let o=0;o<this.specializerSpecs.length;o++)this.specialized[o]=this.specializerSpecs[o].term;this.specializers=this.specializerSpecs.map(Cf),this.states=af(t.states,Uint32Array),this.data=af(t.stateData),this.goto=af(t.goto),this.maxTerm=t.maxTerm,this.tokenizers=t.tokenizers.map(t=>\"number\"==typeof t?new uf(s,t):t),this.topRules=t.topRules,this.dialects=t.dialects||{},this.dynamicPrecedences=t.dynamicPrecedences||null,this.tokenPrecTable=t.tokenPrec,this.termNames=t.termNames||null,this.maxNode=this.nodeSet.types.length-1,this.dialect=this.parseDialect(),this.top=this.topRules[Object.keys(this.topRules)[0]]}createParse(t,e,i){let n=new xf(this,t,e,i);for(let r of this.wrappers)n=r(n,t,e,i);return n}getGoto(t,e,i=!1){let n=this.goto;if(e>=n[0])return-1;for(let r=n[e+1];;){let e=n[r++],s=1&e,o=n[r++];if(s&&i)return o;for(let i=r+(e>>1);r<i;r++)if(n[r]==t)return o;if(s)return-1}}hasAction(t,e){let i=this.data;for(let n=0;n<2;n++)for(let r,s=this.stateSlot(t,n?2:1);;s+=3){if(65535==(r=i[s])){if(1!=i[s+1]){if(2==i[s+1])return Tf(i,s+2);break}r=i[s=Tf(i,s+2)]}if(r==e||0==r)return Tf(i,s+1)}return 0}stateSlot(t,e){return this.states[6*t+e]}stateFlag(t,e){return(this.stateSlot(t,0)&e)>0}validAction(t,e){return!!this.allActions(t,t=>t==e||null)}allActions(t,e){let i=this.stateSlot(t,4),n=i?e(i):void 0;for(let r=this.stateSlot(t,1);null==n;r+=3){if(65535==this.data[r]){if(1!=this.data[r+1])break;r=Tf(this.data,r+2)}n=e(Tf(this.data,r+1))}return n}nextStates(t){let e=[];for(let i=this.stateSlot(t,1);;i+=3){if(65535==this.data[i]){if(1!=this.data[i+1])break;i=Tf(this.data,i+2)}if(!(1&this.data[i+2])){let t=this.data[i+1];e.some((e,i)=>1&i&&e==t)||e.push(this.data[i],t)}}return e}configure(t){let e=Object.assign(Object.create(Pf.prototype),this);if(t.props&&(e.nodeSet=this.nodeSet.extend(...t.props)),t.top){let i=this.topRules[t.top];if(!i)throw new RangeError(`Invalid top rule name ${t.top}`);e.top=i}return t.tokenizers&&(e.tokenizers=this.tokenizers.map(e=>{let i=t.tokenizers.find(t=>t.from==e);return i?i.to:e})),t.specializers&&(e.specializers=this.specializers.slice(),e.specializerSpecs=this.specializerSpecs.map((i,n)=>{let r=t.specializers.find(t=>t.from==i.external);if(!r)return i;let s=Object.assign(Object.assign({},i),{external:r.to});return e.specializers[n]=Cf(s),s})),t.contextTracker&&(e.context=t.contextTracker),t.dialect&&(e.dialect=this.parseDialect(t.dialect)),null!=t.strict&&(e.strict=t.strict),t.wrap&&(e.wrappers=e.wrappers.concat(t.wrap)),null!=t.bufferLength&&(e.bufferLength=t.bufferLength),e}hasWrappers(){return this.wrappers.length>0}getName(t){return this.termNames?this.termNames[t]:String(t<=this.maxNode&&this.nodeSet.types[t].name||t)}get eofTerm(){return this.maxNode+1}get topNode(){return this.nodeSet.types[this.top[1]]}dynamicPrecedence(t){let e=this.dynamicPrecedences;return null==e?0:e[t]||0}parseDialect(t){let e=Object.keys(this.dialects),i=e.map(()=>!1);if(t)for(let r of t.split(\" \")){let t=e.indexOf(r);t>=0&&(i[t]=!0)}let n=null;for(let r=0;r<e.length;r++)if(!i[r])for(let t,i=this.dialects[e[r]];65535!=(t=this.data[i++]);)(n||(n=new Uint8Array(this.maxTerm+1)))[t]=1;return new yf(t,i,n)}static deserialize(t){return new Pf(t)}}function Tf(t,e){return t[e]|t[e+1]<<16}function Cf(t){if(t.external){let e=t.extend?1:0;return(i,n)=>t.external(i,n)<<1|e}return t.get}const Zf={area:!0,base:!0,br:!0,col:!0,command:!0,embed:!0,frame:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0,menuitem:!0},Xf={dd:!0,li:!0,optgroup:!0,option:!0,p:!0,rp:!0,rt:!0,tbody:!0,td:!0,tfoot:!0,th:!0,tr:!0},Af={dd:{dd:!0,dt:!0},dt:{dd:!0,dt:!0},li:{li:!0},option:{option:!0,optgroup:!0},optgroup:{optgroup:!0},p:{address:!0,article:!0,aside:!0,blockquote:!0,dir:!0,div:!0,dl:!0,fieldset:!0,footer:!0,form:!0,h1:!0,h2:!0,h3:!0,h4:!0,h5:!0,h6:!0,header:!0,hgroup:!0,hr:!0,menu:!0,nav:!0,ol:!0,p:!0,pre:!0,section:!0,table:!0,ul:!0},rp:{rp:!0,rt:!0},rt:{rp:!0,rt:!0},tbody:{tbody:!0,tfoot:!0},td:{td:!0,th:!0},tfoot:{tbody:!0},th:{td:!0,th:!0},thead:{tbody:!0,tfoot:!0},tr:{tr:!0}};function Mf(t){return 45==t||46==t||58==t||t>=65&&t<=90||95==t||t>=97&&t<=122||t>=161}let Rf=null,zf=null,_f=0;function Ef(t,e){let i=t.pos+e;if(_f==i&&zf==t)return Rf;let n=t.peek(e),r=\"\";for(;Mf(n);)r+=String.fromCharCode(n),n=t.peek(++e);return zf=t,_f=i,Rf=r?r.toLowerCase():n==Yf||n==Lf?void 0:null}const Yf=63,Lf=33;function qf(t,e){this.name=t,this.parent=e}const Vf=[6,10,7,8,9],Wf=new $f({start:null,shift:(t,e,i,n)=>Vf.indexOf(e)>-1?new qf(Ef(n,1)||\"\",t):t,reduce:(t,e)=>21==e&&t?t.parent:t,reuse(t,e,i,n){let r=e.type.id;return 6==r||37==r?new qf(Ef(n,1)||\"\",t):t},strict:!1}),Df=new ff((t,e)=>{if(60!=t.next)return void(t.next<0&&e.context&&t.acceptToken(58));t.advance();let i=47==t.next;i&&t.advance();let n=Ef(t,0);if(void 0===n)return;if(!n)return t.acceptToken(i?15:14);let r=e.context?e.context.name:null;if(i){if(n==r)return t.acceptToken(11);if(r&&Xf[r])return t.acceptToken(58,-2);if(e.dialectEnabled(0))return t.acceptToken(12);for(let t=e.context;t;t=t.parent)if(t.name==n)return;t.acceptToken(13)}else{if(\"script\"==n)return t.acceptToken(7);if(\"style\"==n)return t.acceptToken(8);if(\"textarea\"==n)return t.acceptToken(9);if(Zf.hasOwnProperty(n))return t.acceptToken(10);r&&Af[r]&&Af[r][n]?t.acceptToken(58,-1):t.acceptToken(6)}},{contextual:!0}),Bf=new ff(t=>{for(let e=0,i=0;;i++){if(t.next<0){i&&t.acceptToken(59);break}if(45==t.next)e++;else{if(62==t.next&&e>=2){i>=3&&t.acceptToken(59,-2);break}e=0}t.advance()}});const jf=new ff((t,e)=>{if(47==t.next&&62==t.peek(1)){let i=e.dialectEnabled(1)||function(t){for(;t;t=t.parent)if(\"svg\"==t.name||\"math\"==t.name)return!0;return!1}(e.context);t.acceptToken(i?5:4,2)}else 62==t.next&&t.acceptToken(4,1)});function If(t,e,i){let n=2+t.length;return new ff(r=>{for(let s=0,o=0,a=0;;a++){if(r.next<0){a&&r.acceptToken(e);break}if(0==s&&60==r.next||1==s&&47==r.next||s>=2&&s<n&&r.next==t.charCodeAt(s-2))s++,o++;else{if(s==n&&62==r.next){a>o?r.acceptToken(e,-o):r.acceptToken(i,-(o-2));break}if((10==r.next||13==r.next)&&a){r.acceptToken(e,1);break}s=o=0}r.advance()}})}const Gf=If(\"script\",55,1),Nf=If(\"style\",56,2),Uf=If(\"textarea\",57,3),Hf=tl({\"Text RawText IncompleteTag IncompleteCloseTag\":wl.content,\"StartTag StartCloseTag SelfClosingEndTag EndTag\":wl.angleBracket,TagName:wl.tagName,\"MismatchedCloseTag/TagName\":[wl.tagName,wl.invalid],AttributeName:wl.attributeName,\"AttributeValue UnquotedAttributeValue\":wl.attributeValue,Is:wl.definitionOperator,\"EntityReference CharacterReference\":wl.character,Comment:wl.blockComment,ProcessingInst:wl.processingInstruction,DoctypeDecl:wl.documentMeta}),Ff=Pf.deserialize({version:14,states:\",xOVO!rOOO!ZQ#tO'#CrO!`Q#tO'#C{O!eQ#tO'#DOO!jQ#tO'#DRO!oQ#tO'#DTO!tOaO'#CqO#PObO'#CqO#[OdO'#CqO$kO!rO'#CqOOO`'#Cq'#CqO$rO$fO'#DUO$zQ#tO'#DWO%PQ#tO'#DXOOO`'#Dl'#DlOOO`'#DZ'#DZQVO!rOOO%UQ&rO,59^O%aQ&rO,59gO%lQ&rO,59jO%wQ&rO,59mO&SQ&rO,59oOOOa'#D_'#D_O&_OaO'#CyO&jOaO,59]OOOb'#D`'#D`O&rObO'#C|O&}ObO,59]OOOd'#Da'#DaO'VOdO'#DPO'bOdO,59]OOO`'#Db'#DbO'jO!rO,59]O'qQ#tO'#DSOOO`,59],59]OOOp'#Dc'#DcO'vO$fO,59pOOO`,59p,59pO(OQ#|O,59rO(TQ#|O,59sOOO`-E7X-E7XO(YQ&rO'#CtOOQW'#D['#D[O(hQ&rO1G.xOOOa1G.x1G.xOOO`1G/Z1G/ZO(sQ&rO1G/ROOOb1G/R1G/RO)OQ&rO1G/UOOOd1G/U1G/UO)ZQ&rO1G/XOOO`1G/X1G/XO)fQ&rO1G/ZOOOa-E7]-E7]O)qQ#tO'#CzOOO`1G.w1G.wOOOb-E7^-E7^O)vQ#tO'#C}OOOd-E7_-E7_O){Q#tO'#DQOOO`-E7`-E7`O*QQ#|O,59nOOOp-E7a-E7aOOO`1G/[1G/[OOO`1G/^1G/^OOO`1G/_1G/_O*VQ,UO,59`OOQW-E7Y-E7YOOOa7+$d7+$dOOO`7+$u7+$uOOOb7+$m7+$mOOOd7+$p7+$pOOO`7+$s7+$sO*bQ#|O,59fO*gQ#|O,59iO*lQ#|O,59lOOO`1G/Y1G/YO*qO7[O'#CwO+SOMhO'#CwOOQW1G.z1G.zOOO`1G/Q1G/QOOO`1G/T1G/TOOO`1G/W1G/WOOOO'#D]'#D]O+eO7[O,59cOOQW,59c,59cOOOO'#D^'#D^O+vOMhO,59cOOOO-E7Z-E7ZOOQW1G.}1G.}OOOO-E7[-E7[\",stateData:\",c~O!_OS~OUSOVPOWQOXROYTO[]O][O^^O_^Oa^Ob^Oc^Od^Oy^O|_O!eZO~OgaO~OgbO~OgcO~OgdO~OgeO~O!XfOPmP![mP~O!YiOQpP![pP~O!ZlORsP![sP~OUSOVPOWQOXROYTOZqO[]O][O^^O_^Oa^Ob^Oc^Od^Oy^O!eZO~O![rO~P#gO!]sO!fuO~OgvO~OgwO~OS|OT}OiyO~OS!POT}OiyO~OS!ROT}OiyO~OS!TOT}OiyO~OS}OT}OiyO~O!XfOPmX![mX~OP!WO![!XO~O!YiOQpX![pX~OQ!ZO![!XO~O!ZlORsX![sX~OR!]O![!XO~O![!XO~P#gOg!_O~O!]sO!f!aO~OS!bO~OS!cO~Oj!dOShXThXihX~OS!fOT!gOiyO~OS!hOT!gOiyO~OS!iOT!gOiyO~OS!jOT!gOiyO~OS!gOT!gOiyO~Og!kO~Og!lO~Og!mO~OS!nO~Ol!qO!a!oO!c!pO~OS!rO~OS!sO~OS!tO~Ob!uOc!uOd!uO!a!wO!b!uO~Ob!xOc!xOd!xO!c!wO!d!xO~Ob!uOc!uOd!uO!a!{O!b!uO~Ob!xOc!xOd!xO!c!{O!d!xO~OT~cbd!ey|!e~\",goto:\"%q!aPPPPPPPPPPPPPPPPPPPPP!b!hP!nPP!zP!}#Q#T#Z#^#a#g#j#m#s#y!bP!b!bP$P$V$m$s$y%P%V%]%cPPPPPPPP%iX^OX`pXUOX`pezabcde{!O!Q!S!UR!q!dRhUR!XhXVOX`pRkVR!XkXWOX`pRnWR!XnXXOX`pQrXR!XpXYOX`pQ`ORx`Q{aQ!ObQ!QcQ!SdQ!UeZ!e{!O!Q!S!UQ!v!oR!z!vQ!y!pR!|!yQgUR!VgQjVR!YjQmWR![mQpXR!^pQtZR!`tS_O`ToXp\",nodeNames:\"⚠ StartCloseTag StartCloseTag StartCloseTag EndTag SelfClosingEndTag StartTag StartTag StartTag StartTag StartTag StartCloseTag StartCloseTag StartCloseTag IncompleteTag IncompleteCloseTag Document Text EntityReference CharacterReference InvalidEntity Element OpenTag TagName Attribute AttributeName Is AttributeValue UnquotedAttributeValue ScriptText CloseTag OpenTag StyleText CloseTag OpenTag TextareaText CloseTag OpenTag CloseTag SelfClosingTag Comment ProcessingInst MismatchedCloseTag CloseTag DoctypeDecl\",maxTerm:68,context:Wf,nodeProps:[[\"closedBy\",-10,1,2,3,7,8,9,10,11,12,13,\"EndTag\",6,\"EndTag SelfClosingEndTag\",-4,22,31,34,37,\"CloseTag\"],[\"openedBy\",4,\"StartTag StartCloseTag\",5,\"StartTag\",-4,30,33,36,38,\"OpenTag\"],[\"group\",-10,14,15,18,19,20,21,40,41,42,43,\"Entity\",17,\"Entity TextContent\",-3,29,32,35,\"TextContent Entity\"],[\"isolate\",-11,22,30,31,33,34,36,37,38,39,42,43,\"ltr\",-3,27,28,40,\"\"]],propSources:[Hf],skippedNodes:[0],repeatNodeCount:9,tokenData:\"!<p!aR!YOX$qXY,QYZ,QZ[$q[]&X]^,Q^p$qpq,Qqr-_rs3_sv-_vw3}wxHYx}-_}!OH{!O!P-_!P!Q$q!Q![-_![!]Mz!]!^-_!^!_!$S!_!`!;x!`!a&X!a!c-_!c!}Mz!}#R-_#R#SMz#S#T1k#T#oMz#o#s-_#s$f$q$f%W-_%W%oMz%o%p-_%p&aMz&a&b-_&b1pMz1p4U-_4U4dMz4d4e-_4e$ISMz$IS$I`-_$I`$IbMz$Ib$Kh-_$Kh%#tMz%#t&/x-_&/x&EtMz&Et&FV-_&FV;'SMz;'S;:j!#|;:j;=`3X<%l?&r-_?&r?AhMz?Ah?BY$q?BY?MnMz?MnO$q!Z$|caPlW!b`!dpOX$qXZ&XZ[$q[^&X^p$qpq&Xqr$qrs&}sv$qvw+Pwx(tx!^$q!^!_*V!_!a&X!a#S$q#S#T&X#T;'S$q;'S;=`+z<%lO$q!R&bXaP!b`!dpOr&Xrs&}sv&Xwx(tx!^&X!^!_*V!_;'S&X;'S;=`*y<%lO&Xq'UVaP!dpOv&}wx'kx!^&}!^!_(V!_;'S&};'S;=`(n<%lO&}P'pTaPOv'kw!^'k!_;'S'k;'S;=`(P<%lO'kP(SP;=`<%l'kp([S!dpOv(Vx;'S(V;'S;=`(h<%lO(Vp(kP;=`<%l(Vq(qP;=`<%l&}a({WaP!b`Or(trs'ksv(tw!^(t!^!_)e!_;'S(t;'S;=`*P<%lO(t`)jT!b`Or)esv)ew;'S)e;'S;=`)y<%lO)e`)|P;=`<%l)ea*SP;=`<%l(t!Q*^V!b`!dpOr*Vrs(Vsv*Vwx)ex;'S*V;'S;=`*s<%lO*V!Q*vP;=`<%l*V!R*|P;=`<%l&XW+UYlWOX+PZ[+P^p+Pqr+Psw+Px!^+P!a#S+P#T;'S+P;'S;=`+t<%lO+PW+wP;=`<%l+P!Z+}P;=`<%l$q!a,]`aP!b`!dp!_^OX&XXY,QYZ,QZ]&X]^,Q^p&Xpq,Qqr&Xrs&}sv&Xwx(tx!^&X!^!_*V!_;'S&X;'S;=`*y<%lO&X!_-ljiSaPlW!b`!dpOX$qXZ&XZ[$q[^&X^p$qpq&Xqr-_rs&}sv-_vw/^wx(tx!P-_!P!Q$q!Q!^-_!^!_*V!_!a&X!a#S-_#S#T1k#T#s-_#s$f$q$f;'S-_;'S;=`3X<%l?Ah-_?Ah?BY$q?BY?Mn-_?MnO$q[/ebiSlWOX+PZ[+P^p+Pqr/^sw/^x!P/^!P!Q+P!Q!^/^!a#S/^#S#T0m#T#s/^#s$f+P$f;'S/^;'S;=`1e<%l?Ah/^?Ah?BY+P?BY?Mn/^?MnO+PS0rXiSqr0msw0mx!P0m!Q!^0m!a#s0m$f;'S0m;'S;=`1_<%l?Ah0m?BY?Mn0mS1bP;=`<%l0m[1hP;=`<%l/^!V1vciSaP!b`!dpOq&Xqr1krs&}sv1kvw0mwx(tx!P1k!P!Q&X!Q!^1k!^!_*V!_!a&X!a#s1k#s$f&X$f;'S1k;'S;=`3R<%l?Ah1k?Ah?BY&X?BY?Mn1k?MnO&X!V3UP;=`<%l1k!_3[P;=`<%l-_!Z3hV!ahaP!dpOv&}wx'kx!^&}!^!_(V!_;'S&};'S;=`(n<%lO&}!_4WiiSlWd!ROX5uXZ7SZ[5u[^7S^p5uqr8trs7Sst>]tw8twx7Sx!P8t!P!Q5u!Q!]8t!]!^/^!^!a7S!a#S8t#S#T;{#T#s8t#s$f5u$f;'S8t;'S;=`>V<%l?Ah8t?Ah?BY5u?BY?Mn8t?MnO5u!Z5zblWOX5uXZ7SZ[5u[^7S^p5uqr5urs7Sst+Ptw5uwx7Sx!]5u!]!^7w!^!a7S!a#S5u#S#T7S#T;'S5u;'S;=`8n<%lO5u!R7VVOp7Sqs7St!]7S!]!^7l!^;'S7S;'S;=`7q<%lO7S!R7qOb!R!R7tP;=`<%l7S!Z8OYlWb!ROX+PZ[+P^p+Pqr+Psw+Px!^+P!a#S+P#T;'S+P;'S;=`+t<%lO+P!Z8qP;=`<%l5u!_8{iiSlWOX5uXZ7SZ[5u[^7S^p5uqr8trs7Sst/^tw8twx7Sx!P8t!P!Q5u!Q!]8t!]!^:j!^!a7S!a#S8t#S#T;{#T#s8t#s$f5u$f;'S8t;'S;=`>V<%l?Ah8t?Ah?BY5u?BY?Mn8t?MnO5u!_:sbiSlWb!ROX+PZ[+P^p+Pqr/^sw/^x!P/^!P!Q+P!Q!^/^!a#S/^#S#T0m#T#s/^#s$f+P$f;'S/^;'S;=`1e<%l?Ah/^?Ah?BY+P?BY?Mn/^?MnO+P!V<QciSOp7Sqr;{rs7Sst0mtw;{wx7Sx!P;{!P!Q7S!Q!];{!]!^=]!^!a7S!a#s;{#s$f7S$f;'S;{;'S;=`>P<%l?Ah;{?Ah?BY7S?BY?Mn;{?MnO7S!V=dXiSb!Rqr0msw0mx!P0m!Q!^0m!a#s0m$f;'S0m;'S;=`1_<%l?Ah0m?BY?Mn0m!V>SP;=`<%l;{!_>YP;=`<%l8t!_>dhiSlWOX@OXZAYZ[@O[^AY^p@OqrBwrsAYswBwwxAYx!PBw!P!Q@O!Q!]Bw!]!^/^!^!aAY!a#SBw#S#TE{#T#sBw#s$f@O$f;'SBw;'S;=`HS<%l?AhBw?Ah?BY@O?BY?MnBw?MnO@O!Z@TalWOX@OXZAYZ[@O[^AY^p@Oqr@OrsAYsw@OwxAYx!]@O!]!^Az!^!aAY!a#S@O#S#TAY#T;'S@O;'S;=`Bq<%lO@O!RA]UOpAYq!]AY!]!^Ao!^;'SAY;'S;=`At<%lOAY!RAtOc!R!RAwP;=`<%lAY!ZBRYlWc!ROX+PZ[+P^p+Pqr+Psw+Px!^+P!a#S+P#T;'S+P;'S;=`+t<%lO+P!ZBtP;=`<%l@O!_COhiSlWOX@OXZAYZ[@O[^AY^p@OqrBwrsAYswBwwxAYx!PBw!P!Q@O!Q!]Bw!]!^Dj!^!aAY!a#SBw#S#TE{#T#sBw#s$f@O$f;'SBw;'S;=`HS<%l?AhBw?Ah?BY@O?BY?MnBw?MnO@O!_DsbiSlWc!ROX+PZ[+P^p+Pqr/^sw/^x!P/^!P!Q+P!Q!^/^!a#S/^#S#T0m#T#s/^#s$f+P$f;'S/^;'S;=`1e<%l?Ah/^?Ah?BY+P?BY?Mn/^?MnO+P!VFQbiSOpAYqrE{rsAYswE{wxAYx!PE{!P!QAY!Q!]E{!]!^GY!^!aAY!a#sE{#s$fAY$f;'SE{;'S;=`G|<%l?AhE{?Ah?BYAY?BY?MnE{?MnOAY!VGaXiSc!Rqr0msw0mx!P0m!Q!^0m!a#s0m$f;'S0m;'S;=`1_<%l?Ah0m?BY?Mn0m!VHPP;=`<%lE{!_HVP;=`<%lBw!ZHcW!cxaP!b`Or(trs'ksv(tw!^(t!^!_)e!_;'S(t;'S;=`*P<%lO(t!aIYliSaPlW!b`!dpOX$qXZ&XZ[$q[^&X^p$qpq&Xqr-_rs&}sv-_vw/^wx(tx}-_}!OKQ!O!P-_!P!Q$q!Q!^-_!^!_*V!_!a&X!a#S-_#S#T1k#T#s-_#s$f$q$f;'S-_;'S;=`3X<%l?Ah-_?Ah?BY$q?BY?Mn-_?MnO$q!aK_kiSaPlW!b`!dpOX$qXZ&XZ[$q[^&X^p$qpq&Xqr-_rs&}sv-_vw/^wx(tx!P-_!P!Q$q!Q!^-_!^!_*V!_!`&X!`!aMS!a#S-_#S#T1k#T#s-_#s$f$q$f;'S-_;'S;=`3X<%l?Ah-_?Ah?BY$q?BY?Mn-_?MnO$q!TM_XaP!b`!dp!fQOr&Xrs&}sv&Xwx(tx!^&X!^!_*V!_;'S&X;'S;=`*y<%lO&X!aNZ!ZiSgQaPlW!b`!dpOX$qXZ&XZ[$q[^&X^p$qpq&Xqr-_rs&}sv-_vw/^wx(tx}-_}!OMz!O!PMz!P!Q$q!Q![Mz![!]Mz!]!^-_!^!_*V!_!a&X!a!c-_!c!}Mz!}#R-_#R#SMz#S#T1k#T#oMz#o#s-_#s$f$q$f$}-_$}%OMz%O%W-_%W%oMz%o%p-_%p&aMz&a&b-_&b1pMz1p4UMz4U4dMz4d4e-_4e$ISMz$IS$I`-_$I`$IbMz$Ib$Je-_$Je$JgMz$Jg$Kh-_$Kh%#tMz%#t&/x-_&/x&EtMz&Et&FV-_&FV;'SMz;'S;:j!#|;:j;=`3X<%l?&r-_?&r?AhMz?Ah?BY$q?BY?MnMz?MnO$q!a!$PP;=`<%lMz!R!$ZY!b`!dpOq*Vqr!$yrs(Vsv*Vwx)ex!a*V!a!b!4t!b;'S*V;'S;=`*s<%lO*V!R!%Q]!b`!dpOr*Vrs(Vsv*Vwx)ex}*V}!O!%y!O!f*V!f!g!']!g#W*V#W#X!0`#X;'S*V;'S;=`*s<%lO*V!R!&QX!b`!dpOr*Vrs(Vsv*Vwx)ex}*V}!O!&m!O;'S*V;'S;=`*s<%lO*V!R!&vV!b`!dp!ePOr*Vrs(Vsv*Vwx)ex;'S*V;'S;=`*s<%lO*V!R!'dX!b`!dpOr*Vrs(Vsv*Vwx)ex!q*V!q!r!(P!r;'S*V;'S;=`*s<%lO*V!R!(WX!b`!dpOr*Vrs(Vsv*Vwx)ex!e*V!e!f!(s!f;'S*V;'S;=`*s<%lO*V!R!(zX!b`!dpOr*Vrs(Vsv*Vwx)ex!v*V!v!w!)g!w;'S*V;'S;=`*s<%lO*V!R!)nX!b`!dpOr*Vrs(Vsv*Vwx)ex!{*V!{!|!*Z!|;'S*V;'S;=`*s<%lO*V!R!*bX!b`!dpOr*Vrs(Vsv*Vwx)ex!r*V!r!s!*}!s;'S*V;'S;=`*s<%lO*V!R!+UX!b`!dpOr*Vrs(Vsv*Vwx)ex!g*V!g!h!+q!h;'S*V;'S;=`*s<%lO*V!R!+xY!b`!dpOr!+qrs!,hsv!+qvw!-Swx!.[x!`!+q!`!a!/j!a;'S!+q;'S;=`!0Y<%lO!+qq!,mV!dpOv!,hvx!-Sx!`!,h!`!a!-q!a;'S!,h;'S;=`!.U<%lO!,hP!-VTO!`!-S!`!a!-f!a;'S!-S;'S;=`!-k<%lO!-SP!-kO|PP!-nP;=`<%l!-Sq!-xS!dp|POv(Vx;'S(V;'S;=`(h<%lO(Vq!.XP;=`<%l!,ha!.aX!b`Or!.[rs!-Ssv!.[vw!-Sw!`!.[!`!a!.|!a;'S!.[;'S;=`!/d<%lO!.[a!/TT!b`|POr)esv)ew;'S)e;'S;=`)y<%lO)ea!/gP;=`<%l!.[!R!/sV!b`!dp|POr*Vrs(Vsv*Vwx)ex;'S*V;'S;=`*s<%lO*V!R!0]P;=`<%l!+q!R!0gX!b`!dpOr*Vrs(Vsv*Vwx)ex#c*V#c#d!1S#d;'S*V;'S;=`*s<%lO*V!R!1ZX!b`!dpOr*Vrs(Vsv*Vwx)ex#V*V#V#W!1v#W;'S*V;'S;=`*s<%lO*V!R!1}X!b`!dpOr*Vrs(Vsv*Vwx)ex#h*V#h#i!2j#i;'S*V;'S;=`*s<%lO*V!R!2qX!b`!dpOr*Vrs(Vsv*Vwx)ex#m*V#m#n!3^#n;'S*V;'S;=`*s<%lO*V!R!3eX!b`!dpOr*Vrs(Vsv*Vwx)ex#d*V#d#e!4Q#e;'S*V;'S;=`*s<%lO*V!R!4XX!b`!dpOr*Vrs(Vsv*Vwx)ex#X*V#X#Y!+q#Y;'S*V;'S;=`*s<%lO*V!R!4{Y!b`!dpOr!4trs!5ksv!4tvw!6Vwx!8]x!a!4t!a!b!:]!b;'S!4t;'S;=`!;r<%lO!4tq!5pV!dpOv!5kvx!6Vx!a!5k!a!b!7W!b;'S!5k;'S;=`!8V<%lO!5kP!6YTO!a!6V!a!b!6i!b;'S!6V;'S;=`!7Q<%lO!6VP!6lTO!`!6V!`!a!6{!a;'S!6V;'S;=`!7Q<%lO!6VP!7QOyPP!7TP;=`<%l!6Vq!7]V!dpOv!5kvx!6Vx!`!5k!`!a!7r!a;'S!5k;'S;=`!8V<%lO!5kq!7yS!dpyPOv(Vx;'S(V;'S;=`(h<%lO(Vq!8YP;=`<%l!5ka!8bX!b`Or!8]rs!6Vsv!8]vw!6Vw!a!8]!a!b!8}!b;'S!8];'S;=`!:V<%lO!8]a!9SX!b`Or!8]rs!6Vsv!8]vw!6Vw!`!8]!`!a!9o!a;'S!8];'S;=`!:V<%lO!8]a!9vT!b`yPOr)esv)ew;'S)e;'S;=`)y<%lO)ea!:YP;=`<%l!8]!R!:dY!b`!dpOr!4trs!5ksv!4tvw!6Vwx!8]x!`!4t!`!a!;S!a;'S!4t;'S;=`!;r<%lO!4t!R!;]V!b`!dpyPOr*Vrs(Vsv*Vwx)ex;'S*V;'S;=`*s<%lO*V!R!;uP;=`<%l!4t!V!<TXjSaP!b`!dpOr&Xrs&}sv&Xwx(tx!^&X!^!_*V!_;'S&X;'S;=`*y<%lO&X\",tokenizers:[Gf,Nf,Uf,jf,Df,Bf,0,1,2,3,4,5],topRules:{Document:[0,16]},dialects:{noMatch:0,selfClosing:515},tokenPrec:517});function Kf(t,e){let i=Object.create(null);for(let n of t.getChildren(24)){let t=n.getChild(25),r=n.getChild(27)||n.getChild(28);t&&(i[e.read(t.from,t.to)]=r?27==r.type.id?e.read(r.from+1,r.to-1):e.read(r.from,r.to):\"\")}return i}function Jf(t,e){let i=t.getChild(23);return i?e.read(i.from,i.to):\" \"}function tO(t,e,i){let n;for(let r of i)if(!r.attrs||r.attrs(n||(n=Kf(t.node.parent.firstChild,e))))return{parser:r.parser,bracketed:!0};return null}function eO(t=[],e=[]){let i=[],n=[],r=[],s=[];for(let a of t){(\"script\"==a.tag?i:\"style\"==a.tag?n:\"textarea\"==a.tag?r:s).push(a)}let o=e.length?Object.create(null):null;for(let a of e)(o[a.name]||(o[a.name]=[])).push(a);return _a((t,e)=>{let a=t.type.id;if(29==a)return tO(t,e,i);if(32==a)return tO(t,e,n);if(35==a)return tO(t,e,r);if(21==a&&s.length){let i,n=t.node,r=n.firstChild,o=r&&Jf(r,e);if(o)for(let t of s)if(t.tag==o&&(!t.attrs||t.attrs(i||(i=Kf(r,e))))){let e=n.lastChild,i=38==e.type.id?e.from:n.to;if(i>r.to)return{parser:t.parser,overlay:[{from:r.to,to:i}]}}}if(o&&24==a){let i,n=t.node;if(i=n.firstChild){let t=o[e.read(i.from,i.to)];if(t)for(let i of t){if(i.tagName&&i.tagName!=Jf(n.parent,e))continue;let t=n.lastChild;if(27==t.type.id){let e=t.from+1,n=t.lastChild,r=t.to-(n&&n.isError?0:1);if(r>e)return{parser:i.parser,overlay:[{from:e,to:r}],bracketed:!0}}else if(28==t.type.id)return{parser:i.parser,overlay:[{from:t.from,to:t.to}]}}}}return null})}const iO=[9,10,11,12,13,32,133,160,5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8232,8233,8239,8287,12288];function nO(t){return t>=65&&t<=90||t>=97&&t<=122||t>=161}function rO(t){return t>=48&&t<=57}function sO(t){return rO(t)||t>=97&&t<=102||t>=65&&t<=70}const oO=(t,e,i)=>(n,r)=>{for(let s=!1,o=0,a=0;;a++){let{next:l}=n;if(nO(l)||45==l||95==l||s&&rO(l))!s&&(45!=l||a>0)&&(s=!0),o===a&&45==l&&o++,n.advance();else{if(92!=l||10==n.peek(1)){s&&n.acceptToken(2==o&&r.canShift(2)?e:40==l?i:t);break}if(n.advance(),sO(n.next)){do{n.advance()}while(sO(n.next));32==n.next&&n.advance()}else n.next>-1&&n.advance();s=!0}}},aO=new ff(oO(123,2,124)),lO=new ff(oO(125,3,4)),hO=new ff(t=>{if(iO.includes(t.peek(-1))){let{next:e}=t;(nO(e)||95==e||35==e||46==e||42==e||91==e||58==e&&nO(t.peek(1))||45==e||38==e)&&t.acceptToken(122)}}),cO=new ff(t=>{if(!iO.includes(t.peek(-1))){let{next:e}=t;if(37==e&&(t.advance(),t.acceptToken(1)),nO(e)){do{t.advance()}while(nO(t.next)||rO(t.next));t.acceptToken(1)}}}),uO=tl({\"AtKeyword import charset namespace keyframes media supports\":wl.definitionKeyword,\"from to selector\":wl.keyword,NamespaceName:wl.namespace,KeyframeName:wl.labelName,KeyframeRangeName:wl.operatorKeyword,TagName:wl.tagName,ClassName:wl.className,PseudoClassName:wl.constant(wl.className),IdName:wl.labelName,\"FeatureName PropertyName\":wl.propertyName,AttributeName:wl.attributeName,NumberLiteral:wl.number,KeywordQuery:wl.keyword,UnaryQueryOp:wl.operatorKeyword,\"CallTag ValueName\":wl.atom,VariableName:wl.variableName,Callee:wl.operatorKeyword,Unit:wl.unit,\"UniversalSelector NestingSelector\":wl.definitionOperator,\"MatchOp CompareOp\":wl.compareOperator,\"ChildOp SiblingOp, LogicOp\":wl.logicOperator,BinOp:wl.arithmeticOperator,Important:wl.modifier,Comment:wl.blockComment,ColorLiteral:wl.color,\"ParenthesizedContent StringLiteral\":wl.string,\":\":wl.punctuation,\"PseudoOp #\":wl.derefOperator,\"; ,\":wl.separator,\"( )\":wl.paren,\"[ ]\":wl.squareBracket,\"{ }\":wl.brace}),dO={__proto__:null,lang:38,\"nth-child\":38,\"nth-last-child\":38,\"nth-of-type\":38,\"nth-last-of-type\":38,dir:38,\"host-context\":38,if:84,url:124,\"url-prefix\":124,domain:124,regexp:124},fO={__proto__:null,or:98,and:98,not:106,only:106,layer:170},OO={__proto__:null,selector:112,layer:166},pO={__proto__:null,\"@import\":162,\"@media\":174,\"@charset\":178,\"@namespace\":182,\"@keyframes\":188,\"@supports\":200,\"@scope\":204},mO={__proto__:null,to:207},gO=Pf.deserialize({version:14,states:\"EbQYQdOOO#qQdOOP#xO`OOOOQP'#Cf'#CfOOQP'#Ce'#CeO#}QdO'#ChO$nQaO'#CcO$xQdO'#CkO%TQdO'#DpO%YQdO'#DrO%_QdO'#DuO%_QdO'#DxOOQP'#FV'#FVO&eQhO'#EhOOQS'#FU'#FUOOQS'#Ek'#EkQYQdOOO&lQdO'#EOO&PQhO'#EUO&lQdO'#EWO'aQdO'#EYO'lQdO'#E]O'tQhO'#EcO(VQdO'#EeO(bQaO'#CfO)VQ`O'#D{O)[Q`O'#F`O)gQdO'#F`QOQ`OOP)qO&jO'#CaPOOO)C@t)C@tOOQP'#Cj'#CjOOQP,59S,59SO#}QdO,59SO)|QdO,59VO%TQdO,5:[O%YQdO,5:^O%_QdO,5:aO%_QdO,5:cO%_QdO,5:dO%_QdO'#ErO*XQ`O,58}O*aQdO'#DzOOQS,58},58}OOQP'#Cn'#CnOOQO'#Dn'#DnOOQP,59V,59VO*hQ`O,59VO*mQ`O,59VOOQP'#Dq'#DqOOQP,5:[,5:[OOQO'#Ds'#DsO*rQpO,5:^O+]QaO,5:aO+sQaO,5:dOOQW'#DZ'#DZO,ZQhO'#DdO,xQhO'#FaO'tQhO'#DbO-WQ`O'#DhOOQW'#F['#F[O-]Q`O,5;SO-eQ`O'#DeOOQS-E8i-E8iOOQ['#Cs'#CsO-jQdO'#CtO.QQdO'#CzO.hQdO'#C}O/OQ!pO'#DPO1RQ!jO,5:jOOQO'#DU'#DUO*mQ`O'#DTO1cQ!nO'#FXO3`Q`O'#DVO3eQ`O'#DkOOQ['#FX'#FXO-`Q`O,5:pO3jQ!bO,5:rOOQS'#E['#E[O3rQ`O,5:tO3wQdO,5:tOOQO'#E_'#E_O4PQ`O,5:wO4UQhO,5:}O%_QdO'#DgOOQS,5;P,5;PO-eQ`O,5;PO4^QdO,5;PO4fQdO,5:gO4vQdO'#EtO5TQ`O,5;zO5TQ`O,5;zPOOO'#Ej'#EjP5`O&jO,58{POOO,58{,58{OOQP1G.n1G.nOOQP1G.q1G.qO*hQ`O1G.qO*mQ`O1G.qOOQP1G/v1G/vO5kQpO1G/xO5sQaO1G/{O6ZQaO1G/}O6qQaO1G0OO7XQaO,5;^OOQO-E8p-E8pOOQS1G.i1G.iO7cQ`O,5:fO7hQdO'#DoO7oQdO'#CrOOQP1G/x1G/xO&lQdO1G/xO7vQ!jO'#DZO8UQ!bO,59vO8^QhO,5:OOOQO'#F]'#F]O8XQ!bO,59zO'tQhO,59xO8fQhO'#EvO8sQ`O,5;{O9OQhO,59|O9uQhO'#DiOOQW,5:S,5:SOOQS1G0n1G0nOOQW,5:P,5:PO9|Q!fO'#FYOOQS'#FY'#FYOOQS'#Em'#EmO;^QdO,59`OOQ[,59`,59`O;tQdO,59fOOQ[,59f,59fO<[QdO,59iOOQ[,59i,59iOOQ[,59k,59kO&lQdO,59mO<rQhO'#EQOOQW'#EQ'#EQO=WQ`O1G0UO1[QhO1G0UOOQ[,59o,59oO'tQhO'#DXOOQ[,59q,59qO=]Q#tO,5:VOOQS1G0[1G0[OOQS1G0^1G0^OOQS1G0`1G0`O=hQ`O1G0`O=mQdO'#E`OOQS1G0c1G0cOOQS1G0i1G0iO=xQaO,5:RO-`Q`O1G0kOOQS1G0k1G0kO-eQ`O1G0kO>PQ!fO1G0ROOQO1G0R1G0ROOQO,5;`,5;`O>gQdO,5;`OOQO-E8r-E8rO>tQ`O1G1fPOOO-E8h-E8hPOOO1G.g1G.gOOQP7+$]7+$]OOQP7+%d7+%dO&lQdO7+%dOOQS1G0Q1G0QO?PQaO'#F_O?ZQ`O,5:ZO?`Q!fO'#ElO@^QdO'#FWO@hQ`O,59^O@mQ!bO7+%dO&lQdO1G/bO@uQhO1G/fOOQW1G/j1G/jOOQW1G/d1G/dOAWQhO,5;bOOQO-E8t-E8tOAfQhO'#DZOAtQhO'#F^OBPQ`O'#F^OBUQ`O,5:TOOQS-E8k-E8kOOQ[1G.z1G.zOOQ[1G/Q1G/QOOQ[1G/T1G/TOOQ[1G/X1G/XOBZQdO,5:lOOQS7+%p7+%pOB`Q`O7+%pOBeQhO'#DYOBmQ`O,59sO'tQhO,59sOOQ[1G/q1G/qOBuQ`O1G/qOOQS7+%z7+%zOBzQbO'#DPOOQO'#Eb'#EbOCYQ`O'#EaOOQO'#Ea'#EaOCeQ`O'#EwOCmQdO,5:zOOQS,5:z,5:zOOQ[1G/m1G/mOOQS7+&V7+&VO-`Q`O7+&VOCxQ!fO'#EsO&lQdO'#EsOEPQdO7+%mOOQO7+%m7+%mOOQO1G0z1G0zOEdQ!bO<<IOOElQdO'#EqOEvQ`O,5;yOOQP1G/u1G/uOOQS-E8j-E8jOFOQdO'#EpOFYQ`O,5;rOOQ]1G.x1G.xOOQP<<IO<<IOOFbQdO7+$|OOQO'#D]'#D]OFiQ!bO7+%QOFqQhO'#EoOF{Q`O,5;xO&lQdO,5;xOOQW1G/o1G/oOOQO'#ES'#ESOGTQ`O1G0WOOQS<<I[<<I[O&lQdO,59tOGnQhO1G/_OOQ[1G/_1G/_OGuQ`O1G/_OOQW-E8l-E8lOOQ[7+%]7+%]OOQO,5:{,5:{O=pQdO'#ExOCeQ`O,5;cOOQS,5;c,5;cOOQS-E8u-E8uOOQS1G0f1G0fOOQS<<Iq<<IqOG}Q!fO,5;_OOQS-E8q-E8qOOQO<<IX<<IXOOQPAN>jAN>jOIUQaO,5;]OOQO-E8o-E8oOI`QdO,5;[OOQO-E8n-E8nOOQW<<Hh<<HhOOQW<<Hl<<HlOIjQhO<<HlOI{QhO,5;ZOJWQ`O,5;ZOOQO-E8m-E8mOJ]QdO1G1dOBZQdO'#EuOJgQ`O7+%rOOQW7+%r7+%rOJoQ!bO1G/`OOQ[7+$y7+$yOJzQhO7+$yPKRQ`O'#EnOOQO,5;d,5;dOOQO-E8v-E8vOOQS1G0}1G0}OKWQ`OAN>WO&lQdO1G0uOK]Q`O7+'OOOQO,5;a,5;aOOQO-E8s-E8sOOQW<<I^<<I^OOQ[<<He<<HePOQW,5;Y,5;YOOQWG23rG23rOKeQdO7+&a\",stateData:\"Kx~O#sOS#tQQ~OW[OZ[O]TO`VOaVOi]OjWOmXO!jYO!mZO!saO!ybO!{cO!}dO#QeO#WfO#YgO#oRO~OQiOW[OZ[O]TO`VOaVOi]OjWOmXO!jYO!mZO!saO!ybO!{cO!}dO#QeO#WfO#YgO#ohO~O#m$SP~P!dO#tmO~O#ooO~O]qO`rOarOjsOmtO!juO!mwO#nvO~OpzO!^xO~P$SOc!QO#o|O#p}O~O#o!RO~O#o!TO~OW[OZ[O]TO`VOaVOjWOmXO!jYO!mZO#oRO~OS!]Oe!YO!V![O!Y!`O#q!XOp$TP~Ok$TP~P&POQ!jOe!cOm!dOp!eOr!mOt!mOz!kO!`!lO#o!bO#p!hO#}!fO~Ot!qO!`!lO#o!pO~Ot!sO#o!sO~OS!]Oe!YO!V![O!Y!`O#q!XO~Oe!vOpzO#Z!xO~O]YX`YX`!pXaYXjYXmYXpYX!^YX!jYX!mYX#nYX~O`!zO~Ok!{O#m$SXo$SX~O#m$SXo$SX~P!dO#u#OO#v#OO#w#QO~Oc#UO#o|O#p}O~OpzO!^xO~Oo$SP~P!dOe#`O~Oe#aO~Ol#bO!h#cO~O]qO`rOarOjsOmtO~Op!ia!^!ia!j!ia!m!ia#n!iad!ia~P*zOp!la!^!la!j!la!m!la#n!lad!la~P*zOR#gOS!]Oe!YOr#gOt#gO!V![O!Y!`O#q#dO#}!fO~O!R#iO!^#jOk$TXp$TX~Oe#mO~Ok#oOpzO~Oe!vO~O]#rO`#rOd#uOi#rOj#rOk#rO~P&lO]#rO`#rOi#rOj#rOk#rOl#wO~P&lO]#rO`#rOi#rOj#rOk#rOo#yO~P&lOP#zOSsXesXksXvsX!VsX!YsX!usX!wsX#qsX!TsXQsX]sX`sXdsXisXjsXmsXpsXrsXtsXzsX!`sX#osX#psX#}sXlsXosX!^sX!qsX#msX~Ov#{O!u#|O!w#}Ok$TP~P'tOe#aOS#{Xk#{Xv#{X!V#{X!Y#{X!u#{X!w#{X#q#{XQ#{X]#{X`#{Xd#{Xi#{Xj#{Xm#{Xp#{Xr#{Xt#{Xz#{X!`#{X#o#{X#p#{X#}#{Xl#{Xo#{X!^#{X!q#{X#m#{X~Oe$RO~Oe$TO~Ok$VOv#{O~Ok$WO~Ot$XO!`!lO~Op$YO~OpzO!R#iO~OpzO#Z$`O~O!q$bOk!oa#m!oao!oa~P&lOk#hX#m#hXo#hX~P!dOk!{O#m$Sao$Sa~O#u#OO#v#OO#w$hO~Ol$jO!h$kO~Op!ii!^!ii!j!ii!m!ii#n!iid!ii~P*zOp!ki!^!ki!j!ki!m!ki#n!kid!ki~P*zOp!li!^!li!j!li!m!li#n!lid!li~P*zOp#fa!^#fa~P$SOo$lO~Od$RP~P%_Od#zP~P&lO`!PXd}X!R}X!T!PX~O`$sO!T$tO~Od$uO!R#iO~Ok#jXp#jX!^#jX~P'tO!^#jOk$Tap$Ta~O!R#iOk!Uap!Ua!^!Uad!Ua`!Ua~OS!]Oe!YO!V![O!Y!`O#q$yO~Od$QP~P9dOv#{OQ#|X]#|X`#|Xd#|Xe#|Xi#|Xj#|Xk#|Xm#|Xp#|Xr#|Xt#|Xz#|X!`#|X#o#|X#p#|X#}#|Xl#|Xo#|X~O]#rO`#rOd%OOi#rOj#rOk#rO~P&lO]#rO`#rOi#rOj#rOk#rOl%PO~P&lO]#rO`#rOi#rOj#rOk#rOo%QO~P&lOe%SOS!tXk!tX!V!tX!Y!tX#q!tX~Ok%TO~Od%YOt%ZO!a%ZO~Ok%[O~Oo%cO#o%^O#}%]O~Od%dO~P$SOv#{O!^%hO!q%jOk!oi#m!oio!oi~P&lOk#ha#m#hao#ha~P!dOk!{O#m$Sio$Si~O!^%mOd$RX~P$SOd%oO~Ov#{OQ#`Xd#`Xe#`Xm#`Xp#`Xr#`Xt#`Xz#`X!^#`X!`#`X#o#`X#p#`X#}#`X~O!^%qOd#zX~P&lOd%sO~Ol%tOv#{O~OR#gOr#gOt#gO#q%vO#}!fO~O!R#iOk#jap#ja!^#ja~O`!PXd}X!R}X!^}X~O!R#iO!^%xOd$QX~O`%zO~Od%{O~O#o%|O~Ok&OO~O`&PO!R#iO~Od&ROk&QO~Od&UO~OP#zOpsX!^sXdsX~O#}%]Op#TX!^#TX~OpzO!^&WO~Oo&[O#o%^O#}%]O~Ov#{OQ#gXe#gXk#gXm#gXp#gXr#gXt#gXz#gX!^#gX!`#gX!q#gX#m#gX#o#gX#p#gX#}#gXo#gX~O!^%hO!q&`Ok!oq#m!oqo!oq~P&lOl&aOv#{O~Od#eX!^#eX~P%_O!^%mOd$Ra~Od#dX!^#dX~P&lO!^%qOd#za~Od&fO~P&lOd&gO!T&hO~Od#cX!^#cX~P9dO!^%xOd$Qa~O]&mOd&oO~OS#bae#ba!V#ba!Y#ba#q#ba~Od&qO~PG]Od&qOk&rO~Ov#{OQ#gae#gak#gam#gap#gar#gat#gaz#ga!^#ga!`#ga!q#ga#m#ga#o#ga#p#ga#}#gao#ga~Od#ea!^#ea~P$SOd#da!^#da~P&lOR#gOr#gOt#gO#q%vO#}%]O~O!R#iOd#ca!^#ca~O`&xO~O!^%xOd$Qi~P&lO]&mOd&|O~Ov#{Od|ik|i~Od&}O~PG]Ok'OO~Od'PO~O!^%xOd$Qq~Od#cq!^#cq~P&lO#s!a#t#}]#}v!m~\",goto:\"2h$UPPPPP$VP$YP$c$uP$cP%X$cPP%_PPP%e%o%oPPPPP%oPP%oP&]P%oP%o'W%oP't'w'}'}(^'}P'}P'}P'}'}P(m'}(yP(|PP)p)v$c)|$c*SP$cP$c$cP*Y*{+YP$YP+aP+dP$YP$YP$YP+j$YP+m+p+s+z$YP$YPP$YP,P,V,f,|-[-b-l-r-x.O.U.`.f.l.rPPPPPPPPPPP.x/R/w/z0|P1U1u2O2R2U2[RnQ_^OP`kz!{$dq[OPYZ`kuvwxz!v!{#`$d%mqSOPYZ`kuvwxz!v!{#`$d%mQpTR#RqQ!OVR#SrQ#S!QS$Q!i!jR$i#U!V!mac!c!d!e!z#a#c#t#v#x#{$a$k$p$s%h%i%q%u%z&P&d&l&x'Q!U!mac!c!d!e!z#a#c#t#v#x#{$a$k$p$s%h%i%q%u%z&P&d&l&x'QU#g!Y$t&hU%`$Y%b&WR&V%_!V!iac!c!d!e!z#a#c#t#v#x#{$a$k$p$s%h%i%q%u%z&P&d&l&x'QR$S!kQ%W$RR&S%Xk!^]bf!Y![!g#i#j#m$P$R%X%xQ#e!YQ${#mQ%w$tQ&j%xR&w&hQ!ygQ#p!`Q$^!xR%f$`R#n!]!U!mac!c!d!e!z#a#c#t#v#x#{$a$k$p$s%h%i%q%u%z&P&d&l&x'QQ!qdR$X!rQ!PVR#TrQ#S!PR$i#TQ!SWR#VsQ!UXR#WtQ{UQ!wgQ#^yQ#o!_Q$U!nQ$[!uQ$_!yQ%e$^Q&Y%aQ&]%fR&v&XSjPzQ!}kQ$c!{R%k$dZiPkz!{$dR$P!gQ%}%SR&z&mR!rdR!teR$Z!tS%a$Y%bR&t&WV%_$Y%b&WQ#PmR$g#PQ`OSkPzU!a`k$dR$d!{Q$p#aY%p$p%u&d&l'QQ%u$sQ&d%qQ&l%zR'Q&xQ#t!cQ#v!dQ#x!eV$}#t#v#xQ%X$RR&T%XQ%y$zS&k%y&yR&y&lQ%r$pR&e%rQ%n$mR&c%nQyUR#]yQ%i$aR&_%iQ!|jS$e!|$fR$f!}Q&n%}R&{&nQ#k!ZR$x#kQ%b$YR&Z%bQ&X%aR&u&X__OP`kz!{$d^UOP`kz!{$dQ!VYQ!WZQ#XuQ#YvQ#ZwQ#[xQ$]!vQ$m#`R&b%mR$q#aQ!gaQ!oc[#q!c!d!e#t#v#xQ$a!zd$o#a$p$s%q%u%z&d&l&x'QQ$r#cQ%R#{S%g$a%iQ%l$kQ&^%hR&p&P]#s!c!d!e#t#v#xW!Z]b!g$PQ!ufQ#f!YQ#l![Q$v#iQ$w#jQ$z#mS%V$R%XR&i%xQ#h!YQ%w$tR&w&hR$|#mR$n#`QlPR#_zQ!_]Q!nbQ$O!gR%U$P\",nodeNames:\"⚠ Unit VariableName VariableName QueryCallee Comment StyleSheet RuleSet UniversalSelector TagSelector TagName NestingSelector ClassSelector . ClassName PseudoClassSelector : :: PseudoClassName PseudoClassName ) ( ArgList ValueName ParenthesizedValue AtKeyword # ; ] [ BracketedValue } { BracedValue ColorLiteral NumberLiteral StringLiteral BinaryExpression BinOp CallExpression Callee IfExpression if ArgList IfBranch KeywordQuery FeatureQuery FeatureName BinaryQuery LogicOp ComparisonQuery CompareOp UnaryQuery UnaryQueryOp ParenthesizedQuery SelectorQuery selector ParenthesizedSelector CallQuery ArgList , CallLiteral CallTag ParenthesizedContent PseudoClassName ArgList IdSelector IdName AttributeSelector AttributeName MatchOp ChildSelector ChildOp DescendantSelector SiblingSelector SiblingOp Block Declaration PropertyName Important ImportStatement import Layer layer LayerName layer MediaStatement media CharsetStatement charset NamespaceStatement namespace NamespaceName KeyframesStatement keyframes KeyframeName KeyframeList KeyframeSelector KeyframeRangeName SupportsStatement supports ScopeStatement scope to AtRule Styles\",maxTerm:143,nodeProps:[[\"isolate\",-2,5,36,\"\"],[\"openedBy\",20,\"(\",28,\"[\",31,\"{\"],[\"closedBy\",21,\")\",29,\"]\",32,\"}\"]],propSources:[uO],skippedNodes:[0,5,106],repeatNodeCount:15,tokenData:\"JQ~R!YOX$qX^%i^p$qpq%iqr({rs-ust/itu6Wuv$qvw7Qwx7cxy9Qyz9cz{9h{|:R|}>t}!O?V!O!P?t!P!Q@]!Q![AU![!]BP!]!^B{!^!_C^!_!`DY!`!aDm!a!b$q!b!cEn!c!}$q!}#OG{#O#P$q#P#QH^#Q#R6W#R#o$q#o#pHo#p#q6W#q#rIQ#r#sIc#s#y$q#y#z%i#z$f$q$f$g%i$g#BY$q#BY#BZ%i#BZ$IS$q$IS$I_%i$I_$I|$q$I|$JO%i$JO$JT$q$JT$JU%i$JU$KV$q$KV$KW%i$KW&FU$q&FU&FV%i&FV;'S$q;'S;=`Iz<%lO$q`$tSOy%Qz;'S%Q;'S;=`%c<%lO%Q`%VS!a`Oy%Qz;'S%Q;'S;=`%c<%lO%Q`%fP;=`<%l%Q~%nh#s~OX%QX^'Y^p%Qpq'Yqy%Qz#y%Q#y#z'Y#z$f%Q$f$g'Y$g#BY%Q#BY#BZ'Y#BZ$IS%Q$IS$I_'Y$I_$I|%Q$I|$JO'Y$JO$JT%Q$JT$JU'Y$JU$KV%Q$KV$KW'Y$KW&FU%Q&FU&FV'Y&FV;'S%Q;'S;=`%c<%lO%Q~'ah#s~!a`OX%QX^'Y^p%Qpq'Yqy%Qz#y%Q#y#z'Y#z$f%Q$f$g'Y$g#BY%Q#BY#BZ'Y#BZ$IS%Q$IS$I_'Y$I_$I|%Q$I|$JO'Y$JO$JT%Q$JT$JU'Y$JU$KV%Q$KV$KW'Y$KW&FU%Q&FU&FV'Y&FV;'S%Q;'S;=`%c<%lO%Qj)OUOy%Qz#]%Q#]#^)b#^;'S%Q;'S;=`%c<%lO%Qj)gU!a`Oy%Qz#a%Q#a#b)y#b;'S%Q;'S;=`%c<%lO%Qj*OU!a`Oy%Qz#d%Q#d#e*b#e;'S%Q;'S;=`%c<%lO%Qj*gU!a`Oy%Qz#c%Q#c#d*y#d;'S%Q;'S;=`%c<%lO%Qj+OU!a`Oy%Qz#f%Q#f#g+b#g;'S%Q;'S;=`%c<%lO%Qj+gU!a`Oy%Qz#h%Q#h#i+y#i;'S%Q;'S;=`%c<%lO%Qj,OU!a`Oy%Qz#T%Q#T#U,b#U;'S%Q;'S;=`%c<%lO%Qj,gU!a`Oy%Qz#b%Q#b#c,y#c;'S%Q;'S;=`%c<%lO%Qj-OU!a`Oy%Qz#h%Q#h#i-b#i;'S%Q;'S;=`%c<%lO%Qj-iS!qY!a`Oy%Qz;'S%Q;'S;=`%c<%lO%Q~-xWOY-uZr-urs.bs#O-u#O#P.g#P;'S-u;'S;=`/c<%lO-u~.gOt~~.jRO;'S-u;'S;=`.s;=`O-u~.vXOY-uZr-urs.bs#O-u#O#P.g#P;'S-u;'S;=`/c;=`<%l-u<%lO-u~/fP;=`<%l-uj/nYjYOy%Qz!Q%Q!Q![0^![!c%Q!c!i0^!i#T%Q#T#Z0^#Z;'S%Q;'S;=`%c<%lO%Qj0cY!a`Oy%Qz!Q%Q!Q![1R![!c%Q!c!i1R!i#T%Q#T#Z1R#Z;'S%Q;'S;=`%c<%lO%Qj1WY!a`Oy%Qz!Q%Q!Q![1v![!c%Q!c!i1v!i#T%Q#T#Z1v#Z;'S%Q;'S;=`%c<%lO%Qj1}YrY!a`Oy%Qz!Q%Q!Q![2m![!c%Q!c!i2m!i#T%Q#T#Z2m#Z;'S%Q;'S;=`%c<%lO%Qj2tYrY!a`Oy%Qz!Q%Q!Q![3d![!c%Q!c!i3d!i#T%Q#T#Z3d#Z;'S%Q;'S;=`%c<%lO%Qj3iY!a`Oy%Qz!Q%Q!Q![4X![!c%Q!c!i4X!i#T%Q#T#Z4X#Z;'S%Q;'S;=`%c<%lO%Qj4`YrY!a`Oy%Qz!Q%Q!Q![5O![!c%Q!c!i5O!i#T%Q#T#Z5O#Z;'S%Q;'S;=`%c<%lO%Qj5TY!a`Oy%Qz!Q%Q!Q![5s![!c%Q!c!i5s!i#T%Q#T#Z5s#Z;'S%Q;'S;=`%c<%lO%Qj5zSrY!a`Oy%Qz;'S%Q;'S;=`%c<%lO%Qd6ZUOy%Qz!_%Q!_!`6m!`;'S%Q;'S;=`%c<%lO%Qd6tS!hS!a`Oy%Qz;'S%Q;'S;=`%c<%lO%Qb7VSZQOy%Qz;'S%Q;'S;=`%c<%lO%Q~7fWOY7cZw7cwx.bx#O7c#O#P8O#P;'S7c;'S;=`8z<%lO7c~8RRO;'S7c;'S;=`8[;=`O7c~8_XOY7cZw7cwx.bx#O7c#O#P8O#P;'S7c;'S;=`8z;=`<%l7c<%lO7c~8}P;=`<%l7cj9VSeYOy%Qz;'S%Q;'S;=`%c<%lO%Q~9hOd~n9oUWQvWOy%Qz!_%Q!_!`6m!`;'S%Q;'S;=`%c<%lO%Qj:YWvW!mQOy%Qz!O%Q!O!P:r!P!Q%Q!Q![=w![;'S%Q;'S;=`%c<%lO%Qj:wU!a`Oy%Qz!Q%Q!Q![;Z![;'S%Q;'S;=`%c<%lO%Qj;bY!a`#}YOy%Qz!Q%Q!Q![;Z![!g%Q!g!h<Q!h#X%Q#X#Y<Q#Y;'S%Q;'S;=`%c<%lO%Qj<VY!a`Oy%Qz{%Q{|<u|}%Q}!O<u!O!Q%Q!Q![=^![;'S%Q;'S;=`%c<%lO%Qj<zU!a`Oy%Qz!Q%Q!Q![=^![;'S%Q;'S;=`%c<%lO%Qj=eU!a`#}YOy%Qz!Q%Q!Q![=^![;'S%Q;'S;=`%c<%lO%Qj>O[!a`#}YOy%Qz!O%Q!O!P;Z!P!Q%Q!Q![=w![!g%Q!g!h<Q!h#X%Q#X#Y<Q#Y;'S%Q;'S;=`%c<%lO%Qj>yS!^YOy%Qz;'S%Q;'S;=`%c<%lO%Qj?[WvWOy%Qz!O%Q!O!P:r!P!Q%Q!Q![=w![;'S%Q;'S;=`%c<%lO%Qj?yU]YOy%Qz!Q%Q!Q![;Z![;'S%Q;'S;=`%c<%lO%Q~@bTvWOy%Qz{@q{;'S%Q;'S;=`%c<%lO%Q~@xS!a`#t~Oy%Qz;'S%Q;'S;=`%c<%lO%QjAZ[#}YOy%Qz!O%Q!O!P;Z!P!Q%Q!Q![=w![!g%Q!g!h<Q!h#X%Q#X#Y<Q#Y;'S%Q;'S;=`%c<%lO%QjBUU`YOy%Qz![%Q![!]Bh!];'S%Q;'S;=`%c<%lO%QbBoSaQ!a`Oy%Qz;'S%Q;'S;=`%c<%lO%QjCQSkYOy%Qz;'S%Q;'S;=`%c<%lO%QhCcU!TWOy%Qz!_%Q!_!`Cu!`;'S%Q;'S;=`%c<%lO%QhC|S!TW!a`Oy%Qz;'S%Q;'S;=`%c<%lO%QlDaS!TW!hSOy%Qz;'S%Q;'S;=`%c<%lO%QjDtV!jQ!TWOy%Qz!_%Q!_!`Cu!`!aEZ!a;'S%Q;'S;=`%c<%lO%QbEbS!jQ!a`Oy%Qz;'S%Q;'S;=`%c<%lO%QjEqYOy%Qz}%Q}!OFa!O!c%Q!c!}GO!}#T%Q#T#oGO#o;'S%Q;'S;=`%c<%lO%QjFfW!a`Oy%Qz!c%Q!c!}GO!}#T%Q#T#oGO#o;'S%Q;'S;=`%c<%lO%QjGV[iY!a`Oy%Qz}%Q}!OGO!O!Q%Q!Q![GO![!c%Q!c!}GO!}#T%Q#T#oGO#o;'S%Q;'S;=`%c<%lO%QjHQSmYOy%Qz;'S%Q;'S;=`%c<%lO%QnHcSl^Oy%Qz;'S%Q;'S;=`%c<%lO%QjHtSpYOy%Qz;'S%Q;'S;=`%c<%lO%QjIVSoYOy%Qz;'S%Q;'S;=`%c<%lO%QfIhU!mQOy%Qz!_%Q!_!`6m!`;'S%Q;'S;=`%c<%lO%Q`I}P;=`<%l$q\",tokenizers:[hO,cO,aO,lO,1,2,3,4,new df(\"m~RRYZ[z{a~~g~aO#v~~dP!P!Qg~lO#w~~\",28,129)],topRules:{StyleSheet:[0,6],Styles:[1,105]},specialized:[{term:124,get:t=>dO[t]||-1},{term:125,get:t=>fO[t]||-1},{term:4,get:t=>OO[t]||-1},{term:25,get:t=>pO[t]||-1},{term:123,get:t=>mO[t]||-1}],tokenPrec:1963});let QO=null;function bO(){if(!QO&&\"object\"==typeof document&&document.body){let{style:t}=document.body,e=[],i=new Set;for(let n in t)\"cssText\"!=n&&\"cssFloat\"!=n&&\"string\"==typeof t[n]&&(/[A-Z]/.test(n)&&(n=n.replace(/[A-Z]/g,t=>\"-\"+t.toLowerCase())),i.has(n)||(e.push(n),i.add(n)));QO=e.sort().map(t=>({type:\"property\",label:t,apply:t+\": \"}))}return QO||[]}const vO=[\"active\",\"after\",\"any-link\",\"autofill\",\"backdrop\",\"before\",\"checked\",\"cue\",\"default\",\"defined\",\"disabled\",\"empty\",\"enabled\",\"file-selector-button\",\"first\",\"first-child\",\"first-letter\",\"first-line\",\"first-of-type\",\"focus\",\"focus-visible\",\"focus-within\",\"fullscreen\",\"has\",\"host\",\"host-context\",\"hover\",\"in-range\",\"indeterminate\",\"invalid\",\"is\",\"lang\",\"last-child\",\"last-of-type\",\"left\",\"link\",\"marker\",\"modal\",\"not\",\"nth-child\",\"nth-last-child\",\"nth-last-of-type\",\"nth-of-type\",\"only-child\",\"only-of-type\",\"optional\",\"out-of-range\",\"part\",\"placeholder\",\"placeholder-shown\",\"read-only\",\"read-write\",\"required\",\"right\",\"root\",\"scope\",\"selection\",\"slotted\",\"target\",\"target-text\",\"valid\",\"visited\",\"where\"].map(t=>({type:\"class\",label:t})),wO=[\"above\",\"absolute\",\"activeborder\",\"additive\",\"activecaption\",\"after-white-space\",\"ahead\",\"alias\",\"all\",\"all-scroll\",\"alphabetic\",\"alternate\",\"always\",\"antialiased\",\"appworkspace\",\"asterisks\",\"attr\",\"auto\",\"auto-flow\",\"avoid\",\"avoid-column\",\"avoid-page\",\"avoid-region\",\"axis-pan\",\"background\",\"backwards\",\"baseline\",\"below\",\"bidi-override\",\"blink\",\"block\",\"block-axis\",\"bold\",\"bolder\",\"border\",\"border-box\",\"both\",\"bottom\",\"break\",\"break-all\",\"break-word\",\"bullets\",\"button\",\"button-bevel\",\"buttonface\",\"buttonhighlight\",\"buttonshadow\",\"buttontext\",\"calc\",\"capitalize\",\"caps-lock-indicator\",\"caption\",\"captiontext\",\"caret\",\"cell\",\"center\",\"checkbox\",\"circle\",\"cjk-decimal\",\"clear\",\"clip\",\"close-quote\",\"col-resize\",\"collapse\",\"color\",\"color-burn\",\"color-dodge\",\"column\",\"column-reverse\",\"compact\",\"condensed\",\"contain\",\"content\",\"contents\",\"content-box\",\"context-menu\",\"continuous\",\"copy\",\"counter\",\"counters\",\"cover\",\"crop\",\"cross\",\"crosshair\",\"currentcolor\",\"cursive\",\"cyclic\",\"darken\",\"dashed\",\"decimal\",\"decimal-leading-zero\",\"default\",\"default-button\",\"dense\",\"destination-atop\",\"destination-in\",\"destination-out\",\"destination-over\",\"difference\",\"disc\",\"discard\",\"disclosure-closed\",\"disclosure-open\",\"document\",\"dot-dash\",\"dot-dot-dash\",\"dotted\",\"double\",\"down\",\"e-resize\",\"ease\",\"ease-in\",\"ease-in-out\",\"ease-out\",\"element\",\"ellipse\",\"ellipsis\",\"embed\",\"end\",\"ethiopic-abegede-gez\",\"ethiopic-halehame-aa-er\",\"ethiopic-halehame-gez\",\"ew-resize\",\"exclusion\",\"expanded\",\"extends\",\"extra-condensed\",\"extra-expanded\",\"fantasy\",\"fast\",\"fill\",\"fill-box\",\"fixed\",\"flat\",\"flex\",\"flex-end\",\"flex-start\",\"footnotes\",\"forwards\",\"from\",\"geometricPrecision\",\"graytext\",\"grid\",\"groove\",\"hand\",\"hard-light\",\"help\",\"hidden\",\"hide\",\"higher\",\"highlight\",\"highlighttext\",\"horizontal\",\"hsl\",\"hsla\",\"hue\",\"icon\",\"ignore\",\"inactiveborder\",\"inactivecaption\",\"inactivecaptiontext\",\"infinite\",\"infobackground\",\"infotext\",\"inherit\",\"initial\",\"inline\",\"inline-axis\",\"inline-block\",\"inline-flex\",\"inline-grid\",\"inline-table\",\"inset\",\"inside\",\"intrinsic\",\"invert\",\"italic\",\"justify\",\"keep-all\",\"landscape\",\"large\",\"larger\",\"left\",\"level\",\"lighter\",\"lighten\",\"line-through\",\"linear\",\"linear-gradient\",\"lines\",\"list-item\",\"listbox\",\"listitem\",\"local\",\"logical\",\"loud\",\"lower\",\"lower-hexadecimal\",\"lower-latin\",\"lower-norwegian\",\"lowercase\",\"ltr\",\"luminosity\",\"manipulation\",\"match\",\"matrix\",\"matrix3d\",\"medium\",\"menu\",\"menutext\",\"message-box\",\"middle\",\"min-intrinsic\",\"mix\",\"monospace\",\"move\",\"multiple\",\"multiple_mask_images\",\"multiply\",\"n-resize\",\"narrower\",\"ne-resize\",\"nesw-resize\",\"no-close-quote\",\"no-drop\",\"no-open-quote\",\"no-repeat\",\"none\",\"normal\",\"not-allowed\",\"nowrap\",\"ns-resize\",\"numbers\",\"numeric\",\"nw-resize\",\"nwse-resize\",\"oblique\",\"opacity\",\"open-quote\",\"optimizeLegibility\",\"optimizeSpeed\",\"outset\",\"outside\",\"outside-shape\",\"overlay\",\"overline\",\"padding\",\"padding-box\",\"painted\",\"page\",\"paused\",\"perspective\",\"pinch-zoom\",\"plus-darker\",\"plus-lighter\",\"pointer\",\"polygon\",\"portrait\",\"pre\",\"pre-line\",\"pre-wrap\",\"preserve-3d\",\"progress\",\"push-button\",\"radial-gradient\",\"radio\",\"read-only\",\"read-write\",\"read-write-plaintext-only\",\"rectangle\",\"region\",\"relative\",\"repeat\",\"repeating-linear-gradient\",\"repeating-radial-gradient\",\"repeat-x\",\"repeat-y\",\"reset\",\"reverse\",\"rgb\",\"rgba\",\"ridge\",\"right\",\"rotate\",\"rotate3d\",\"rotateX\",\"rotateY\",\"rotateZ\",\"round\",\"row\",\"row-resize\",\"row-reverse\",\"rtl\",\"run-in\",\"running\",\"s-resize\",\"sans-serif\",\"saturation\",\"scale\",\"scale3d\",\"scaleX\",\"scaleY\",\"scaleZ\",\"screen\",\"scroll\",\"scrollbar\",\"scroll-position\",\"se-resize\",\"self-start\",\"self-end\",\"semi-condensed\",\"semi-expanded\",\"separate\",\"serif\",\"show\",\"single\",\"skew\",\"skewX\",\"skewY\",\"skip-white-space\",\"slide\",\"slider-horizontal\",\"slider-vertical\",\"sliderthumb-horizontal\",\"sliderthumb-vertical\",\"slow\",\"small\",\"small-caps\",\"small-caption\",\"smaller\",\"soft-light\",\"solid\",\"source-atop\",\"source-in\",\"source-out\",\"source-over\",\"space\",\"space-around\",\"space-between\",\"space-evenly\",\"spell-out\",\"square\",\"start\",\"static\",\"status-bar\",\"stretch\",\"stroke\",\"stroke-box\",\"sub\",\"subpixel-antialiased\",\"svg_masks\",\"super\",\"sw-resize\",\"symbolic\",\"symbols\",\"system-ui\",\"table\",\"table-caption\",\"table-cell\",\"table-column\",\"table-column-group\",\"table-footer-group\",\"table-header-group\",\"table-row\",\"table-row-group\",\"text\",\"text-bottom\",\"text-top\",\"textarea\",\"textfield\",\"thick\",\"thin\",\"threeddarkshadow\",\"threedface\",\"threedhighlight\",\"threedlightshadow\",\"threedshadow\",\"to\",\"top\",\"transform\",\"translate\",\"translate3d\",\"translateX\",\"translateY\",\"translateZ\",\"transparent\",\"ultra-condensed\",\"ultra-expanded\",\"underline\",\"unidirectional-pan\",\"unset\",\"up\",\"upper-latin\",\"uppercase\",\"url\",\"var\",\"vertical\",\"vertical-text\",\"view-box\",\"visible\",\"visibleFill\",\"visiblePainted\",\"visibleStroke\",\"visual\",\"w-resize\",\"wait\",\"wave\",\"wider\",\"window\",\"windowframe\",\"windowtext\",\"words\",\"wrap\",\"wrap-reverse\",\"x-large\",\"x-small\",\"xor\",\"xx-large\",\"xx-small\"].map(t=>({type:\"keyword\",label:t})).concat([\"aliceblue\",\"antiquewhite\",\"aqua\",\"aquamarine\",\"azure\",\"beige\",\"bisque\",\"black\",\"blanchedalmond\",\"blue\",\"blueviolet\",\"brown\",\"burlywood\",\"cadetblue\",\"chartreuse\",\"chocolate\",\"coral\",\"cornflowerblue\",\"cornsilk\",\"crimson\",\"cyan\",\"darkblue\",\"darkcyan\",\"darkgoldenrod\",\"darkgray\",\"darkgreen\",\"darkkhaki\",\"darkmagenta\",\"darkolivegreen\",\"darkorange\",\"darkorchid\",\"darkred\",\"darksalmon\",\"darkseagreen\",\"darkslateblue\",\"darkslategray\",\"darkturquoise\",\"darkviolet\",\"deeppink\",\"deepskyblue\",\"dimgray\",\"dodgerblue\",\"firebrick\",\"floralwhite\",\"forestgreen\",\"fuchsia\",\"gainsboro\",\"ghostwhite\",\"gold\",\"goldenrod\",\"gray\",\"grey\",\"green\",\"greenyellow\",\"honeydew\",\"hotpink\",\"indianred\",\"indigo\",\"ivory\",\"khaki\",\"lavender\",\"lavenderblush\",\"lawngreen\",\"lemonchiffon\",\"lightblue\",\"lightcoral\",\"lightcyan\",\"lightgoldenrodyellow\",\"lightgray\",\"lightgreen\",\"lightpink\",\"lightsalmon\",\"lightseagreen\",\"lightskyblue\",\"lightslategray\",\"lightsteelblue\",\"lightyellow\",\"lime\",\"limegreen\",\"linen\",\"magenta\",\"maroon\",\"mediumaquamarine\",\"mediumblue\",\"mediumorchid\",\"mediumpurple\",\"mediumseagreen\",\"mediumslateblue\",\"mediumspringgreen\",\"mediumturquoise\",\"mediumvioletred\",\"midnightblue\",\"mintcream\",\"mistyrose\",\"moccasin\",\"navajowhite\",\"navy\",\"oldlace\",\"olive\",\"olivedrab\",\"orange\",\"orangered\",\"orchid\",\"palegoldenrod\",\"palegreen\",\"paleturquoise\",\"palevioletred\",\"papayawhip\",\"peachpuff\",\"peru\",\"pink\",\"plum\",\"powderblue\",\"purple\",\"rebeccapurple\",\"red\",\"rosybrown\",\"royalblue\",\"saddlebrown\",\"salmon\",\"sandybrown\",\"seagreen\",\"seashell\",\"sienna\",\"silver\",\"skyblue\",\"slateblue\",\"slategray\",\"snow\",\"springgreen\",\"steelblue\",\"tan\",\"teal\",\"thistle\",\"tomato\",\"turquoise\",\"violet\",\"wheat\",\"white\",\"whitesmoke\",\"yellow\",\"yellowgreen\"].map(t=>({type:\"constant\",label:t}))),xO=[\"a\",\"abbr\",\"address\",\"article\",\"aside\",\"b\",\"bdi\",\"bdo\",\"blockquote\",\"body\",\"br\",\"button\",\"canvas\",\"caption\",\"cite\",\"code\",\"col\",\"colgroup\",\"dd\",\"del\",\"details\",\"dfn\",\"dialog\",\"div\",\"dl\",\"dt\",\"em\",\"figcaption\",\"figure\",\"footer\",\"form\",\"header\",\"hgroup\",\"h1\",\"h2\",\"h3\",\"h4\",\"h5\",\"h6\",\"hr\",\"html\",\"i\",\"iframe\",\"img\",\"input\",\"ins\",\"kbd\",\"label\",\"legend\",\"li\",\"main\",\"meter\",\"nav\",\"ol\",\"output\",\"p\",\"pre\",\"ruby\",\"section\",\"select\",\"small\",\"source\",\"span\",\"strong\",\"sub\",\"summary\",\"sup\",\"table\",\"tbody\",\"td\",\"template\",\"textarea\",\"tfoot\",\"th\",\"thead\",\"tr\",\"u\",\"ul\"].map(t=>({type:\"type\",label:t})),SO=[\"@charset\",\"@color-profile\",\"@container\",\"@counter-style\",\"@font-face\",\"@font-feature-values\",\"@font-palette-values\",\"@import\",\"@keyframes\",\"@layer\",\"@media\",\"@namespace\",\"@page\",\"@position-try\",\"@property\",\"@scope\",\"@starting-style\",\"@supports\",\"@view-transition\"].map(t=>({type:\"keyword\",label:t})),yO=/^(\\w[\\w-]*|-\\w[\\w-]*|)$/,kO=/^-(-[\\w-]*)?$/;const $O=new Aa,PO=[\"Declaration\"];function TO(t){for(let e=t;;){if(e.type.isTop)return e;if(!(e=e.parent))return t}}function CO(t,e,i){if(e.to-e.from>4096){let n=$O.get(e);if(n)return n;let r=[],s=new Set,o=e.cursor(da.IncludeAnonymous);if(o.firstChild())do{for(let e of CO(t,o.node,i))s.has(e.label)||(s.add(e.label),r.push(e))}while(o.nextSibling());return $O.set(e,r),r}{let n=[],r=new Set;return e.cursor().iterate(e=>{var s;if(i(e)&&e.matchContext(PO)&&\":\"==(null===(s=e.node.nextSibling)||void 0===s?void 0:s.name)){let i=t.sliceString(e.from,e.to);r.has(i)||(r.add(i),n.push({label:i,type:\"variable\"}))}}),n}}const ZO=(t=>e=>{let{state:i,pos:n}=e,r=Cl(i).resolveInner(n,-1),s=r.type.isError&&r.from==r.to-1&&\"-\"==i.doc.sliceString(r.from,r.to);if(\"PropertyName\"==r.name||(s||\"TagName\"==r.name)&&/^(Block|Styles)$/.test(r.resolve(r.to).name))return{from:r.from,options:bO(),validFor:yO};if(\"ValueName\"==r.name)return{from:r.from,options:wO,validFor:yO};if(\"PseudoClassName\"==r.name)return{from:r.from,options:vO,validFor:yO};if(t(r)||(e.explicit||s)&&function(t,e){var i;if((\"(\"==t.name||t.type.isError)&&(t=t.parent||t),\"ArgList\"!=t.name)return!1;let n=null===(i=t.parent)||void 0===i?void 0:i.firstChild;return\"Callee\"==(null==n?void 0:n.name)&&\"var\"==e.sliceString(n.from,n.to)}(r,i.doc))return{from:t(r)||s?r.from:n,options:CO(i.doc,TO(r),t),validFor:kO};if(\"TagName\"==r.name){for(let{parent:t}=r;t;t=t.parent)if(\"Block\"==t.name)return{from:r.from,options:bO(),validFor:yO};return{from:r.from,options:xO,validFor:yO}}if(\"AtKeyword\"==r.name)return{from:r.from,options:SO,validFor:yO};if(!e.explicit)return null;let o=r.resolve(n),a=o.childBefore(n);return a&&\":\"==a.name&&\"PseudoClassSelector\"==o.name?{from:n,options:vO,validFor:yO}:a&&\":\"==a.name&&\"Declaration\"==o.name||\"ArgList\"==o.name?{from:n,options:wO,validFor:yO}:\"Block\"==o.name||\"Styles\"==o.name?{from:n,options:bO(),validFor:yO}:null})(t=>\"VariableName\"==t.name),XO=Tl.define({name:\"css\",parser:gO.configure({props:[Gl.add({Declaration:eh()}),nh.add({\"Block KeyframeList\":rh})]}),languageData:{commentTokens:{block:{open:\"/*\",close:\"*/\"}},indentOnInput:/^\\s*\\}$/,wordChars:\"-\"}});const AO=[9,10,11,12,13,32,133,160,5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8232,8233,8239,8287,12288],MO=new $f({start:!1,shift:(t,e)=>5==e||6==e||320==e?t:321==e,strict:!1}),RO=new ff((t,e)=>{let{next:i}=t;(125==i||-1==i||e.context)&&t.acceptToken(318)},{contextual:!0,fallback:!0}),zO=new ff((t,e)=>{let i,{next:n}=t;AO.indexOf(n)>-1||(47!=n||47!=(i=t.peek(1))&&42!=i)&&(125==n||59==n||-1==n||e.context||t.acceptToken(316))},{contextual:!0}),_O=new ff((t,e)=>{91!=t.next||e.context||t.acceptToken(317)},{contextual:!0}),EO=new ff((t,e)=>{let{next:i}=t;if(43==i||45==i){if(t.advance(),i==t.next){t.advance();let i=!e.context&&e.canShift(1);t.acceptToken(i?1:2)}}else 63==i&&46==t.peek(1)&&(t.advance(),t.advance(),(t.next<48||t.next>57)&&t.acceptToken(3))},{contextual:!0});function YO(t,e){return t>=65&&t<=90||t>=97&&t<=122||95==t||t>=192||!e&&t>=48&&t<=57}const LO=new ff((t,e)=>{if(60!=t.next||!e.dialectEnabled(0))return;if(t.advance(),47==t.next)return;let i=0;for(;AO.indexOf(t.next)>-1;)t.advance(),i++;if(YO(t.next,!0)){for(t.advance(),i++;YO(t.next,!1);)t.advance(),i++;for(;AO.indexOf(t.next)>-1;)t.advance(),i++;if(44==t.next)return;for(let e=0;;e++){if(7==e){if(!YO(t.next,!0))return;break}if(t.next!=\"extends\".charCodeAt(e))break;t.advance(),i++}}t.acceptToken(4,-i)}),qO=tl({\"get set async static\":wl.modifier,\"for while do if else switch try catch finally return throw break continue default case defer\":wl.controlKeyword,\"in of await yield void typeof delete instanceof as satisfies\":wl.operatorKeyword,\"let var const using function class extends\":wl.definitionKeyword,\"import export from\":wl.moduleKeyword,\"with debugger new\":wl.keyword,TemplateString:wl.special(wl.string),super:wl.atom,BooleanLiteral:wl.bool,this:wl.self,null:wl.null,Star:wl.modifier,VariableName:wl.variableName,\"CallExpression/VariableName TaggedTemplateExpression/VariableName\":wl.function(wl.variableName),VariableDefinition:wl.definition(wl.variableName),Label:wl.labelName,PropertyName:wl.propertyName,PrivatePropertyName:wl.special(wl.propertyName),\"CallExpression/MemberExpression/PropertyName\":wl.function(wl.propertyName),\"FunctionDeclaration/VariableDefinition\":wl.function(wl.definition(wl.variableName)),\"ClassDeclaration/VariableDefinition\":wl.definition(wl.className),\"NewExpression/VariableName\":wl.className,PropertyDefinition:wl.definition(wl.propertyName),PrivatePropertyDefinition:wl.definition(wl.special(wl.propertyName)),UpdateOp:wl.updateOperator,\"LineComment Hashbang\":wl.lineComment,BlockComment:wl.blockComment,Number:wl.number,String:wl.string,Escape:wl.escape,ArithOp:wl.arithmeticOperator,LogicOp:wl.logicOperator,BitOp:wl.bitwiseOperator,CompareOp:wl.compareOperator,RegExp:wl.regexp,Equals:wl.definitionOperator,Arrow:wl.function(wl.punctuation),\": Spread\":wl.punctuation,\"( )\":wl.paren,\"[ ]\":wl.squareBracket,\"{ }\":wl.brace,\"InterpolationStart InterpolationEnd\":wl.special(wl.brace),\".\":wl.derefOperator,\", ;\":wl.separator,\"@\":wl.meta,TypeName:wl.typeName,TypeDefinition:wl.definition(wl.typeName),\"type enum interface implements namespace module declare\":wl.definitionKeyword,\"abstract global Privacy readonly override\":wl.modifier,\"is keyof unique infer asserts\":wl.operatorKeyword,JSXAttributeValue:wl.attributeValue,JSXText:wl.content,\"JSXStartTag JSXStartCloseTag JSXSelfCloseEndTag JSXEndTag\":wl.angleBracket,\"JSXIdentifier JSXNameSpacedName\":wl.tagName,\"JSXAttribute/JSXIdentifier JSXAttribute/JSXNameSpacedName\":wl.attributeName,\"JSXBuiltin/JSXIdentifier\":wl.standard(wl.tagName)}),VO={__proto__:null,export:20,as:25,from:33,default:36,async:41,function:42,in:52,out:55,const:56,extends:60,this:64,true:72,false:72,null:84,void:88,typeof:92,super:108,new:142,delete:154,yield:163,await:167,class:172,public:235,private:235,protected:235,readonly:237,instanceof:256,satisfies:259,import:292,keyof:349,unique:353,infer:359,asserts:395,is:397,abstract:417,implements:419,type:421,let:424,var:426,using:429,interface:435,enum:439,namespace:445,module:447,declare:451,global:455,defer:471,for:476,of:485,while:488,with:492,do:496,if:500,else:502,switch:506,case:512,try:518,catch:522,finally:526,return:530,throw:534,break:538,continue:542,debugger:546},WO={__proto__:null,async:129,get:131,set:133,declare:195,public:197,private:197,protected:197,static:199,abstract:201,override:203,readonly:209,accessor:211,new:401},DO={__proto__:null,\"<\":193},BO=Pf.deserialize({version:14,states:\"$F|Q%TQlOOO%[QlOOO'_QpOOP(lO`OOO*zQ!0MxO'#CiO+RO#tO'#CjO+aO&jO'#CjO+oO#@ItO'#DaO.QQlO'#DgO.bQlO'#DrO%[QlO'#DzO0fQlO'#ESOOQ!0Lf'#E['#E[O1PQ`O'#EXOOQO'#Ep'#EpOOQO'#Il'#IlO1XQ`O'#GsO1dQ`O'#EoO1iQ`O'#EoO3hQ!0MxO'#JrO6[Q!0MxO'#JsO6uQ`O'#F]O6zQ,UO'#FtOOQ!0Lf'#Ff'#FfO7VO7dO'#FfO9XQMhO'#F|O9`Q`O'#F{OOQ!0Lf'#Js'#JsOOQ!0Lb'#Jr'#JrO9eQ`O'#GwOOQ['#K_'#K_O9pQ`O'#IYO9uQ!0LrO'#IZOOQ['#J`'#J`OOQ['#I_'#I_Q`QlOOQ`QlOOO9}Q!L^O'#DvO:UQlO'#EOO:]QlO'#EQO9kQ`O'#GsO:dQMhO'#CoO:rQ`O'#EnO:}Q`O'#EyO;hQMhO'#FeO;xQ`O'#GsOOQO'#K`'#K`O;}Q`O'#K`O<]Q`O'#G{O<]Q`O'#G|O<]Q`O'#HOO9kQ`O'#HRO=SQ`O'#HUO>kQ`O'#CeO>{Q`O'#HcO?TQ`O'#HiO?TQ`O'#HkO`QlO'#HmO?TQ`O'#HoO?TQ`O'#HrO?YQ`O'#HxO?_Q!0LsO'#IOO%[QlO'#IQO?jQ!0LsO'#ISO?uQ!0LsO'#IUO9uQ!0LrO'#IWO@QQ!0MxO'#CiOASQpO'#DlQOQ`OOO%[QlO'#EQOAjQ`O'#ETO:dQMhO'#EnOAuQ`O'#EnOBQQ!bO'#FeOOQ['#Cg'#CgOOQ!0Lb'#Dq'#DqOOQ!0Lb'#Jv'#JvO%[QlO'#JvOOQO'#Jy'#JyOOQO'#Ih'#IhOCQQpO'#EgOOQ!0Lb'#Ef'#EfOOQ!0Lb'#J}'#J}OC|Q!0MSO'#EgODWQpO'#EWOOQO'#Jx'#JxODlQpO'#JyOEyQpO'#EWODWQpO'#EgPFWO&2DjO'#CbPOOO)CD})CD}OOOO'#I`'#I`OFcO#tO,59UOOQ!0Lh,59U,59UOOOO'#Ia'#IaOFqO&jO,59UOGPQ!L^O'#DcOOOO'#Ic'#IcOGWO#@ItO,59{OOQ!0Lf,59{,59{OGfQlO'#IdOGyQ`O'#JtOIxQ!fO'#JtO+}QlO'#JtOJPQ`O,5:ROJgQ`O'#EpOJtQ`O'#KTOKPQ`O'#KSOKPQ`O'#KSOKXQ`O,5;^OK^Q`O'#KROOQ!0Ln,5:^,5:^OKeQlO,5:^OMcQ!0MxO,5:fONSQ`O,5:nONmQ!0LrO'#KQONtQ`O'#KPO9eQ`O'#KPO! YQ`O'#KPO! bQ`O,5;]O! gQ`O'#KPO!#lQ!fO'#JsOOQ!0Lh'#Ci'#CiO%[QlO'#ESO!$[Q!fO,5:sOOQS'#Jz'#JzOOQO-E<j-E<jO9kQ`O,5=_O!$rQ`O,5=_O!$wQlO,5;ZO!&zQMhO'#EkO!(eQ`O,5;ZO!(jQlO'#DyO!(tQpO,5;dO!(|QpO,5;dO%[QlO,5;dOOQ['#FT'#FTOOQ['#FV'#FVO%[QlO,5;eO%[QlO,5;eO%[QlO,5;eO%[QlO,5;eO%[QlO,5;eO%[QlO,5;eO%[QlO,5;eO%[QlO,5;eO%[QlO,5;eO%[QlO,5;eOOQ['#FZ'#FZO!)[QlO,5;tOOQ!0Lf,5;y,5;yOOQ!0Lf,5;z,5;zOOQ!0Lf,5;|,5;|O%[QlO'#IpO!+_Q!0LrO,5<iO%[QlO,5;eO!&zQMhO,5;eO!+|QMhO,5;eO!-nQMhO'#E^O%[QlO,5;wOOQ!0Lf,5;{,5;{O!-uQ,UO'#FjO!.rQ,UO'#KXO!.^Q,UO'#KXO!.yQ,UO'#KXOOQO'#KX'#KXO!/_Q,UO,5<SOOOW,5<`,5<`O!/pQlO'#FvOOOW'#Io'#IoO7VO7dO,5<QO!/wQ,UO'#FxOOQ!0Lf,5<Q,5<QO!0hQ$IUO'#CyOOQ!0Lh'#C}'#C}O!0{O#@ItO'#DRO!1iQMjO,5<eO!1pQ`O,5<hO!3YQ(CWO'#GXO!3jQ`O'#GYO!3oQ`O'#GYO!5_Q(CWO'#G^O!6dQpO'#GbOOQO'#Gn'#GnO!,TQMhO'#GmOOQO'#Gp'#GpO!,TQMhO'#GoO!7VQ$IUO'#JlOOQ!0Lh'#Jl'#JlO!7aQ`O'#JkO!7oQ`O'#JjO!7wQ`O'#CuOOQ!0Lh'#C{'#C{O!8YQ`O'#C}OOQ!0Lh'#DV'#DVOOQ!0Lh'#DX'#DXO!8_Q`O,5<eO1SQ`O'#DZO!,TQMhO'#GPO!,TQMhO'#GRO!8gQ`O'#GTO!8lQ`O'#GUO!3oQ`O'#G[O!,TQMhO'#GaO<]Q`O'#JkO!8qQ`O'#EqO!9`Q`O,5<gOOQ!0Lb'#Cr'#CrO!9hQ`O'#ErO!:bQpO'#EsOOQ!0Lb'#KR'#KRO!:iQ!0LrO'#KaO9uQ!0LrO,5=cO`QlO,5>tOOQ['#Jh'#JhOOQ[,5>u,5>uOOQ[-E<]-E<]O!<hQ!0MxO,5:bO!:]QpO,5:`O!?RQ!0MxO,5:jO%[QlO,5:jO!AiQ!0MxO,5:lOOQO,5@z,5@zO!BYQMhO,5=_O!BhQ!0LrO'#JiO9`Q`O'#JiO!ByQ!0LrO,59ZO!CUQpO,59ZO!C^QMhO,59ZO:dQMhO,59ZO!CiQ`O,5;ZO!CqQ`O'#HbO!DVQ`O'#KdO%[QlO,5;}O!:]QpO,5<PO!D_Q`O,5=zO!DdQ`O,5=zO!DiQ`O,5=zO!DwQ`O,5=zO9uQ!0LrO,5=zO<]Q`O,5=jOOQO'#Cy'#CyO!EOQpO,5=gO!EWQMhO,5=hO!EcQ`O,5=jO!EhQ!bO,5=mO!EpQ`O'#K`O?YQ`O'#HWO9kQ`O'#HYO!EuQ`O'#HYO:dQMhO'#H[O!EzQ`O'#H[OOQ[,5=p,5=pO!FPQ`O'#H]O!FbQ`O'#CoO!FgQ`O,59PO!FqQ`O,59PO!HvQlO,59POOQ[,59P,59PO!IWQ!0LrO,59PO%[QlO,59PO!KcQlO'#HeOOQ['#Hf'#HfOOQ['#Hg'#HgO`QlO,5=}O!KyQ`O,5=}O`QlO,5>TO`QlO,5>VO!LOQ`O,5>XO`QlO,5>ZO!LTQ`O,5>^O!LYQlO,5>dOOQ[,5>j,5>jO%[QlO,5>jO9uQ!0LrO,5>lOOQ[,5>n,5>nO#!dQ`O,5>nOOQ[,5>p,5>pO#!dQ`O,5>pOOQ[,5>r,5>rO##QQpO'#D_O%[QlO'#JvO##sQpO'#JvO##}QpO'#DmO#$`QpO'#DmO#&qQlO'#DmO#&xQ`O'#JuO#'QQ`O,5:WO#'VQ`O'#EtO#'eQ`O'#KUO#'mQ`O,5;_O#'rQpO'#DmO#(PQpO'#EVOOQ!0Lf,5:o,5:oO%[QlO,5:oO#(WQ`O,5:oO?YQ`O,5;YO!CUQpO,5;YO!C^QMhO,5;YO:dQMhO,5;YO#(`Q`O,5@bO#(eQ07dO,5:sOOQO-E<f-E<fO#)kQ!0MSO,5;RODWQpO,5:rO#)uQpO,5:rODWQpO,5;RO!ByQ!0LrO,5:rOOQ!0Lb'#Ej'#EjOOQO,5;R,5;RO%[QlO,5;RO#*SQ!0LrO,5;RO#*_Q!0LrO,5;RO!CUQpO,5:rOOQO,5;X,5;XO#*mQ!0LrO,5;RPOOO'#I^'#I^P#+RO&2DjO,58|POOO,58|,58|OOOO-E<^-E<^OOQ!0Lh1G.p1G.pOOOO-E<_-E<_OOOO,59},59}O#+^Q!bO,59}OOOO-E<a-E<aOOQ!0Lf1G/g1G/gO#+cQ!fO,5?OO+}QlO,5?OOOQO,5?U,5?UO#+mQlO'#IdOOQO-E<b-E<bO#+zQ`O,5@`O#,SQ!fO,5@`O#,ZQ`O,5@nOOQ!0Lf1G/m1G/mO%[QlO,5@oO#,cQ`O'#IjOOQO-E<h-E<hO#,ZQ`O,5@nOOQ!0Lb1G0x1G0xOOQ!0Ln1G/x1G/xOOQ!0Ln1G0Y1G0YO%[QlO,5@lO#,wQ!0LrO,5@lO#-YQ!0LrO,5@lO#-aQ`O,5@kO9eQ`O,5@kO#-iQ`O,5@kO#-wQ`O'#ImO#-aQ`O,5@kOOQ!0Lb1G0w1G0wO!(tQpO,5:uO!)PQpO,5:uOOQS,5:w,5:wO#.iQdO,5:wO#.qQMhO1G2yO9kQ`O1G2yOOQ!0Lf1G0u1G0uO#/PQ!0MxO1G0uO#0UQ!0MvO,5;VOOQ!0Lh'#GW'#GWO#0rQ!0MzO'#JlO!$wQlO1G0uO#2}Q!fO'#JwO%[QlO'#JwO#3XQ`O,5:eOOQ!0Lh'#D_'#D_OOQ!0Lf1G1O1G1OO%[QlO1G1OOOQ!0Lf1G1f1G1fO#3^Q`O1G1OO#5rQ!0MxO1G1PO#5yQ!0MxO1G1PO#8aQ!0MxO1G1PO#8hQ!0MxO1G1PO#;OQ!0MxO1G1PO#=fQ!0MxO1G1PO#=mQ!0MxO1G1PO#=tQ!0MxO1G1PO#@[Q!0MxO1G1PO#@cQ!0MxO1G1PO#BpQ?MtO'#CiO#DkQ?MtO1G1`O#DrQ?MtO'#JsO#EVQ!0MxO,5?[OOQ!0Lb-E<n-E<nO#GdQ!0MxO1G1PO#HaQ!0MzO1G1POOQ!0Lf1G1P1G1PO#IdQMjO'#J|O#InQ`O,5:xO#IsQ!0MxO1G1cO#JgQ,UO,5<WO#JoQ,UO,5<XO#JwQ,UO'#FoO#K`Q`O'#FnOOQO'#KY'#KYOOQO'#In'#InO#KeQ,UO1G1nOOQ!0Lf1G1n1G1nOOOW1G1y1G1yO#KvQ?MtO'#JrO#LQQ`O,5<bO!)[QlO,5<bOOOW-E<m-E<mOOQ!0Lf1G1l1G1lO#LVQpO'#KXOOQ!0Lf,5<d,5<dO#L_QpO,5<dO#LdQMhO'#DTOOOO'#Ib'#IbO#LkO#@ItO,59mOOQ!0Lh,59m,59mO%[QlO1G2PO!8lQ`O'#IrO#LvQ`O,5<zOOQ!0Lh,5<w,5<wO!,TQMhO'#IuO#MdQMjO,5=XO!,TQMhO'#IwO#NVQMjO,5=ZO!&zQMhO,5=]OOQO1G2S1G2SO#NaQ!dO'#CrO#NtQ(CWO'#ErO$ |QpO'#GbO$!dQ!dO,5<sO$!kQ`O'#K[O9eQ`O'#K[O$!yQ`O,5<uO$#aQ!dO'#C{O!,TQMhO,5<tO$#kQ`O'#GZO$$PQ`O,5<tO$$UQ!dO'#GWO$$cQ!dO'#K]O$$mQ`O'#K]O!&zQMhO'#K]O$$rQ`O,5<xO$$wQlO'#JvO$%RQpO'#GcO#$`QpO'#GcO$%dQ`O'#GgO!3oQ`O'#GkO$%iQ!0LrO'#ItO$%tQpO,5<|OOQ!0Lp,5<|,5<|O$%{QpO'#GcO$&YQpO'#GdO$&kQpO'#GdO$&pQMjO,5=XO$'QQMjO,5=ZOOQ!0Lh,5=^,5=^O!,TQMhO,5@VO!,TQMhO,5@VO$'bQ`O'#IyO$'vQ`O,5@UO$(OQ`O,59aOOQ!0Lh,59i,59iO$(TQ`O,5@VO$)TQ$IYO,59uOOQ!0Lh'#Jp'#JpO$)vQMjO,5<kO$*iQMjO,5<mO@zQ`O,5<oOOQ!0Lh,5<p,5<pO$*sQ`O,5<vO$*xQMjO,5<{O$+YQ`O'#KPO!$wQlO1G2RO$+_Q`O1G2RO9eQ`O'#KSO9eQ`O'#EtO%[QlO'#EtO9eQ`O'#I{O$+dQ!0LrO,5@{OOQ[1G2}1G2}OOQ[1G4`1G4`OOQ!0Lf1G/|1G/|OOQ!0Lf1G/z1G/zO$-fQ!0MxO1G0UOOQ[1G2y1G2yO!&zQMhO1G2yO%[QlO1G2yO#.tQ`O1G2yO$/jQMhO'#EkOOQ!0Lb,5@T,5@TO$/wQ!0LrO,5@TOOQ[1G.u1G.uO!ByQ!0LrO1G.uO!CUQpO1G.uO!C^QMhO1G.uO$0YQ`O1G0uO$0_Q`O'#CiO$0jQ`O'#KeO$0rQ`O,5=|O$0wQ`O'#KeO$0|Q`O'#KeO$1[Q`O'#JRO$1jQ`O,5AOO$1rQ!fO1G1iOOQ!0Lf1G1k1G1kO9kQ`O1G3fO@zQ`O1G3fO$1yQ`O1G3fO$2OQ`O1G3fO!DiQ`O1G3fO9uQ!0LrO1G3fOOQ[1G3f1G3fO!EcQ`O1G3UO!&zQMhO1G3RO$2TQ`O1G3ROOQ[1G3S1G3SO!&zQMhO1G3SO$2YQ`O1G3SO$2bQpO'#HQOOQ[1G3U1G3UO!6_QpO'#I}O!EhQ!bO1G3XOOQ[1G3X1G3XOOQ[,5=r,5=rO$2jQMhO,5=tO9kQ`O,5=tO$%dQ`O,5=vO9`Q`O,5=vO!CUQpO,5=vO!C^QMhO,5=vO:dQMhO,5=vO$2xQ`O'#KcO$3TQ`O,5=wOOQ[1G.k1G.kO$3YQ!0LrO1G.kO@zQ`O1G.kO$3eQ`O1G.kO9uQ!0LrO1G.kO$5mQ!fO,5AQO$5zQ`O,5AQO9eQ`O,5AQO$6VQlO,5>PO$6^Q`O,5>POOQ[1G3i1G3iO`QlO1G3iOOQ[1G3o1G3oOOQ[1G3q1G3qO?TQ`O1G3sO$6cQlO1G3uO$:gQlO'#HtOOQ[1G3x1G3xO$:tQ`O'#HzO?YQ`O'#H|OOQ[1G4O1G4OO$:|QlO1G4OO9uQ!0LrO1G4UOOQ[1G4W1G4WOOQ!0Lb'#G_'#G_O9uQ!0LrO1G4YO9uQ!0LrO1G4[O$?TQ`O,5@bO!)[QlO,5;`O9eQ`O,5;`O?YQ`O,5:XO!)[QlO,5:XO!CUQpO,5:XO$?YQ?MtO,5:XOOQO,5;`,5;`O$?dQpO'#IeO$?zQ`O,5@aOOQ!0Lf1G/r1G/rO$@SQpO'#IkO$@^Q`O,5@pOOQ!0Lb1G0y1G0yO#$`QpO,5:XOOQO'#Ig'#IgO$@fQpO,5:qOOQ!0Ln,5:q,5:qO#(ZQ`O1G0ZOOQ!0Lf1G0Z1G0ZO%[QlO1G0ZOOQ!0Lf1G0t1G0tO?YQ`O1G0tO!CUQpO1G0tO!C^QMhO1G0tOOQ!0Lb1G5|1G5|O!ByQ!0LrO1G0^OOQO1G0m1G0mO%[QlO1G0mO$@mQ!0LrO1G0mO$@xQ!0LrO1G0mO!CUQpO1G0^ODWQpO1G0^O$AWQ!0LrO1G0mOOQO1G0^1G0^O$AlQ!0MxO1G0mPOOO-E<[-E<[POOO1G.h1G.hOOOO1G/i1G/iO$AvQ!bO,5<iO$BOQ!fO1G4jOOQO1G4p1G4pO%[QlO,5?OO$BYQ`O1G5zO$BbQ`O1G6YO$BjQ!fO1G6ZO9eQ`O,5?UO$BtQ!0MxO1G6WO%[QlO1G6WO$CUQ!0LrO1G6WO$CgQ`O1G6VO$CgQ`O1G6VO9eQ`O1G6VO$CoQ`O,5?XO9eQ`O,5?XOOQO,5?X,5?XO$DTQ`O,5?XO$+YQ`O,5?XOOQO-E<k-E<kOOQS1G0a1G0aOOQS1G0c1G0cO#.lQ`O1G0cOOQ[7+(e7+(eO!&zQMhO7+(eO%[QlO7+(eO$DcQ`O7+(eO$DnQMhO7+(eO$D|Q!0MzO,5=XO$GXQ!0MzO,5=ZO$IdQ!0MzO,5=XO$KuQ!0MzO,5=ZO$NWQ!0MzO,59uO%!]Q!0MzO,5<kO%$hQ!0MzO,5<mO%&sQ!0MzO,5<{OOQ!0Lf7+&a7+&aO%)UQ!0MxO7+&aO%)xQlO'#IfO%*VQ`O,5@cO%*_Q!fO,5@cOOQ!0Lf1G0P1G0PO%*iQ`O7+&jOOQ!0Lf7+&j7+&jO%*nQ?MtO,5:fO%[QlO7+&zO%*xQ?MtO,5:bO%+VQ?MtO,5:jO%+aQ?MtO,5:lO%+kQMhO'#IiO%+uQ`O,5@hOOQ!0Lh1G0d1G0dOOQO1G1r1G1rOOQO1G1s1G1sO%+}Q!jO,5<ZO!)[QlO,5<YOOQO-E<l-E<lOOQ!0Lf7+'Y7+'YOOOW7+'e7+'eOOOW1G1|1G1|O%,YQ`O1G1|OOQ!0Lf1G2O1G2OOOOO,59o,59oO%,_Q!dO,59oOOOO-E<`-E<`OOQ!0Lh1G/X1G/XO%,fQ!0MxO7+'kOOQ!0Lh,5?^,5?^O%-YQMhO1G2fP%-aQ`O'#IrPOQ!0Lh-E<p-E<pO%-}QMjO,5?aOOQ!0Lh-E<s-E<sO%.pQMjO,5?cOOQ!0Lh-E<u-E<uO%.zQ!dO1G2wO%/RQ!dO'#CrO%/iQMhO'#KSO$$wQlO'#JvOOQ!0Lh1G2_1G2_O%/sQ`O'#IqO%0[Q`O,5@vO%0[Q`O,5@vO%0dQ`O,5@vO%0oQ`O,5@vOOQO1G2a1G2aO%0}QMjO1G2`O$+YQ`O'#K[O!,TQMhO1G2`O%1_Q(CWO'#IsO%1lQ`O,5@wO!&zQMhO,5@wO%1tQ!dO,5@wOOQ!0Lh1G2d1G2dO%4UQ!fO'#CiO%4`Q`O,5=POOQ!0Lb,5<},5<}O%4hQpO,5<}OOQ!0Lb,5=O,5=OOCwQ`O,5<}O%4sQpO,5<}OOQ!0Lb,5=R,5=RO$+YQ`O,5=VOOQO,5?`,5?`OOQO-E<r-E<rOOQ!0Lp1G2h1G2hO#$`QpO,5<}O$$wQlO,5=PO%5RQ`O,5=OO%5^QpO,5=OO!,TQMhO'#IuO%6WQMjO1G2sO!,TQMhO'#IwO%6yQMjO1G2uO%7TQMjO1G5qO%7_QMjO1G5qOOQO,5?e,5?eOOQO-E<w-E<wOOQO1G.{1G.{O!,TQMhO1G5qO!,TQMhO1G5qO!:]QpO,59wO%[QlO,59wOOQ!0Lh,5<j,5<jO%7lQ`O1G2ZO!,TQMhO1G2bO%7qQ!0MxO7+'mOOQ!0Lf7+'m7+'mO!$wQlO7+'mO%8eQ`O,5;`OOQ!0Lb,5?g,5?gOOQ!0Lb-E<y-E<yO%8jQ!dO'#K^O#(ZQ`O7+(eO4UQ!fO7+(eO$DfQ`O7+(eO%8tQ!0MvO'#CiO%9XQ!0MvO,5=SO%9lQ`O,5=SO%9tQ`O,5=SOOQ!0Lb1G5o1G5oOOQ[7+$a7+$aO!ByQ!0LrO7+$aO!CUQpO7+$aO!$wQlO7+&aO%9yQ`O'#JQO%:bQ`O,5APOOQO1G3h1G3hO9kQ`O,5APO%:bQ`O,5APO%:jQ`O,5APOOQO,5?m,5?mOOQO-E=P-E=POOQ!0Lf7+'T7+'TO%:oQ`O7+)QO9uQ!0LrO7+)QO9kQ`O7+)QO@zQ`O7+)QO%:tQ`O7+)QOOQ[7+)Q7+)QOOQ[7+(p7+(pO%:yQ!0MvO7+(mO!&zQMhO7+(mO!E^Q`O7+(nOOQ[7+(n7+(nO!&zQMhO7+(nO%;TQ`O'#KbO%;`Q`O,5=lOOQO,5?i,5?iOOQO-E<{-E<{OOQ[7+(s7+(sO%<rQpO'#HZOOQ[1G3`1G3`O!&zQMhO1G3`O%[QlO1G3`O%<yQ`O1G3`O%=UQMhO1G3`O9uQ!0LrO1G3bO$%dQ`O1G3bO9`Q`O1G3bO!CUQpO1G3bO!C^QMhO1G3bO%=dQ`O'#JPO%=xQ`O,5@}O%>QQpO,5@}OOQ!0Lb1G3c1G3cOOQ[7+$V7+$VO@zQ`O7+$VO9uQ!0LrO7+$VO%>]Q`O7+$VO%[QlO1G6lO%[QlO1G6mO%>bQ!0LrO1G6lO%>lQlO1G3kO%>sQ`O1G3kO%>xQlO1G3kOOQ[7+)T7+)TO9uQ!0LrO7+)_O`QlO7+)aOOQ['#Kh'#KhOOQ['#JS'#JSO%?PQlO,5>`OOQ[,5>`,5>`O%[QlO'#HuO%?^Q`O'#HwOOQ[,5>f,5>fO9eQ`O,5>fOOQ[,5>h,5>hOOQ[7+)j7+)jOOQ[7+)p7+)pOOQ[7+)t7+)tOOQ[7+)v7+)vO%?cQpO1G5|O%?}Q?MtO1G0zO%@XQ`O1G0zOOQO1G/s1G/sO%@dQ?MtO1G/sO?YQ`O1G/sO!)[QlO'#DmOOQO,5?P,5?POOQO-E<c-E<cOOQO,5?V,5?VOOQO-E<i-E<iO!CUQpO1G/sOOQO-E<e-E<eOOQ!0Ln1G0]1G0]OOQ!0Lf7+%u7+%uO#(ZQ`O7+%uOOQ!0Lf7+&`7+&`O?YQ`O7+&`O!CUQpO7+&`OOQO7+%x7+%xO$AlQ!0MxO7+&XOOQO7+&X7+&XO%[QlO7+&XO%@nQ!0LrO7+&XO!ByQ!0LrO7+%xO!CUQpO7+%xO%@yQ!0LrO7+&XO%AXQ!0MxO7++rO%[QlO7++rO%AiQ`O7++qO%AiQ`O7++qOOQO1G4s1G4sO9eQ`O1G4sO%AqQ`O1G4sOOQS7+%}7+%}O#(ZQ`O<<LPO4UQ!fO<<LPO%BPQ`O<<LPOOQ[<<LP<<LPO!&zQMhO<<LPO%[QlO<<LPO%BXQ`O<<LPO%BdQ!0MzO,5?aO%DoQ!0MzO,5?cO%FzQ!0MzO1G2`O%I]Q!0MzO1G2sO%KhQ!0MzO1G2uO%MsQ!fO,5?QO%[QlO,5?QOOQO-E<d-E<dO%M}Q`O1G5}OOQ!0Lf<<JU<<JUO%NVQ?MtO1G0uO&!^Q?MtO1G1PO&!eQ?MtO1G1PO&$fQ?MtO1G1PO&$mQ?MtO1G1PO&&nQ?MtO1G1PO&(oQ?MtO1G1PO&(vQ?MtO1G1PO&(}Q?MtO1G1PO&+OQ?MtO1G1PO&+VQ?MtO1G1PO&+^Q!0MxO<<JfO&-UQ?MtO1G1PO&.RQ?MvO1G1PO&/UQ?MvO'#JlO&1[Q?MtO1G1cO&1iQ?MtO1G0UO&1sQMjO,5?TOOQO-E<g-E<gO!)[QlO'#FqOOQO'#KZ'#KZOOQO1G1u1G1uO&1}Q`O1G1tO&2SQ?MtO,5?[OOOW7+'h7+'hOOOO1G/Z1G/ZO&2^Q!dO1G4xOOQ!0Lh7+(Q7+(QP!&zQMhO,5?^O!,TQMhO7+(cO&2eQ`O,5?]O9eQ`O,5?]O$+YQ`O,5?]OOQO-E<o-E<oO&2sQ`O1G6bO&2sQ`O1G6bO&2{Q`O1G6bO&3WQMjO7+'zO&3hQ!dO,5?_O&3rQ`O,5?_O!&zQMhO,5?_OOQO-E<q-E<qO&3wQ!dO1G6cO&4RQ`O1G6cO&4ZQ`O1G2kO!&zQMhO1G2kOOQ!0Lb1G2i1G2iOOQ!0Lb1G2j1G2jO%4hQpO1G2iO!CUQpO1G2iOCwQ`O1G2iOOQ!0Lb1G2q1G2qO&4`QpO1G2iO&4nQ`O1G2kO$+YQ`O1G2jOCwQ`O1G2jO$$wQlO1G2kO&4vQ`O1G2jO&5jQMjO,5?aOOQ!0Lh-E<t-E<tO&6]QMjO,5?cOOQ!0Lh-E<v-E<vO!,TQMhO7++]O&6gQMjO7++]O&6qQMjO7++]OOQ!0Lh1G/c1G/cO&7OQ`O1G/cOOQ!0Lh7+'u7+'uO&7TQMjO7+'|O&7eQ!0MxO<<KXOOQ!0Lf<<KX<<KXO&8XQ`O1G0zO!&zQMhO'#IzO&8^Q`O,5@xO&:`Q!fO<<LPO!&zQMhO1G2nO&:gQ!0LrO1G2nOOQ[<<G{<<G{O!ByQ!0LrO<<G{O&:xQ!0MxO<<I{OOQ!0Lf<<I{<<I{OOQO,5?l,5?lO&;lQ`O,5?lO&;qQ`O,5?lOOQO-E=O-E=OO&<PQ`O1G6kO&<PQ`O1G6kO9kQ`O1G6kO@zQ`O<<LlOOQ[<<Ll<<LlO&<XQ`O<<LlO9uQ!0LrO<<LlO9kQ`O<<LlOOQ[<<LX<<LXO%:yQ!0MvO<<LXOOQ[<<LY<<LYO!E^Q`O<<LYO&<^QpO'#I|O&<iQ`O,5@|O!)[QlO,5@|OOQ[1G3W1G3WOOQO'#JO'#JOO9uQ!0LrO'#JOO&<qQpO,5=uOOQ[,5=u,5=uO&<xQpO'#EgO&=PQpO'#GeO&=UQ`O7+(zO&=ZQ`O7+(zOOQ[7+(z7+(zO!&zQMhO7+(zO%[QlO7+(zO&=cQ`O7+(zOOQ[7+(|7+(|O9uQ!0LrO7+(|O$%dQ`O7+(|O9`Q`O7+(|O!CUQpO7+(|O&=nQ`O,5?kOOQO-E<}-E<}OOQO'#H^'#H^O&=yQ`O1G6iO9uQ!0LrO<<GqOOQ[<<Gq<<GqO@zQ`O<<GqO&>RQ`O7+,WO&>WQ`O7+,XO%[QlO7+,WO%[QlO7+,XOOQ[7+)V7+)VO&>]Q`O7+)VO&>bQlO7+)VO&>iQ`O7+)VOOQ[<<Ly<<LyOOQ[<<L{<<L{OOQ[-E=Q-E=QOOQ[1G3z1G3zO&>nQ`O,5>aOOQ[,5>c,5>cO&>sQ`O1G4QO9eQ`O7+&fO!)[QlO7+&fOOQO7+%_7+%_O&>xQ?MtO1G6ZO?YQ`O7+%_OOQ!0Lf<<Ia<<IaOOQ!0Lf<<Iz<<IzO?YQ`O<<IzOOQO<<Is<<IsO$AlQ!0MxO<<IsO%[QlO<<IsOOQO<<Id<<IdO!ByQ!0LrO<<IdO&?SQ!0LrO<<IsO&?_Q!0MxO<= ^O&?oQ`O<= ]OOQO7+*_7+*_O9eQ`O7+*_OOQ[ANAkANAkO&?wQ!fOANAkO!&zQMhOANAkO#(ZQ`OANAkO4UQ!fOANAkO&@OQ`OANAkO%[QlOANAkO&@WQ!0MzO7+'zO&BiQ!0MzO,5?aO&DtQ!0MzO,5?cO&GPQ!0MzO7+'|O&IbQ!fO1G4lO&IlQ?MtO7+&aO&KpQ?MvO,5=XO&MwQ?MvO,5=ZO&NXQ?MvO,5=XO&NiQ?MvO,5=ZO&NyQ?MvO,59uO'#PQ?MvO,5<kO'%SQ?MvO,5<mO''hQ?MvO,5<{O')^Q?MtO7+'kO')kQ?MtO7+'mO')xQ`O,5<]OOQO7+'`7+'`OOQ!0Lh7+*d7+*dO')}QMjO<<K}OOQO1G4w1G4wO'*UQ`O1G4wO'*aQ`O1G4wO'*oQ`O7++|O'*oQ`O7++|O!&zQMhO1G4yO'*wQ!dO1G4yO'+RQ`O7++}O'+ZQ`O7+(VO'+fQ!dO7+(VOOQ!0Lb7+(T7+(TOOQ!0Lb7+(U7+(UO!CUQpO7+(TOCwQ`O7+(TO'+pQ`O7+(VO!&zQMhO7+(VO$+YQ`O7+(UO'+uQ`O7+(VOCwQ`O7+(UO'+}QMjO<<NwO!,TQMhO<<NwOOQ!0Lh7+$}7+$}O',XQ!dO,5?fOOQO-E<x-E<xO',cQ!0MvO7+(YO!&zQMhO7+(YOOQ[AN=gAN=gO9kQ`O1G5WOOQO1G5W1G5WO',sQ`O1G5WO',xQ`O7+,VO',xQ`O7+,VO9uQ!0LrOANBWO@zQ`OANBWOOQ[ANBWANBWO'-QQ`OANBWOOQ[ANAsANAsOOQ[ANAtANAtO'-VQ`O,5?hOOQO-E<z-E<zO'-bQ?MtO1G6hOOQO,5?j,5?jOOQO-E<|-E<|OOQ[1G3a1G3aO'-lQ`O,5=POOQ[<<Lf<<LfO!&zQMhO<<LfO&=UQ`O<<LfO'-qQ`O<<LfO%[QlO<<LfOOQ[<<Lh<<LhO9uQ!0LrO<<LhO$%dQ`O<<LhO9`Q`O<<LhO'-yQpO1G5VO'.UQ`O7+,TOOQ[AN=]AN=]O9uQ!0LrOAN=]OOQ[<= r<= rOOQ[<= s<= sO'.^Q`O<= rO'.cQ`O<= sOOQ[<<Lq<<LqO'.hQ`O<<LqO'.mQlO<<LqOOQ[1G3{1G3{O?YQ`O7+)lO'.tQ`O<<JQO'/PQ?MtO<<JQOOQO<<Hy<<HyOOQ!0LfAN?fAN?fOOQOAN?_AN?_O$AlQ!0MxOAN?_OOQOAN?OAN?OO%[QlOAN?_OOQO<<My<<MyOOQ[G27VG27VO!&zQMhOG27VO#(ZQ`OG27VO'/ZQ!fOG27VO4UQ!fOG27VO'/bQ`OG27VO'/jQ?MtO<<JfO'/wQ?MvO1G2`O'1mQ?MvO,5?aO'3pQ?MvO,5?cO'5sQ?MvO1G2sO'7vQ?MvO1G2uO'9yQ?MtO<<KXO':WQ?MtO<<I{OOQO1G1w1G1wO!,TQMhOANAiOOQO7+*c7+*cO':eQ`O7+*cO':pQ`O<= hO':xQ!dO7+*eOOQ!0Lb<<Kq<<KqO$+YQ`O<<KqOCwQ`O<<KqO';SQ`O<<KqO!&zQMhO<<KqOOQ!0Lb<<Ko<<KoO!CUQpO<<KoO';_Q!dO<<KqOOQ!0Lb<<Kp<<KpO';iQ`O<<KqO!&zQMhO<<KqO$+YQ`O<<KpO';nQMjOANDcO';xQ!0MvO<<KtOOQO7+*r7+*rO9kQ`O7+*rO'<YQ`O<= qOOQ[G27rG27rO9uQ!0LrOG27rO@zQ`OG27rO!)[QlO1G5SO'<bQ`O7+,SO'<jQ`O1G2kO&=UQ`OANBQOOQ[ANBQANBQO!&zQMhOANBQO'<oQ`OANBQOOQ[ANBSANBSO9uQ!0LrOANBSO$%dQ`OANBSOOQO'#H_'#H_OOQO7+*q7+*qOOQ[G22wG22wOOQ[ANE^ANE^OOQ[ANE_ANE_OOQ[ANB]ANB]O'<wQ`OANB]OOQ[<<MW<<MWO!)[QlOAN?lOOQOG24yG24yO$AlQ!0MxOG24yO#(ZQ`OLD,qOOQ[LD,qLD,qO!&zQMhOLD,qO'<|Q!fOLD,qO'=TQ?MvO7+'zO'>yQ?MvO,5?aO'@|Q?MvO,5?cO'CPQ?MvO7+'|O'DuQMjOG27TOOQO<<M}<<M}OOQ!0LbANA]ANA]O$+YQ`OANA]OCwQ`OANA]O'EVQ!dOANA]OOQ!0LbANAZANAZO'E^Q`OANA]O!&zQMhOANA]O'EiQ!dOANA]OOQ!0LbANA[ANA[OOQO<<N^<<N^OOQ[LD-^LD-^O9uQ!0LrOLD-^O'EsQ?MtO7+*nOOQO'#Gf'#GfOOQ[G27lG27lO&=UQ`OG27lO!&zQMhOG27lOOQ[G27nG27nO9uQ!0LrOG27nOOQ[G27wG27wO'E}Q?MtOG25WOOQOLD*eLD*eOOQ[!$(!]!$(!]O#(ZQ`O!$(!]O!&zQMhO!$(!]O'FXQ!0MzOG27TOOQ!0LbG26wG26wO$+YQ`OG26wO'HjQ`OG26wOCwQ`OG26wO'HuQ!dOG26wO!&zQMhOG26wOOQ[!$(!x!$(!xOOQ[LD-WLD-WO&=UQ`OLD-WOOQ[LD-YLD-YOOQ[!)9Ew!)9EwO#(ZQ`O!)9EwOOQ!0LbLD,cLD,cO$+YQ`OLD,cOCwQ`OLD,cO'H|Q`OLD,cO'IXQ!dOLD,cOOQ[!$(!r!$(!rOOQ[!.K;c!.K;cO'I`Q?MvOG27TOOQ!0Lb!$( }!$( }O$+YQ`O!$( }OCwQ`O!$( }O'KUQ`O!$( }OOQ!0Lb!)9Ei!)9EiO$+YQ`O!)9EiOCwQ`O!)9EiOOQ!0Lb!.K;T!.K;TO$+YQ`O!.K;TOOQ!0Lb!4/0o!4/0oO!)[QlO'#DzO1PQ`O'#EXO'KaQ!fO'#JrO'KhQ!L^O'#DvO'KoQlO'#EOO'KvQ!fO'#CiO'N^Q!fO'#CiO!)[QlO'#EQO'NnQlO,5;ZO!)[QlO,5;eO!)[QlO,5;eO!)[QlO,5;eO!)[QlO,5;eO!)[QlO,5;eO!)[QlO,5;eO!)[QlO,5;eO!)[QlO,5;eO!)[QlO,5;eO!)[QlO,5;eO!)[QlO'#IpO(!qQ`O,5<iO!)[QlO,5;eO(!yQMhO,5;eO($dQMhO,5;eO!)[QlO,5;wO!&zQMhO'#GmO(!yQMhO'#GmO!&zQMhO'#GoO(!yQMhO'#GoO1SQ`O'#DZO1SQ`O'#DZO!&zQMhO'#GPO(!yQMhO'#GPO!&zQMhO'#GRO(!yQMhO'#GRO!&zQMhO'#GaO(!yQMhO'#GaO!)[QlO,5:jO($kQpO'#D_O($uQpO'#JvO!)[QlO,5@oO'NnQlO1G0uO(%PQ?MtO'#CiO!)[QlO1G2PO!&zQMhO'#IuO(!yQMhO'#IuO!&zQMhO'#IwO(!yQMhO'#IwO(%ZQ!dO'#CrO!&zQMhO,5<tO(!yQMhO,5<tO'NnQlO1G2RO!)[QlO7+&zO!&zQMhO1G2`O(!yQMhO1G2`O!&zQMhO'#IuO(!yQMhO'#IuO!&zQMhO'#IwO(!yQMhO'#IwO!&zQMhO1G2bO(!yQMhO1G2bO'NnQlO7+'mO'NnQlO7+&aO!&zQMhOANAiO(!yQMhOANAiO(%nQ`O'#EoO(%sQ`O'#EoO(%{Q`O'#F]O(&QQ`O'#EyO(&VQ`O'#KTO(&bQ`O'#KRO(&mQ`O,5;ZO(&rQMjO,5<eO(&yQ`O'#GYO('OQ`O'#GYO('TQ`O,5<eO(']Q`O,5<gO('eQ`O,5;ZO('mQ?MtO1G1`O('tQ`O,5<tO('yQ`O,5<tO((OQ`O,5<vO((TQ`O,5<vO((YQ`O1G2RO((_Q`O1G0uO((dQMjO<<K}O((kQMjO<<K}O((rQMhO'#F|O9`Q`O'#F{OAuQ`O'#EnO!)[QlO,5;tO!3oQ`O'#GYO!3oQ`O'#GYO!3oQ`O'#G[O!3oQ`O'#G[O!,TQMhO7+(cO!,TQMhO7+(cO%.zQ!dO1G2wO%.zQ!dO1G2wO!&zQMhO,5=]O!&zQMhO,5=]\",stateData:\"()x~O'|OS'}OSTOS(ORQ~OPYOQYOSfOY!VOaqOdzOeyOl!POpkOrYOskOtkOzkO|YO!OYO!SWO!WkO!XkO!_XO!iuO!lZO!oYO!pYO!qYO!svO!uwO!xxO!|]O$W|O$niO%h}O%j!QO%l!OO%m!OO%n!OO%q!RO%s!SO%v!TO%w!TO%y!UO&W!WO&^!XO&`!YO&b!ZO&d![O&g!]O&m!^O&s!_O&u!`O&w!aO&y!bO&{!cO(TSO(VTO(YUO(aVO(o[O~OWtO~P`OPYOQYOSfOd!jOe!iOpkOrYOskOtkOzkO|YO!OYO!SWO!WkO!XkO!_!eO!iuO!lZO!oYO!pYO!qYO!svO!u!gO!x!hO$W!kO$niO(T!dO(VTO(YUO(aVO(o[O~Oa!wOs!nO!S!oO!b!yO!c!vO!d!vO!|<VO#T!pO#U!pO#V!xO#W!pO#X!pO#[!zO#]!zO(U!lO(VTO(YUO(e!mO(o!sO~O(O!{O~OP]XR]X[]Xa]Xj]Xr]X!Q]X!S]X!]]X!l]X!p]X#R]X#S]X#`]X#kfX#n]X#o]X#p]X#q]X#r]X#s]X#t]X#u]X#v]X#x]X#z]X#{]X$Q]X'z]X(a]X(r]X(y]X(z]X~O!g%RX~P(qO_!}O(V#PO(W!}O(X#PO~O_#QO(X#PO(Y#PO(Z#QO~Ox#SO!U#TO(b#TO(c#VO~OPYOQYOSfOd!jOe!iOpkOrYOskOtkOzkO|YO!OYO!SWO!WkO!XkO!_!eO!iuO!lZO!oYO!pYO!qYO!svO!u!gO!x!hO$W!kO$niO(T<ZO(VTO(YUO(aVO(o[O~O![#ZO!]#WO!Y(hP!Y(vP~P+}O!^#cO~P`OPYOQYOSfOd!jOe!iOrYOskOtkOzkO|YO!OYO!SWO!WkO!XkO!_!eO!iuO!lZO!oYO!pYO!qYO!svO!u!gO!x!hO$W!kO$niO(VTO(YUO(aVO(o[O~Op#mO![#iO!|]O#i#lO#j#iO(T<[O!k(sP~P.iO!l#oO(T#nO~O!x#sO!|]O%h#tO~O#k#uO~O!g#vO#k#uO~OP$[OR#zO[$cOj$ROr$aO!Q#yO!S#{O!]$_O!l#xO!p$[O#R$RO#n$OO#o$PO#p$PO#q$PO#r$QO#s$RO#t$RO#u$bO#v$SO#x$UO#z$WO#{$XO(aVO(r$YO(y#|O(z#}O~Oa(fX'z(fX'w(fX!k(fX!Y(fX!_(fX%i(fX!g(fX~P1qO#S$dO#`$eO$Q$eOP(gXR(gX[(gXj(gXr(gX!Q(gX!S(gX!](gX!l(gX!p(gX#R(gX#n(gX#o(gX#p(gX#q(gX#r(gX#s(gX#t(gX#u(gX#v(gX#x(gX#z(gX#{(gX(a(gX(r(gX(y(gX(z(gX!_(gX%i(gX~Oa(gX'z(gX'w(gX!Y(gX!k(gXv(gX!g(gX~P4UO#`$eO~O$]$hO$_$gO$f$mO~OSfO!_$nO$i$oO$k$qO~Oh%VOj%dOk%dOp%WOr%XOs$tOt$tOz%YO|%ZO!O%]O!S${O!_$|O!i%bO!l$xO#j%cO$W%`O$t%^O$v%_O$y%aO(T$sO(VTO(YUO(a$uO(y$}O(z%POg(^P~Ol%[O~P7eO!l%eO~O!S%hO!_%iO(T%gO~O!g%mO~Oa%nO'z%nO~O!Q%rO~P%[O(U!lO~P%[O%n%vO~P%[Oh%VO!l%eO(T%gO(U!lO~Oe%}O!l%eO(T%gO~Oj$RO~O!_&PO(T%gO(U!lO(VTO(YUO`)WP~O!Q&SO!l&RO%j&VO&T&WO~P;SO!x#sO~O%s&YO!S)SX!_)SX(T)SX~O(T&ZO~Ol!PO!u&`O%j!QO%l!OO%m!OO%n!OO%q!RO%s!SO%v!TO%w!TO~Od&eOe&dO!x&bO%h&cO%{&aO~P<bOd&hOeyOl!PO!_&gO!u&`O!xxO!|]O%h}O%l!OO%m!OO%n!OO%q!RO%s!SO%v!TO%w!TO%y!UO~Ob&kO#`&nO%j&iO(U!lO~P=gO!l&oO!u&sO~O!l#oO~O!_XO~Oa%nO'x&{O'z%nO~Oa%nO'x'OO'z%nO~Oa%nO'x'QO'z%nO~O'w]X!Y]Xv]X!k]X&[]X!_]X%i]X!g]X~P(qO!b'_O!c'WO!d'WO(U!lO(VTO(YUO~Os'UO!S'TO!['XO(e'SO!^(iP!^(xP~P@nOn'bO!_'`O(T%gO~Oe'gO!l%eO(T%gO~O!Q&SO!l&RO~Os!nO!S!oO!|<VO#T!pO#U!pO#W!pO#X!pO(U!lO(VTO(YUO(e!mO(o!sO~O!b'mO!c'lO!d'lO#V!pO#['nO#]'nO~PBYOa%nOh%VO!g#vO!l%eO'z%nO(r'pO~O!p'tO#`'rO~PChOs!nO!S!oO(VTO(YUO(e!mO(o!sO~O!_XOs(mX!S(mX!b(mX!c(mX!d(mX!|(mX#T(mX#U(mX#V(mX#W(mX#X(mX#[(mX#](mX(U(mX(V(mX(Y(mX(e(mX(o(mX~O!c'lO!d'lO(U!lO~PDWO(P'xO(Q'xO(R'zO~O_!}O(V'|O(W!}O(X'|O~O_#QO(X'|O(Y'|O(Z#QO~Ov(OO~P%[Ox#SO!U#TO(b#TO(c(RO~O![(TO!Y'WX!Y'^X!]'WX!]'^X~P+}O!](VO!Y(hX~OP$[OR#zO[$cOj$ROr$aO!Q#yO!S#{O!](VO!l#xO!p$[O#R$RO#n$OO#o$PO#p$PO#q$PO#r$QO#s$RO#t$RO#u$bO#v$SO#x$UO#z$WO#{$XO(aVO(r$YO(y#|O(z#}O~O!Y(hX~PHRO!Y([O~O!Y(uX!](uX!g(uX!k(uX(r(uX~O#`(uX#k#dX!^(uX~PJUO#`(]O!Y(wX!](wX~O!](^O!Y(vX~O!Y(aO~O#`$eO~PJUO!^(bO~P`OR#zO!Q#yO!S#{O!l#xO(aVOP!na[!naj!nar!na!]!na!p!na#R!na#n!na#o!na#p!na#q!na#r!na#s!na#t!na#u!na#v!na#x!na#z!na#{!na(r!na(y!na(z!na~Oa!na'z!na'w!na!Y!na!k!nav!na!_!na%i!na!g!na~PKlO!k(cO~O!g#vO#`(dO(r'pO!](tXa(tX'z(tX~O!k(tX~PNXO!S%hO!_%iO!|]O#i(iO#j(hO(T%gO~O!](jO!k(sX~O!k(lO~O!S%hO!_%iO#j(hO(T%gO~OP(gXR(gX[(gXj(gXr(gX!Q(gX!S(gX!](gX!l(gX!p(gX#R(gX#n(gX#o(gX#p(gX#q(gX#r(gX#s(gX#t(gX#u(gX#v(gX#x(gX#z(gX#{(gX(a(gX(r(gX(y(gX(z(gX~O!g#vO!k(gX~P! uOR(nO!Q(mO!l#xO#S$dO!|!{a!S!{a~O!x!{a%h!{a!_!{a#i!{a#j!{a(T!{a~P!#vO!x(rO~OPYOQYOSfOd!jOe!iOpkOrYOskOtkOzkO|YO!OYO!SWO!WkO!XkO!_XO!iuO!lZO!oYO!pYO!qYO!svO!u!gO!x!hO$W!kO$niO(T!dO(VTO(YUO(aVO(o[O~Oh%VOp%WOr%XOs$tOt$tOz%YO|%ZO!O<sO!S${O!_$|O!i>VO!l$xO#j<yO$W%`O$t<uO$v<wO$y%aO(T(vO(VTO(YUO(a$uO(y$}O(z%PO~O#k(xO~O![(zO!k(kP~P%[O(e(|O(o[O~O!S)OO!l#xO(e(|O(o[O~OP<UOQ<UOSfOd>ROe!iOpkOr<UOskOtkOzkO|<UO!O<UO!SWO!WkO!XkO!_!eO!i<XO!lZO!o<UO!p<UO!q<UO!s<YO!u<]O!x!hO$W!kO$n>PO(T)]O(VTO(YUO(aVO(o[O~O!]$_Oa$qa'z$qa'w$qa!k$qa!Y$qa!_$qa%i$qa!g$qa~Ol)dO~P!&zOh%VOp%WOr%XOs$tOt$tOz%YO|%ZO!O%]O!S${O!_$|O!i%bO!l$xO#j%cO$W%`O$t%^O$v%_O$y%aO(T(vO(VTO(YUO(a$uO(y$}O(z%PO~Og(pP~P!,TO!Q)iO!g)hO!_$^X$Z$^X$]$^X$_$^X$f$^X~O!g)hO!_({X$Z({X$]({X$_({X$f({X~O!Q)iO~P!.^O!Q)iO!_({X$Z({X$]({X$_({X$f({X~O!_)kO$Z)oO$])jO$_)jO$f)pO~O![)sO~P!)[O$]$hO$_$gO$f)wO~On$zX!Q$zX#S$zX'y$zX(y$zX(z$zX~OgmXg$zXnmX!]mX#`mX~P!0SOx)yO(b)zO(c)|O~On*VO!Q*OO'y*PO(y$}O(z%PO~Og)}O~P!1WOg*WO~Oh%VOr%XOs$tOt$tOz%YO|%ZO!O<sO!S*YO!_*ZO!i>VO!l$xO#j<yO$W%`O$t<uO$v<wO$y%aO(VTO(YUO(a$uO(y$}O(z%PO~Op*`O![*^O(T*XO!k)OP~P!1uO#k*aO~O!l*bO~Oh%VOp%WOr%XOs$tOt$tOz%YO|%ZO!O<sO!S${O!_$|O!i>VO!l$xO#j<yO$W%`O$t<uO$v<wO$y%aO(T*dO(VTO(YUO(a$uO(y$}O(z%PO~O![*gO!Y)PP~P!3tOr*sOs!nO!S*iO!b*qO!c*kO!d*kO!l*bO#[*rO%`*mO(U!lO(VTO(YUO(e!mO~O!^*pO~P!5iO#S$dOn(`X!Q(`X'y(`X(y(`X(z(`X!](`X#`(`X~Og(`X$O(`X~P!6kOn*xO#`*wOg(_X!](_X~O!]*yOg(^X~Oj%dOk%dOl%dO(T&ZOg(^P~Os*|O~Og)}O(T&ZO~O!l+SO~O(T(vO~Op+WO!S%hO![#iO!_%iO!|]O#i#lO#j#iO(T%gO!k(sP~O!g#vO#k+XO~O!S%hO![+ZO!](^O!_%iO(T%gO!Y(vP~Os'[O!S+]O![+[O(VTO(YUO(e(|O~O!^(xP~P!9|O!]+^Oa)TX'z)TX~OP$[OR#zO[$cOj$ROr$aO!Q#yO!S#{O!l#xO!p$[O#R$RO#n$OO#o$PO#p$PO#q$PO#r$QO#s$RO#t$RO#u$bO#v$SO#x$UO#z$WO#{$XO(aVO(r$YO(y#|O(z#}O~Oa!ja!]!ja'z!ja'w!ja!Y!ja!k!jav!ja!_!ja%i!ja!g!ja~P!:tOR#zO!Q#yO!S#{O!l#xO(aVOP!ra[!raj!rar!ra!]!ra!p!ra#R!ra#n!ra#o!ra#p!ra#q!ra#r!ra#s!ra#t!ra#u!ra#v!ra#x!ra#z!ra#{!ra(r!ra(y!ra(z!ra~Oa!ra'z!ra'w!ra!Y!ra!k!rav!ra!_!ra%i!ra!g!ra~P!=[OR#zO!Q#yO!S#{O!l#xO(aVOP!ta[!taj!tar!ta!]!ta!p!ta#R!ta#n!ta#o!ta#p!ta#q!ta#r!ta#s!ta#t!ta#u!ta#v!ta#x!ta#z!ta#{!ta(r!ta(y!ta(z!ta~Oa!ta'z!ta'w!ta!Y!ta!k!tav!ta!_!ta%i!ta!g!ta~P!?rOh%VOn+gO!_'`O%i+fO~O!g+iOa(]X!_(]X'z(]X!](]X~Oa%nO!_XO'z%nO~Oh%VO!l%eO~Oh%VO!l%eO(T%gO~O!g#vO#k(xO~Ob+tO%j+uO(T+qO(VTO(YUO!^)XP~O!]+vO`)WX~O[+zO~O`+{O~O!_&PO(T%gO(U!lO`)WP~O%j,OO~P;SOh%VO#`,SO~Oh%VOn,VO!_$|O~O!_,XO~O!Q,ZO!_XO~O%n%vO~O!x,`O~Oe,eO~Ob,fO(T#nO(VTO(YUO!^)VP~Oe%}O~O%j!QO(T&ZO~P=gO[,kO`,jO~OPYOQYOSfOdzOeyOpkOrYOskOtkOzkO|YO!OYO!SWO!WkO!XkO!iuO!lZO!oYO!pYO!qYO!svO!xxO!|]O$niO%h}O(VTO(YUO(aVO(o[O~O!_!eO!u!gO$W!kO(T!dO~P!FyO`,jOa%nO'z%nO~OPYOQYOSfOd!jOe!iOpkOrYOskOtkOzkO|YO!OYO!SWO!WkO!XkO!_!eO!iuO!lZO!oYO!pYO!qYO!svO!x!hO$W!kO$niO(T!dO(VTO(YUO(aVO(o[O~Oa,pOl!OO!uwO%l!OO%m!OO%n!OO~P!IcO!l&oO~O&^,vO~O!_,xO~O&o,zO&q,{OP&laQ&laS&laY&laa&lad&lae&lal&lap&lar&las&lat&laz&la|&la!O&la!S&la!W&la!X&la!_&la!i&la!l&la!o&la!p&la!q&la!s&la!u&la!x&la!|&la$W&la$n&la%h&la%j&la%l&la%m&la%n&la%q&la%s&la%v&la%w&la%y&la&W&la&^&la&`&la&b&la&d&la&g&la&m&la&s&la&u&la&w&la&y&la&{&la'w&la(T&la(V&la(Y&la(a&la(o&la!^&la&e&lab&la&j&la~O(T-QO~Oh!eX!]!RX!^!RX!g!RX!g!eX!l!eX#`!RX~O!]!eX!^!eX~P#!iO!g-VO#`-UOh(jX!]#hX!^#hX!g(jX!l(jX~O!](jX!^(jX~P##[Oh%VO!g-XO!l%eO!]!aX!^!aX~Os!nO!S!oO(VTO(YUO(e!mO~OP<UOQ<UOSfOd>ROe!iOpkOr<UOskOtkOzkO|<UO!O<UO!SWO!WkO!XkO!_!eO!i<XO!lZO!o<UO!p<UO!q<UO!s<YO!u<]O!x!hO$W!kO$n>PO(VTO(YUO(aVO(o[O~O(T=QO~P#$qO!]-]O!^(iX~O!^-_O~O!g-VO#`-UO!]#hX!^#hX~O!]-`O!^(xX~O!^-bO~O!c-cO!d-cO(U!lO~P#$`O!^-fO~P'_On-iO!_'`O~O!Y-nO~Os!{a!b!{a!c!{a!d!{a#T!{a#U!{a#V!{a#W!{a#X!{a#[!{a#]!{a(U!{a(V!{a(Y!{a(e!{a(o!{a~P!#vO!p-sO#`-qO~PChO!c-uO!d-uO(U!lO~PDWOa%nO#`-qO'z%nO~Oa%nO!g#vO#`-qO'z%nO~Oa%nO!g#vO!p-sO#`-qO'z%nO(r'pO~O(P'xO(Q'xO(R-zO~Ov-{O~O!Y'Wa!]'Wa~P!:tO![.PO!Y'WX!]'WX~P%[O!](VO!Y(ha~O!Y(ha~PHRO!](^O!Y(va~O!S%hO![.TO!_%iO(T%gO!Y'^X!]'^X~O#`.VO!](ta!k(taa(ta'z(ta~O!g#vO~P#,wO!](jO!k(sa~O!S%hO!_%iO#j.ZO(T%gO~Op.`O!S%hO![.]O!_%iO!|]O#i._O#j.]O(T%gO!]'aX!k'aX~OR.dO!l#xO~Oh%VOn.gO!_'`O%i.fO~Oa#ci!]#ci'z#ci'w#ci!Y#ci!k#civ#ci!_#ci%i#ci!g#ci~P!:tOn>]O!Q*OO'y*PO(y$}O(z%PO~O#k#_aa#_a#`#_a'z#_a!]#_a!k#_a!_#_a!Y#_a~P#/sO#k(`XP(`XR(`X[(`Xa(`Xj(`Xr(`X!S(`X!l(`X!p(`X#R(`X#n(`X#o(`X#p(`X#q(`X#r(`X#s(`X#t(`X#u(`X#v(`X#x(`X#z(`X#{(`X'z(`X(a(`X(r(`X!k(`X!Y(`X'w(`Xv(`X!_(`X%i(`X!g(`X~P!6kO!].tO!k(kX~P!:tO!k.wO~O!Y.yO~OP$[OR#zO!Q#yO!S#{O!l#xO!p$[O(aVO[#mia#mij#mir#mi!]#mi#R#mi#o#mi#p#mi#q#mi#r#mi#s#mi#t#mi#u#mi#v#mi#x#mi#z#mi#{#mi'z#mi(r#mi(y#mi(z#mi'w#mi!Y#mi!k#miv#mi!_#mi%i#mi!g#mi~O#n#mi~P#3cO#n$OO~P#3cOP$[OR#zOr$aO!Q#yO!S#{O!l#xO!p$[O#n$OO#o$PO#p$PO#q$PO(aVO[#mia#mij#mi!]#mi#R#mi#s#mi#t#mi#u#mi#v#mi#x#mi#z#mi#{#mi'z#mi(r#mi(y#mi(z#mi'w#mi!Y#mi!k#miv#mi!_#mi%i#mi!g#mi~O#r#mi~P#6QO#r$QO~P#6QOP$[OR#zO[$cOj$ROr$aO!Q#yO!S#{O!l#xO!p$[O#R$RO#n$OO#o$PO#p$PO#q$PO#r$QO#s$RO#t$RO#u$bO(aVOa#mi!]#mi#x#mi#z#mi#{#mi'z#mi(r#mi(y#mi(z#mi'w#mi!Y#mi!k#miv#mi!_#mi%i#mi!g#mi~O#v#mi~P#8oOP$[OR#zO[$cOj$ROr$aO!Q#yO!S#{O!l#xO!p$[O#R$RO#n$OO#o$PO#p$PO#q$PO#r$QO#s$RO#t$RO#u$bO#v$SO(aVO(z#}Oa#mi!]#mi#z#mi#{#mi'z#mi(r#mi(y#mi'w#mi!Y#mi!k#miv#mi!_#mi%i#mi!g#mi~O#x$UO~P#;VO#x#mi~P#;VO#v$SO~P#8oOP$[OR#zO[$cOj$ROr$aO!Q#yO!S#{O!l#xO!p$[O#R$RO#n$OO#o$PO#p$PO#q$PO#r$QO#s$RO#t$RO#u$bO#v$SO#x$UO(aVO(y#|O(z#}Oa#mi!]#mi#{#mi'z#mi(r#mi'w#mi!Y#mi!k#miv#mi!_#mi%i#mi!g#mi~O#z#mi~P#={O#z$WO~P#={OP]XR]X[]Xj]Xr]X!Q]X!S]X!l]X!p]X#R]X#S]X#`]X#kfX#n]X#o]X#p]X#q]X#r]X#s]X#t]X#u]X#v]X#x]X#z]X#{]X$Q]X(a]X(r]X(y]X(z]X!]]X!^]X~O$O]X~P#@jOP$[OR#zO[<mOj<bOr<kO!Q#yO!S#{O!l#xO!p$[O#R<bO#n<_O#o<`O#p<`O#q<`O#r<aO#s<bO#t<bO#u<lO#v<cO#x<eO#z<gO#{<hO(aVO(r$YO(y#|O(z#}O~O$O.{O~P#BwO#S$dO#`<nO$Q<nO$O(gX!^(gX~P! uOa'da!]'da'z'da'w'da!k'da!Y'dav'da!_'da%i'da!g'da~P!:tO[#mia#mij#mir#mi!]#mi#R#mi#r#mi#s#mi#t#mi#u#mi#v#mi#x#mi#z#mi#{#mi'z#mi(r#mi'w#mi!Y#mi!k#miv#mi!_#mi%i#mi!g#mi~OP$[OR#zO!Q#yO!S#{O!l#xO!p$[O#n$OO#o$PO#p$PO#q$PO(aVO(y#mi(z#mi~P#EyOn>]O!Q*OO'y*PO(y$}O(z%POP#miR#mi!S#mi!l#mi!p#mi#n#mi#o#mi#p#mi#q#mi(a#mi~P#EyO!]/POg(pX~P!1WOg/RO~Oa$Pi!]$Pi'z$Pi'w$Pi!Y$Pi!k$Piv$Pi!_$Pi%i$Pi!g$Pi~P!:tO$]/SO$_/SO~O$]/TO$_/TO~O!g)hO#`/UO!_$cX$Z$cX$]$cX$_$cX$f$cX~O![/VO~O!_)kO$Z/XO$])jO$_)jO$f/YO~O!]<iO!^(fX~P#BwO!^/ZO~O!g)hO$f({X~O$f/]O~Ov/^O~P!&zOx)yO(b)zO(c/aO~O!S/dO~O(y$}On%aa!Q%aa'y%aa(z%aa!]%aa#`%aa~Og%aa$O%aa~P#L{O(z%POn%ca!Q%ca'y%ca(y%ca!]%ca#`%ca~Og%ca$O%ca~P#MnO!]fX!gfX!kfX!k$zX(rfX~P!0SOp%WO![/mO!](^O(T/lO!Y(vP!Y)PP~P!1uOr*sO!b*qO!c*kO!d*kO!l*bO#[*rO%`*mO(U!lO(VTO(YUO~Os<}O!S/nO![+[O!^*pO(e<|O!^(xP~P$ [O!k/oO~P#/sO!]/pO!g#vO(r'pO!k)OX~O!k/uO~OnoX!QoX'yoX(yoX(zoX~O!g#vO!koX~P$#OOp/wO!S%hO![*^O!_%iO(T%gO!k)OP~O#k/xO~O!Y$zX!]$zX!g%RX~P!0SO!]/yO!Y)PX~P#/sO!g/{O~O!Y/}O~OpkO(T0OO~P.iOh%VOr0TO!g#vO!l%eO(r'pO~O!g+iO~Oa%nO!]0XO'z%nO~O!^0ZO~P!5iO!c0[O!d0[O(U!lO~P#$`Os!nO!S0]O(VTO(YUO(e!mO~O#[0_O~Og%aa!]%aa#`%aa$O%aa~P!1WOg%ca!]%ca#`%ca$O%ca~P!1WOj%dOk%dOl%dO(T&ZOg'mX!]'mX~O!]*yOg(^a~Og0hO~On0jO#`0iOg(_a!](_a~OR0kO!Q0kO!S0lO#S$dOn}a'y}a(y}a(z}a!]}a#`}a~Og}a$O}a~P$(cO!Q*OO'y*POn$sa(y$sa(z$sa!]$sa#`$sa~Og$sa$O$sa~P$)_O!Q*OO'y*POn$ua(y$ua(z$ua!]$ua#`$ua~Og$ua$O$ua~P$*QO#k0oO~Og%Ta!]%Ta#`%Ta$O%Ta~P!1WO!g#vO~O#k0rO~O!]+^Oa)Ta'z)Ta~OR#zO!Q#yO!S#{O!l#xO(aVOP!ri[!rij!rir!ri!]!ri!p!ri#R!ri#n!ri#o!ri#p!ri#q!ri#r!ri#s!ri#t!ri#u!ri#v!ri#x!ri#z!ri#{!ri(r!ri(y!ri(z!ri~Oa!ri'z!ri'w!ri!Y!ri!k!riv!ri!_!ri%i!ri!g!ri~P$+oOh%VOr%XOs$tOt$tOz%YO|%ZO!O<sO!S${O!_$|O!i>VO!l$xO#j<yO$W%`O$t<uO$v<wO$y%aO(VTO(YUO(a$uO(y$}O(z%PO~Op0{O%]0|O(T0zO~P$.VO!g+iOa(]a!_(]a'z(]a!](]a~O#k1SO~O[]X!]fX!^fX~O!]1TO!^)XX~O!^1VO~O[1WO~Ob1YO(T+qO(VTO(YUO~O!_&PO(T%gO`'uX!]'uX~O!]+vO`)Wa~O!k1]O~P!:tO[1`O~O`1aO~O#`1fO~On1iO!_$|O~O(e(|O!^)UP~Oh%VOn1rO!_1oO%i1qO~O[1|O!]1zO!^)VX~O!^1}O~O`2POa%nO'z%nO~O(T#nO(VTO(YUO~O#S$dO#`$eO$Q$eOP(gXR(gX[(gXr(gX!Q(gX!S(gX!](gX!l(gX!p(gX#R(gX#n(gX#o(gX#p(gX#q(gX#r(gX#s(gX#t(gX#u(gX#v(gX#x(gX#z(gX#{(gX(a(gX(r(gX(y(gX(z(gX~Oj2SO&[2TOa(gX~P$3pOj2SO#`$eO&[2TO~Oa2VO~P%[Oa2XO~O&e2[OP&ciQ&ciS&ciY&cia&cid&cie&cil&cip&cir&cis&cit&ciz&ci|&ci!O&ci!S&ci!W&ci!X&ci!_&ci!i&ci!l&ci!o&ci!p&ci!q&ci!s&ci!u&ci!x&ci!|&ci$W&ci$n&ci%h&ci%j&ci%l&ci%m&ci%n&ci%q&ci%s&ci%v&ci%w&ci%y&ci&W&ci&^&ci&`&ci&b&ci&d&ci&g&ci&m&ci&s&ci&u&ci&w&ci&y&ci&{&ci'w&ci(T&ci(V&ci(Y&ci(a&ci(o&ci!^&cib&ci&j&ci~Ob2bO!^2`O&j2aO~P`O!_XO!l2dO~O&q,{OP&liQ&liS&liY&lia&lid&lie&lil&lip&lir&lis&lit&liz&li|&li!O&li!S&li!W&li!X&li!_&li!i&li!l&li!o&li!p&li!q&li!s&li!u&li!x&li!|&li$W&li$n&li%h&li%j&li%l&li%m&li%n&li%q&li%s&li%v&li%w&li%y&li&W&li&^&li&`&li&b&li&d&li&g&li&m&li&s&li&u&li&w&li&y&li&{&li'w&li(T&li(V&li(Y&li(a&li(o&li!^&li&e&lib&li&j&li~O!Y2jO~O!]!aa!^!aa~P#BwOs!nO!S!oO![2pO(e!mO!]'XX!^'XX~P@nO!]-]O!^(ia~O!]'_X!^'_X~P!9|O!]-`O!^(xa~O!^2wO~P'_Oa%nO#`3QO'z%nO~Oa%nO!g#vO#`3QO'z%nO~Oa%nO!g#vO!p3UO#`3QO'z%nO(r'pO~Oa%nO'z%nO~P!:tO!]$_Ov$qa~O!Y'Wi!]'Wi~P!:tO!](VO!Y(hi~O!](^O!Y(vi~O!Y(wi!](wi~P!:tO!](ti!k(tia(ti'z(ti~P!:tO#`3WO!](ti!k(tia(ti'z(ti~O!](jO!k(si~O!S%hO!_%iO!|]O#i3]O#j3[O(T%gO~O!S%hO!_%iO#j3[O(T%gO~On3dO!_'`O%i3cO~Oh%VOn3dO!_'`O%i3cO~O#k%aaP%aaR%aa[%aaa%aaj%aar%aa!S%aa!l%aa!p%aa#R%aa#n%aa#o%aa#p%aa#q%aa#r%aa#s%aa#t%aa#u%aa#v%aa#x%aa#z%aa#{%aa'z%aa(a%aa(r%aa!k%aa!Y%aa'w%aav%aa!_%aa%i%aa!g%aa~P#L{O#k%caP%caR%ca[%caa%caj%car%ca!S%ca!l%ca!p%ca#R%ca#n%ca#o%ca#p%ca#q%ca#r%ca#s%ca#t%ca#u%ca#v%ca#x%ca#z%ca#{%ca'z%ca(a%ca(r%ca!k%ca!Y%ca'w%cav%ca!_%ca%i%ca!g%ca~P#MnO#k%aaP%aaR%aa[%aaa%aaj%aar%aa!S%aa!]%aa!l%aa!p%aa#R%aa#n%aa#o%aa#p%aa#q%aa#r%aa#s%aa#t%aa#u%aa#v%aa#x%aa#z%aa#{%aa'z%aa(a%aa(r%aa!k%aa!Y%aa'w%aa#`%aav%aa!_%aa%i%aa!g%aa~P#/sO#k%caP%caR%ca[%caa%caj%car%ca!S%ca!]%ca!l%ca!p%ca#R%ca#n%ca#o%ca#p%ca#q%ca#r%ca#s%ca#t%ca#u%ca#v%ca#x%ca#z%ca#{%ca'z%ca(a%ca(r%ca!k%ca!Y%ca'w%ca#`%cav%ca!_%ca%i%ca!g%ca~P#/sO#k}aP}a[}aa}aj}ar}a!l}a!p}a#R}a#n}a#o}a#p}a#q}a#r}a#s}a#t}a#u}a#v}a#x}a#z}a#{}a'z}a(a}a(r}a!k}a!Y}a'w}av}a!_}a%i}a!g}a~P$(cO#k$saP$saR$sa[$saa$saj$sar$sa!S$sa!l$sa!p$sa#R$sa#n$sa#o$sa#p$sa#q$sa#r$sa#s$sa#t$sa#u$sa#v$sa#x$sa#z$sa#{$sa'z$sa(a$sa(r$sa!k$sa!Y$sa'w$sav$sa!_$sa%i$sa!g$sa~P$)_O#k$uaP$uaR$ua[$uaa$uaj$uar$ua!S$ua!l$ua!p$ua#R$ua#n$ua#o$ua#p$ua#q$ua#r$ua#s$ua#t$ua#u$ua#v$ua#x$ua#z$ua#{$ua'z$ua(a$ua(r$ua!k$ua!Y$ua'w$uav$ua!_$ua%i$ua!g$ua~P$*QO#k%TaP%TaR%Ta[%Taa%Taj%Tar%Ta!S%Ta!]%Ta!l%Ta!p%Ta#R%Ta#n%Ta#o%Ta#p%Ta#q%Ta#r%Ta#s%Ta#t%Ta#u%Ta#v%Ta#x%Ta#z%Ta#{%Ta'z%Ta(a%Ta(r%Ta!k%Ta!Y%Ta'w%Ta#`%Tav%Ta!_%Ta%i%Ta!g%Ta~P#/sOa#cq!]#cq'z#cq'w#cq!Y#cq!k#cqv#cq!_#cq%i#cq!g#cq~P!:tO![3lO!]'YX!k'YX~P%[O!].tO!k(ka~O!].tO!k(ka~P!:tO!Y3oO~O$O!na!^!na~PKlO$O!ja!]!ja!^!ja~P#BwO$O!ra!^!ra~P!=[O$O!ta!^!ta~P!?rOg']X!]']X~P!,TO!]/POg(pa~OSfO!_4TO$d4UO~O!^4YO~Ov4ZO~P#/sOa$mq!]$mq'z$mq'w$mq!Y$mq!k$mqv$mq!_$mq%i$mq!g$mq~P!:tO!Y4]O~P!&zO!S4^O~O!Q*OO'y*PO(z%POn'ia(y'ia!]'ia#`'ia~Og'ia$O'ia~P%-fO!Q*OO'y*POn'ka(y'ka(z'ka!]'ka#`'ka~Og'ka$O'ka~P%.XO(r$YO~P#/sO!YfX!Y$zX!]fX!]$zX!g%RX#`fX~P!0SOp%WO(T=WO~P!1uOp4bO!S%hO![4aO!_%iO(T%gO!]'eX!k'eX~O!]/pO!k)Oa~O!]/pO!g#vO!k)Oa~O!]/pO!g#vO(r'pO!k)Oa~Og$|i!]$|i#`$|i$O$|i~P!1WO![4jO!Y'gX!]'gX~P!3tO!]/yO!Y)Pa~O!]/yO!Y)Pa~P#/sOP]XR]X[]Xj]Xr]X!Q]X!S]X!Y]X!]]X!l]X!p]X#R]X#S]X#`]X#kfX#n]X#o]X#p]X#q]X#r]X#s]X#t]X#u]X#v]X#x]X#z]X#{]X$Q]X(a]X(r]X(y]X(z]X~Oj%YX!g%YX~P%2OOj4oO!g#vO~Oh%VO!g#vO!l%eO~Oh%VOr4tO!l%eO(r'pO~Or4yO!g#vO(r'pO~Os!nO!S4zO(VTO(YUO(e!mO~O(y$}On%ai!Q%ai'y%ai(z%ai!]%ai#`%ai~Og%ai$O%ai~P%5oO(z%POn%ci!Q%ci'y%ci(y%ci!]%ci#`%ci~Og%ci$O%ci~P%6bOg(_i!](_i~P!1WO#`5QOg(_i!](_i~P!1WO!k5VO~Oa$oq!]$oq'z$oq'w$oq!Y$oq!k$oqv$oq!_$oq%i$oq!g$oq~P!:tO!Y5ZO~O!]5[O!_)QX~P#/sOa$zX!_$zX%^]X'z$zX!]$zX~P!0SO%^5_OaoX!_oX'zoX!]oX~P$#OOp5`O(T#nO~O%^5_O~Ob5fO%j5gO(T+qO(VTO(YUO!]'tX!^'tX~O!]1TO!^)Xa~O[5kO~O`5lO~O[5pO~Oa%nO'z%nO~P#/sO!]5uO#`5wO!^)UX~O!^5xO~Or6OOs!nO!S*iO!b!yO!c!vO!d!vO!|<VO#T!pO#U!pO#V!pO#W!pO#X!pO#[5}O#]!zO(U!lO(VTO(YUO(e!mO(o!sO~O!^5|O~P%;eOn6TO!_1oO%i6SO~Oh%VOn6TO!_1oO%i6SO~Ob6[O(T#nO(VTO(YUO!]'sX!^'sX~O!]1zO!^)Va~O(VTO(YUO(e6^O~O`6bO~Oj6eO&[6fO~PNXO!k6gO~P%[Oa6iO~Oa6iO~P%[Ob2bO!^6nO&j2aO~P`O!g6pO~O!g6rOh(ji!](ji!^(ji!g(ji!l(jir(ji(r(ji~O!]#hi!^#hi~P#BwO#`6sO!]#hi!^#hi~O!]!ai!^!ai~P#BwOa%nO#`6|O'z%nO~Oa%nO!g#vO#`6|O'z%nO~O!](tq!k(tqa(tq'z(tq~P!:tO!](jO!k(sq~O!S%hO!_%iO#j7TO(T%gO~O!_'`O%i7WO~On7[O!_'`O%i7WO~O#k'iaP'iaR'ia['iaa'iaj'iar'ia!S'ia!l'ia!p'ia#R'ia#n'ia#o'ia#p'ia#q'ia#r'ia#s'ia#t'ia#u'ia#v'ia#x'ia#z'ia#{'ia'z'ia(a'ia(r'ia!k'ia!Y'ia'w'iav'ia!_'ia%i'ia!g'ia~P%-fO#k'kaP'kaR'ka['kaa'kaj'kar'ka!S'ka!l'ka!p'ka#R'ka#n'ka#o'ka#p'ka#q'ka#r'ka#s'ka#t'ka#u'ka#v'ka#x'ka#z'ka#{'ka'z'ka(a'ka(r'ka!k'ka!Y'ka'w'kav'ka!_'ka%i'ka!g'ka~P%.XO#k$|iP$|iR$|i[$|ia$|ij$|ir$|i!S$|i!]$|i!l$|i!p$|i#R$|i#n$|i#o$|i#p$|i#q$|i#r$|i#s$|i#t$|i#u$|i#v$|i#x$|i#z$|i#{$|i'z$|i(a$|i(r$|i!k$|i!Y$|i'w$|i#`$|iv$|i!_$|i%i$|i!g$|i~P#/sO#k%aiP%aiR%ai[%aia%aij%air%ai!S%ai!l%ai!p%ai#R%ai#n%ai#o%ai#p%ai#q%ai#r%ai#s%ai#t%ai#u%ai#v%ai#x%ai#z%ai#{%ai'z%ai(a%ai(r%ai!k%ai!Y%ai'w%aiv%ai!_%ai%i%ai!g%ai~P%5oO#k%ciP%ciR%ci[%cia%cij%cir%ci!S%ci!l%ci!p%ci#R%ci#n%ci#o%ci#p%ci#q%ci#r%ci#s%ci#t%ci#u%ci#v%ci#x%ci#z%ci#{%ci'z%ci(a%ci(r%ci!k%ci!Y%ci'w%civ%ci!_%ci%i%ci!g%ci~P%6bO!]'Ya!k'Ya~P!:tO!].tO!k(ki~O$O#ci!]#ci!^#ci~P#BwOP$[OR#zO!Q#yO!S#{O!l#xO!p$[O(aVO[#mij#mir#mi#R#mi#o#mi#p#mi#q#mi#r#mi#s#mi#t#mi#u#mi#v#mi#x#mi#z#mi#{#mi$O#mi(r#mi(y#mi(z#mi!]#mi!^#mi~O#n#mi~P%NdO#n<_O~P%NdOP$[OR#zOr<kO!Q#yO!S#{O!l#xO!p$[O#n<_O#o<`O#p<`O#q<`O(aVO[#mij#mi#R#mi#s#mi#t#mi#u#mi#v#mi#x#mi#z#mi#{#mi$O#mi(r#mi(y#mi(z#mi!]#mi!^#mi~O#r#mi~P&!lO#r<aO~P&!lOP$[OR#zO[<mOj<bOr<kO!Q#yO!S#{O!l#xO!p$[O#R<bO#n<_O#o<`O#p<`O#q<`O#r<aO#s<bO#t<bO#u<lO(aVO#x#mi#z#mi#{#mi$O#mi(r#mi(y#mi(z#mi!]#mi!^#mi~O#v#mi~P&$tOP$[OR#zO[<mOj<bOr<kO!Q#yO!S#{O!l#xO!p$[O#R<bO#n<_O#o<`O#p<`O#q<`O#r<aO#s<bO#t<bO#u<lO#v<cO(aVO(z#}O#z#mi#{#mi$O#mi(r#mi(y#mi!]#mi!^#mi~O#x<eO~P&&uO#x#mi~P&&uO#v<cO~P&$tOP$[OR#zO[<mOj<bOr<kO!Q#yO!S#{O!l#xO!p$[O#R<bO#n<_O#o<`O#p<`O#q<`O#r<aO#s<bO#t<bO#u<lO#v<cO#x<eO(aVO(y#|O(z#}O#{#mi$O#mi(r#mi!]#mi!^#mi~O#z#mi~P&)UO#z<gO~P&)UOa#|y!]#|y'z#|y'w#|y!Y#|y!k#|yv#|y!_#|y%i#|y!g#|y~P!:tO[#mij#mir#mi#R#mi#r#mi#s#mi#t#mi#u#mi#v#mi#x#mi#z#mi#{#mi$O#mi(r#mi!]#mi!^#mi~OP$[OR#zO!Q#yO!S#{O!l#xO!p$[O#n<_O#o<`O#p<`O#q<`O(aVO(y#mi(z#mi~P&,QOn>^O!Q*OO'y*PO(y$}O(z%POP#miR#mi!S#mi!l#mi!p#mi#n#mi#o#mi#p#mi#q#mi(a#mi~P&,QO#S$dOP(`XR(`X[(`Xj(`Xn(`Xr(`X!Q(`X!S(`X!l(`X!p(`X#R(`X#n(`X#o(`X#p(`X#q(`X#r(`X#s(`X#t(`X#u(`X#v(`X#x(`X#z(`X#{(`X$O(`X'y(`X(a(`X(r(`X(y(`X(z(`X!](`X!^(`X~O$O$Pi!]$Pi!^$Pi~P#BwO$O!ri!^!ri~P$+oOg']a!]']a~P!1WO!^7nO~O!]'da!^'da~P#BwO!Y7oO~P#/sO!g#vO(r'pO!]'ea!k'ea~O!]/pO!k)Oi~O!]/pO!g#vO!k)Oi~Og$|q!]$|q#`$|q$O$|q~P!1WO!Y'ga!]'ga~P#/sO!g7vO~O!]/yO!Y)Pi~P#/sO!]/yO!Y)Pi~O!Y7yO~Oh%VOr8OO!l%eO(r'pO~Oj8QO!g#vO~Or8TO!g#vO(r'pO~O!Q*OO'y*PO(z%POn'ja(y'ja!]'ja#`'ja~Og'ja$O'ja~P&5RO!Q*OO'y*POn'la(y'la(z'la!]'la#`'la~Og'la$O'la~P&5tOg(_q!](_q~P!1WO#`8VOg(_q!](_q~P!1WO!Y8WO~Og%Oq!]%Oq#`%Oq$O%Oq~P!1WOa$oy!]$oy'z$oy'w$oy!Y$oy!k$oyv$oy!_$oy%i$oy!g$oy~P!:tO!g6rO~O!]5[O!_)Qa~O!_'`OP$TaR$Ta[$Taj$Tar$Ta!Q$Ta!S$Ta!]$Ta!l$Ta!p$Ta#R$Ta#n$Ta#o$Ta#p$Ta#q$Ta#r$Ta#s$Ta#t$Ta#u$Ta#v$Ta#x$Ta#z$Ta#{$Ta(a$Ta(r$Ta(y$Ta(z$Ta~O%i7WO~P&8fO%^8[Oa%[i!_%[i'z%[i!]%[i~Oa#cy!]#cy'z#cy'w#cy!Y#cy!k#cyv#cy!_#cy%i#cy!g#cy~P!:tO[8^O~Ob8`O(T+qO(VTO(YUO~O!]1TO!^)Xi~O`8dO~O(e(|O!]'pX!^'pX~O!]5uO!^)Ua~O!^8nO~P%;eO(o!sO~P$&YO#[8oO~O!_1oO~O!_1oO%i8qO~On8tO!_1oO%i8qO~O[8yO!]'sa!^'sa~O!]1zO!^)Vi~O!k8}O~O!k9OO~O!k9RO~O!k9RO~P%[Oa9TO~O!g9UO~O!k9VO~O!](wi!^(wi~P#BwOa%nO#`9_O'z%nO~O!](ty!k(tya(ty'z(ty~P!:tO!](jO!k(sy~O%i9bO~P&8fO!_'`O%i9bO~O#k$|qP$|qR$|q[$|qa$|qj$|qr$|q!S$|q!]$|q!l$|q!p$|q#R$|q#n$|q#o$|q#p$|q#q$|q#r$|q#s$|q#t$|q#u$|q#v$|q#x$|q#z$|q#{$|q'z$|q(a$|q(r$|q!k$|q!Y$|q'w$|q#`$|qv$|q!_$|q%i$|q!g$|q~P#/sO#k'jaP'jaR'ja['jaa'jaj'jar'ja!S'ja!l'ja!p'ja#R'ja#n'ja#o'ja#p'ja#q'ja#r'ja#s'ja#t'ja#u'ja#v'ja#x'ja#z'ja#{'ja'z'ja(a'ja(r'ja!k'ja!Y'ja'w'jav'ja!_'ja%i'ja!g'ja~P&5RO#k'laP'laR'la['laa'laj'lar'la!S'la!l'la!p'la#R'la#n'la#o'la#p'la#q'la#r'la#s'la#t'la#u'la#v'la#x'la#z'la#{'la'z'la(a'la(r'la!k'la!Y'la'w'lav'la!_'la%i'la!g'la~P&5tO#k%OqP%OqR%Oq[%Oqa%Oqj%Oqr%Oq!S%Oq!]%Oq!l%Oq!p%Oq#R%Oq#n%Oq#o%Oq#p%Oq#q%Oq#r%Oq#s%Oq#t%Oq#u%Oq#v%Oq#x%Oq#z%Oq#{%Oq'z%Oq(a%Oq(r%Oq!k%Oq!Y%Oq'w%Oq#`%Oqv%Oq!_%Oq%i%Oq!g%Oq~P#/sO!]'Yi!k'Yi~P!:tO$O#cq!]#cq!^#cq~P#BwO(y$}OP%aaR%aa[%aaj%aar%aa!S%aa!l%aa!p%aa#R%aa#n%aa#o%aa#p%aa#q%aa#r%aa#s%aa#t%aa#u%aa#v%aa#x%aa#z%aa#{%aa$O%aa(a%aa(r%aa!]%aa!^%aa~On%aa!Q%aa'y%aa(z%aa~P&IyO(z%POP%caR%ca[%caj%car%ca!S%ca!l%ca!p%ca#R%ca#n%ca#o%ca#p%ca#q%ca#r%ca#s%ca#t%ca#u%ca#v%ca#x%ca#z%ca#{%ca$O%ca(a%ca(r%ca!]%ca!^%ca~On%ca!Q%ca'y%ca(y%ca~P&LQOn>^O!Q*OO'y*PO(z%PO~P&IyOn>^O!Q*OO'y*PO(y$}O~P&LQOR0kO!Q0kO!S0lO#S$dOP}a[}aj}an}ar}a!l}a!p}a#R}a#n}a#o}a#p}a#q}a#r}a#s}a#t}a#u}a#v}a#x}a#z}a#{}a$O}a'y}a(a}a(r}a(y}a(z}a!]}a!^}a~O!Q*OO'y*POP$saR$sa[$saj$san$sar$sa!S$sa!l$sa!p$sa#R$sa#n$sa#o$sa#p$sa#q$sa#r$sa#s$sa#t$sa#u$sa#v$sa#x$sa#z$sa#{$sa$O$sa(a$sa(r$sa(y$sa(z$sa!]$sa!^$sa~O!Q*OO'y*POP$uaR$ua[$uaj$uan$uar$ua!S$ua!l$ua!p$ua#R$ua#n$ua#o$ua#p$ua#q$ua#r$ua#s$ua#t$ua#u$ua#v$ua#x$ua#z$ua#{$ua$O$ua(a$ua(r$ua(y$ua(z$ua!]$ua!^$ua~On>^O!Q*OO'y*PO(y$}O(z%PO~OP%TaR%Ta[%Taj%Tar%Ta!S%Ta!l%Ta!p%Ta#R%Ta#n%Ta#o%Ta#p%Ta#q%Ta#r%Ta#s%Ta#t%Ta#u%Ta#v%Ta#x%Ta#z%Ta#{%Ta$O%Ta(a%Ta(r%Ta!]%Ta!^%Ta~P''VO$O$mq!]$mq!^$mq~P#BwO$O$oq!]$oq!^$oq~P#BwO!^9oO~O$O9pO~P!1WO!g#vO!]'ei!k'ei~O!g#vO(r'pO!]'ei!k'ei~O!]/pO!k)Oq~O!Y'gi!]'gi~P#/sO!]/yO!Y)Pq~Or9wO!g#vO(r'pO~O[9yO!Y9xO~P#/sO!Y9xO~Oj:PO!g#vO~Og(_y!](_y~P!1WO!]'na!_'na~P#/sOa%[q!_%[q'z%[q!]%[q~P#/sO[:UO~O!]1TO!^)Xq~O`:YO~O#`:ZO!]'pa!^'pa~O!]5uO!^)Ui~P#BwO!S:]O~O!_1oO%i:`O~O(VTO(YUO(e:eO~O!]1zO!^)Vq~O!k:hO~O!k:iO~O!k:jO~O!k:jO~P%[O#`:mO!]#hy!^#hy~O!]#hy!^#hy~P#BwO%i:rO~P&8fO!_'`O%i:rO~O$O#|y!]#|y!^#|y~P#BwOP$|iR$|i[$|ij$|ir$|i!S$|i!l$|i!p$|i#R$|i#n$|i#o$|i#p$|i#q$|i#r$|i#s$|i#t$|i#u$|i#v$|i#x$|i#z$|i#{$|i$O$|i(a$|i(r$|i!]$|i!^$|i~P''VO!Q*OO'y*PO(z%POP'iaR'ia['iaj'ian'iar'ia!S'ia!l'ia!p'ia#R'ia#n'ia#o'ia#p'ia#q'ia#r'ia#s'ia#t'ia#u'ia#v'ia#x'ia#z'ia#{'ia$O'ia(a'ia(r'ia(y'ia!]'ia!^'ia~O!Q*OO'y*POP'kaR'ka['kaj'kan'kar'ka!S'ka!l'ka!p'ka#R'ka#n'ka#o'ka#p'ka#q'ka#r'ka#s'ka#t'ka#u'ka#v'ka#x'ka#z'ka#{'ka$O'ka(a'ka(r'ka(y'ka(z'ka!]'ka!^'ka~O(y$}OP%aiR%ai[%aij%ain%air%ai!Q%ai!S%ai!l%ai!p%ai#R%ai#n%ai#o%ai#p%ai#q%ai#r%ai#s%ai#t%ai#u%ai#v%ai#x%ai#z%ai#{%ai$O%ai'y%ai(a%ai(r%ai(z%ai!]%ai!^%ai~O(z%POP%ciR%ci[%cij%cin%cir%ci!Q%ci!S%ci!l%ci!p%ci#R%ci#n%ci#o%ci#p%ci#q%ci#r%ci#s%ci#t%ci#u%ci#v%ci#x%ci#z%ci#{%ci$O%ci'y%ci(a%ci(r%ci(y%ci!]%ci!^%ci~O$O$oy!]$oy!^$oy~P#BwO$O#cy!]#cy!^#cy~P#BwO!g#vO!]'eq!k'eq~O!]/pO!k)Oy~O!Y'gq!]'gq~P#/sOr:|O!g#vO(r'pO~O[;QO!Y;PO~P#/sO!Y;PO~Og(_!R!](_!R~P!1WOa%[y!_%[y'z%[y!]%[y~P#/sO!]1TO!^)Xy~O!]5uO!^)Uq~O(T;XO~O!_1oO%i;[O~O!k;_O~O%i;dO~P&8fOP$|qR$|q[$|qj$|qr$|q!S$|q!l$|q!p$|q#R$|q#n$|q#o$|q#p$|q#q$|q#r$|q#s$|q#t$|q#u$|q#v$|q#x$|q#z$|q#{$|q$O$|q(a$|q(r$|q!]$|q!^$|q~P''VO!Q*OO'y*PO(z%POP'jaR'ja['jaj'jan'jar'ja!S'ja!l'ja!p'ja#R'ja#n'ja#o'ja#p'ja#q'ja#r'ja#s'ja#t'ja#u'ja#v'ja#x'ja#z'ja#{'ja$O'ja(a'ja(r'ja(y'ja!]'ja!^'ja~O!Q*OO'y*POP'laR'la['laj'lan'lar'la!S'la!l'la!p'la#R'la#n'la#o'la#p'la#q'la#r'la#s'la#t'la#u'la#v'la#x'la#z'la#{'la$O'la(a'la(r'la(y'la(z'la!]'la!^'la~OP%OqR%Oq[%Oqj%Oqr%Oq!S%Oq!l%Oq!p%Oq#R%Oq#n%Oq#o%Oq#p%Oq#q%Oq#r%Oq#s%Oq#t%Oq#u%Oq#v%Oq#x%Oq#z%Oq#{%Oq$O%Oq(a%Oq(r%Oq!]%Oq!^%Oq~P''VOg%e!Z!]%e!Z#`%e!Z$O%e!Z~P!1WO!Y;hO~P#/sOr;iO!g#vO(r'pO~O[;kO!Y;hO~P#/sO!]'pq!^'pq~P#BwO!]#h!Z!^#h!Z~P#BwO#k%e!ZP%e!ZR%e!Z[%e!Za%e!Zj%e!Zr%e!Z!S%e!Z!]%e!Z!l%e!Z!p%e!Z#R%e!Z#n%e!Z#o%e!Z#p%e!Z#q%e!Z#r%e!Z#s%e!Z#t%e!Z#u%e!Z#v%e!Z#x%e!Z#z%e!Z#{%e!Z'z%e!Z(a%e!Z(r%e!Z!k%e!Z!Y%e!Z'w%e!Z#`%e!Zv%e!Z!_%e!Z%i%e!Z!g%e!Z~P#/sOr;tO!g#vO(r'pO~O!Y;uO~P#/sOr;|O!g#vO(r'pO~O!Y;}O~P#/sOP%e!ZR%e!Z[%e!Zj%e!Zr%e!Z!S%e!Z!l%e!Z!p%e!Z#R%e!Z#n%e!Z#o%e!Z#p%e!Z#q%e!Z#r%e!Z#s%e!Z#t%e!Z#u%e!Z#v%e!Z#x%e!Z#z%e!Z#{%e!Z$O%e!Z(a%e!Z(r%e!Z!]%e!Z!^%e!Z~P''VOr<QO!g#vO(r'pO~Ov(fX~P1qO!Q%rO~P!)[O(U!lO~P!)[O!YfX!]fX#`fX~P%2OOP]XR]X[]Xj]Xr]X!Q]X!S]X!]]X!]fX!l]X!p]X#R]X#S]X#`]X#`fX#kfX#n]X#o]X#p]X#q]X#r]X#s]X#t]X#u]X#v]X#x]X#z]X#{]X$Q]X(a]X(r]X(y]X(z]X~O!gfX!k]X!kfX(rfX~P'LTOP<UOQ<UOSfOd>ROe!iOpkOr<UOskOtkOzkO|<UO!O<UO!SWO!WkO!XkO!_XO!i<XO!lZO!o<UO!p<UO!q<UO!s<YO!u<]O!x!hO$W!kO$n>PO(T)]O(VTO(YUO(aVO(o[O~O!]<iO!^$qa~Oh%VOp%WOr%XOs$tOt$tOz%YO|%ZO!O<tO!S${O!_$|O!i>WO!l$xO#j<zO$W%`O$t<vO$v<xO$y%aO(T(vO(VTO(YUO(a$uO(y$}O(z%PO~Ol)dO~P(!yOr!eX(r!eX~P#!iOr(jX(r(jX~P##[O!^]X!^fX~P'LTO!YfX!Y$zX!]fX!]$zX#`fX~P!0SO#k<^O~O!g#vO#k<^O~O#`<nO~Oj<bO~O#`=OO!](wX!^(wX~O#`<nO!](uX!^(uX~O#k=PO~Og=RO~P!1WO#k=XO~O#k=YO~Og=RO(T&ZO~O!g#vO#k=ZO~O!g#vO#k=PO~O$O=[O~P#BwO#k=]O~O#k=^O~O#k=cO~O#k=dO~O#k=eO~O#k=fO~O$O=gO~P!1WO$O=hO~P!1WOl=sO~P7eOk#S#T#U#W#X#[#i#j#u$n$t$v$y%]%^%h%i%j%q%s%v%w%y%{~(OT#o!X'|(U#ps#n#qr!Q'}$]'}(T$_(e~\",goto:\"$9Y)]PPPPPP)^PP)aP)rP+W/]PPPP6mPP7TPP=QPPP@tPA^PA^PPPA^PCfPA^PA^PA^PCjPCoPD^PIWPPPI[PPPPI[L_PPPLeMVPI[PI[PP! eI[PPPI[PI[P!#lI[P!'S!(X!(bP!)U!)Y!)U!,gPPPPPPP!-W!(XPP!-h!/YP!2iI[I[!2n!5z!:h!:h!>gPPP!>oI[PPPPPPPPP!BOP!C]PPI[!DnPI[PI[I[I[I[I[PI[!FQP!I[P!LbP!Lf!Lp!Lt!LtP!IXP!Lx!LxP#!OP#!SI[PI[#!Y#%_CjA^PA^PA^A^P#&lA^A^#)OA^#+vA^#.SA^A^#.r#1W#1W#1]#1f#1W#1qPP#1WPA^#2ZA^#6YA^A^6mPPP#:_PPP#:x#:xP#:xP#;`#:xPP#;fP#;]P#;]#;y#;]#<e#<k#<n)aP#<q)aP#<z#<z#<zP)aP)aP)aP)aPP)aP#=Q#=TP#=T)aP#=XP#=[P)aP)aP)aP)aP)aP)a)aPP#=b#=h#=s#=y#>P#>V#>]#>k#>q#>{#?R#?]#?c#?s#?y#@k#@}#AT#AZ#Ai#BO#Cs#DR#DY#Et#FS#Gt#HS#HY#H`#Hf#Hp#Hv#H|#IW#Ij#IpPPPPPPPPPPP#IvPPPPPPP#Jk#Mx$ b$ i$ qPPP$']P$'f$*_$0x$0{$1O$1}$2Q$2X$2aP$2g$2jP$3W$3[$4S$5b$5g$5}PP$6S$6Y$6^$6a$6e$6i$7e$7|$8e$8i$8l$8o$8y$8|$9Q$9UR!|RoqOXst!Z#d%m&r&t&u&w,s,x2[2_Y!vQ'`-e1o5{Q%tvQ%|yQ&T|Q&j!VS'W!e-]Q'f!iS'l!r!yU*k$|*Z*oQ+o%}S+|&V&WQ,d&dQ-c'_Q-m'gQ-u'mQ0[*qQ1b,OQ1y,eR<{<Y%SdOPWXYZstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$_$a$e%m%t&R&k&n&r&t&u&w&{'T'b'r(T(V(](d(x(z)O)}*i+X+],p,s,x-i-q.P.V.t.{/n0]0l0r1S1r2S2T2V2X2[2_2a3Q3W3l4z6T6e6f6i6|8t9T9_S#q]<V!r)_$Z$n'X)s-U-X/V2p4T5w6s:Z:m<U<X<Y<]<^<_<`<a<b<c<d<e<f<g<h<i<k<n<{=O=P=R=Z=[=e=f>SU+P%]<s<tQ+t&PQ,f&gQ,m&oQ0x+gQ0}+iQ1Y+uQ2R,kQ3`.gQ5`0|Q5f1TQ6[1zQ7Y3dQ8`5gR9e7['QkOPWXYZstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$Z$_$a$e$n%m%t&R&k&n&o&r&t&u&w&{'T'X'b'r(T(V(](d(x(z)O)s)}*i+X+]+g,p,s,x-U-X-i-q.P.V.g.t.{/V/n0]0l0r1S1r2S2T2V2X2[2_2a2p3Q3W3d3l4T4z5w6T6e6f6i6s6|7[8t9T9_:Z:m<U<X<Y<]<^<_<`<a<b<c<d<e<f<g<h<i<k<n<{=O=P=R=Z=[=e=f>S!S!nQ!r!v!y!z$|'W'_'`'l'm'n*k*o*q*r-]-c-e-u0[0_1o5{5}%[$ti#v$b$c$d$x${%O%Q%^%_%c)y*R*T*V*Y*a*g*w*x+f+i,S,V.f/P/d/m/x/y/{0`0b0i0j0o1f1i1q3c4^4_4j4o5Q5[5_6S7W7v8Q8V8[8q9b9p9y:P:`:r;Q;[;d;k<l<m<o<p<q<r<u<v<w<x<y<z=S=T=U=V=X=Y=]=^=_=`=a=b=c=d=g=h>P>X>Y>]>^Q&X|Q'U!eS'[%i-`Q+t&PQ,P&WQ,f&gQ0n+SQ1Y+uQ1_+{Q2Q,jQ2R,kQ5f1TQ5o1aQ6[1zQ6_1|Q6`2PQ8`5gQ8c5lQ8|6bQ:X8dQ:f8yQ;V:YR<}*ZrnOXst!V!Z#d%m&i&r&t&u&w,s,x2[2_R,h&k&z^OPXYstuvwz!Z!`!g!j!o#S#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$Z$_$a$e$n%m%t&R&k&n&o&r&t&u&w&{'T'b'r(V(](d(x(z)O)s)}*i+X+]+g,p,s,x-U-X-i-q.P.V.g.t.{/V/n0]0l0r1S1r2S2T2V2X2[2_2a2p3Q3W3d3l4T4z5w6T6e6f6i6s6|7[8t9T9_:Z:m<U<X<Y<]<^<_<`<a<b<c<d<e<f<g<h<i<k<n<{=O=P=R=Z=[=e=f>R>S[#]WZ#W#Z'X(T!b%jm#h#i#l$x%e%h(^(h(i(j*Y*^*b+Z+[+^,o-V.T.Z.[.]._/m/p2d3[3]4a6r7TQ%wxQ%{yW&Q|&V&W,OQ&_!TQ'c!hQ'e!iQ(q#sS+n%|%}Q+r&PQ,_&bQ,c&dS-l'f'gQ.i(rQ1R+oQ1X+uQ1Z+vQ1^+zQ1t,`S1x,d,eQ2|-mQ5e1TQ5i1WQ5n1`Q6Z1yQ8_5gQ8b5kQ8f5pQ:T8^R;T:U!U$zi$d%O%Q%^%_%c*R*T*a*w*x/P/x0`0b0i0j0o4_5Q8V9p>P>X>Y!^%yy!i!u%{%|%}'V'e'f'g'k'u*j+n+o-Y-l-m-t0R0U1R2u2|3T4r4s4v7}9{Q+h%wQ,T&[Q,W&]Q,b&dQ.h(qQ1s,_U1w,c,d,eQ3e.iQ6U1tS6Y1x1yQ8x6Z#f>T#v$b$c$x${)y*V*Y*g+f+i,S,V.f/d/m/y/{1f1i1q3c4^4j4o5[5_6S7W7v8Q8[8q9b9y:P:`:r;Q;[;d;k<o<q<u<w<y=S=U=X=]=_=a=c=g>]>^o>U<l<m<p<r<v<x<z=T=V=Y=^=`=b=d=hW%Ti%V*y>PS&[!Q&iQ&]!RQ&^!SU*}%[%d=sR,R&Y%]%Si#v$b$c$d$x${%O%Q%^%_%c)y*R*T*V*Y*a*g*w*x+f+i,S,V.f/P/d/m/x/y/{0`0b0i0j0o1f1i1q3c4^4_4j4o5Q5[5_6S7W7v8Q8V8[8q9b9p9y:P:`:r;Q;[;d;k<l<m<o<p<q<r<u<v<w<x<y<z=S=T=U=V=X=Y=]=^=_=`=a=b=c=d=g=h>P>X>Y>]>^T)z$u){V+P%]<s<tW'[!e%i*Z-`S(}#y#zQ+c%rQ+y&SS.b(m(nQ1j,XQ5T0kR8i5u'QkOPWXYZstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$Z$_$a$e$n%m%t&R&k&n&o&r&t&u&w&{'T'X'b'r(T(V(](d(x(z)O)s)}*i+X+]+g,p,s,x-U-X-i-q.P.V.g.t.{/V/n0]0l0r1S1r2S2T2V2X2[2_2a2p3Q3W3d3l4T4z5w6T6e6f6i6s6|7[8t9T9_:Z:m<U<X<Y<]<^<_<`<a<b<c<d<e<f<g<h<i<k<n<{=O=P=R=Z=[=e=f>S$i$^c#Y#e%q%s%u(S(Y(t(y)R)S)T)U)V)W)X)Y)Z)[)^)`)b)g)q+d+x-Z-x-}.S.U.s.v.z.|.}/O/b0p2k2n3O3V3k3p3q3r3s3t3u3v3w3x3y3z3{3|4P4Q4X5X5c6u6{7Q7a7b7k7l8k9X9]9g9m9n:o;W;`<W=vT#TV#U'RkOPWXYZstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$Z$_$a$e$n%m%t&R&k&n&o&r&t&u&w&{'T'X'b'r(T(V(](d(x(z)O)s)}*i+X+]+g,p,s,x-U-X-i-q.P.V.g.t.{/V/n0]0l0r1S1r2S2T2V2X2[2_2a2p3Q3W3d3l4T4z5w6T6e6f6i6s6|7[8t9T9_:Z:m<U<X<Y<]<^<_<`<a<b<c<d<e<f<g<h<i<k<n<{=O=P=R=Z=[=e=f>SQ'Y!eR2q-]!W!nQ!e!r!v!y!z$|'W'_'`'l'm'n*Z*k*o*q*r-]-c-e-u0[0_1o5{5}R1l,ZnqOXst!Z#d%m&r&t&u&w,s,x2[2_Q&y!^Q'v!xS(s#u<^Q+l%zQ,]&_Q,^&aQ-j'dQ-w'oS.r(x=PS0q+X=ZQ1P+mQ1n,[Q2c,zQ2e,{Q2m-WQ2z-kQ2}-oS5Y0r=eQ5a1QS5d1S=fQ6t2oQ6x2{Q6}3SQ8]5bQ9Y6vQ9Z6yQ9^7OR:l9V$d$]c#Y#e%s%u(S(Y(t(y)R)S)T)U)V)W)X)Y)Z)[)^)`)b)g)q+d+x-Z-x-}.S.U.s.v.z.}/O/b0p2k2n3O3V3k3p3q3r3s3t3u3v3w3x3y3z3{3|4P4Q4X5X5c6u6{7Q7a7b7k7l8k9X9]9g9m9n:o;W;`<W=vS(o#p'iQ)P#zS+b%q.|S.c(n(pR3^.d'QkOPWXYZstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$Z$_$a$e$n%m%t&R&k&n&o&r&t&u&w&{'T'X'b'r(T(V(](d(x(z)O)s)}*i+X+]+g,p,s,x-U-X-i-q.P.V.g.t.{/V/n0]0l0r1S1r2S2T2V2X2[2_2a2p3Q3W3d3l4T4z5w6T6e6f6i6s6|7[8t9T9_:Z:m<U<X<Y<]<^<_<`<a<b<c<d<e<f<g<h<i<k<n<{=O=P=R=Z=[=e=f>SS#q]<VQ&t!XQ&u!YQ&w![Q&x!]R2Z,vQ'a!hQ+e%wQ-h'cS.e(q+hQ2x-gW3b.h.i0w0yQ6w2yW7U3_3a3e5^U9a7V7X7ZU:q9c9d9fS;b:p:sQ;p;cR;x;qU!wQ'`-eT5y1o5{!Q_OXZ`st!V!Z#d#h%e%m&i&k&r&t&u&w(j,s,x.[2[2_]!pQ!r'`-e1o5{T#q]<V%^{OPWXYZstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$_$a$e%m%t&R&k&n&o&r&t&u&w&{'T'b'r(T(V(](d(x(z)O)}*i+X+]+g,p,s,x-i-q.P.V.g.t.{/n0]0l0r1S1r2S2T2V2X2[2_2a3Q3W3d3l4z6T6e6f6i6|7[8t9T9_S(}#y#zS.b(m(n!s=l$Z$n'X)s-U-X/V2p4T5w6s:Z:m<U<X<Y<]<^<_<`<a<b<c<d<e<f<g<h<i<k<n<{=O=P=R=Z=[=e=f>SU$fd)_,mS(p#p'iU*v%R(w4OU0m+O.n7gQ5^0xQ7V3`Q9d7YR:s9em!tQ!r!v!y!z'`'l'm'n-e-u1o5{5}Q't!uS(f#g2US-s'k'wQ/s*]Q0R*jQ3U-vQ4f/tQ4r0TQ4s0UQ4x0^Q7r4`S7}4t4vS8R4y4{Q9r7sQ9v7yQ9{8OQ:Q8TS:{9w9xS;g:|;PS;s;h;iS;{;t;uS<P;|;}R<S<QQ#wbQ's!uS(e#g2US(g#m+WQ+Y%fQ+j%xQ+p&OU-r'k't'wQ.W(fU/r*]*`/wQ0S*jQ0V*lQ1O+kQ1u,aS3R-s-vQ3Z.`S4e/s/tQ4n0PS4q0R0^Q4u0WQ6W1vQ7P3US7q4`4bQ7u4fU7|4r4x4{Q8P4wQ8v6XS9q7r7sQ9u7yQ9}8RQ:O8SQ:c8wQ:y9rS:z9v9xQ;S:QQ;^:dS;f:{;PS;r;g;hS;z;s;uS<O;{;}Q<R<PQ<T<SQ=o=jQ={=tR=|=uV!wQ'`-e%^aOPWXYZstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$_$a$e%m%t&R&k&n&o&r&t&u&w&{'T'b'r(T(V(](d(x(z)O)}*i+X+]+g,p,s,x-i-q.P.V.g.t.{/n0]0l0r1S1r2S2T2V2X2[2_2a3Q3W3d3l4z6T6e6f6i6|7[8t9T9_S#wz!j!r=i$Z$n'X)s-U-X/V2p4T5w6s:Z:m<U<X<Y<]<^<_<`<a<b<c<d<e<f<g<h<i<k<n<{=O=P=R=Z=[=e=f>SR=o>R%^bOPWXYZstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$_$a$e%m%t&R&k&n&o&r&t&u&w&{'T'b'r(T(V(](d(x(z)O)}*i+X+]+g,p,s,x-i-q.P.V.g.t.{/n0]0l0r1S1r2S2T2V2X2[2_2a3Q3W3d3l4z6T6e6f6i6|7[8t9T9_Q%fj!^%xy!i!u%{%|%}'V'e'f'g'k'u*j+n+o-Y-l-m-t0R0U1R2u2|3T4r4s4v7}9{S&Oz!jQ+k%yQ,a&dW1v,b,c,d,eU6X1w1x1yS8w6Y6ZQ:d8x!r=j$Z$n'X)s-U-X/V2p4T5w6s:Z:m<U<X<Y<]<^<_<`<a<b<c<d<e<f<g<h<i<k<n<{=O=P=R=Z=[=e=f>SQ=t>QR=u>R%QeOPXYstuvw!Z!`!g!o#S#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$_$a$e%m%t&R&k&n&r&t&u&w&{'T'b'r(V(](d(x(z)O)}*i+X+]+g,p,s,x-i-q.P.V.g.t.{/n0]0l0r1S1r2S2T2V2X2[2_2a3Q3W3d3l4z6T6e6f6i6|7[8t9T9_Y#bWZ#W#Z(T!b%jm#h#i#l$x%e%h(^(h(i(j*Y*^*b+Z+[+^,o-V.T.Z.[.]._/m/p2d3[3]4a6r7TQ,n&o!p=k$Z$n)s-U-X/V2p4T5w6s:Z:m<U<X<Y<]<^<_<`<a<b<c<d<e<f<g<h<i<k<n<{=O=P=R=Z=[=e=f>SR=n'XU']!e%i*ZR2s-`%SdOPWXYZstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$_$a$e%m%t&R&k&n&r&t&u&w&{'T'b'r(T(V(](d(x(z)O)}*i+X+],p,s,x-i-q.P.V.t.{/n0]0l0r1S1r2S2T2V2X2[2_2a3Q3W3l4z6T6e6f6i6|8t9T9_!r)_$Z$n'X)s-U-X/V2p4T5w6s:Z:m<U<X<Y<]<^<_<`<a<b<c<d<e<f<g<h<i<k<n<{=O=P=R=Z=[=e=f>SQ,m&oQ0x+gQ3`.gQ7Y3dR9e7[!b$Tc#Y%q(S(Y(t(y)Z)[)`)g+x-x-}.S.U.s.v/b0p3O3V3k3{5X5c6{7Q7a9]:o<W!P<d)^)q-Z.|2k2n3p3y3z4P4X6u7b7k7l8k9X9g9m9n;W;`=v!f$Vc#Y%q(S(Y(t(y)W)X)Z)[)`)g+x-x-}.S.U.s.v/b0p3O3V3k3{5X5c6{7Q7a9]:o<W!T<f)^)q-Z.|2k2n3p3v3w3y3z4P4X6u7b7k7l8k9X9g9m9n;W;`=v!^$Zc#Y%q(S(Y(t(y)`)g+x-x-}.S.U.s.v/b0p3O3V3k3{5X5c6{7Q7a9]:o<WQ4_/kz>S)^)q-Z.|2k2n3p4P4X6u7b7k7l8k9X9g9m9n;W;`=vQ>X>ZR>Y>['QkOPWXYZstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$Z$_$a$e$n%m%t&R&k&n&o&r&t&u&w&{'T'X'b'r(T(V(](d(x(z)O)s)}*i+X+]+g,p,s,x-U-X-i-q.P.V.g.t.{/V/n0]0l0r1S1r2S2T2V2X2[2_2a2p3Q3W3d3l4T4z5w6T6e6f6i6s6|7[8t9T9_:Z:m<U<X<Y<]<^<_<`<a<b<c<d<e<f<g<h<i<k<n<{=O=P=R=Z=[=e=f>SS$oh$pR4U/U'XgOPWXYZhstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$Z$_$a$e$n$p%m%t&R&k&n&o&r&t&u&w&{'T'X'b'r(T(V(](d(x(z)O)s)}*i+X+]+g,p,s,x-U-X-i-q.P.V.g.t.{/U/V/n0]0l0r1S1r2S2T2V2X2[2_2a2p3Q3W3d3l4T4z5w6T6e6f6i6s6|7[8t9T9_:Z:m<U<X<Y<]<^<_<`<a<b<c<d<e<f<g<h<i<k<n<{=O=P=R=Z=[=e=f>ST$kf$qQ$ifS)j$l)nR)v$qT$jf$qT)l$l)n'XhOPWXYZhstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$Z$_$a$e$n$p%m%t&R&k&n&o&r&t&u&w&{'T'X'b'r(T(V(](d(x(z)O)s)}*i+X+]+g,p,s,x-U-X-i-q.P.V.g.t.{/U/V/n0]0l0r1S1r2S2T2V2X2[2_2a2p3Q3W3d3l4T4z5w6T6e6f6i6s6|7[8t9T9_:Z:m<U<X<Y<]<^<_<`<a<b<c<d<e<f<g<h<i<k<n<{=O=P=R=Z=[=e=f>ST$oh$pQ$rhR)u$p%^jOPWXYZstuvw!Z!`!g!o#S#W#Z#d#o#u#x#{$O$P$Q$R$S$T$U$V$W$X$_$a$e%m%t&R&k&n&o&r&t&u&w&{'T'b'r(T(V(](d(x(z)O)}*i+X+]+g,p,s,x-i-q.P.V.g.t.{/n0]0l0r1S1r2S2T2V2X2[2_2a3Q3W3d3l4z6T6e6f6i6|7[8t9T9_!s>Q$Z$n'X)s-U-X/V2p4T5w6s:Z:m<U<X<Y<]<^<_<`<a<b<c<d<e<f<g<h<i<k<n<{=O=P=R=Z=[=e=f>S#glOPXZst!Z!`!o#S#d#o#{$n%m&k&n&o&r&t&u&w&{'T'b)O)s*i+]+g,p,s,x-i.g/V/n0]0l1r2S2T2V2X2[2_2a3d4T4z6T6e6f6i7[8t9T!U%Ri$d%O%Q%^%_%c*R*T*a*w*x/P/x0`0b0i0j0o4_5Q8V9p>P>X>Y#f(w#v$b$c$x${)y*V*Y*g+f+i,S,V.f/d/m/y/{1f1i1q3c4^4j4o5[5_6S7W7v8Q8[8q9b9y:P:`:r;Q;[;d;k<o<q<u<w<y=S=U=X=]=_=a=c=g>]>^Q+T%aQ/c*Oo4O<l<m<p<r<v<x<z=T=V=Y=^=`=b=d=h!U$yi$d%O%Q%^%_%c*R*T*a*w*x/P/x0`0b0i0j0o4_5Q8V9p>P>X>YQ*c$zU*l$|*Z*oQ+U%bQ0W*m#f=q#v$b$c$x${)y*V*Y*g+f+i,S,V.f/d/m/y/{1f1i1q3c4^4j4o5[5_6S7W7v8Q8[8q9b9y:P:`:r;Q;[;d;k<o<q<u<w<y=S=U=X=]=_=a=c=g>]>^n=r<l<m<p<r<v<x<z=T=V=Y=^=`=b=d=hQ=w>TQ=x>UQ=y>VR=z>W!U%Ri$d%O%Q%^%_%c*R*T*a*w*x/P/x0`0b0i0j0o4_5Q8V9p>P>X>Y#f(w#v$b$c$x${)y*V*Y*g+f+i,S,V.f/d/m/y/{1f1i1q3c4^4j4o5[5_6S7W7v8Q8[8q9b9y:P:`:r;Q;[;d;k<o<q<u<w<y=S=U=X=]=_=a=c=g>]>^o4O<l<m<p<r<v<x<z=T=V=Y=^=`=b=d=hnoOXst!Z#d%m&r&t&u&w,s,x2[2_S*f${*YQ-R'OQ-S'QR4i/y%[%Si#v$b$c$d$x${%O%Q%^%_%c)y*R*T*V*Y*a*g*w*x+f+i,S,V.f/P/d/m/x/y/{0`0b0i0j0o1f1i1q3c4^4_4j4o5Q5[5_6S7W7v8Q8V8[8q9b9p9y:P:`:r;Q;[;d;k<l<m<o<p<q<r<u<v<w<x<y<z=S=T=U=V=X=Y=]=^=_=`=a=b=c=d=g=h>P>X>Y>]>^Q,U&]Q1h,WQ5s1gR8h5tV*n$|*Z*oU*n$|*Z*oT5z1o5{S0P*i/nQ4w0]T8S4z:]Q+j%xQ0V*lQ1O+kQ1u,aQ6W1vQ8v6XQ:c8wR;^:d!U%Oi$d%O%Q%^%_%c*R*T*a*w*x/P/x0`0b0i0j0o4_5Q8V9p>P>X>Yx*R$v)e*S*u+V/v0d0e4R4g5R5S5W7p8U:R:x=p=}>OS0`*t0a#f<o#v$b$c$x${)y*V*Y*g+f+i,S,V.f/d/m/y/{1f1i1q3c4^4j4o5[5_6S7W7v8Q8[8q9b9y:P:`:r;Q;[;d;k<o<q<u<w<y=S=U=X=]=_=a=c=g>]>^n<p<l<m<p<r<v<x<z=T=V=Y=^=`=b=d=h!d=S(u)c*[*e.j.m.q/_/k/|0v1e3h4[4h4l5r7]7`7w7z8X8Z9t9|:S:};R;e;j;v>Z>[`=T3}7c7f7j9h:t:w;yS=_.l3iT=`7e9k!U%Qi$d%O%Q%^%_%c*R*T*a*w*x/P/x0`0b0i0j0o4_5Q8V9p>P>X>Y|*T$v)e*U*t+V/g/v0d0e4R4g4|5R5S5W7p8U:R:x=p=}>OS0b*u0c#f<q#v$b$c$x${)y*V*Y*g+f+i,S,V.f/d/m/y/{1f1i1q3c4^4j4o5[5_6S7W7v8Q8[8q9b9y:P:`:r;Q;[;d;k<o<q<u<w<y=S=U=X=]=_=a=c=g>]>^n<r<l<m<p<r<v<x<z=T=V=Y=^=`=b=d=h!h=U(u)c*[*e.k.l.q/_/k/|0v1e3f3h4[4h4l5r7]7^7`7w7z8X8Z9t9|:S:};R;e;j;v>Z>[d=V3}7d7e7j9h9i:t:u:w;yS=a.m3jT=b7f9lrnOXst!V!Z#d%m&i&r&t&u&w,s,x2[2_Q&f!UR,p&ornOXst!V!Z#d%m&i&r&t&u&w,s,x2[2_R&f!UQ,Y&^R1d,RsnOXst!V!Z#d%m&i&r&t&u&w,s,x2[2_Q1p,_S6R1s1tU8p6P6Q6US:_8r8sS;Y:^:aQ;m;ZR;w;nQ&m!VR,i&iR6_1|R:f8yW&Q|&V&W,OR1Z+vQ&r!WR,s&sR,y&xT2],x2_R,}&yQ,|&yR2f,}Q'y!{R-y'ySsOtQ#dXT%ps#dQ#OTR'{#OQ#RUR'}#RQ){$uR/`){Q#UVR(Q#UQ#XWU(W#X(X.QQ(X#YR.Q(YQ-^'YR2r-^Q.u(yS3m.u3nR3n.vQ-e'`R2v-eY!rQ'`-e1o5{R'j!rQ/Q)eR4S/QU#_W%h*YU(_#_(`.RQ(`#`R.R(ZQ-a']R2t-at`OXst!V!Z#d%m&i&k&r&t&u&w,s,x2[2_S#hZ%eU#r`#h.[R.[(jQ(k#jQ.X(gW.a(k.X3X7RQ3X.YR7R3YQ)n$lR/W)nQ$phR)t$pQ$`cU)a$`-|<jQ-|<WR<j)qQ/q*]W4c/q4d7t9sU4d/r/s/tS7t4e4fR9s7u$e*Q$v(u)c)e*[*e*t*u+Q+R+V.l.m.o.p.q/_/g/i/k/v/|0d0e0v1e3f3g3h3}4R4[4g4h4l4|5O5R5S5W5r7]7^7_7`7e7f7h7i7j7p7w7z8U8X8Z9h9i9j9t9|:R:S:t:u:v:w:x:};R;e;j;v;y=p=}>O>Z>[Q/z*eU4k/z4m7xQ4m/|R7x4lS*o$|*ZR0Y*ox*S$v)e*t*u+V/v0d0e4R4g5R5S5W7p8U:R:x=p=}>O!d.j(u)c*[*e.l.m.q/_/k/|0v1e3h4[4h4l5r7]7`7w7z8X8Z9t9|:S:};R;e;j;v>Z>[U/h*S.j7ca7c3}7e7f7j9h:t:w;yQ0a*tQ3i.lU4}0a3i9kR9k7e|*U$v)e*t*u+V/g/v0d0e4R4g4|5R5S5W7p8U:R:x=p=}>O!h.k(u)c*[*e.l.m.q/_/k/|0v1e3f3h4[4h4l5r7]7^7`7w7z8X8Z9t9|:S:};R;e;j;v>Z>[U/j*U.k7de7d3}7e7f7j9h9i:t:u:w;yQ0c*uQ3j.mU5P0c3j9lR9l7fQ*z%UR0g*zQ5]0vR8Y5]Q+_%kR0u+_Q5v1jS8j5v:[R:[8kQ,[&_R1m,[Q5{1oR8m5{Q1{,fS6]1{8zR8z6_Q1U+rW5h1U5j8a:VQ5j1XQ8a5iR:V8bQ+w&QR1[+wQ2_,xR6m2_YrOXst#dQ&v!ZQ+a%mQ,r&rQ,t&tQ,u&uQ,w&wQ2Y,sS2],x2_R6l2[Q%opQ&z!_Q&}!aQ'P!bQ'R!cQ'q!uQ+`%lQ+l%zQ,Q&XQ,h&mQ-P&|W-p'k's't'wQ-w'oQ0X*nQ1P+mQ1c,PS2O,i,lQ2g-OQ2h-RQ2i-SQ2}-oW3P-r-s-v-xQ5a1QQ5m1_Q5q1eQ6V1uQ6a2QQ6k2ZU6z3O3R3UQ6}3SQ8]5bQ8e5oQ8g5rQ8l5zQ8u6WQ8{6`S9[6{7PQ9^7OQ:W8cQ:b8vQ:g8|Q:n9]Q;U:XQ;]:cQ;a:oQ;l;VR;o;^Q%zyQ'd!iQ'o!uU+m%{%|%}Q-W'VU-k'e'f'gS-o'k'uQ0Q*jS1Q+n+oQ2o-YS2{-l-mQ3S-tS4p0R0UQ5b1RQ6v2uQ6y2|Q7O3TU7{4r4s4vQ9z7}R;O9{S$wi>PR*{%VU%Ui%V>PR0f*yQ$viS(u#v+iS)c$b$cQ)e$dQ*[$xS*e${*YQ*t%OQ*u%QQ+Q%^Q+R%_Q+V%cQ.l<oQ.m<qQ.o<uQ.p<wQ.q<yQ/_)yQ/g*RQ/i*TQ/k*VQ/v*aS/|*g/mQ0d*wQ0e*xl0v+f,V.f1i1q3c6S7W8q9b:`:r;[;dQ1e,SQ3f=SQ3g=UQ3h=XS3}<l<mQ4R/PS4[/d4^Q4g/xQ4h/yQ4l/{Q4|0`Q5O0bQ5R0iQ5S0jQ5W0oQ5r1fQ7]=]Q7^=_Q7_=aQ7`=cQ7e<pQ7f<rQ7h<vQ7i<xQ7j<zQ7p4_Q7w4jQ7z4oQ8U5QQ8X5[Q8Z5_Q9h=YQ9i=TQ9j=VQ9t7vQ9|8QQ:R8VQ:S8[Q:t=^Q:u=`Q:v=bQ:w=dQ:x9pQ:}9yQ;R:PQ;e=gQ;j;QQ;v;kQ;y=hQ=p>PQ=}>XQ>O>YQ>Z>]R>[>^Q+O%]Q.n<sR7g<tnpOXst!Z#d%m&r&t&u&w,s,x2[2_Q!fPS#fZ#oQ&|!`W'h!o*i0]4zQ(P#SQ)Q#{Q)r$nS,l&k&nQ,q&oQ-O&{S-T'T/nQ-g'bQ.x)OQ/[)sQ0s+]Q0y+gQ2W,pQ2y-iQ3a.gQ4W/VQ5U0lQ6Q1rQ6c2SQ6d2TQ6h2VQ6j2XQ6o2aQ7Z3dQ7m4TQ8s6TQ9P6eQ9Q6fQ9S6iQ9f7[Q:a8tR:k9T#[cOPXZst!Z!`!o#d#o#{%m&k&n&o&r&t&u&w&{'T'b)O*i+]+g,p,s,x-i.g/n0]0l1r2S2T2V2X2[2_2a3d4z6T6e6f6i7[8t9TQ#YWQ#eYQ%quQ%svS%uw!gS(S#W(VQ(Y#ZQ(t#uQ(y#xQ)R$OQ)S$PQ)T$QQ)U$RQ)V$SQ)W$TQ)X$UQ)Y$VQ)Z$WQ)[$XQ)^$ZQ)`$_Q)b$aQ)g$eW)q$n)s/V4TQ+d%tQ+x&RS-Z'X2pQ-x'rS-}(T.PQ.S(]Q.U(dQ.s(xQ.v(zQ.z<UQ.|<XQ.}<YQ/O<]Q/b)}Q0p+XQ2k-UQ2n-XQ3O-qQ3V.VQ3k.tQ3p<^Q3q<_Q3r<`Q3s<aQ3t<bQ3u<cQ3v<dQ3w<eQ3x<fQ3y<gQ3z<hQ3{.{Q3|<kQ4P<nQ4Q<{Q4X<iQ5X0rQ5c1SQ6u=OQ6{3QQ7Q3WQ7a3lQ7b=PQ7k=RQ7l=ZQ8k5wQ9X6sQ9]6|Q9g=[Q9m=eQ9n=fQ:o9_Q;W:ZQ;`:mQ<W#SR=v>SR#[WR'Z!el!tQ!r!v!y!z'`'l'm'n-e-u1o5{5}S'V!e-]U*j$|*Z*oS-Y'W'_S0U*k*qQ0^*rQ2u-cQ4v0[R4{0_R({#xQ!fQT-d'`-e]!qQ!r'`-e1o5{Q#p]R'i<VR)f$dY!uQ'`-e1o5{Q'k!rS'u!v!yS'w!z5}S-t'l'mQ-v'nR3T-uT#kZ%eS#jZ%eS%km,oU(g#h#i#lS.Y(h(iQ.^(jQ0t+^Q3Y.ZU3Z.[.]._S7S3[3]R9`7Td#^W#W#Z%h(T(^*Y+Z.T/mr#gZm#h#i#l%e(h(i(j+^.Z.[.]._3[3]7TS*]$x*bQ/t*^Q2U,oQ2l-VQ4`/pQ6q2dQ7s4aQ9W6rT=m'X+[V#aW%h*YU#`W%h*YS(U#W(^U(Z#Z+Z/mS-['X+[T.O(T.TV'^!e%i*ZQ$lfR)x$qT)m$l)nR4V/UT*_$x*bT*h${*YQ0w+fQ1g,VQ3_.fQ5t1iQ6P1qQ7X3cQ8r6SQ9c7WQ:^8qQ:p9bQ;Z:`Q;c:rQ;n;[R;q;dnqOXst!Z#d%m&r&t&u&w,s,x2[2_Q&l!VR,h&itmOXst!U!V!Z#d%m&i&r&t&u&w,s,x2[2_R,o&oT%lm,oR1k,XR,g&gQ&U|S+}&V&WR1^,OR+s&PT&p!W&sT&q!W&sT2^,x2_\",nodeNames:\"⚠ ArithOp ArithOp ?. JSXStartTag LineComment BlockComment Script Hashbang ExportDeclaration export Star as VariableName String Escape from ; default FunctionDeclaration async function VariableDefinition > < TypeParamList in out const TypeDefinition extends ThisType this LiteralType ArithOp Number BooleanLiteral TemplateType InterpolationEnd Interpolation InterpolationStart NullType null VoidType void TypeofType typeof MemberExpression . PropertyName [ TemplateString Escape Interpolation super RegExp ] ArrayExpression Spread , } { ObjectExpression Property async get set PropertyDefinition Block : NewTarget new NewExpression ) ( ArgList UnaryExpression delete LogicOp BitOp YieldExpression yield AwaitExpression await ParenthesizedExpression ClassExpression class ClassBody MethodDeclaration Decorator @ MemberExpression PrivatePropertyName CallExpression TypeArgList CompareOp < declare Privacy static abstract override PrivatePropertyDefinition PropertyDeclaration readonly accessor Optional TypeAnnotation Equals StaticBlock FunctionExpression ArrowFunction ParamList ParamList ArrayPattern ObjectPattern PatternProperty Privacy readonly Arrow MemberExpression BinaryExpression ArithOp ArithOp ArithOp ArithOp BitOp CompareOp instanceof satisfies CompareOp BitOp BitOp BitOp LogicOp LogicOp ConditionalExpression LogicOp LogicOp AssignmentExpression UpdateOp PostfixExpression CallExpression InstantiationExpression TaggedTemplateExpression DynamicImport import ImportMeta JSXElement JSXSelfCloseEndTag JSXSelfClosingTag JSXIdentifier JSXBuiltin JSXIdentifier JSXNamespacedName JSXMemberExpression JSXSpreadAttribute JSXAttribute JSXAttributeValue JSXEscape JSXEndTag JSXOpenTag JSXFragmentTag JSXText JSXEscape JSXStartCloseTag JSXCloseTag PrefixCast < ArrowFunction TypeParamList SequenceExpression InstantiationExpression KeyofType keyof UniqueType unique ImportType InferredType infer TypeName ParenthesizedType FunctionSignature ParamList NewSignature IndexedType TupleType Label ArrayType ReadonlyType ObjectType MethodType PropertyType IndexSignature PropertyDefinition CallSignature TypePredicate asserts is NewSignature new UnionType LogicOp IntersectionType LogicOp ConditionalType ParameterizedType ClassDeclaration abstract implements type VariableDeclaration let var using TypeAliasDeclaration InterfaceDeclaration interface EnumDeclaration enum EnumBody NamespaceDeclaration namespace module AmbientDeclaration declare GlobalDeclaration global ClassDeclaration ClassBody AmbientFunctionDeclaration ExportGroup VariableName VariableName ImportDeclaration defer ImportGroup ForStatement for ForSpec ForInSpec ForOfSpec of WhileStatement while WithStatement with DoStatement do IfStatement if else SwitchStatement switch SwitchBody CaseLabel case DefaultLabel TryStatement try CatchClause catch FinallyClause finally ReturnStatement return ThrowStatement throw BreakStatement break ContinueStatement continue DebuggerStatement debugger LabeledStatement ExpressionStatement SingleExpression SingleClassItem\",maxTerm:380,context:MO,nodeProps:[[\"isolate\",-8,5,6,14,37,39,51,53,55,\"\"],[\"group\",-26,9,17,19,68,207,211,215,216,218,221,224,234,237,243,245,247,249,252,258,264,266,268,270,272,274,275,\"Statement\",-34,13,14,32,35,36,42,51,54,55,57,62,70,72,76,80,82,84,85,110,111,120,121,136,139,141,142,143,144,145,147,148,167,169,171,\"Expression\",-23,31,33,37,41,43,45,173,175,177,178,180,181,182,184,185,186,188,189,190,201,203,205,206,\"Type\",-3,88,103,109,\"ClassItem\"],[\"openedBy\",23,\"<\",38,\"InterpolationStart\",56,\"[\",60,\"{\",73,\"(\",160,\"JSXStartCloseTag\"],[\"closedBy\",-2,24,168,\">\",40,\"InterpolationEnd\",50,\"]\",61,\"}\",74,\")\",165,\"JSXEndTag\"]],propSources:[qO],skippedNodes:[0,5,6,278],repeatNodeCount:37,tokenData:\"$Fq07[R!bOX%ZXY+gYZ-yZ[+g[]%Z]^.c^p%Zpq+gqr/mrs3cst:_tuEruvJSvwLkwx! Yxy!'iyz!(sz{!)}{|!,q|}!.O}!O!,q!O!P!/Y!P!Q!9j!Q!R#:O!R![#<_![!]#I_!]!^#Jk!^!_#Ku!_!`$![!`!a$$v!a!b$*T!b!c$,r!c!}Er!}#O$-|#O#P$/W#P#Q$4o#Q#R$5y#R#SEr#S#T$7W#T#o$8b#o#p$<r#p#q$=h#q#r$>x#r#s$@U#s$f%Z$f$g+g$g#BYEr#BY#BZ$A`#BZ$ISEr$IS$I_$A`$I_$I|Er$I|$I}$Dk$I}$JO$Dk$JO$JTEr$JT$JU$A`$JU$KVEr$KV$KW$A`$KW&FUEr&FU&FV$A`&FV;'SEr;'S;=`I|<%l?HTEr?HT?HU$A`?HUOEr(n%d_$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z&j&hT$i&jO!^&c!_#o&c#p;'S&c;'S;=`&w<%lO&c&j&zP;=`<%l&c'|'U]$i&j(Z!bOY&}YZ&cZw&}wx&cx!^&}!^!_'}!_#O&}#O#P&c#P#o&}#o#p'}#p;'S&};'S;=`(l<%lO&}!b(SU(Z!bOY'}Zw'}x#O'}#P;'S'};'S;=`(f<%lO'}!b(iP;=`<%l'}'|(oP;=`<%l&}'[(y]$i&j(WpOY(rYZ&cZr(rrs&cs!^(r!^!_)r!_#O(r#O#P&c#P#o(r#o#p)r#p;'S(r;'S;=`*a<%lO(rp)wU(WpOY)rZr)rs#O)r#P;'S)r;'S;=`*Z<%lO)rp*^P;=`<%l)r'[*dP;=`<%l(r#S*nX(Wp(Z!bOY*gZr*grs'}sw*gwx)rx#O*g#P;'S*g;'S;=`+Z<%lO*g#S+^P;=`<%l*g(n+dP;=`<%l%Z07[+rq$i&j(Wp(Z!b'|0/lOX%ZXY+gYZ&cZ[+g[p%Zpq+gqr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p$f%Z$f$g+g$g#BY%Z#BY#BZ+g#BZ$IS%Z$IS$I_+g$I_$JT%Z$JT$JU+g$JU$KV%Z$KV$KW+g$KW&FU%Z&FU&FV+g&FV;'S%Z;'S;=`+a<%l?HT%Z?HT?HU+g?HUO%Z07[.ST(X#S$i&j'}0/lO!^&c!_#o&c#p;'S&c;'S;=`&w<%lO&c07[.n_$i&j(Wp(Z!b'}0/lOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z)3p/x`$i&j!p),Q(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`0z!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(KW1V`#v(Ch$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`2X!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(KW2d_#v(Ch$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'At3l_(V':f$i&j(Z!bOY4kYZ5qZr4krs7nsw4kwx5qx!^4k!^!_8p!_#O4k#O#P5q#P#o4k#o#p8p#p;'S4k;'S;=`:X<%lO4k(^4r_$i&j(Z!bOY4kYZ5qZr4krs7nsw4kwx5qx!^4k!^!_8p!_#O4k#O#P5q#P#o4k#o#p8p#p;'S4k;'S;=`:X<%lO4k&z5vX$i&jOr5qrs6cs!^5q!^!_6y!_#o5q#o#p6y#p;'S5q;'S;=`7h<%lO5q&z6jT$d`$i&jO!^&c!_#o&c#p;'S&c;'S;=`&w<%lO&c`6|TOr6yrs7]s;'S6y;'S;=`7b<%lO6y`7bO$d``7eP;=`<%l6y&z7kP;=`<%l5q(^7w]$d`$i&j(Z!bOY&}YZ&cZw&}wx&cx!^&}!^!_'}!_#O&}#O#P&c#P#o&}#o#p'}#p;'S&};'S;=`(l<%lO&}!r8uZ(Z!bOY8pYZ6yZr8prs9hsw8pwx6yx#O8p#O#P6y#P;'S8p;'S;=`:R<%lO8p!r9oU$d`(Z!bOY'}Zw'}x#O'}#P;'S'};'S;=`(f<%lO'}!r:UP;=`<%l8p(^:[P;=`<%l4k%9[:hh$i&j(Wp(Z!bOY%ZYZ&cZq%Zqr<Srs&}st%ZtuCruw%Zwx(rx!^%Z!^!_*g!_!c%Z!c!}Cr!}#O%Z#O#P&c#P#R%Z#R#SCr#S#T%Z#T#oCr#o#p*g#p$g%Z$g;'SCr;'S;=`El<%lOCr(r<__WS$i&j(Wp(Z!bOY<SYZ&cZr<Srs=^sw<Swx@nx!^<S!^!_Bm!_#O<S#O#P>`#P#o<S#o#pBm#p;'S<S;'S;=`Cl<%lO<S(Q=g]WS$i&j(Z!bOY=^YZ&cZw=^wx>`x!^=^!^!_?q!_#O=^#O#P>`#P#o=^#o#p?q#p;'S=^;'S;=`@h<%lO=^&n>gXWS$i&jOY>`YZ&cZ!^>`!^!_?S!_#o>`#o#p?S#p;'S>`;'S;=`?k<%lO>`S?XSWSOY?SZ;'S?S;'S;=`?e<%lO?SS?hP;=`<%l?S&n?nP;=`<%l>`!f?xWWS(Z!bOY?qZw?qwx?Sx#O?q#O#P?S#P;'S?q;'S;=`@b<%lO?q!f@eP;=`<%l?q(Q@kP;=`<%l=^'`@w]WS$i&j(WpOY@nYZ&cZr@nrs>`s!^@n!^!_Ap!_#O@n#O#P>`#P#o@n#o#pAp#p;'S@n;'S;=`Bg<%lO@ntAwWWS(WpOYApZrAprs?Ss#OAp#O#P?S#P;'SAp;'S;=`Ba<%lOAptBdP;=`<%lAp'`BjP;=`<%l@n#WBvYWS(Wp(Z!bOYBmZrBmrs?qswBmwxApx#OBm#O#P?S#P;'SBm;'S;=`Cf<%lOBm#WCiP;=`<%lBm(rCoP;=`<%l<S%9[C}i$i&j(o%1l(Wp(Z!bOY%ZYZ&cZr%Zrs&}st%ZtuCruw%Zwx(rx!Q%Z!Q![Cr![!^%Z!^!_*g!_!c%Z!c!}Cr!}#O%Z#O#P&c#P#R%Z#R#SCr#S#T%Z#T#oCr#o#p*g#p$g%Z$g;'SCr;'S;=`El<%lOCr%9[EoP;=`<%lCr07[FRk$i&j(Wp(Z!b$]#t(T,2j(e$I[OY%ZYZ&cZr%Zrs&}st%ZtuEruw%Zwx(rx}%Z}!OGv!O!Q%Z!Q![Er![!^%Z!^!_*g!_!c%Z!c!}Er!}#O%Z#O#P&c#P#R%Z#R#SEr#S#T%Z#T#oEr#o#p*g#p$g%Z$g;'SEr;'S;=`I|<%lOEr+dHRk$i&j(Wp(Z!b$]#tOY%ZYZ&cZr%Zrs&}st%ZtuGvuw%Zwx(rx}%Z}!OGv!O!Q%Z!Q![Gv![!^%Z!^!_*g!_!c%Z!c!}Gv!}#O%Z#O#P&c#P#R%Z#R#SGv#S#T%Z#T#oGv#o#p*g#p$g%Z$g;'SGv;'S;=`Iv<%lOGv+dIyP;=`<%lGv07[JPP;=`<%lEr(KWJ_`$i&j(Wp(Z!b#p(ChOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(KWKl_$i&j$Q(Ch(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z,#xLva(z+JY$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sv%ZvwM{wx(rx!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(KWNW`$i&j#z(Ch(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'At! c_(Y';W$i&j(WpOY!!bYZ!#hZr!!brs!#hsw!!bwx!$xx!^!!b!^!_!%z!_#O!!b#O#P!#h#P#o!!b#o#p!%z#p;'S!!b;'S;=`!'c<%lO!!b'l!!i_$i&j(WpOY!!bYZ!#hZr!!brs!#hsw!!bwx!$xx!^!!b!^!_!%z!_#O!!b#O#P!#h#P#o!!b#o#p!%z#p;'S!!b;'S;=`!'c<%lO!!b&z!#mX$i&jOw!#hwx6cx!^!#h!^!_!$Y!_#o!#h#o#p!$Y#p;'S!#h;'S;=`!$r<%lO!#h`!$]TOw!$Ywx7]x;'S!$Y;'S;=`!$l<%lO!$Y`!$oP;=`<%l!$Y&z!$uP;=`<%l!#h'l!%R]$d`$i&j(WpOY(rYZ&cZr(rrs&cs!^(r!^!_)r!_#O(r#O#P&c#P#o(r#o#p)r#p;'S(r;'S;=`*a<%lO(r!Q!&PZ(WpOY!%zYZ!$YZr!%zrs!$Ysw!%zwx!&rx#O!%z#O#P!$Y#P;'S!%z;'S;=`!']<%lO!%z!Q!&yU$d`(WpOY)rZr)rs#O)r#P;'S)r;'S;=`*Z<%lO)r!Q!'`P;=`<%l!%z'l!'fP;=`<%l!!b/5|!'t_!l/.^$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z#&U!)O_!k!Lf$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z-!n!*[b$i&j(Wp(Z!b(U%&f#q(ChOY%ZYZ&cZr%Zrs&}sw%Zwx(rxz%Zz{!+d{!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(KW!+o`$i&j(Wp(Z!b#n(ChOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z+;x!,|`$i&j(Wp(Z!br+4YOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z,$U!.Z_!]+Jf$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z07[!/ec$i&j(Wp(Z!b!Q.2^OY%ZYZ&cZr%Zrs&}sw%Zwx(rx!O%Z!O!P!0p!P!Q%Z!Q![!3Y![!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z#%|!0ya$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!O%Z!O!P!2O!P!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z#%|!2Z_![!L^$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad!3eg$i&j(Wp(Z!bs'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q![!3Y![!^%Z!^!_*g!_!g%Z!g!h!4|!h#O%Z#O#P&c#P#R%Z#R#S!3Y#S#X%Z#X#Y!4|#Y#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad!5Vg$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx{%Z{|!6n|}%Z}!O!6n!O!Q%Z!Q![!8S![!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S!8S#S#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad!6wc$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q![!8S![!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S!8S#S#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad!8_c$i&j(Wp(Z!bs'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q![!8S![!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S!8S#S#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z07[!9uf$i&j(Wp(Z!b#o(ChOY!;ZYZ&cZr!;Zrs!<nsw!;Zwx!Lcxz!;Zz{#-}{!P!;Z!P!Q#/d!Q!^!;Z!^!_#(i!_!`#7S!`!a#8i!a!}!;Z!}#O#,f#O#P!Dy#P#o!;Z#o#p#(i#p;'S!;Z;'S;=`#-w<%lO!;Z?O!;fb$i&j(Wp(Z!b!X7`OY!;ZYZ&cZr!;Zrs!<nsw!;Zwx!Lcx!P!;Z!P!Q#&`!Q!^!;Z!^!_#(i!_!}!;Z!}#O#,f#O#P!Dy#P#o!;Z#o#p#(i#p;'S!;Z;'S;=`#-w<%lO!;Z>^!<w`$i&j(Z!b!X7`OY!<nYZ&cZw!<nwx!=yx!P!<n!P!Q!Eq!Q!^!<n!^!_!Gr!_!}!<n!}#O!KS#O#P!Dy#P#o!<n#o#p!Gr#p;'S!<n;'S;=`!L]<%lO!<n<z!>Q^$i&j!X7`OY!=yYZ&cZ!P!=y!P!Q!>|!Q!^!=y!^!_!@c!_!}!=y!}#O!CW#O#P!Dy#P#o!=y#o#p!@c#p;'S!=y;'S;=`!Ek<%lO!=y<z!?Td$i&j!X7`O!^&c!_#W&c#W#X!>|#X#Z&c#Z#[!>|#[#]&c#]#^!>|#^#a&c#a#b!>|#b#g&c#g#h!>|#h#i&c#i#j!>|#j#k!>|#k#m&c#m#n!>|#n#o&c#p;'S&c;'S;=`&w<%lO&c7`!@hX!X7`OY!@cZ!P!@c!P!Q!AT!Q!}!@c!}#O!Ar#O#P!Bq#P;'S!@c;'S;=`!CQ<%lO!@c7`!AYW!X7`#W#X!AT#Z#[!AT#]#^!AT#a#b!AT#g#h!AT#i#j!AT#j#k!AT#m#n!AT7`!AuVOY!ArZ#O!Ar#O#P!B[#P#Q!@c#Q;'S!Ar;'S;=`!Bk<%lO!Ar7`!B_SOY!ArZ;'S!Ar;'S;=`!Bk<%lO!Ar7`!BnP;=`<%l!Ar7`!BtSOY!@cZ;'S!@c;'S;=`!CQ<%lO!@c7`!CTP;=`<%l!@c<z!C][$i&jOY!CWYZ&cZ!^!CW!^!_!Ar!_#O!CW#O#P!DR#P#Q!=y#Q#o!CW#o#p!Ar#p;'S!CW;'S;=`!Ds<%lO!CW<z!DWX$i&jOY!CWYZ&cZ!^!CW!^!_!Ar!_#o!CW#o#p!Ar#p;'S!CW;'S;=`!Ds<%lO!CW<z!DvP;=`<%l!CW<z!EOX$i&jOY!=yYZ&cZ!^!=y!^!_!@c!_#o!=y#o#p!@c#p;'S!=y;'S;=`!Ek<%lO!=y<z!EnP;=`<%l!=y>^!Ezl$i&j(Z!b!X7`OY&}YZ&cZw&}wx&cx!^&}!^!_'}!_#O&}#O#P&c#P#W&}#W#X!Eq#X#Z&}#Z#[!Eq#[#]&}#]#^!Eq#^#a&}#a#b!Eq#b#g&}#g#h!Eq#h#i&}#i#j!Eq#j#k!Eq#k#m&}#m#n!Eq#n#o&}#o#p'}#p;'S&};'S;=`(l<%lO&}8r!GyZ(Z!b!X7`OY!GrZw!Grwx!@cx!P!Gr!P!Q!Hl!Q!}!Gr!}#O!JU#O#P!Bq#P;'S!Gr;'S;=`!J|<%lO!Gr8r!Hse(Z!b!X7`OY'}Zw'}x#O'}#P#W'}#W#X!Hl#X#Z'}#Z#[!Hl#[#]'}#]#^!Hl#^#a'}#a#b!Hl#b#g'}#g#h!Hl#h#i'}#i#j!Hl#j#k!Hl#k#m'}#m#n!Hl#n;'S'};'S;=`(f<%lO'}8r!JZX(Z!bOY!JUZw!JUwx!Arx#O!JU#O#P!B[#P#Q!Gr#Q;'S!JU;'S;=`!Jv<%lO!JU8r!JyP;=`<%l!JU8r!KPP;=`<%l!Gr>^!KZ^$i&j(Z!bOY!KSYZ&cZw!KSwx!CWx!^!KS!^!_!JU!_#O!KS#O#P!DR#P#Q!<n#Q#o!KS#o#p!JU#p;'S!KS;'S;=`!LV<%lO!KS>^!LYP;=`<%l!KS>^!L`P;=`<%l!<n=l!Ll`$i&j(Wp!X7`OY!LcYZ&cZr!Lcrs!=ys!P!Lc!P!Q!Mn!Q!^!Lc!^!_# o!_!}!Lc!}#O#%P#O#P!Dy#P#o!Lc#o#p# o#p;'S!Lc;'S;=`#&Y<%lO!Lc=l!Mwl$i&j(Wp!X7`OY(rYZ&cZr(rrs&cs!^(r!^!_)r!_#O(r#O#P&c#P#W(r#W#X!Mn#X#Z(r#Z#[!Mn#[#](r#]#^!Mn#^#a(r#a#b!Mn#b#g(r#g#h!Mn#h#i(r#i#j!Mn#j#k!Mn#k#m(r#m#n!Mn#n#o(r#o#p)r#p;'S(r;'S;=`*a<%lO(r8Q# vZ(Wp!X7`OY# oZr# ors!@cs!P# o!P!Q#!i!Q!}# o!}#O#$R#O#P!Bq#P;'S# o;'S;=`#$y<%lO# o8Q#!pe(Wp!X7`OY)rZr)rs#O)r#P#W)r#W#X#!i#X#Z)r#Z#[#!i#[#])r#]#^#!i#^#a)r#a#b#!i#b#g)r#g#h#!i#h#i)r#i#j#!i#j#k#!i#k#m)r#m#n#!i#n;'S)r;'S;=`*Z<%lO)r8Q#$WX(WpOY#$RZr#$Rrs!Ars#O#$R#O#P!B[#P#Q# o#Q;'S#$R;'S;=`#$s<%lO#$R8Q#$vP;=`<%l#$R8Q#$|P;=`<%l# o=l#%W^$i&j(WpOY#%PYZ&cZr#%Prs!CWs!^#%P!^!_#$R!_#O#%P#O#P!DR#P#Q!Lc#Q#o#%P#o#p#$R#p;'S#%P;'S;=`#&S<%lO#%P=l#&VP;=`<%l#%P=l#&]P;=`<%l!Lc?O#&kn$i&j(Wp(Z!b!X7`OY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#W%Z#W#X#&`#X#Z%Z#Z#[#&`#[#]%Z#]#^#&`#^#a%Z#a#b#&`#b#g%Z#g#h#&`#h#i%Z#i#j#&`#j#k#&`#k#m%Z#m#n#&`#n#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z9d#(r](Wp(Z!b!X7`OY#(iZr#(irs!Grsw#(iwx# ox!P#(i!P!Q#)k!Q!}#(i!}#O#+`#O#P!Bq#P;'S#(i;'S;=`#,`<%lO#(i9d#)th(Wp(Z!b!X7`OY*gZr*grs'}sw*gwx)rx#O*g#P#W*g#W#X#)k#X#Z*g#Z#[#)k#[#]*g#]#^#)k#^#a*g#a#b#)k#b#g*g#g#h#)k#h#i*g#i#j#)k#j#k#)k#k#m*g#m#n#)k#n;'S*g;'S;=`+Z<%lO*g9d#+gZ(Wp(Z!bOY#+`Zr#+`rs!JUsw#+`wx#$Rx#O#+`#O#P!B[#P#Q#(i#Q;'S#+`;'S;=`#,Y<%lO#+`9d#,]P;=`<%l#+`9d#,cP;=`<%l#(i?O#,o`$i&j(Wp(Z!bOY#,fYZ&cZr#,frs!KSsw#,fwx#%Px!^#,f!^!_#+`!_#O#,f#O#P!DR#P#Q!;Z#Q#o#,f#o#p#+`#p;'S#,f;'S;=`#-q<%lO#,f?O#-tP;=`<%l#,f?O#-zP;=`<%l!;Z07[#.[b$i&j(Wp(Z!b(O0/l!X7`OY!;ZYZ&cZr!;Zrs!<nsw!;Zwx!Lcx!P!;Z!P!Q#&`!Q!^!;Z!^!_#(i!_!}!;Z!}#O#,f#O#P!Dy#P#o!;Z#o#p#(i#p;'S!;Z;'S;=`#-w<%lO!;Z07[#/o_$i&j(Wp(Z!bT0/lOY#/dYZ&cZr#/drs#0nsw#/dwx#4Ox!^#/d!^!_#5}!_#O#/d#O#P#1p#P#o#/d#o#p#5}#p;'S#/d;'S;=`#6|<%lO#/d06j#0w]$i&j(Z!bT0/lOY#0nYZ&cZw#0nwx#1px!^#0n!^!_#3R!_#O#0n#O#P#1p#P#o#0n#o#p#3R#p;'S#0n;'S;=`#3x<%lO#0n05W#1wX$i&jT0/lOY#1pYZ&cZ!^#1p!^!_#2d!_#o#1p#o#p#2d#p;'S#1p;'S;=`#2{<%lO#1p0/l#2iST0/lOY#2dZ;'S#2d;'S;=`#2u<%lO#2d0/l#2xP;=`<%l#2d05W#3OP;=`<%l#1p01O#3YW(Z!bT0/lOY#3RZw#3Rwx#2dx#O#3R#O#P#2d#P;'S#3R;'S;=`#3r<%lO#3R01O#3uP;=`<%l#3R06j#3{P;=`<%l#0n05x#4X]$i&j(WpT0/lOY#4OYZ&cZr#4Ors#1ps!^#4O!^!_#5Q!_#O#4O#O#P#1p#P#o#4O#o#p#5Q#p;'S#4O;'S;=`#5w<%lO#4O00^#5XW(WpT0/lOY#5QZr#5Qrs#2ds#O#5Q#O#P#2d#P;'S#5Q;'S;=`#5q<%lO#5Q00^#5tP;=`<%l#5Q05x#5zP;=`<%l#4O01p#6WY(Wp(Z!bT0/lOY#5}Zr#5}rs#3Rsw#5}wx#5Qx#O#5}#O#P#2d#P;'S#5};'S;=`#6v<%lO#5}01p#6yP;=`<%l#5}07[#7PP;=`<%l#/d)3h#7ab$i&j$Q(Ch(Wp(Z!b!X7`OY!;ZYZ&cZr!;Zrs!<nsw!;Zwx!Lcx!P!;Z!P!Q#&`!Q!^!;Z!^!_#(i!_!}!;Z!}#O#,f#O#P!Dy#P#o!;Z#o#p#(i#p;'S!;Z;'S;=`#-w<%lO!;ZAt#8vb$Z#t$i&j(Wp(Z!b!X7`OY!;ZYZ&cZr!;Zrs!<nsw!;Zwx!Lcx!P!;Z!P!Q#&`!Q!^!;Z!^!_#(i!_!}!;Z!}#O#,f#O#P!Dy#P#o!;Z#o#p#(i#p;'S!;Z;'S;=`#-w<%lO!;Z'Ad#:Zp$i&j(Wp(Z!bs'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!O%Z!O!P!3Y!P!Q%Z!Q![#<_![!^%Z!^!_*g!_!g%Z!g!h!4|!h#O%Z#O#P&c#P#R%Z#R#S#<_#S#U%Z#U#V#?i#V#X%Z#X#Y!4|#Y#b%Z#b#c#>_#c#d#Bq#d#l%Z#l#m#Es#m#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#<jk$i&j(Wp(Z!bs'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!O%Z!O!P!3Y!P!Q%Z!Q![#<_![!^%Z!^!_*g!_!g%Z!g!h!4|!h#O%Z#O#P&c#P#R%Z#R#S#<_#S#X%Z#X#Y!4|#Y#b%Z#b#c#>_#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#>j_$i&j(Wp(Z!bs'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#?rd$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!R#AQ!R!S#AQ!S!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#AQ#S#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#A]f$i&j(Wp(Z!bs'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!R#AQ!R!S#AQ!S!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#AQ#S#b%Z#b#c#>_#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#Bzc$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!Y#DV!Y!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#DV#S#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#Dbe$i&j(Wp(Z!bs'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!Y#DV!Y!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#DV#S#b%Z#b#c#>_#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#E|g$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q![#Ge![!^%Z!^!_*g!_!c%Z!c!i#Ge!i#O%Z#O#P&c#P#R%Z#R#S#Ge#S#T%Z#T#Z#Ge#Z#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'Ad#Gpi$i&j(Wp(Z!bs'9tOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q![#Ge![!^%Z!^!_*g!_!c%Z!c!i#Ge!i#O%Z#O#P&c#P#R%Z#R#S#Ge#S#T%Z#T#Z#Ge#Z#b%Z#b#c#>_#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z*)x#Il_!g$b$i&j$O)Lv(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z)[#Jv_al$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z04f#LS^h#)`#R-<U(Wp(Z!b$n7`OY*gZr*grs'}sw*gwx)rx!P*g!P!Q#MO!Q!^*g!^!_#Mt!_!`$ f!`#O*g#P;'S*g;'S;=`+Z<%lO*g(n#MXX$k&j(Wp(Z!bOY*gZr*grs'}sw*gwx)rx#O*g#P;'S*g;'S;=`+Z<%lO*g(El#M}Z#r(Ch(Wp(Z!bOY*gZr*grs'}sw*gwx)rx!_*g!_!`#Np!`#O*g#P;'S*g;'S;=`+Z<%lO*g(El#NyX$Q(Ch(Wp(Z!bOY*gZr*grs'}sw*gwx)rx#O*g#P;'S*g;'S;=`+Z<%lO*g(El$ oX#s(Ch(Wp(Z!bOY*gZr*grs'}sw*gwx)rx#O*g#P;'S*g;'S;=`+Z<%lO*g*)x$!ga#`*!Y$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`0z!`!a$#l!a#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(K[$#w_#k(Cl$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z*)x$%Vag!*r#s(Ch$f#|$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`$&[!`!a$'f!a#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(KW$&g_#s(Ch$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(KW$'qa#r(Ch$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`Ka!`!a$(v!a#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(KW$)R`#r(Ch$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(Kd$*`a(r(Ct$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!a%Z!a!b$+e!b#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(KW$+p`$i&j#{(Ch(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z%#`$,}_!|$Ip$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z04f$.X_!S0,v$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(n$/]Z$i&jO!^$0O!^!_$0f!_#i$0O#i#j$0k#j#l$0O#l#m$2^#m#o$0O#o#p$0f#p;'S$0O;'S;=`$4i<%lO$0O(n$0VT_#S$i&jO!^&c!_#o&c#p;'S&c;'S;=`&w<%lO&c#S$0kO_#S(n$0p[$i&jO!Q&c!Q![$1f![!^&c!_!c&c!c!i$1f!i#T&c#T#Z$1f#Z#o&c#o#p$3|#p;'S&c;'S;=`&w<%lO&c(n$1kZ$i&jO!Q&c!Q![$2^![!^&c!_!c&c!c!i$2^!i#T&c#T#Z$2^#Z#o&c#p;'S&c;'S;=`&w<%lO&c(n$2cZ$i&jO!Q&c!Q![$3U![!^&c!_!c&c!c!i$3U!i#T&c#T#Z$3U#Z#o&c#p;'S&c;'S;=`&w<%lO&c(n$3ZZ$i&jO!Q&c!Q![$0O![!^&c!_!c&c!c!i$0O!i#T&c#T#Z$0O#Z#o&c#p;'S&c;'S;=`&w<%lO&c#S$4PR!Q![$4Y!c!i$4Y#T#Z$4Y#S$4]S!Q![$4Y!c!i$4Y#T#Z$4Y#q#r$0f(n$4lP;=`<%l$0O#1[$4z_!Y#)l$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(KW$6U`#x(Ch$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z+;p$7c_$i&j(Wp(Z!b(a+4QOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z07[$8qk$i&j(Wp(Z!b(T,2j$_#t(e$I[OY%ZYZ&cZr%Zrs&}st%Ztu$8buw%Zwx(rx}%Z}!O$:f!O!Q%Z!Q![$8b![!^%Z!^!_*g!_!c%Z!c!}$8b!}#O%Z#O#P&c#P#R%Z#R#S$8b#S#T%Z#T#o$8b#o#p*g#p$g%Z$g;'S$8b;'S;=`$<l<%lO$8b+d$:qk$i&j(Wp(Z!b$_#tOY%ZYZ&cZr%Zrs&}st%Ztu$:fuw%Zwx(rx}%Z}!O$:f!O!Q%Z!Q![$:f![!^%Z!^!_*g!_!c%Z!c!}$:f!}#O%Z#O#P&c#P#R%Z#R#S$:f#S#T%Z#T#o$:f#o#p*g#p$g%Z$g;'S$:f;'S;=`$<f<%lO$:f+d$<iP;=`<%l$:f07[$<oP;=`<%l$8b#Jf$<{X!_#Hb(Wp(Z!bOY*gZr*grs'}sw*gwx)rx#O*g#P;'S*g;'S;=`+Z<%lO*g,#x$=sa(y+JY$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p#q$+e#q;'S%Z;'S;=`+a<%lO%Z)>v$?V_!^(CdvBr$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z?O$@a_!q7`$i&j(Wp(Z!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z07[$Aq|$i&j(Wp(Z!b'|0/l$]#t(T,2j(e$I[OX%ZXY+gYZ&cZ[+g[p%Zpq+gqr%Zrs&}st%ZtuEruw%Zwx(rx}%Z}!OGv!O!Q%Z!Q![Er![!^%Z!^!_*g!_!c%Z!c!}Er!}#O%Z#O#P&c#P#R%Z#R#SEr#S#T%Z#T#oEr#o#p*g#p$f%Z$f$g+g$g#BYEr#BY#BZ$A`#BZ$ISEr$IS$I_$A`$I_$JTEr$JT$JU$A`$JU$KVEr$KV$KW$A`$KW&FUEr&FU&FV$A`&FV;'SEr;'S;=`I|<%l?HTEr?HT?HU$A`?HUOEr07[$D|k$i&j(Wp(Z!b'}0/l$]#t(T,2j(e$I[OY%ZYZ&cZr%Zrs&}st%ZtuEruw%Zwx(rx}%Z}!OGv!O!Q%Z!Q![Er![!^%Z!^!_*g!_!c%Z!c!}Er!}#O%Z#O#P&c#P#R%Z#R#SEr#S#T%Z#T#oEr#o#p*g#p$g%Z$g;'SEr;'S;=`I|<%lOEr\",tokenizers:[zO,_O,EO,LO,2,3,4,5,6,7,8,9,10,11,12,13,14,RO,new df(\"$S~RRtu[#O#Pg#S#T#|~_P#o#pb~gOx~~jVO#i!P#i#j!U#j#l!P#l#m!q#m;'S!P;'S;=`#v<%lO!P~!UO!U~~!XS!Q![!e!c!i!e#T#Z!e#o#p#Z~!hR!Q![!q!c!i!q#T#Z!q~!tR!Q![!}!c!i!}#T#Z!}~#QR!Q![!P!c!i!P#T#Z!P~#^R!Q![#g!c!i#g#T#Z#g~#jS!Q![#g!c!i#g#T#Z#g#q#r!P~#yP;=`<%l!P~$RO(c~~\",141,340),new df(\"j~RQYZXz{^~^O(Q~~aP!P!Qd~iO(R~~\",25,323)],topRules:{Script:[0,7],SingleExpression:[1,276],SingleClassItem:[2,277]},dialects:{jsx:0,ts:15175},dynamicPrecedences:{80:1,82:1,94:1,169:1,199:1},specialized:[{term:327,get:t=>VO[t]||-1},{term:343,get:t=>WO[t]||-1},{term:95,get:t=>DO[t]||-1}],tokenPrec:15201}),jO=[nu(\"function ${name}(${params}) {\\n\\t${}\\n}\",{label:\"function\",detail:\"definition\",type:\"keyword\"}),nu(\"for (let ${index} = 0; ${index} < ${bound}; ${index}++) {\\n\\t${}\\n}\",{label:\"for\",detail:\"loop\",type:\"keyword\"}),nu(\"for (let ${name} of ${collection}) {\\n\\t${}\\n}\",{label:\"for\",detail:\"of loop\",type:\"keyword\"}),nu(\"do {\\n\\t${}\\n} while (${})\",{label:\"do\",detail:\"loop\",type:\"keyword\"}),nu(\"while (${}) {\\n\\t${}\\n}\",{label:\"while\",detail:\"loop\",type:\"keyword\"}),nu(\"try {\\n\\t${}\\n} catch (${error}) {\\n\\t${}\\n}\",{label:\"try\",detail:\"/ catch block\",type:\"keyword\"}),nu(\"if (${}) {\\n\\t${}\\n}\",{label:\"if\",detail:\"block\",type:\"keyword\"}),nu(\"if (${}) {\\n\\t${}\\n} else {\\n\\t${}\\n}\",{label:\"if\",detail:\"/ else block\",type:\"keyword\"}),nu(\"class ${name} {\\n\\tconstructor(${params}) {\\n\\t\\t${}\\n\\t}\\n}\",{label:\"class\",detail:\"definition\",type:\"keyword\"}),nu('import {${names}} from \"${module}\"\\n${}',{label:\"import\",detail:\"named\",type:\"keyword\"}),nu('import ${name} from \"${module}\"\\n${}',{label:\"import\",detail:\"default\",type:\"keyword\"})],IO=jO.concat([nu(\"interface ${name} {\\n\\t${}\\n}\",{label:\"interface\",detail:\"definition\",type:\"keyword\"}),nu(\"type ${name} = ${type}\",{label:\"type\",detail:\"definition\",type:\"keyword\"}),nu(\"enum ${name} {\\n\\t${}\\n}\",{label:\"enum\",detail:\"definition\",type:\"keyword\"})]),GO=new Aa,NO=new Set([\"Script\",\"Block\",\"FunctionExpression\",\"FunctionDeclaration\",\"ArrowFunction\",\"MethodDeclaration\",\"ForStatement\"]);function UO(t){return(e,i)=>{let n=e.node.getChild(\"VariableDefinition\");return n&&i(n,t),!0}}const HO=[\"FunctionDeclaration\"],FO={FunctionDeclaration:UO(\"function\"),ClassDeclaration:UO(\"class\"),ClassExpression:()=>!0,EnumDeclaration:UO(\"constant\"),TypeAliasDeclaration:UO(\"type\"),NamespaceDeclaration:UO(\"namespace\"),VariableDefinition(t,e){t.matchContext(HO)||e(t,\"variable\")},TypeDefinition(t,e){e(t,\"type\")},__proto__:null};function KO(t,e){let i=GO.get(e);if(i)return i;let n=[],r=!0;function s(e,i){let r=t.sliceString(e.from,e.to);n.push({label:r,type:i})}return e.cursor(da.IncludeAnonymous).iterate(e=>{if(r)r=!1;else if(e.name){let t=FO[e.name];if(t&&t(e,s)||NO.has(e.name))return!1}else if(e.to-e.from>8192){for(let i of KO(t,e.node))n.push(i);return!1}}),GO.set(e,n),n}const JO=/^[\\w$\\xa1-\\uffff][\\w$\\d\\xa1-\\uffff]*$/,tp=[\"TemplateString\",\"String\",\"RegExp\",\"LineComment\",\"BlockComment\",\"VariableDefinition\",\"TypeDefinition\",\"Label\",\"PropertyDefinition\",\"PropertyName\",\"PrivatePropertyDefinition\",\"PrivatePropertyName\",\"JSXText\",\"JSXAttributeValue\",\"JSXOpenTag\",\"JSXCloseTag\",\"JSXSelfClosingTag\",\".\",\"?.\"];function ep(t){let e=Cl(t.state).resolveInner(t.pos,-1);if(tp.indexOf(e.name)>-1)return null;let i=\"VariableName\"==e.name||e.to-e.from<20&&JO.test(t.state.sliceDoc(e.from,e.to));if(!i&&!t.explicit)return null;let n=[];for(let r=e;r;r=r.parent)NO.has(r.name)&&(n=n.concat(KO(t.state.doc,r)));return{options:n,from:i?e.from:t.pos,validFor:JO}}const ip=Tl.define({name:\"javascript\",parser:BO.configure({props:[Gl.add({IfStatement:eh({except:/^\\s*({|else\\b)/}),TryStatement:eh({except:/^\\s*({|catch\\b|finally\\b)/}),LabeledStatement:t=>t.baseIndent,SwitchBody:t=>{let e=t.textAfter,i=/^\\s*\\}/.test(e),n=/^\\s*(case|default)\\b/.test(e);return t.baseIndent+(i?0:n?1:2)*t.unit},Block:Jl({closing:\"}\"}),ArrowFunction:t=>t.baseIndent+t.unit,\"TemplateString BlockComment\":()=>null,\"Statement Property\":eh({except:/^\\s*{/}),JSXElement(t){let e=/^\\s*<\\//.test(t.textAfter);return t.lineIndent(t.node.from)+(e?0:t.unit)},JSXEscape(t){let e=/\\s*\\}/.test(t.textAfter);return t.lineIndent(t.node.from)+(e?0:t.unit)},\"JSXOpenTag JSXSelfClosingTag\":t=>t.column(t.node.from)+t.unit}),nh.add({\"Block ClassBody SwitchBody EnumBody ObjectExpression ArrayExpression ObjectType\":rh,BlockComment:t=>({from:t.from+2,to:t.to-2})})]}),languageData:{closeBrackets:{brackets:[\"(\",\"[\",\"{\",\"'\",'\"',\"`\"]},commentTokens:{line:\"//\",block:{open:\"/*\",close:\"*/\"}},indentOnInput:/^\\s*(?:case |default:|\\{|\\}|<\\/)$/,wordChars:\"$\"}}),np={test:t=>/^JSX/.test(t.name),facet:yl({commentTokens:{block:{open:\"{/*\",close:\"*/}\"}}})},rp=ip.configure({dialect:\"ts\"},\"typescript\"),sp=ip.configure({dialect:\"jsx\",props:[kl.add(t=>t.isTop?[np]:void 0)]}),op=ip.configure({dialect:\"jsx ts\",props:[kl.add(t=>t.isTop?[np]:void 0)]},\"typescript\");let ap=t=>({label:t,type:\"keyword\"});const lp=\"break case const continue default delete export extends false finally in instanceof let new return static super switch this throw true typeof var yield\".split(\" \").map(ap),hp=lp.concat([\"declare\",\"implements\",\"private\",\"protected\",\"public\"].map(ap));function cp(t={}){let e=t.jsx?t.typescript?op:sp:t.typescript?rp:ip,i=t.typescript?IO.concat(hp):jO.concat(lp);return new Ll(e,[ip.data.of({autocomplete:(n=tp,r=ic(i),t=>{for(let e=Cl(t.state).resolveInner(t.pos,-1);e;e=e.parent){if(n.indexOf(e.name)>-1)return null;if(e.type.isTop)break}return r(t)})}),ip.data.of({autocomplete:ep}),t.jsx?fp:[]]);var n,r}function up(t,e,i=t.length){for(let n=null==e?void 0:e.firstChild;n;n=n.nextSibling)if(\"JSXIdentifier\"==n.name||\"JSXBuiltin\"==n.name||\"JSXNamespacedName\"==n.name||\"JSXMemberExpression\"==n.name)return t.sliceString(n.from,Math.min(n.to,i));return\"\"}const dp=\"object\"==typeof navigator&&/Android\\b/.test(navigator.userAgent),fp=fs.inputHandler.of((t,e,i,n,r)=>{if((dp?t.composing:t.compositionStarted)||t.state.readOnly||e!=i||\">\"!=n&&\"/\"!=n||!ip.isActiveAt(t.state,e,-1))return!1;let s=r(),{state:o}=s,a=o.changeByRange(t=>{var e;let i,{head:r}=t,s=Cl(o).resolveInner(r-1,-1);if(\"JSXStartTag\"==s.name&&(s=s.parent),o.doc.sliceString(r-1,r)!=n||\"JSXAttributeValue\"==s.name&&s.to>r);else{if(\">\"==n&&\"JSXFragmentTag\"==s.name)return{range:t,changes:{from:r,insert:\"</>\"}};if(\"/\"==n&&\"JSXStartCloseTag\"==s.name){let t=s.parent,n=t.parent;if(n&&t.from==r-2&&((i=up(o.doc,n.firstChild,r))||\"JSXFragmentTag\"==(null===(e=n.firstChild)||void 0===e?void 0:e.name))){let t=`${i}>`;return{range:Y.cursor(r+t.length,-1),changes:{from:r,insert:t}}}}else if(\">\"==n){let e=function(t){for(;;){if(\"JSXOpenTag\"==t.name||\"JSXSelfClosingTag\"==t.name||\"JSXFragmentTag\"==t.name)return t;if(\"JSXEscape\"==t.name||!t.parent)return null;t=t.parent}}(s);if(e&&\"JSXOpenTag\"==e.name&&!/^\\/?>|^<\\//.test(o.doc.sliceString(r,r+2))&&(i=up(o.doc,e,r)))return{range:t,changes:{from:r,insert:`</${i}>`}}}}return{range:t}});return!a.changes.empty&&(t.dispatch([s,o.update(a,{userEvent:\"input.complete\",scrollIntoView:!0})]),!0)}),Op=[\"_blank\",\"_self\",\"_top\",\"_parent\"],pp=[\"ascii\",\"utf-8\",\"utf-16\",\"latin1\",\"latin1\"],mp=[\"get\",\"post\",\"put\",\"delete\"],gp=[\"application/x-www-form-urlencoded\",\"multipart/form-data\",\"text/plain\"],Qp=[\"true\",\"false\"],bp={},vp={a:{attrs:{href:null,ping:null,type:null,media:null,target:Op,hreflang:null}},abbr:bp,address:bp,area:{attrs:{alt:null,coords:null,href:null,target:null,ping:null,media:null,hreflang:null,type:null,shape:[\"default\",\"rect\",\"circle\",\"poly\"]}},article:bp,aside:bp,audio:{attrs:{src:null,mediagroup:null,crossorigin:[\"anonymous\",\"use-credentials\"],preload:[\"none\",\"metadata\",\"auto\"],autoplay:[\"autoplay\"],loop:[\"loop\"],controls:[\"controls\"]}},b:bp,base:{attrs:{href:null,target:Op}},bdi:bp,bdo:bp,blockquote:{attrs:{cite:null}},body:bp,br:bp,button:{attrs:{form:null,formaction:null,name:null,value:null,autofocus:[\"autofocus\"],disabled:[\"autofocus\"],formenctype:gp,formmethod:mp,formnovalidate:[\"novalidate\"],formtarget:Op,type:[\"submit\",\"reset\",\"button\"]}},canvas:{attrs:{width:null,height:null}},caption:bp,center:bp,cite:bp,code:bp,col:{attrs:{span:null}},colgroup:{attrs:{span:null}},command:{attrs:{type:[\"command\",\"checkbox\",\"radio\"],label:null,icon:null,radiogroup:null,command:null,title:null,disabled:[\"disabled\"],checked:[\"checked\"]}},data:{attrs:{value:null}},datagrid:{attrs:{disabled:[\"disabled\"],multiple:[\"multiple\"]}},datalist:{attrs:{data:null}},dd:bp,del:{attrs:{cite:null,datetime:null}},details:{attrs:{open:[\"open\"]}},dfn:bp,div:bp,dl:bp,dt:bp,em:bp,embed:{attrs:{src:null,type:null,width:null,height:null}},eventsource:{attrs:{src:null}},fieldset:{attrs:{disabled:[\"disabled\"],form:null,name:null}},figcaption:bp,figure:bp,footer:bp,form:{attrs:{action:null,name:null,\"accept-charset\":pp,autocomplete:[\"on\",\"off\"],enctype:gp,method:mp,novalidate:[\"novalidate\"],target:Op}},h1:bp,h2:bp,h3:bp,h4:bp,h5:bp,h6:bp,head:{children:[\"title\",\"base\",\"link\",\"style\",\"meta\",\"script\",\"noscript\",\"command\"]},header:bp,hgroup:bp,hr:bp,html:{attrs:{manifest:null}},i:bp,iframe:{attrs:{src:null,srcdoc:null,name:null,width:null,height:null,sandbox:[\"allow-top-navigation\",\"allow-same-origin\",\"allow-forms\",\"allow-scripts\"],seamless:[\"seamless\"]}},img:{attrs:{alt:null,src:null,ismap:null,usemap:null,width:null,height:null,crossorigin:[\"anonymous\",\"use-credentials\"]}},input:{attrs:{alt:null,dirname:null,form:null,formaction:null,height:null,list:null,max:null,maxlength:null,min:null,name:null,pattern:null,placeholder:null,size:null,src:null,step:null,value:null,width:null,accept:[\"audio/*\",\"video/*\",\"image/*\"],autocomplete:[\"on\",\"off\"],autofocus:[\"autofocus\"],checked:[\"checked\"],disabled:[\"disabled\"],formenctype:gp,formmethod:mp,formnovalidate:[\"novalidate\"],formtarget:Op,multiple:[\"multiple\"],readonly:[\"readonly\"],required:[\"required\"],type:[\"hidden\",\"text\",\"search\",\"tel\",\"url\",\"email\",\"password\",\"datetime\",\"date\",\"month\",\"week\",\"time\",\"datetime-local\",\"number\",\"range\",\"color\",\"checkbox\",\"radio\",\"file\",\"submit\",\"image\",\"reset\",\"button\"]}},ins:{attrs:{cite:null,datetime:null}},kbd:bp,keygen:{attrs:{challenge:null,form:null,name:null,autofocus:[\"autofocus\"],disabled:[\"disabled\"],keytype:[\"RSA\"]}},label:{attrs:{for:null,form:null}},legend:bp,li:{attrs:{value:null}},link:{attrs:{href:null,type:null,hreflang:null,media:null,sizes:[\"all\",\"16x16\",\"16x16 32x32\",\"16x16 32x32 64x64\"]}},map:{attrs:{name:null}},mark:bp,menu:{attrs:{label:null,type:[\"list\",\"context\",\"toolbar\"]}},meta:{attrs:{content:null,charset:pp,name:[\"viewport\",\"application-name\",\"author\",\"description\",\"generator\",\"keywords\"],\"http-equiv\":[\"content-language\",\"content-type\",\"default-style\",\"refresh\"]}},meter:{attrs:{value:null,min:null,low:null,high:null,max:null,optimum:null}},nav:bp,noscript:bp,object:{attrs:{data:null,type:null,name:null,usemap:null,form:null,width:null,height:null,typemustmatch:[\"typemustmatch\"]}},ol:{attrs:{reversed:[\"reversed\"],start:null,type:[\"1\",\"a\",\"A\",\"i\",\"I\"]},children:[\"li\",\"script\",\"template\",\"ul\",\"ol\"]},optgroup:{attrs:{disabled:[\"disabled\"],label:null}},option:{attrs:{disabled:[\"disabled\"],label:null,selected:[\"selected\"],value:null}},output:{attrs:{for:null,form:null,name:null}},p:bp,param:{attrs:{name:null,value:null}},pre:bp,progress:{attrs:{value:null,max:null}},q:{attrs:{cite:null}},rp:bp,rt:bp,ruby:bp,samp:bp,script:{attrs:{type:[\"text/javascript\"],src:null,async:[\"async\"],defer:[\"defer\"],charset:pp}},section:bp,select:{attrs:{form:null,name:null,size:null,autofocus:[\"autofocus\"],disabled:[\"disabled\"],multiple:[\"multiple\"]}},slot:{attrs:{name:null}},small:bp,source:{attrs:{src:null,type:null,media:null}},span:bp,strong:bp,style:{attrs:{type:[\"text/css\"],media:null,scoped:null}},sub:bp,summary:bp,sup:bp,table:bp,tbody:bp,td:{attrs:{colspan:null,rowspan:null,headers:null}},template:bp,textarea:{attrs:{dirname:null,form:null,maxlength:null,name:null,placeholder:null,rows:null,cols:null,autofocus:[\"autofocus\"],disabled:[\"disabled\"],readonly:[\"readonly\"],required:[\"required\"],wrap:[\"soft\",\"hard\"]}},tfoot:bp,th:{attrs:{colspan:null,rowspan:null,headers:null,scope:[\"row\",\"col\",\"rowgroup\",\"colgroup\"]}},thead:bp,time:{attrs:{datetime:null}},title:bp,tr:bp,track:{attrs:{src:null,label:null,default:null,kind:[\"subtitles\",\"captions\",\"descriptions\",\"chapters\",\"metadata\"],srclang:null}},ul:{children:[\"li\",\"script\",\"template\",\"ul\",\"ol\"]},var:bp,video:{attrs:{src:null,poster:null,width:null,height:null,crossorigin:[\"anonymous\",\"use-credentials\"],preload:[\"auto\",\"metadata\",\"none\"],autoplay:[\"autoplay\"],mediagroup:[\"movie\"],muted:[\"muted\"],controls:[\"controls\"]}},wbr:bp},wp={accesskey:null,class:null,contenteditable:Qp,contextmenu:null,dir:[\"ltr\",\"rtl\",\"auto\"],draggable:[\"true\",\"false\",\"auto\"],dropzone:[\"copy\",\"move\",\"link\",\"string:\",\"file:\"],hidden:[\"hidden\"],id:null,inert:[\"inert\"],itemid:null,itemprop:null,itemref:null,itemscope:[\"itemscope\"],itemtype:null,lang:[\"ar\",\"bn\",\"de\",\"en-GB\",\"en-US\",\"es\",\"fr\",\"hi\",\"id\",\"ja\",\"pa\",\"pt\",\"ru\",\"tr\",\"zh\"],spellcheck:Qp,autocorrect:Qp,autocapitalize:Qp,style:null,tabindex:null,title:null,translate:[\"yes\",\"no\"],rel:[\"stylesheet\",\"alternate\",\"author\",\"bookmark\",\"help\",\"license\",\"next\",\"nofollow\",\"noreferrer\",\"prefetch\",\"prev\",\"search\",\"tag\"],role:\"alert application article banner button cell checkbox complementary contentinfo dialog document feed figure form grid gridcell heading img list listbox listitem main navigation region row rowgroup search switch tab table tabpanel textbox timer\".split(\" \"),\"aria-activedescendant\":null,\"aria-atomic\":Qp,\"aria-autocomplete\":[\"inline\",\"list\",\"both\",\"none\"],\"aria-busy\":Qp,\"aria-checked\":[\"true\",\"false\",\"mixed\",\"undefined\"],\"aria-controls\":null,\"aria-describedby\":null,\"aria-disabled\":Qp,\"aria-dropeffect\":null,\"aria-expanded\":[\"true\",\"false\",\"undefined\"],\"aria-flowto\":null,\"aria-grabbed\":[\"true\",\"false\",\"undefined\"],\"aria-haspopup\":Qp,\"aria-hidden\":Qp,\"aria-invalid\":[\"true\",\"false\",\"grammar\",\"spelling\"],\"aria-label\":null,\"aria-labelledby\":null,\"aria-level\":null,\"aria-live\":[\"off\",\"polite\",\"assertive\"],\"aria-multiline\":Qp,\"aria-multiselectable\":Qp,\"aria-owns\":null,\"aria-posinset\":null,\"aria-pressed\":[\"true\",\"false\",\"mixed\",\"undefined\"],\"aria-readonly\":Qp,\"aria-relevant\":null,\"aria-required\":Qp,\"aria-selected\":[\"true\",\"false\",\"undefined\"],\"aria-setsize\":null,\"aria-sort\":[\"ascending\",\"descending\",\"none\",\"other\"],\"aria-valuemax\":null,\"aria-valuemin\":null,\"aria-valuenow\":null,\"aria-valuetext\":null},xp=\"beforeunload copy cut dragstart dragover dragleave dragenter dragend drag paste focus blur change click load mousedown mouseenter mouseleave mouseup keydown keyup resize scroll unload\".split(\" \").map(t=>\"on\"+t);for(let Mb of xp)wp[Mb]=null;class Sp{constructor(t,e){this.tags={...vp,...t},this.globalAttrs={...wp,...e},this.allTags=Object.keys(this.tags),this.globalAttrNames=Object.keys(this.globalAttrs)}}function yp(t,e,i=t.length){if(!e)return\"\";let n=e.firstChild,r=n&&n.getChild(\"TagName\");return r?t.sliceString(r.from,Math.min(r.to,i)):\"\"}function kp(t,e=!1){for(;t;t=t.parent)if(\"Element\"==t.name){if(!e)return t;e=!1}return null}function $p(t,e,i){let n=i.tags[yp(t,kp(e))];return(null==n?void 0:n.children)||i.allTags}function Pp(t,e){let i=[];for(let n=kp(e);n&&!n.type.isTop;n=kp(n.parent)){let r=yp(t,n);if(r&&\"CloseTag\"==n.lastChild.name)break;r&&i.indexOf(r)<0&&(\"EndTag\"==e.name||e.from>=n.firstChild.to)&&i.push(r)}return i}Sp.default=new Sp;const Tp=/^[:\\-\\.\\w\\u00b7-\\uffff]*$/;function Cp(t,e,i,n,r){let s=/\\s*>/.test(t.sliceDoc(r,r+5))?\"\":\">\",o=kp(i,\"StartTag\"==i.name||\"TagName\"==i.name);return{from:n,to:r,options:$p(t.doc,o,e).map(t=>({label:t,type:\"type\"})).concat(Pp(t.doc,i).map((t,e)=>({label:\"/\"+t,apply:\"/\"+t+s,type:\"type\",boost:99-e}))),validFor:/^\\/?[:\\-\\.\\w\\u00b7-\\uffff]*$/}}function Zp(t,e,i,n){let r=/\\s*>/.test(t.sliceDoc(n,n+5))?\"\":\">\";return{from:i,to:n,options:Pp(t.doc,e).map((t,e)=>({label:t,apply:t+r,type:\"type\",boost:99-e})),validFor:Tp}}function Xp(t,e){let{state:i,pos:n}=e,r=Cl(i).resolveInner(n,-1),s=r.resolve(n);for(let o,a=n;s==r&&(o=r.childBefore(a));){let t=o.lastChild;if(!t||!t.type.isError||t.from<t.to)break;s=r=o,a=t.from}return\"TagName\"==r.name?r.parent&&/CloseTag$/.test(r.parent.name)?Zp(i,r,r.from,n):Cp(i,t,r,r.from,n):\"StartTag\"==r.name||\"IncompleteTag\"==r.name?Cp(i,t,r,n,n):\"StartCloseTag\"==r.name||\"IncompleteCloseTag\"==r.name?Zp(i,r,n,n):\"OpenTag\"==r.name||\"SelfClosingTag\"==r.name||\"AttributeName\"==r.name?function(t,e,i,n,r){let s=kp(i),o=s?e.tags[yp(t.doc,s)]:null,a=o&&o.attrs?Object.keys(o.attrs):[];return{from:n,to:r,options:(o&&!1===o.globalAttrs?a:a.length?a.concat(e.globalAttrNames):e.globalAttrNames).map(t=>({label:t,type:\"property\"})),validFor:Tp}}(i,t,r,\"AttributeName\"==r.name?r.from:n,n):\"Is\"==r.name||\"AttributeValue\"==r.name||\"UnquotedAttributeValue\"==r.name?function(t,e,i,n,r){var s;let o,a=null===(s=i.parent)||void 0===s?void 0:s.getChild(\"AttributeName\"),l=[];if(a){let s=t.sliceDoc(a.from,a.to),h=e.globalAttrs[s];if(!h){let n=kp(i),r=n?e.tags[yp(t.doc,n)]:null;h=(null==r?void 0:r.attrs)&&r.attrs[s]}if(h){let e=t.sliceDoc(n,r).toLowerCase(),i='\"',s='\"';/^['\"]/.test(e)?(o='\"'==e[0]?/^[^\"]*$/:/^[^']*$/,i=\"\",s=t.sliceDoc(r,r+1)==e[0]?\"\":e[0],e=e.slice(1),n++):o=/^[^\\s<>='\"]*$/;for(let t of h)l.push({label:t,apply:i+t+s,type:\"constant\"})}}return{from:n,to:r,options:l,validFor:o}}(i,t,r,\"Is\"==r.name?n:r.from,n):!e.explicit||\"Element\"!=s.name&&\"Text\"!=s.name&&\"Document\"!=s.name?null:function(t,e,i,n){let r=[],s=0;for(let o of $p(t.doc,i,e))r.push({label:\"<\"+o,type:\"type\"});for(let o of Pp(t.doc,i))r.push({label:\"</\"+o+\">\",type:\"type\",boost:99-s++});return{from:n,to:n,options:r,validFor:/^<\\/?[:\\-\\.\\w\\u00b7-\\uffff]*$/}}(i,t,r,n)}function Ap(t){let{extraTags:e,extraGlobalAttributes:i}=t,n=i||e?new Sp(e,i):Sp.default;return t=>Xp(n,t)}const Mp=ip.parser.configure({top:\"SingleExpression\"}),Rp=[{tag:\"script\",attrs:t=>\"text/typescript\"==t.type||\"ts\"==t.lang,parser:rp.parser},{tag:\"script\",attrs:t=>\"text/babel\"==t.type||\"text/jsx\"==t.type,parser:sp.parser},{tag:\"script\",attrs:t=>\"text/typescript-jsx\"==t.type,parser:op.parser},{tag:\"script\",attrs:t=>/^(importmap|speculationrules|application\\/(.+\\+)?json)$/i.test(t.type),parser:Mp},{tag:\"script\",attrs:t=>!t.type||/^(?:text|application)\\/(?:x-)?(?:java|ecma)script$|^module$|^$/i.test(t.type),parser:ip.parser},{tag:\"style\",attrs:t=>(!t.lang||\"css\"==t.lang)&&(!t.type||/^(text\\/)?(x-)?(stylesheet|css)$/i.test(t.type)),parser:XO.parser}],zp=[{name:\"style\",parser:XO.parser.configure({top:\"Styles\"})}].concat(xp.map(t=>({name:t,parser:ip.parser}))),_p=Tl.define({name:\"html\",parser:Ff.configure({props:[Gl.add({Element(t){let e=/^(\\s*)(<\\/)?/.exec(t.textAfter);return t.node.to<=t.pos+e[0].length?t.continue():t.lineIndent(t.node.from)+(e[2]?0:t.unit)},\"OpenTag CloseTag SelfClosingTag\":t=>t.column(t.node.from)+t.unit,Document(t){if(t.pos+/\\s*/.exec(t.textAfter)[0].length<t.node.to)return t.continue();let e,i=null;for(let n=t.node;;){let t=n.lastChild;if(!t||\"Element\"!=t.name||t.to!=n.to)break;i=n=t}return i&&(!(e=i.lastChild)||\"CloseTag\"!=e.name&&\"SelfClosingTag\"!=e.name)?t.lineIndent(i.from)+t.unit:null}}),nh.add({Element(t){let e=t.firstChild,i=t.lastChild;return e&&\"OpenTag\"==e.name?{from:e.to,to:\"CloseTag\"==i.name?i.from:t.to}:null}}),Wh.add({\"OpenTag CloseTag\":t=>t.getChild(\"TagName\")})]}),languageData:{commentTokens:{block:{open:\"\\x3c!--\",close:\"--\\x3e\"}},indentOnInput:/^\\s*<\\/\\w+\\W$/,wordChars:\"-_\"}}),Ep=_p.configure({wrap:eO(Rp,zp)});function Yp(t={}){let e,i=\"\";!1===t.matchClosingTags&&(i=\"noMatch\"),!0===t.selfClosingTags&&(i=(i?i+\" \":\"\")+\"selfClosing\"),(t.nestedLanguages&&t.nestedLanguages.length||t.nestedAttributes&&t.nestedAttributes.length)&&(e=eO((t.nestedLanguages||[]).concat(Rp),(t.nestedAttributes||[]).concat(zp)));let n=e?_p.configure({wrap:e,dialect:i}):i?Ep.configure({dialect:i}):Ep;return new Ll(n,[Ep.data.of({autocomplete:Ap(t)}),!1!==t.autoCloseTags?qp:[],cp().support,new Ll(XO,XO.data.of({autocomplete:ZO})).support])}const Lp=new Set(\"area base br col command embed frame hr img input keygen link meta param source track wbr menuitem\".split(\" \")),qp=fs.inputHandler.of((t,e,i,n,r)=>{if(t.composing||t.state.readOnly||e!=i||\">\"!=n&&\"/\"!=n||!Ep.isActiveAt(t.state,e,-1))return!1;let s=r(),{state:o}=s,a=o.changeByRange(t=>{var e,i,r;let s,a=o.doc.sliceString(t.from-1,t.to)==n,{head:l}=t,h=Cl(o).resolveInner(l,-1);if(a&&\">\"==n&&\"EndTag\"==h.name){let n=h.parent;if(\"CloseTag\"!=(null===(i=null===(e=n.parent)||void 0===e?void 0:e.lastChild)||void 0===i?void 0:i.name)&&(s=yp(o.doc,n.parent,l))&&!Lp.has(s)){return{range:t,changes:{from:l,to:l+(\">\"===o.doc.sliceString(l,l+1)?1:0),insert:`</${s}>`}}}}else if(a&&\"/\"==n&&\"IncompleteCloseTag\"==h.name){let t=h.parent;if(h.from==l-2&&\"CloseTag\"!=(null===(r=t.lastChild)||void 0===r?void 0:r.name)&&(s=yp(o.doc,t,l))&&!Lp.has(s)){let t=l+(\">\"===o.doc.sliceString(l,l+1)?1:0),e=`${s}>`;return{range:Y.cursor(l+e.length,-1),changes:{from:l,to:t,insert:e}}}}return{range:t}});return!a.changes.empty&&(t.dispatch([s,o.update(a,{userEvent:\"input.complete\",scrollIntoView:!0})]),!0)}),Vp=yl({commentTokens:{block:{open:\"\\x3c!--\",close:\"--\\x3e\"}}}),Wp=new sa,Dp=Ad.configure({props:[nh.add(t=>!t.is(\"Block\")||t.is(\"Document\")||null!=Bp(t)||function(t){return\"OrderedList\"==t.name||\"BulletList\"==t.name}(t)?void 0:(t,e)=>({from:e.doc.lineAt(t.from).to,to:t.to})),Wp.add(Bp),Gl.add({Document:()=>null}),Sl.add({Document:Vp})]});function Bp(t){let e=/^(?:ATX|Setext)Heading(\\d)$/.exec(t.name);return e?+e[1]:void 0}function jp(t,e){let i=t;for(;;){let t,n=i.nextSibling;if(!n||null!=(t=Bp(n.type))&&t<=e)break;i=n}return i.to}const Ip=ih.of((t,e,i)=>{for(let n=Cl(t).resolveInner(i,-1);n&&!(n.from<e);n=n.parent){let t=n.type.prop(Wp);if(null==t)continue;let e=jp(n,t);if(e>i)return{from:i,to:e}}return null});function Gp(t){return new $l(Vp,t,[],\"markdown\")}const Np=Gp(Dp),Up=Gp(Dp.configure([Hd,Jd,Kd,tf,{props:[nh.add({Table:(t,e)=>({from:e.doc.lineAt(t.from).to,to:t.to})})]}]));class Hp{constructor(t,e,i,n,r,s,o){this.node=t,this.from=e,this.to=i,this.spaceBefore=n,this.spaceAfter=r,this.type=s,this.item=o}blank(t,e=!0){let i=this.spaceBefore+(\"Blockquote\"==this.node.name?\">\":\"\");if(null!=t){for(;i.length<t;)i+=\" \";return i}for(let n=this.to-this.from-i.length-this.spaceAfter.length;n>0;n--)i+=\" \";return i+(e?this.spaceAfter:\"\")}marker(t,e){let i=\"OrderedList\"==this.node.name?String(+Kp(this.item,t)[2]+e):\"\";return this.spaceBefore+i+this.type+this.spaceAfter}}function Fp(t,e){let i=[],n=[];for(let r=t;r;r=r.parent){if(\"FencedCode\"==r.name)return n;\"ListItem\"!=r.name&&\"Blockquote\"!=r.name||i.push(r)}for(let r=i.length-1;r>=0;r--){let t,s=i[r],o=e.lineAt(s.from),a=s.from-o.from;if(\"Blockquote\"==s.name&&(t=/^ *>( ?)/.exec(o.text.slice(a))))n.push(new Hp(s,a,a+t[0].length,\"\",t[1],\">\",null));else if(\"ListItem\"==s.name&&\"OrderedList\"==s.parent.name&&(t=/^( *)\\d+([.)])( *)/.exec(o.text.slice(a)))){let e=t[3],i=t[0].length;e.length>=4&&(e=e.slice(0,e.length-4),i-=4),n.push(new Hp(s.parent,a,a+i,t[1],e,t[2],s))}else if(\"ListItem\"==s.name&&\"BulletList\"==s.parent.name&&(t=/^( *)([-+*])( {1,4}\\[[ xX]\\])?( +)/.exec(o.text.slice(a)))){let e=t[4],i=t[0].length;e.length>4&&(e=e.slice(0,e.length-4),i-=4);let r=t[2];t[3]&&(r+=t[3].replace(/[xX]/,\" \")),n.push(new Hp(s.parent,a,a+i,t[1],e,r,s))}}return n}function Kp(t,e){return/^(\\s*)(\\d+)(?=[.)])/.exec(e.sliceString(t.from,t.from+10))}function Jp(t,e,i,n=0){for(let r=-1,s=t;;){if(\"ListItem\"==s.name){let t=Kp(s,e),o=+t[2];if(r>=0){if(o!=r+1)return;i.push({from:s.from+t[1].length,to:s.from+t[0].length,insert:String(r+2+n)})}r=o}let t=s.nextSibling;if(!t)break;s=t}}function tm(t,e){let i=/^[ \\t]*/.exec(t)[0].length;if(!i||\"\\t\"!=e.facet(Wl))return t;let n=\"\";for(let r=Nt(t,4,i);r>0;)r>=4?(n+=\"\\t\",r-=4):(n+=\" \",r--);return n+t.slice(i)}const em=(t={})=>({state:e,dispatch:i})=>{let n=Cl(e),{doc:r}=e,s=null,o=e.changeByRange(i=>{if(!i.empty||!Up.isActiveAt(e,i.from,-1)&&!Up.isActiveAt(e,i.from,1))return s={range:i};let o=i.from,a=r.lineAt(o),l=Fp(n.resolveInner(o,-1),r);for(;l.length&&l[l.length-1].from>o-a.from;)l.pop();if(!l.length)return s={range:i};let h=l[l.length-1];if(h.to-h.spaceAfter.length>o-a.from)return s={range:i};let c=o>=h.to-h.spaceAfter.length&&!/\\S/.test(a.text.slice(h.to));if(h.item&&c){let i=h.node.firstChild,n=h.node.getChild(\"ListItem\",\"ListItem\");if(i.to>=o||n&&n.to<o||a.from>0&&!/[^\\s>]/.test(r.lineAt(a.from-1).text)||!1===t.nonTightLists){let t,e=l.length>1?l[l.length-2]:null,i=\"\";e&&e.item?(t=a.from+e.from,i=e.marker(r,1)):t=a.from+(e?e.to:0);let n=[{from:t,to:o,insert:i}];return\"OrderedList\"==h.node.name&&Jp(h.item,r,n,-2),e&&\"OrderedList\"==e.node.name&&Jp(e.item,r,n),{range:Y.cursor(t+i.length),changes:n}}{let t=nm(l,e,a);return{range:Y.cursor(o+t.length+1),changes:{from:a.from,insert:t+e.lineBreak}}}}if(\"Blockquote\"==h.node.name&&c&&a.from){let t=r.lineAt(a.from-1),n=/>\\s*$/.exec(t.text);if(n&&n.index==h.from){let r=e.changes([{from:t.from+n.index,to:t.to},{from:a.from+h.from,to:a.to}]);return{range:i.map(r),changes:r}}}let u=[];\"OrderedList\"==h.node.name&&Jp(h.item,r,u);let d=h.item&&h.item.from<a.from,f=\"\";if(!d||/^[\\s\\d.)\\-+*>]*/.exec(a.text)[0].length>=h.to)for(let t=0,e=l.length-1;t<=e;t++)f+=t!=e||d?l[t].blank(t<e?Nt(a.text,4,l[t+1].from)-f.length:null):l[t].marker(r,1);let O=o;for(;O>a.from&&/\\s/.test(a.text.charAt(O-a.from-1));)O--;return f=tm(f,e),function(t,e){if(\"OrderedList\"!=t.name&&\"BulletList\"!=t.name)return!1;let i=t.firstChild,n=t.getChild(\"ListItem\",\"ListItem\");if(!n)return!1;let r=e.lineAt(i.to),s=e.lineAt(n.from),o=/^[\\s>]*$/.test(r.text);return r.number+(o?0:1)<s.number}(h.node,e.doc)&&(f=nm(l,e,a)+e.lineBreak+f),u.push({from:O,to:o,insert:e.lineBreak+f}),{range:Y.cursor(O+f.length+1),changes:u}});return!s&&(i(e.update(o,{scrollIntoView:!0,userEvent:\"input\"})),!0)};function im(t){return\"QuoteMark\"==t.name||\"ListMark\"==t.name}function nm(t,e,i){let n=\"\";for(let r=0,s=t.length-2;r<=s;r++)n+=t[r].blank(r<s?Nt(i.text,4,t[r+1].from)-n.length:null,r<s);return tm(n,e)}const rm=[{key:\"Enter\",run:em()},{key:\"Backspace\",run:({state:t,dispatch:e})=>{let i=Cl(t),n=null,r=t.changeByRange(e=>{let r=e.from,{doc:s}=t;if(e.empty&&Up.isActiveAt(t,e.from)){let e=s.lineAt(r),n=Fp(function(t,e){let i=t.resolveInner(e,-1),n=e;im(i)&&(n=i.from,i=i.parent);for(let r;r=i.childBefore(n);)if(im(r))n=r.from;else{if(\"OrderedList\"!=r.name&&\"BulletList\"!=r.name)break;i=r.lastChild,n=i.to}return i}(i,r),s);if(n.length){let i=n[n.length-1],s=i.to-i.spaceAfter.length+(i.spaceAfter?1:0);if(r-e.from>s&&!/\\S/.test(e.text.slice(s,r-e.from)))return{range:Y.cursor(e.from+s),changes:{from:e.from+s,to:r}};if(r-e.from==s&&(!i.item||e.from<=i.item.from||!/\\S/.test(e.text.slice(0,i.to)))){let n=e.from+i.from;if(i.item&&i.node.from<i.item.from&&/\\S/.test(e.text.slice(i.from,i.to))){let r=i.blank(Nt(e.text,4,i.to)-Nt(e.text,4,i.from));return n==e.from&&(r=tm(r,t)),{range:Y.cursor(n+r.length),changes:{from:n,to:e.from+i.to,insert:r}}}if(n<r)return{range:Y.cursor(n),changes:{from:n,to:r}}}}}return n={range:e}});return!n&&(e(t.update(r,{scrollIntoView:!0,userEvent:\"delete\"})),!0)}}],sm=Yp({matchClosingTags:!1});function om(t={}){let{codeLanguages:e,defaultCodeLanguage:i,addKeymap:n=!0,base:{parser:r}=Np,completeHTMLTags:s=!0,pasteURLAsLink:o=!0,htmlTagLanguage:a=sm}=t;if(!(r instanceof rd))throw new RangeError(\"Base parser provided to `markdown` should be a Markdown parser\");let l,h=t.extensions?[t.extensions]:[],c=[a.support,Ip];o&&c.push(um),i instanceof Ll?(c.push(i.support),l=i.language):i&&(l=i);let u=e||l?(d=e,f=l,t=>{if(t&&d){let e=null;if(t=/\\S*/.exec(t)[0],e=\"function\"==typeof d?d(t):ql.matchLanguageName(d,t,!0),e instanceof ql)return e.support?e.support.language.parser:Al.getSkippingParser(e.load());if(e)return e.parser}return f?f.parser:null}):void 0;var d,f;h.push(function(t){let{codeParser:e,htmlParser:i}=t;return{wrap:_a((t,n)=>{let r=t.type.id;if(!e||r!=ku.CodeBlock&&r!=ku.FencedCode){if(i&&(r==ku.HTMLBlock||r==ku.HTMLTag||r==ku.CommentBlock))return{parser:i,overlay:Md(t.node,t.from,t.to)}}else{let i=\"\";if(r==ku.FencedCode){let e=t.node.getChild(ku.CodeInfo);e&&(i=n.read(e.from,e.to))}let s=e(i);if(s)return{parser:s,overlay:t=>t.type.id==ku.CodeText,bracketed:r==ku.FencedCode}}return null})}}({codeParser:u,htmlParser:a.language.parser})),n&&c.push(tt.high(ws.of(rm)));let O=Gp(r.configure(h));return s&&c.push(O.data.of({autocomplete:am})),new Ll(O,c)}function am(t){let{state:e,pos:i}=t,n=/<[:\\-\\.\\w\\u00b7-\\uffff]*$/.exec(e.sliceDoc(i-25,i));if(!n)return null;let r=Cl(e).resolveInner(i,-1);for(;r&&!r.type.isTop;){if(\"CodeBlock\"==r.name||\"FencedCode\"==r.name||\"ProcessingInstructionBlock\"==r.name||\"CommentBlock\"==r.name||\"Link\"==r.name||\"Image\"==r.name)return null;r=r.parent}return{from:i-n[0].length,to:i,options:hm(),validFor:/^<[:\\-\\.\\w\\u00b7-\\uffff]*$/}}let lm=null;function hm(){if(lm)return lm;let t=(e=new tc(Ct.create({extensions:sm}),0,!0),Xp(Sp.default,e));var e;return lm=t?t.options:[]}const cm=/code|horizontalrule|html|link|comment|processing|escape|entity|image|mark|url/i,um=fs.domEventHandlers({paste:(t,e)=>{var i;let{main:n}=e.state.selection;if(n.empty)return!1;let r=null===(i=t.clipboardData)||void 0===i?void 0:i.getData(\"text/plain\");if(!r||!/^(https?:\\/\\/|mailto:|xmpp:|www\\.)/.test(r))return!1;if(/^www\\./.test(r)&&(r=\"https://\"+r),!Up.isActiveAt(e.state,n.from,1))return!1;let s=Cl(e.state),o=!1;return s.iterate({from:n.from,to:n.to,enter:t=>{(t.from>n.from||cm.test(t.name))&&(o=!0)},leave:t=>{t.to<n.to&&(o=!0)}}),!o&&(e.dispatch({changes:[{from:n.from,insert:\"[\"},{from:n.to,insert:`](${r})`}],userEvent:\"input.paste\",scrollIntoView:!0}),!0)}});function dm(){return dm=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var i=arguments[e];for(var n in i)({}).hasOwnProperty.call(i,n)&&(t[n]=i[n])}return t},dm.apply(null,arguments)}function fm(t,e){return({state:i,dispatch:n})=>{if(i.readOnly)return!1;let r=t(e,i);return!!r&&(n(i.update(r)),!0)}}const Om=fm(vm,0),pm=fm(bm,0),mm=fm((t,e)=>bm(t,e,function(t){let e=[];for(let i of t.selection.ranges){let n=t.doc.lineAt(i.from),r=i.to<=n.to?n:t.doc.lineAt(i.to);r.from>n.from&&r.from==i.to&&(r=i.to==n.to+1?n:t.doc.lineAt(i.to-1));let s=e.length-1;s>=0&&e[s].to>n.from?e[s].to=r.to:e.push({from:n.from+/^\\s*/.exec(n.text)[0].length,to:r.to})}return e}(e)),0);function gm(t,e){let i=t.languageDataAt(\"commentTokens\",e,1);return i.length?i[0]:{}}const Qm=50;function bm(t,e,i=e.selection.ranges){let n=i.map(t=>gm(e,t.from).block);if(!n.every(t=>t))return null;let r=i.map((t,i)=>function(t,{open:e,close:i},n,r){let s,o,a=t.sliceDoc(n-Qm,n),l=t.sliceDoc(r,r+Qm),h=/\\s*$/.exec(a)[0].length,c=/^\\s*/.exec(l)[0].length,u=a.length-h;if(a.slice(u-e.length,u)==e&&l.slice(c,c+i.length)==i)return{open:{pos:n-h,margin:h&&1},close:{pos:r+c,margin:c&&1}};r-n<=2*Qm?s=o=t.sliceDoc(n,r):(s=t.sliceDoc(n,n+Qm),o=t.sliceDoc(r-Qm,r));let d=/^\\s*/.exec(s)[0].length,f=/\\s*$/.exec(o)[0].length,O=o.length-f-i.length;return s.slice(d,d+e.length)==e&&o.slice(O,O+i.length)==i?{open:{pos:n+d+e.length,margin:/\\s/.test(s.charAt(d+e.length))?1:0},close:{pos:r-f-i.length,margin:/\\s/.test(o.charAt(O-1))?1:0}}:null}(e,n[i],t.from,t.to));if(2!=t&&!r.every(t=>t))return{changes:e.changes(i.map((t,e)=>r[e]?[]:[{from:t.from,insert:n[e].open+\" \"},{from:t.to,insert:\" \"+n[e].close}]))};if(1!=t&&r.some(t=>t)){let t=[];for(let e,i=0;i<r.length;i++)if(e=r[i]){let r=n[i],{open:s,close:o}=e;t.push({from:s.pos-r.open.length,to:s.pos+s.margin},{from:o.pos-o.margin,to:o.pos+r.close.length})}return{changes:t}}return null}function vm(t,e,i=e.selection.ranges){let n=[],r=-1;for(let{from:s,to:o}of i){let t=n.length,i=1e9,a=gm(e,s).line;if(a){for(let t=s;t<=o;){let l=e.doc.lineAt(t);if(l.from>r&&(s==o||o>l.from)){r=l.from;let t=/^\\s*/.exec(l.text)[0].length,e=t==l.length,s=l.text.slice(t,t+a.length)==a?t:-1;t<l.text.length&&t<i&&(i=t),n.push({line:l,comment:s,token:a,indent:t,empty:e,single:!1})}t=l.to+1}if(i<1e9)for(let e=t;e<n.length;e++)n[e].indent<n[e].line.text.length&&(n[e].indent=i);n.length==t+1&&(n[t].single=!0)}}if(2!=t&&n.some(t=>t.comment<0&&(!t.empty||t.single))){let t=[];for(let{line:e,token:r,indent:s,empty:o,single:a}of n)!a&&o||t.push({from:e.from+s,insert:r+\" \"});let i=e.changes(t);return{changes:i,selection:e.selection.map(i,1)}}if(1!=t&&n.some(t=>t.comment>=0)){let t=[];for(let{line:e,comment:i,token:r}of n)if(i>=0){let n=e.from+i,s=n+r.length;\" \"==e.text[s-e.from]&&s++,t.push({from:n,to:s})}return{changes:t}}return null}const wm=Ot.define(),xm=Ot.define(),Sm=V.define(),ym=V.define({combine:t=>Zt(t,{minDepth:100,newGroupDelay:500,joinToEvent:(t,e)=>e},{minDepth:Math.max,newGroupDelay:Math.min,joinToEvent:(t,e)=>(i,n)=>t(i,n)||e(i,n)})}),km=N.define({create:()=>Vm.empty,update(t,e){let i=e.state.facet(ym),n=e.annotation(wm);if(n){let r=Xm.fromTransaction(e,n.selection),s=n.side,o=0==s?t.undone:t.done;return o=r?Am(o,o.length,i.minDepth,r):_m(o,e.startState.selection),new Vm(0==s?n.rest:o,0==s?o:n.rest)}let r=e.annotation(xm);if(\"full\"!=r&&\"before\"!=r||(t=t.isolate()),!1===e.annotation(Qt.addToHistory))return e.changes.empty?t:t.addMapping(e.changes.desc);let s=Xm.fromTransaction(e),o=e.annotation(Qt.time),a=e.annotation(Qt.userEvent);return s?t=t.addChanges(s,o,a,i,e):e.selection&&(t=t.addSelection(e.startState.selection,o,a,i.newGroupDelay)),\"full\"!=r&&\"after\"!=r||(t=t.isolate()),t},toJSON:t=>({done:t.done.map(t=>t.toJSON()),undone:t.undone.map(t=>t.toJSON())}),fromJSON:t=>new Vm(t.done.map(Xm.fromJSON),t.undone.map(Xm.fromJSON))});function $m(t,e){return function({state:i,dispatch:n}){if(!e&&i.readOnly)return!1;let r=i.field(km,!1);if(!r)return!1;let s=r.pop(t,i,e);return!!s&&(n(s),!0)}}const Pm=$m(0,!1),Tm=$m(1,!1),Cm=$m(0,!0),Zm=$m(1,!0);class Xm{constructor(t,e,i,n,r){this.changes=t,this.effects=e,this.mapped=i,this.startSelection=n,this.selectionsAfter=r}setSelAfter(t){return new Xm(this.changes,this.effects,this.mapped,this.startSelection,t)}toJSON(){var t,e,i;return{changes:null===(t=this.changes)||void 0===t?void 0:t.toJSON(),mapped:null===(e=this.mapped)||void 0===e?void 0:e.toJSON(),startSelection:null===(i=this.startSelection)||void 0===i?void 0:i.toJSON(),selectionsAfter:this.selectionsAfter.map(t=>t.toJSON())}}static fromJSON(t){return new Xm(t.changes&&Z.fromJSON(t.changes),[],t.mapped&&C.fromJSON(t.mapped),t.startSelection&&Y.fromJSON(t.startSelection),t.selectionsAfter.map(Y.fromJSON))}static fromTransaction(t,e){let i=Rm;for(let n of t.startState.facet(Sm)){let e=n(t);e.length&&(i=i.concat(e))}return!i.length&&t.changes.empty?null:new Xm(t.changes.invert(t.startState.doc),i,void 0,e||t.startState.selection,Rm)}static selection(t){return new Xm(void 0,Rm,void 0,void 0,t)}}function Am(t,e,i,n){let r=e+1>i+20?e-i-1:0,s=t.slice(r,e);return s.push(n),s}function Mm(t,e){return t.length?e.length?t.concat(e):t:e}const Rm=[],zm=200;function _m(t,e){if(t.length){let i=t[t.length-1],n=i.selectionsAfter.slice(Math.max(0,i.selectionsAfter.length-zm));return n.length&&n[n.length-1].eq(e)?t:(n.push(e),Am(t,t.length-1,1e9,i.setSelAfter(n)))}return[Xm.selection([e])]}function Em(t){let e=t[t.length-1],i=t.slice();return i[t.length-1]=e.setSelAfter(e.selectionsAfter.slice(0,e.selectionsAfter.length-1)),i}function Ym(t,e){if(!t.length)return t;let i=t.length,n=Rm;for(;i;){let r=Lm(t[i-1],e,n);if(r.changes&&!r.changes.empty||r.effects.length){let e=t.slice(0,i);return e[i-1]=r,e}e=r.mapped,i--,n=r.selectionsAfter}return n.length?[Xm.selection(n)]:Rm}function Lm(t,e,i){let n=Mm(t.selectionsAfter.length?t.selectionsAfter.map(t=>t.map(e)):Rm,i);if(!t.changes)return Xm.selection(n);let r=t.changes.map(e),s=e.mapDesc(t.changes,!0),o=t.mapped?t.mapped.composeDesc(s):s;return new Xm(r,gt.mapEffects(t.effects,e),o,t.startSelection.map(s),n)}const qm=/^(input\\.type|delete)($|\\.)/;class Vm{constructor(t,e,i=0,n=void 0){this.done=t,this.undone=e,this.prevTime=i,this.prevUserEvent=n}isolate(){return this.prevTime?new Vm(this.done,this.undone):this}addChanges(t,e,i,n,r){let s=this.done,o=s[s.length-1];return s=o&&o.changes&&!o.changes.empty&&t.changes&&(!i||qm.test(i))&&(!o.selectionsAfter.length&&e-this.prevTime<n.newGroupDelay&&n.joinToEvent(r,function(t,e){let i=[],n=!1;return t.iterChangedRanges((t,e)=>i.push(t,e)),e.iterChangedRanges((t,e,r,s)=>{for(let o=0;o<i.length;){let t=i[o++],e=i[o++];s>=t&&r<=e&&(n=!0)}}),n}(o.changes,t.changes))||\"input.type.compose\"==i)?Am(s,s.length-1,n.minDepth,new Xm(t.changes.compose(o.changes),Mm(gt.mapEffects(t.effects,o.changes),o.effects),o.mapped,o.startSelection,Rm)):Am(s,s.length,n.minDepth,t),new Vm(s,Rm,e,i)}addSelection(t,e,i,n){let r=this.done.length?this.done[this.done.length-1].selectionsAfter:Rm;return r.length>0&&e-this.prevTime<n&&i==this.prevUserEvent&&i&&/^select($|\\.)/.test(i)&&(s=r[r.length-1],o=t,s.ranges.length==o.ranges.length&&0===s.ranges.filter((t,e)=>t.empty!=o.ranges[e].empty).length)?this:new Vm(_m(this.done,t),this.undone,e,i);var s,o}addMapping(t){return new Vm(Ym(this.done,t),Ym(this.undone,t),this.prevTime,this.prevUserEvent)}pop(t,e,i){let n=0==t?this.done:this.undone;if(0==n.length)return null;let r=n[n.length-1],s=r.selectionsAfter[0]||e.selection;if(i&&r.selectionsAfter.length)return e.update({selection:r.selectionsAfter[r.selectionsAfter.length-1],annotations:wm.of({side:t,rest:Em(n),selection:s}),userEvent:0==t?\"select.undo\":\"select.redo\",scrollIntoView:!0});if(r.changes){let i=1==n.length?Rm:n.slice(0,n.length-1);return r.mapped&&(i=Ym(i,r.mapped)),e.update({changes:r.changes,selection:r.startSelection,effects:r.effects,annotations:wm.of({side:t,rest:i,selection:s}),filter:!1,userEvent:0==t?\"undo\":\"redo\",scrollIntoView:!0})}return null}}Vm.empty=new Vm(Rm,Rm);const Wm=[{key:\"Mod-z\",run:Pm,preventDefault:!0},{key:\"Mod-y\",mac:\"Mod-Shift-z\",run:Tm,preventDefault:!0},{linux:\"Ctrl-Shift-z\",run:Tm,preventDefault:!0},{key:\"Mod-u\",run:Cm,preventDefault:!0},{key:\"Alt-u\",mac:\"Mod-Shift-u\",run:Zm,preventDefault:!0}];function Dm(t,e){return Y.create(t.ranges.map(e),t.mainIndex)}function Bm(t,e){return t.update({selection:e,scrollIntoView:!0,userEvent:\"select\"})}function jm({state:t,dispatch:e},i){let n=Dm(t.selection,i);return!n.eq(t.selection,!0)&&(e(Bm(t,n)),!0)}function Im(t,e){return Y.cursor(e?t.to:t.from)}function Gm(t,e){return jm(t,i=>i.empty?t.moveByChar(i,e):Im(i,e))}function Nm(t){return t.textDirectionAt(t.state.selection.main.head)==ri.LTR}const Um=t=>Gm(t,!Nm(t)),Hm=t=>Gm(t,Nm(t));function Fm(t,e){return jm(t,i=>i.empty?t.moveByGroup(i,e):Im(i,e))}function Km(t,e,i){if(e.type.prop(i))return!0;let n=e.to-e.from;return n&&(n>2||/[^\\s,.;:]/.test(t.sliceDoc(e.from,e.to)))||e.firstChild}function Jm(t,e,i){let n,r,s=Cl(t).resolveInner(e.head),o=i?sa.closedBy:sa.openedBy;for(let a=e.head;;){let e=i?s.childAfter(a):s.childBefore(a);if(!e)break;Km(t,e,o)?s=e:a=i?e.to:e.from}return r=s.type.prop(o)&&(n=i?jh(t,s.from,1):jh(t,s.to,-1))&&n.matched?i?n.end.to:n.end.from:i?s.to:s.from,Y.cursor(r,i?-1:1)}function tg(t,e){return jm(t,i=>{if(!i.empty)return Im(i,e);let n=t.moveVertically(i,e);return n.head!=i.head?n:t.moveToLineBoundary(i,e)})}const eg=t=>tg(t,!1),ig=t=>tg(t,!0);function ng(t){let e,i=t.scrollDOM.clientHeight<t.scrollDOM.scrollHeight-2,n=0,r=0;if(i){for(let e of t.state.facet(fs.scrollMargins)){let i=e(t);(null==i?void 0:i.top)&&(n=Math.max(null==i?void 0:i.top,n)),(null==i?void 0:i.bottom)&&(r=Math.max(null==i?void 0:i.bottom,r))}e=t.scrollDOM.clientHeight-n-r}else e=(t.dom.ownerDocument.defaultView||window).innerHeight;return{marginTop:n,marginBottom:r,selfScroll:i,height:Math.max(t.defaultLineHeight,e-5)}}function rg(t,e){let i,n=ng(t),{state:r}=t,s=Dm(r.selection,i=>i.empty?t.moveVertically(i,e,n.height):Im(i,e));if(s.eq(r.selection))return!1;if(n.selfScroll){let e=t.coordsAtPos(r.selection.main.head),o=t.scrollDOM.getBoundingClientRect(),a=o.top+n.marginTop,l=o.bottom-n.marginBottom;e&&e.top>a&&e.bottom<l&&(i=fs.scrollIntoView(s.main.head,{y:\"start\",yMargin:e.top-a}))}return t.dispatch(Bm(r,s),{effects:i}),!0}const sg=t=>rg(t,!1),og=t=>rg(t,!0);function ag(t,e,i){let n=t.lineBlockAt(e.head),r=t.moveToLineBoundary(e,i);if(r.head==e.head&&r.head!=(i?n.to:n.from)&&(r=t.moveToLineBoundary(e,i,!1)),!i&&r.head==n.from&&n.length){let i=/^\\s*/.exec(t.state.sliceDoc(n.from,Math.min(n.from+100,n.to)))[0].length;i&&e.head!=n.from+i&&(r=Y.cursor(n.from+i))}return r}function lg(t,e){let i=Dm(t.state.selection,t=>{let i=e(t);return Y.range(t.anchor,i.head,i.goalColumn,i.bidiLevel||void 0)});return!i.eq(t.state.selection)&&(t.dispatch(Bm(t.state,i)),!0)}function hg(t,e){return lg(t,i=>t.moveByChar(i,e))}const cg=t=>hg(t,!Nm(t)),ug=t=>hg(t,Nm(t));function dg(t,e){return lg(t,i=>t.moveByGroup(i,e))}function fg(t,e){return lg(t,i=>t.moveVertically(i,e))}const Og=t=>fg(t,!1),pg=t=>fg(t,!0);function mg(t,e){return lg(t,i=>t.moveVertically(i,e,ng(t).height))}const gg=t=>mg(t,!1),Qg=t=>mg(t,!0),bg=({state:t,dispatch:e})=>(e(Bm(t,{anchor:0})),!0),vg=({state:t,dispatch:e})=>(e(Bm(t,{anchor:t.doc.length})),!0),wg=({state:t,dispatch:e})=>(e(Bm(t,{anchor:t.selection.main.anchor,head:0})),!0),xg=({state:t,dispatch:e})=>(e(Bm(t,{anchor:t.selection.main.anchor,head:t.doc.length})),!0);function Sg(t,e){let{state:i}=t,n=i.selection,r=i.selection.ranges.slice();for(let s of i.selection.ranges){let n=i.doc.lineAt(s.head);if(e?n.to<t.state.doc.length:n.from>0)for(let i=s;;){let s=t.moveVertically(i,e);if(s.head<n.from||s.head>n.to){r.some(t=>t.head==s.head)||r.push(s);break}if(s.head==i.head)break;i=s}}return r.length!=n.ranges.length&&(t.dispatch(Bm(i,Y.create(r,r.length-1))),!0)}function yg(t,e){if(t.state.readOnly)return!1;let i=\"delete.selection\",{state:n}=t,r=n.changeByRange(n=>{let{from:r,to:s}=n;if(r==s){let o=e(n);o<r?(i=\"delete.backward\",o=kg(t,o,!1)):o>r&&(i=\"delete.forward\",o=kg(t,o,!0)),r=Math.min(r,o),s=Math.max(s,o)}else r=kg(t,r,!1),s=kg(t,s,!0);return r==s?{range:n}:{changes:{from:r,to:s},range:Y.cursor(r,r<n.head?-1:1)}});return!r.changes.empty&&(t.dispatch(n.update(r,{scrollIntoView:!0,userEvent:i,effects:\"delete.selection\"==i?fs.announce.of(n.phrase(\"Selection deleted\")):void 0})),!0)}function kg(t,e,i){if(t instanceof fs)for(let n of t.state.facet(fs.atomicRanges).map(e=>e(t)))n.between(e,e,(t,n)=>{t<e&&n>e&&(e=i?n:t)});return e}const $g=(t,e,i)=>yg(t,n=>{let r,s,o=n.from,{state:a}=t,l=a.doc.lineAt(o);if(i&&!e&&o>l.from&&o<l.from+200&&!/[^ \\t]/.test(r=l.text.slice(0,o-l.from))){if(\"\\t\"==r[r.length-1])return o-1;let t=Nt(r,a.tabSize)%Dl(a)||Dl(a);for(let e=0;e<t&&\" \"==r[r.length-1-e];e++)o--;s=o}else s=S(l.text,o-l.from,e,e)+l.from,s==o&&l.number!=(e?a.doc.lines:1)?s+=e?1:-1:!e&&/[\\ufe00-\\ufe0f]/.test(l.text.slice(s-l.from,o-l.from))&&(s=S(l.text,s-l.from,!1,!1)+l.from);return s}),Pg=t=>$g(t,!1,!0),Tg=t=>$g(t,!0,!1),Cg=(t,e)=>yg(t,i=>{let n=i.head,{state:r}=t,s=r.doc.lineAt(n),o=r.charCategorizer(n);for(let t=null;;){if(n==(e?s.to:s.from)){n==i.head&&s.number!=(e?r.doc.lines:1)&&(n+=e?1:-1);break}let a=S(s.text,n-s.from,e)+s.from,l=s.text.slice(Math.min(n,a)-s.from,Math.max(n,a)-s.from),h=o(l);if(null!=t&&h!=t)break;\" \"==l&&n==i.head||(t=h),n=a}return n}),Zg=t=>Cg(t,!1);function Xg(t){let e=[],i=-1;for(let n of t.selection.ranges){let r=t.doc.lineAt(n.from),s=t.doc.lineAt(n.to);if(n.empty||n.to!=s.from||(s=t.doc.lineAt(n.to-1)),i>=r.number){let t=e[e.length-1];t.to=s.to,t.ranges.push(n)}else e.push({from:r.from,to:s.to,ranges:[n]});i=s.number+1}return e}function Ag(t,e,i){if(t.readOnly)return!1;let n=[],r=[];for(let s of Xg(t)){if(i?s.to==t.doc.length:0==s.from)continue;let e=t.doc.lineAt(i?s.to+1:s.from-1),o=e.length+1;if(i){n.push({from:s.to,to:e.to},{from:s.from,insert:e.text+t.lineBreak});for(let e of s.ranges)r.push(Y.range(Math.min(t.doc.length,e.anchor+o),Math.min(t.doc.length,e.head+o)))}else{n.push({from:e.from,to:s.from},{from:s.to,insert:t.lineBreak+e.text});for(let t of s.ranges)r.push(Y.range(t.anchor-o,t.head-o))}}return!!n.length&&(e(t.update({changes:n,scrollIntoView:!0,selection:Y.create(r,t.selection.mainIndex),userEvent:\"move.line\"})),!0)}function Mg(t,e,i){if(t.readOnly)return!1;let n=[];for(let s of Xg(t))i?n.push({from:s.from,insert:t.doc.slice(s.from,s.to)+t.lineBreak}):n.push({from:s.to,insert:t.lineBreak+t.doc.slice(s.from,s.to)});let r=t.changes(n);return e(t.update({changes:r,selection:t.selection.map(r,i?1:-1),scrollIntoView:!0,userEvent:\"input.copyline\"})),!0}const Rg=zg(!1);function zg(t){return({state:e,dispatch:i})=>{if(e.readOnly)return!1;let n=e.changeByRange(i=>{let{from:n,to:r}=i,s=e.doc.lineAt(n),o=!t&&n==r&&function(t,e){if(/\\(\\)|\\[\\]|\\{\\}/.test(t.sliceDoc(e-1,e+1)))return{from:e,to:e};let i,n=Cl(t).resolveInner(e),r=n.childBefore(e),s=n.childAfter(e);return r&&s&&r.to<=e&&s.from>=e&&(i=r.type.prop(sa.closedBy))&&i.indexOf(s.name)>-1&&t.doc.lineAt(r.to).from==t.doc.lineAt(s.from).from&&!/\\S/.test(t.sliceDoc(r.to,s.from))?{from:r.to,to:s.from}:null}(e,n);t&&(n=r=(r<=s.to?s:e.doc.lineAt(r)).to);let a=new Il(e,{simulateBreak:n,simulateDoubleBreak:!!o}),l=jl(a,n);for(null==l&&(l=Nt(/^\\s*/.exec(e.doc.lineAt(n).text)[0],e.tabSize));r<s.to&&/\\s/.test(s.text[r-s.from]);)r++;o?({from:n,to:r}=o):n>s.from&&n<s.from+100&&!/\\S/.test(s.text.slice(0,n))&&(n=s.from);let h=[\"\",Bl(e,l)];return o&&h.push(Bl(e,a.lineIndent(s.from,-1))),{changes:{from:n,to:r,insert:f.of(h)},range:Y.cursor(n+1+h[1].length)}});return i(e.update(n,{scrollIntoView:!0,userEvent:\"input\"})),!0}}function _g(t,e){let i=-1;return t.changeByRange(n=>{let r=[];for(let o=n.from;o<=n.to;){let s=t.doc.lineAt(o);s.number>i&&(n.empty||n.to>s.from)&&(e(s,r,n),i=s.number),o=s.to+1}let s=t.changes(r);return{changes:r,range:Y.range(s.mapPos(n.anchor,1),s.mapPos(n.head,1))}})}const Eg=({state:t,dispatch:e})=>!t.readOnly&&(e(t.update(_g(t,(e,i)=>{i.push({from:e.from,insert:t.facet(Wl)})}),{userEvent:\"input.indent\"})),!0),Yg=({state:t,dispatch:e})=>!t.readOnly&&(e(t.update(_g(t,(e,i)=>{let n=/^\\s*/.exec(e.text)[0];if(!n)return;let r=Nt(n,t.tabSize),s=0,o=Bl(t,Math.max(0,r-Dl(t)));for(;s<n.length&&s<o.length&&n.charCodeAt(s)==o.charCodeAt(s);)s++;i.push({from:e.from+s,to:e.from+n.length,insert:o.slice(s)})}),{userEvent:\"delete.dedent\"})),!0),Lg=[{key:\"Alt-ArrowLeft\",mac:\"Ctrl-ArrowLeft\",run:t=>jm(t,e=>Jm(t.state,e,!Nm(t))),shift:t=>lg(t,e=>Jm(t.state,e,!Nm(t)))},{key:\"Alt-ArrowRight\",mac:\"Ctrl-ArrowRight\",run:t=>jm(t,e=>Jm(t.state,e,Nm(t))),shift:t=>lg(t,e=>Jm(t.state,e,Nm(t)))},{key:\"Alt-ArrowUp\",run:({state:t,dispatch:e})=>Ag(t,e,!1)},{key:\"Shift-Alt-ArrowUp\",run:({state:t,dispatch:e})=>Mg(t,e,!1)},{key:\"Alt-ArrowDown\",run:({state:t,dispatch:e})=>Ag(t,e,!0)},{key:\"Shift-Alt-ArrowDown\",run:({state:t,dispatch:e})=>Mg(t,e,!0)},{key:\"Mod-Alt-ArrowUp\",run:t=>Sg(t,!1)},{key:\"Mod-Alt-ArrowDown\",run:t=>Sg(t,!0)},{key:\"Escape\",run:({state:t,dispatch:e})=>{let i=t.selection,n=null;return i.ranges.length>1?n=Y.create([i.main]):i.main.empty||(n=Y.create([Y.cursor(i.main.head)])),!!n&&(e(Bm(t,n)),!0)}},{key:\"Mod-Enter\",run:zg(!0)},{key:\"Alt-l\",mac:\"Ctrl-l\",run:({state:t,dispatch:e})=>{let i=Xg(t).map(({from:e,to:i})=>Y.range(e,Math.min(i+1,t.doc.length)));return e(t.update({selection:Y.create(i),userEvent:\"select\"})),!0}},{key:\"Mod-i\",run:({state:t,dispatch:e})=>{let i=Dm(t.selection,e=>{let i=Cl(t),n=i.resolveStack(e.from,1);if(e.empty){let t=i.resolveStack(e.from,-1);t.node.from>=n.node.from&&t.node.to<=n.node.to&&(n=t)}for(let t=n;t;t=t.next){let{node:i}=t;if((i.from<e.from&&i.to>=e.to||i.to>e.to&&i.from<=e.from)&&t.next)return Y.range(i.to,i.from)}return e});return!i.eq(t.selection)&&(e(Bm(t,i)),!0)},preventDefault:!0},{key:\"Mod-[\",run:Yg},{key:\"Mod-]\",run:Eg},{key:\"Mod-Alt-\\\\\",run:({state:t,dispatch:e})=>{if(t.readOnly)return!1;let i=Object.create(null),n=new Il(t,{overrideIndentation:t=>{let e=i[t];return null==e?-1:e}}),r=_g(t,(e,r,s)=>{let o=jl(n,e.from);if(null==o)return;/\\S/.test(e.text)||(o=0);let a=/^\\s*/.exec(e.text)[0],l=Bl(t,o);(a!=l||s.from<e.from+a.length)&&(i[e.from]=o,r.push({from:e.from,to:e.from+a.length,insert:l}))});return r.changes.empty||e(t.update(r,{userEvent:\"indent\"})),!0}},{key:\"Shift-Mod-k\",run:t=>{if(t.state.readOnly)return!1;let{state:e}=t,i=e.changes(Xg(e).map(({from:t,to:i})=>(t>0?t--:i<e.doc.length&&i++,{from:t,to:i}))),n=Dm(e.selection,e=>{let i;if(t.lineWrapping){let n=t.lineBlockAt(e.head),r=t.coordsAtPos(e.head,e.assoc||1);r&&(i=n.bottom+t.documentTop-r.bottom+t.defaultLineHeight/2)}return t.moveVertically(e,!0,i)}).map(i);return t.dispatch({changes:i,selection:n,scrollIntoView:!0,userEvent:\"delete.line\"}),!0}},{key:\"Shift-Mod-\\\\\",run:({state:t,dispatch:e})=>function(t,e){let i=!1,n=Dm(t.selection,e=>{let n=jh(t,e.head,-1)||jh(t,e.head,1)||e.head>0&&jh(t,e.head-1,1)||e.head<t.doc.length&&jh(t,e.head+1,-1);if(!n||!n.end)return e;i=!0;let r=n.start.from==e.head?n.end.to:n.end.from;return Y.cursor(r)});return!!i&&(e(Bm(t,n)),!0)}(t,e)},{key:\"Mod-/\",run:t=>{let{state:e}=t,i=e.doc.lineAt(e.selection.main.from),n=gm(t.state,i.from);return n.line?Om(t):!!n.block&&mm(t)}},{key:\"Alt-A\",run:pm},{key:\"Ctrl-m\",mac:\"Shift-Alt-m\",run:t=>(t.setTabFocusMode(),!0)}].concat([{key:\"ArrowLeft\",run:Um,shift:cg,preventDefault:!0},{key:\"Mod-ArrowLeft\",mac:\"Alt-ArrowLeft\",run:t=>Fm(t,!Nm(t)),shift:t=>dg(t,!Nm(t)),preventDefault:!0},{mac:\"Cmd-ArrowLeft\",run:t=>jm(t,e=>ag(t,e,!Nm(t))),shift:t=>lg(t,e=>ag(t,e,!Nm(t))),preventDefault:!0},{key:\"ArrowRight\",run:Hm,shift:ug,preventDefault:!0},{key:\"Mod-ArrowRight\",mac:\"Alt-ArrowRight\",run:t=>Fm(t,Nm(t)),shift:t=>dg(t,Nm(t)),preventDefault:!0},{mac:\"Cmd-ArrowRight\",run:t=>jm(t,e=>ag(t,e,Nm(t))),shift:t=>lg(t,e=>ag(t,e,Nm(t))),preventDefault:!0},{key:\"ArrowUp\",run:eg,shift:Og,preventDefault:!0},{mac:\"Cmd-ArrowUp\",run:bg,shift:wg},{mac:\"Ctrl-ArrowUp\",run:sg,shift:gg},{key:\"ArrowDown\",run:ig,shift:pg,preventDefault:!0},{mac:\"Cmd-ArrowDown\",run:vg,shift:xg},{mac:\"Ctrl-ArrowDown\",run:og,shift:Qg},{key:\"PageUp\",run:sg,shift:gg},{key:\"PageDown\",run:og,shift:Qg},{key:\"Home\",run:t=>jm(t,e=>ag(t,e,!1)),shift:t=>lg(t,e=>ag(t,e,!1)),preventDefault:!0},{key:\"Mod-Home\",run:bg,shift:wg},{key:\"End\",run:t=>jm(t,e=>ag(t,e,!0)),shift:t=>lg(t,e=>ag(t,e,!0)),preventDefault:!0},{key:\"Mod-End\",run:vg,shift:xg},{key:\"Enter\",run:Rg,shift:Rg},{key:\"Mod-a\",run:({state:t,dispatch:e})=>(e(t.update({selection:{anchor:0,head:t.doc.length},userEvent:\"select\"})),!0)},{key:\"Backspace\",run:Pg,shift:Pg,preventDefault:!0},{key:\"Delete\",run:Tg,preventDefault:!0},{key:\"Mod-Backspace\",mac:\"Alt-Backspace\",run:Zg,preventDefault:!0},{key:\"Mod-Delete\",mac:\"Alt-Delete\",run:t=>Cg(t,!0),preventDefault:!0},{mac:\"Mod-Backspace\",run:t=>yg(t,e=>{let i=t.moveToLineBoundary(e,!1).head;return e.head>i?i:Math.max(0,e.head-1)}),preventDefault:!0},{mac:\"Mod-Delete\",run:t=>yg(t,e=>{let i=t.moveToLineBoundary(e,!0).head;return e.head<i?i:Math.min(t.state.doc.length,e.head+1)}),preventDefault:!0}].concat([{key:\"Ctrl-b\",run:Um,shift:cg,preventDefault:!0},{key:\"Ctrl-f\",run:Hm,shift:ug},{key:\"Ctrl-p\",run:eg,shift:Og},{key:\"Ctrl-n\",run:ig,shift:pg},{key:\"Ctrl-a\",run:t=>jm(t,e=>Y.cursor(t.lineBlockAt(e.head).from,1)),shift:t=>lg(t,e=>Y.cursor(t.lineBlockAt(e.head).from))},{key:\"Ctrl-e\",run:t=>jm(t,e=>Y.cursor(t.lineBlockAt(e.head).to,-1)),shift:t=>lg(t,e=>Y.cursor(t.lineBlockAt(e.head).to))},{key:\"Ctrl-d\",run:Tg},{key:\"Ctrl-h\",run:Pg},{key:\"Ctrl-k\",run:t=>yg(t,e=>{let i=t.lineBlockAt(e.head).to;return e.head<i?i:Math.min(t.state.doc.length,e.head+1)})},{key:\"Ctrl-Alt-h\",run:Zg},{key:\"Ctrl-o\",run:({state:t,dispatch:e})=>{if(t.readOnly)return!1;let i=t.changeByRange(t=>({changes:{from:t.from,to:t.to,insert:f.of([\"\",\"\"])},range:Y.cursor(t.from)}));return e(t.update(i,{scrollIntoView:!0,userEvent:\"input\"})),!0}},{key:\"Ctrl-t\",run:({state:t,dispatch:e})=>{if(t.readOnly)return!1;let i=t.changeByRange(e=>{if(!e.empty||0==e.from||e.from==t.doc.length)return{range:e};let i=e.from,n=t.doc.lineAt(i),r=i==n.from?i-1:S(n.text,i-n.from,!1)+n.from,s=i==n.to?i+1:S(n.text,i-n.from,!0)+n.from;return{changes:{from:r,to:s,insert:t.doc.slice(i,s).append(t.doc.slice(r,i))},range:Y.cursor(s)}});return!i.changes.empty&&(e(t.update(i,{scrollIntoView:!0,userEvent:\"move.character\"})),!0)}},{key:\"Ctrl-v\",run:og}].map(t=>({mac:t.key,run:t.run,shift:t.shift})))),qg={key:\"Tab\",run:Eg,shift:Yg},Vg=\"function\"==typeof String.prototype.normalize?t=>t.normalize(\"NFKD\"):t=>t;class Wg{constructor(t,e,i=0,n=t.length,r,s){this.test=s,this.value={from:0,to:0},this.done=!1,this.matches=[],this.buffer=\"\",this.bufferPos=0,this.iter=t.iterRange(i,n),this.bufferStart=i,this.normalize=r?t=>r(Vg(t)):Vg,this.query=this.normalize(e)}peek(){if(this.bufferPos==this.buffer.length){if(this.bufferStart+=this.buffer.length,this.iter.next(),this.iter.done)return-1;this.bufferPos=0,this.buffer=this.iter.value}return y(this.buffer,this.bufferPos)}next(){for(;this.matches.length;)this.matches.pop();return this.nextOverlapping()}nextOverlapping(){for(;;){let t=this.peek();if(t<0)return this.done=!0,this;let e=k(t),i=this.bufferStart+this.bufferPos;this.bufferPos+=$(t);let n=this.normalize(e);if(n.length)for(let r=0,s=i;;r++){let t=n.charCodeAt(r),o=this.match(t,s,this.bufferPos+this.bufferStart);if(r==n.length-1){if(o)return this.value=o,this;break}s==i&&r<e.length&&e.charCodeAt(r)==t&&s++}}}match(t,e,i){let n=null;for(let r=0;r<this.matches.length;r+=2){let e=this.matches[r],s=!1;this.query.charCodeAt(e)==t&&(e==this.query.length-1?n={from:this.matches[r+1],to:i}:(this.matches[r]++,s=!0)),s||(this.matches.splice(r,2),r-=2)}return this.query.charCodeAt(0)==t&&(1==this.query.length?n={from:e,to:i}:this.matches.push(1,e)),n&&this.test&&!this.test(n.from,n.to,this.buffer,this.bufferStart)&&(n=null),n}}\"undefined\"!=typeof Symbol&&(Wg.prototype[Symbol.iterator]=function(){return this});const Dg={from:-1,to:-1,match:/.*/.exec(\"\")},Bg=\"gm\"+(null==/x/.unicode?\"\":\"u\");class jg{constructor(t,e,i,n=0,r=t.length){if(this.text=t,this.to=r,this.curLine=\"\",this.done=!1,this.value=Dg,/\\\\[sWDnr]|\\n|\\r|\\[\\^/.test(e))return new Ng(t,e,i,n,r);this.re=new RegExp(e,Bg+((null==i?void 0:i.ignoreCase)?\"i\":\"\")),this.test=null==i?void 0:i.test,this.iter=t.iter();let s=t.lineAt(n);this.curLineStart=s.from,this.matchPos=Ug(t,n),this.getLine(this.curLineStart)}getLine(t){this.iter.next(t),this.iter.lineBreak?this.curLine=\"\":(this.curLine=this.iter.value,this.curLineStart+this.curLine.length>this.to&&(this.curLine=this.curLine.slice(0,this.to-this.curLineStart)),this.iter.next())}nextLine(){this.curLineStart=this.curLineStart+this.curLine.length+1,this.curLineStart>this.to?this.curLine=\"\":this.getLine(0)}next(){for(let t=this.matchPos-this.curLineStart;;){this.re.lastIndex=t;let e=this.matchPos<=this.to&&this.re.exec(this.curLine);if(e){let i=this.curLineStart+e.index,n=i+e[0].length;if(this.matchPos=Ug(this.text,n+(i==n?1:0)),i==this.curLineStart+this.curLine.length&&this.nextLine(),(i<n||i>this.value.to)&&(!this.test||this.test(i,n,e)))return this.value={from:i,to:n,match:e},this;t=this.matchPos-this.curLineStart}else{if(!(this.curLineStart+this.curLine.length<this.to))return this.done=!0,this;this.nextLine(),t=0}}}}const Ig=new WeakMap;class Gg{constructor(t,e){this.from=t,this.text=e}get to(){return this.from+this.text.length}static get(t,e,i){let n=Ig.get(t);if(!n||n.from>=i||n.to<=e){let n=new Gg(e,t.sliceString(e,i));return Ig.set(t,n),n}if(n.from==e&&n.to==i)return n;let{text:r,from:s}=n;return s>e&&(r=t.sliceString(e,s)+r,s=e),n.to<i&&(r+=t.sliceString(n.to,i)),Ig.set(t,new Gg(s,r)),new Gg(e,r.slice(e-s,i-s))}}class Ng{constructor(t,e,i,n,r){this.text=t,this.to=r,this.done=!1,this.value=Dg,this.matchPos=Ug(t,n),this.re=new RegExp(e,Bg+((null==i?void 0:i.ignoreCase)?\"i\":\"\")),this.test=null==i?void 0:i.test,this.flat=Gg.get(t,n,this.chunkEnd(n+5e3))}chunkEnd(t){return t>=this.to?this.to:this.text.lineAt(t).to}next(){for(;;){let t=this.re.lastIndex=this.matchPos-this.flat.from,e=this.re.exec(this.flat.text);if(e&&!e[0]&&e.index==t&&(this.re.lastIndex=t+1,e=this.re.exec(this.flat.text)),e){let t=this.flat.from+e.index,i=t+e[0].length;if((this.flat.to>=this.to||e.index+e[0].length<=this.flat.text.length-10)&&(!this.test||this.test(t,i,e)))return this.value={from:t,to:i,match:e},this.matchPos=Ug(this.text,i+(t==i?1:0)),this}if(this.flat.to==this.to)return this.done=!0,this;this.flat=Gg.get(this.text,this.flat.from,this.chunkEnd(this.flat.from+2*this.flat.text.length))}}}function Ug(t,e){if(e>=t.length)return e;let i,n=t.lineAt(e);for(;e<n.to&&(i=n.text.charCodeAt(e-n.from))>=56320&&i<57344;)e++;return e}function Hg(t){let e=le(\"input\",{class:\"cm-textfield\",name:\"line\",value:String(t.state.doc.lineAt(t.state.selection.main.head).number)});function i(){let i=/^([+-])?(\\d+)?(:\\d+)?(%)?$/.exec(e.value);if(!i)return;let{state:n}=t,r=n.doc.lineAt(n.selection.main.head),[,s,o,a,l]=i,h=a?+a.slice(1):0,c=o?+o:r.number;if(o&&l){let t=c/100;s&&(t=t*(\"-\"==s?-1:1)+r.number/n.doc.lines),c=Math.round(n.doc.lines*t)}else o&&s&&(c=c*(\"-\"==s?-1:1)+r.number);let u=n.doc.line(Math.max(1,Math.min(n.doc.lines,c))),d=Y.cursor(u.from+Math.max(0,Math.min(h,u.length)));t.dispatch({effects:[Fg.of(!1),fs.scrollIntoView(d.from,{y:\"center\"})],selection:d}),t.focus()}return{dom:le(\"form\",{class:\"cm-gotoLine\",onkeydown:e=>{27==e.keyCode?(e.preventDefault(),t.dispatch({effects:Fg.of(!1)}),t.focus()):13==e.keyCode&&(e.preventDefault(),i())},onsubmit:t=>{t.preventDefault(),i()}},le(\"label\",t.state.phrase(\"Go to line\"),\": \",e),\" \",le(\"button\",{class:\"cm-button\",type:\"submit\"},t.state.phrase(\"go\")),le(\"button\",{name:\"close\",onclick:()=>{t.dispatch({effects:Fg.of(!1)}),t.focus()},\"aria-label\":t.state.phrase(\"close\"),type:\"button\"},[\"×\"]))}}\"undefined\"!=typeof Symbol&&(jg.prototype[Symbol.iterator]=Ng.prototype[Symbol.iterator]=function(){return this});const Fg=gt.define(),Kg=N.define({create:()=>!0,update(t,e){for(let i of e.effects)i.is(Fg)&&(t=i.value);return t},provide:t=>Ao.from(t,t=>t?Hg:null)}),Jg=fs.baseTheme({\".cm-panel.cm-gotoLine\":{padding:\"2px 6px 4px\",position:\"relative\",\"& label\":{fontSize:\"80%\"},\"& [name=close]\":{position:\"absolute\",top:\"0\",bottom:\"0\",right:\"4px\",backgroundColor:\"inherit\",border:\"none\",font:\"inherit\",padding:\"0\"}}}),tQ={highlightWordAroundCursor:!1,minSelectionLength:1,maxMatches:100,wholeWords:!1},eQ=V.define({combine:t=>Zt(t,tQ,{highlightWordAroundCursor:(t,e)=>t||e,minSelectionLength:Math.min,maxMatches:Math.min})});const iQ=Ce.mark({class:\"cm-selectionMatch\"}),nQ=Ce.mark({class:\"cm-selectionMatch cm-selectionMatch-main\"});function rQ(t,e,i,n){return!(0!=i&&t(e.sliceDoc(i-1,i))==kt.Word||n!=e.doc.length&&t(e.sliceDoc(n,n+1))==kt.Word)}const sQ=Wi.fromClass(class{constructor(t){this.decorations=this.getDeco(t)}update(t){(t.selectionSet||t.docChanged||t.viewportChanged)&&(this.decorations=this.getDeco(t.view))}getDeco(t){let e=t.state.facet(eQ),{state:i}=t,n=i.selection;if(n.ranges.length>1)return Ce.none;let r,s=n.main,o=null;if(s.empty){if(!e.highlightWordAroundCursor)return Ce.none;let t=i.wordAt(s.head);if(!t)return Ce.none;o=i.charCategorizer(s.head),r=i.sliceDoc(t.from,t.to)}else{let t=s.to-s.from;if(t<e.minSelectionLength||t>200)return Ce.none;if(e.wholeWords){if(r=i.sliceDoc(s.from,s.to),o=i.charCategorizer(s.head),!rQ(o,i,s.from,s.to)||!function(t,e,i,n){return t(e.sliceDoc(i,i+1))==kt.Word&&t(e.sliceDoc(n-1,n))==kt.Word}(o,i,s.from,s.to))return Ce.none}else if(r=i.sliceDoc(s.from,s.to),!r)return Ce.none}let a=[];for(let l of t.visibleRanges){let t=new Wg(i.doc,r,l.from,l.to);for(;!t.next().done;){let{from:n,to:r}=t.value;if((!o||rQ(o,i,n,r))&&(s.empty&&n<=s.from&&r>=s.to?a.push(nQ.range(n,r)):(n>=s.to||r<=s.from)&&a.push(iQ.range(n,r)),a.length>e.maxMatches))return Ce.none}}return Ce.set(a)}},{decorations:t=>t.decorations}),oQ=fs.baseTheme({\".cm-selectionMatch\":{backgroundColor:\"#99ff7780\"},\".cm-searchMatch .cm-selectionMatch\":{backgroundColor:\"transparent\"}});const aQ=V.define({combine:t=>Zt(t,{top:!1,caseSensitive:!1,literal:!1,regexp:!1,wholeWord:!1,createPanel:t=>new _Q(t),scrollToMatch:t=>fs.scrollIntoView(t)})});class lQ{constructor(t){this.search=t.search,this.caseSensitive=!!t.caseSensitive,this.literal=!!t.literal,this.regexp=!!t.regexp,this.replace=t.replace||\"\",this.valid=!!this.search&&(!this.regexp||function(t){try{return new RegExp(t,Bg),!0}catch(e){return!1}}(this.search)),this.unquoted=this.unquote(this.search),this.wholeWord=!!t.wholeWord}unquote(t){return this.literal?t:t.replace(/\\\\([nrt\\\\])/g,(t,e)=>\"n\"==e?\"\\n\":\"r\"==e?\"\\r\":\"t\"==e?\"\\t\":\"\\\\\")}eq(t){return this.search==t.search&&this.replace==t.replace&&this.caseSensitive==t.caseSensitive&&this.regexp==t.regexp&&this.wholeWord==t.wholeWord}create(){return this.regexp?new pQ(this):new uQ(this)}getCursor(t,e=0,i){let n=t.doc?t:Ct.create({doc:t});return null==i&&(i=n.doc.length),this.regexp?dQ(this,n,e,i):cQ(this,n,e,i)}}class hQ{constructor(t){this.spec=t}}function cQ(t,e,i,n){return new Wg(e.doc,t.unquoted,i,n,t.caseSensitive?void 0:t=>t.toLowerCase(),t.wholeWord?(r=e.doc,s=e.charCategorizer(e.selection.main.head),(t,e,i,n)=>((n>t||n+i.length<e)&&(n=Math.max(0,t-2),i=r.sliceString(n,Math.min(r.length,e+2))),!(s(fQ(i,t-n))==kt.Word&&s(OQ(i,t-n))==kt.Word||s(OQ(i,e-n))==kt.Word&&s(fQ(i,e-n))==kt.Word))):void 0);var r,s}class uQ extends hQ{constructor(t){super(t)}nextMatch(t,e,i){let n=cQ(this.spec,t,i,t.doc.length).nextOverlapping();if(n.done){let i=Math.min(t.doc.length,e+this.spec.unquoted.length);n=cQ(this.spec,t,0,i).nextOverlapping()}return n.done||n.value.from==e&&n.value.to==i?null:n.value}prevMatchInRange(t,e,i){for(let n=i;;){let i=Math.max(e,n-1e4-this.spec.unquoted.length),r=cQ(this.spec,t,i,n),s=null;for(;!r.nextOverlapping().done;)s=r.value;if(s)return s;if(i==e)return null;n-=1e4}}prevMatch(t,e,i){let n=this.prevMatchInRange(t,0,e);return n||(n=this.prevMatchInRange(t,Math.max(0,i-this.spec.unquoted.length),t.doc.length)),!n||n.from==e&&n.to==i?null:n}getReplacement(t){return this.spec.unquote(this.spec.replace)}matchAll(t,e){let i=cQ(this.spec,t,0,t.doc.length),n=[];for(;!i.next().done;){if(n.length>=e)return null;n.push(i.value)}return n}highlight(t,e,i,n){let r=cQ(this.spec,t,Math.max(0,e-this.spec.unquoted.length),Math.min(i+this.spec.unquoted.length,t.doc.length));for(;!r.next().done;)n(r.value.from,r.value.to)}}function dQ(t,e,i,n){return new jg(e.doc,t.search,{ignoreCase:!t.caseSensitive,test:t.wholeWord?(r=e.charCategorizer(e.selection.main.head),(t,e,i)=>!i[0].length||(r(fQ(i.input,i.index))!=kt.Word||r(OQ(i.input,i.index))!=kt.Word)&&(r(OQ(i.input,i.index+i[0].length))!=kt.Word||r(fQ(i.input,i.index+i[0].length))!=kt.Word)):void 0},i,n);var r}function fQ(t,e){return t.slice(S(t,e,!1),e)}function OQ(t,e){return t.slice(e,S(t,e))}class pQ extends hQ{nextMatch(t,e,i){let n=dQ(this.spec,t,i,t.doc.length).next();return n.done&&(n=dQ(this.spec,t,0,e).next()),n.done?null:n.value}prevMatchInRange(t,e,i){for(let n=1;;n++){let r=Math.max(e,i-1e4*n),s=dQ(this.spec,t,r,i),o=null;for(;!s.next().done;)o=s.value;if(o&&(r==e||o.from>r+10))return o;if(r==e)return null}}prevMatch(t,e,i){return this.prevMatchInRange(t,0,e)||this.prevMatchInRange(t,i,t.doc.length)}getReplacement(t){return this.spec.unquote(this.spec.replace).replace(/\\$([$&]|\\d+)/g,(e,i)=>{if(\"&\"==i)return t.match[0];if(\"$\"==i)return\"$\";for(let n=i.length;n>0;n--){let e=+i.slice(0,n);if(e>0&&e<t.match.length)return t.match[e]+i.slice(n)}return e})}matchAll(t,e){let i=dQ(this.spec,t,0,t.doc.length),n=[];for(;!i.next().done;){if(n.length>=e)return null;n.push(i.value)}return n}highlight(t,e,i,n){let r=dQ(this.spec,t,Math.max(0,e-250),Math.min(i+250,t.doc.length));for(;!r.next().done;)n(r.value.from,r.value.to)}}const mQ=gt.define(),gQ=gt.define(),QQ=N.define({create:t=>new bQ(ZQ(t).create(),null),update(t,e){for(let i of e.effects)i.is(mQ)?t=new bQ(i.value.create(),t.panel):i.is(gQ)&&(t=new bQ(t.query,i.value?CQ:null));return t},provide:t=>Ao.from(t,t=>t.panel)});class bQ{constructor(t,e){this.query=t,this.panel=e}}const vQ=Ce.mark({class:\"cm-searchMatch\"}),wQ=Ce.mark({class:\"cm-searchMatch cm-searchMatch-selected\"}),xQ=Wi.fromClass(class{constructor(t){this.view=t,this.decorations=this.highlight(t.state.field(QQ))}update(t){let e=t.state.field(QQ);(e!=t.startState.field(QQ)||t.docChanged||t.selectionSet||t.viewportChanged)&&(this.decorations=this.highlight(e))}highlight({query:t,panel:e}){if(!e||!t.spec.valid)return Ce.none;let{view:i}=this,n=new Et;for(let r=0,s=i.visibleRanges,o=s.length;r<o;r++){let{from:e,to:a}=s[r];for(;r<o-1&&a>s[r+1].from-500;)a=s[++r].to;t.highlight(i.state,e,a,(t,e)=>{let r=i.state.selection.ranges.some(i=>i.from==t&&i.to==e);n.add(t,e,r?wQ:vQ)})}return n.finish()}},{decorations:t=>t.decorations});function SQ(t){return e=>{let i=e.state.field(QQ,!1);return i&&i.query.spec.valid?t(e,i):MQ(e)}}const yQ=SQ((t,{query:e})=>{let{to:i}=t.state.selection.main,n=e.nextMatch(t.state,i,i);if(!n)return!1;let r=Y.single(n.from,n.to),s=t.state.facet(aQ);return t.dispatch({selection:r,effects:[qQ(t,n),s.scrollToMatch(r.main,t)],userEvent:\"select.search\"}),AQ(t),!0}),kQ=SQ((t,{query:e})=>{let{state:i}=t,{from:n}=i.selection.main,r=e.prevMatch(i,n,n);if(!r)return!1;let s=Y.single(r.from,r.to),o=t.state.facet(aQ);return t.dispatch({selection:s,effects:[qQ(t,r),o.scrollToMatch(s.main,t)],userEvent:\"select.search\"}),AQ(t),!0}),$Q=SQ((t,{query:e})=>{let i=e.matchAll(t.state,1e3);return!(!i||!i.length)&&(t.dispatch({selection:Y.create(i.map(t=>Y.range(t.from,t.to))),userEvent:\"select.search.matches\"}),!0)}),PQ=SQ((t,{query:e})=>{let{state:i}=t,{from:n,to:r}=i.selection.main;if(i.readOnly)return!1;let s=e.nextMatch(i,n,n);if(!s)return!1;let o,a,l=s,h=[],c=[];l.from==n&&l.to==r&&(a=i.toText(e.getReplacement(l)),h.push({from:l.from,to:l.to,insert:a}),l=e.nextMatch(i,l.from,l.to),c.push(fs.announce.of(i.phrase(\"replaced match on line $\",i.doc.lineAt(n).number)+\".\")));let u=t.state.changes(h);return l&&(o=Y.single(l.from,l.to).map(u),c.push(qQ(t,l)),c.push(i.facet(aQ).scrollToMatch(o.main,t))),t.dispatch({changes:u,selection:o,effects:c,userEvent:\"input.replace\"}),!0}),TQ=SQ((t,{query:e})=>{if(t.state.readOnly)return!1;let i=e.matchAll(t.state,1e9).map(t=>{let{from:i,to:n}=t;return{from:i,to:n,insert:e.getReplacement(t)}});if(!i.length)return!1;let n=t.state.phrase(\"replaced $ matches\",i.length)+\".\";return t.dispatch({changes:i,effects:fs.announce.of(n),userEvent:\"input.replace.all\"}),!0});function CQ(t){return t.state.facet(aQ).createPanel(t)}function ZQ(t,e){var i,n,r,s,o;let a=t.selection.main,l=a.empty||a.to>a.from+100?\"\":t.sliceDoc(a.from,a.to);if(e&&!l)return e;let h=t.facet(aQ);return new lQ({search:(null!==(i=null==e?void 0:e.literal)&&void 0!==i?i:h.literal)?l:l.replace(/\\n/g,\"\\\\n\"),caseSensitive:null!==(n=null==e?void 0:e.caseSensitive)&&void 0!==n?n:h.caseSensitive,literal:null!==(r=null==e?void 0:e.literal)&&void 0!==r?r:h.literal,regexp:null!==(s=null==e?void 0:e.regexp)&&void 0!==s?s:h.regexp,wholeWord:null!==(o=null==e?void 0:e.wholeWord)&&void 0!==o?o:h.wholeWord})}function XQ(t){let e=To(t,CQ);return e&&e.dom.querySelector(\"[main-field]\")}function AQ(t){let e=XQ(t);e&&e==t.root.activeElement&&e.select()}const MQ=t=>{let e=t.state.field(QQ,!1);if(e&&e.panel){let i=XQ(t);if(i&&i!=t.root.activeElement){let n=ZQ(t.state,e.query.spec);n.valid&&t.dispatch({effects:mQ.of(n)}),i.focus(),i.select()}}else t.dispatch({effects:[gQ.of(!0),e?mQ.of(ZQ(t.state,e.query.spec)):gt.appendConfig.of(WQ)]});return!0},RQ=t=>{let e=t.state.field(QQ,!1);if(!e||!e.panel)return!1;let i=To(t,CQ);return i&&i.dom.contains(t.root.activeElement)&&t.focus(),t.dispatch({effects:gQ.of(!1)}),!0},zQ=[{key:\"Mod-f\",run:MQ,scope:\"editor search-panel\"},{key:\"F3\",run:yQ,shift:kQ,scope:\"editor search-panel\",preventDefault:!0},{key:\"Mod-g\",run:yQ,shift:kQ,scope:\"editor search-panel\",preventDefault:!0},{key:\"Escape\",run:RQ,scope:\"editor search-panel\"},{key:\"Mod-Shift-l\",run:({state:t,dispatch:e})=>{let i=t.selection;if(i.ranges.length>1||i.main.empty)return!1;let{from:n,to:r}=i.main,s=[],o=0;for(let a=new Wg(t.doc,t.sliceDoc(n,r));!a.next().done;){if(s.length>1e3)return!1;a.value.from==n&&(o=s.length),s.push(Y.range(a.value.from,a.value.to))}return e(t.update({selection:Y.create(s,o),userEvent:\"select.search.matches\"})),!0}},{key:\"Mod-Alt-g\",run:t=>{let e=To(t,Hg);if(!e){let i=[Fg.of(!0)];null==t.state.field(Kg,!1)&&i.push(gt.appendConfig.of([Kg,Jg])),t.dispatch({effects:i}),e=To(t,Hg)}return e&&e.dom.querySelector(\"input\").select(),!0}},{key:\"Mod-d\",run:({state:t,dispatch:e})=>{let{ranges:i}=t.selection;if(i.some(t=>t.from===t.to))return(({state:t,dispatch:e})=>{let{selection:i}=t,n=Y.create(i.ranges.map(e=>t.wordAt(e.head)||Y.cursor(e.head)),i.mainIndex);return!n.eq(i)&&(e(t.update({selection:n})),!0)})({state:t,dispatch:e});let n=t.sliceDoc(i[0].from,i[0].to);if(t.selection.ranges.some(e=>t.sliceDoc(e.from,e.to)!=n))return!1;let r=function(t,e){let{main:i,ranges:n}=t.selection,r=t.wordAt(i.head),s=r&&r.from==i.from&&r.to==i.to;for(let o=!1,a=new Wg(t.doc,e,n[n.length-1].to);;){if(a.next(),!a.done){if(o&&n.some(t=>t.from==a.value.from))continue;if(s){let e=t.wordAt(a.value.from);if(!e||e.from!=a.value.from||e.to!=a.value.to)continue}return a.value}if(o)return null;a=new Wg(t.doc,e,0,Math.max(0,n[n.length-1].from-1)),o=!0}}(t,n);return!!r&&(e(t.update({selection:t.selection.addRange(Y.range(r.from,r.to),!1),effects:fs.scrollIntoView(r.to)})),!0)},preventDefault:!0}];class _Q{constructor(t){this.view=t;let e=this.query=t.state.field(QQ).query.spec;function i(t,e,i){return le(\"button\",{class:\"cm-button\",name:t,onclick:e,type:\"button\"},i)}this.commit=this.commit.bind(this),this.searchField=le(\"input\",{value:e.search,placeholder:EQ(t,\"Find\"),\"aria-label\":EQ(t,\"Find\"),class:\"cm-textfield\",name:\"search\",form:\"\",\"main-field\":\"true\",onchange:this.commit,onkeyup:this.commit}),this.replaceField=le(\"input\",{value:e.replace,placeholder:EQ(t,\"Replace\"),\"aria-label\":EQ(t,\"Replace\"),class:\"cm-textfield\",name:\"replace\",form:\"\",onchange:this.commit,onkeyup:this.commit}),this.caseField=le(\"input\",{type:\"checkbox\",name:\"case\",form:\"\",checked:e.caseSensitive,onchange:this.commit}),this.reField=le(\"input\",{type:\"checkbox\",name:\"re\",form:\"\",checked:e.regexp,onchange:this.commit}),this.wordField=le(\"input\",{type:\"checkbox\",name:\"word\",form:\"\",checked:e.wholeWord,onchange:this.commit}),this.dom=le(\"div\",{onkeydown:t=>this.keydown(t),class:\"cm-search\"},[this.searchField,i(\"next\",()=>yQ(t),[EQ(t,\"next\")]),i(\"prev\",()=>kQ(t),[EQ(t,\"previous\")]),i(\"select\",()=>$Q(t),[EQ(t,\"all\")]),le(\"label\",null,[this.caseField,EQ(t,\"match case\")]),le(\"label\",null,[this.reField,EQ(t,\"regexp\")]),le(\"label\",null,[this.wordField,EQ(t,\"by word\")]),...t.state.readOnly?[]:[le(\"br\"),this.replaceField,i(\"replace\",()=>PQ(t),[EQ(t,\"replace\")]),i(\"replaceAll\",()=>TQ(t),[EQ(t,\"replace all\")])],le(\"button\",{name:\"close\",onclick:()=>RQ(t),\"aria-label\":EQ(t,\"close\"),type:\"button\"},[\"×\"])])}commit(){let t=new lQ({search:this.searchField.value,caseSensitive:this.caseField.checked,regexp:this.reField.checked,wholeWord:this.wordField.checked,replace:this.replaceField.value});t.eq(this.query)||(this.query=t,this.view.dispatch({effects:mQ.of(t)}))}keydown(t){var e,i,n;e=this.view,i=t,n=\"search-panel\",Ps(Ss(e.state),i,e,n)?t.preventDefault():13==t.keyCode&&t.target==this.searchField?(t.preventDefault(),(t.shiftKey?kQ:yQ)(this.view)):13==t.keyCode&&t.target==this.replaceField&&(t.preventDefault(),PQ(this.view))}update(t){for(let e of t.transactions)for(let t of e.effects)t.is(mQ)&&!t.value.eq(this.query)&&this.setQuery(t.value)}setQuery(t){this.query=t,this.searchField.value=t.search,this.replaceField.value=t.replace,this.caseField.checked=t.caseSensitive,this.reField.checked=t.regexp,this.wordField.checked=t.wholeWord}mount(){this.searchField.select()}get pos(){return 80}get top(){return this.view.state.facet(aQ).top}}function EQ(t,e){return t.state.phrase(e)}const YQ=30,LQ=/[\\s\\.,:;?!]/;function qQ(t,{from:e,to:i}){let n=t.state.doc.lineAt(e),r=t.state.doc.lineAt(i).to,s=Math.max(n.from,e-YQ),o=Math.min(r,i+YQ),a=t.state.sliceDoc(s,o);if(s!=n.from)for(let l=0;l<YQ;l++)if(!LQ.test(a[l+1])&&LQ.test(a[l])){a=a.slice(l);break}if(o!=r)for(let l=a.length-1;l>a.length-YQ;l--)if(!LQ.test(a[l-1])&&LQ.test(a[l])){a=a.slice(0,l);break}return fs.announce.of(`${t.state.phrase(\"current match\")}. ${a} ${t.state.phrase(\"on line\")} ${n.number}.`)}const VQ=fs.baseTheme({\".cm-panel.cm-search\":{padding:\"2px 6px 4px\",position:\"relative\",\"& [name=close]\":{position:\"absolute\",top:\"0\",right:\"4px\",backgroundColor:\"inherit\",border:\"none\",font:\"inherit\",padding:0,margin:0},\"& input, & button, & label\":{margin:\".2em .6em .2em 0\"},\"& input[type=checkbox]\":{marginRight:\".2em\"},\"& label\":{fontSize:\"80%\",whiteSpace:\"pre\"}},\"&light .cm-searchMatch\":{backgroundColor:\"#ffff0054\"},\"&dark .cm-searchMatch\":{backgroundColor:\"#00ffff8a\"},\"&light .cm-searchMatch-selected\":{backgroundColor:\"#ff6a0054\"},\"&dark .cm-searchMatch-selected\":{backgroundColor:\"#ff00ff8a\"}}),WQ=[QQ,tt.low(xQ),VQ];class DQ{constructor(t,e,i){this.from=t,this.to=e,this.diagnostic=i}}class BQ{constructor(t,e,i){this.diagnostics=t,this.panel=e,this.selected=i}static init(t,e,i){let n=i.facet(eb).markerFilter;n&&(t=n(t,i));let r=t.slice().sort((t,e)=>t.from-e.from||t.to-e.to),s=new Et,o=[],a=0,l=i.doc.iter(),h=0,c=i.doc.length;for(let d=0;;){let t,e,i=d==r.length?null:r[d];if(!i&&!o.length)break;if(o.length)t=a,e=o.reduce((t,e)=>Math.min(t,e.to),i&&i.from>t?i.from:1e8);else{if(t=i.from,t>c)break;e=i.to,o.push(i),d++}for(;d<r.length;){let i=r[d];if(i.from!=t||!(i.to>i.from||i.to==t)){e=Math.min(i.from,e);break}o.push(i),d++,e=Math.min(i.to,e)}e=Math.min(e,c);let n=!1;if(o.some(i=>i.from==t&&(i.to==e||e==c))&&(n=t==e,!n&&e-t<10)){let i=t-(h+l.value.length);i>0&&(l.next(i),h=t);for(let r=t;;){if(r>=e){n=!0;break}if(!l.lineBreak&&h+l.value.length>r)break;r=h+l.value.length,h+=l.value.length,l.next()}}let u=ub(o);if(n)s.add(t,t,Ce.widget({widget:new sb(u),diagnostics:o.slice()}));else{let i=o.reduce((t,e)=>e.markClass?t+\" \"+e.markClass:t,\"\");s.add(t,e,Ce.mark({class:\"cm-lintRange cm-lintRange-\"+u+i,diagnostics:o.slice(),inclusiveEnd:o.some(t=>t.to>e)}))}if(a=e,a==c)break;for(let r=0;r<o.length;r++)o[r].to<=a&&o.splice(r--,1)}let u=s.finish();return new BQ(u,e,jQ(u))}}function jQ(t,e=null,i=0){let n=null;return t.between(i,1e9,(t,i,{spec:r})=>{if(!(e&&r.diagnostics.indexOf(e)<0))if(n){if(r.diagnostics.indexOf(n.diagnostic)<0)return!1;n=new DQ(n.from,i,n.diagnostic)}else n=new DQ(t,i,e||r.diagnostics[0])}),n}const IQ=gt.define(),GQ=gt.define(),NQ=gt.define(),UQ=N.define({create:()=>new BQ(Ce.none,null,null),update(t,e){if(e.docChanged&&t.diagnostics.size){let i=t.diagnostics.map(e.changes),n=null,r=t.panel;if(t.selected){let r=e.changes.mapPos(t.selected.from,1);n=jQ(i,t.selected.diagnostic,r)||jQ(i,null,r)}!i.size&&r&&e.state.facet(eb).autoPanel&&(r=null),t=new BQ(i,r,n)}for(let i of e.effects)if(i.is(IQ)){let n=e.state.facet(eb).autoPanel?i.value.length?ab.open:null:t.panel;t=BQ.init(i.value,n,e.state)}else i.is(GQ)?t=new BQ(t.diagnostics,i.value?ab.open:null,t.selected):i.is(NQ)&&(t=new BQ(t.diagnostics,t.panel,i.value));return t},provide:t=>[Ao.from(t,t=>t.panel),fs.decorations.from(t,t=>t.diagnostics)]}),HQ=Ce.mark({class:\"cm-lintRange cm-lintRange-active\"});function FQ(t,e,i){let n,{diagnostics:r}=t.state.field(UQ),s=-1,o=-1;r.between(e-(i<0?1:0),e+(i>0?1:0),(t,r,{spec:a})=>{if(e>=t&&e<=r&&(t==r||(e>t||i>0)&&(e<r||i<0)))return n=a.diagnostics,s=t,o=r,!1});let a=t.state.facet(eb).tooltipFilter;return n&&a&&(n=a(n,t.state)),n?{pos:s,end:o,above:t.state.doc.lineAt(s).to<o,create:()=>({dom:KQ(t,n)})}:null}function KQ(t,e){return le(\"ul\",{class:\"cm-tooltip-lint\"},e.map(e=>rb(t,e,!1)))}const JQ=t=>{let e=t.state.field(UQ,!1);return!(!e||!e.panel)&&(t.dispatch({effects:GQ.of(!1)}),!0)},tb=[{key:\"Mod-Shift-m\",run:t=>{let e=t.state.field(UQ,!1);var i,n;e&&e.panel||t.dispatch({effects:(i=t.state,n=[GQ.of(!0)],i.field(UQ,!1)?n:n.concat(gt.appendConfig.of(db)))});let r=To(t,ab.open);return r&&r.dom.querySelector(\".cm-panel-lint ul\").focus(),!0},preventDefault:!0},{key:\"F8\",run:t=>{let e=t.state.field(UQ,!1);if(!e)return!1;let i=t.state.selection.main,n=e.diagnostics.iter(i.to+1);return!(!n.value&&(n=e.diagnostics.iter(0),!n.value||n.from==i.from&&n.to==i.to))&&(t.dispatch({selection:{anchor:n.from,head:n.to},scrollIntoView:!0}),!0)}}],eb=V.define({combine:t=>({sources:t.map(t=>t.source).filter(t=>null!=t),...Zt(t.map(t=>t.config),{delay:750,markerFilter:null,tooltipFilter:null,needsRefresh:null,hideOn:()=>null},{delay:Math.max,markerFilter:ib,tooltipFilter:ib,needsRefresh:(t,e)=>t?e?i=>t(i)||e(i):t:e,hideOn:(t,e)=>t?e?(i,n,r)=>t(i,n,r)||e(i,n,r):t:e,autoPanel:(t,e)=>t||e})})});function ib(t,e){return t?e?(i,n)=>e(t(i,n),n):t:e}function nb(t){let e=[];if(t)t:for(let{name:i}of t){for(let t=0;t<i.length;t++){let n=i[t];if(/[a-zA-Z]/.test(n)&&!e.some(t=>t.toLowerCase()==n.toLowerCase())){e.push(n);continue t}}e.push(\"\")}return e}function rb(t,e,i){var n;let r=i?nb(e.actions):[];return le(\"li\",{class:\"cm-diagnostic cm-diagnostic-\"+e.severity},le(\"span\",{class:\"cm-diagnosticText\"},e.renderMessage?e.renderMessage(t):e.message),null===(n=e.actions)||void 0===n?void 0:n.map((i,n)=>{let s=!1,o=n=>{if(n.preventDefault(),s)return;s=!0;let r=jQ(t.state.field(UQ).diagnostics,e);r&&i.apply(t,r.from,r.to)},{name:a}=i,l=r[n]?a.indexOf(r[n]):-1,h=l<0?a:[a.slice(0,l),le(\"u\",a.slice(l,l+1)),a.slice(l+1)];return le(\"button\",{type:\"button\",class:\"cm-diagnosticAction\"+(i.markClass?\" \"+i.markClass:\"\"),onclick:o,onmousedown:o,\"aria-label\":` Action: ${a}${l<0?\"\":` (access key \"${r[n]})\"`}.`},h)}),e.source&&le(\"div\",{class:\"cm-diagnosticSource\"},e.source))}class sb extends Pe{constructor(t){super(),this.sev=t}eq(t){return t.sev==this.sev}toDOM(){return le(\"span\",{class:\"cm-lintPoint cm-lintPoint-\"+this.sev})}}class ob{constructor(t,e){this.diagnostic=e,this.id=\"item_\"+Math.floor(4294967295*Math.random()).toString(16),this.dom=rb(t,e,!0),this.dom.id=this.id,this.dom.setAttribute(\"role\",\"option\")}}class ab{constructor(t){this.view=t,this.items=[];this.list=le(\"ul\",{tabIndex:0,role:\"listbox\",\"aria-label\":this.view.state.phrase(\"Diagnostics\"),onkeydown:e=>{if(27==e.keyCode)JQ(this.view),this.view.focus();else if(38==e.keyCode||33==e.keyCode)this.moveSelection((this.selectedIndex-1+this.items.length)%this.items.length);else if(40==e.keyCode||34==e.keyCode)this.moveSelection((this.selectedIndex+1)%this.items.length);else if(36==e.keyCode)this.moveSelection(0);else if(35==e.keyCode)this.moveSelection(this.items.length-1);else if(13==e.keyCode)this.view.focus();else{if(!(e.keyCode>=65&&e.keyCode<=90&&this.selectedIndex>=0))return;{let{diagnostic:i}=this.items[this.selectedIndex],n=nb(i.actions);for(let r=0;r<n.length;r++)if(n[r].toUpperCase().charCodeAt(0)==e.keyCode){let e=jQ(this.view.state.field(UQ).diagnostics,i);e&&i.actions[r].apply(t,e.from,e.to)}}}e.preventDefault()},onclick:t=>{for(let e=0;e<this.items.length;e++)this.items[e].dom.contains(t.target)&&this.moveSelection(e)}}),this.dom=le(\"div\",{class:\"cm-panel-lint\"},this.list,le(\"button\",{type:\"button\",name:\"close\",\"aria-label\":this.view.state.phrase(\"close\"),onclick:()=>JQ(this.view)},\"×\")),this.update()}get selectedIndex(){let t=this.view.state.field(UQ).selected;if(!t)return-1;for(let e=0;e<this.items.length;e++)if(this.items[e].diagnostic==t.diagnostic)return e;return-1}update(){let{diagnostics:t,selected:e}=this.view.state.field(UQ),i=0,n=!1,r=null,s=new Set;for(t.between(0,this.view.state.doc.length,(t,o,{spec:a})=>{for(let l of a.diagnostics){if(s.has(l))continue;s.add(l);let t,o=-1;for(let e=i;e<this.items.length;e++)if(this.items[e].diagnostic==l){o=e;break}o<0?(t=new ob(this.view,l),this.items.splice(i,0,t),n=!0):(t=this.items[o],o>i&&(this.items.splice(i,o-i),n=!0)),e&&t.diagnostic==e.diagnostic?t.dom.hasAttribute(\"aria-selected\")||(t.dom.setAttribute(\"aria-selected\",\"true\"),r=t):t.dom.hasAttribute(\"aria-selected\")&&t.dom.removeAttribute(\"aria-selected\"),i++}});i<this.items.length&&!(1==this.items.length&&this.items[0].diagnostic.from<0);)n=!0,this.items.pop();0==this.items.length&&(this.items.push(new ob(this.view,{from:-1,to:-1,severity:\"info\",message:this.view.state.phrase(\"No diagnostics\")})),n=!0),r?(this.list.setAttribute(\"aria-activedescendant\",r.id),this.view.requestMeasure({key:this,read:()=>({sel:r.dom.getBoundingClientRect(),panel:this.list.getBoundingClientRect()}),write:({sel:t,panel:e})=>{let i=e.height/this.list.offsetHeight;t.top<e.top?this.list.scrollTop-=(e.top-t.top)/i:t.bottom>e.bottom&&(this.list.scrollTop+=(t.bottom-e.bottom)/i)}})):this.selectedIndex<0&&this.list.removeAttribute(\"aria-activedescendant\"),n&&this.sync()}sync(){let t=this.list.firstChild;function e(){let e=t;t=e.nextSibling,e.remove()}for(let i of this.items)if(i.dom.parentNode==this.list){for(;t!=i.dom;)e();t=i.dom.nextSibling}else this.list.insertBefore(i.dom,t);for(;t;)e()}moveSelection(t){if(this.selectedIndex<0)return;let e=jQ(this.view.state.field(UQ).diagnostics,this.items[t].diagnostic);e&&this.view.dispatch({selection:{anchor:e.from,head:e.to},scrollIntoView:!0,effects:NQ.of(e)})}static open(t){return new ab(t)}}function lb(t){return function(t,e='viewBox=\"0 0 40 40\"'){return`url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" ${e}>${encodeURIComponent(t)}</svg>')`}(`<path d=\"m0 2.5 l2 -1.5 l1 0 l2 1.5 l1 0\" stroke=\"${t}\" fill=\"none\" stroke-width=\".7\"/>`,'width=\"6\" height=\"3\"')}const hb=fs.baseTheme({\".cm-diagnostic\":{padding:\"3px 6px 3px 8px\",marginLeft:\"-1px\",display:\"block\",whiteSpace:\"pre-wrap\"},\".cm-diagnostic-error\":{borderLeft:\"5px solid #d11\"},\".cm-diagnostic-warning\":{borderLeft:\"5px solid orange\"},\".cm-diagnostic-info\":{borderLeft:\"5px solid #999\"},\".cm-diagnostic-hint\":{borderLeft:\"5px solid #66d\"},\".cm-diagnosticAction\":{font:\"inherit\",border:\"none\",padding:\"2px 4px\",backgroundColor:\"#444\",color:\"white\",borderRadius:\"3px\",marginLeft:\"8px\",cursor:\"pointer\"},\".cm-diagnosticSource\":{fontSize:\"70%\",opacity:.7},\".cm-lintRange\":{backgroundPosition:\"left bottom\",backgroundRepeat:\"repeat-x\",paddingBottom:\"0.7px\"},\".cm-lintRange-error\":{backgroundImage:lb(\"#d11\")},\".cm-lintRange-warning\":{backgroundImage:lb(\"orange\")},\".cm-lintRange-info\":{backgroundImage:lb(\"#999\")},\".cm-lintRange-hint\":{backgroundImage:lb(\"#66d\")},\".cm-lintRange-active\":{backgroundColor:\"#ffdd9980\"},\".cm-tooltip-lint\":{padding:0,margin:0},\".cm-lintPoint\":{position:\"relative\",\"&:after\":{content:'\"\"',position:\"absolute\",bottom:0,left:\"-2px\",borderLeft:\"3px solid transparent\",borderRight:\"3px solid transparent\",borderBottom:\"4px solid #d11\"}},\".cm-lintPoint-warning\":{\"&:after\":{borderBottomColor:\"orange\"}},\".cm-lintPoint-info\":{\"&:after\":{borderBottomColor:\"#999\"}},\".cm-lintPoint-hint\":{\"&:after\":{borderBottomColor:\"#66d\"}},\".cm-panel.cm-panel-lint\":{position:\"relative\",\"& ul\":{maxHeight:\"100px\",overflowY:\"auto\",\"& [aria-selected]\":{backgroundColor:\"#ddd\",\"& u\":{textDecoration:\"underline\"}},\"&:focus [aria-selected]\":{background_fallback:\"#bdf\",backgroundColor:\"Highlight\",color_fallback:\"white\",color:\"HighlightText\"},\"& u\":{textDecoration:\"none\"},padding:0,margin:0},\"& [name=close]\":{position:\"absolute\",top:\"0\",right:\"2px\",background:\"inherit\",border:\"none\",font:\"inherit\",padding:0,margin:0}}});function cb(t){return\"error\"==t?4:\"warning\"==t?3:\"info\"==t?2:1}function ub(t){let e=\"hint\",i=1;for(let n of t){let t=cb(n.severity);t>i&&(i=t,e=n.severity)}return e}const db=[UQ,fs.decorations.compute([UQ],t=>{let{selected:e,panel:i}=t.field(UQ);return e&&i&&e.from!=e.to?Ce.set([HQ.range(e.from,e.to)]):Ce.none}),yo(FQ,{hideOn:function(t,e){let i=e.pos,n=e.end||i,r=t.state.facet(eb).hideOn(t,i,n);if(null!=r)return r;let s=t.startState.doc.lineAt(e.pos);return!(!t.effects.some(t=>t.is(IQ))&&!t.changes.touchesRange(s.from,Math.max(s.to,n)))}}),hb];var fb=function(t){void 0===t&&(t={});var{crosshairCursor:e=!1}=t,i=[];!1!==t.closeBracketsKeymap&&(i=i.concat(Ou)),!1!==t.defaultKeymap&&(i=i.concat(Lg)),!1!==t.searchKeymap&&(i=i.concat(zQ)),!1!==t.historyKeymap&&(i=i.concat(Wm)),!1!==t.foldKeymap&&(i=i.concat(gh)),!1!==t.completionKeymap&&(i=i.concat(xu)),!1!==t.lintKeymap&&(i=i.concat(tb));var n=[];return!1!==t.lineNumbers&&n.push(function(t={}){return[Uo.of(t),qo(),Ko]}()),!1!==t.highlightActiveLineGutter&&n.push(ea),!1!==t.highlightSpecialChars&&n.push(function(t={}){return[Us.of(t),Hs||(Hs=Wi.fromClass(class{constructor(t){this.view=t,this.decorations=Ce.none,this.decorationCache=Object.create(null),this.decorator=this.makeDecorator(t.state.facet(Us)),this.decorations=this.decorator.createDeco(t)}makeDecorator(t){return new Bs({regexp:t.specialChars,decoration:(e,i,n)=>{let{doc:r}=i.state,s=y(e[0],0);if(9==s){let t=r.lineAt(n),e=i.state.tabSize,s=Nt(t.text,e,n-t.from);return Ce.replace({widget:new Ks((e-s%e)*this.view.defaultCharacterWidth/this.view.scaleX)})}return this.decorationCache[s]||(this.decorationCache[s]=Ce.replace({widget:new Fs(t,s)}))},boundary:t.replaceTabs?void 0:/[^]/})}update(t){let e=t.state.facet(Us);t.startState.facet(Us)!=e?(this.decorator=this.makeDecorator(e),this.decorations=this.decorator.createDeco(t.view)):this.decorations=this.decorator.updateDeco(t,this.decorations)}},{decorations:t=>t.decorations}))]}()),!1!==t.history&&n.push(function(t={}){return[km,ym.of(t),fs.domEventHandlers({beforeinput(t,e){let i=\"historyUndo\"==t.inputType?Pm:\"historyRedo\"==t.inputType?Tm:null;return!!i&&(t.preventDefault(),i(e))}})]}()),!1!==t.foldGutter&&n.push(function(t={}){let e={...yh,...t},i=new kh(e,!0),n=new kh(e,!1),r=Wi.fromClass(class{constructor(t){this.from=t.viewport.from,this.markers=this.buildMarkers(t)}update(t){(t.docChanged||t.viewportChanged||t.startState.facet(Yl)!=t.state.facet(Yl)||t.startState.field(uh,!1)!=t.state.field(uh,!1)||Cl(t.startState)!=Cl(t.state)||e.foldingChanged(t))&&(this.markers=this.buildMarkers(t.view))}buildMarkers(t){let e=new Et;for(let r of t.viewportLineBlocks){let s=fh(t.state,r.from,r.to)?n:oh(t.state,r.from,r.to)?i:null;s&&e.add(r.from,r.from,s)}return e.finish()}}),{domEventHandlers:s}=e;return[r,Yo({class:\"cm-foldGutter\",markers(t){var e;return(null===(e=t.plugin(r))||void 0===e?void 0:e.markers)||_t.empty},initialSpacer:()=>new kh(e,!1),domEventHandlers:{...s,click:(t,e,i)=>{if(s.click&&s.click(t,e,i))return!0;let n=fh(t.state,e.from,e.to);if(n)return t.dispatch({effects:hh.of(n)}),!0;let r=oh(t.state,e.from,e.to);return!!r&&(t.dispatch({effects:lh.of(r)}),!0)}}}),vh()]}()),!1!==t.drawSelection&&n.push(function(t={}){return[Rs.of(t),_s,Ys,Ls,Mi.of(!0)]}()),!1!==t.dropCursor&&n.push([Vs,Ws]),!1!==t.allowMultipleSelections&&n.push(Ct.allowMultipleSelections.of(!0)),!1!==t.indentOnInput&&n.push(Ct.transactionFilter.of(t=>{if(!t.docChanged||!t.isUserEvent(\"input.type\")&&!t.isUserEvent(\"input.complete\"))return t;let e=t.startState.languageDataAt(\"indentOnInput\",t.startState.selection.main.head);if(!e.length)return t;let i=t.newDoc,{head:n}=t.newSelection.main,r=i.lineAt(n);if(n>r.from+200)return t;let s=i.sliceString(r.from,n);if(!e.some(t=>t.test(s)))return t;let{state:o}=t,a=-1,l=[];for(let{head:h}of o.selection.ranges){let t=o.doc.lineAt(h);if(t.from==a)continue;a=t.from;let e=jl(o,t.from);if(null==e)continue;let i=/^\\s*/.exec(t.text)[0],n=Bl(o,e);i!=n&&l.push({from:t.from,to:t.from+i.length,insert:n})}return l.length?[t,{changes:l,sequential:!0}]:t})),!1!==t.syntaxHighlighting&&n.push(Xh(Rh,{fallback:!0})),!1!==t.bracketMatching&&n.push(function(t={}){return[Eh.of(t),Vh]}()),!1!==t.closeBrackets&&n.push([fu,lu]),!1!==t.autocompletion&&n.push(function(t={}){return[qc,Ac,Oc.of(t),Yc,Su,Vc]}()),!1!==t.rectangularSelection&&n.push(fs.mouseSelectionStyle.of((t,e)=>{return(i=e).altKey&&0==i.button?so(t,e):null;var i})),!1!==e&&n.push(function(t={}){let[e,i]=oo[t.key||\"Alt\"],n=Wi.fromClass(class{constructor(t){this.view=t,this.isDown=!1}set(t){this.isDown!=t&&(this.isDown=t,this.view.update([]))}},{eventObservers:{keydown(t){this.set(t.keyCode==e||i(t))},keyup(t){t.keyCode!=e&&i(t)||this.set(!1)},mousemove(t){this.set(i(t))}}});return[n,fs.contentAttributes.of(t=>{var e;return(null===(e=t.plugin(n))||void 0===e?void 0:e.isDown)?ao:null})]}()),!1!==t.highlightActiveLine&&n.push(to),!1!==t.highlightSelectionMatches&&n.push([oQ,sQ]),t.tabSize&&\"number\"==typeof t.tabSize&&n.push(Wl.of(\" \".repeat(t.tabSize))),n.concat([ws.of(i.flat())]).filter(Boolean)};const Ob=\"#e06c75\",pb=\"#abb2bf\",mb=\"#7d8799\",gb=\"#d19a66\",Qb=\"#2c313a\",bb=\"#282c34\",vb=\"#353a42\",wb=\"#528bff\",xb=[fs.theme({\"&\":{color:pb,backgroundColor:bb},\".cm-content\":{caretColor:wb},\".cm-cursor, .cm-dropCursor\":{borderLeftColor:wb},\"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection\":{backgroundColor:\"#3E4451\"},\".cm-panels\":{backgroundColor:\"#21252b\",color:pb},\".cm-panels.cm-panels-top\":{borderBottom:\"2px solid black\"},\".cm-panels.cm-panels-bottom\":{borderTop:\"2px solid black\"},\".cm-searchMatch\":{backgroundColor:\"#72a1ff59\",outline:\"1px solid #457dff\"},\".cm-searchMatch.cm-searchMatch-selected\":{backgroundColor:\"#6199ff2f\"},\".cm-activeLine\":{backgroundColor:\"#6699ff0b\"},\".cm-selectionMatch\":{backgroundColor:\"#aafe661a\"},\"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket\":{backgroundColor:\"#bad0f847\"},\".cm-gutters\":{backgroundColor:bb,color:mb,border:\"none\"},\".cm-activeLineGutter\":{backgroundColor:Qb},\".cm-foldPlaceholder\":{backgroundColor:\"transparent\",border:\"none\",color:\"#ddd\"},\".cm-tooltip\":{border:\"none\",backgroundColor:vb},\".cm-tooltip .cm-tooltip-arrow:before\":{borderTopColor:\"transparent\",borderBottomColor:\"transparent\"},\".cm-tooltip .cm-tooltip-arrow:after\":{borderTopColor:vb,borderBottomColor:vb},\".cm-tooltip-autocomplete\":{\"& > ul > li[aria-selected]\":{backgroundColor:Qb,color:pb}}},{dark:!0}),Xh(Ph.define([{tag:wl.keyword,color:\"#c678dd\"},{tag:[wl.name,wl.deleted,wl.character,wl.propertyName,wl.macroName],color:Ob},{tag:[wl.function(wl.variableName),wl.labelName],color:\"#61afef\"},{tag:[wl.color,wl.constant(wl.name),wl.standard(wl.name)],color:gb},{tag:[wl.definition(wl.name),wl.separator],color:pb},{tag:[wl.typeName,wl.className,wl.number,wl.changed,wl.annotation,wl.modifier,wl.self,wl.namespace],color:\"#e5c07b\"},{tag:[wl.operator,wl.operatorKeyword,wl.url,wl.escape,wl.regexp,wl.link,wl.special(wl.string)],color:\"#56b6c2\"},{tag:[wl.meta,wl.comment],color:mb},{tag:wl.strong,fontWeight:\"bold\"},{tag:wl.emphasis,fontStyle:\"italic\"},{tag:wl.strikethrough,textDecoration:\"line-through\"},{tag:wl.link,color:mb,textDecoration:\"underline\"},{tag:wl.heading,fontWeight:\"bold\",color:Ob},{tag:[wl.atom,wl.bool,wl.special(wl.variableName)],color:gb},{tag:[wl.processingInstruction,wl.string,wl.inserted],color:\"#98c379\"},{tag:wl.invalid,color:\"#ffffff\"}]))];var Sb=fs.theme({\"&\":{backgroundColor:\"#fff\"}},{dark:!1});class yb{constructor(t,e){this.timeLeftMS=void 0,this.timeoutMS=void 0,this.isCancelled=!1,this.isTimeExhausted=!1,this.callbacks=[],this.timeLeftMS=e,this.timeoutMS=e,this.callbacks.push(t)}tick(){if(!this.isCancelled&&!this.isTimeExhausted&&(this.timeLeftMS--,this.timeLeftMS<=0)){this.isTimeExhausted=!0;var t=this.callbacks.slice();this.callbacks.length=0,t.forEach(t=>{try{t()}catch(e){}})}}cancel(){this.isCancelled=!0,this.callbacks.length=0}reset(){this.timeLeftMS=this.timeoutMS,this.isCancelled=!1,this.isTimeExhausted=!1}get isDone(){return this.isCancelled||this.isTimeExhausted}}class kb{constructor(){this.interval=null,this.latches=new Set}add(t){this.latches.add(t),this.start()}remove(t){this.latches.delete(t),0===this.latches.size&&this.stop()}start(){null===this.interval&&(this.interval=setInterval(()=>{this.latches.forEach(t=>{t.tick(),t.isDone&&this.remove(t)})},1))}stop(){null!==this.interval&&(clearInterval(this.interval),this.interval=null)}}var $b=null,Pb=Ot.define(),Tb=[];function Cb(e){var{value:i,selection:n,onChange:r,onStatistics:s,onCreateEditor:o,onUpdate:a,extensions:l=Tb,autoFocus:h,theme:c=\"light\",height:u=null,minHeight:d=null,maxHeight:f=null,width:O=null,minWidth:p=null,maxWidth:m=null,placeholder:g=\"\",editable:Q=!0,readOnly:b=!1,indentWithTab:v=!0,basicSetup:w=!0,root:x,initialState:S}=e,[y,k]=t.useState(),[$,P]=t.useState(),[T,C]=t.useState(),Z=t.useState(()=>({current:null}))[0],X=t.useState(()=>({current:null}))[0],A=fs.theme({\"&\":{height:u,minHeight:d,maxHeight:f,width:O,minWidth:p,maxWidth:m},\"& .cm-scroller\":{height:\"100% !important\"}}),M=fs.updateListener.of(t=>{if(t.docChanged&&\"function\"==typeof r&&!t.transactions.some(t=>t.annotation(Pb))){Z.current?Z.current.reset():(Z.current=new yb(()=>{if(X.current){var t=X.current;X.current=null,t()}Z.current=null},200),(\"undefined\"==typeof window?new kb:($b||($b=new kb),$b)).add(Z.current));var e=t.state.doc.toString();r(e,t)}s&&s((t=>({line:t.state.doc.lineAt(t.state.selection.main.from),lineCount:t.state.doc.lines,lineBreak:t.state.lineBreak,length:t.state.doc.length,readOnly:t.state.readOnly,tabSize:t.state.tabSize,selection:t.state.selection,selectionAsSingle:t.state.selection.asSingle().main,ranges:t.state.selection.ranges,selectionCode:t.state.sliceDoc(t.state.selection.main.from,t.state.selection.main.to),selections:t.state.selection.ranges.map(e=>t.state.sliceDoc(e.from,e.to)),selectedText:t.state.selection.ranges.some(t=>!t.empty)}))(t))}),R=function(t){void 0===t&&(t={});var{indentWithTab:e=!0,editable:i=!0,readOnly:n=!1,theme:r=\"light\",placeholder:s=\"\",basicSetup:o=!0}=t,a=[];switch(e&&a.unshift(ws.of([qg])),o&&(\"boolean\"==typeof o?a.unshift(fb()):a.unshift(fb(o))),s&&a.unshift(io(s)),r){case\"light\":a.push(Sb);break;case\"dark\":a.push(xb);break;case\"none\":break;default:a.push(r)}return!1===i&&a.push(fs.editable.of(!1)),n&&a.push(Ct.readOnly.of(!0)),[...a]}({theme:c,editable:Q,readOnly:b,placeholder:g,indentWithTab:v,basicSetup:w}),z=[M,A,...R];return a&&\"function\"==typeof a&&z.push(fs.updateListener.of(a)),z=z.concat(l),t.useLayoutEffect(()=>{if(y&&!T){var t={doc:i,selection:n,extensions:z},e=S?Ct.fromJSON(S.json,t,S.fields):Ct.create(t);if(C(e),!$){var r=new fs({state:e,parent:y,root:x});P(r),o&&o(r,e)}}return()=>{$&&(C(void 0),P(void 0))}},[y,T]),t.useEffect(()=>{e.container&&k(e.container)},[e.container]),t.useEffect(()=>()=>{$&&($.destroy(),P(void 0)),Z.current&&(Z.current.cancel(),Z.current=null)},[$]),t.useEffect(()=>{h&&$&&$.focus()},[h,$]),t.useEffect(()=>{$&&$.dispatch({effects:gt.reconfigure.of(z)})},[c,l,u,d,f,O,p,m,g,Q,b,v,w,r,a]),t.useEffect(()=>{if(void 0!==i){var t=$?$.state.doc.toString():\"\";if($&&i!==t){var e=()=>{$&&i!==$.state.doc.toString()&&$.dispatch({changes:{from:0,to:$.state.doc.toString().length,insert:i||\"\"},annotations:[Pb.of(!0)]})};Z.current&&!Z.current.isDone?X.current=e:e()}}},[i,$]),{state:T,setState:C,view:$,setView:P,container:y,setContainer:k}}var Zb=[\"className\",\"value\",\"selection\",\"extensions\",\"onChange\",\"onStatistics\",\"onCreateEditor\",\"onUpdate\",\"autoFocus\",\"theme\",\"height\",\"minHeight\",\"maxHeight\",\"width\",\"minWidth\",\"maxWidth\",\"basicSetup\",\"placeholder\",\"indentWithTab\",\"editable\",\"readOnly\",\"root\",\"initialState\"],Xb=t.forwardRef((i,n)=>{var{className:r,value:s=\"\",selection:o,extensions:a=[],onChange:l,onStatistics:h,onCreateEditor:c,onUpdate:u,autoFocus:d,theme:f=\"light\",height:O,minHeight:p,maxHeight:m,width:g,minWidth:Q,maxWidth:b,basicSetup:v,placeholder:w,indentWithTab:x,editable:S,readOnly:y,root:k,initialState:$}=i,P=function(t,e){if(null==t)return{};var i={};for(var n in t)if({}.hasOwnProperty.call(t,n)){if(-1!==e.indexOf(n))continue;i[n]=t[n]}return i}(i,Zb),T=t.useRef(null),{state:C,view:Z,container:X,setContainer:A}=Cb({root:k,value:s,autoFocus:d,theme:f,height:O,minHeight:p,maxHeight:m,width:g,minWidth:Q,maxWidth:b,basicSetup:v,placeholder:w,indentWithTab:x,editable:S,readOnly:y,selection:o,onChange:l,onStatistics:h,onCreateEditor:c,onUpdate:u,extensions:a,initialState:$});t.useImperativeHandle(n,()=>({editor:T.current,state:C,view:Z}),[T,X,C,Z]);var M=t.useCallback(t=>{T.current=t,A(t)},[A]);if(\"string\"!=typeof s)throw new Error(\"value must be typeof string but got \"+typeof s);var R=\"string\"==typeof f?\"cm-theme-\"+f:\"cm-theme\";return e.jsx(\"div\",dm({ref:M,className:R+(r?\" \"+r:\"\")},P))});Xb.displayName=\"CodeMirror\";export{fs as E,Xb as R,om as m,io as p};\n"
  },
  {
    "path": "frontend/assets/ja-Q5acyAjl.js",
    "content": "const e={\"ui.common.title\":\"Fast Note Sync\",\"ui.common.subtitle\":\"高性能、低遅延のノート同期、管理、REST サービス\",\"ui.common.footerTitle\":\"Golang + Websocket + Sqlite + React で構築\\n<a href='https://github.com/haierkeys/obsidian-fast-note-sync' target='_blank'>Obsidian Fast Note Sync Plugin</a> と併せてご使用ください\",\"ui.common.loading\":\"読み込み中...\",\"ui.common.downloading\":\"ダウンロード中...\",\"ui.common.actions\":\"操作\",\"ui.common.save\":\"保存\",\"ui.common.add\":\"新規追加\",\"ui.common.edit\":\"編集\",\"ui.common.view\":\"表示\",\"ui.common.viewDetail\":\"詳細を表示\",\"ui.common.delete\":\"削除\",\"ui.common.restore\":\"復元\",\"ui.common.permanentDelete\":\"永久削除\",\"ui.common.clear\":\"クリア\",\"ui.common.batchPermanentDelete\":\"一括永久削除\",\"ui.common.batchPermanentDeleteConfirm\":\"選択した {{count}} 項目を永久削除してもよろしいですか？この操作は元に戻せません！\",\"ui.common.search\":\"検索\",\"ui.common.refresh\":\"更新\",\"ui.common.retry\":\"再試行\",\"ui.common.refreshSuccess\":\"更新に成功しました\",\"ui.common.close\":\"閉じる\",\"ui.common.cancel\":\"キャンセル\",\"ui.common.confirm\":\"確定\",\"ui.common.success\":\"成功\",\"ui.common.error\":\"エラー\",\"ui.common.warning\":\"警告\",\"ui.common.info\":\"情報\",\"ui.common.total\":\"合計\",\"ui.common.isEnabled\":\"有効\",\"ui.common.isDisabled\":\"無効\",\"ui.common.switchLanguage\":\"言語切り替え\",\"ui.common.comingSoon\":\"近日公開...\",\"ui.common.comingSoonDescription\":\"この機能は開発中です。近日公開をお待ちください...\",\"ui.common.helpAndSupport\":\"ヘルプとサポート\",\"ui.common.githubIssue\":\"フィードバック\",\"ui.common.githubIssueDesc\":\"バグや機能要望を報告\",\"ui.common.telegramGroup\":\"コミュニティ交流\",\"ui.common.telegramGroupDesc\":\"Telegram コミュニティでの議論とサポート\",\"ui.common.planned\":\"開発予定\",\"ui.common.unknown\":\"不明\",\"ui.common.copied\":\"コピーしました\",\"ui.common.restoring\":\"復元中...\",\"ui.common.rename\":\"名前変更\",\"ui.common.toggleTheme\":\"テーマ切り替え\",\"ui.common.previous\":\"前ページ\",\"ui.common.next\":\"次ページ\",\"ui.common.page\":\"第\",\"ui.common.of\":\"全\",\"ui.common.to\":\"から\",\"ui.common.perPage\":\"1 ページあたりの件数\",\"ui.common.copy\":\"コピー\",\"ui.common.createdAt\":\"作成日時\",\"ui.common.updatedAt\":\"更新日時\",\"ui.common.noSearchResults\":\"一致する結果が見つかりませんでした\",\"ui.common.selectAll\":\"すべて選択\",\"ui.common.items\":\"件\",\"ui.common.count\":\"個\",\"ui.common.saveSuccess\":\"保存に成功しました\",\"ui.common.selectVault\":\"ノートライブラリを選択\",\"ui.common.sourceCode\":\"ソースコード\",\"ui.common.wideMode\":\"ワイドモード\",\"ui.common.narrowMode\":\"標準幅\",\"ui.common.fold\":\"折りたたむ\",\"ui.common.noChange\":\"内容に変更はありません\",\"ui.common.na\":\"N/A\",\"ui.common.name\":\"名前\",\"ui.auth.login\":\"ログイン\",\"ui.auth.logout\":\"ログアウト\",\"ui.auth.register\":\"登録\",\"ui.auth.registerButton\":\"登録\",\"ui.auth.registerClosed\":\"ユーザー登録は終了しています\",\"ui.auth.credentials\":\"ユーザー名（またはメールアドレス）\",\"ui.auth.username\":\"ユーザー名\",\"ui.auth.password\":\"パスワード\",\"ui.auth.remember\":\"ログイン状態を保持\",\"ui.auth.confirmPassword\":\"パスワードの確認\",\"ui.auth.email\":\"メールアドレス\",\"ui.auth.credentialsPlaceholder\":\"ユーザー名（またはメールアドレス）を入力してください\",\"ui.auth.usernamePlaceholder\":\"ユーザー名を入力してください\",\"ui.auth.passwordPlaceholder\":\"パスワードを入力してください\",\"ui.auth.emailPlaceholder\":\"メールアドレスを入力してください\",\"ui.auth.confirmPasswordPlaceholder\":\"パスワードを確認してください\",\"ui.auth.credentialsRequired\":\"ユーザー名（またはメールアドレス）は必須です\",\"ui.auth.passwordMinLength\":\"パスワードは最低 6 文字必要です\",\"ui.auth.usernameMinLength\":\"ユーザー名は最低 3 文字必要です\",\"ui.auth.emailInvalid\":\"有効なメールアドレスを入力してください\",\"ui.auth.passwordMismatch\":\"2 回入力したパスワードが一致しません\",\"ui.auth.changePassword\":\"パスワードの変更\",\"ui.auth.currentPassword\":\"現在のパスワード\",\"ui.auth.newPassword\":\"新しいパスワード\",\"ui.auth.confirmNewPassword\":\"新しいパスワードの確認\",\"ui.auth.passwordChangedSuccess\":\"パスワードが正常に変更されました\",\"ui.auth.passwordChangeFailed\":\"パスワードの変更に失敗しました\",\"ui.auth.sessionExpired\":\"ログインセッションが期限切れです。再度ログインしてください\",\"ui.auth.submitting\":\"送信中...\",\"ui.auth.unknownUser\":\"不明なユーザー\",\"ui.auth.userUid\":\"ユーザー UID: {{uid}}\",\"ui.auth.loginRequestFailed\":\"ログインリクエストに失敗しました。ネットワーク状態を確認してください\",\"ui.auth.registerRequestFailed\":\"登録に失敗しました。再試行してください\",\"ui.user.password\":\"アクセスパスワード\",\"ui.nav.navigation\":\"機能ナビゲーション\",\"ui.nav.menuDashboard\":\"ダッシュボード\",\"ui.nav.menuVaults\":\"ノートライブラリ\",\"ui.nav.menuNotes\":\"ノート管理\",\"ui.nav.menuTrash\":\"ゴミ箱\",\"ui.nav.menuSync\":\"バックアップと同期\",\"ui.nav.menuSettings\":\"システム設定\",\"ui.nav.menuGit\":\"Git 自動化\",\"ui.nav.menuFiles\":\"添付ファイル管理\",\"ui.nav.menuShares\":\"共有管理\",\"ui.nav.menuSettingsBrowser\":\"ノートライブラリ設定ファイル\",\"ui.nav.menuSyncLogs\":\"ノートライブラリ更新ログ\",\"ui.nav.mainNavigation\":\"メインナビゲーション\",\"ui.vault.vault\":\"ノートライブラリ\",\"ui.vault.title\":\"ノートライブラリ管理\",\"ui.vault.add\":\"ノートライブラリの追加\",\"ui.vault.edit\":\"ノートライブラリの編集\",\"ui.vault.delete\":\"ノートライブラリの削除\",\"ui.vault.name\":\"ノートライブラリ名\",\"ui.vault.nameRequired\":\"ノートライブラリ名は必須です\",\"ui.vault.confirmDelete\":\"このノートライブラリを削除してもよろしいですか？この操作は取り消せません！\",\"ui.vault.noVaults\":\"現在、ノートライブラリがありません\",\"ui.vault.count\":\"合計 {{count}} 個のノートライブラリ\",\"ui.vault.searchPlaceholder\":\"ノートライブラリを検索...\",\"ui.vault.note\":\"ノート\",\"ui.vault.attachmentCount\":\"添付ファイル\",\"ui.vault.totalSize\":\"総容量: {{size}}\",\"ui.vault.authTokenConfig\":\"認証設定\",\"ui.vault.copyConfigSuccess\":\"設定がクリップボードにコピーされました\",\"ui.vault.copyConfigError\":\"コピーに失敗しました。手動で選択してコピーしてください\",\"ui.vault.oneClickImport\":\"Obsidian へのワンクリックインポート\",\"ui.vault.pleaseCreateVault\":\"新しいノートライブラリを作成するか、Obsidian への認証設定を適用して自動作成してください\",\"ui.vault.createVaultFirst\":\"まずノートライブラリを作成してから管理してください\",\"ui.vault.goToVaultManagement\":\"ノートライブラリ管理へ移動\",\"ui.vault.setAsDefault\":\"デフォルトに設定\",\"ui.note.note\":\"ノート\",\"ui.note.notes\":\"ノートリスト\",\"ui.note.newNote\":\"ノートの作成\",\"ui.note.noNotes\":\"ノートがありません\",\"ui.note.viewNote\":\"ノートの表示\",\"ui.note.editNote\":\"ノートの編集\",\"ui.note.search\":\"検索\",\"ui.note.searchPlaceholder\":\"ノートを検索...\",\"ui.note.noteTitlePlaceholder\":\"ノートタイトル (例: note.md)\",\"ui.note.noteContentPlaceholder\":\"ノートの内容を入力してください...\",\"ui.note.noteCount\":\"ノートの数\",\"ui.note.noteTitleRequired\":\"タイトルは必須です\",\"ui.note.results\":\"件のノート\",\"ui.note.saving\":\"保存中...\",\"ui.note.lastSavedAt\":\"最終保存\",\"ui.note.renameNote\":\"ノートの名前を変更\",\"ui.note.renameNotePlaceholder\":\"新しいノート名を入力してください (例: new-note.md)\",\"ui.note.renameSuccess\":\"ノートの名前変更が成功しました\",\"ui.note.deleteNoteConfirm\":'ノート \"{{title}}\" を削除してもよろしいですか？',\"ui.note.permanentDeleteConfirm\":'ノート \"{{title}}\" を永久削除してもよろしいですか？この操作は元に戻せません！',\"ui.note.restoreNoteConfirm\":'ノート \"{{title}}\" を復元してもよろしいですか？',\"ui.note.clearRecycleConfirm\":\"ノートのゴミ箱を空にしますか？この操作は元に戻せません！\",\"ui.note.noVaultsForNotes\":\"まだノート庫がありません\",\"ui.note.searchPath\":\"パス\",\"ui.note.searchContentMode\":\"内容\",\"ui.note.sortByBy\":\"ソート\",\"ui.note.sortByMtime\":\"更新日時\",\"ui.note.sortByCtime\":\"作成日時\",\"ui.note.sortByPath\":\"パス\",\"ui.note.sortAsc\":\"昇順\",\"ui.note.sortDesc\":\"降順\",\"ui.note.viewFlat\":\"フラット表示\",\"ui.note.viewFolder\":\"フォルダ表示\",\"ui.note.editorHr\":\"区切り線\",\"ui.note.exportPdfPlanned\":\"PDF 出力機能は開発中です...\",\"ui.note.undo\":\"元に戻す\",\"ui.note.redo\":\"やり直す\",\"ui.note.cut\":\"切り取り\",\"ui.note.copy\":\"コピー\",\"ui.note.paste\":\"貼り付け\",\"ui.note.selectAll\":\"すべて選択\",\"ui.note.fullscreen\":\"全画面表示\",\"ui.note.exitFullscreen\":\"全画面表示を終了\",\"ui.note.contextMenu\":\"コンテキストメニュー\",\"ui.note.loadingEditor\":\"エディタを読み込んでいます...\",\"ui.note.unsavedContentWithoutTitle\":\"タイトルが空のため、内容は保存されませんでした。\",\"ui.note.wikiLinkNotFound\":'ノート \"{{target}}\" が見つかりません',\"ui.history.title\":\"ノート履歴\",\"ui.history.description\":\"ノートの履歴バージョンを表示して復元します\",\"ui.history.version\":\"バージョン\",\"ui.history.versionLabel\":\"バージョン\",\"ui.history.time\":\"更新日時\",\"ui.history.action\":\"操作\",\"ui.history.view\":\"表示\",\"ui.history.count\":\"合計 {{count}} 件の履歴\",\"ui.history.loading\":\"読み込み中...\",\"ui.history.noHistory\":\"履歴はありません\",\"ui.history.clientSource\":\"クライアント\",\"ui.history.diffDetails\":\"バージョン v{{version}} の差分詳細\",\"ui.history.showDiffOnly\":\"差分のみ表示\",\"ui.history.showOriginalContent\":\"変更前の内容\",\"ui.history.diffLegendAdd\":\"追加\",\"ui.history.diffLegendDel\":\"削除\",\"ui.history.restore\":\"復元\",\"ui.history.restoreToVersion\":\"このバージョンに復元\",\"ui.history.restoreVersionConfirmTitle\":\"復元を確認\",\"ui.history.restoreVersionConfirmDesc\":\"ノートをバージョン v{{version}} に復元してもよろしいですか？現在の内容は上書きされますが、自動的に新しい履歴バージョンとして保存されます。\",\"ui.history.restoring\":\"復元中...\",\"ui.file.file\":\"添付ファイル\",\"ui.file.files\":\"添付ファイル一覧\",\"ui.file.noFiles\":\"添付ファイルはありません\",\"ui.file.searchFilePlaceholder\":\"添付ファイルを検索...\",\"ui.file.deleteFileConfirm\":'添付ファイル \"{{title}}\" を削除してもよろしいですか？',\"ui.file.restoreFileConfirm\":'添付ファイル \"{{title}}\" を復元してもよろしいですか？',\"ui.file.permanentDeleteConfirm\":'添付ファイル \"{{title}}\" を永久削除してもよろしいですか？この操作は元に戻せません！',\"ui.file.clearRecycleConfirm\":\"添付ファイルのゴミ箱を空にしますか？この操作は元に戻せません！\",\"ui.file.attachmentCount\":\"添付ファイル\",\"ui.file.results\":\"件の添付ファイル\",\"ui.file.searchPlaceholder\":\"添付ファイルを検索...\",\"ui.file.totalSize\":\"合計 {{size}}\",\"ui.file.renameFile\":\"添付ファイルの名前を変更\",\"ui.file.renameFilePlaceholder\":\"新しい添付ファイル名を入力してください\",\"ui.file.renameSuccess\":\"添付ファイルの名前変更が成功しました\",\"ui.file.size\":\"使用容量\",\"ui.file.fileDetail\":\"添付ファイルの詳細\",\"ui.file.imagePreview\":\"画像プレビュー\",\"ui.file.audioPreview\":\"オーディオ再生\",\"ui.file.videoPreview\":\"ビデオ再生\",\"ui.file.pdfPreview\":\"PDF ドキュメント\",\"ui.file.codePreview\":\"スクリプトコード\",\"ui.file.unsupportedPreview\":\"このファイルタイプは直接プレビューできません\",\"ui.file.openInNewWindow\":\"新しいウィンドウで開く\",\"ui.file.browserDownload\":\"ブラウザでダウンロード\",\"ui.file.batchRestore\":\"一括復元\",\"ui.file.batchPermanentDelete\":\"一括恒久的削除\",\"ui.file.selectedCount\":\"{{count}} 件選択済み\",\"ui.file.batchRestoreConfirm\":\"選択した {{count}} 件を復元してもよろしいですか？\",\"ui.file.batchPermanentDeleteConfirm\":\"選択した {{count}} 件を恒久的に削除してもよろしいですか？この操作は取り消せません！\",\"ui.file.noVaultsForFiles\":\"まだノートライブラリがありません\",\"ui.settings.systemConfig\":\"共有短縮リンク\",\"ui.settings.securityConfig\":\"セキュリティトークン\",\"ui.settings.noteRelatedConfig\":\"ノート添付ファイル\",\"ui.settings.fontConfig\":\"一般\",\"ui.settings.saveSettings\":\"設定を保存\",\"ui.settings.saveSuccess\":\"設定の保存に成功しました\",\"ui.settings.saveFailed\":\"設定の保存に失敗しました\",\"ui.settings.fontSet\":\"WebGui フォント設定\",\"ui.settings.authTokenKey\":\"ユーザーサービストークン暗号化混淆文字\",\"ui.settings.tokenExpiry\":\"ユーザーサービストークンの有効期限\",\"ui.settings.shareTokenKey\":\"共有トークン暗号化混淆文字\",\"ui.settings.shareTokenExpiry\":\"共有トークンの有効期限\",\"ui.settings.registerIsEnable\":\"登録を許可\",\"ui.settings.fileChunkSize\":\"添付ファイルアップロードのチャンクサイズ\",\"ui.settings.softDeleteRetentionTime\":\"ゴミ箱（ソフト削除）の保持期間\",\"ui.settings.uploadSessionTimeout\":\"添付ファイルアップロードセッションのタイムアウト\",\"ui.settings.historyKeepVersions\":\"ノート履歴の保持バージョン数\",\"ui.settings.historySaveDelay\":\"ノート履歴の保存遅延\",\"ui.settings.historySaveDelayFormatError\":\"履歴保存遅延の形式が無効です\",\"ui.settings.adminUid\":\"システム設定のアクセス制限（ユーザー UID）\",\"ui.settings.adminUidDesc\":\"管理権限を持つ UID を指定します。0 はすべてのログインユーザーが管理可能であることを示します。デフォルト：0\",\"ui.settings.onlyAdminAccess\":\"管理者のみがこのページにアクセスできます\",\"ui.settings.fontSetDesc\":\"WebGui インターフェースのフォントを設定します。空の場合はシステムデフォルトのフォントを使用します。\\n<u>リモート CSS フォントスタイルシート</u>、<u>プリセット CSS スタイルシートキーワード</u>、<u>フォントネットワークアドレス</u>、および<u>ローカルフォント</u>（storage/user_static/ ディレクトリに配置）をサポートします。デフォルト：空。\\n\\n例:\\n&nbsp;&nbsp;- リモート CSS フォントスタイルシート: <b>https://fonts.googleapis.com/css2?family=Noto+Sans+SC&display=swap</b>\\n&nbsp;&nbsp;- プリセット CSS スタイルシートキーワード: <b>local</b> または <b>remote</b>\\n&nbsp;&nbsp;- ローカルプリセット CSS フォントスタイルシート: <b>/static/fonts/font.css</b>\\n&nbsp;&nbsp;- フォントネットワークアドレス: <b>https://ik.imagekit.io/name/your-font.woff2</b>\\n&nbsp;&nbsp;- ローカルフォント: <b>/user_static/your-font.woff2</b>、フォントファイル <u>your-font.woff2</u> を storage/user_static/ ディレクトリに配置する必要があります\",\"ui.settings.authTokenKeyDesc\":\"この設定項目は、すべてのユーザーのサービストークン生成プロセスに関与します。初回デプロイ後、サーバー側のセキュリティのため、<u><b>この項目の変更を強く推奨します</b></u>。変更すると既存のトークンは即座に無効になり、WebGui にも再ログインが必要です。\",\"ui.settings.tokenExpiryDesc\":\"ユーザーサービストークンの有効期限。サポートされている形式：7d（日）、24h（時間）、30m（分）。デフォルト：<b>365d</b>\",\"ui.settings.shareTokenKeyDesc\":\"共有トークンの生成に使用される暗号化混淆 Key。変更すると既存の共有リンクが無効になります。デフォルト：<b>fns</b>\",\"ui.settings.shareTokenExpiryDesc\":\"共有トークンの有効期限。サポートされている形式：7d（日）、24h（時間）、30m（分）。デフォルト：<b>30d</b>\",\"ui.settings.registerIsEnableDesc\":\"新しいユーザーがこのサービスでアカウントを作成できるかどうか。デフォルト：<b>有効</b>\",\"ui.settings.fileChunkSizeDesc\":\"アップロード時のチャンクサイズ（例：1MB、512KB）。推奨：512KB-2MB。デフォルト：<b>512KB</b>\",\"ui.settings.softDeleteRetentionTimeDesc\":\"ノートと添付ファイルを削除した後の保持期間（例：30d、24h）。0 は自動クリーンアップを行わないことを示します。この設定が短すぎると、オフラインデバイスが削除操作を同期できなくなるため、慎重に設定してください。デフォルト：7d\",\"ui.settings.uploadSessionTimeoutDesc\":\"ファイルチャンクアップロードセッションの有効期限（例：1h、30m）。デフォルト：<b>1d</b>\",\"ui.settings.historyKeepVersionsDesc\":\"各ノートで保持する履歴バージョンの数。この数を超過すると、最も古いバージョンが自動的に削除されます。最小値：100。デフォルト：<b>100</b>\",\"ui.settings.historySaveDelayDesc\":\"履歴保存の遅延時間。頻繁な編集による過剰な履歴バージョンの生成を防ぐために使用されます（例：10s、1m）。デフォルト：<b>10s</b>\",\"ui.settings.toastPosition\":\"通知表示位置\",\"ui.settings.position.top-left\":\"左上\",\"ui.settings.position.top-center\":\"上部中央\",\"ui.settings.position.top-right\":\"右上\",\"ui.settings.position.bottom-left\":\"左下\",\"ui.settings.position.bottom-center\":\"下部中央\",\"ui.settings.position.bottom-right\":\"右下\",\"ui.settings.colorScheme\":\"カラースキーム\",\"ui.settings.colorSchemeSwitched\":\"{{scheme}} カラースキームに切り替えました\",\"ui.settings.colorScheme.default\":\"標準\",\"ui.settings.colorScheme.green\":\"緑\",\"ui.settings.colorScheme.blue\":\"青\",\"ui.settings.colorScheme.skyBlue\":\"空色\",\"ui.settings.colorScheme.purple\":\"紫\",\"ui.settings.colorScheme.orange\":\"オレンジ\",\"ui.settings.colorScheme.rose\":\"ローズ\",\"ui.settings.colorScheme.teal\":\"ティール\",\"ui.settings.themeAuto\":\"自動 (18:00-06:00 ダーク)\",\"ui.settings.themeLight\":\"ライト\",\"ui.settings.themeDark\":\"ダーク\",\"ui.settings.cloudflaredTestRequired\":\"まず「トンネルプログラムダウンロード」ボタンをクリックしてください\",\"ui.settings.downloadSuccess\":\"ダウンロード成功\",\"ui.settings.downloadFailed\":\"ダウンロード失敗\",\"ui.settings.tunnelGatewayConfig\":\"中継ゲートウェイ\",\"ui.settings.tunnelGatewayDesc\":\"<b>FNS</b> に統合されたローカルネットワーク突破サービス。ローカル <b>FNS</b> サービスを、複雑な HTTPS や WebSocket プロキシの設定なしに、パブリックネットワークから安全に直接アクセス可能にします。\\nNgrok または Cloudflare トンネルでのアクセスをサポートしています。\",\"ui.settings.ngrokDesc\":'Ngrok ローカルネットワーク突破トンネルを使用して、ローカルサービスを安全にパブリックネットワークに公開します。\\n公式申請入口: <a href=\"https://dashboard.ngrok.com/get-started/setup\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary hover:underline\">https://dashboard.ngrok.com/get-started/setup</a>\\n <b>注意</b>: Ngrok の無料アカウントには各種制限があります。詳細は <a href=\"https://ngrok.com/docs/pricing-limits/free-plan-limits\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary hover:underline\">https://ngrok.com/docs/pricing-limits/free-plan-limits</a> を参照してください。',\"ui.settings.customDomain\":\"カスタムドメイン\",\"ui.settings.customDomainDesc\":\"オプション、ngrok 有料アカウントが必要です\",\"ui.settings.saveNgrok\":\"設定を保存\",\"ui.settings.cloudflareDesc\":'Cloudflare ゼロトラストネットワークに基づくリバーストンネル。より高いセキュリティと安定性を提供します。\\n公式申請入口: <a href=\"https://one.dash.cloudflare.com\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary hover:underline\">https://one.dash.cloudflare.com</a>\\n設定を保存する前に、まず <b>トンネルプログラムダウンロード</b> を行ってください。',\"ui.settings.enableLog\":\"ログ記録\",\"ui.settings.cloudflareLogDesc\":\"ログは storage/logs/cloudflared_tunnel.log に記録されます\",\"ui.settings.saveCloudflare\":\"設定を保存\",\"ui.settings.downloadCloudflared\":\"トンネルプログラムダウンロード\",\"ui.settings.pullSource\":\"バージョン検出ソース\",\"ui.settings.pullSourceDesc\":\"バージョン検出および更新時のソースリポジトリを選択します。\",\"ui.settings.pullSource.auto\":\"自動検出\",\"ui.settings.pullSource.github\":\"github.com\",\"ui.settings.pullSource.cnb\":\"Tencent cnb.cool\",\"ui.settings.userDatabaseConfig\":\"データベース強化\",\"ui.settings.userDatabaseDesc\":\"デフォルトは <b>SQLite</b>（メンテナンス不要、並行読み取り/単一書き込み対応）。個人および小規模なデプロイに適しており、個人ユーザーの大部分のニーズを満たします。<br/><div class='mt-2 pt-2 border-t border-border/50 opacity-90'><div class='mb-2 text-amber-500 font-medium'>プロフェッショナルなデータベース (PostgreSQL / MySQL) への切り替えを推奨するシナリオ：</div><ul class='list-disc list-inside space-y-1 mb-1'><li>多ユーザー、複数のノートライブラリで規模が非常に大きい場合</li><li>ノートの数や添付ファイルの容量が膨大な場合</li><li>頻繁に並行処理関連のエラーが発生する場合</li></ul><div>プロフェッショナルなデータベースは、並行処理と大規模データ処理においてより信頼性が高いですが、ハードウェア要件と運用コストが高まります（運用能力が必要です）。</div><div class='mt-2 pt-2 border-t border-border/50 text-xs text-rose-500 font-medium italic'>注意：データベースを変更した場合、元のデータは新しいデータベースに自動的に移行されません。ノートライブラリの強制同期を再実行する必要があります。また、データベースタイプを変更する前に、接続テストに成功している必要があります。</div></div>\",\"ui.settings.databaseType\":\"データベースタイプ\",\"ui.settings.databaseType.sqlite\":\"SQLite (埋め込み)\",\"ui.settings.databaseType.mysql\":\"MySQL\",\"ui.settings.databaseType.postgres\":\"PostgreSQL\",\"ui.settings.databaseHost\":\"データベースアドレス (Host)\",\"ui.settings.databasePort\":\"ポート (Port)\",\"ui.settings.databaseUser\":\"ユーザー名 (User)\",\"ui.settings.databasePassword\":\"パスワード (Password)\",\"ui.settings.databaseName\":\"データベース名 (Database)\",\"ui.settings.databaseMaxIdleConns\":\"最大アイドル接続数 (MaxIdle)\",\"ui.settings.databaseMaxOpenConns\":\"最大オープン接続数 (MaxOpen)\",\"ui.settings.databaseConnMaxLifetime\":\"接続の最大存続時間 (MaxLifetime)\",\"ui.settings.databaseConnMaxIdleTime\":\"接続の最大アイドル時間 (MaxIdleTime)\",\"ui.settings.databaseMaxWriteConcurrency\":\"最大書き込み並行数 (MaxWriteConcurrency)\",\"ui.settings.databaseSchema\":\"データベーススキーマ (Postgres)\",\"ui.settings.databaseSslMode\":\"SSL モード (SSL Mode)\",\"ui.settings.mysqlPermissionWarning\":\"注意：MySQL を使用する際、提供されたアカウントにはデータベース作成 (CREATE DATABASE) の権限が必要です。これはユーザー情報の分離に使用されます。\",\"ui.settings.postgresPermissionWarning\":\"注意：PostgreSQL を使用する際、提供されたアカウントにはデータベース作成 (CREATE DATABASE) の権限が必要です。これはユーザー情報の分離に使用されます。\",\"ui.settings.testConnection\":\"接続テスト\",\"ui.settings.testSuccess\":\"接続テスト成功\",\"ui.settings.testFailed\":\"接続テスト失敗\",\"ui.settings.testRequiredBeforeSave\":\"保存する前に、まず接続テストを行い、成功していることを確認してください\",\"ui.settingsBrowser.title\":\"ノートライブラリ設定ファイル\",\"ui.settingsBrowser.description\":\"ノートライブラリの設定情報（ノートライブラリ設定、プラグイン、テーマなど）を管理します\",\"ui.settingsBrowser.add\":\"新規設定\",\"ui.settingsBrowser.edit\":\"設定編集\",\"ui.settingsBrowser.key\":\"パス\",\"ui.settingsBrowser.value\":\"サイズ\",\"ui.settingsBrowser.content\":\"内容\",\"ui.settingsBrowser.keyRequired\":\"パスは空にできません\",\"ui.settingsBrowser.confirmDelete\":'設定項目 \"{{key}}\" を削除してもよろしいですか？',\"ui.settingsBrowser.rename\":\"パスの名前変更\",\"ui.settingsBrowser.newKey\":\"新しいパス\",\"ui.settingsBrowser.renameSuccess\":\"設定項目の名前変更が成功しました\",\"ui.settingsBrowser.noSettings\":\"設定項目はありません\",\"ui.obsidian.authTokenConfig\":\"認証設定\",\"ui.obsidian.authTokenConfigTo\":\"ワンクリックで Obsidian に認証\",\"ui.obsidian.oneClickImport\":\"ワンクリックで Obsidian に認証\",\"ui.obsidian.copyConfigSuccess\":\"認証設定をコピーしました。Obsidian プラグインに戻って設定を貼り付けてください\",\"ui.obsidian.copyConfigError\":\"HTTPS ではないページのため、クリップボード機能を使用できません。認証設定を手動でコピーしてください\",\"ui.system.serviceInfo\":\"サービス情報\",\"ui.system.versionInfo\":\"バージョン情報\",\"ui.system.repo\":\"プロジェクトリポジトリ\",\"ui.system.githubRepo\":\"GitHub リポジトリ\",\"ui.system.cnbMirror\":\"CNB ミラーリポジトリ\",\"ui.system.currentVersion\":\"現在のバージョン\",\"ui.system.checkUpdate\":\"更新を確認\",\"ui.system.checkNow\":\"今すぐ確認\",\"ui.system.checking\":\"確認中...\",\"ui.system.newVersionAvailable\":\"新しいバージョンが利用可能です\",\"ui.system.alreadyLatest\":\"最新バージョンです\",\"ui.system.viewRelease\":\"リリースページを表示\",\"ui.system.upgradeNow\":\"今すぐアップグレード\",\"ui.system.upgrading\":\"アップグレード中...\",\"ui.system.upgradeSuccess\":\"アップグレードのトリガーに成功しました。ページが再読み込みされます。\",\"ui.system.upgradeFailed\":\"アップグレードのトリガーに失敗しました\",\"ui.system.upgradeRefreshTimeout\":\"アップグレードが完了しましたが、サービスはまだ利用できません。ページを手動でリフレッシュしてください。\",\"ui.system.viewChangelog\":\"更新履歴を表示\",\"ui.system.getVersionError\":\"バージョン情報の取得に失敗しました\",\"ui.system.getWebGuiConfigError\":\"WebGui 設定の取得に失敗しました:\",\"ui.system.restartService\":\"サービスを再起動\",\"ui.system.restartServiceConfirm\":\"今すぐサービスを再起動してもよろしいですか？再起動中は接続が切断されます。\",\"ui.system.manualGC\":\"メモリ回収\",\"ui.system.manualGCConfirm\":\"今すぐメモリ回収 (GC) を実行してもよろしいですか？\",\"ui.system.manualGCSuccess\":\"手動メモリ回収 (GC) のトリガーに成功しました\",\"ui.system.serverSystemInfo\":\"サーバー情報\",\"ui.system.modelName\":\"プロセッサモデル\",\"ui.system.hostInfo\":\"システム情報\",\"ui.system.systemTime\":\"システム時刻\",\"ui.system.runtimeInfo\":\"サービス情報\",\"ui.system.startTime\":\"起動時刻\",\"ui.system.serviceUptime\":\"稼働時間\",\"ui.system.physicalCores\":\"コア (論理/物理)\",\"ui.system.cpuLoad\":\"平均負荷 (1/5/15)\",\"ui.system.totalMemory\":\"総メモリ\",\"ui.system.usedMemory\":\"使用メモリ\",\"ui.system.memoryUsage\":\"メモリ使用率\",\"ui.system.os\":\"オペレーティングシステム\",\"ui.system.kernelVersion\":\"カーネルバージョン\",\"ui.system.uptime\":\"稼働時間\",\"ui.system.goVersion\":\"ランタイムバージョン\",\"ui.system.goroutines\":\"ゴルーチン数\",\"ui.system.heapMemory\":\"ヒープメモリ / 総使用量\",\"ui.system.numGc\":\"GC 回数\",\"ui.system.createdAt\":\"作成日時\",\"ui.system.updatedAt\":\"更新日時\",\"ui.system.websocketClients\":\"オンラインクライアント\",\"ui.system.wsNickname\":\"ニックネーム\",\"ui.system.wsClientName\":\"クライアント\",\"ui.system.wsClientType\":\"タイプ\",\"ui.system.wsRemoteAddr\":\"アドレス\",\"ui.system.wsStartTime\":\"接続時刻\",\"ui.system.wsTraceId\":\"トレース ID\",\"ui.system.wsNoClients\":\"オンラインクライアントはありません\",\"ui.storage.management\":\"ストレージ設定\",\"ui.storage.add\":\"ストレージの追加\",\"ui.storage.edit\":\"ストレージの編集\",\"ui.storage.noStorage\":\"ストレージ設定はありません\",\"ui.storage.type\":\"タイプ\",\"ui.storage.storageType.oss\":\"アリババクラウド OSS\",\"ui.storage.storageType.s3\":\"AWS S3\",\"ui.storage.storageType.r2\":\"Cloudflare R2\",\"ui.storage.storageType.minio\":\"MinIO\",\"ui.storage.storageType.localfs\":\"ローカルストレージ\",\"ui.storage.storageType.webdav\":\"WebDAV\",\"ui.storage.selectType\":\"ストレージタイプを選択\",\"ui.storage.endpoint\":\"エンドポイント ( Endpoint )\",\"ui.storage.region\":\"リージョン ( S3 Region )\",\"ui.storage.accountId\":\"アカウント ID ( R2 Account ID )\",\"ui.storage.bucketName\":\"バケット\",\"ui.storage.accessKeyId\":\"アクセスキー ID ( AccessKey ID )\",\"ui.storage.accessKeySecret\":\"シークレット ( AccessKey Secret )\",\"ui.storage.webdavUrl\":\"WebDAV アドレス\",\"ui.storage.webdavUser\":\"ユーザー名\",\"ui.storage.webdavPassword\":\"パスワード\",\"ui.storage.customPath\":\"カスタムパス\",\"ui.storage.accessUrlPrefix\":\"アクセス URL プレフィックス\",\"ui.storage.placeholder.endpoint.oss\":\"oss-cn-hangzhou.aliyuncs.com\",\"ui.storage.placeholder.endpoint.minio\":\"http://192.168.1.100:9000\",\"ui.storage.placeholder.region\":\"us-east-1\",\"ui.storage.placeholder.accountId\":\"your-account-id\",\"ui.storage.placeholder.bucketName\":\"my-bucket\",\"ui.storage.placeholder.accessKeyId\":\"\",\"ui.storage.placeholder.accessKeySecret\":\"\",\"ui.storage.placeholder.webdavUrl\":\"http://192.168.1.100:5244/dav\",\"ui.storage.placeholder.webdavUser\":\"admin\",\"ui.storage.placeholder.webdavPassword\":\"\",\"ui.storage.placeholder.customPath\":\"data/obsidian\",\"ui.storage.placeholder.accessUrlPrefix\":\"http://192.168.1.100:5244\",\"ui.storage.help.endpoint.oss\":\"Bucket 名を含まないアリババクラウド OSS の Endpoint アドレス\",\"ui.storage.help.endpoint.minio\":\"MinIO サービスのアドレス。プロトコルプレフィックスとポートを含める必要があります\",\"ui.storage.help.region\":\"S3 ストレージバケットが所在するリージョン\",\"ui.storage.help.accountId\":\"Cloudflare アカウント ID。コンソールの右サイドバーで確認できます\",\"ui.storage.help.webdavUrl\":\"プロトコルプレフィックス (http:// または https://) と完全なパスを含める必要があります\",\"ui.storage.help.webdavUser\":\"WebDAV サービスのログインユーザー名\",\"ui.storage.help.webdavPassword\":\"WebDAV サービスのログインパスワード\",\"ui.storage.help.customPath\":\"ファイルを保存するサブディレクトリのパス。先頭と末尾のスラッシュは不要です\",\"ui.storage.help.accessUrlPrefix\":\"ファイルアクセスリンクの生成に使用するプレフィックスアドレス。表示用のみです\",\"ui.storage.confirmDelete\":\"このストレージ設定を削除してもよろしいですか？\",\"ui.storage.validate.title\":\"接続テスト\",\"ui.storage.validate.loading\":\"テスト中...\",\"ui.backup.management\":\"タスク管理\",\"ui.backup.add\":\"タスクの追加\",\"ui.backup.edit\":\"タスクの編集\",\"ui.backup.noBackup\":\"バックアップおよび同期タスクがありません\",\"ui.backup.selectType\":\"バックアップタイプを選択してください\",\"ui.backup.confirmDelete\":\"このバックアップタスクを削除してもよろしいですか？\",\"ui.backup.vault\":\"ノートライブラリ\",\"ui.backup.selectVault\":\"ノートライブラリを選択\",\"ui.backup.type\":\"バックアップタイプ\",\"ui.backup.backupType.full\":\"フルバックアップ\",\"ui.backup.backupType.incremental\":\"インクリメンタルバックアップ\",\"ui.backup.backupType.sync\":\"ミラーリング同期\",\"ui.backup.cronStrategy\":\"スケジュール戦略\",\"ui.backup.strategy.daily\":\"毎日バックアップ (00:00)\",\"ui.backup.strategy.weekly\":\"毎週バックアップ (月曜日 00:00)\",\"ui.backup.strategy.monthly\":\"毎月バックアップ (1 日 00:00)\",\"ui.backup.strategy.custom\":\"カスタム Cron\",\"ui.backup.cronExpression\":\"Cron 式\",\"ui.backup.retentionDays\":\"保持日数\",\"ui.backup.fileCountUnit\":\"ファイル\",\"ui.backup.retentionDays.sync\":\"履歴保持日数 (0: 履歴を削除しない、-1: 履歴を保持しない)\",\"ui.backup.retentionDays.backup\":\"バックアップパッケージの保持日数 (0: バックアップを削除しない、-1: 履歴バックアップを保持しない)\",\"ui.backup.includeVaultName.label\":\"ノートライブラリ名を含む\",\"ui.backup.includeVaultName.tooltip\":\"オフ: {customPath}/notes/xxx.md\\nオン: {customPath}/{vaultName}/notes/xxx.md\",\"ui.backup.noAvailableStorage\":\"利用可能なストレージ設定がありません\",\"ui.backup.addStorageTip\":\"ストレージ管理でストレージバックエンドを追加して有効化してください\",\"ui.backup.storages\":\"ストレージ選択\",\"ui.backup.validation.vaultRequired\":\"ノートライブラリを選択してください\",\"ui.backup.validation.typeRequired\":\"バックアップタイプを選択してください\",\"ui.backup.validation.strategyRequired\":\"スケジュール戦略を選択してください\",\"ui.backup.validation.storageRequired\":\"少なくとも 1 つのストレージを選択してください\",\"ui.backup.validation.retentionDaysMin\":\"保持日数は -1 以上である必要があります\",\"ui.backup.validation.cronExpressionRequired\":\"カスタム戦略では Cron 式が空であってはなりません\",\"ui.backup.lastRunTime\":\"最終実行時間\",\"ui.backup.nextRunTime\":\"次回実行時間\",\"ui.backup.executeNow\":\"今すぐ実行\",\"ui.backup.executeSuccess\":\"手動バックアップのトリガーに成功しました\",\"ui.backup.status.0\":\"待機中\",\"ui.backup.status.1\":\"実行中\",\"ui.backup.status.2\":\"成功\",\"ui.backup.status.3\":\"失敗\",\"ui.backup.status.4\":\"キャンセル\",\"ui.backup.status.5\":\"更新なし\",\"ui.backup.history.title\":\"タスク履歴\",\"ui.backup.history.startTime\":\"タスク実行時間\",\"ui.backup.history.storage\":\"ストレージ\",\"ui.backup.history.status\":\"ステータス\",\"ui.backup.history.backupStats\":\"バックアップ統計\",\"ui.backup.history.syncStats\":\"同期統計\",\"ui.backup.history.backupFile\":\"バックアップファイル\",\"ui.backup.history.message\":\"メッセージ\",\"ui.backup.history.copyError\":\"エラーメッセージをコピー\",\"ui.backup.history.noData\":\"タスク記録がありません\",\"ui.git.title\":\"Git 自動化管理\",\"ui.git.config\":\"Git リポジトリ設定\",\"ui.git.repoUrl\":\"Git リポジトリ URL (ssh アドレスはサポートされていません)\",\"ui.git.status\":\"Git ステータス概要\",\"ui.git.history\":\"自動コミット履歴\",\"ui.git.comingSoon\":\"Git 自動化機能は開発中です...\",\"ui.git.configDesc\":\"Git リポジトリの URL、ブランチ、認証情報を設定します。\",\"ui.git.statusDesc\":\"現在のリポジトリのコミットステータスと同期進捗をリアルタイムで監視します。\",\"ui.git.historyDesc\":\"最近の自動コミット記録を表示します。\",\"ui.git.retentionDays\":\"履歴保持日数\",\"ui.git.retentionDaysDesc\":\"0: 履歴を削除しない、-1: 履歴を保持しない\",\"ui.git.addConfig\":\"Git 同期設定を追加\",\"ui.git.editConfig\":\"Git 同期設定を編集\",\"ui.git.loading\":\"設定を読み込んでいます...\",\"ui.git.noConfig\":\"Git 同期設定がありません\",\"ui.git.addFirst\":\"最初のリポジトリを今すぐ追加\",\"ui.git.lastCommit\":\"最終コミット\",\"ui.git.checkTime\":\"最終検出時刻\",\"ui.git.neverRun\":\"未実行\",\"ui.git.execute.title\":\"同期を即時トリガー\",\"ui.git.clean.title\":\"ワークスペースをクリーンアップ\",\"ui.git.clean.confirm\":\"この Git ワークスペースをクリーンアップしますか？ローカルのクローンが削除され、再初期化されます。\",\"ui.git.delete.confirm\":\"この Git 設定を削除しますか？この操作は元に戻せません。\",\"ui.git.form.branch\":\"ブランチ名\",\"ui.git.form.delay\":\"自動同期遅延時間（秒）\",\"ui.git.history.title\":\"コミット履歴\",\"ui.git.history.configId\":\"設定 ID\",\"ui.git.history.startTime\":\"コミット時刻\",\"ui.git.history.endTime\":\"終了時刻\",\"ui.git.history.status\":\"ステータス\",\"ui.git.history.message\":\"メッセージ\",\"ui.git.history.noData\":\"コミット履歴がありません\",\"ui.git.history.duration\":\"コミット所要時間\",\"ui.git.history.vault\":\"ノートライブラリ\",\"ui.git.status.0\":\"待機中\",\"ui.git.status.1\":\"検出中\",\"ui.git.status.2\":\"成功\",\"ui.git.status.3\":\"失敗\",\"ui.git.status.4\":\"停止済み\",\"ui.git.validate.title\":\"接続を検出\",\"ui.git.validate.loading\":\"検出中...\",\"ui.validation.git.vaultRequired\":\"ノートブックを選択してください\",\"ui.validation.git.repoUrlRequired\":\"リポジトリ URL を入力してください\",\"ui.validation.git.repoUrlInvalid\":\"有効な URL を入力してください\",\"ui.validation.git.branchRequired\":\"ブランチ名を入力してください\",\"ui.validation.git.delayMin\":\"遅延時間は負の値にできません\",\"ui.validation.git.retentionDaysMin\":\"保持日数は -1 以上である必要があります\",\"ui.validation.storage.typeRequired\":\"ストレージタイプを選択してください\",\"ui.validation.storage.accessUrlPrefixRequired\":\"アクセス URL プレフィックスは空にできません\",\"ui.validation.vault.nameRequired\":\"リポジトリ名は空にできません\",\"api.git.list.error\":\"Git 設定リストの取得に失敗しました\",\"api.git.save.success\":\"設定の保存に成功しました\",\"api.git.save.error\":\"設定の保存に失敗しました\",\"api.git.delete.success\":\"設定の削除に成功しました\",\"api.git.delete.error\":\"設定の削除に失敗しました\",\"api.git.execute.success\":\"同期タスクをトリガーしました\",\"api.git.execute.error\":\"同期のトリガーに失敗しました\",\"api.git.clean.success\":\"ワークスペースのクリーンアップに成功しました\",\"api.git.clean.error\":\"ワークスペースのクリーンアップに失敗しました\",\"api.git.history.error\":\"履歴の取得に失敗しました\",\"api.git.validate.success\":\"Git リポジトリ接続テストに成功しました\",\"api.git.validate.error\":\"Git リポジトリ接続テストに失敗しました\",\"api.backup.configList.error\":\"バックアップ設定リストの取得に失敗しました\",\"api.backup.delete.success\":\"削除に成功しました\",\"api.backup.delete.error\":\"バックアップ設定の削除に失敗しました\",\"api.backup.save.success\":\"保存に成功しました\",\"api.backup.save.error\":\"バックアップ設定の保存に失敗しました\",\"api.backup.execute.success\":\"手動トリガーに成功しました\",\"api.backup.execute.error\":\"バックアップのトリガーに失敗しました\",\"api.backup.history.error\":\"バックアップ履歴の取得に失敗しました\",\"api.system.restart.success\":\"サービスの再起動がトリガーされました\",\"api.system.restart.error\":\"サービス再起動リクエストに失敗しました\",\"api.system.gc.success\":\"メモリ回収がトリガーされました\",\"api.system.gc.error\":\"メモリ回収リクエストに失敗しました\",\"api.storage.list.error\":\"ストレージ設定リストの取得に失敗しました\",\"api.storage.delete.success\":\"削除に成功しました\",\"api.storage.delete.error\":\"ストレージ設定の削除に失敗しました\",\"api.storage.save.error\":\"ストレージ設定の保存に失敗しました\",\"api.storage.types.error\":\"ストレージタイプの取得に失敗しました\",\"api.storage.validate.success\":\"ストレージ接続テストに成功しました\",\"api.storage.validate.error\":\"ストレージ接続テストに失敗しました\",\"error.storage.webdav.unauthorized\":\"WebDAV 認証に失敗しました。ユーザー名とパスワードを確認してください\",\"error.storage.webdav.forbidden\":\"WebDAV 権限が不足しています。アカウントの書き込み権限を確認してください\",\"error.storage.webdav.notFound\":\"WebDAV パスが存在しません。カスタムパス設定を確認してください\",\"error.storage.webdav.methodNotAllowed\":\"WebDAV メソッドが許可されていません。サーバー側で WebDAV が有効になっているか確認してください\",\"error.storage.webdav.unreachable\":\"WebDAV アドレスにアクセスできません。ストレージ設定内のアドレスを確認してください\",\"error.storage.webdav.connectionRefused\":\"WebDAV 接続が拒否されました。サービスが実行されているか確認してください\",\"error.storage.webdav.timeout\":\"WebDAV 接続がタイムアウトしました。ネットワークまたはサービス状態を確認してください\",\"error.storage.webdav.generic\":\"WebDAV 操作に失敗しました。ストレージ設定を確認してください\",\"error.storage.s3.noSuchBucket\":\"S3 バケットが存在しません。バケット名の設定を確認してください\",\"error.storage.s3.accessDenied\":\"S3 アクセスが拒否されました。Access Key の権限を確認してください\",\"error.storage.s3.unreachable\":\"S3 エンドポイントにアクセスできません。Endpoint アドレスを確認してください\",\"error.storage.s3.timeout\":\"S3 接続がタイムアウトしました。ネットワークまたはエンドポイント設定を確認してください\",\"error.storage.oss.noSuchBucket\":\"OSS バケットが存在しません。Bucket 名を確認してください\",\"error.storage.oss.accessDenied\":\"OSS アクセスが拒否されました。AccessKey の権限を確認してください\",\"error.storage.oss.unreachable\":\"OSS エンドポイントにアクセスできません。Endpoint アドレスを確認してください\",\"error.storage.local.noPermission\":\"ローカルストレージに書き込み権限がありません\",\"error.storage.local.createDirFailed\":\"ローカルストレージディレクトリの作成に失敗しました\",\"error.storage.local.permissionDenied\":\"ローカルストレージのアクセスが拒否されました\",\"error.backup.partialFailure\":\"一部のファイルの同期に失敗しました。詳細なエラーメッセージを確認してください\",\"error.backup.uploadFailed\":\"バックアップファイルのアップロードに失敗しました\",\"error.backup.openFileFailed\":\"バックアップファイルのオープンに失敗しました\",\"error.backup.vaultNotExist\":\"ノートライブラリが存在しません。設定を確認してください\",\"error.network.unreachable\":\"ターゲットアドレスにアクセスできません。ネットワーク設定を確認してください\",\"error.network.connectionRefused\":\"接続が拒否されました。ターゲットサービスが実行されているか確認してください\",\"error.network.timeout\":\"接続がタイムアウトしました。ネットワーク状態を確認してください\",\"ui.support.title\":\"このプロジェクトを支援する\",\"ui.support.supportRequest\":\"このプロジェクトが役に立ち、開発を継続していただければ幸いです。以下の方法で支援してください。オープンソースソフトウェアへの支援をありがとうございます！\",\"ui.support.listTitle\":\"支援履歴\",\"ui.support.noData\":\"記録がありません\",\"ui.support.item\":\"項目\",\"ui.support.amount\":\"金額\",\"ui.support.time\":\"時間\",\"ui.support.message\":\"メッセージ\",\"ui.support.thanks\":\"Fast Note Sync への皆様のご支援に感謝します！支援リストは不定期に更新されます\",\"ui.support.sortDefault\":\"デフォルト (金額)\",\"ui.support.sortTime\":\"時間順\",\"ui.support.sort\":\"ソート\",\"ui.support.buyMeACoffee\":\"作者にコーヒーを一杯\",\"ui.support.wechatReward\":\"WeChat で寄付\",\"ui.share.invalidLink\":\"無効な共有リンクです。ID または Token が不足しています。\",\"ui.share.errorTitle\":\"共有エラー\",\"ui.share.noteNotFound\":\"共有されているノートが見つかりません。\",\"ui.share.poweredByPrefix\":\"Powered by \",\"ui.share.poweredBySuffix\":\" 提供支持\",\"ui.share.version\":\"バージョン\",\"ui.share.tabActive\":\"共有中\",\"ui.share.noShares\":\"共有履歴がありません\",\"ui.share.viewShare\":\"共有ページを表示\",\"ui.share.cancelShare\":\"共有をキャンセル\",\"ui.share.cancelConfirm\":\"このノートの共有をキャンセルしますか？キャンセルすると共有リンクは即座に無効になります。\",\"ui.share.shareNotFound\":\"共有記録が見つかりません\",\"ui.share.title\":\"ノートを共有\",\"ui.share.checking\":\"共有ステータスを確認中...\",\"ui.share.create\":\"共有を開始\",\"ui.share.creating\":\"作成中...\",\"ui.share.success\":\"共有に成功しました\",\"ui.share.shortLinkCreate\":\"短縮リンクを生成\",\"ui.share.link\":\"共有リンク\",\"ui.share.shortLink\":\"短縮リンク\",\"ui.share.copy\":\"リンクをコピー\",\"ui.share.copySuccess\":\"リンクをクリップボードにコピーしました\",\"ui.share.shortLinkCopy\":\"短縮リンクをコピー\",\"ui.share.cancelSuccess\":\"共有をキャンセルしました\",\"ui.share.buttonCreating\":\"作成中...\",\"ui.share.passwordRequired\":\"パスワードが必要です\",\"ui.share.passwordHint\":\"この共有はパスワードで保護されています。コンテンツを表示するにはパスワードを入力してください。\",\"ui.share.passwordPlaceholder\":\"パスワードを入力してください...\",\"ui.share.preview\":\"コンテンツのプレビュー\",\"ui.syncLog.title\":\"ノートライブラリ更新ログ\",\"ui.syncLog.vault\":\"ノートライブラリ\",\"ui.syncLog.type\":\"タイプ\",\"ui.syncLog.action\":\"操作\",\"ui.syncLog.path\":\"ファイルパス\",\"ui.syncLog.size\":\"サイズ\",\"ui.syncLog.client\":\"クライアント\",\"ui.syncLog.status\":\"ステータス\",\"ui.syncLog.message\":\"詳細情報\",\"ui.syncLog.time\":\"記録時刻\",\"ui.syncLog.changedFields\":\"変更内容\",\"ui.syncLog.noLogs\":\"同期記録はありません\",\"ui.syncLog.noLogsDescription\":\"更新記録が見つかりません\",\"ui.syncLog.description\":\"ノートライブラリ内のノート、添付ファイル、フォルダ、設定ファイルの同期変更履歴を追跡します\",\"ui.syncLog.allVaults\":\"すべてのノートライブラリ\",\"ui.syncLog.allTypes\":\"すべてのタイプ\",\"ui.syncLog.allActions\":\"すべての操作\",\"ui.syncLog.resetFilters\":\"フィルターをリセット\",\"ui.syncLog.statusSuccess\":\"成功\",\"ui.syncLog.statusFailed\":\"失敗\",\"ui.syncLog.type.note\":\"ノート\",\"ui.syncLog.type.file\":\"添付ファイル\",\"ui.syncLog.type.setting\":\"設定\",\"ui.syncLog.type.folder\":\"フォルダ\",\"ui.syncLog.action.create\":\"新規作成\",\"ui.syncLog.action.modify\":\"修正\",\"ui.syncLog.action.soft_delete\":\"論理削除\",\"ui.syncLog.action.delete\":\"永続削除\",\"ui.syncLog.action.rename\":\"名前変更\",\"ui.syncLog.action.restore\":\"復元\"};export{e as default};\n"
  },
  {
    "path": "frontend/assets/ko-CMKMFQrR.js",
    "content": "const e={\"ui.common.title\":\"Fast Note Sync\",\"ui.common.subtitle\":\"고성능, 저지연 노트 동기화, 관리, REST 서비스\",\"ui.common.footerTitle\":\"Golang + Websocket + Sqlite + React 기반\\n<a href='https://github.com/haierkeys/obsidian-fast-note-sync' target='_blank'>Obsidian Fast Note Sync Plugin</a>과 함께 사용해야 합니다\",\"ui.common.loading\":\"로딩 중...\",\"ui.common.downloading\":\"다운로드 중...\",\"ui.common.actions\":\"작업\",\"ui.common.save\":\"저장\",\"ui.common.add\":\"추가\",\"ui.common.edit\":\"편집\",\"ui.common.view\":\"보기\",\"ui.common.viewDetail\":\"상세 정보 보기\",\"ui.common.delete\":\"삭제\",\"ui.common.restore\":\"복원\",\"ui.common.permanentDelete\":\"영구 삭제\",\"ui.common.clear\":\"비우기\",\"ui.common.batchPermanentDelete\":\"일괄 영구 삭제\",\"ui.common.batchPermanentDeleteConfirm\":\"선택한 {{count}}개 항목을 영구적으로 삭제하시겠습니까? 이 작업은 취소할 수 없습니다!\",\"ui.common.search\":\"검색\",\"ui.common.refresh\":\"새로고침\",\"ui.common.retry\":\"재시도\",\"ui.common.refreshSuccess\":\"새로고침 성공\",\"ui.common.close\":\"닫기\",\"ui.common.cancel\":\"취소\",\"ui.common.confirm\":\"확인\",\"ui.common.success\":\"성공\",\"ui.common.error\":\"오류\",\"ui.common.warning\":\"경고\",\"ui.common.info\":\"정보\",\"ui.common.total\":\"총계\",\"ui.common.isEnabled\":\"활성화됨\",\"ui.common.isDisabled\":\"비활성화됨\",\"ui.common.switchLanguage\":\"언어 변경\",\"ui.common.comingSoon\":\"곧 출시...\",\"ui.common.comingSoonDescription\":\"이 기능은 개발 중입니다. 곧 출시될 예정입니다...\",\"ui.common.helpAndSupport\":\"도움말 및 지원\",\"ui.common.githubIssue\":\"피드백\",\"ui.common.githubIssueDesc\":\"버그 또는 기능 제안 제출\",\"ui.common.telegramGroup\":\"커뮤니티\",\"ui.common.telegramGroupDesc\":\"Telegram 커뮤니티에서 논의하고 지원 받기\",\"ui.common.planned\":\"개발 예정\",\"ui.common.unknown\":\"알 수 없음\",\"ui.common.copied\":\"복사됨\",\"ui.common.restoring\":\"복원 중...\",\"ui.common.rename\":\"이름 변경\",\"ui.common.toggleTheme\":\"테마 변경\",\"ui.common.previous\":\"이전 페이지\",\"ui.common.next\":\"다음 페이지\",\"ui.common.page\":\"페이지\",\"ui.common.of\":\"/\",\"ui.common.to\":\"~\",\"ui.common.perPage\":\"페이지당\",\"ui.common.copy\":\"복사\",\"ui.common.createdAt\":\"생성일\",\"ui.common.updatedAt\":\"수정일\",\"ui.common.noSearchResults\":\"일치하는 결과를 찾을 수 없습니다\",\"ui.common.selectAll\":\"전체 선택\",\"ui.common.items\":\"개\",\"ui.common.count\":\"개\",\"ui.common.saveSuccess\":\"저장 성공\",\"ui.common.selectVault\":\"노트 저장소 선택\",\"ui.common.sourceCode\":\"소스 코드\",\"ui.common.wideMode\":\"광각 모드\",\"ui.common.narrowMode\":\"일반 너비\",\"ui.common.fold\":\"접기\",\"ui.common.noChange\":\"내용이 변경되지 않았습니다\",\"ui.common.na\":\"N/A\",\"ui.common.name\":\"이름\",\"ui.auth.login\":\"로그인\",\"ui.auth.logout\":\"로그아웃\",\"ui.auth.register\":\"회원가입\",\"ui.auth.registerButton\":\"회원가입\",\"ui.auth.registerClosed\":\"사용자 회원가입이 닫혔습니다\",\"ui.auth.credentials\":\"사용자명 (또는 이메일)\",\"ui.auth.username\":\"사용자 이름\",\"ui.auth.password\":\"비밀번호\",\"ui.auth.remember\":\"기억하기\",\"ui.auth.confirmPassword\":\"비밀번호 확인\",\"ui.auth.email\":\"이메일\",\"ui.auth.credentialsPlaceholder\":\"사용자 이름 (또는 이메일) 을 (를) 입력하세요\",\"ui.auth.usernamePlaceholder\":\"사용자 이름을 입력하세요\",\"ui.auth.passwordPlaceholder\":\"비밀번호를 입력하세요\",\"ui.auth.emailPlaceholder\":\"이메일을 입력하세요\",\"ui.auth.confirmPasswordPlaceholder\":\"비밀번호를 확인하세요\",\"ui.auth.credentialsRequired\":\"사용자 이름 (또는 이메일) 은 비워둘 수 없습니다\",\"ui.auth.passwordMinLength\":\"비밀번호는 최소 6 자 이상이어야 합니다\",\"ui.auth.usernameMinLength\":\"사용자 이름은 최소 3 자 이상이어야 합니다\",\"ui.auth.emailInvalid\":\"유효한 이메일 주소를 입력하세요\",\"ui.auth.passwordMismatch\":\"입력한 비밀번호가 일치하지 않습니다\",\"ui.auth.changePassword\":\"비밀번호 변경\",\"ui.auth.currentPassword\":\"현재 비밀번호\",\"ui.auth.newPassword\":\"새 비밀번호\",\"ui.auth.confirmNewPassword\":\"새 비밀번호 확인\",\"ui.auth.passwordChangedSuccess\":\"비밀번호가 성공적으로 변경되었습니다\",\"ui.auth.passwordChangeFailed\":\"비밀번호 변경에 실패했습니다\",\"ui.auth.sessionExpired\":\"사용자 로그인 상태가 만료되었습니다. 다시 로그인하세요\",\"ui.auth.submitting\":\"제출 중...\",\"ui.auth.unknownUser\":\"알 수 없는 사용자\",\"ui.auth.userUid\":\"사용자 UID: {{uid}}\",\"ui.auth.loginRequestFailed\":\"로그인 요청에 실패했습니다. 네트워크 상태를 확인하세요\",\"ui.auth.registerRequestFailed\":\"등록에 실패했습니다. 다시 시도하세요\",\"ui.user.password\":\"접근 비밀번호\",\"ui.nav.navigation\":\"기능 탐색\",\"ui.nav.menuDashboard\":\"대시보드\",\"ui.nav.menuVaults\":\"노트 저장소\",\"ui.nav.menuNotes\":\"노트 관리\",\"ui.nav.menuTrash\":\"휴지통\",\"ui.nav.menuSync\":\"백업 및 동기화\",\"ui.nav.menuSettings\":\"시스템 설정\",\"ui.nav.menuGit\":\"Git 자동화\",\"ui.nav.menuFiles\":\"첨부 파일 관리\",\"ui.nav.menuShares\":\"공유 관리\",\"ui.nav.menuSettingsBrowser\":\"노트북 설정 파일\",\"ui.nav.menuSyncLogs\":\"노트북 업데이트 로그\",\"ui.nav.mainNavigation\":\"주 탐색\",\"ui.vault.vault\":\"노트 저장소\",\"ui.vault.title\":\"노트 저장소 관리\",\"ui.vault.add\":\"노트 저장소 추가\",\"ui.vault.edit\":\"노트 저장소 편집\",\"ui.vault.delete\":\"노트 저장소 삭제\",\"ui.vault.name\":\"노트 저장소 이름\",\"ui.vault.nameRequired\":\"노트 저장소 이름은 비워둘 수 없습니다\",\"ui.vault.confirmDelete\":\"이 노트 저장소를 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다!\",\"ui.vault.noVaults\":\"현재 노트 저장소가 없습니다\",\"ui.vault.count\":\"총 {{count}} 개의 노트 저장소\",\"ui.vault.searchPlaceholder\":\"노트 저장소 검색...\",\"ui.vault.note\":\"노트\",\"ui.vault.attachmentCount\":\"첨부 파일\",\"ui.vault.totalSize\":\"총 용량: {{size}}\",\"ui.vault.authTokenConfig\":\"인증 구성\",\"ui.vault.copyConfigSuccess\":\"구성이 클립보드에 복사되었습니다\",\"ui.vault.copyConfigError\":\"복사에 실패했습니다. 수동으로 선택하여 복사하세요\",\"ui.vault.oneClickImport\":\"한 번에 Obsidian 으로 가져오기\",\"ui.vault.pleaseCreateVault\":\"노트 저장소를 새로 만들거나 Obsidian 으로 인증 구성하여 자동으로 생성하세요\",\"ui.vault.createVaultFirst\":\"먼저 노트 저장소를 생성한 후 관리하세요\",\"ui.vault.goToVaultManagement\":\"노트 저장소 관리로 이동\",\"ui.vault.setAsDefault\":\"기본으로 설정\",\"ui.note.note\":\"노트\",\"ui.note.notes\":\"노트 목록\",\"ui.note.newNote\":\"새 노트\",\"ui.note.noNotes\":\"노트가 없습니다\",\"ui.note.viewNote\":\"노트 보기\",\"ui.note.editNote\":\"노트 편집\",\"ui.note.search\":\"검색\",\"ui.note.searchPlaceholder\":\"노트 검색...\",\"ui.note.noteTitlePlaceholder\":\"노트 제목 (예: note.md)\",\"ui.note.noteContentPlaceholder\":\"노트 내용을 입력하세요...\",\"ui.note.noteCount\":\"노트 수\",\"ui.note.noteTitleRequired\":\"제목은 비워둘 수 없습니다\",\"ui.note.results\":\"개의 노트\",\"ui.note.saving\":\"저장 중...\",\"ui.note.lastSavedAt\":\"마지막 저장\",\"ui.note.renameNote\":\"노트 이름 변경\",\"ui.note.renameNotePlaceholder\":\"새 노트 이름을 입력하세요 (예: new-note.md)\",\"ui.note.renameSuccess\":\"노트 이름 변경 성공\",\"ui.note.deleteNoteConfirm\":'노트 \"{{title}}\" 를 삭제하시겠습니까?',\"ui.note.permanentDeleteConfirm\":'노트 \"{{title}}\" 를 영구 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다!',\"ui.note.restoreNoteConfirm\":'노트 \"{{title}}\" 를 복구하시겠습니까?',\"ui.note.clearRecycleConfirm\":\"노트 휴지통을 비우시겠습니까? 이 작업은 되돌릴 수 없습니다!\",\"ui.note.noVaultsForNotes\":\"아직 노트 저장소가 없습니다\",\"ui.note.searchPath\":\"경로\",\"ui.note.searchContentMode\":\"내용\",\"ui.note.sortByBy\":\"정렬\",\"ui.note.sortByMtime\":\"수정 시간\",\"ui.note.sortByCtime\":\"생성 시간\",\"ui.note.sortByPath\":\"경로\",\"ui.note.sortAsc\":\"오름차순\",\"ui.note.sortDesc\":\"내림차순\",\"ui.note.viewFlat\":\"플랫 뷰\",\"ui.note.viewFolder\":\"폴더 뷰\",\"ui.note.editorHr\":\"구분선\",\"ui.note.exportPdfPlanned\":\"PDF 내보내기 기능 개발 중...\",\"ui.note.undo\":\"실행 취소\",\"ui.note.redo\":\"다시 실행\",\"ui.note.cut\":\"잘라내기\",\"ui.note.copy\":\"복사\",\"ui.note.paste\":\"붙여넣기\",\"ui.note.selectAll\":\"전체 선택\",\"ui.note.fullscreen\":\"전체 화면\",\"ui.note.exitFullscreen\":\"전체 화면 종료\",\"ui.note.contextMenu\":\"컨텍스트 메뉴\",\"ui.note.loadingEditor\":\"편집기 로딩 중...\",\"ui.note.unsavedContentWithoutTitle\":\"제목이 비어 있어 내용이 저장되지 않았습니다.\",\"ui.note.wikiLinkNotFound\":'노트 \"{{target}}\" 을(를) 찾을 수 없습니다',\"ui.history.title\":\"노트 기록\",\"ui.history.description\":\"노트의 이전 버전을 확인하고 복구하세요\",\"ui.history.version\":\"버전\",\"ui.history.versionLabel\":\"버전\",\"ui.history.time\":\"업데이트 시간\",\"ui.history.action\":\"작업\",\"ui.history.view\":\"보기\",\"ui.history.count\":\"총 {{count}} 개의 기록\",\"ui.history.loading\":\"로딩 중...\",\"ui.history.noHistory\":\"기록이 없습니다\",\"ui.history.clientSource\":\"클라이언트\",\"ui.history.diffDetails\":\"버전 v{{version}} 차이 상세\",\"ui.history.showDiffOnly\":\"차이만 보기\",\"ui.history.showOriginalContent\":\"수정 전 내용\",\"ui.history.diffLegendAdd\":\"추가\",\"ui.history.diffLegendDel\":\"삭제\",\"ui.history.restore\":\"복원\",\"ui.history.restoreToVersion\":\"이 버전으로 복구\",\"ui.history.restoreVersionConfirmTitle\":\"복원 확인\",\"ui.history.restoreVersionConfirmDesc\":\"노트를 버전 v{{version}} 으로 복구하시겠습니까? 현재 내용은 덮어쓰이지만 자동으로 새로운 기록으로 저장됩니다.\",\"ui.history.restoring\":\"복구 중...\",\"ui.file.file\":\"첨부 파일\",\"ui.file.files\":\"첨부 파일 목록\",\"ui.file.noFiles\":\"첨부 파일이 없습니다\",\"ui.file.searchFilePlaceholder\":\"첨부 파일 검색...\",\"ui.file.deleteFileConfirm\":'첨부 파일 \"{{title}}\" 을(를) 삭제하시겠습니까?',\"ui.file.restoreFileConfirm\":'첨부 파일 \"{{title}}\" 을(를) 복구하시겠습니까?',\"ui.file.permanentDeleteConfirm\":'첨부 파일 \"{{title}}\" 을(를) 영구 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다!',\"ui.file.clearRecycleConfirm\":\"첨부 파일 휴지통을 비우시겠습니까? 이 작업은 되돌릴 수 없습니다!\",\"ui.file.attachmentCount\":\"첨부 파일\",\"ui.file.results\":\"개의 첨부 파일\",\"ui.file.searchPlaceholder\":\"첨부 파일 검색...\",\"ui.file.totalSize\":\"총 {{size}}\",\"ui.file.renameFile\":\"첨부 파일 이름 변경\",\"ui.file.renameFilePlaceholder\":\"새 첨부 파일 이름을 입력하세요\",\"ui.file.renameSuccess\":\"첨부 파일 이름 변경 성공\",\"ui.file.size\":\"사용 공간\",\"ui.file.fileDetail\":\"첨부 파일 상세\",\"ui.file.imagePreview\":\"이미지 미리보기\",\"ui.file.audioPreview\":\"오디오 재생\",\"ui.file.videoPreview\":\"비디오 재생\",\"ui.file.pdfPreview\":\"PDF 문서\",\"ui.file.codePreview\":\"스크립트 코드\",\"ui.file.unsupportedPreview\":\"해당 파일 형식은 직접 미리보기를 지원하지 않습니다\",\"ui.file.openInNewWindow\":\"새 창에서 열기\",\"ui.file.browserDownload\":\"브라우저로 다운로드\",\"ui.file.batchRestore\":\"일괄 복원\",\"ui.file.batchPermanentDelete\":\"일괄 영구 삭제\",\"ui.file.selectedCount\":\"{{count}} 개 선택됨\",\"ui.file.batchRestoreConfirm\":\"선택한 {{count}} 개를 복원하시겠습니까?\",\"ui.file.batchPermanentDeleteConfirm\":\"선택한 {{count}} 개를 영구 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다!\",\"ui.file.noVaultsForFiles\":\"아직 노트 저장소가 없습니다\",\"ui.settings.systemConfig\":\"공유 단축 링크\",\"ui.settings.securityConfig\":\"보안 토큰\",\"ui.settings.noteRelatedConfig\":\"노트 첨부 파일\",\"ui.settings.fontConfig\":\"일반\",\"ui.settings.saveSettings\":\"설정 저장\",\"ui.settings.saveSuccess\":\"설정 저장 성공\",\"ui.settings.saveFailed\":\"설정 저장 실패\",\"ui.settings.fontSet\":\"WebGui 폰트 설정\",\"ui.settings.authTokenKey\":\"사용자 서비스 토큰 암호화 난독화 문자\",\"ui.settings.tokenExpiry\":\"사용자 서비스 토큰 만료 시간\",\"ui.settings.shareTokenKey\":\"공유 토큰 암호화 난독화 문자\",\"ui.settings.shareTokenExpiry\":\"공유 토큰 만료 시간\",\"ui.settings.registerIsEnable\":\"등록 허용\",\"ui.settings.fileChunkSize\":\"첨부 파일 업로드 청크 크기\",\"ui.settings.softDeleteRetentionTime\":\"휴지통 (소프트 삭제) 보관 시간\",\"ui.settings.uploadSessionTimeout\":\"첨부 파일 업로드 세션 시간 초과\",\"ui.settings.historyKeepVersions\":\"노트 기록 보관 버전 수\",\"ui.settings.historySaveDelay\":\"노트 기록 저장 지연 시간\",\"ui.settings.historySaveDelayFormatError\":\"기록 저장 지연 시간 형식이 유효하지 않습니다\",\"ui.settings.adminUid\":\"시스템 설정 액세스 제한 (사용자 UID)\",\"ui.settings.adminUidDesc\":\"관리 권한을 가진 UID 를 지정합니다. 0 은 모든 로그인 사용자가 관리할 수 있음을 의미하며, 기본값: 0\",\"ui.settings.onlyAdminAccess\":\"관리자만 이 페이지에 액세스할 수 있습니다\",\"ui.settings.fontSetDesc\":\"WebGui 인터페이스 폰트를 설정합니다. 비워 두면 시스템 기본 폰트를 사용합니다.\\n<u>원격 CSS 폰트 스타일시트</u> 또는 <u>미리 정의된 CSS 스타일시트 키워드</u> 또는 <u>폰트 네트워크 주소</u> 및 <u>로컬 폰트</u>(storage/user_static/ 디렉토리에 배치) 를 지원합니다. 기본값: 비어 있음.\\n\\n예시: \\n&nbsp;&nbsp;- 원격 CSS 폰트 스타일시트: <b>https://fonts.googleapis.com/css2?family=Noto+Sans+SC&display=swap</b>\\n&nbsp;&nbsp;- 미리 정의된 CSS 스타일시트 키워드: <b>local</b> 또는 <b>remote</b>\\n&nbsp;&nbsp;- 로컬 미리 정의된 CSS 폰트 스타일시트: <b>/static/fonts/font.css</b>\\n&nbsp;&nbsp;- 폰트 네트워크 주소: <b>https://ik.imagekit.io/name/your-font.woff2</b>\\n&nbsp;&nbsp;- 로컬 폰트: <b>/user_static/your-font.woff2</b>, 폰트 파일 <u>your-font.woff2</u> 를 storage/user_static/ 디렉토리에 배치해야 합니다\",\"ui.settings.authTokenKeyDesc\":\"이 설정 항목은 모든 사용자의 서비스 토큰 생성 과정에 참여합니다. 처음 배포한 후 서버 보안 강화를 위해 <u><b>이 항목을 수정하는 것을 강력히 권장합니다</b></u>. 수정하면 기존 토큰이 즉시 무효화되며 WebGui 에 다시 로그인해야 합니다.\",\"ui.settings.tokenExpiryDesc\":\"사용자 서비스 토큰의 만료 시간입니다. 지원 형식: 7d(일), 24h(시간), 30m(분). 기본값: <b>365d</b>\",\"ui.settings.shareTokenKeyDesc\":\"공유 토큰 생성에 사용되는 암호화 난독화 키입니다. 수정하면 기존 공유 링크가 무효화됩니다. 기본값: <b>fns</b>\",\"ui.settings.shareTokenExpiryDesc\":\"공유 토큰의 만료 시간입니다. 지원 형식: 7d(일), 24h(시간), 30m(분). 기본값: <b>30d</b>\",\"ui.settings.registerIsEnableDesc\":\"새 사용자가 이 서비스에서 계정을 생성할 수 있는지 여부입니다. 기본값: <b>활성화</b>\",\"ui.settings.fileChunkSizeDesc\":\"업로드 시 청크 크기 (예: 1MB, 512KB) 입니다. 512KB-2MB 를 권장합니다. 기본값: <b>512KB</b>\",\"ui.settings.softDeleteRetentionTimeDesc\":\"노트 및 첨부 파일 삭제 후 보관 기간 (예: 30d, 24h) 입니다. 0 은 자동 정리 안 함을 의미합니다. 이 설정이 너무 짧으면 오프라인 장치가 삭제 작업을 동기화하지 못할 수 있으므로 신중하게 설정하세요. 기본값: 7d\",\"ui.settings.uploadSessionTimeoutDesc\":\"파일 청크 업로드 세션의 유효 기간 (예: 1h, 30m) 입니다. 기본값: <b>1d</b>\",\"ui.settings.historyKeepVersionsDesc\":\"각 노트에 보관할 기록 버전 수입니다. 이 수를 초과하면 가장 오래된 버전이 자동으로 삭제됩니다. 최소값: 100, 기본값: <b>100</b>\",\"ui.settings.historySaveDelayDesc\":\"기록 저장 지연 시간으로, 빈번한 편집으로 인해 과도한 기록 버전이 생성되는 것을 방지합니다 (예: 10s, 1m). 기본값: <b>10s</b>\",\"ui.settings.toastPosition\":\"알림 표시 위치\",\"ui.settings.position.top-left\":\"왼쪽 상단\",\"ui.settings.position.top-center\":\"상단 중앙\",\"ui.settings.position.top-right\":\"오른쪽 상단\",\"ui.settings.position.bottom-left\":\"왼쪽 하단\",\"ui.settings.position.bottom-center\":\"하단 중앙\",\"ui.settings.position.bottom-right\":\"오른쪽 하단\",\"ui.settings.colorScheme\":\"색상 스키마\",\"ui.settings.colorSchemeSwitched\":\"{{scheme}} 색상으로 변경됨\",\"ui.settings.colorScheme.default\":\"표준\",\"ui.settings.colorScheme.green\":\"초록색\",\"ui.settings.colorScheme.blue\":\"파란색\",\"ui.settings.colorScheme.skyBlue\":\"하늘색\",\"ui.settings.colorScheme.purple\":\"보라색\",\"ui.settings.colorScheme.orange\":\"주황색\",\"ui.settings.colorScheme.rose\":\"장미\",\"ui.settings.colorScheme.teal\":\"청록색\",\"ui.settings.themeAuto\":\"자동 (18:00-06:00 다크 모드)\",\"ui.settings.themeLight\":\"라이트 모드\",\"ui.settings.themeDark\":\"다크 모드\",\"ui.settings.cloudflaredTestRequired\":\"먼저 '터널 프로그램 다운로드' 버튼을 클릭하세요.\",\"ui.settings.downloadSuccess\":\"다운로드 성공\",\"ui.settings.downloadFailed\":\"다운로드 실패\",\"ui.settings.tunnelGatewayConfig\":\"중계 게이트웨이\",\"ui.settings.tunnelGatewayDesc\":\"<b>FNS</b>에 통합된 내부 네트워크 관통 서비스로, 로컬 <b>FNS</b> 서비스를公网에서 안전하게 직접 액세스할 수 있게 합니다. 복잡한 HTTPS 및 WebSocket 프록시 설정 없이도 안전한 액세스 서비스를 이용하실 수 있습니다.\\nNgrok 또는 Cloudflare 터널을 통한 접속을 지원합니다.\",\"ui.settings.ngrokDesc\":'Ngrok 내부 네트워크 관통 터널을 사용하여 로컬 서비스를公网에 안전하게 노출합니다.\\n공식 신청 링크: <a href=\"https://dashboard.ngrok.com/get-started/setup\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary hover:underline\">https://dashboard.ngrok.com/get-started/setup</a>\\n <b>주의</b>: Ngrok 무료 계정은 다양한 제한이 있습니다. 자세한 내용은 <a href=\"https://ngrok.com/docs/pricing-limits/free-plan-limits\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary hover:underline\">https://ngrok.com/docs/pricing-limits/free-plan-limits</a>를 참조하세요.',\"ui.settings.customDomain\":\"사용자 정의 도메인\",\"ui.settings.customDomainDesc\":\"선택 사항, ngrok 유료 계정 필요\",\"ui.settings.saveNgrok\":\"설정 저장\",\"ui.settings.cloudflareDesc\":'Cloudflare 제로 트러스트 네트워크를 기반으로 한 역터널로, 더 높은 보안성과 안정성을 제공합니다.\\n공식 신청 링크: <a href=\"https://one.dash.cloudflare.com\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary hover:underline\">https://one.dash.cloudflare.com</a>\\n설정을 저장하기 전에 먼저 <b>터널 프로그램 다운로드</b>를 수행해야 합니다.',\"ui.settings.enableLog\":\"로그 기록\",\"ui.settings.cloudflareLogDesc\":\"로그는 storage/logs/cloudflared_tunnel.log에 기록됩니다.\",\"ui.settings.saveCloudflare\":\"설정 저장\",\"ui.settings.downloadCloudflared\":\"터널 프로그램 다운로드\",\"ui.settings.pullSource\":\"버전 확인 소스\",\"ui.settings.pullSourceDesc\":\"버전 확인 및 업데이트 시 사용할 소스 저장소를 선택합니다.\",\"ui.settings.pullSource.auto\":\"자동 감지\",\"ui.settings.pullSource.github\":\"github.com\",\"ui.settings.pullSource.cnb\":\"텐센트 cnb.cool\",\"ui.settings.userDatabaseConfig\":\"데이터베이스 강화\",\"ui.settings.userDatabaseDesc\":\"기본 <b>SQLite</b>(유지보수 불필요, 동시 읽기/단일 쓰기 지원)로, 개인 및 소규모 배포에 적합하며 개인 사용자의 대부분 요구사항을 충족합니다.<br/><div class='mt-2 pt-2 border-t border-border/50 opacity-90'><div class='mb-2 text-amber-500 font-medium'>전문 데이터베이스 (PostgreSQL / MySQL) 사용 권장 시나리오:</div><ul class='list-disc list-inside space-y-1 mb-1'><li>다중 사용자, 여러 노트 라이브러리 및 대규모 규모</li><li>노트 수 또는 첨부 파일 크기가 매우 큼</li><li>동시성 관련 오류가 자주 발생함</li></ul><div>전문 데이터베이스는 동시성 및 대규모 데이터 처리에서 더 신뢰할 수 있지만, 하드웨어 요구사항과 운영 비용을 증가시킵니다 (운영 능력 필요).</div><div class='mt-2 pt-2 border-t border-border/50 text-xs text-rose-500 font-medium italic'>주의: 데이터베이스를 변경한 후 기존 데이터는 새 데이터베이스로 마이그레이션되지 않습니다. 노트 라이브러리를 다시 강제 동기화해야 합니다. 데이터베이스 유형을 변경하려면 먼저 연결 테스트를 통과해야 합니다.</div></div>\",\"ui.settings.databaseType\":\"데이터베이스 유형\",\"ui.settings.databaseType.sqlite\":\"SQLite (임베디드)\",\"ui.settings.databaseType.mysql\":\"MySQL\",\"ui.settings.databaseType.postgres\":\"PostgreSQL\",\"ui.settings.databaseHost\":\"데이터베이스 주소 (Host)\",\"ui.settings.databasePort\":\"포트 (Port)\",\"ui.settings.databaseUser\":\"사용자 이름 (User)\",\"ui.settings.databasePassword\":\"비밀번호 (Password)\",\"ui.settings.databaseName\":\"데이터베이스 이름 (Database)\",\"ui.settings.databaseMaxIdleConns\":\"최대 유휴 연결 수 (MaxIdle)\",\"ui.settings.databaseMaxOpenConns\":\"최대 열린 연결 수 (MaxOpen)\",\"ui.settings.databaseConnMaxLifetime\":\"연결 최대 수명 (MaxLifetime)\",\"ui.settings.databaseConnMaxIdleTime\":\"연결 최대 유휴 시간 (MaxIdleTime)\",\"ui.settings.databaseMaxWriteConcurrency\":\"최대 쓰기 동시성 (MaxWriteConcurrency)\",\"ui.settings.databaseSchema\":\"데이터베이스 스키마 (Postgres)\",\"ui.settings.databaseSslMode\":\"SSL 모드 (SSL Mode)\",\"ui.settings.mysqlPermissionWarning\":\"주의: MySQL을 사용할 경우 제공된 계정은 사용자 정보 격리를 위해 데이터베이스 생성 (CREATE DATABASE) 권한을 가져야 합니다.\",\"ui.settings.postgresPermissionWarning\":\"주의: PostgreSQL을 사용할 경우 제공된 계정은 사용자 정보 격리를 위해 데이터베이스 생성 (CREATE DATABASE) 권한을 가져야 합니다.\",\"ui.settings.testConnection\":\"연결 테스트\",\"ui.settings.testSuccess\":\"연결 테스트 성공\",\"ui.settings.testFailed\":\"연결 테스트 실패\",\"ui.settings.testRequiredBeforeSave\":\"저장하기 전에 먼저 연결 테스트를 수행하고 성공했는지 확인하세요.\",\"ui.settingsBrowser.title\":\"노트북 설정 파일\",\"ui.settingsBrowser.description\":\"노트북 설정 정보 (예: 노트북 설정, 플러그인, 테마 등) 관리\",\"ui.settingsBrowser.add\":\"새 설정 추가\",\"ui.settingsBrowser.edit\":\"설정 편집\",\"ui.settingsBrowser.key\":\"경로\",\"ui.settingsBrowser.value\":\"크기\",\"ui.settingsBrowser.content\":\"내용\",\"ui.settingsBrowser.keyRequired\":\"경로는 비워둘 수 없습니다\",\"ui.settingsBrowser.confirmDelete\":'설정 항목 \"{{key}}\"를 삭제하시겠습니까?',\"ui.settingsBrowser.rename\":\"경로 이름 변경\",\"ui.settingsBrowser.newKey\":\"새 경로\",\"ui.settingsBrowser.renameSuccess\":\"설정 항목 이름 변경 성공\",\"ui.settingsBrowser.noSettings\":\"설정 항목이 없습니다.\",\"ui.obsidian.authTokenConfig\":\"인증 설정\",\"ui.obsidian.authTokenConfigTo\":\"Obsidian으로 원클릭 인증\",\"ui.obsidian.oneClickImport\":\"Obsidian으로 원클릭 인증\",\"ui.obsidian.copyConfigSuccess\":\"인증 설정이 복사되었습니다. Obsidian 플러그인으로 돌아가서 설정을 붙여넣으세요.\",\"ui.obsidian.copyConfigError\":\"HTTPS 페이지가 아니므로 클립보드 기능을 사용할 수 없습니다. 인증 설정을 수동으로 복사하세요.\",\"ui.system.serviceInfo\":\"서비스 정보\",\"ui.system.versionInfo\":\"버전 정보\",\"ui.system.repo\":\"프로젝트 저장소\",\"ui.system.githubRepo\":\"GitHub 저장소\",\"ui.system.cnbMirror\":\"CNB 미러 저장소\",\"ui.system.currentVersion\":\"현재 버전\",\"ui.system.checkUpdate\":\"업데이트 확인\",\"ui.system.checkNow\":\"지금 확인\",\"ui.system.checking\":\"확인 중...\",\"ui.system.newVersionAvailable\":\"새 버전 발견\",\"ui.system.alreadyLatest\":\"최신 버전입니다\",\"ui.system.viewRelease\":\"릴리스 페이지 보기\",\"ui.system.upgradeNow\":\"지금 업그레이드\",\"ui.system.upgrading\":\"업그레이드 중...\",\"ui.system.upgradeSuccess\":\"업그레이드 트리거 성공, 페이지가 곧 새로고침됩니다\",\"ui.system.upgradeFailed\":\"업그레이드 트리거 실패\",\"ui.system.upgradeRefreshTimeout\":\"업그레이드가 완료되었으나 서비스가 아직 복구되지 않았습니다. 페이지를 수동으로 새로고침하세요.\",\"ui.system.viewChangelog\":\"변경 로그 보기\",\"ui.system.getVersionError\":\"버전 정보 가져오기 실패\",\"ui.system.getWebGuiConfigError\":\"WebGui 설정 가져오기 실패:\",\"ui.system.restartService\":\"서비스 재시작\",\"ui.system.restartServiceConfirm\":\"서비스를 즉시 재시작하시겠습니까? 재시작 중 연결이 끊어집니다.\",\"ui.system.manualGC\":\"메모리 회수\",\"ui.system.manualGCConfirm\":\"메모리 회수 (GC) 를 즉시 실행하시겠습니까?\",\"ui.system.manualGCSuccess\":\"수동 메모리 회수 (GC) 트리거 성공\",\"ui.system.serverSystemInfo\":\"서버 정보\",\"ui.system.modelName\":\"프로세서 모델\",\"ui.system.hostInfo\":\"시스템 정보\",\"ui.system.systemTime\":\"시스템 시간\",\"ui.system.runtimeInfo\":\"서비스 정보\",\"ui.system.startTime\":\"시작 시간\",\"ui.system.serviceUptime\":\"가동 시간\",\"ui.system.physicalCores\":\"코어 (논리/물리)\",\"ui.system.cpuLoad\":\"평균 부하 (1/5/15)\",\"ui.system.totalMemory\":\"총 메모리\",\"ui.system.usedMemory\":\"사용된 메모리\",\"ui.system.memoryUsage\":\"메모리 사용률\",\"ui.system.os\":\"운영 체제\",\"ui.system.kernelVersion\":\"커널 버전\",\"ui.system.uptime\":\"가동 시간\",\"ui.system.goVersion\":\"런타임 버전\",\"ui.system.goroutines\":\"고루틴 수\",\"ui.system.heapMemory\":\"힙 메모리 / 총 사용량\",\"ui.system.numGc\":\"GC 횟수\",\"ui.system.createdAt\":\"생성 시간\",\"ui.system.updatedAt\":\"업데이트 시간\",\"ui.system.websocketClients\":\"온라인 클라이언트\",\"ui.system.wsNickname\":\"닉네임\",\"ui.system.wsClientName\":\"클라이언트\",\"ui.system.wsClientType\":\"유형\",\"ui.system.wsRemoteAddr\":\"주소\",\"ui.system.wsStartTime\":\"연결 시간\",\"ui.system.wsTraceId\":\"추적 ID\",\"ui.system.wsNoClients\":\"온라인 클라이언트가 없습니다\",\"ui.storage.management\":\"저장소 설정\",\"ui.storage.add\":\"저장소 추가\",\"ui.storage.edit\":\"저장소 편집\",\"ui.storage.noStorage\":\"저장소 설정이 없습니다\",\"ui.storage.type\":\"유형\",\"ui.storage.storageType.oss\":\"알리바바 OSS\",\"ui.storage.storageType.s3\":\"AWS S3\",\"ui.storage.storageType.r2\":\"Cloudflare R2\",\"ui.storage.storageType.minio\":\"MinIO\",\"ui.storage.storageType.localfs\":\"로컬 저장소\",\"ui.storage.storageType.webdav\":\"WebDAV\",\"ui.storage.selectType\":\"저장소 유형 선택\",\"ui.storage.endpoint\":\"엔드포인트 ( Endpoint )\",\"ui.storage.region\":\"리전 ( S3 Region )\",\"ui.storage.accountId\":\"계정 ID ( R2 Account ID )\",\"ui.storage.bucketName\":\"버킷\",\"ui.storage.accessKeyId\":\"키 ID ( AccessKey ID )\",\"ui.storage.accessKeySecret\":\"비밀 키 ( AccessKey Secret )\",\"ui.storage.webdavUrl\":\"WebDAV 주소\",\"ui.storage.webdavUser\":\"사용자 이름\",\"ui.storage.webdavPassword\":\"비밀번호\",\"ui.storage.customPath\":\"사용자 정의 경로\",\"ui.storage.accessUrlPrefix\":\"접근 URL 접두사\",\"ui.storage.placeholder.endpoint.oss\":\"oss-cn-hangzhou.aliyuncs.com\",\"ui.storage.placeholder.endpoint.minio\":\"http://192.168.1.100:9000\",\"ui.storage.placeholder.region\":\"us-east-1\",\"ui.storage.placeholder.accountId\":\"your-account-id\",\"ui.storage.placeholder.bucketName\":\"my-bucket\",\"ui.storage.placeholder.accessKeyId\":\"\",\"ui.storage.placeholder.accessKeySecret\":\"\",\"ui.storage.placeholder.webdavUrl\":\"http://192.168.1.100:5244/dav\",\"ui.storage.placeholder.webdavUser\":\"admin\",\"ui.storage.placeholder.webdavPassword\":\"\",\"ui.storage.placeholder.customPath\":\"data/obsidian\",\"ui.storage.placeholder.accessUrlPrefix\":\"http://192.168.1.100:5244\",\"ui.storage.help.endpoint.oss\":\"Bucket 이름이 없는 Aliyun OSS Endpoint 주소\",\"ui.storage.help.endpoint.minio\":\"MinIO 서비스 주소 (프로토콜 접두사와 포트 포함)\",\"ui.storage.help.region\":\"S3 버킷이 위치한 지역\",\"ui.storage.help.accountId\":\"Cloudflare 계정 ID (콘솔 오른쪽 사이드바에서 찾을 수 있음)\",\"ui.storage.help.webdavUrl\":\"프로토콜 접두사 (http:// 또는 https://) 와 전체 경로를 포함해야 합니다\",\"ui.storage.help.webdavUser\":\"WebDAV 서비스 로그인 사용자명\",\"ui.storage.help.webdavPassword\":\"WebDAV 서비스 로그인 비밀번호\",\"ui.storage.help.customPath\":\"파일이 저장될 하위 디렉토리 경로 (앞뒤 슬래시 제외)\",\"ui.storage.help.accessUrlPrefix\":\"파일 액세스 링크 생성에 사용되는 접두사 주소 (표시용)\",\"ui.storage.confirmDelete\":\"이 저장소 구성을 삭제하시겠습니까?\",\"ui.storage.validate.title\":\"연결 테스트\",\"ui.storage.validate.loading\":\"테스트 중...\",\"ui.backup.management\":\"작업 관리\",\"ui.backup.add\":\"작업 추가\",\"ui.backup.edit\":\"작업 편집\",\"ui.backup.noBackup\":\"백업 및 동기화 작업이 없습니다\",\"ui.backup.selectType\":\"백업 유형을 선택하세요\",\"ui.backup.confirmDelete\":\"이 백업 작업을 삭제하시겠습니까?\",\"ui.backup.vault\":\"노트 라이브러리\",\"ui.backup.selectVault\":\"노트 라이브러리 선택\",\"ui.backup.type\":\"백업 유형\",\"ui.backup.backupType.full\":\"전체 백업\",\"ui.backup.backupType.incremental\":\"증분 백업\",\"ui.backup.backupType.sync\":\"미러 동기화\",\"ui.backup.cronStrategy\":\"정기 전략\",\"ui.backup.strategy.daily\":\"매일 백업 (00:00)\",\"ui.backup.strategy.weekly\":\"매주 백업 (월요일 00:00)\",\"ui.backup.strategy.monthly\":\"매월 백업 (1 일 00:00)\",\"ui.backup.strategy.custom\":\"사용자 정의 Cron\",\"ui.backup.cronExpression\":\"Cron 식\",\"ui.backup.retentionDays\":\"보관 일수\",\"ui.backup.fileCountUnit\":\"개 파일\",\"ui.backup.retentionDays.sync\":\"이력 보관 일수 (0: 이력 삭제 안 함, -1: 이력 보관 안 함)\",\"ui.backup.retentionDays.backup\":\"백업 패키지 보관 일수 (0: 백업 삭제 안 함, -1: 과거 백업 보관 안 함)\",\"ui.backup.includeVaultName.label\":\"노트 라이브러리 이름 포함\",\"ui.backup.includeVaultName.tooltip\":\"끔: {customPath}/notes/xxx.md\\n켬: {customPath}/{vaultName}/notes/xxx.md\",\"ui.backup.noAvailableStorage\":\"사용 가능한 저장소 구성이 없습니다\",\"ui.backup.addStorageTip\":\"저장소 관리에서 저장소 백엔드를 추가하고 활성화하세요\",\"ui.backup.storages\":\"저장소 선택\",\"ui.backup.validation.vaultRequired\":\"노트 라이브러리를 선택하세요\",\"ui.backup.validation.typeRequired\":\"백업 유형을 선택하세요\",\"ui.backup.validation.strategyRequired\":\"정기 전략을 선택하세요\",\"ui.backup.validation.storageRequired\":\"최소 하나의 저장소를 선택하세요\",\"ui.backup.validation.retentionDaysMin\":\"보관 일수는 ≥ -1 이어야 합니다\",\"ui.backup.validation.cronExpressionRequired\":\"사용자 정의 전략에서 Cron 식은 비워둘 수 없습니다\",\"ui.backup.lastRunTime\":\"마지막 실행\",\"ui.backup.nextRunTime\":\"다음 실행\",\"ui.backup.executeNow\":\"지금 실행\",\"ui.backup.executeSuccess\":\"수동 백업 트리거 성공\",\"ui.backup.status.0\":\"대기 중\",\"ui.backup.status.1\":\"진행 중\",\"ui.backup.status.2\":\"성공\",\"ui.backup.status.3\":\"실패\",\"ui.backup.status.4\":\"취소\",\"ui.backup.status.5\":\"변경 없음\",\"ui.backup.history.title\":\"작업 기록\",\"ui.backup.history.startTime\":\"작업 실행 시간\",\"ui.backup.history.storage\":\"저장소\",\"ui.backup.history.status\":\"상태\",\"ui.backup.history.backupStats\":\"백업 통계\",\"ui.backup.history.syncStats\":\"동기화 통계\",\"ui.backup.history.backupFile\":\"백업 파일\",\"ui.backup.history.message\":\"메시지\",\"ui.backup.history.copyError\":\"오류 메시지 복사\",\"ui.backup.history.noData\":\"작업 기록이 없습니다.\",\"ui.git.title\":\"Git 자동화 관리\",\"ui.git.config\":\"Git 저장소 설정\",\"ui.git.repoUrl\":\"Git 저장소 주소 (ssh 주소 지원 안 함)\",\"ui.git.status\":\"Git 상태 개요\",\"ui.git.history\":\"자동 커밋 기록\",\"ui.git.comingSoon\":\"Git 자동화 기능 개발 중...\",\"ui.git.configDesc\":\"Git 저장소 주소, 브랜치 및 인증 정보를 설정합니다.\",\"ui.git.statusDesc\":\"현재 저장소의 커밋 상태와 동기화 진행 상황을 실시간으로 모니터링합니다.\",\"ui.git.historyDesc\":\"최근 자동 커밋 기록을 확인합니다.\",\"ui.git.retentionDays\":\"기록 보관 일수\",\"ui.git.retentionDaysDesc\":\"0: 기록 정리 안 함, -1: 기록 보관 안 함\",\"ui.git.addConfig\":\"Git 동기화 설정 추가\",\"ui.git.editConfig\":\"Git 동기화 설정 편집\",\"ui.git.loading\":\"설정 로딩 중...\",\"ui.git.noConfig\":\"Git 동기화 설정이 없습니다.\",\"ui.git.addFirst\":\"첫 번째 저장소 추가하기\",\"ui.git.lastCommit\":\"마지막 커밋\",\"ui.git.checkTime\":\"마지막 확인\",\"ui.git.neverRun\":\"미执행\",\"ui.git.execute.title\":\"즉시 동기화 실행\",\"ui.git.clean.title\":\"작업 영역 정리\",\"ui.git.clean.confirm\":\"이 Git 작업 영역을 정리하시겠습니까? 로컬 클론이 삭제되고 다시 초기화됩니다.\",\"ui.git.delete.confirm\":\"이 Git 설정을 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.\",\"ui.git.form.branch\":\"브랜치 이름\",\"ui.git.form.delay\":\"자동 동기화 지연 시간 (초)\",\"ui.git.history.title\":\"커밋 기록\",\"ui.git.history.configId\":\"설정 ID\",\"ui.git.history.startTime\":\"커밋 시간\",\"ui.git.history.endTime\":\"종료 시간\",\"ui.git.history.status\":\"상태\",\"ui.git.history.message\":\"메시지\",\"ui.git.history.noData\":\"커밋 기록이 없습니다.\",\"ui.git.history.duration\":\"커밋 소요 시간\",\"ui.git.history.vault\":\"노트북\",\"ui.git.status.0\":\"대기 중\",\"ui.git.status.1\":\"확인 중\",\"ui.git.status.2\":\"성공\",\"ui.git.status.3\":\"실패\",\"ui.git.status.4\":\"중지됨\",\"ui.git.validate.title\":\"연결 확인\",\"ui.git.validate.loading\":\"확인 중...\",\"ui.validation.git.vaultRequired\":\"노트북을 선택하세요\",\"ui.validation.git.repoUrlRequired\":\"저장소 주소를 입력하세요\",\"ui.validation.git.repoUrlInvalid\":\"유효한 URL 을 입력하세요\",\"ui.validation.git.branchRequired\":\"브랜치 이름을 입력하세요\",\"ui.validation.git.delayMin\":\"지연 시간은 음수일 수 없습니다\",\"ui.validation.git.retentionDaysMin\":\"보관 일수는 -1 이상이어야 합니다\",\"ui.validation.storage.typeRequired\":\"저장소 유형을 선택하세요\",\"ui.validation.storage.accessUrlPrefixRequired\":\"접근 주소 접두사는 비워둘 수 없습니다\",\"ui.validation.vault.nameRequired\":\"저장소 이름은 비워둘 수 없습니다\",\"api.git.list.error\":\"Git 설정 목록 가져오기 실패\",\"api.git.save.success\":\"설정 저장 성공\",\"api.git.save.error\":\"설정 저장 실패\",\"api.git.delete.success\":\"설정 삭제 성공\",\"api.git.delete.error\":\"설정 삭제 실패\",\"api.git.execute.success\":\"동기화 작업이 실행되었습니다\",\"api.git.execute.error\":\"동기화 실행 실패\",\"api.git.clean.success\":\"작업 영역 정리 성공\",\"api.git.clean.error\":\"작업 영역 정리 실패\",\"api.git.history.error\":\"기록 가져오기 실패\",\"api.git.validate.success\":\"Git 저장소 연결 테스트 성공\",\"api.git.validate.error\":\"Git 저장소 연결 테스트 실패\",\"api.backup.configList.error\":\"백업 설정 목록 가져오기 실패\",\"api.backup.delete.success\":\"삭제 성공\",\"api.backup.delete.error\":\"백업 설정 삭제 실패\",\"api.backup.save.success\":\"저장 성공\",\"api.backup.save.error\":\"백업 설정 저장 실패\",\"api.backup.execute.success\":\"수동 실행 성공\",\"api.backup.execute.error\":\"백업 실행 실패\",\"api.backup.history.error\":\"백업 내역 조회 실패\",\"api.system.restart.success\":\"서비스 재시작이 트리거되었습니다\",\"api.system.restart.error\":\"서비스 재시작 요청 실패\",\"api.system.gc.success\":\"메모리 회수가 트리거되었습니다\",\"api.system.gc.error\":\"메모리 회수 요청 실패\",\"api.storage.list.error\":\"저장소 설정 목록 조회 실패\",\"api.storage.delete.success\":\"삭제 성공\",\"api.storage.delete.error\":\"저장소 설정 삭제 실패\",\"api.storage.save.error\":\"저장소 설정 저장 실패\",\"api.storage.types.error\":\"저장소 유형 조회 실패\",\"api.storage.validate.success\":\"저장소 연결 테스트 성공\",\"api.storage.validate.error\":\"저장소 연결 테스트 실패\",\"error.storage.webdav.unauthorized\":\"WebDAV 인증 실패, 사용자 이름과 비밀번호를 확인하세요\",\"error.storage.webdav.forbidden\":\"WebDAV 권한이 부족합니다. 계정 쓰기 권한을 확인하세요\",\"error.storage.webdav.notFound\":\"WebDAV 경로가 존재하지 않습니다. 사용자 정의 경로 설정을 확인하세요\",\"error.storage.webdav.methodNotAllowed\":\"WebDAV 메서드가 허용되지 않았습니다. 서버에서 WebDAV 가 활성화되었는지 확인하세요\",\"error.storage.webdav.unreachable\":\"WebDAV 주소에 접근할 수 없습니다. 저장소 설정의 주소를 확인하세요\",\"error.storage.webdav.connectionRefused\":\"WebDAV 연결이 거절되었습니다. 서비스가 실행 중인지 확인하세요\",\"error.storage.webdav.timeout\":\"WebDAV 연결 시간 초과, 네트워크 또는 서비스 상태를 확인하세요\",\"error.storage.webdav.generic\":\"WebDAV 작업 실패, 저장소 설정을 확인하세요\",\"error.storage.s3.noSuchBucket\":\"S3 저장소가 존재하지 않습니다. 버킷 이름 설정을 확인하세요\",\"error.storage.s3.accessDenied\":\"S3 액세스가 거절되었습니다. Access Key 권한을 확인하세요\",\"error.storage.s3.unreachable\":\"S3 엔드포인트에 접근할 수 없습니다. Endpoint 주소를 확인하세요\",\"error.storage.s3.timeout\":\"S3 연결 시간 초과, 네트워크 또는 엔드포인트 설정을 확인하세요\",\"error.storage.oss.noSuchBucket\":\"OSS 저장소가 존재하지 않습니다. Bucket 이름을 확인하세요\",\"error.storage.oss.accessDenied\":\"OSS 액세스가 거절되었습니다. AccessKey 권한을 확인하세요\",\"error.storage.oss.unreachable\":\"OSS 엔드포인트에 접근할 수 없습니다. Endpoint 주소를 확인하세요\",\"error.storage.local.noPermission\":\"로컬 저장소에 쓰기 권한이 없습니다\",\"error.storage.local.createDirFailed\":\"로컬 저장소 디렉토리 생성 실패\",\"error.storage.local.permissionDenied\":\"로컬 저장소 권한이 거절되었습니다\",\"error.backup.partialFailure\":\"일부 파일 동기화 실패, 자세한 오류 정보를 확인하세요\",\"error.backup.uploadFailed\":\"백업 파일 업로드 실패\",\"error.backup.openFileFailed\":\"백업 파일 열기 실패\",\"error.backup.vaultNotExist\":\"노트 보관함이 존재하지 않습니다. 설정을 확인하세요\",\"error.network.unreachable\":\"대상 주소에 접근할 수 없습니다. 네트워크 설정을 확인하세요\",\"error.network.connectionRefused\":\"연결이 거절되었습니다. 대상 서비스가 실행 중인지 확인하세요\",\"error.network.timeout\":\"연결 시간 초과, 네트워크 상태를 확인하세요\",\"ui.support.title\":\"이 프로젝트 지원하기\",\"ui.support.supportRequest\":\"이 프로젝트가 도움이 되셨다면, 계속 개발되기를 원하신다면 아래 방법으로 저희를 지원해 주세요. 오픈소스 소프트웨어에 대한 귀하의 지원에 감사드립니다!\",\"ui.support.listTitle\":\"지원 내역\",\"ui.support.noData\":\"기록이 없습니다\",\"ui.support.item\":\"항목\",\"ui.support.amount\":\"금액\",\"ui.support.time\":\"시간\",\"ui.support.message\":\"메시지\",\"ui.support.thanks\":\"Fast Note Sync 를 지원해 주셔서 감사합니다! 지원 목록은 불규칙적으로 업데이트됩니다\",\"ui.support.sortDefault\":\"기본 (금액)\",\"ui.support.sortTime\":\"시간순\",\"ui.support.sort\":\"정렬\",\"ui.support.buyMeACoffee\":\"저자에게 커피 한 잔 사주기\",\"ui.support.wechatReward\":\"위챗 팁 지원\",\"ui.share.invalidLink\":\"유효하지 않은 공유 링크입니다. ID 또는 Token 이 누락되었습니다.\",\"ui.share.errorTitle\":\"공유 오류\",\"ui.share.noteNotFound\":\"공유된 노트를 찾을 수 없습니다.\",\"ui.share.poweredByPrefix\":\"제공: \",\"ui.share.poweredBySuffix\":\" 지원\",\"ui.share.version\":\"버전\",\"ui.share.tabActive\":\"공유 중\",\"ui.share.noShares\":\"공유 기록이 없습니다\",\"ui.share.viewShare\":\"공유 페이지 보기\",\"ui.share.cancelShare\":\"공유 취소\",\"ui.share.cancelConfirm\":\"이 노트의 공유를 취소하시겠습니까? 취소하면 공유 링크가 즉시 무효화됩니다.\",\"ui.share.shareNotFound\":\"공유 기록을 찾을 수 없습니다\",\"ui.share.title\":\"노트 공유\",\"ui.share.checking\":\"공유 상태 확인 중...\",\"ui.share.create\":\"공유 시작\",\"ui.share.creating\":\"시작 중...\",\"ui.share.success\":\"공유 성공\",\"ui.share.shortLinkCreate\":\"짧은 링크 생성\",\"ui.share.link\":\"공유 링크\",\"ui.share.shortLink\":\"짧은 링크\",\"ui.share.copy\":\"링크 복사\",\"ui.share.copySuccess\":\"링크가 클립보드에 복사되었습니다\",\"ui.share.shortLinkCopy\":\"짧은 링크 복사\",\"ui.share.cancelSuccess\":\"공유가 취소되었습니다\",\"ui.share.buttonCreating\":\"시작 중...\",\"ui.share.passwordRequired\":\"비밀번호 필요\",\"ui.share.passwordHint\":\"이 공유는 비밀번호로 보호됩니다. 내용을 보려면 비밀번호를 입력하세요.\",\"ui.share.passwordPlaceholder\":\"비밀번호를 입력하세요...\",\"ui.share.preview\":\"내용 미리보기\",\"ui.syncLog.title\":\"노트북 업데이트 로그\",\"ui.syncLog.vault\":\"노트북\",\"ui.syncLog.type\":\"유형\",\"ui.syncLog.action\":\"동작\",\"ui.syncLog.path\":\"파일 경로\",\"ui.syncLog.size\":\"크기\",\"ui.syncLog.client\":\"클라이언트\",\"ui.syncLog.status\":\"상태\",\"ui.syncLog.message\":\"상세 정보\",\"ui.syncLog.time\":\"기록 시간\",\"ui.syncLog.changedFields\":\"변경 내용\",\"ui.syncLog.noLogs\":\"동기화 기록 없음\",\"ui.syncLog.noLogsDescription\":\"업데이트 기록을 찾을 수 없습니다\",\"ui.syncLog.description\":\"모든 노트북의 노트, 첨부 파일, 폴더 및 설정 파일의 동기화 변경 기록 추적\",\"ui.syncLog.allVaults\":\"모든 노트북\",\"ui.syncLog.allTypes\":\"모든 유형\",\"ui.syncLog.allActions\":\"모든 동작\",\"ui.syncLog.resetFilters\":\"필터 초기화\",\"ui.syncLog.statusSuccess\":\"성공\",\"ui.syncLog.statusFailed\":\"실패\",\"ui.syncLog.type.note\":\"노트\",\"ui.syncLog.type.file\":\"첨부 파일\",\"ui.syncLog.type.setting\":\"설정\",\"ui.syncLog.type.folder\":\"폴더\",\"ui.syncLog.action.create\":\"새로 만들기\",\"ui.syncLog.action.modify\":\"수정\",\"ui.syncLog.action.soft_delete\":\"소프트 삭제\",\"ui.syncLog.action.delete\":\"영구 삭제\",\"ui.syncLog.action.rename\":\"이름 변경\",\"ui.syncLog.action.restore\":\"복원\"};export{e as default};\n"
  },
  {
    "path": "frontend/assets/main-BIi-kGYY.js",
    "content": "const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=[\"assets/note-manager-DjJcxkCE.js\",\"assets/font-loader-CIrh3KnA.js\",\"assets/font-loader-B-ynJ_1p.css\",\"assets/note-handle-IK8dQjtF.js\",\"assets/alert-dialog-CfMssux5.js\",\"assets/table-D9wbHMTA.js\",\"assets/checkbox-DhTHgmeh.js\",\"assets/select-CJF_alSt.js\",\"assets/tooltip-Dr-qRlmI.js\",\"assets/badge-C63ATniC.js\",\"assets/history-BseqF3eb.js\",\"assets/canvas-viewer-Cxwbo1vR.js\",\"assets/markdown-editor-CX5kQlgI.js\",\"assets/index-JfsWWBj_.js\",\"assets/zap-CLLhzk_y.js\",\"assets/pencil-DqQhr35g.js\",\"assets/markdown-editor-DMUawZD_.css\",\"assets/format-CdHm7RWL.js\",\"assets/canvas-viewer-Bt8OKmt9.css\",\"assets/copy-CEhXannp.js\",\"assets/eye-DrvrOb4o.js\",\"assets/share-2-BVJjAadJ.js\",\"assets/search-DdihTHF8.js\",\"assets/refresh-cw-BxIJAPy3.js\",\"assets/plus-BBfuNxDX.js\",\"assets/clock-C9LPHszx.js\",\"assets/database-eyf5nvY6.js\",\"assets/trash-2-ad7PiUnC.js\",\"assets/text-cursor-input-Bphfsfyn.js\",\"assets/file-manager-Bz0QGSbU.js\",\"assets/image-BFJJNQpe.js\",\"assets/download-CKtDCbjj.js\",\"assets/system-settings-DSUsRYMo.js\",\"assets/git-branch-B1vNHBXG.js\",\"assets/circle-alert-EFzISefA.js\",\"assets/monitor-BGNS5Y9j.js\",\"assets/server-DzJVVqse.js\",\"assets/github-Bzk-4SPC.js\",\"assets/hard-drive-Dw58lXyp.js\",\"assets/vault-list-BzYzvdPK.js\",\"assets/auth-form-BjZ9qVzL.js\",\"assets/zod-B54Zg8Xp.js\",\"assets/sync-backup-Bp7n2yHp.js\",\"assets/shield-check-CH_gKEpx.js\",\"assets/git-automation-tBJ0Wppw.js\",\"assets/setting-manager-DaP9o-yD.js\",\"assets/file-type-DbD_pFnN.js\",\"assets/sync-log-manager-Zjq-lA99.js\"])))=>i.map(i=>d[i]);\nimport{c as t,r as e,j as n,a as s,b as i,t as o,e as r,u as a,d as l,p as u,f as c,g as h,C as d,O as m,P as p,h as f,i as g,X as y,T as v,D as x,R as w,k as b,l as S,I as T,B as P,m as k,n as j,o as M,q as E,s as A,v as C,w as V,x as D,y as R,z as L,A as N,E as I,F as B,L as F,G as O,S as U,M as z,H as _,J as W,K as $,N as Y,_ as K,Q as X,U as H,V as q,W as G,Y as Z,Z as J}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const Q=t(\"ArchiveX\",[[\"rect\",{width:\"20\",height:\"5\",x:\"2\",y:\"3\",rx:\"1\",key:\"1wp1u1\"}],[\"path\",{d:\"M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8\",key:\"1s80jp\"}],[\"path\",{d:\"m9.5 17 5-5\",key:\"nakeu6\"}],[\"path\",{d:\"m9.5 12 5 5\",key:\"1hccrj\"}]]),tt=t(\"Bell\",[[\"path\",{d:\"M10.268 21a2 2 0 0 0 3.464 0\",key:\"vwvbt9\"}],[\"path\",{d:\"M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326\",key:\"11g9vi\"}]]),et=t(\"ChevronDown\",[[\"path\",{d:\"m6 9 6 6 6-6\",key:\"qrunsl\"}]]),nt=t(\"ChevronLeft\",[[\"path\",{d:\"m15 18-6-6 6-6\",key:\"1wnfg3\"}]]),st=t(\"ChevronUp\",[[\"path\",{d:\"m18 15-6-6-6 6\",key:\"153udz\"}]]),it=t(\"Clipboard\",[[\"rect\",{width:\"8\",height:\"4\",x:\"8\",y:\"2\",rx:\"1\",ry:\"1\",key:\"tgr4d6\"}],[\"path\",{d:\"M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2\",key:\"116196\"}]]),ot=t(\"DatabaseBackup\",[[\"ellipse\",{cx:\"12\",cy:\"5\",rx:\"9\",ry:\"3\",key:\"msslwz\"}],[\"path\",{d:\"M3 12a9 3 0 0 0 5 2.69\",key:\"1ui2ym\"}],[\"path\",{d:\"M21 9.3V5\",key:\"6k6cib\"}],[\"path\",{d:\"M3 5v14a9 3 0 0 0 6.47 2.88\",key:\"i62tjy\"}],[\"path\",{d:\"M12 12v4h4\",key:\"1bxaet\"}],[\"path\",{d:\"M13 20a5 5 0 0 0 9-3 4.5 4.5 0 0 0-4.5-4.5c-1.33 0-2.54.54-3.41 1.41L12 16\",key:\"1f4ei9\"}]]),rt=t(\"ExternalLink\",[[\"path\",{d:\"M15 3h6v6\",key:\"1q9fwt\"}],[\"path\",{d:\"M10 14 21 3\",key:\"gplh6r\"}],[\"path\",{d:\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\",key:\"a6xqqp\"}]]),at=t(\"FileJson\",[[\"path\",{d:\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\",key:\"1rqfz7\"}],[\"path\",{d:\"M14 2v4a2 2 0 0 0 2 2h4\",key:\"tnqrlb\"}],[\"path\",{d:\"M10 12a1 1 0 0 0-1 1v1a1 1 0 0 1-1 1 1 1 0 0 1 1 1v1a1 1 0 0 0 1 1\",key:\"1oajmo\"}],[\"path\",{d:\"M14 18a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1 1 1 0 0 1-1-1v-1a1 1 0 0 0-1-1\",key:\"mpwhp6\"}]]),lt=t(\"GitPullRequestArrow\",[[\"circle\",{cx:\"5\",cy:\"6\",r:\"3\",key:\"1qnov2\"}],[\"path\",{d:\"M5 9v12\",key:\"ih889a\"}],[\"circle\",{cx:\"19\",cy:\"18\",r:\"3\",key:\"1qljk2\"}],[\"path\",{d:\"m15 9-3-3 3-3\",key:\"1lwv8l\"}],[\"path\",{d:\"M12 6h5a2 2 0 0 1 2 2v7\",key:\"1yj91y\"}]]),ut=t(\"Layers\",[[\"path\",{d:\"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z\",key:\"zw3jo\"}],[\"path\",{d:\"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12\",key:\"1wduqc\"}],[\"path\",{d:\"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17\",key:\"kqbvx6\"}]]),ct=t(\"Library\",[[\"path\",{d:\"m16 6 4 14\",key:\"ji33uf\"}],[\"path\",{d:\"M12 6v14\",key:\"1n7gus\"}],[\"path\",{d:\"M8 8v12\",key:\"1gg7y9\"}],[\"path\",{d:\"M4 4v16\",key:\"6qkkli\"}]]),ht=t(\"LogOut\",[[\"path\",{d:\"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4\",key:\"1uf3rs\"}],[\"polyline\",{points:\"16 17 21 12 16 7\",key:\"1gabdz\"}],[\"line\",{x1:\"21\",x2:\"9\",y1:\"12\",y2:\"12\",key:\"1uyos4\"}]]),dt=t(\"Logs\",[[\"path\",{d:\"M13 12h8\",key:\"h98zly\"}],[\"path\",{d:\"M13 18h8\",key:\"oe0vm4\"}],[\"path\",{d:\"M13 6h8\",key:\"15sg57\"}],[\"path\",{d:\"M3 12h1\",key:\"lp3yf2\"}],[\"path\",{d:\"M3 18h1\",key:\"1eiwyy\"}],[\"path\",{d:\"M3 6h1\",key:\"rgxa97\"}],[\"path\",{d:\"M8 12h1\",key:\"1con00\"}],[\"path\",{d:\"M8 18h1\",key:\"13wk12\"}],[\"path\",{d:\"M8 6h1\",key:\"tn6mkg\"}]]),mt=t(\"NotepadText\",[[\"path\",{d:\"M8 2v4\",key:\"1cmpym\"}],[\"path\",{d:\"M12 2v4\",key:\"3427ic\"}],[\"path\",{d:\"M16 2v4\",key:\"4m81vk\"}],[\"rect\",{width:\"16\",height:\"18\",x:\"4\",y:\"4\",rx:\"2\",key:\"1u9h20\"}],[\"path\",{d:\"M8 10h6\",key:\"3oa6kw\"}],[\"path\",{d:\"M8 14h8\",key:\"1fgep2\"}],[\"path\",{d:\"M8 18h5\",key:\"17enja\"}]]),pt=t(\"Paperclip\",[[\"path\",{d:\"M13.234 20.252 21 12.3\",key:\"1cbrk9\"}],[\"path\",{d:\"m16 6-8.414 8.586a2 2 0 0 0 0 2.828 2 2 0 0 0 2.828 0l8.414-8.586a4 4 0 0 0 0-5.656 4 4 0 0 0-5.656 0l-8.415 8.585a6 6 0 1 0 8.486 8.486\",key:\"1pkts6\"}]]),ft=t(\"Settings\",[[\"path\",{d:\"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z\",key:\"1qme2f\"}],[\"circle\",{cx:\"12\",cy:\"12\",r:\"3\",key:\"1v7zrd\"}]]),gt=t(\"Wifi\",[[\"path\",{d:\"M12 20h.01\",key:\"zekei9\"}],[\"path\",{d:\"M2 8.82a15 15 0 0 1 20 0\",key:\"dnpr2z\"}],[\"path\",{d:\"M5 12.859a10 10 0 0 1 14 0\",key:\"1x1e6c\"}],[\"path\",{d:\"M8.5 16.429a5 5 0 0 1 7 0\",key:\"1bycff\"}]]),yt=e.createContext(void 0),vt=({children:t})=>{const[s,i]=e.useState(()=>null!==localStorage.getItem(\"user\"));return n.jsx(yt.Provider,{value:{isLoggedIn:s,login:()=>{i(!0),localStorage.setItem(\"user\",\"true\")},logout:()=>{i(!1),localStorage.removeItem(\"user\"),localStorage.removeItem(\"token\"),localStorage.removeItem(\"username\"),localStorage.removeItem(\"avatar\"),localStorage.removeItem(\"uid\"),localStorage.removeItem(\"email\")}},children:t})};\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */function xt(){const t=localStorage.getItem(\"token\"),n=e.useCallback(async e=>{const n=await fetch(s(r.API_URL+\"/api/vault?limit=100\"),{method:\"GET\",headers:i({token:t})});if(!n.ok)throw new Error(\"Network response was not ok\");const o=await n.json();if(!(o.code<100&&o.code>0)){const t=Array.isArray(o.details)?o.details.join(\", \"):o.details,e=o.message||\"Failed to load vault list\";throw new Error(t?`${e}: ${t}`:e)}e(o.data||[])},[t]),a=e.useCallback(async e=>{const n={id:e},a=await fetch(s(r.API_URL+\"/api/vault\"),{method:\"DELETE\",body:JSON.stringify(n),headers:i({token:t})});if(!a.ok)throw new Error(\"Network response was not ok\");const l=await a.json();l.code>100&&o.error(l.message+\": \"+l.details)},[t]),l=e.useCallback(async(e,n)=>{const a=await fetch(s(r.API_URL+\"/api/vault\"),{method:\"POST\",body:JSON.stringify(e),headers:i({token:t})});if(!a.ok)throw new Error(\"Network response was not ok\");const l=await a.json();l.code<100&&l.code>0?(o.success(l.message),n(l.data)):o.error(l.message+\": \"+l.details)},[t]);return e.useMemo(()=>({handleVaultList:n,handleVaultDelete:a,handleVaultUpdate:l}),[n,a,l])}const wt={currentModule:\"dashboard\",zenMode:!1,userMenuOpen:!1,trashType:\"notes\",versionInfo:null},bt=l()(u(t=>({...wt,setModule:(e,n)=>t(t=>({currentModule:e,trashType:n??t.trashType})),toggleZenMode:()=>t(t=>({zenMode:!t.zenMode})),setZenMode:e=>t({zenMode:e}),toggleUserMenu:()=>t(t=>({userMenuOpen:!t.userMenuOpen})),setUserMenuOpen:e=>t({userMenuOpen:e}),setVersionInfo:e=>t({versionInfo:e}),resetState:()=>{t(wt),localStorage.removeItem(\"app-storage\")}}),{name:\"app-storage\",partialize:t=>({currentModule:t.currentModule,trashType:t.trashType}),onRehydrateStorage:()=>t=>{if(!t)return;\"vaults\"===t.currentModule&&(t.currentModule=\"dashboard\");const e=t.currentModule;\"settings\"===e?t.currentModule=\"config\":\"settings-browser\"===e&&(t.currentModule=\"settings\")}}));function St(t=768){const[n,s]=e.useState(()=>\"undefined\"!=typeof window&&window.innerWidth<t);return e.useEffect(()=>{const e=()=>{s(window.innerWidth<t)};return e(),window.addEventListener(\"resize\",e),()=>{window.removeEventListener(\"resize\",e)}},[t]),n}function Tt({children:t,className:e}){const{currentModule:s}=bt(),i=St(),o=\"dashboard\"===s||\"sync\"===s||\"git\"===s||\"config\"===s;return n.jsx(\"main\",{className:c(\"flex-1 overflow-y-auto\",\"p-2 sm:p-4 md:p-6\",i&&!o&&\"pb-18\",e),children:o?n.jsx(\"div\",{children:t}):n.jsx(\"div\",{className:\"bg-card rounded-lg sm:rounded-xl border border-sidebar-border custom-shadow p-3 sm:p-6 md:p-5 min-h-full\",children:n.jsx(\"div\",{className:\"px-1\",children:t})})})}const Pt=e.createContext({});function kt(t){const n=e.useRef(null);return null===n.current&&(n.current=t()),n.current}const jt=\"undefined\"!=typeof window,Mt=jt?e.useLayoutEffect:e.useEffect,Et=e.createContext(null);function At(t,e){-1===t.indexOf(e)&&t.push(e)}function Ct(t,e){const n=t.indexOf(e);n>-1&&t.splice(n,1)}const Vt=(t,e,n)=>n>e?e:n<t?t:n;const Dt={},Rt=t=>/^-?(?:\\d+(?:\\.\\d+)?|\\.\\d+)$/u.test(t);function Lt(t){return\"object\"==typeof t&&null!==t}const Nt=t=>/^0[^.\\s]+$/u.test(t);function It(t){let e;return()=>(void 0===e&&(e=t()),e)}const Bt=t=>t,Ft=(t,e)=>n=>e(t(n)),Ot=(...t)=>t.reduce(Ft),Ut=(t,e,n)=>{const s=e-t;return 0===s?1:(n-t)/s};class zt{constructor(){this.subscriptions=[]}add(t){return At(this.subscriptions,t),()=>Ct(this.subscriptions,t)}notify(t,e,n){const s=this.subscriptions.length;if(s)if(1===s)this.subscriptions[0](t,e,n);else for(let i=0;i<s;i++){const s=this.subscriptions[i];s&&s(t,e,n)}}getSize(){return this.subscriptions.length}clear(){this.subscriptions.length=0}}const _t=t=>1e3*t,Wt=t=>t/1e3;function $t(t,e){return e?t*(1e3/e):0}const Yt=(t,e,n)=>(((1-3*n+3*e)*t+(3*n-6*e))*t+3*e)*t;function Kt(t,e,n,s){if(t===e&&n===s)return Bt;const i=e=>function(t,e,n,s,i){let o,r,a=0;do{r=e+(n-e)/2,o=Yt(r,s,i)-t,o>0?n=r:e=r}while(Math.abs(o)>1e-7&&++a<12);return r}(e,0,1,t,n);return t=>0===t||1===t?t:Yt(i(t),e,s)}const Xt=t=>e=>e<=.5?t(2*e)/2:(2-t(2*(1-e)))/2,Ht=t=>e=>1-t(1-e),qt=Kt(.33,1.53,.69,.99),Gt=Ht(qt),Zt=Xt(Gt),Jt=t=>(t*=2)<1?.5*Gt(t):.5*(2-Math.pow(2,-10*(t-1))),Qt=t=>1-Math.sin(Math.acos(t)),te=Ht(Qt),ee=Xt(Qt),ne=Kt(.42,0,1,1),se=Kt(0,0,.58,1),ie=Kt(.42,0,.58,1),oe=t=>Array.isArray(t)&&\"number\"==typeof t[0],re={linear:Bt,easeIn:ne,easeInOut:ie,easeOut:se,circIn:Qt,circInOut:ee,circOut:te,backIn:Gt,backInOut:Zt,backOut:qt,anticipate:Jt},ae=t=>{if(oe(t)){t.length;const[e,n,s,i]=t;return Kt(e,n,s,i)}return\"string\"==typeof t?re[t]:t},le=[\"setup\",\"read\",\"resolveKeyframes\",\"preUpdate\",\"update\",\"preRender\",\"render\",\"postRender\"];function ue(t,e){let n=!1,s=!0;const i={delta:0,timestamp:0,isProcessing:!1},o=()=>n=!0,r=le.reduce((t,e)=>(t[e]=function(t){let e=new Set,n=new Set,s=!1,i=!1;const o=new WeakSet;let r={delta:0,timestamp:0,isProcessing:!1};function a(e){o.has(e)&&(l.schedule(e),t()),e(r)}const l={schedule:(t,i=!1,r=!1)=>{const a=r&&s?e:n;return i&&o.add(t),a.has(t)||a.add(t),t},cancel:t=>{n.delete(t),o.delete(t)},process:t=>{r=t,s?i=!0:(s=!0,[e,n]=[n,e],e.forEach(a),e.clear(),s=!1,i&&(i=!1,l.process(t)))}};return l}(o),t),{}),{setup:a,read:l,resolveKeyframes:u,preUpdate:c,update:h,preRender:d,render:m,postRender:p}=r,f=()=>{const o=Dt.useManualTiming?i.timestamp:performance.now();n=!1,Dt.useManualTiming||(i.delta=s?1e3/60:Math.max(Math.min(o-i.timestamp,40),1)),i.timestamp=o,i.isProcessing=!0,a.process(i),l.process(i),u.process(i),c.process(i),h.process(i),d.process(i),m.process(i),p.process(i),i.isProcessing=!1,n&&e&&(s=!1,t(f))};return{schedule:le.reduce((e,o)=>{const a=r[o];return e[o]=(e,o=!1,r=!1)=>(n||(n=!0,s=!0,i.isProcessing||t(f)),a.schedule(e,o,r)),e},{}),cancel:t=>{for(let e=0;e<le.length;e++)r[le[e]].cancel(t)},state:i,steps:r}}const{schedule:ce,cancel:he,state:de,steps:me}=ue(\"undefined\"!=typeof requestAnimationFrame?requestAnimationFrame:Bt,!0);let pe;function fe(){pe=void 0}const ge={now:()=>(void 0===pe&&ge.set(de.isProcessing||Dt.useManualTiming?de.timestamp:performance.now()),pe),set:t=>{pe=t,queueMicrotask(fe)}},ye=t=>e=>\"string\"==typeof e&&e.startsWith(t),ve=ye(\"--\"),xe=ye(\"var(--\"),we=t=>!!xe(t)&&be.test(t.split(\"/*\")[0].trim()),be=/var\\(--(?:[\\w-]+\\s*|[\\w-]+\\s*,(?:\\s*[^)(\\s]|\\s*\\((?:[^)(]|\\([^)(]*\\))*\\))+\\s*)\\)$/iu;function Se(t){return\"string\"==typeof t&&t.split(\"/*\")[0].includes(\"var(--\")}const Te={test:t=>\"number\"==typeof t,parse:parseFloat,transform:t=>t},Pe={...Te,transform:t=>Vt(0,1,t)},ke={...Te,default:1},je=t=>Math.round(1e5*t)/1e5,Me=/-?(?:\\d+(?:\\.\\d+)?|\\.\\d+)/gu;const Ee=/^(?:#[\\da-f]{3,8}|(?:rgb|hsl)a?\\((?:-?[\\d.]+%?[,\\s]+){2}-?[\\d.]+%?\\s*(?:[,/]\\s*)?(?:\\b\\d+(?:\\.\\d+)?|\\.\\d+)?%?\\))$/iu,Ae=(t,e)=>n=>Boolean(\"string\"==typeof n&&Ee.test(n)&&n.startsWith(t)||e&&!function(t){return null==t}(n)&&Object.prototype.hasOwnProperty.call(n,e)),Ce=(t,e,n)=>s=>{if(\"string\"!=typeof s)return s;const[i,o,r,a]=s.match(Me);return{[t]:parseFloat(i),[e]:parseFloat(o),[n]:parseFloat(r),alpha:void 0!==a?parseFloat(a):1}},Ve={...Te,transform:t=>Math.round((t=>Vt(0,255,t))(t))},De={test:Ae(\"rgb\",\"red\"),parse:Ce(\"red\",\"green\",\"blue\"),transform:({red:t,green:e,blue:n,alpha:s=1})=>\"rgba(\"+Ve.transform(t)+\", \"+Ve.transform(e)+\", \"+Ve.transform(n)+\", \"+je(Pe.transform(s))+\")\"};const Re={test:Ae(\"#\"),parse:function(t){let e=\"\",n=\"\",s=\"\",i=\"\";return t.length>5?(e=t.substring(1,3),n=t.substring(3,5),s=t.substring(5,7),i=t.substring(7,9)):(e=t.substring(1,2),n=t.substring(2,3),s=t.substring(3,4),i=t.substring(4,5),e+=e,n+=n,s+=s,i+=i),{red:parseInt(e,16),green:parseInt(n,16),blue:parseInt(s,16),alpha:i?parseInt(i,16)/255:1}},transform:De.transform},Le=t=>({test:e=>\"string\"==typeof e&&e.endsWith(t)&&1===e.split(\" \").length,parse:parseFloat,transform:e=>`${e}${t}`}),Ne=Le(\"deg\"),Ie=Le(\"%\"),Be=Le(\"px\"),Fe=Le(\"vh\"),Oe=Le(\"vw\"),Ue=(()=>({...Ie,parse:t=>Ie.parse(t)/100,transform:t=>Ie.transform(100*t)}))(),ze={test:Ae(\"hsl\",\"hue\"),parse:Ce(\"hue\",\"saturation\",\"lightness\"),transform:({hue:t,saturation:e,lightness:n,alpha:s=1})=>\"hsla(\"+Math.round(t)+\", \"+Ie.transform(je(e))+\", \"+Ie.transform(je(n))+\", \"+je(Pe.transform(s))+\")\"},_e={test:t=>De.test(t)||Re.test(t)||ze.test(t),parse:t=>De.test(t)?De.parse(t):ze.test(t)?ze.parse(t):Re.parse(t),transform:t=>\"string\"==typeof t?t:t.hasOwnProperty(\"red\")?De.transform(t):ze.transform(t),getAnimatableNone:t=>{const e=_e.parse(t);return e.alpha=0,_e.transform(e)}},We=/(?:#[\\da-f]{3,8}|(?:rgb|hsl)a?\\((?:-?[\\d.]+%?[,\\s]+){2}-?[\\d.]+%?\\s*(?:[,/]\\s*)?(?:\\b\\d+(?:\\.\\d+)?|\\.\\d+)?%?\\))/giu;const $e=\"number\",Ye=\"color\",Ke=/var\\s*\\(\\s*--(?:[\\w-]+\\s*|[\\w-]+\\s*,(?:\\s*[^)(\\s]|\\s*\\((?:[^)(]|\\([^)(]*\\))*\\))+\\s*)\\)|#[\\da-f]{3,8}|(?:rgb|hsl)a?\\((?:-?[\\d.]+%?[,\\s]+){2}-?[\\d.]+%?\\s*(?:[,/]\\s*)?(?:\\b\\d+(?:\\.\\d+)?|\\.\\d+)?%?\\)|-?(?:\\d+(?:\\.\\d+)?|\\.\\d+)/giu;function Xe(t){const e=t.toString(),n=[],s={color:[],number:[],var:[]},i=[];let o=0;const r=e.replace(Ke,t=>(_e.test(t)?(s.color.push(o),i.push(Ye),n.push(_e.parse(t))):t.startsWith(\"var(\")?(s.var.push(o),i.push(\"var\"),n.push(t)):(s.number.push(o),i.push($e),n.push(parseFloat(t))),++o,\"${}\")).split(\"${}\");return{values:n,split:r,indexes:s,types:i}}function He(t){return Xe(t).values}function qe(t){const{split:e,types:n}=Xe(t),s=e.length;return t=>{let i=\"\";for(let o=0;o<s;o++)if(i+=e[o],void 0!==t[o]){const e=n[o];i+=e===$e?je(t[o]):e===Ye?_e.transform(t[o]):t[o]}return i}}const Ge=t=>\"number\"==typeof t?0:_e.test(t)?_e.getAnimatableNone(t):t;const Ze={test:function(t){var e,n;return isNaN(t)&&\"string\"==typeof t&&((null==(e=t.match(Me))?void 0:e.length)||0)+((null==(n=t.match(We))?void 0:n.length)||0)>0},parse:He,createTransformer:qe,getAnimatableNone:function(t){const e=He(t);return qe(t)(e.map(Ge))}};function Je(t,e,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?t+6*(e-t)*n:n<.5?e:n<2/3?t+(e-t)*(2/3-n)*6:t}function Qe(t,e){return n=>n>0?e:t}const tn=(t,e,n)=>t+(e-t)*n,en=(t,e,n)=>{const s=t*t,i=n*(e*e-s)+s;return i<0?0:Math.sqrt(i)},nn=[Re,De,ze];function sn(t){const e=(n=t,nn.find(t=>t.test(n)));var n;if(!Boolean(e))return!1;let s=e.parse(t);return e===ze&&(s=function({hue:t,saturation:e,lightness:n,alpha:s}){t/=360,n/=100;let i=0,o=0,r=0;if(e/=100){const s=n<.5?n*(1+e):n+e-n*e,a=2*n-s;i=Je(a,s,t+1/3),o=Je(a,s,t),r=Je(a,s,t-1/3)}else i=o=r=n;return{red:Math.round(255*i),green:Math.round(255*o),blue:Math.round(255*r),alpha:s}}(s)),s}const on=(t,e)=>{const n=sn(t),s=sn(e);if(!n||!s)return Qe(t,e);const i={...n};return t=>(i.red=en(n.red,s.red,t),i.green=en(n.green,s.green,t),i.blue=en(n.blue,s.blue,t),i.alpha=tn(n.alpha,s.alpha,t),De.transform(i))},rn=new Set([\"none\",\"hidden\"]);function an(t,e){return n=>tn(t,e,n)}function ln(t){return\"number\"==typeof t?an:\"string\"==typeof t?we(t)?Qe:_e.test(t)?on:hn:Array.isArray(t)?un:\"object\"==typeof t?_e.test(t)?on:cn:Qe}function un(t,e){const n=[...t],s=n.length,i=t.map((t,n)=>ln(t)(t,e[n]));return t=>{for(let e=0;e<s;e++)n[e]=i[e](t);return n}}function cn(t,e){const n={...t,...e},s={};for(const i in n)void 0!==t[i]&&void 0!==e[i]&&(s[i]=ln(t[i])(t[i],e[i]));return t=>{for(const e in s)n[e]=s[e](t);return n}}const hn=(t,e)=>{const n=Ze.createTransformer(e),s=Xe(t),i=Xe(e);return s.indexes.var.length===i.indexes.var.length&&s.indexes.color.length===i.indexes.color.length&&s.indexes.number.length>=i.indexes.number.length?rn.has(t)&&!i.values.length||rn.has(e)&&!s.values.length?function(t,e){return rn.has(t)?n=>n<=0?t:e:n=>n>=1?e:t}(t,e):Ot(un(function(t,e){const n=[],s={color:0,var:0,number:0};for(let i=0;i<e.values.length;i++){const o=e.types[i],r=t.indexes[o][s[o]],a=t.values[r]??0;n[i]=a,s[o]++}return n}(s,i),i.values),n):Qe(t,e)};function dn(t,e,n){if(\"number\"==typeof t&&\"number\"==typeof e&&\"number\"==typeof n)return tn(t,e,n);return ln(t)(t,e)}const mn=t=>{const e=({timestamp:e})=>t(e);return{start:(t=!0)=>ce.update(e,t),stop:()=>he(e),now:()=>de.isProcessing?de.timestamp:ge.now()}},pn=(t,e,n=10)=>{let s=\"\";const i=Math.max(Math.round(e/n),2);for(let o=0;o<i;o++)s+=Math.round(1e4*t(o/(i-1)))/1e4+\", \";return`linear(${s.substring(0,s.length-2)})`},fn=2e4;function gn(t){let e=0;let n=t.next(e);for(;!n.done&&e<fn;)e+=50,n=t.next(e);return e>=fn?1/0:e}function yn(t,e,n){const s=Math.max(e-5,0);return $t(n-t(s),e-s)}const vn=100,xn=10,wn=1,bn=0,Sn=800,Tn=.3,Pn=.3,kn={granular:.01,default:2},jn={granular:.005,default:.5},Mn=.01,En=10,An=.05,Cn=1,Vn=.001;function Dn({duration:t=Sn,bounce:e=Tn,velocity:n=bn,mass:s=wn}){let i,o,r=1-e;r=Vt(An,Cn,r),t=Vt(Mn,En,Wt(t)),r<1?(i=e=>{const s=e*r,i=s*t,o=s-n,a=Ln(e,r),l=Math.exp(-i);return Vn-o/a*l},o=e=>{const s=e*r*t,o=s*n+n,a=Math.pow(r,2)*Math.pow(e,2)*t,l=Math.exp(-s),u=Ln(Math.pow(e,2),r);return(-i(e)+Vn>0?-1:1)*((o-a)*l)/u}):(i=e=>Math.exp(-e*t)*((e-n)*t+1)-.001,o=e=>Math.exp(-e*t)*(t*t*(n-e)));const a=function(t,e,n){let s=n;for(let i=1;i<Rn;i++)s-=t(s)/e(s);return s}(i,o,5/t);if(t=_t(t),isNaN(a))return{stiffness:vn,damping:xn,duration:t};{const e=Math.pow(a,2)*s;return{stiffness:e,damping:2*r*Math.sqrt(s*e),duration:t}}}const Rn=12;function Ln(t,e){return t*Math.sqrt(1-e*e)}const Nn=[\"duration\",\"bounce\"],In=[\"stiffness\",\"damping\",\"mass\"];function Bn(t,e){return e.some(e=>void 0!==t[e])}function Fn(t=Pn,e=Tn){const n=\"object\"!=typeof t?{visualDuration:t,keyframes:[0,1],bounce:e}:t;let{restSpeed:s,restDelta:i}=n;const o=n.keyframes[0],r=n.keyframes[n.keyframes.length-1],a={done:!1,value:o},{stiffness:l,damping:u,mass:c,duration:h,velocity:d,isResolvedFromDuration:m}=function(t){let e={velocity:bn,stiffness:vn,damping:xn,mass:wn,isResolvedFromDuration:!1,...t};if(!Bn(t,In)&&Bn(t,Nn))if(t.visualDuration){const n=t.visualDuration,s=2*Math.PI/(1.2*n),i=s*s,o=2*Vt(.05,1,1-(t.bounce||0))*Math.sqrt(i);e={...e,mass:wn,stiffness:i,damping:o}}else{const n=Dn(t);e={...e,...n,mass:wn},e.isResolvedFromDuration=!0}return e}({...n,velocity:-Wt(n.velocity||0)}),p=d||0,f=u/(2*Math.sqrt(l*c)),g=r-o,y=Wt(Math.sqrt(l/c)),v=Math.abs(g)<5;let x;if(s||(s=v?kn.granular:kn.default),i||(i=v?jn.granular:jn.default),f<1){const t=Ln(y,f);x=e=>{const n=Math.exp(-f*y*e);return r-n*((p+f*y*g)/t*Math.sin(t*e)+g*Math.cos(t*e))}}else if(1===f)x=t=>r-Math.exp(-y*t)*(g+(p+y*g)*t);else{const t=y*Math.sqrt(f*f-1);x=e=>{const n=Math.exp(-f*y*e),s=Math.min(t*e,300);return r-n*((p+f*y*g)*Math.sinh(s)+t*g*Math.cosh(s))/t}}const w={calculatedDuration:m&&h||null,next:t=>{const e=x(t);if(m)a.done=t>=h;else{let n=0===t?p:0;f<1&&(n=0===t?_t(p):yn(x,t,e));const o=Math.abs(n)<=s,l=Math.abs(r-e)<=i;a.done=o&&l}return a.value=a.done?r:e,a},toString:()=>{const t=Math.min(gn(w),fn),e=pn(e=>w.next(t*e).value,t,30);return t+\"ms \"+e},toTransition:()=>{}};return w}function On({keyframes:t,velocity:e=0,power:n=.8,timeConstant:s=325,bounceDamping:i=10,bounceStiffness:o=500,modifyTarget:r,min:a,max:l,restDelta:u=.5,restSpeed:c}){const h=t[0],d={done:!1,value:h},m=t=>void 0===a?l:void 0===l||Math.abs(a-t)<Math.abs(l-t)?a:l;let p=n*e;const f=h+p,g=void 0===r?f:r(f);g!==f&&(p=g-h);const y=t=>-p*Math.exp(-t/s),v=t=>g+y(t),x=t=>{const e=y(t),n=v(t);d.done=Math.abs(e)<=u,d.value=d.done?g:n};let w,b;const S=t=>{var e;(e=d.value,void 0!==a&&e<a||void 0!==l&&e>l)&&(w=t,b=Fn({keyframes:[d.value,m(d.value)],velocity:yn(v,t,d.value),damping:i,stiffness:o,restDelta:u,restSpeed:c}))};return S(0),{calculatedDuration:null,next:t=>{let e=!1;return b||void 0!==w||(e=!0,x(t),S(t)),void 0!==w&&t>=w?b.next(t-w):(!e&&x(t),d)}}}function Un(t,e,{clamp:n=!0,ease:s,mixer:i}={}){const o=t.length;if(e.length,1===o)return()=>e[0];if(2===o&&e[0]===e[1])return()=>e[1];const r=t[0]===t[1];t[0]>t[o-1]&&(t=[...t].reverse(),e=[...e].reverse());const a=function(t,e,n){const s=[],i=n||Dt.mix||dn,o=t.length-1;for(let r=0;r<o;r++){let n=i(t[r],t[r+1]);if(e){const t=Array.isArray(e)?e[r]||Bt:e;n=Ot(t,n)}s.push(n)}return s}(e,s,i),l=a.length,u=n=>{if(r&&n<t[0])return e[0];let s=0;if(l>1)for(;s<t.length-2&&!(n<t[s+1]);s++);const i=Ut(t[s],t[s+1],n);return a[s](i)};return n?e=>u(Vt(t[0],t[o-1],e)):u}function zn(t){const e=[0];return function(t,e){const n=t[t.length-1];for(let s=1;s<=e;s++){const i=Ut(0,e,s);t.push(tn(n,1,i))}}(e,t.length-1),e}function _n({duration:t=300,keyframes:e,times:n,ease:s=\"easeInOut\"}){const i=(t=>Array.isArray(t)&&\"number\"!=typeof t[0])(s)?s.map(ae):ae(s),o={done:!1,value:e[0]},r=function(t,e){return t.map(t=>t*e)}(n&&n.length===e.length?n:zn(e),t),a=Un(r,e,{ease:Array.isArray(i)?i:(l=e,u=i,l.map(()=>u||ie).splice(0,l.length-1))});var l,u;return{calculatedDuration:t,next:e=>(o.value=a(e),o.done=e>=t,o)}}Fn.applyToOptions=t=>{const e=function(t,e=100,n){const s=n({...t,keyframes:[0,e]}),i=Math.min(gn(s),fn);return{type:\"keyframes\",ease:t=>s.next(i*t).value/e,duration:Wt(i)}}(t,100,Fn);return t.ease=e.ease,t.duration=_t(e.duration),t.type=\"keyframes\",t};const Wn=t=>null!==t;function $n(t,{repeat:e,repeatType:n=\"loop\"},s,i=1){const o=t.filter(Wn),r=i<0||e&&\"loop\"!==n&&e%2==1?0:o.length-1;return r&&void 0!==s?s:o[r]}const Yn={decay:On,inertia:On,tween:_n,keyframes:_n,spring:Fn};function Kn(t){\"string\"==typeof t.type&&(t.type=Yn[t.type])}class Xn{constructor(){this.updateFinished()}get finished(){return this._finished}updateFinished(){this._finished=new Promise(t=>{this.resolve=t})}notifyFinished(){this.resolve()}then(t,e){return this.finished.then(t,e)}}const Hn=t=>t/100;class qn extends Xn{constructor(t){super(),this.state=\"idle\",this.startTime=null,this.isStopped=!1,this.currentTime=0,this.holdTime=null,this.playbackSpeed=1,this.stop=()=>{var t,e;const{motionValue:n}=this.options;n&&n.updatedAt!==ge.now()&&this.tick(ge.now()),this.isStopped=!0,\"idle\"!==this.state&&(this.teardown(),null==(e=(t=this.options).onStop)||e.call(t))},this.options=t,this.initAnimation(),this.play(),!1===t.autoplay&&this.pause()}initAnimation(){const{options:t}=this;Kn(t);const{type:e=_n,repeat:n=0,repeatDelay:s=0,repeatType:i,velocity:o=0}=t;let{keyframes:r}=t;const a=e||_n;a!==_n&&\"number\"!=typeof r[0]&&(this.mixKeyframes=Ot(Hn,dn(r[0],r[1])),r=[0,100]);const l=a({...t,keyframes:r});\"mirror\"===i&&(this.mirroredGenerator=a({...t,keyframes:[...r].reverse(),velocity:-o})),null===l.calculatedDuration&&(l.calculatedDuration=gn(l));const{calculatedDuration:u}=l;this.calculatedDuration=u,this.resolvedDuration=u+s,this.totalDuration=this.resolvedDuration*(n+1)-s,this.generator=l}updateTime(t){const e=Math.round(t-this.startTime)*this.playbackSpeed;null!==this.holdTime?this.currentTime=this.holdTime:this.currentTime=e}tick(t,e=!1){const{generator:n,totalDuration:s,mixKeyframes:i,mirroredGenerator:o,resolvedDuration:r,calculatedDuration:a}=this;if(null===this.startTime)return n.next(0);const{delay:l=0,keyframes:u,repeat:c,repeatType:h,repeatDelay:d,type:m,onUpdate:p,finalKeyframe:f}=this.options;this.speed>0?this.startTime=Math.min(this.startTime,t):this.speed<0&&(this.startTime=Math.min(t-s/this.speed,this.startTime)),e?this.currentTime=t:this.updateTime(t);const g=this.currentTime-l*(this.playbackSpeed>=0?1:-1),y=this.playbackSpeed>=0?g<0:g>s;this.currentTime=Math.max(g,0),\"finished\"===this.state&&null===this.holdTime&&(this.currentTime=s);let v=this.currentTime,x=n;if(c){const t=Math.min(this.currentTime,s)/r;let e=Math.floor(t),n=t%1;!n&&t>=1&&(n=1),1===n&&e--,e=Math.min(e,c+1);Boolean(e%2)&&(\"reverse\"===h?(n=1-n,d&&(n-=d/r)):\"mirror\"===h&&(x=o)),v=Vt(0,1,n)*r}const w=y?{done:!1,value:u[0]}:x.next(v);i&&(w.value=i(w.value));let{done:b}=w;y||null===a||(b=this.playbackSpeed>=0?this.currentTime>=s:this.currentTime<=0);const S=null===this.holdTime&&(\"finished\"===this.state||\"running\"===this.state&&b);return S&&m!==On&&(w.value=$n(u,this.options,f,this.speed)),p&&p(w.value),S&&this.finish(),w}then(t,e){return this.finished.then(t,e)}get duration(){return Wt(this.calculatedDuration)}get iterationDuration(){const{delay:t=0}=this.options||{};return this.duration+Wt(t)}get time(){return Wt(this.currentTime)}set time(t){var e;t=_t(t),this.currentTime=t,null===this.startTime||null!==this.holdTime||0===this.playbackSpeed?this.holdTime=t:this.driver&&(this.startTime=this.driver.now()-t/this.playbackSpeed),null==(e=this.driver)||e.start(!1)}get speed(){return this.playbackSpeed}set speed(t){this.updateTime(ge.now());const e=this.playbackSpeed!==t;this.playbackSpeed=t,e&&(this.time=Wt(this.currentTime))}play(){var t,e;if(this.isStopped)return;const{driver:n=mn,startTime:s}=this.options;this.driver||(this.driver=n(t=>this.tick(t))),null==(e=(t=this.options).onPlay)||e.call(t);const i=this.driver.now();\"finished\"===this.state?(this.updateFinished(),this.startTime=i):null!==this.holdTime?this.startTime=i-this.holdTime:this.startTime||(this.startTime=s??i),\"finished\"===this.state&&this.speed<0&&(this.startTime+=this.calculatedDuration),this.holdTime=null,this.state=\"running\",this.driver.start()}pause(){this.state=\"paused\",this.updateTime(ge.now()),this.holdTime=this.currentTime}complete(){\"running\"!==this.state&&this.play(),this.state=\"finished\",this.holdTime=null}finish(){var t,e;this.notifyFinished(),this.teardown(),this.state=\"finished\",null==(e=(t=this.options).onComplete)||e.call(t)}cancel(){var t,e;this.holdTime=null,this.startTime=0,this.tick(0),this.teardown(),null==(e=(t=this.options).onCancel)||e.call(t)}teardown(){this.state=\"idle\",this.stopDriver(),this.startTime=this.holdTime=null}stopDriver(){this.driver&&(this.driver.stop(),this.driver=void 0)}sample(t){return this.startTime=0,this.tick(t,!0)}attachTimeline(t){var e;return this.options.allowFlatten&&(this.options.type=\"keyframes\",this.options.ease=\"linear\",this.initAnimation()),null==(e=this.driver)||e.stop(),t.observe(this)}}const Gn=t=>180*t/Math.PI,Zn=t=>{const e=Gn(Math.atan2(t[1],t[0]));return Qn(e)},Jn={x:4,y:5,translateX:4,translateY:5,scaleX:0,scaleY:3,scale:t=>(Math.abs(t[0])+Math.abs(t[3]))/2,rotate:Zn,rotateZ:Zn,skewX:t=>Gn(Math.atan(t[1])),skewY:t=>Gn(Math.atan(t[2])),skew:t=>(Math.abs(t[1])+Math.abs(t[2]))/2},Qn=t=>((t%=360)<0&&(t+=360),t),ts=t=>Math.sqrt(t[0]*t[0]+t[1]*t[1]),es=t=>Math.sqrt(t[4]*t[4]+t[5]*t[5]),ns={x:12,y:13,z:14,translateX:12,translateY:13,translateZ:14,scaleX:ts,scaleY:es,scale:t=>(ts(t)+es(t))/2,rotateX:t=>Qn(Gn(Math.atan2(t[6],t[5]))),rotateY:t=>Qn(Gn(Math.atan2(-t[2],t[0]))),rotateZ:Zn,rotate:Zn,skewX:t=>Gn(Math.atan(t[4])),skewY:t=>Gn(Math.atan(t[1])),skew:t=>(Math.abs(t[1])+Math.abs(t[4]))/2};function ss(t){return t.includes(\"scale\")?1:0}function is(t,e){if(!t||\"none\"===t)return ss(e);const n=t.match(/^matrix3d\\(([-\\d.e\\s,]+)\\)$/u);let s,i;if(n)s=ns,i=n;else{const e=t.match(/^matrix\\(([-\\d.e\\s,]+)\\)$/u);s=Jn,i=e}if(!i)return ss(e);const o=s[e],r=i[1].split(\",\").map(os);return\"function\"==typeof o?o(r):r[o]}function os(t){return parseFloat(t.trim())}const rs=[\"transformPerspective\",\"x\",\"y\",\"z\",\"translateX\",\"translateY\",\"translateZ\",\"scale\",\"scaleX\",\"scaleY\",\"rotate\",\"rotateX\",\"rotateY\",\"rotateZ\",\"skew\",\"skewX\",\"skewY\"],as=(()=>new Set(rs))(),ls=t=>t===Te||t===Be,us=new Set([\"x\",\"y\",\"z\"]),cs=rs.filter(t=>!us.has(t));const hs={width:({x:t},{paddingLeft:e=\"0\",paddingRight:n=\"0\"})=>t.max-t.min-parseFloat(e)-parseFloat(n),height:({y:t},{paddingTop:e=\"0\",paddingBottom:n=\"0\"})=>t.max-t.min-parseFloat(e)-parseFloat(n),top:(t,{top:e})=>parseFloat(e),left:(t,{left:e})=>parseFloat(e),bottom:({y:t},{top:e})=>parseFloat(e)+(t.max-t.min),right:({x:t},{left:e})=>parseFloat(e)+(t.max-t.min),x:(t,{transform:e})=>is(e,\"x\"),y:(t,{transform:e})=>is(e,\"y\")};hs.translateX=hs.x,hs.translateY=hs.y;const ds=new Set;let ms=!1,ps=!1,fs=!1;function gs(){if(ps){const t=Array.from(ds).filter(t=>t.needsMeasurement),e=new Set(t.map(t=>t.element)),n=new Map;e.forEach(t=>{const e=function(t){const e=[];return cs.forEach(n=>{const s=t.getValue(n);void 0!==s&&(e.push([n,s.get()]),s.set(n.startsWith(\"scale\")?1:0))}),e}(t);e.length&&(n.set(t,e),t.render())}),t.forEach(t=>t.measureInitialState()),e.forEach(t=>{t.render();const e=n.get(t);e&&e.forEach(([e,n])=>{var s;null==(s=t.getValue(e))||s.set(n)})}),t.forEach(t=>t.measureEndState()),t.forEach(t=>{void 0!==t.suspendedScrollY&&window.scrollTo(0,t.suspendedScrollY)})}ps=!1,ms=!1,ds.forEach(t=>t.complete(fs)),ds.clear()}function ys(){ds.forEach(t=>{t.readKeyframes(),t.needsMeasurement&&(ps=!0)})}class vs{constructor(t,e,n,s,i,o=!1){this.state=\"pending\",this.isAsync=!1,this.needsMeasurement=!1,this.unresolvedKeyframes=[...t],this.onComplete=e,this.name=n,this.motionValue=s,this.element=i,this.isAsync=o}scheduleResolve(){this.state=\"scheduled\",this.isAsync?(ds.add(this),ms||(ms=!0,ce.read(ys),ce.resolveKeyframes(gs))):(this.readKeyframes(),this.complete())}readKeyframes(){const{unresolvedKeyframes:t,name:e,element:n,motionValue:s}=this;if(null===t[0]){const i=null==s?void 0:s.get(),o=t[t.length-1];if(void 0!==i)t[0]=i;else if(n&&e){const s=n.readValue(e,o);null!=s&&(t[0]=s)}void 0===t[0]&&(t[0]=o),s&&void 0===i&&s.set(t[0])}!function(t){for(let e=1;e<t.length;e++)t[e]??(t[e]=t[e-1])}(t)}setFinalKeyframe(){}measureInitialState(){}renderEndStyles(){}measureEndState(){}complete(t=!1){this.state=\"complete\",this.onComplete(this.unresolvedKeyframes,this.finalKeyframe,t),ds.delete(this)}cancel(){\"scheduled\"===this.state&&(ds.delete(this),this.state=\"pending\")}resume(){\"pending\"===this.state&&this.scheduleResolve()}}const xs=It(()=>void 0!==window.ScrollTimeline),ws={};function bs(t,e){const n=It(t);return()=>ws[e]??n()}const Ss=bs(()=>{try{document.createElement(\"div\").animate({opacity:0},{easing:\"linear(0, 1)\"})}catch(t){return!1}return!0},\"linearEasing\"),Ts=([t,e,n,s])=>`cubic-bezier(${t}, ${e}, ${n}, ${s})`,Ps={linear:\"linear\",ease:\"ease\",easeIn:\"ease-in\",easeOut:\"ease-out\",easeInOut:\"ease-in-out\",circIn:Ts([0,.65,.55,1]),circOut:Ts([.55,0,1,.45]),backIn:Ts([.31,.01,.66,-.59]),backOut:Ts([.33,1.53,.69,.99])};function ks(t,e){return t?\"function\"==typeof t?Ss()?pn(t,e):\"ease-out\":oe(t)?Ts(t):Array.isArray(t)?t.map(t=>ks(t,e)||Ps.easeOut):Ps[t]:void 0}function js(t,e,n,{delay:s=0,duration:i=300,repeat:o=0,repeatType:r=\"loop\",ease:a=\"easeOut\",times:l}={},u=void 0){const c={[e]:n};l&&(c.offset=l);const h=ks(a,i);Array.isArray(h)&&(c.easing=h);const d={delay:s,duration:i,easing:Array.isArray(h)?\"linear\":h,fill:\"both\",iterations:o+1,direction:\"reverse\"===r?\"alternate\":\"normal\"};u&&(d.pseudoElement=u);return t.animate(c,d)}function Ms(t){return\"function\"==typeof t&&\"applyToOptions\"in t}class Es extends Xn{constructor(t){if(super(),this.finishedTime=null,this.isStopped=!1,this.manualStartTime=null,!t)return;const{element:e,name:n,keyframes:s,pseudoElement:i,allowFlatten:o=!1,finalKeyframe:r,onComplete:a}=t;this.isPseudoElement=Boolean(i),this.allowFlatten=o,this.options=t,t.type;const l=function({type:t,...e}){return Ms(t)&&Ss()?t.applyToOptions(e):(e.duration??(e.duration=300),e.ease??(e.ease=\"easeOut\"),e)}(t);this.animation=js(e,n,s,l,i),!1===l.autoplay&&this.animation.pause(),this.animation.onfinish=()=>{if(this.finishedTime=this.time,!i){const t=$n(s,this.options,r,this.speed);this.updateMotionValue?this.updateMotionValue(t):function(t,e,n){(t=>t.startsWith(\"--\"))(e)?t.style.setProperty(e,n):t.style[e]=n}(e,n,t),this.animation.cancel()}null==a||a(),this.notifyFinished()}}play(){this.isStopped||(this.manualStartTime=null,this.animation.play(),\"finished\"===this.state&&this.updateFinished())}pause(){this.animation.pause()}complete(){var t,e;null==(e=(t=this.animation).finish)||e.call(t)}cancel(){try{this.animation.cancel()}catch(t){}}stop(){if(this.isStopped)return;this.isStopped=!0;const{state:t}=this;\"idle\"!==t&&\"finished\"!==t&&(this.updateMotionValue?this.updateMotionValue():this.commitStyles(),this.isPseudoElement||this.cancel())}commitStyles(){var t,e;this.isPseudoElement||null==(e=(t=this.animation).commitStyles)||e.call(t)}get duration(){var t,e;const n=(null==(e=null==(t=this.animation.effect)?void 0:t.getComputedTiming)?void 0:e.call(t).duration)||0;return Wt(Number(n))}get iterationDuration(){const{delay:t=0}=this.options||{};return this.duration+Wt(t)}get time(){return Wt(Number(this.animation.currentTime)||0)}set time(t){this.manualStartTime=null,this.finishedTime=null,this.animation.currentTime=_t(t)}get speed(){return this.animation.playbackRate}set speed(t){t<0&&(this.finishedTime=null),this.animation.playbackRate=t}get state(){return null!==this.finishedTime?\"finished\":this.animation.playState}get startTime(){return this.manualStartTime??Number(this.animation.startTime)}set startTime(t){this.manualStartTime=this.animation.startTime=t}attachTimeline({timeline:t,observe:e}){var n;return this.allowFlatten&&(null==(n=this.animation.effect)||n.updateTiming({easing:\"linear\"})),this.animation.onfinish=null,t&&xs()?(this.animation.timeline=t,Bt):e(this)}}const As={anticipate:Jt,backInOut:Zt,circInOut:ee};function Cs(t){\"string\"==typeof t.ease&&t.ease in As&&(t.ease=As[t.ease])}class Vs extends Es{constructor(t){Cs(t),Kn(t),super(t),void 0!==t.startTime&&(this.startTime=t.startTime),this.options=t}updateMotionValue(t){const{motionValue:e,onUpdate:n,onComplete:s,element:i,...o}=this.options;if(!e)return;if(void 0!==t)return void e.set(t);const r=new qn({...o,autoplay:!1}),a=Math.max(10,ge.now()-this.startTime),l=Vt(0,10,a-10);e.setWithVelocity(r.sample(Math.max(0,a-l)).value,r.sample(a).value,l),r.stop()}}const Ds=(t,e)=>\"zIndex\"!==e&&(!(\"number\"!=typeof t&&!Array.isArray(t))||!(\"string\"!=typeof t||!Ze.test(t)&&\"0\"!==t||t.startsWith(\"url(\")));function Rs(t){t.duration=0,t.type=\"keyframes\"}const Ls=new Set([\"opacity\",\"clipPath\",\"filter\",\"transform\"]),Ns=It(()=>Object.hasOwnProperty.call(Element.prototype,\"animate\"));class Is extends Xn{constructor({autoplay:t=!0,delay:e=0,type:n=\"keyframes\",repeat:s=0,repeatDelay:i=0,repeatType:o=\"loop\",keyframes:r,name:a,motionValue:l,element:u,...c}){var h;super(),this.stop=()=>{var t,e;this._animation&&(this._animation.stop(),null==(t=this.stopTimeline)||t.call(this)),null==(e=this.keyframeResolver)||e.cancel()},this.createdAt=ge.now();const d={autoplay:t,delay:e,type:n,repeat:s,repeatDelay:i,repeatType:o,name:a,motionValue:l,element:u,...c},m=(null==u?void 0:u.KeyframeResolver)||vs;this.keyframeResolver=new m(r,(t,e,n)=>this.onKeyframesResolved(t,e,d,!n),a,l,u),null==(h=this.keyframeResolver)||h.scheduleResolve()}onKeyframesResolved(t,e,n,s){this.keyframeResolver=void 0;const{name:i,type:o,velocity:r,delay:a,isHandoff:l,onUpdate:u}=n;this.resolvedAt=ge.now(),function(t,e,n,s){const i=t[0];if(null===i)return!1;if(\"display\"===e||\"visibility\"===e)return!0;const o=t[t.length-1],r=Ds(i,e),a=Ds(o,e);return!(!r||!a)&&(function(t){const e=t[0];if(1===t.length)return!0;for(let n=0;n<t.length;n++)if(t[n]!==e)return!0}(t)||(\"spring\"===n||Ms(n))&&s)}(t,i,o,r)||(!Dt.instantAnimations&&a||null==u||u($n(t,n,e)),t[0]=t[t.length-1],Rs(n),n.repeat=0);const c={startTime:s?this.resolvedAt&&this.resolvedAt-this.createdAt>40?this.resolvedAt:this.createdAt:void 0,finalKeyframe:e,...n,keyframes:t},h=!l&&function(t){var e;const{motionValue:n,name:s,repeatDelay:i,repeatType:o,damping:r,type:a}=t;if(!((null==(e=null==n?void 0:n.owner)?void 0:e.current)instanceof HTMLElement))return!1;const{onUpdate:l,transformTemplate:u}=n.owner.getProps();return Ns()&&s&&Ls.has(s)&&(\"transform\"!==s||!u)&&!l&&!i&&\"mirror\"!==o&&0!==r&&\"inertia\"!==a}(c)?new Vs({...c,element:c.motionValue.owner.current}):new qn(c);h.finished.then(()=>this.notifyFinished()).catch(Bt),this.pendingTimeline&&(this.stopTimeline=h.attachTimeline(this.pendingTimeline),this.pendingTimeline=void 0),this._animation=h}get finished(){return this._animation?this.animation.finished:this._finished}then(t,e){return this.finished.finally(t).then(()=>{})}get animation(){var t;return this._animation||(null==(t=this.keyframeResolver)||t.resume(),fs=!0,ys(),gs(),fs=!1),this._animation}get duration(){return this.animation.duration}get iterationDuration(){return this.animation.iterationDuration}get time(){return this.animation.time}set time(t){this.animation.time=t}get speed(){return this.animation.speed}get state(){return this.animation.state}set speed(t){this.animation.speed=t}get startTime(){return this.animation.startTime}attachTimeline(t){return this._animation?this.stopTimeline=this.animation.attachTimeline(t):this.pendingTimeline=t,()=>this.stop()}play(){this.animation.play()}pause(){this.animation.pause()}complete(){this.animation.complete()}cancel(){var t;this._animation&&this.animation.cancel(),null==(t=this.keyframeResolver)||t.cancel()}}const Bs=/^var\\(--(?:([\\w-]+)|([\\w-]+), ?([a-zA-Z\\d ()%#.,-]+))\\)/u;function Fs(t,e,n=1){const[s,i]=function(t){const e=Bs.exec(t);if(!e)return[,];const[,n,s,i]=e;return[`--${n??s}`,i]}(t);if(!s)return;const o=window.getComputedStyle(e).getPropertyValue(s);if(o){const t=o.trim();return Rt(t)?parseFloat(t):t}return we(i)?Fs(i,e,n+1):i}function Os(t,e){return(null==t?void 0:t[e])??(null==t?void 0:t.default)??t}const Us=new Set([\"width\",\"height\",\"top\",\"left\",\"right\",\"bottom\",...rs]),zs=t=>e=>e.test(t),_s=[Te,Be,Ie,Ne,Oe,Fe,{test:t=>\"auto\"===t,parse:t=>t}],Ws=t=>_s.find(zs(t));function $s(t){return\"number\"==typeof t?0===t:null===t||(\"none\"===t||\"0\"===t||Nt(t))}const Ys=new Set([\"brightness\",\"contrast\",\"saturate\",\"opacity\"]);function Ks(t){const[e,n]=t.slice(0,-1).split(\"(\");if(\"drop-shadow\"===e)return t;const[s]=n.match(Me)||[];if(!s)return t;const i=n.replace(s,\"\");let o=Ys.has(e)?1:0;return s!==n&&(o*=100),e+\"(\"+o+i+\")\"}const Xs=/\\b([a-z-]*)\\(.*?\\)/gu,Hs={...Ze,getAnimatableNone:t=>{const e=t.match(Xs);return e?e.map(Ks).join(\" \"):t}},qs={...Te,transform:Math.round},Gs={borderWidth:Be,borderTopWidth:Be,borderRightWidth:Be,borderBottomWidth:Be,borderLeftWidth:Be,borderRadius:Be,radius:Be,borderTopLeftRadius:Be,borderTopRightRadius:Be,borderBottomRightRadius:Be,borderBottomLeftRadius:Be,width:Be,maxWidth:Be,height:Be,maxHeight:Be,top:Be,right:Be,bottom:Be,left:Be,inset:Be,insetBlock:Be,insetBlockStart:Be,insetBlockEnd:Be,insetInline:Be,insetInlineStart:Be,insetInlineEnd:Be,padding:Be,paddingTop:Be,paddingRight:Be,paddingBottom:Be,paddingLeft:Be,paddingBlock:Be,paddingBlockStart:Be,paddingBlockEnd:Be,paddingInline:Be,paddingInlineStart:Be,paddingInlineEnd:Be,margin:Be,marginTop:Be,marginRight:Be,marginBottom:Be,marginLeft:Be,marginBlock:Be,marginBlockStart:Be,marginBlockEnd:Be,marginInline:Be,marginInlineStart:Be,marginInlineEnd:Be,backgroundPositionX:Be,backgroundPositionY:Be,...{rotate:Ne,rotateX:Ne,rotateY:Ne,rotateZ:Ne,scale:ke,scaleX:ke,scaleY:ke,scaleZ:ke,skew:Ne,skewX:Ne,skewY:Ne,distance:Be,translateX:Be,translateY:Be,translateZ:Be,x:Be,y:Be,z:Be,perspective:Be,transformPerspective:Be,opacity:Pe,originX:Ue,originY:Ue,originZ:Be},zIndex:qs,fillOpacity:Pe,strokeOpacity:Pe,numOctaves:qs},Zs={...Gs,color:_e,backgroundColor:_e,outlineColor:_e,fill:_e,stroke:_e,borderColor:_e,borderTopColor:_e,borderRightColor:_e,borderBottomColor:_e,borderLeftColor:_e,filter:Hs,WebkitFilter:Hs},Js=t=>Zs[t];function Qs(t,e){let n=Js(t);return n!==Hs&&(n=Ze),n.getAnimatableNone?n.getAnimatableNone(e):void 0}const ti=new Set([\"auto\",\"none\",\"0\"]);class ei extends vs{constructor(t,e,n,s,i){super(t,e,n,s,i,!0)}readKeyframes(){const{unresolvedKeyframes:t,element:e,name:n}=this;if(!e||!e.current)return;super.readKeyframes();for(let a=0;a<t.length;a++){let n=t[a];if(\"string\"==typeof n&&(n=n.trim(),we(n))){const s=Fs(n,e.current);void 0!==s&&(t[a]=s),a===t.length-1&&(this.finalKeyframe=n)}}if(this.resolveNoneKeyframes(),!Us.has(n)||2!==t.length)return;const[s,i]=t,o=Ws(s),r=Ws(i);if(Se(s)!==Se(i)&&hs[n])this.needsMeasurement=!0;else if(o!==r)if(ls(o)&&ls(r))for(let a=0;a<t.length;a++){const e=t[a];\"string\"==typeof e&&(t[a]=parseFloat(e))}else hs[n]&&(this.needsMeasurement=!0)}resolveNoneKeyframes(){const{unresolvedKeyframes:t,name:e}=this,n=[];for(let s=0;s<t.length;s++)(null===t[s]||$s(t[s]))&&n.push(s);n.length&&function(t,e,n){let s,i=0;for(;i<t.length&&!s;){const e=t[i];\"string\"==typeof e&&!ti.has(e)&&Xe(e).values.length&&(s=t[i]),i++}if(s&&n)for(const o of e)t[o]=Qs(n,s)}(t,n,e)}measureInitialState(){const{element:t,unresolvedKeyframes:e,name:n}=this;if(!t||!t.current)return;\"height\"===n&&(this.suspendedScrollY=window.pageYOffset),this.measuredOrigin=hs[n](t.measureViewportBox(),window.getComputedStyle(t.current)),e[0]=this.measuredOrigin;const s=e[e.length-1];void 0!==s&&t.getValue(n,s).jump(s,!1)}measureEndState(){var t;const{element:e,name:n,unresolvedKeyframes:s}=this;if(!e||!e.current)return;const i=e.getValue(n);i&&i.jump(this.measuredOrigin,!1);const o=s.length-1,r=s[o];s[o]=hs[n](e.measureViewportBox(),window.getComputedStyle(e.current)),null!==r&&void 0===this.finalKeyframe&&(this.finalKeyframe=r),(null==(t=this.removedTransforms)?void 0:t.length)&&this.removedTransforms.forEach(([t,n])=>{e.getValue(t).set(n)}),this.resolveNoneKeyframes()}}const ni=(t,e)=>e&&\"number\"==typeof t?e.transform(t):t;function si(t){return Lt(t)&&\"offsetHeight\"in t}class ii{constructor(t,e={}){this.canTrackVelocity=null,this.events={},this.updateAndNotify=t=>{var e;const n=ge.now();if(this.updatedAt!==n&&this.setPrevFrameValue(),this.prev=this.current,this.setCurrent(t),this.current!==this.prev&&(null==(e=this.events.change)||e.notify(this.current),this.dependents))for(const s of this.dependents)s.dirty()},this.hasAnimated=!1,this.setCurrent(t),this.owner=e.owner}setCurrent(t){var e;this.current=t,this.updatedAt=ge.now(),null===this.canTrackVelocity&&void 0!==t&&(this.canTrackVelocity=(e=this.current,!isNaN(parseFloat(e))))}setPrevFrameValue(t=this.current){this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt}onChange(t){return this.on(\"change\",t)}on(t,e){this.events[t]||(this.events[t]=new zt);const n=this.events[t].add(e);return\"change\"===t?()=>{n(),ce.read(()=>{this.events.change.getSize()||this.stop()})}:n}clearListeners(){for(const t in this.events)this.events[t].clear()}attach(t,e){this.passiveEffect=t,this.stopPassiveEffect=e}set(t){this.passiveEffect?this.passiveEffect(t,this.updateAndNotify):this.updateAndNotify(t)}setWithVelocity(t,e,n){this.set(e),this.prev=void 0,this.prevFrameValue=t,this.prevUpdatedAt=this.updatedAt-n}jump(t,e=!0){this.updateAndNotify(t),this.prev=t,this.prevUpdatedAt=this.prevFrameValue=void 0,e&&this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}dirty(){var t;null==(t=this.events.change)||t.notify(this.current)}addDependent(t){this.dependents||(this.dependents=new Set),this.dependents.add(t)}removeDependent(t){this.dependents&&this.dependents.delete(t)}get(){return this.current}getPrevious(){return this.prev}getVelocity(){const t=ge.now();if(!this.canTrackVelocity||void 0===this.prevFrameValue||t-this.updatedAt>30)return 0;const e=Math.min(this.updatedAt-this.prevUpdatedAt,30);return $t(parseFloat(this.current)-parseFloat(this.prevFrameValue),e)}start(t){return this.stop(),new Promise(e=>{this.hasAnimated=!0,this.animation=t(e),this.events.animationStart&&this.events.animationStart.notify()}).then(()=>{this.events.animationComplete&&this.events.animationComplete.notify(),this.clearAnimation()})}stop(){this.animation&&(this.animation.stop(),this.events.animationCancel&&this.events.animationCancel.notify()),this.clearAnimation()}isAnimating(){return!!this.animation}clearAnimation(){delete this.animation}destroy(){var t,e;null==(t=this.dependents)||t.clear(),null==(e=this.events.destroy)||e.notify(),this.clearListeners(),this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}}function oi(t,e){return new ii(t,e)}const{schedule:ri}=ue(queueMicrotask,!1),ai={x:!1,y:!1};function li(){return ai.x||ai.y}function ui(t,e){const n=function(t,e,n){if(t instanceof EventTarget)return[t];if(\"string\"==typeof t){let e=document;const s=(null==n?void 0:n[t])??e.querySelectorAll(t);return s?Array.from(s):[]}return Array.from(t)}(t),s=new AbortController;return[n,{passive:!0,...e,signal:s.signal},()=>s.abort()]}function ci(t){return!(\"touch\"===t.pointerType||li())}const hi=(t,e)=>!!e&&(t===e||hi(t,e.parentElement)),di=t=>\"mouse\"===t.pointerType?\"number\"!=typeof t.button||t.button<=0:!1!==t.isPrimary,mi=new Set([\"BUTTON\",\"INPUT\",\"SELECT\",\"TEXTAREA\",\"A\"]);function pi(t){return mi.has(t.tagName)||!0===t.isContentEditable}const fi=new WeakSet;function gi(t){return e=>{\"Enter\"===e.key&&t(e)}}function yi(t,e){t.dispatchEvent(new PointerEvent(\"pointer\"+e,{isPrimary:!0,bubbles:!0}))}function vi(t){return di(t)&&!li()}function xi(t,e,n={}){const[s,i,o]=ui(t,n),r=t=>{const s=t.currentTarget;if(!vi(t))return;fi.add(s);const o=e(s,t),r=(t,e)=>{window.removeEventListener(\"pointerup\",a),window.removeEventListener(\"pointercancel\",l),fi.has(s)&&fi.delete(s),vi(t)&&\"function\"==typeof o&&o(t,{success:e})},a=t=>{r(t,s===window||s===document||n.useGlobalTarget||hi(s,t.target))},l=t=>{r(t,!1)};window.addEventListener(\"pointerup\",a,i),window.addEventListener(\"pointercancel\",l,i)};return s.forEach(t=>{(n.useGlobalTarget?window:t).addEventListener(\"pointerdown\",r,i),si(t)&&(t.addEventListener(\"focus\",t=>((t,e)=>{const n=t.currentTarget;if(!n)return;const s=gi(()=>{if(fi.has(n))return;yi(n,\"down\");const t=gi(()=>{yi(n,\"up\")});n.addEventListener(\"keyup\",t,e),n.addEventListener(\"blur\",()=>yi(n,\"cancel\"),e)});n.addEventListener(\"keydown\",s,e),n.addEventListener(\"blur\",()=>n.removeEventListener(\"keydown\",s),e)})(t,i)),pi(t)||t.hasAttribute(\"tabindex\")||(t.tabIndex=0))}),o}function wi(t){return Lt(t)&&\"ownerSVGElement\"in t}const bi=t=>Boolean(t&&t.getVelocity),Si=[..._s,_e,Ze],Ti=e.createContext({transformPagePoint:t=>t,isStatic:!1,reducedMotion:\"never\"});function Pi(t,e){if(\"function\"==typeof t)return t(e);null!=t&&(t.current=e)}function ki(...t){return e.useCallback(function(...t){return e=>{let n=!1;const s=t.map(t=>{const s=Pi(t,e);return n||\"function\"!=typeof s||(n=!0),s});if(n)return()=>{for(let e=0;e<s.length;e++){const n=s[e];\"function\"==typeof n?n():Pi(t[e],null)}}}}(...t),t)}class ji extends e.Component{getSnapshotBeforeUpdate(t){const e=this.props.childRef.current;if(e&&t.isPresent&&!this.props.isPresent){const t=e.offsetParent,n=si(t)&&t.offsetWidth||0,s=this.props.sizeRef.current;s.height=e.offsetHeight||0,s.width=e.offsetWidth||0,s.top=e.offsetTop,s.left=e.offsetLeft,s.right=n-s.width-s.left}return null}componentDidUpdate(){}render(){return this.props.children}}function Mi({children:t,isPresent:s,anchorX:i,root:o}){var r;const a=e.useId(),l=e.useRef(null),u=e.useRef({width:0,height:0,top:0,left:0,right:0}),{nonce:c}=e.useContext(Ti),h=(null==(r=t.props)?void 0:r.ref)??(null==t?void 0:t.ref),d=ki(l,h);return e.useInsertionEffect(()=>{const{width:t,height:e,top:n,left:r,right:h}=u.current;if(s||!l.current||!t||!e)return;const d=\"left\"===i?`left: ${r}`:`right: ${h}`;l.current.dataset.motionPopId=a;const m=document.createElement(\"style\");c&&(m.nonce=c);const p=o??document.head;return p.appendChild(m),m.sheet&&m.sheet.insertRule(`\\n          [data-motion-pop-id=\"${a}\"] {\\n            position: absolute !important;\\n            width: ${t}px !important;\\n            height: ${e}px !important;\\n            ${d}px !important;\\n            top: ${n}px !important;\\n          }\\n        `),()=>{p.contains(m)&&p.removeChild(m)}},[s]),n.jsx(ji,{isPresent:s,childRef:l,sizeRef:u,children:e.cloneElement(t,{ref:d})})}const Ei=({children:t,initial:s,isPresent:i,onExitComplete:o,custom:r,presenceAffectsLayout:a,mode:l,anchorX:u,root:c})=>{const h=kt(Ai),d=e.useId();let m=!0,p=e.useMemo(()=>(m=!1,{id:d,initial:s,isPresent:i,custom:r,onExitComplete:t=>{h.set(t,!0);for(const e of h.values())if(!e)return;o&&o()},register:t=>(h.set(t,!1),()=>h.delete(t))}),[i,h,o]);return a&&m&&(p={...p}),e.useMemo(()=>{h.forEach((t,e)=>h.set(e,!1))},[i]),e.useEffect(()=>{!i&&!h.size&&o&&o()},[i]),\"popLayout\"===l&&(t=n.jsx(Mi,{isPresent:i,anchorX:u,root:c,children:t})),n.jsx(Et.Provider,{value:p,children:t})};function Ai(){return new Map}function Ci(t=!0){const n=e.useContext(Et);if(null===n)return[!0,null];const{isPresent:s,onExitComplete:i,register:o}=n,r=e.useId();e.useEffect(()=>{if(t)return o(r)},[t]);const a=e.useCallback(()=>t&&i&&i(r),[r,i,t]);return!s&&i?[!1,a]:[!0]}const Vi=t=>t.key||\"\";function Di(t){const n=[];return e.Children.forEach(t,t=>{e.isValidElement(t)&&n.push(t)}),n}const Ri=({children:t,custom:s,initial:i=!0,onExitComplete:o,presenceAffectsLayout:r=!0,mode:a=\"sync\",propagate:l=!1,anchorX:u=\"left\",root:c})=>{const[h,d]=Ci(l),m=e.useMemo(()=>Di(t),[t]),p=l&&!h?[]:m.map(Vi),f=e.useRef(!0),g=e.useRef(m),y=kt(()=>new Map),v=e.useRef(new Set),[x,w]=e.useState(m),[b,S]=e.useState(m);Mt(()=>{f.current=!1,g.current=m;for(let t=0;t<b.length;t++){const e=Vi(b[t]);p.includes(e)?(y.delete(e),v.current.delete(e)):!0!==y.get(e)&&y.set(e,!1)}},[b,p.length,p.join(\"-\")]);const T=[];if(m!==x){let t=[...m];for(let e=0;e<b.length;e++){const n=b[e],s=Vi(n);p.includes(s)||(t.splice(e,0,n),T.push(n))}return\"wait\"===a&&T.length&&(t=T),S(Di(t)),w(m),null}const{forceRender:P}=e.useContext(Pt);return n.jsx(n.Fragment,{children:b.map(t=>{const e=Vi(t),x=!(l&&!h)&&(m===b||p.includes(e));return n.jsx(Ei,{isPresent:x,initial:!(f.current&&!i)&&void 0,custom:s,presenceAffectsLayout:r,mode:a,root:c,onExitComplete:x?void 0:()=>{if(v.current.has(e))return;if(v.current.add(e),!y.has(e))return;y.set(e,!0);let t=!0;y.forEach(e=>{e||(t=!1)}),t&&(null==P||P(),S(g.current),l&&(null==d||d()),o&&o())},anchorX:u,children:t},e)})})},Li=e.createContext({strict:!1}),Ni={animation:[\"animate\",\"variants\",\"whileHover\",\"whileTap\",\"exit\",\"whileInView\",\"whileFocus\",\"whileDrag\"],exit:[\"exit\"],drag:[\"drag\",\"dragControls\"],focus:[\"whileFocus\"],hover:[\"whileHover\",\"onHoverStart\",\"onHoverEnd\"],tap:[\"whileTap\",\"onTap\",\"onTapStart\",\"onTapCancel\"],pan:[\"onPan\",\"onPanStart\",\"onPanSessionStart\",\"onPanEnd\"],inView:[\"whileInView\",\"onViewportEnter\",\"onViewportLeave\"],layout:[\"layout\",\"layoutId\"]},Ii={};for(const cu in Ni)Ii[cu]={isEnabled:t=>Ni[cu].some(e=>!!t[e])};const Bi=new Set([\"animate\",\"exit\",\"variants\",\"initial\",\"style\",\"values\",\"variants\",\"transition\",\"transformTemplate\",\"custom\",\"inherit\",\"onBeforeLayoutMeasure\",\"onAnimationStart\",\"onAnimationComplete\",\"onUpdate\",\"onDragStart\",\"onDrag\",\"onDragEnd\",\"onMeasureDragConstraints\",\"onDirectionLock\",\"onDragTransitionEnd\",\"_dragX\",\"_dragY\",\"onHoverStart\",\"onHoverEnd\",\"onViewportEnter\",\"onViewportLeave\",\"globalTapTarget\",\"ignoreStrict\",\"viewport\"]);function Fi(t){return t.startsWith(\"while\")||t.startsWith(\"drag\")&&\"draggable\"!==t||t.startsWith(\"layout\")||t.startsWith(\"onTap\")||t.startsWith(\"onPan\")||t.startsWith(\"onLayout\")||Bi.has(t)}let Oi=t=>!Fi(t);try{\"function\"==typeof(Ui=require(\"@emotion/is-prop-valid\").default)&&(Oi=t=>t.startsWith(\"on\")?!Fi(t):Ui(t))}catch{}var Ui;const zi=e.createContext({});function _i(t){return null!==t&&\"object\"==typeof t&&\"function\"==typeof t.start}function Wi(t){return\"string\"==typeof t||Array.isArray(t)}const $i=[\"animate\",\"whileInView\",\"whileFocus\",\"whileHover\",\"whileTap\",\"whileDrag\",\"exit\"],Yi=[\"initial\",...$i];function Ki(t){return _i(t.animate)||Yi.some(e=>Wi(t[e]))}function Xi(t){return Boolean(Ki(t)||t.variants)}function Hi(t){const{initial:n,animate:s}=function(t,e){if(Ki(t)){const{initial:e,animate:n}=t;return{initial:!1===e||Wi(e)?e:void 0,animate:Wi(n)?n:void 0}}return!1!==t.inherit?e:{}}(t,e.useContext(zi));return e.useMemo(()=>({initial:n,animate:s}),[qi(n),qi(s)])}function qi(t){return Array.isArray(t)?t.join(\" \"):t}function Gi(t,e){return e.max===e.min?0:t/(e.max-e.min)*100}const Zi={correct:(t,e)=>{if(!e.target)return t;if(\"string\"==typeof t){if(!Be.test(t))return t;t=parseFloat(t)}return`${Gi(t,e.target.x)}% ${Gi(t,e.target.y)}%`}},Ji={correct:(t,{treeScale:e,projectionDelta:n})=>{const s=t,i=Ze.parse(t);if(i.length>5)return s;const o=Ze.createTransformer(t),r=\"number\"!=typeof i[0]?1:0,a=n.x.scale*e.x,l=n.y.scale*e.y;i[0+r]/=a,i[1+r]/=l;const u=tn(a,l,.5);return\"number\"==typeof i[2+r]&&(i[2+r]/=u),\"number\"==typeof i[3+r]&&(i[3+r]/=u),o(i)}},Qi={borderRadius:{...Zi,applyTo:[\"borderTopLeftRadius\",\"borderTopRightRadius\",\"borderBottomLeftRadius\",\"borderBottomRightRadius\"]},borderTopLeftRadius:Zi,borderTopRightRadius:Zi,borderBottomLeftRadius:Zi,borderBottomRightRadius:Zi,boxShadow:Ji};function to(t,{layout:e,layoutId:n}){return as.has(t)||t.startsWith(\"origin\")||(e||void 0!==n)&&(!!Qi[t]||\"opacity\"===t)}const eo={x:\"translateX\",y:\"translateY\",z:\"translateZ\",transformPerspective:\"perspective\"},no=rs.length;function so(t,e,n){const{style:s,vars:i,transformOrigin:o}=t;let r=!1,a=!1;for(const l in e){const t=e[l];if(as.has(l))r=!0;else if(ve(l))i[l]=t;else{const e=ni(t,Gs[l]);l.startsWith(\"origin\")?(a=!0,o[l]=e):s[l]=e}}if(e.transform||(r||n?s.transform=function(t,e,n){let s=\"\",i=!0;for(let o=0;o<no;o++){const r=rs[o],a=t[r];if(void 0===a)continue;let l=!0;if(l=\"number\"==typeof a?a===(r.startsWith(\"scale\")?1:0):0===parseFloat(a),!l||n){const t=ni(a,Gs[r]);l||(i=!1,s+=`${eo[r]||r}(${t}) `),n&&(e[r]=t)}}return s=s.trim(),n?s=n(e,i?\"\":s):i&&(s=\"none\"),s}(e,t.transform,n):s.transform&&(s.transform=\"none\")),a){const{originX:t=\"50%\",originY:e=\"50%\",originZ:n=0}=o;s.transformOrigin=`${t} ${e} ${n}`}}const io=()=>({style:{},transform:{},transformOrigin:{},vars:{}});function oo(t,e,n){for(const s in e)bi(e[s])||to(s,n)||(t[s]=e[s])}function ro(t,n){const s={};return oo(s,t.style||{},t),Object.assign(s,function({transformTemplate:t},n){return e.useMemo(()=>{const e={style:{},transform:{},transformOrigin:{},vars:{}};return so(e,n,t),Object.assign({},e.vars,e.style)},[n])}(t,n)),s}function ao(t,e){const n={},s=ro(t,e);return t.drag&&!1!==t.dragListener&&(n.draggable=!1,s.userSelect=s.WebkitUserSelect=s.WebkitTouchCallout=\"none\",s.touchAction=!0===t.drag?\"none\":\"pan-\"+(\"x\"===t.drag?\"y\":\"x\")),void 0===t.tabIndex&&(t.onTap||t.onTapStart||t.whileTap)&&(n.tabIndex=0),n.style=s,n}const lo={offset:\"stroke-dashoffset\",array:\"stroke-dasharray\"},uo={offset:\"strokeDashoffset\",array:\"strokeDasharray\"};const co=[\"offsetDistance\",\"offsetPath\",\"offsetRotate\",\"offsetAnchor\"];function ho(t,{attrX:e,attrY:n,attrScale:s,pathLength:i,pathSpacing:o=1,pathOffset:r=0,...a},l,u,c){if(so(t,a,u),l)return void(t.style.viewBox&&(t.attrs.viewBox=t.style.viewBox));t.attrs=t.style,t.style={};const{attrs:h,style:d}=t;h.transform&&(d.transform=h.transform,delete h.transform),(d.transform||h.transformOrigin)&&(d.transformOrigin=h.transformOrigin??\"50% 50%\",delete h.transformOrigin),d.transform&&(d.transformBox=(null==c?void 0:c.transformBox)??\"fill-box\",delete h.transformBox);for(const m of co)void 0!==h[m]&&(d[m]=h[m],delete h[m]);void 0!==e&&(h.x=e),void 0!==n&&(h.y=n),void 0!==s&&(h.scale=s),void 0!==i&&function(t,e,n=1,s=0,i=!0){t.pathLength=1;const o=i?lo:uo;t[o.offset]=Be.transform(-s);const r=Be.transform(e),a=Be.transform(n);t[o.array]=`${r} ${a}`}(h,i,o,r,!1)}const mo=()=>({style:{},transform:{},transformOrigin:{},vars:{},attrs:{}}),po=t=>\"string\"==typeof t&&\"svg\"===t.toLowerCase();function fo(t,n,s,i){const o=e.useMemo(()=>{const e={style:{},transform:{},transformOrigin:{},vars:{},attrs:{}};return ho(e,n,po(i),t.transformTemplate,t.style),{...e.attrs,style:{...e.style}}},[n]);if(t.style){const e={};oo(e,t.style,t),o.style={...e,...o.style}}return o}const go=[\"animate\",\"circle\",\"defs\",\"desc\",\"ellipse\",\"g\",\"image\",\"line\",\"filter\",\"marker\",\"mask\",\"metadata\",\"path\",\"pattern\",\"polygon\",\"polyline\",\"rect\",\"stop\",\"switch\",\"symbol\",\"svg\",\"text\",\"tspan\",\"use\",\"view\"];function yo(t){return\"string\"==typeof t&&!t.includes(\"-\")&&!!(go.indexOf(t)>-1||/[A-Z]/u.test(t))}function vo(t,n,s,{latestValues:i},o,r=!1,a){const l=(a??yo(t)?fo:ao)(n,i,o,t),u=function(t,e,n){const s={};for(const i in t)\"values\"===i&&\"object\"==typeof t.values||(Oi(i)||!0===n&&Fi(i)||!e&&!Fi(i)||t.draggable&&i.startsWith(\"onDrag\"))&&(s[i]=t[i]);return s}(n,\"string\"==typeof t,r),c=t!==e.Fragment?{...u,...l,ref:s}:{},{children:h}=n,d=e.useMemo(()=>bi(h)?h.get():h,[h]);return e.createElement(t,{...c,children:d})}function xo(t){const e=[{},{}];return null==t||t.values.forEach((t,n)=>{e[0][n]=t.get(),e[1][n]=t.getVelocity()}),e}function wo(t,e,n,s){if(\"function\"==typeof e){const[i,o]=xo(s);e=e(void 0!==n?n:t.custom,i,o)}if(\"string\"==typeof e&&(e=t.variants&&t.variants[e]),\"function\"==typeof e){const[i,o]=xo(s);e=e(void 0!==n?n:t.custom,i,o)}return e}function bo(t){return bi(t)?t.get():t}function So(t,e,n,s){const i={},o=s(t,{});for(const d in o)i[d]=bo(o[d]);let{initial:r,animate:a}=t;const l=Ki(t),u=Xi(t);e&&u&&!l&&!1!==t.inherit&&(void 0===r&&(r=e.initial),void 0===a&&(a=e.animate));let c=!!n&&!1===n.initial;c=c||!1===r;const h=c?a:r;if(h&&\"boolean\"!=typeof h&&!_i(h)){const e=Array.isArray(h)?h:[h];for(let n=0;n<e.length;n++){const s=wo(t,e[n]);if(s){const{transitionEnd:t,transition:e,...n}=s;for(const s in n){let t=n[s];if(Array.isArray(t)){t=t[c?t.length-1:0]}null!==t&&(i[s]=t)}for(const s in t)i[s]=t[s]}}}return i}const To=t=>(n,s)=>{const i=e.useContext(zi),o=e.useContext(Et),r=()=>function({scrapeMotionValuesFromProps:t,createRenderState:e},n,s,i){return{latestValues:So(n,s,i,t),renderState:e()}}(t,n,i,o);return s?r():kt(r)};function Po(t,e,n){var s;const{style:i}=t,o={};for(const r in i)(bi(i[r])||e.style&&bi(e.style[r])||to(r,t)||void 0!==(null==(s=null==n?void 0:n.getValue(r))?void 0:s.liveStyle))&&(o[r]=i[r]);return o}const ko=To({scrapeMotionValuesFromProps:Po,createRenderState:io});function jo(t,e,n){const s=Po(t,e,n);for(const i in t)if(bi(t[i])||bi(e[i])){s[-1!==rs.indexOf(i)?\"attr\"+i.charAt(0).toUpperCase()+i.substring(1):i]=t[i]}return s}const Mo=To({scrapeMotionValuesFromProps:jo,createRenderState:mo}),Eo=Symbol.for(\"motionComponentSymbol\");function Ao(t,n,s){const i=e.useRef(s);e.useInsertionEffect(()=>{i.current=s});const o=e.useRef(null);return e.useCallback(e=>{var s;e&&(null==(s=t.onMount)||s.call(t,e)),n&&(e?n.mount(e):n.unmount());const r=i.current;if(\"function\"==typeof r)if(e){const t=r(e);\"function\"==typeof t&&(o.current=t)}else o.current?(o.current(),o.current=null):r(e);else r&&(r.current=e)},[n])}const Co=t=>t.replace(/([a-z])([A-Z])/gu,\"$1-$2\").toLowerCase(),Vo=\"data-\"+Co(\"framerAppearId\"),Do=e.createContext({});function Ro(t){return t&&\"object\"==typeof t&&Object.prototype.hasOwnProperty.call(t,\"current\")}function Lo(t,n,s,i,o,r){var a,l;const{visualElement:u}=e.useContext(zi),c=e.useContext(Li),h=e.useContext(Et),d=e.useContext(Ti).reducedMotion,m=e.useRef(null);i=i||c.renderer,!m.current&&i&&(m.current=i(t,{visualState:n,parent:u,props:s,presenceContext:h,blockInitialAnimation:!!h&&!1===h.initial,reducedMotionConfig:d,isSVG:r}));const p=m.current,f=e.useContext(Do);!p||p.projection||!o||\"html\"!==p.type&&\"svg\"!==p.type||function(t,e,n,s){const{layoutId:i,layout:o,drag:r,dragConstraints:a,layoutScroll:l,layoutRoot:u,layoutCrossfade:c}=e;t.projection=new n(t.latestValues,e[\"data-framer-portal-id\"]?void 0:No(t.parent)),t.projection.setOptions({layoutId:i,layout:o,alwaysMeasureLayout:Boolean(r)||a&&Ro(a),visualElement:t,animationType:\"string\"==typeof o?o:\"both\",initialPromotionConfig:s,crossfade:c,layoutScroll:l,layoutRoot:u})}(m.current,s,o,f);const g=e.useRef(!1);e.useInsertionEffect(()=>{p&&g.current&&p.update(s,h)});const y=s[Vo],v=e.useRef(Boolean(y)&&!(null==(a=window.MotionHandoffIsComplete)?void 0:a.call(window,y))&&(null==(l=window.MotionHasOptimisedAnimation)?void 0:l.call(window,y)));return Mt(()=>{p&&(g.current=!0,window.MotionIsMounted=!0,p.updateFeatures(),p.scheduleRenderMicrotask(),v.current&&p.animationState&&p.animationState.animateChanges())}),e.useEffect(()=>{p&&(!v.current&&p.animationState&&p.animationState.animateChanges(),v.current&&(queueMicrotask(()=>{var t;null==(t=window.MotionHandoffMarkAsComplete)||t.call(window,y)}),v.current=!1),p.enteringChildren=void 0)}),p}function No(t){if(t)return!1!==t.options.allowProjection?t.projection:No(t.parent)}function Io(t,{forwardMotionProps:s=!1,type:i}={},o,r){o&&function(t){for(const e in t)Ii[e]={...Ii[e],...t[e]}}(o);const a=i?\"svg\"===i:yo(t),l=a?Mo:ko;function u(i,o){let u;const c={...e.useContext(Ti),...i,layoutId:Bo(i)},{isStatic:h}=c,d=Hi(i),m=l(i,h);if(!h&&jt){e.useContext(Li).strict;const n=function(t){const{drag:e,layout:n}=Ii;if(!e&&!n)return{};const s={...e,...n};return{MeasureLayout:(null==e?void 0:e.isEnabled(t))||(null==n?void 0:n.isEnabled(t))?s.MeasureLayout:void 0,ProjectionNode:s.ProjectionNode}}(c);u=n.MeasureLayout,d.visualElement=Lo(t,m,c,r,n.ProjectionNode,a)}return n.jsxs(zi.Provider,{value:d,children:[u&&d.visualElement?n.jsx(u,{visualElement:d.visualElement,...c}):null,vo(t,i,Ao(m,d.visualElement,o),m,h,s,a)]})}u.displayName=`motion.${\"string\"==typeof t?t:`create(${t.displayName??t.name??\"\"})`}`;const c=e.forwardRef(u);return c[Eo]=t,c}function Bo({layoutId:t}){const n=e.useContext(Pt).id;return n&&void 0!==t?n+\"-\"+t:t}function Fo(t,e){if(\"undefined\"==typeof Proxy)return Io;const n=new Map,s=(n,s)=>Io(n,s,t,e);return new Proxy((t,e)=>s(t,e),{get:(i,o)=>\"create\"===o?s:(n.has(o)||n.set(o,Io(o,void 0,t,e)),n.get(o))})}function Oo({top:t,left:e,right:n,bottom:s}){return{x:{min:e,max:n},y:{min:t,max:s}}}function Uo(t){return void 0===t||1===t}function zo({scale:t,scaleX:e,scaleY:n}){return!Uo(t)||!Uo(e)||!Uo(n)}function _o(t){return zo(t)||Wo(t)||t.z||t.rotate||t.rotateX||t.rotateY||t.skewX||t.skewY}function Wo(t){return $o(t.x)||$o(t.y)}function $o(t){return t&&\"0%\"!==t}function Yo(t,e,n){return n+e*(t-n)}function Ko(t,e,n,s,i){return void 0!==i&&(t=Yo(t,i,s)),Yo(t,n,s)+e}function Xo(t,e=0,n=1,s,i){t.min=Ko(t.min,e,n,s,i),t.max=Ko(t.max,e,n,s,i)}function Ho(t,{x:e,y:n}){Xo(t.x,e.translate,e.scale,e.originPoint),Xo(t.y,n.translate,n.scale,n.originPoint)}const qo=.999999999999,Go=1.0000000000001;function Zo(t,e){t.min=t.min+e,t.max=t.max+e}function Jo(t,e,n,s,i=.5){Xo(t,e,n,tn(t.min,t.max,i),s)}function Qo(t,e){Jo(t.x,e.x,e.scaleX,e.scale,e.originX),Jo(t.y,e.y,e.scaleY,e.scale,e.originY)}function tr(t,e){return Oo(function(t,e){if(!e)return t;const n=e({x:t.left,y:t.top}),s=e({x:t.right,y:t.bottom});return{top:n.y,left:n.x,bottom:s.y,right:s.x}}(t.getBoundingClientRect(),e))}const er=()=>({x:{min:0,max:0},y:{min:0,max:0}}),nr={current:null},sr={current:!1};const ir=new WeakMap;const or=[\"AnimationStart\",\"AnimationComplete\",\"Update\",\"BeforeLayoutMeasure\",\"LayoutMeasure\",\"LayoutAnimationStart\",\"LayoutAnimationComplete\"];class rr{scrapeMotionValuesFromProps(t,e,n){return{}}constructor({parent:t,props:e,presenceContext:n,reducedMotionConfig:s,blockInitialAnimation:i,visualState:o},r={}){this.current=null,this.children=new Set,this.isVariantNode=!1,this.isControllingVariants=!1,this.shouldReduceMotion=null,this.values=new Map,this.KeyframeResolver=vs,this.features={},this.valueSubscriptions=new Map,this.prevMotionValues={},this.events={},this.propEventSubscriptions={},this.notifyUpdate=()=>this.notify(\"Update\",this.latestValues),this.render=()=>{this.current&&(this.triggerBuild(),this.renderInstance(this.current,this.renderState,this.props.style,this.projection))},this.renderScheduledAt=0,this.scheduleRender=()=>{const t=ge.now();this.renderScheduledAt<t&&(this.renderScheduledAt=t,ce.render(this.render,!1,!0))};const{latestValues:a,renderState:l}=o;this.latestValues=a,this.baseTarget={...a},this.initialValues=e.initial?{...a}:{},this.renderState=l,this.parent=t,this.props=e,this.presenceContext=n,this.depth=t?t.depth+1:0,this.reducedMotionConfig=s,this.options=r,this.blockInitialAnimation=Boolean(i),this.isControllingVariants=Ki(e),this.isVariantNode=Xi(e),this.isVariantNode&&(this.variantChildren=new Set),this.manuallyAnimateOnMount=Boolean(t&&t.current);const{willChange:u,...c}=this.scrapeMotionValuesFromProps(e,{},this);for(const h in c){const t=c[h];void 0!==a[h]&&bi(t)&&t.set(a[h])}}mount(t){var e;this.current=t,ir.set(t,this),this.projection&&!this.projection.instance&&this.projection.mount(t),this.parent&&this.isVariantNode&&!this.isControllingVariants&&(this.removeFromVariantTree=this.parent.addVariantChild(this)),this.values.forEach((t,e)=>this.bindToMotionValue(e,t)),\"never\"===this.reducedMotionConfig?this.shouldReduceMotion=!1:\"always\"===this.reducedMotionConfig?this.shouldReduceMotion=!0:(sr.current||function(){if(sr.current=!0,jt)if(window.matchMedia){const t=window.matchMedia(\"(prefers-reduced-motion)\"),e=()=>nr.current=t.matches;t.addEventListener(\"change\",e),e()}else nr.current=!1}(),this.shouldReduceMotion=nr.current),null==(e=this.parent)||e.addChild(this),this.update(this.props,this.presenceContext)}unmount(){var t;this.projection&&this.projection.unmount(),he(this.notifyUpdate),he(this.render),this.valueSubscriptions.forEach(t=>t()),this.valueSubscriptions.clear(),this.removeFromVariantTree&&this.removeFromVariantTree(),null==(t=this.parent)||t.removeChild(this);for(const e in this.events)this.events[e].clear();for(const e in this.features){const t=this.features[e];t&&(t.unmount(),t.isMounted=!1)}this.current=null}addChild(t){this.children.add(t),this.enteringChildren??(this.enteringChildren=new Set),this.enteringChildren.add(t)}removeChild(t){this.children.delete(t),this.enteringChildren&&this.enteringChildren.delete(t)}bindToMotionValue(t,e){this.valueSubscriptions.has(t)&&this.valueSubscriptions.get(t)();const n=as.has(t);n&&this.onBindTransform&&this.onBindTransform();const s=e.on(\"change\",e=>{this.latestValues[t]=e,this.props.onUpdate&&ce.preRender(this.notifyUpdate),n&&this.projection&&(this.projection.isTransformDirty=!0),this.scheduleRender()});let i;window.MotionCheckAppearSync&&(i=window.MotionCheckAppearSync(this,t,e)),this.valueSubscriptions.set(t,()=>{s(),i&&i(),e.owner&&e.stop()})}sortNodePosition(t){return this.current&&this.sortInstanceNodePosition&&this.type===t.type?this.sortInstanceNodePosition(this.current,t.current):0}updateFeatures(){let t=\"animation\";for(t in Ii){const e=Ii[t];if(!e)continue;const{isEnabled:n,Feature:s}=e;if(!this.features[t]&&s&&n(this.props)&&(this.features[t]=new s(this)),this.features[t]){const e=this.features[t];e.isMounted?e.update():(e.mount(),e.isMounted=!0)}}}triggerBuild(){this.build(this.renderState,this.latestValues,this.props)}measureViewportBox(){return this.current?this.measureInstanceViewportBox(this.current,this.props):{x:{min:0,max:0},y:{min:0,max:0}}}getStaticValue(t){return this.latestValues[t]}setStaticValue(t,e){this.latestValues[t]=e}update(t,e){(t.transformTemplate||this.props.transformTemplate)&&this.scheduleRender(),this.prevProps=this.props,this.props=t,this.prevPresenceContext=this.presenceContext,this.presenceContext=e;for(let n=0;n<or.length;n++){const e=or[n];this.propEventSubscriptions[e]&&(this.propEventSubscriptions[e](),delete this.propEventSubscriptions[e]);const s=t[\"on\"+e];s&&(this.propEventSubscriptions[e]=this.on(e,s))}this.prevMotionValues=function(t,e,n){for(const s in e){const i=e[s],o=n[s];if(bi(i))t.addValue(s,i);else if(bi(o))t.addValue(s,oi(i,{owner:t}));else if(o!==i)if(t.hasValue(s)){const e=t.getValue(s);!0===e.liveStyle?e.jump(i):e.hasAnimated||e.set(i)}else{const e=t.getStaticValue(s);t.addValue(s,oi(void 0!==e?e:i,{owner:t}))}}for(const s in n)void 0===e[s]&&t.removeValue(s);return e}(this,this.scrapeMotionValuesFromProps(t,this.prevProps,this),this.prevMotionValues),this.handleChildMotionValue&&this.handleChildMotionValue()}getProps(){return this.props}getVariant(t){return this.props.variants?this.props.variants[t]:void 0}getDefaultTransition(){return this.props.transition}getTransformPagePoint(){return this.props.transformPagePoint}getClosestVariantNode(){return this.isVariantNode?this:this.parent?this.parent.getClosestVariantNode():void 0}addVariantChild(t){const e=this.getClosestVariantNode();if(e)return e.variantChildren&&e.variantChildren.add(t),()=>e.variantChildren.delete(t)}addValue(t,e){const n=this.values.get(t);e!==n&&(n&&this.removeValue(t),this.bindToMotionValue(t,e),this.values.set(t,e),this.latestValues[t]=e.get())}removeValue(t){this.values.delete(t);const e=this.valueSubscriptions.get(t);e&&(e(),this.valueSubscriptions.delete(t)),delete this.latestValues[t],this.removeValueFromRenderState(t,this.renderState)}hasValue(t){return this.values.has(t)}getValue(t,e){if(this.props.values&&this.props.values[t])return this.props.values[t];let n=this.values.get(t);return void 0===n&&void 0!==e&&(n=oi(null===e?void 0:e,{owner:this}),this.addValue(t,n)),n}readValue(t,e){let n=void 0===this.latestValues[t]&&this.current?this.getBaseTargetFromProps(this.props,t)??this.readValueFromInstance(this.current,t,this.options):this.latestValues[t];var s;return null!=n&&(\"string\"==typeof n&&(Rt(n)||Nt(n))?n=parseFloat(n):(s=n,!Si.find(zs(s))&&Ze.test(e)&&(n=Qs(t,e))),this.setBaseTarget(t,bi(n)?n.get():n)),bi(n)?n.get():n}setBaseTarget(t,e){this.baseTarget[t]=e}getBaseTarget(t){var e;const{initial:n}=this.props;let s;if(\"string\"==typeof n||\"object\"==typeof n){const i=wo(this.props,n,null==(e=this.presenceContext)?void 0:e.custom);i&&(s=i[t])}if(n&&void 0!==s)return s;const i=this.getBaseTargetFromProps(this.props,t);return void 0===i||bi(i)?void 0!==this.initialValues[t]&&void 0===s?void 0:this.baseTarget[t]:i}on(t,e){return this.events[t]||(this.events[t]=new zt),this.events[t].add(e)}notify(t,...e){this.events[t]&&this.events[t].notify(...e)}scheduleRenderMicrotask(){ri.render(this.render)}}class ar extends rr{constructor(){super(...arguments),this.KeyframeResolver=ei}sortInstanceNodePosition(t,e){return 2&t.compareDocumentPosition(e)?1:-1}getBaseTargetFromProps(t,e){return t.style?t.style[e]:void 0}removeValueFromRenderState(t,{vars:e,style:n}){delete e[t],delete n[t]}handleChildMotionValue(){this.childSubscription&&(this.childSubscription(),delete this.childSubscription);const{children:t}=this.props;bi(t)&&(this.childSubscription=t.on(\"change\",t=>{this.current&&(this.current.textContent=`${t}`)}))}}function lr(t,{style:e,vars:n},s,i){const o=t.style;let r;for(r in e)o[r]=e[r];for(r in null==i||i.applyProjectionStyles(o,s),n)o.setProperty(r,n[r])}class ur extends ar{constructor(){super(...arguments),this.type=\"html\",this.renderInstance=lr}readValueFromInstance(t,e){var n,s;if(as.has(e))return(null==(n=this.projection)?void 0:n.isProjecting)?ss(e):((t,e)=>{const{transform:n=\"none\"}=getComputedStyle(t);return is(n,e)})(t,e);{const n=(s=t,window.getComputedStyle(s)),i=(ve(e)?n.getPropertyValue(e):n[e])||0;return\"string\"==typeof i?i.trim():i}}measureInstanceViewportBox(t,{transformPagePoint:e}){return tr(t,e)}build(t,e,n){so(t,e,n.transformTemplate)}scrapeMotionValuesFromProps(t,e,n){return Po(t,e,n)}}const cr=new Set([\"baseFrequency\",\"diffuseConstant\",\"kernelMatrix\",\"kernelUnitLength\",\"keySplines\",\"keyTimes\",\"limitingConeAngle\",\"markerHeight\",\"markerWidth\",\"numOctaves\",\"targetX\",\"targetY\",\"surfaceScale\",\"specularConstant\",\"specularExponent\",\"stdDeviation\",\"tableValues\",\"viewBox\",\"gradientTransform\",\"pathLength\",\"startOffset\",\"textLength\",\"lengthAdjust\"]);class hr extends ar{constructor(){super(...arguments),this.type=\"svg\",this.isSVGTag=!1,this.measureInstanceViewportBox=er}getBaseTargetFromProps(t,e){return t[e]}readValueFromInstance(t,e){if(as.has(e)){const t=Js(e);return t&&t.default||0}return e=cr.has(e)?e:Co(e),t.getAttribute(e)}scrapeMotionValuesFromProps(t,e,n){return jo(t,e,n)}build(t,e,n){ho(t,e,this.isSVGTag,n.transformTemplate,n.style)}renderInstance(t,e,n,s){!function(t,e,n,s){lr(t,e,void 0,s);for(const i in e.attrs)t.setAttribute(cr.has(i)?i:Co(i),e.attrs[i])}(t,e,0,s)}mount(t){this.isSVGTag=po(t.tagName),super.mount(t)}}const dr=(t,n)=>n.isSVG??yo(t)?new hr(n):new ur(n,{allowProjection:t!==e.Fragment});function mr(t,e,n){const s=t.getProps();return wo(s,e,void 0!==n?n:s.custom,t)}const pr=t=>Array.isArray(t);function fr(t,e,n){t.hasValue(e)?t.getValue(e).set(n):t.addValue(e,oi(n))}function gr(t){return pr(t)?t[t.length-1]||0:t}function yr(t,e){const n=t.getValue(\"willChange\");if(s=n,Boolean(bi(s)&&s.add))return n.add(e);if(!n&&Dt.WillChange){const n=new Dt.WillChange(\"auto\");t.addValue(\"willChange\",n),n.add(e)}var s}function vr(t){return t.props[Vo]}const xr=t=>null!==t;const wr={type:\"spring\",stiffness:500,damping:25,restSpeed:10},br={type:\"keyframes\",duration:.8},Sr={type:\"keyframes\",ease:[.25,.1,.35,1],duration:.3},Tr=(t,{keyframes:e})=>e.length>2?br:as.has(t)?t.startsWith(\"scale\")?{type:\"spring\",stiffness:550,damping:0===e[1]?2*Math.sqrt(550):30,restSpeed:10}:wr:Sr;const Pr=(t,e,n,s={},i,o)=>r=>{const a=Os(s,t)||{},l=a.delay||s.delay||0;let{elapsed:u=0}=s;u-=_t(l);const c={keyframes:Array.isArray(n)?n:[null,n],ease:\"easeOut\",velocity:e.getVelocity(),...a,delay:-u,onUpdate:t=>{e.set(t),a.onUpdate&&a.onUpdate(t)},onComplete:()=>{r(),a.onComplete&&a.onComplete()},name:t,motionValue:e,element:o?void 0:i};(function({when:t,delay:e,delayChildren:n,staggerChildren:s,staggerDirection:i,repeat:o,repeatType:r,repeatDelay:a,from:l,elapsed:u,...c}){return!!Object.keys(c).length})(a)||Object.assign(c,Tr(t,c)),c.duration&&(c.duration=_t(c.duration)),c.repeatDelay&&(c.repeatDelay=_t(c.repeatDelay)),void 0!==c.from&&(c.keyframes[0]=c.from);let h=!1;if((!1===c.type||0===c.duration&&!c.repeatDelay)&&(Rs(c),0===c.delay&&(h=!0)),(Dt.instantAnimations||Dt.skipAnimations)&&(h=!0,Rs(c),c.delay=0),c.allowFlatten=!a.type&&!a.ease,h&&!o&&void 0!==e.get()){const t=function(t,{repeat:e,repeatType:n=\"loop\"}){const s=t.filter(xr);return s[e&&\"loop\"!==n&&e%2==1?0:s.length-1]}(c.keyframes,a);if(void 0!==t)return void ce.update(()=>{c.onUpdate(t),c.onComplete()})}return a.isSync?new qn(c):new Is(c)};function kr({protectedKeys:t,needsAnimating:e},n){const s=t.hasOwnProperty(n)&&!0!==e[n];return e[n]=!1,s}function jr(t,e,{delay:n=0,transitionOverride:s,type:i}={}){let{transition:o=t.getDefaultTransition(),transitionEnd:r,...a}=e;s&&(o=s);const l=[],u=i&&t.animationState&&t.animationState.getState()[i];for(const c in a){const e=t.getValue(c,t.latestValues[c]??null),s=a[c];if(void 0===s||u&&kr(u,c))continue;const i={delay:n,...Os(o||{},c)},r=e.get();if(void 0!==r&&!e.isAnimating&&!Array.isArray(s)&&s===r&&!i.velocity)continue;let h=!1;if(window.MotionHandoffAnimation){const e=vr(t);if(e){const t=window.MotionHandoffAnimation(e,c,ce);null!==t&&(i.startTime=t,h=!0)}}yr(t,c),e.start(Pr(c,e,s,t.shouldReduceMotion&&Us.has(c)?{type:!1}:i,t,h));const d=e.animation;d&&l.push(d)}return r&&Promise.all(l).then(()=>{ce.update(()=>{r&&function(t,e){const n=mr(t,e);let{transitionEnd:s={},transition:i={},...o}=n||{};o={...o,...s};for(const r in o)fr(t,r,gr(o[r]))}(t,r)})}),l}function Mr(t,e,n,s=0,i=1){const o=Array.from(t).sort((t,e)=>t.sortNodePosition(e)).indexOf(e),r=t.size,a=(r-1)*s;return\"function\"==typeof n?n(o,r):1===i?o*s:a-o*s}function Er(t,e,n={}){var s;const i=mr(t,e,\"exit\"===n.type?null==(s=t.presenceContext)?void 0:s.custom:void 0);let{transition:o=t.getDefaultTransition()||{}}=i||{};n.transitionOverride&&(o=n.transitionOverride);const r=i?()=>Promise.all(jr(t,i,n)):()=>Promise.resolve(),a=t.variantChildren&&t.variantChildren.size?(s=0)=>{const{delayChildren:i=0,staggerChildren:r,staggerDirection:a}=o;return function(t,e,n=0,s=0,i=0,o=1,r){const a=[];for(const l of t.variantChildren)l.notify(\"AnimationStart\",e),a.push(Er(l,e,{...r,delay:n+(\"function\"==typeof s?0:s)+Mr(t.variantChildren,l,s,i,o)}).then(()=>l.notify(\"AnimationComplete\",e)));return Promise.all(a)}(t,e,s,i,r,a,n)}:()=>Promise.resolve(),{when:l}=o;if(l){const[t,e]=\"beforeChildren\"===l?[r,a]:[a,r];return t().then(()=>e())}return Promise.all([r(),a(n.delay)])}function Ar(t,e){if(!Array.isArray(e))return!1;const n=e.length;if(n!==t.length)return!1;for(let s=0;s<n;s++)if(e[s]!==t[s])return!1;return!0}const Cr=Yi.length;function Vr(t){if(!t)return;if(!t.isControllingVariants){const e=t.parent&&Vr(t.parent)||{};return void 0!==t.props.initial&&(e.initial=t.props.initial),e}const e={};for(let n=0;n<Cr;n++){const s=Yi[n],i=t.props[s];(Wi(i)||!1===i)&&(e[s]=i)}return e}const Dr=[...$i].reverse(),Rr=$i.length;function Lr(t){return e=>Promise.all(e.map(({animation:e,options:n})=>function(t,e,n={}){let s;if(t.notify(\"AnimationStart\",e),Array.isArray(e)){const i=e.map(e=>Er(t,e,n));s=Promise.all(i)}else if(\"string\"==typeof e)s=Er(t,e,n);else{const i=\"function\"==typeof e?mr(t,e,n.custom):e;s=Promise.all(jr(t,i,n))}return s.then(()=>{t.notify(\"AnimationComplete\",e)})}(t,e,n)))}function Nr(t){let e=Lr(t),n=Fr(),s=!0;const i=e=>(n,s)=>{var i;const o=mr(t,s,\"exit\"===e?null==(i=t.presenceContext)?void 0:i.custom:void 0);if(o){const{transition:t,transitionEnd:e,...s}=o;n={...n,...s,...e}}return n};function o(o){const{props:r}=t,a=Vr(t.parent)||{},l=[],u=new Set;let c={},h=1/0;for(let e=0;e<Rr;e++){const d=Dr[e],m=n[d],p=void 0!==r[d]?r[d]:a[d],f=Wi(p),g=d===o?m.isActive:null;!1===g&&(h=e);let y=p===a[d]&&p!==r[d]&&f;if(y&&s&&t.manuallyAnimateOnMount&&(y=!1),m.protectedKeys={...c},!m.isActive&&null===g||!p&&!m.prevProp||_i(p)||\"boolean\"==typeof p)continue;const v=Ir(m.prevProp,p);let x=v||d===o&&m.isActive&&!y&&f||e>h&&f,w=!1;const b=Array.isArray(p)?p:[p];let S=b.reduce(i(d),{});!1===g&&(S={});const{prevResolvedValues:T={}}=m,P={...T,...S},k=e=>{x=!0,u.has(e)&&(w=!0,u.delete(e)),m.needsAnimating[e]=!0;const n=t.getValue(e);n&&(n.liveStyle=!1)};for(const t in P){const e=S[t],n=T[t];if(c.hasOwnProperty(t))continue;let s=!1;s=pr(e)&&pr(n)?!Ar(e,n):e!==n,s?null!=e?k(t):u.add(t):void 0!==e&&u.has(t)?k(t):m.protectedKeys[t]=!0}m.prevProp=p,m.prevResolvedValues=S,m.isActive&&(c={...c,...S}),s&&t.blockInitialAnimation&&(x=!1);const j=y&&v;x&&(!j||w)&&l.push(...b.map(e=>{const n={type:d};if(\"string\"==typeof e&&s&&!j&&t.manuallyAnimateOnMount&&t.parent){const{parent:s}=t,i=mr(s,e);if(s.enteringChildren&&i){const{delayChildren:e}=i.transition||{};n.delay=Mr(s.enteringChildren,t,e)}}return{animation:e,options:n}}))}if(u.size){const e={};if(\"boolean\"!=typeof r.initial){const n=mr(t,Array.isArray(r.initial)?r.initial[0]:r.initial);n&&n.transition&&(e.transition=n.transition)}u.forEach(n=>{const s=t.getBaseTarget(n),i=t.getValue(n);i&&(i.liveStyle=!0),e[n]=s??null}),l.push({animation:e})}let d=Boolean(l.length);return!s||!1!==r.initial&&r.initial!==r.animate||t.manuallyAnimateOnMount||(d=!1),s=!1,d?e(l):Promise.resolve()}return{animateChanges:o,setActive:function(e,s){var i;if(n[e].isActive===s)return Promise.resolve();null==(i=t.variantChildren)||i.forEach(t=>{var n;return null==(n=t.animationState)?void 0:n.setActive(e,s)}),n[e].isActive=s;const r=o(e);for(const t in n)n[t].protectedKeys={};return r},setAnimateFunction:function(n){e=n(t)},getState:()=>n,reset:()=>{n=Fr()}}}function Ir(t,e){return\"string\"==typeof e?e!==t:!!Array.isArray(e)&&!Ar(e,t)}function Br(t=!1){return{isActive:t,protectedKeys:{},needsAnimating:{},prevResolvedValues:{}}}function Fr(){return{animate:Br(!0),whileInView:Br(),whileHover:Br(),whileTap:Br(),whileDrag:Br(),whileFocus:Br(),exit:Br()}}class Or{constructor(t){this.isMounted=!1,this.node=t}update(){}}let Ur=0;const zr={animation:{Feature:class extends Or{constructor(t){super(t),t.animationState||(t.animationState=Nr(t))}updateAnimationControlsSubscription(){const{animate:t}=this.node.getProps();_i(t)&&(this.unmountControls=t.subscribe(this.node))}mount(){this.updateAnimationControlsSubscription()}update(){const{animate:t}=this.node.getProps(),{animate:e}=this.node.prevProps||{};t!==e&&this.updateAnimationControlsSubscription()}unmount(){var t;this.node.animationState.reset(),null==(t=this.unmountControls)||t.call(this)}}},exit:{Feature:class extends Or{constructor(){super(...arguments),this.id=Ur++}update(){if(!this.node.presenceContext)return;const{isPresent:t,onExitComplete:e}=this.node.presenceContext,{isPresent:n}=this.node.prevPresenceContext||{};if(!this.node.animationState||t===n)return;const s=this.node.animationState.setActive(\"exit\",!t);e&&!t&&s.then(()=>{e(this.id)})}mount(){const{register:t,onExitComplete:e}=this.node.presenceContext||{};e&&e(this.id),t&&(this.unmount=t(this.id))}unmount(){}}}};function _r(t,e,n,s={passive:!0}){return t.addEventListener(e,n,s),()=>t.removeEventListener(e,n)}function Wr(t){return{point:{x:t.pageX,y:t.pageY}}}function $r(t,e,n,s){return _r(t,e,(t=>e=>di(e)&&t(e,Wr(e)))(n),s)}function Yr(t){return t.max-t.min}function Kr(t,e,n,s=.5){t.origin=s,t.originPoint=tn(e.min,e.max,t.origin),t.scale=Yr(n)/Yr(e),t.translate=tn(n.min,n.max,t.origin)-t.originPoint,(t.scale>=.9999&&t.scale<=1.0001||isNaN(t.scale))&&(t.scale=1),(t.translate>=-.01&&t.translate<=.01||isNaN(t.translate))&&(t.translate=0)}function Xr(t,e,n,s){Kr(t.x,e.x,n.x,s?s.originX:void 0),Kr(t.y,e.y,n.y,s?s.originY:void 0)}function Hr(t,e,n){t.min=n.min+e.min,t.max=t.min+Yr(e)}function qr(t,e,n){t.min=e.min-n.min,t.max=t.min+Yr(e)}function Gr(t,e,n){qr(t.x,e.x,n.x),qr(t.y,e.y,n.y)}function Zr(t){return[t(\"x\"),t(\"y\")]}const Jr=({current:t})=>t?t.ownerDocument.defaultView:null,Qr=(t,e)=>Math.abs(t-e);const ta=new Set([\"auto\",\"scroll\"]);class ea{constructor(t,e,{transformPagePoint:n,contextWindow:s=window,dragSnapToOrigin:i=!1,distanceThreshold:o=3,element:r}={}){if(this.startEvent=null,this.lastMoveEvent=null,this.lastMoveEventInfo=null,this.handlers={},this.contextWindow=window,this.scrollPositions=new Map,this.removeScrollListeners=null,this.onElementScroll=t=>{this.handleScroll(t.target)},this.onWindowScroll=()=>{this.handleScroll(window)},this.updatePoint=()=>{if(!this.lastMoveEvent||!this.lastMoveEventInfo)return;const t=ia(this.lastMoveEventInfo,this.history),e=null!==this.startEvent,n=function(t,e){const n=Qr(t.x,e.x),s=Qr(t.y,e.y);return Math.sqrt(n**2+s**2)}(t.offset,{x:0,y:0})>=this.distanceThreshold;if(!e&&!n)return;const{point:s}=t,{timestamp:i}=de;this.history.push({...s,timestamp:i});const{onStart:o,onMove:r}=this.handlers;e||(o&&o(this.lastMoveEvent,t),this.startEvent=this.lastMoveEvent),r&&r(this.lastMoveEvent,t)},this.handlePointerMove=(t,e)=>{this.lastMoveEvent=t,this.lastMoveEventInfo=na(e,this.transformPagePoint),ce.update(this.updatePoint,!0)},this.handlePointerUp=(t,e)=>{this.end();const{onEnd:n,onSessionEnd:s,resumeAnimation:i}=this.handlers;if(!this.dragSnapToOrigin&&this.startEvent||i&&i(),!this.lastMoveEvent||!this.lastMoveEventInfo)return;const o=ia(\"pointercancel\"===t.type?this.lastMoveEventInfo:na(e,this.transformPagePoint),this.history);this.startEvent&&n&&n(t,o),s&&s(t,o)},!di(t))return;this.dragSnapToOrigin=i,this.handlers=e,this.transformPagePoint=n,this.distanceThreshold=o,this.contextWindow=s||window;const a=na(Wr(t),this.transformPagePoint),{point:l}=a,{timestamp:u}=de;this.history=[{...l,timestamp:u}];const{onSessionStart:c}=e;c&&c(t,ia(a,this.history)),this.removeListeners=Ot($r(this.contextWindow,\"pointermove\",this.handlePointerMove),$r(this.contextWindow,\"pointerup\",this.handlePointerUp),$r(this.contextWindow,\"pointercancel\",this.handlePointerUp)),r&&this.startScrollTracking(r)}startScrollTracking(t){let e=t.parentElement;for(;e;){const t=getComputedStyle(e);(ta.has(t.overflowX)||ta.has(t.overflowY))&&this.scrollPositions.set(e,{x:e.scrollLeft,y:e.scrollTop}),e=e.parentElement}this.scrollPositions.set(window,{x:window.scrollX,y:window.scrollY}),window.addEventListener(\"scroll\",this.onElementScroll,{capture:!0,passive:!0}),window.addEventListener(\"scroll\",this.onWindowScroll,{passive:!0}),this.removeScrollListeners=()=>{window.removeEventListener(\"scroll\",this.onElementScroll,{capture:!0}),window.removeEventListener(\"scroll\",this.onWindowScroll)}}handleScroll(t){const e=this.scrollPositions.get(t);if(!e)return;const n=t===window,s=n?{x:window.scrollX,y:window.scrollY}:{x:t.scrollLeft,y:t.scrollTop},i=s.x-e.x,o=s.y-e.y;0===i&&0===o||(n?this.lastMoveEventInfo&&(this.lastMoveEventInfo.point.x+=i,this.lastMoveEventInfo.point.y+=o):this.history.length>0&&(this.history[0].x-=i,this.history[0].y-=o),this.scrollPositions.set(t,s),ce.update(this.updatePoint,!0))}updateHandlers(t){this.handlers=t}end(){this.removeListeners&&this.removeListeners(),this.removeScrollListeners&&this.removeScrollListeners(),this.scrollPositions.clear(),he(this.updatePoint)}}function na(t,e){return e?{point:e(t.point)}:t}function sa(t,e){return{x:t.x-e.x,y:t.y-e.y}}function ia({point:t},e){return{point:t,delta:sa(t,ra(e)),offset:sa(t,oa(e)),velocity:aa(e,.1)}}function oa(t){return t[0]}function ra(t){return t[t.length-1]}function aa(t,e){if(t.length<2)return{x:0,y:0};let n=t.length-1,s=null;const i=ra(t);for(;n>=0&&(s=t[n],!(i.timestamp-s.timestamp>_t(e)));)n--;if(!s)return{x:0,y:0};const o=Wt(i.timestamp-s.timestamp);if(0===o)return{x:0,y:0};const r={x:(i.x-s.x)/o,y:(i.y-s.y)/o};return r.x===1/0&&(r.x=0),r.y===1/0&&(r.y=0),r}function la(t,e,n){return{min:void 0!==e?t.min+e:void 0,max:void 0!==n?t.max+n-(t.max-t.min):void 0}}function ua(t,e){let n=e.min-t.min,s=e.max-t.max;return e.max-e.min<t.max-t.min&&([n,s]=[s,n]),{min:n,max:s}}const ca=.35;function ha(t,e,n){return{min:da(t,e),max:da(t,n)}}function da(t,e){return\"number\"==typeof t?t:t[e]||0}const ma=new WeakMap;class pa{constructor(t){this.openDragLock=null,this.isDragging=!1,this.currentDirection=null,this.originPoint={x:0,y:0},this.constraints=!1,this.hasMutatedConstraints=!1,this.elastic={x:{min:0,max:0},y:{min:0,max:0}},this.latestPointerEvent=null,this.latestPanInfo=null,this.visualElement=t}start(t,{snapToCursor:e=!1,distanceThreshold:n}={}){const{presenceContext:s}=this.visualElement;if(s&&!1===s.isPresent)return;const{dragSnapToOrigin:i}=this.getProps();this.panSession=new ea(t,{onSessionStart:t=>{e?(this.stopAnimation(),this.snapToCursor(Wr(t).point)):this.pauseAnimation()},onStart:(t,e)=>{this.stopAnimation();const{drag:n,dragPropagation:s,onDragStart:i}=this.getProps();if(n&&!s&&(this.openDragLock&&this.openDragLock(),this.openDragLock=\"x\"===(o=n)||\"y\"===o?ai[o]?null:(ai[o]=!0,()=>{ai[o]=!1}):ai.x||ai.y?null:(ai.x=ai.y=!0,()=>{ai.x=ai.y=!1}),!this.openDragLock))return;var o;this.latestPointerEvent=t,this.latestPanInfo=e,this.isDragging=!0,this.currentDirection=null,this.resolveConstraints(),this.visualElement.projection&&(this.visualElement.projection.isAnimationBlocked=!0,this.visualElement.projection.target=void 0),Zr(t=>{let e=this.getAxisMotionValue(t).get()||0;if(Ie.test(e)){const{projection:n}=this.visualElement;if(n&&n.layout){const s=n.layout.layoutBox[t];if(s){e=Yr(s)*(parseFloat(e)/100)}}}this.originPoint[t]=e}),i&&ce.postRender(()=>i(t,e)),yr(this.visualElement,\"transform\");const{animationState:r}=this.visualElement;r&&r.setActive(\"whileDrag\",!0)},onMove:(t,e)=>{this.latestPointerEvent=t,this.latestPanInfo=e;const{dragPropagation:n,dragDirectionLock:s,onDirectionLock:i,onDrag:o}=this.getProps();if(!n&&!this.openDragLock)return;const{offset:r}=e;if(s&&null===this.currentDirection)return this.currentDirection=function(t,e=10){let n=null;Math.abs(t.y)>e?n=\"y\":Math.abs(t.x)>e&&(n=\"x\");return n}(r),void(null!==this.currentDirection&&i&&i(this.currentDirection));this.updateAxis(\"x\",e.point,r),this.updateAxis(\"y\",e.point,r),this.visualElement.render(),o&&o(t,e)},onSessionEnd:(t,e)=>{this.latestPointerEvent=t,this.latestPanInfo=e,this.stop(t,e),this.latestPointerEvent=null,this.latestPanInfo=null},resumeAnimation:()=>Zr(t=>{var e;return\"paused\"===this.getAnimationState(t)&&(null==(e=this.getAxisMotionValue(t).animation)?void 0:e.play())})},{transformPagePoint:this.visualElement.getTransformPagePoint(),dragSnapToOrigin:i,distanceThreshold:n,contextWindow:Jr(this.visualElement),element:this.visualElement.current})}stop(t,e){const n=t||this.latestPointerEvent,s=e||this.latestPanInfo,i=this.isDragging;if(this.cancel(),!i||!s||!n)return;const{velocity:o}=s;this.startAnimation(o);const{onDragEnd:r}=this.getProps();r&&ce.postRender(()=>r(n,s))}cancel(){this.isDragging=!1;const{projection:t,animationState:e}=this.visualElement;t&&(t.isAnimationBlocked=!1),this.panSession&&this.panSession.end(),this.panSession=void 0;const{dragPropagation:n}=this.getProps();!n&&this.openDragLock&&(this.openDragLock(),this.openDragLock=null),e&&e.setActive(\"whileDrag\",!1)}updateAxis(t,e,n){const{drag:s}=this.getProps();if(!n||!fa(t,s,this.currentDirection))return;const i=this.getAxisMotionValue(t);let o=this.originPoint[t]+n[t];this.constraints&&this.constraints[t]&&(o=function(t,{min:e,max:n},s){return void 0!==e&&t<e?t=s?tn(e,t,s.min):Math.max(t,e):void 0!==n&&t>n&&(t=s?tn(n,t,s.max):Math.min(t,n)),t}(o,this.constraints[t],this.elastic[t])),i.set(o)}resolveConstraints(){var t;const{dragConstraints:e,dragElastic:n}=this.getProps(),s=this.visualElement.projection&&!this.visualElement.projection.layout?this.visualElement.projection.measure(!1):null==(t=this.visualElement.projection)?void 0:t.layout,i=this.constraints;e&&Ro(e)?this.constraints||(this.constraints=this.resolveRefConstraints()):this.constraints=!(!e||!s)&&function(t,{top:e,left:n,bottom:s,right:i}){return{x:la(t.x,n,i),y:la(t.y,e,s)}}(s.layoutBox,e),this.elastic=function(t=ca){return!1===t?t=0:!0===t&&(t=ca),{x:ha(t,\"left\",\"right\"),y:ha(t,\"top\",\"bottom\")}}(n),i!==this.constraints&&s&&this.constraints&&!this.hasMutatedConstraints&&Zr(t=>{!1!==this.constraints&&this.getAxisMotionValue(t)&&(this.constraints[t]=function(t,e){const n={};return void 0!==e.min&&(n.min=e.min-t.min),void 0!==e.max&&(n.max=e.max-t.min),n}(s.layoutBox[t],this.constraints[t]))})}resolveRefConstraints(){const{dragConstraints:t,onMeasureDragConstraints:e}=this.getProps();if(!t||!Ro(t))return!1;const n=t.current,{projection:s}=this.visualElement;if(!s||!s.layout)return!1;const i=function(t,e,n){const s=tr(t,n),{scroll:i}=e;return i&&(Zo(s.x,i.offset.x),Zo(s.y,i.offset.y)),s}(n,s.root,this.visualElement.getTransformPagePoint());let o=function(t,e){return{x:ua(t.x,e.x),y:ua(t.y,e.y)}}(s.layout.layoutBox,i);if(e){const t=e(function({x:t,y:e}){return{top:e.min,right:t.max,bottom:e.max,left:t.min}}(o));this.hasMutatedConstraints=!!t,t&&(o=Oo(t))}return o}startAnimation(t){const{drag:e,dragMomentum:n,dragElastic:s,dragTransition:i,dragSnapToOrigin:o,onDragTransitionEnd:r}=this.getProps(),a=this.constraints||{},l=Zr(r=>{if(!fa(r,e,this.currentDirection))return;let l=a&&a[r]||{};o&&(l={min:0,max:0});const u=s?200:1e6,c=s?40:1e7,h={type:\"inertia\",velocity:n?t[r]:0,bounceStiffness:u,bounceDamping:c,timeConstant:750,restDelta:1,restSpeed:10,...i,...l};return this.startAxisValueAnimation(r,h)});return Promise.all(l).then(r)}startAxisValueAnimation(t,e){const n=this.getAxisMotionValue(t);return yr(this.visualElement,t),n.start(Pr(t,n,0,e,this.visualElement,!1))}stopAnimation(){Zr(t=>this.getAxisMotionValue(t).stop())}pauseAnimation(){Zr(t=>{var e;return null==(e=this.getAxisMotionValue(t).animation)?void 0:e.pause()})}getAnimationState(t){var e;return null==(e=this.getAxisMotionValue(t).animation)?void 0:e.state}getAxisMotionValue(t){const e=`_drag${t.toUpperCase()}`,n=this.visualElement.getProps(),s=n[e];return s||this.visualElement.getValue(t,(n.initial?n.initial[t]:void 0)||0)}snapToCursor(t){Zr(e=>{const{drag:n}=this.getProps();if(!fa(e,n,this.currentDirection))return;const{projection:s}=this.visualElement,i=this.getAxisMotionValue(e);if(s&&s.layout){const{min:n,max:o}=s.layout.layoutBox[e],r=i.get()||0;i.set(t[e]-tn(n,o,.5)+r)}})}scalePositionWithinConstraints(){if(!this.visualElement.current)return;const{drag:t,dragConstraints:e}=this.getProps(),{projection:n}=this.visualElement;if(!Ro(e)||!n||!this.constraints)return;this.stopAnimation();const s={x:0,y:0};Zr(t=>{const e=this.getAxisMotionValue(t);if(e&&!1!==this.constraints){const n=e.get();s[t]=function(t,e){let n=.5;const s=Yr(t),i=Yr(e);return i>s?n=Ut(e.min,e.max-s,t.min):s>i&&(n=Ut(t.min,t.max-i,e.min)),Vt(0,1,n)}({min:n,max:n},this.constraints[t])}});const{transformTemplate:i}=this.visualElement.getProps();this.visualElement.current.style.transform=i?i({},\"\"):\"none\",n.root&&n.root.updateScroll(),n.updateLayout(),this.resolveConstraints(),Zr(e=>{if(!fa(e,t,null))return;const n=this.getAxisMotionValue(e),{min:i,max:o}=this.constraints[e];n.set(tn(i,o,s[e]))})}addListeners(){if(!this.visualElement.current)return;ma.set(this.visualElement,this);const t=$r(this.visualElement.current,\"pointerdown\",t=>{const{drag:e,dragListener:n=!0}=this.getProps();e&&n&&!pi(t.target)&&this.start(t)}),e=()=>{const{dragConstraints:t}=this.getProps();Ro(t)&&t.current&&(this.constraints=this.resolveRefConstraints())},{projection:n}=this.visualElement,s=n.addEventListener(\"measure\",e);n&&!n.layout&&(n.root&&n.root.updateScroll(),n.updateLayout()),ce.read(e);const i=_r(window,\"resize\",()=>this.scalePositionWithinConstraints()),o=n.addEventListener(\"didUpdate\",({delta:t,hasLayoutChanged:e})=>{this.isDragging&&e&&(Zr(e=>{const n=this.getAxisMotionValue(e);n&&(this.originPoint[e]+=t[e].translate,n.set(n.get()+t[e].translate))}),this.visualElement.render())});return()=>{i(),t(),s(),o&&o()}}getProps(){const t=this.visualElement.getProps(),{drag:e=!1,dragDirectionLock:n=!1,dragPropagation:s=!1,dragConstraints:i=!1,dragElastic:o=ca,dragMomentum:r=!0}=t;return{...t,drag:e,dragDirectionLock:n,dragPropagation:s,dragConstraints:i,dragElastic:o,dragMomentum:r}}}function fa(t,e,n){return!(!0!==e&&e!==t||null!==n&&n!==t)}const ga=t=>(e,n)=>{t&&ce.postRender(()=>t(e,n))};const ya={hasAnimatedSinceResize:!0,hasEverUpdated:!1};let va=!1;class xa extends e.Component{componentDidMount(){const{visualElement:t,layoutGroup:e,switchLayoutGroup:n,layoutId:s}=this.props,{projection:i}=t;i&&(e.group&&e.group.add(i),n&&n.register&&s&&n.register(i),va&&i.root.didUpdate(),i.addEventListener(\"animationComplete\",()=>{this.safeToRemove()}),i.setOptions({...i.options,onExitComplete:()=>this.safeToRemove()})),ya.hasEverUpdated=!0}getSnapshotBeforeUpdate(t){const{layoutDependency:e,visualElement:n,drag:s,isPresent:i}=this.props,{projection:o}=n;return o?(o.isPresent=i,va=!0,s||t.layoutDependency!==e||void 0===e||t.isPresent!==i?o.willUpdate():this.safeToRemove(),t.isPresent!==i&&(i?o.promote():o.relegate()||ce.postRender(()=>{const t=o.getStack();t&&t.members.length||this.safeToRemove()})),null):null}componentDidUpdate(){const{projection:t}=this.props.visualElement;t&&(t.root.didUpdate(),ri.postRender(()=>{!t.currentAnimation&&t.isLead()&&this.safeToRemove()}))}componentWillUnmount(){const{visualElement:t,layoutGroup:e,switchLayoutGroup:n}=this.props,{projection:s}=t;va=!0,s&&(s.scheduleCheckAfterUnmount(),e&&e.group&&e.group.remove(s),n&&n.deregister&&n.deregister(s))}safeToRemove(){const{safeToRemove:t}=this.props;t&&t()}render(){return null}}function wa(t){const[s,i]=Ci(),o=e.useContext(Pt);return n.jsx(xa,{...t,layoutGroup:o,switchLayoutGroup:e.useContext(Do),isPresent:s,safeToRemove:i})}const ba=(t,e)=>t.depth-e.depth;class Sa{constructor(){this.children=[],this.isDirty=!1}add(t){At(this.children,t),this.isDirty=!0}remove(t){Ct(this.children,t),this.isDirty=!0}forEach(t){this.isDirty&&this.children.sort(ba),this.isDirty=!1,this.children.forEach(t)}}const Ta=[\"TopLeft\",\"TopRight\",\"BottomLeft\",\"BottomRight\"],Pa=Ta.length,ka=t=>\"string\"==typeof t?parseFloat(t):t,ja=t=>\"number\"==typeof t||Be.test(t);function Ma(t,e){return void 0!==t[e]?t[e]:t.borderRadius}const Ea=Ca(0,.5,te),Aa=Ca(.5,.95,Bt);function Ca(t,e,n){return s=>s<t?0:s>e?1:n(Ut(t,e,s))}function Va(t,e){t.min=e.min,t.max=e.max}function Da(t,e){Va(t.x,e.x),Va(t.y,e.y)}function Ra(t,e){t.translate=e.translate,t.scale=e.scale,t.originPoint=e.originPoint,t.origin=e.origin}function La(t,e,n,s,i){return t=Yo(t-=e,1/n,s),void 0!==i&&(t=Yo(t,1/i,s)),t}function Na(t,e,[n,s,i],o,r){!function(t,e=0,n=1,s=.5,i,o=t,r=t){Ie.test(e)&&(e=parseFloat(e),e=tn(r.min,r.max,e/100)-r.min);if(\"number\"!=typeof e)return;let a=tn(o.min,o.max,s);t===o&&(a-=e),t.min=La(t.min,e,n,a,i),t.max=La(t.max,e,n,a,i)}(t,e[n],e[s],e[i],e.scale,o,r)}const Ia=[\"x\",\"scaleX\",\"originX\"],Ba=[\"y\",\"scaleY\",\"originY\"];function Fa(t,e,n,s){Na(t.x,e,Ia,n?n.x:void 0,s?s.x:void 0),Na(t.y,e,Ba,n?n.y:void 0,s?s.y:void 0)}function Oa(t){return 0===t.translate&&1===t.scale}function Ua(t){return Oa(t.x)&&Oa(t.y)}function za(t,e){return t.min===e.min&&t.max===e.max}function _a(t,e){return Math.round(t.min)===Math.round(e.min)&&Math.round(t.max)===Math.round(e.max)}function Wa(t,e){return _a(t.x,e.x)&&_a(t.y,e.y)}function $a(t){return Yr(t.x)/Yr(t.y)}function Ya(t,e){return t.translate===e.translate&&t.scale===e.scale&&t.originPoint===e.originPoint}class Ka{constructor(){this.members=[]}add(t){At(this.members,t),t.scheduleRender()}remove(t){if(Ct(this.members,t),t===this.prevLead&&(this.prevLead=void 0),t===this.lead){const t=this.members[this.members.length-1];t&&this.promote(t)}}relegate(t){const e=this.members.findIndex(e=>t===e);if(0===e)return!1;let n;for(let s=e;s>=0;s--){const t=this.members[s];if(!1!==t.isPresent){n=t;break}}return!!n&&(this.promote(n),!0)}promote(t,e){const n=this.lead;if(t!==n&&(this.prevLead=n,this.lead=t,t.show(),n)){n.instance&&n.scheduleRender(),t.scheduleRender(),t.resumeFrom=n,e&&(t.resumeFrom.preserveOpacity=!0),n.snapshot&&(t.snapshot=n.snapshot,t.snapshot.latestValues=n.animationValues||n.latestValues),t.root&&t.root.isUpdating&&(t.isLayoutDirty=!0);const{crossfade:s}=t.options;!1===s&&n.hide()}}exitAnimationComplete(){this.members.forEach(t=>{const{options:e,resumingFrom:n}=t;e.onExitComplete&&e.onExitComplete(),n&&n.options.onExitComplete&&n.options.onExitComplete()})}scheduleRender(){this.members.forEach(t=>{t.instance&&t.scheduleRender(!1)})}removeLeadSnapshot(){this.lead&&this.lead.snapshot&&(this.lead.snapshot=void 0)}}const Xa=[\"\",\"X\",\"Y\",\"Z\"];let Ha=0;function qa(t,e,n,s){const{latestValues:i}=e;i[t]&&(n[t]=i[t],e.setStaticValue(t,0),s&&(s[t]=0))}function Ga(t){if(t.hasCheckedOptimisedAppear=!0,t.root===t)return;const{visualElement:e}=t.options;if(!e)return;const n=vr(e);if(window.MotionHasOptimisedAnimation(n,\"transform\")){const{layout:e,layoutId:s}=t.options;window.MotionCancelOptimisedAnimation(n,\"transform\",ce,!(e||s))}const{parent:s}=t;s&&!s.hasCheckedOptimisedAppear&&Ga(s)}function Za({attachResizeListener:t,defaultParent:e,measureScroll:n,checkIsScrollRoot:s,resetTransform:i}){return class{constructor(t={},n=(null==e?void 0:e())){this.id=Ha++,this.animationId=0,this.animationCommitId=0,this.children=new Set,this.options={},this.isTreeAnimating=!1,this.isAnimationBlocked=!1,this.isLayoutDirty=!1,this.isProjectionDirty=!1,this.isSharedProjectionDirty=!1,this.isTransformDirty=!1,this.updateManuallyBlocked=!1,this.updateBlockedByResize=!1,this.isUpdating=!1,this.isSVG=!1,this.needsReset=!1,this.shouldResetTransform=!1,this.hasCheckedOptimisedAppear=!1,this.treeScale={x:1,y:1},this.eventHandlers=new Map,this.hasTreeAnimated=!1,this.layoutVersion=0,this.updateScheduled=!1,this.scheduleUpdate=()=>this.update(),this.projectionUpdateScheduled=!1,this.checkUpdateFailed=()=>{this.isUpdating&&(this.isUpdating=!1,this.clearAllSnapshots())},this.updateProjection=()=>{this.projectionUpdateScheduled=!1,this.nodes.forEach(tl),this.nodes.forEach(al),this.nodes.forEach(ll),this.nodes.forEach(el)},this.resolvedRelativeTargetAt=0,this.linkedParentVersion=0,this.hasProjected=!1,this.isVisible=!0,this.animationProgress=0,this.sharedNodes=new Map,this.latestValues=t,this.root=n?n.root||n:this,this.path=n?[...n.path,n]:[],this.parent=n,this.depth=n?n.depth+1:0;for(let e=0;e<this.path.length;e++)this.path[e].shouldResetTransform=!0;this.root===this&&(this.nodes=new Sa)}addEventListener(t,e){return this.eventHandlers.has(t)||this.eventHandlers.set(t,new zt),this.eventHandlers.get(t).add(e)}notifyListeners(t,...e){const n=this.eventHandlers.get(t);n&&n.notify(...e)}hasListeners(t){return this.eventHandlers.has(t)}mount(e){if(this.instance)return;var n;this.isSVG=wi(e)&&!(wi(n=e)&&\"svg\"===n.tagName),this.instance=e;const{layoutId:s,layout:i,visualElement:o}=this.options;if(o&&!o.current&&o.mount(e),this.root.nodes.add(this),this.parent&&this.parent.children.add(this),this.root.hasTreeAnimated&&(i||s)&&(this.isLayoutDirty=!0),t){let n,s=0;const i=()=>this.root.updateBlockedByResize=!1;ce.read(()=>{s=window.innerWidth}),t(e,()=>{const t=window.innerWidth;t!==s&&(s=t,this.root.updateBlockedByResize=!0,n&&n(),n=function(t,e){const n=ge.now(),s=({timestamp:i})=>{const o=i-n;o>=e&&(he(s),t(o-e))};return ce.setup(s,!0),()=>he(s)}(i,250),ya.hasAnimatedSinceResize&&(ya.hasAnimatedSinceResize=!1,this.nodes.forEach(rl)))})}s&&this.root.registerSharedNode(s,this),!1!==this.options.animate&&o&&(s||i)&&this.addEventListener(\"didUpdate\",({delta:t,hasLayoutChanged:e,hasRelativeLayoutChanged:n,layout:s})=>{if(this.isTreeAnimationBlocked())return this.target=void 0,void(this.relativeTarget=void 0);const i=this.options.transition||o.getDefaultTransition()||pl,{onLayoutAnimationStart:r,onLayoutAnimationComplete:a}=o.getProps(),l=!this.targetLayout||!Wa(this.targetLayout,s),u=!e&&n;if(this.options.layoutRoot||this.resumeFrom||u||e&&(l||!this.currentAnimation)){this.resumeFrom&&(this.resumingFrom=this.resumeFrom,this.resumingFrom.resumingFrom=void 0);const e={...Os(i,\"layout\"),onPlay:r,onComplete:a};(o.shouldReduceMotion||this.options.layoutRoot)&&(e.delay=0,e.type=!1),this.startAnimation(e),this.setAnimationOrigin(t,u)}else e||rl(this),this.isLead()&&this.options.onExitComplete&&this.options.onExitComplete();this.targetLayout=s})}unmount(){this.options.layoutId&&this.willUpdate(),this.root.nodes.remove(this);const t=this.getStack();t&&t.remove(this),this.parent&&this.parent.children.delete(this),this.instance=void 0,this.eventHandlers.clear(),he(this.updateProjection)}blockUpdate(){this.updateManuallyBlocked=!0}unblockUpdate(){this.updateManuallyBlocked=!1}isUpdateBlocked(){return this.updateManuallyBlocked||this.updateBlockedByResize}isTreeAnimationBlocked(){return this.isAnimationBlocked||this.parent&&this.parent.isTreeAnimationBlocked()||!1}startUpdate(){this.isUpdateBlocked()||(this.isUpdating=!0,this.nodes&&this.nodes.forEach(ul),this.animationId++)}getTransformTemplate(){const{visualElement:t}=this.options;return t&&t.getProps().transformTemplate}willUpdate(t=!0){if(this.root.hasTreeAnimated=!0,this.root.isUpdateBlocked())return void(this.options.onExitComplete&&this.options.onExitComplete());if(window.MotionCancelOptimisedAnimation&&!this.hasCheckedOptimisedAppear&&Ga(this),!this.root.isUpdating&&this.root.startUpdate(),this.isLayoutDirty)return;this.isLayoutDirty=!0;for(let i=0;i<this.path.length;i++){const t=this.path[i];t.shouldResetTransform=!0,t.updateScroll(\"snapshot\"),t.options.layoutRoot&&t.willUpdate(!1)}const{layoutId:e,layout:n}=this.options;if(void 0===e&&!n)return;const s=this.getTransformTemplate();this.prevTransformTemplateValue=s?s(this.latestValues,\"\"):void 0,this.updateSnapshot(),t&&this.notifyListeners(\"willUpdate\")}update(){this.updateScheduled=!1;if(this.isUpdateBlocked())return this.unblockUpdate(),this.clearAllSnapshots(),void this.nodes.forEach(sl);if(this.animationId<=this.animationCommitId)return void this.nodes.forEach(il);this.animationCommitId=this.animationId,this.isUpdating?(this.isUpdating=!1,this.nodes.forEach(ol),this.nodes.forEach(Ja),this.nodes.forEach(Qa)):this.nodes.forEach(il),this.clearAllSnapshots();const t=ge.now();de.delta=Vt(0,1e3/60,t-de.timestamp),de.timestamp=t,de.isProcessing=!0,me.update.process(de),me.preRender.process(de),me.render.process(de),de.isProcessing=!1}didUpdate(){this.updateScheduled||(this.updateScheduled=!0,ri.read(this.scheduleUpdate))}clearAllSnapshots(){this.nodes.forEach(nl),this.sharedNodes.forEach(cl)}scheduleUpdateProjection(){this.projectionUpdateScheduled||(this.projectionUpdateScheduled=!0,ce.preRender(this.updateProjection,!1,!0))}scheduleCheckAfterUnmount(){ce.postRender(()=>{this.isLayoutDirty?this.root.didUpdate():this.root.checkUpdateFailed()})}updateSnapshot(){!this.snapshot&&this.instance&&(this.snapshot=this.measure(),!this.snapshot||Yr(this.snapshot.measuredBox.x)||Yr(this.snapshot.measuredBox.y)||(this.snapshot=void 0))}updateLayout(){if(!this.instance)return;if(this.updateScroll(),!(this.options.alwaysMeasureLayout&&this.isLead()||this.isLayoutDirty))return;if(this.resumeFrom&&!this.resumeFrom.instance)for(let n=0;n<this.path.length;n++){this.path[n].updateScroll()}const t=this.layout;this.layout=this.measure(!1),this.layoutVersion++,this.layoutCorrected={x:{min:0,max:0},y:{min:0,max:0}},this.isLayoutDirty=!1,this.projectionDelta=void 0,this.notifyListeners(\"measure\",this.layout.layoutBox);const{visualElement:e}=this.options;e&&e.notify(\"LayoutMeasure\",this.layout.layoutBox,t?t.layoutBox:void 0)}updateScroll(t=\"measure\"){let e=Boolean(this.options.layoutScroll&&this.instance);if(this.scroll&&this.scroll.animationId===this.root.animationId&&this.scroll.phase===t&&(e=!1),e&&this.instance){const e=s(this.instance);this.scroll={animationId:this.root.animationId,phase:t,isRoot:e,offset:n(this.instance),wasRoot:this.scroll?this.scroll.isRoot:e}}}resetTransform(){if(!i)return;const t=this.isLayoutDirty||this.shouldResetTransform||this.options.alwaysMeasureLayout,e=this.projectionDelta&&!Ua(this.projectionDelta),n=this.getTransformTemplate(),s=n?n(this.latestValues,\"\"):void 0,o=s!==this.prevTransformTemplateValue;t&&this.instance&&(e||_o(this.latestValues)||o)&&(i(this.instance,s),this.shouldResetTransform=!1,this.scheduleRender())}measure(t=!0){const e=this.measurePageBox();let n=this.removeElementScroll(e);var s;return t&&(n=this.removeTransform(n)),yl((s=n).x),yl(s.y),{animationId:this.root.animationId,measuredBox:e,layoutBox:n,latestValues:{},source:this.id}}measurePageBox(){var t;const{visualElement:e}=this.options;if(!e)return{x:{min:0,max:0},y:{min:0,max:0}};const n=e.measureViewportBox();if(!((null==(t=this.scroll)?void 0:t.wasRoot)||this.path.some(xl))){const{scroll:t}=this.root;t&&(Zo(n.x,t.offset.x),Zo(n.y,t.offset.y))}return n}removeElementScroll(t){var e;const n={x:{min:0,max:0},y:{min:0,max:0}};if(Da(n,t),null==(e=this.scroll)?void 0:e.wasRoot)return n;for(let s=0;s<this.path.length;s++){const e=this.path[s],{scroll:i,options:o}=e;e!==this.root&&i&&o.layoutScroll&&(i.wasRoot&&Da(n,t),Zo(n.x,i.offset.x),Zo(n.y,i.offset.y))}return n}applyTransform(t,e=!1){const n={x:{min:0,max:0},y:{min:0,max:0}};Da(n,t);for(let s=0;s<this.path.length;s++){const t=this.path[s];!e&&t.options.layoutScroll&&t.scroll&&t!==t.root&&Qo(n,{x:-t.scroll.offset.x,y:-t.scroll.offset.y}),_o(t.latestValues)&&Qo(n,t.latestValues)}return _o(this.latestValues)&&Qo(n,this.latestValues),n}removeTransform(t){const e={x:{min:0,max:0},y:{min:0,max:0}};Da(e,t);for(let n=0;n<this.path.length;n++){const t=this.path[n];if(!t.instance)continue;if(!_o(t.latestValues))continue;zo(t.latestValues)&&t.updateSnapshot();const s=er();Da(s,t.measurePageBox()),Fa(e,t.latestValues,t.snapshot?t.snapshot.layoutBox:void 0,s)}return _o(this.latestValues)&&Fa(e,this.latestValues),e}setTargetDelta(t){this.targetDelta=t,this.root.scheduleUpdateProjection(),this.isProjectionDirty=!0}setOptions(t){this.options={...this.options,...t,crossfade:void 0===t.crossfade||t.crossfade}}clearMeasurements(){this.scroll=void 0,this.layout=void 0,this.snapshot=void 0,this.prevTransformTemplateValue=void 0,this.targetDelta=void 0,this.target=void 0,this.isLayoutDirty=!1}forceRelativeParentToResolveTarget(){this.relativeParent&&this.relativeParent.resolvedRelativeTargetAt!==de.timestamp&&this.relativeParent.resolveTargetDelta(!0)}resolveTargetDelta(t=!1){var e;const n=this.getLead();this.isProjectionDirty||(this.isProjectionDirty=n.isProjectionDirty),this.isTransformDirty||(this.isTransformDirty=n.isTransformDirty),this.isSharedProjectionDirty||(this.isSharedProjectionDirty=n.isSharedProjectionDirty);const s=Boolean(this.resumingFrom)||this!==n;if(!(t||s&&this.isSharedProjectionDirty||this.isProjectionDirty||(null==(e=this.parent)?void 0:e.isProjectionDirty)||this.attemptToResolveRelativeTarget||this.root.updateBlockedByResize))return;const{layout:i,layoutId:o}=this.options;if(!this.layout||!i&&!o)return;this.resolvedRelativeTargetAt=de.timestamp;const r=this.getClosestProjectingParent();var a,l,u;(r&&this.linkedParentVersion!==r.layoutVersion&&!r.options.layoutRoot&&this.removeRelativeTarget(),this.targetDelta||this.relativeTarget||(r&&r.layout?this.createRelativeTarget(r,this.layout.layoutBox,r.layout.layoutBox):this.removeRelativeTarget()),this.relativeTarget||this.targetDelta)&&(this.target||(this.target={x:{min:0,max:0},y:{min:0,max:0}},this.targetWithTransforms={x:{min:0,max:0},y:{min:0,max:0}}),this.relativeTarget&&this.relativeTargetOrigin&&this.relativeParent&&this.relativeParent.target?(this.forceRelativeParentToResolveTarget(),a=this.target,l=this.relativeTarget,u=this.relativeParent.target,Hr(a.x,l.x,u.x),Hr(a.y,l.y,u.y)):this.targetDelta?(Boolean(this.resumingFrom)?this.target=this.applyTransform(this.layout.layoutBox):Da(this.target,this.layout.layoutBox),Ho(this.target,this.targetDelta)):Da(this.target,this.layout.layoutBox),this.attemptToResolveRelativeTarget&&(this.attemptToResolveRelativeTarget=!1,r&&Boolean(r.resumingFrom)===Boolean(this.resumingFrom)&&!r.options.layoutScroll&&r.target&&1!==this.animationProgress?this.createRelativeTarget(r,this.target,r.target):this.relativeParent=this.relativeTarget=void 0))}getClosestProjectingParent(){if(this.parent&&!zo(this.parent.latestValues)&&!Wo(this.parent.latestValues))return this.parent.isProjecting()?this.parent:this.parent.getClosestProjectingParent()}isProjecting(){return Boolean((this.relativeTarget||this.targetDelta||this.options.layoutRoot)&&this.layout)}createRelativeTarget(t,e,n){this.relativeParent=t,this.linkedParentVersion=t.layoutVersion,this.forceRelativeParentToResolveTarget(),this.relativeTarget={x:{min:0,max:0},y:{min:0,max:0}},this.relativeTargetOrigin={x:{min:0,max:0},y:{min:0,max:0}},Gr(this.relativeTargetOrigin,e,n),Da(this.relativeTarget,this.relativeTargetOrigin)}removeRelativeTarget(){this.relativeParent=this.relativeTarget=void 0}calcProjection(){var t;const e=this.getLead(),n=Boolean(this.resumingFrom)||this!==e;let s=!0;if((this.isProjectionDirty||(null==(t=this.parent)?void 0:t.isProjectionDirty))&&(s=!1),n&&(this.isSharedProjectionDirty||this.isTransformDirty)&&(s=!1),this.resolvedRelativeTargetAt===de.timestamp&&(s=!1),s)return;const{layout:i,layoutId:o}=this.options;if(this.isTreeAnimating=Boolean(this.parent&&this.parent.isTreeAnimating||this.currentAnimation||this.pendingAnimation),this.isTreeAnimating||(this.targetDelta=this.relativeTarget=void 0),!this.layout||!i&&!o)return;Da(this.layoutCorrected,this.layout.layoutBox);const r=this.treeScale.x,a=this.treeScale.y;!function(t,e,n,s=!1){const i=n.length;if(!i)return;let o,r;e.x=e.y=1;for(let a=0;a<i;a++){o=n[a],r=o.projectionDelta;const{visualElement:i}=o.options;i&&i.props.style&&\"contents\"===i.props.style.display||(s&&o.options.layoutScroll&&o.scroll&&o!==o.root&&Qo(t,{x:-o.scroll.offset.x,y:-o.scroll.offset.y}),r&&(e.x*=r.x.scale,e.y*=r.y.scale,Ho(t,r)),s&&_o(o.latestValues)&&Qo(t,o.latestValues))}e.x<Go&&e.x>qo&&(e.x=1),e.y<Go&&e.y>qo&&(e.y=1)}(this.layoutCorrected,this.treeScale,this.path,n),!e.layout||e.target||1===this.treeScale.x&&1===this.treeScale.y||(e.target=e.layout.layoutBox,e.targetWithTransforms={x:{min:0,max:0},y:{min:0,max:0}});const{target:l}=e;l?(this.projectionDelta&&this.prevProjectionDelta?(Ra(this.prevProjectionDelta.x,this.projectionDelta.x),Ra(this.prevProjectionDelta.y,this.projectionDelta.y)):this.createProjectionDeltas(),Xr(this.projectionDelta,this.layoutCorrected,l,this.latestValues),this.treeScale.x===r&&this.treeScale.y===a&&Ya(this.projectionDelta.x,this.prevProjectionDelta.x)&&Ya(this.projectionDelta.y,this.prevProjectionDelta.y)||(this.hasProjected=!0,this.scheduleRender(),this.notifyListeners(\"projectionUpdate\",l))):this.prevProjectionDelta&&(this.createProjectionDeltas(),this.scheduleRender())}hide(){this.isVisible=!1}show(){this.isVisible=!0}scheduleRender(t=!0){var e;if(null==(e=this.options.visualElement)||e.scheduleRender(),t){const t=this.getStack();t&&t.scheduleRender()}this.resumingFrom&&!this.resumingFrom.instance&&(this.resumingFrom=void 0)}createProjectionDeltas(){this.prevProjectionDelta={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}},this.projectionDelta={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}},this.projectionDeltaWithTransform={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}}}setAnimationOrigin(t,e=!1){const n=this.snapshot,s=n?n.latestValues:{},i={...this.latestValues},o={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}};this.relativeParent&&this.relativeParent.options.layoutRoot||(this.relativeTarget=this.relativeTargetOrigin=void 0),this.attemptToResolveRelativeTarget=!e;const r={x:{min:0,max:0},y:{min:0,max:0}},a=(n?n.source:void 0)!==(this.layout?this.layout.source:void 0),l=this.getStack(),u=!l||l.members.length<=1,c=Boolean(a&&!u&&!0===this.options.crossfade&&!this.path.some(ml));let h;this.animationProgress=0,this.mixTargetDelta=e=>{const n=e/1e3;var l,d,m,p,f,g;hl(o.x,t.x,n),hl(o.y,t.y,n),this.setTargetDelta(o),this.relativeTarget&&this.relativeTargetOrigin&&this.layout&&this.relativeParent&&this.relativeParent.layout&&(Gr(r,this.layout.layoutBox,this.relativeParent.layout.layoutBox),m=this.relativeTarget,p=this.relativeTargetOrigin,f=r,g=n,dl(m.x,p.x,f.x,g),dl(m.y,p.y,f.y,g),h&&(l=this.relativeTarget,d=h,za(l.x,d.x)&&za(l.y,d.y))&&(this.isProjectionDirty=!1),h||(h={x:{min:0,max:0},y:{min:0,max:0}}),Da(h,this.relativeTarget)),a&&(this.animationValues=i,function(t,e,n,s,i,o){i?(t.opacity=tn(0,n.opacity??1,Ea(s)),t.opacityExit=tn(e.opacity??1,0,Aa(s))):o&&(t.opacity=tn(e.opacity??1,n.opacity??1,s));for(let r=0;r<Pa;r++){const i=`border${Ta[r]}Radius`;let o=Ma(e,i),a=Ma(n,i);void 0===o&&void 0===a||(o||(o=0),a||(a=0),0===o||0===a||ja(o)===ja(a)?(t[i]=Math.max(tn(ka(o),ka(a),s),0),(Ie.test(a)||Ie.test(o))&&(t[i]+=\"%\")):t[i]=a)}(e.rotate||n.rotate)&&(t.rotate=tn(e.rotate||0,n.rotate||0,s))}(i,s,this.latestValues,n,c,u)),this.root.scheduleUpdateProjection(),this.scheduleRender(),this.animationProgress=n},this.mixTargetDelta(this.options.layoutRoot?1e3:0)}startAnimation(t){var e,n,s;this.notifyListeners(\"animationStart\"),null==(e=this.currentAnimation)||e.stop(),null==(s=null==(n=this.resumingFrom)?void 0:n.currentAnimation)||s.stop(),this.pendingAnimation&&(he(this.pendingAnimation),this.pendingAnimation=void 0),this.pendingAnimation=ce.update(()=>{ya.hasAnimatedSinceResize=!0,this.motionValue||(this.motionValue=oi(0)),this.currentAnimation=function(t,e,n){const s=bi(t)?t:oi(t);return s.start(Pr(\"\",s,e,n)),s.animation}(this.motionValue,[0,1e3],{...t,velocity:0,isSync:!0,onUpdate:e=>{this.mixTargetDelta(e),t.onUpdate&&t.onUpdate(e)},onStop:()=>{},onComplete:()=>{t.onComplete&&t.onComplete(),this.completeAnimation()}}),this.resumingFrom&&(this.resumingFrom.currentAnimation=this.currentAnimation),this.pendingAnimation=void 0})}completeAnimation(){this.resumingFrom&&(this.resumingFrom.currentAnimation=void 0,this.resumingFrom.preserveOpacity=void 0);const t=this.getStack();t&&t.exitAnimationComplete(),this.resumingFrom=this.currentAnimation=this.animationValues=void 0,this.notifyListeners(\"animationComplete\")}finishAnimation(){this.currentAnimation&&(this.mixTargetDelta&&this.mixTargetDelta(1e3),this.currentAnimation.stop()),this.completeAnimation()}applyTransformsToTarget(){const t=this.getLead();let{targetWithTransforms:e,target:n,layout:s,latestValues:i}=t;if(e&&n&&s){if(this!==t&&this.layout&&s&&vl(this.options.animationType,this.layout.layoutBox,s.layoutBox)){n=this.target||{x:{min:0,max:0},y:{min:0,max:0}};const e=Yr(this.layout.layoutBox.x);n.x.min=t.target.x.min,n.x.max=n.x.min+e;const s=Yr(this.layout.layoutBox.y);n.y.min=t.target.y.min,n.y.max=n.y.min+s}Da(e,n),Qo(e,i),Xr(this.projectionDeltaWithTransform,this.layoutCorrected,e,i)}}registerSharedNode(t,e){this.sharedNodes.has(t)||this.sharedNodes.set(t,new Ka);this.sharedNodes.get(t).add(e);const n=e.options.initialPromotionConfig;e.promote({transition:n?n.transition:void 0,preserveFollowOpacity:n&&n.shouldPreserveFollowOpacity?n.shouldPreserveFollowOpacity(e):void 0})}isLead(){const t=this.getStack();return!t||t.lead===this}getLead(){var t;const{layoutId:e}=this.options;return e&&(null==(t=this.getStack())?void 0:t.lead)||this}getPrevLead(){var t;const{layoutId:e}=this.options;return e?null==(t=this.getStack())?void 0:t.prevLead:void 0}getStack(){const{layoutId:t}=this.options;if(t)return this.root.sharedNodes.get(t)}promote({needsReset:t,transition:e,preserveFollowOpacity:n}={}){const s=this.getStack();s&&s.promote(this,n),t&&(this.projectionDelta=void 0,this.needsReset=!0),e&&this.setOptions({transition:e})}relegate(){const t=this.getStack();return!!t&&t.relegate(this)}resetSkewAndRotation(){const{visualElement:t}=this.options;if(!t)return;let e=!1;const{latestValues:n}=t;if((n.z||n.rotate||n.rotateX||n.rotateY||n.rotateZ||n.skewX||n.skewY)&&(e=!0),!e)return;const s={};n.z&&qa(\"z\",t,s,this.animationValues);for(let i=0;i<Xa.length;i++)qa(`rotate${Xa[i]}`,t,s,this.animationValues),qa(`skew${Xa[i]}`,t,s,this.animationValues);t.render();for(const i in s)t.setStaticValue(i,s[i]),this.animationValues&&(this.animationValues[i]=s[i]);t.scheduleRender()}applyProjectionStyles(t,e){if(!this.instance||this.isSVG)return;if(!this.isVisible)return void(t.visibility=\"hidden\");const n=this.getTransformTemplate();if(this.needsReset)return this.needsReset=!1,t.visibility=\"\",t.opacity=\"\",t.pointerEvents=bo(null==e?void 0:e.pointerEvents)||\"\",void(t.transform=n?n(this.latestValues,\"\"):\"none\");const s=this.getLead();if(!this.projectionDelta||!this.layout||!s.target)return this.options.layoutId&&(t.opacity=void 0!==this.latestValues.opacity?this.latestValues.opacity:1,t.pointerEvents=bo(null==e?void 0:e.pointerEvents)||\"\"),void(this.hasProjected&&!_o(this.latestValues)&&(t.transform=n?n({},\"\"):\"none\",this.hasProjected=!1));t.visibility=\"\";const i=s.animationValues||s.latestValues;this.applyTransformsToTarget();let o=function(t,e,n){let s=\"\";const i=t.x.translate/e.x,o=t.y.translate/e.y,r=(null==n?void 0:n.z)||0;if((i||o||r)&&(s=`translate3d(${i}px, ${o}px, ${r}px) `),1===e.x&&1===e.y||(s+=`scale(${1/e.x}, ${1/e.y}) `),n){const{transformPerspective:t,rotate:e,rotateX:i,rotateY:o,skewX:r,skewY:a}=n;t&&(s=`perspective(${t}px) ${s}`),e&&(s+=`rotate(${e}deg) `),i&&(s+=`rotateX(${i}deg) `),o&&(s+=`rotateY(${o}deg) `),r&&(s+=`skewX(${r}deg) `),a&&(s+=`skewY(${a}deg) `)}const a=t.x.scale*e.x,l=t.y.scale*e.y;return 1===a&&1===l||(s+=`scale(${a}, ${l})`),s||\"none\"}(this.projectionDeltaWithTransform,this.treeScale,i);n&&(o=n(i,o)),t.transform=o;const{x:r,y:a}=this.projectionDelta;t.transformOrigin=`${100*r.origin}% ${100*a.origin}% 0`,s.animationValues?t.opacity=s===this?i.opacity??this.latestValues.opacity??1:this.preserveOpacity?this.latestValues.opacity:i.opacityExit:t.opacity=s===this?void 0!==i.opacity?i.opacity:\"\":void 0!==i.opacityExit?i.opacityExit:0;for(const l in Qi){if(void 0===i[l])continue;const{correct:e,applyTo:n,isCSSVariable:r}=Qi[l],a=\"none\"===o?i[l]:e(i[l],s);if(n){const e=n.length;for(let s=0;s<e;s++)t[n[s]]=a}else r?this.options.visualElement.renderState.vars[l]=a:t[l]=a}this.options.layoutId&&(t.pointerEvents=s===this?bo(null==e?void 0:e.pointerEvents)||\"\":\"none\")}clearSnapshot(){this.resumeFrom=this.snapshot=void 0}resetTree(){this.root.nodes.forEach(t=>{var e;return null==(e=t.currentAnimation)?void 0:e.stop()}),this.root.nodes.forEach(sl),this.root.sharedNodes.clear()}}}function Ja(t){t.updateLayout()}function Qa(t){var e;const n=(null==(e=t.resumeFrom)?void 0:e.snapshot)||t.snapshot;if(t.isLead()&&t.layout&&n&&t.hasListeners(\"didUpdate\")){const{layoutBox:e,measuredBox:s}=t.layout,{animationType:i}=t.options,o=n.source!==t.layout.source;\"size\"===i?Zr(t=>{const s=o?n.measuredBox[t]:n.layoutBox[t],i=Yr(s);s.min=e[t].min,s.max=s.min+i}):vl(i,n.layoutBox,e)&&Zr(s=>{const i=o?n.measuredBox[s]:n.layoutBox[s],r=Yr(e[s]);i.max=i.min+r,t.relativeTarget&&!t.currentAnimation&&(t.isProjectionDirty=!0,t.relativeTarget[s].max=t.relativeTarget[s].min+r)});const r={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}};Xr(r,e,n.layoutBox);const a={x:{translate:0,scale:1,origin:0,originPoint:0},y:{translate:0,scale:1,origin:0,originPoint:0}};o?Xr(a,t.applyTransform(s,!0),n.measuredBox):Xr(a,e,n.layoutBox);const l=!Ua(r);let u=!1;if(!t.resumeFrom){const s=t.getClosestProjectingParent();if(s&&!s.resumeFrom){const{snapshot:i,layout:o}=s;if(i&&o){const r={x:{min:0,max:0},y:{min:0,max:0}};Gr(r,n.layoutBox,i.layoutBox);const a={x:{min:0,max:0},y:{min:0,max:0}};Gr(a,e,o.layoutBox),Wa(r,a)||(u=!0),s.options.layoutRoot&&(t.relativeTarget=a,t.relativeTargetOrigin=r,t.relativeParent=s)}}}t.notifyListeners(\"didUpdate\",{layout:e,snapshot:n,delta:a,layoutDelta:r,hasLayoutChanged:l,hasRelativeLayoutChanged:u})}else if(t.isLead()){const{onExitComplete:e}=t.options;e&&e()}t.options.transition=void 0}function tl(t){t.parent&&(t.isProjecting()||(t.isProjectionDirty=t.parent.isProjectionDirty),t.isSharedProjectionDirty||(t.isSharedProjectionDirty=Boolean(t.isProjectionDirty||t.parent.isProjectionDirty||t.parent.isSharedProjectionDirty)),t.isTransformDirty||(t.isTransformDirty=t.parent.isTransformDirty))}function el(t){t.isProjectionDirty=t.isSharedProjectionDirty=t.isTransformDirty=!1}function nl(t){t.clearSnapshot()}function sl(t){t.clearMeasurements()}function il(t){t.isLayoutDirty=!1}function ol(t){const{visualElement:e}=t.options;e&&e.getProps().onBeforeLayoutMeasure&&e.notify(\"BeforeLayoutMeasure\"),t.resetTransform()}function rl(t){t.finishAnimation(),t.targetDelta=t.relativeTarget=t.target=void 0,t.isProjectionDirty=!0}function al(t){t.resolveTargetDelta()}function ll(t){t.calcProjection()}function ul(t){t.resetSkewAndRotation()}function cl(t){t.removeLeadSnapshot()}function hl(t,e,n){t.translate=tn(e.translate,0,n),t.scale=tn(e.scale,1,n),t.origin=e.origin,t.originPoint=e.originPoint}function dl(t,e,n,s){t.min=tn(e.min,n.min,s),t.max=tn(e.max,n.max,s)}function ml(t){return t.animationValues&&void 0!==t.animationValues.opacityExit}const pl={duration:.45,ease:[.4,0,.1,1]},fl=t=>\"undefined\"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().includes(t),gl=fl(\"applewebkit/\")&&!fl(\"chrome/\")?Math.round:Bt;function yl(t){t.min=gl(t.min),t.max=gl(t.max)}function vl(t,e,n){return\"position\"===t||\"preserve-aspect\"===t&&(s=$a(e),i=$a(n),o=.2,!(Math.abs(s-i)<=o));var s,i,o}function xl(t){var e;return t!==t.root&&(null==(e=t.scroll)?void 0:e.wasRoot)}const wl=Za({attachResizeListener:(t,e)=>_r(t,\"resize\",e),measureScroll:()=>({x:document.documentElement.scrollLeft||document.body.scrollLeft,y:document.documentElement.scrollTop||document.body.scrollTop}),checkIsScrollRoot:()=>!0}),bl={current:void 0},Sl=Za({measureScroll:t=>({x:t.scrollLeft,y:t.scrollTop}),defaultParent:()=>{if(!bl.current){const t=new wl({});t.mount(window),t.setOptions({layoutScroll:!0}),bl.current=t}return bl.current},resetTransform:(t,e)=>{t.style.transform=void 0!==e?e:\"none\"},checkIsScrollRoot:t=>Boolean(\"fixed\"===window.getComputedStyle(t).position)}),Tl={pan:{Feature:class extends Or{constructor(){super(...arguments),this.removePointerDownListener=Bt}onPointerDown(t){this.session=new ea(t,this.createPanHandlers(),{transformPagePoint:this.node.getTransformPagePoint(),contextWindow:Jr(this.node)})}createPanHandlers(){const{onPanSessionStart:t,onPanStart:e,onPan:n,onPanEnd:s}=this.node.getProps();return{onSessionStart:ga(t),onStart:ga(e),onMove:n,onEnd:(t,e)=>{delete this.session,s&&ce.postRender(()=>s(t,e))}}}mount(){this.removePointerDownListener=$r(this.node.current,\"pointerdown\",t=>this.onPointerDown(t))}update(){this.session&&this.session.updateHandlers(this.createPanHandlers())}unmount(){this.removePointerDownListener(),this.session&&this.session.end()}}},drag:{Feature:class extends Or{constructor(t){super(t),this.removeGroupControls=Bt,this.removeListeners=Bt,this.controls=new pa(t)}mount(){const{dragControls:t}=this.node.getProps();t&&(this.removeGroupControls=t.subscribe(this.controls)),this.removeListeners=this.controls.addListeners()||Bt}update(){const{dragControls:t}=this.node.getProps(),{dragControls:e}=this.node.prevProps||{};t!==e&&(this.removeGroupControls(),t&&(this.removeGroupControls=t.subscribe(this.controls)))}unmount(){this.removeGroupControls(),this.removeListeners()}},ProjectionNode:Sl,MeasureLayout:wa}};function Pl(t,e,n){const{props:s}=t;t.animationState&&s.whileHover&&t.animationState.setActive(\"whileHover\",\"Start\"===n);const i=s[\"onHover\"+n];i&&ce.postRender(()=>i(e,Wr(e)))}function kl(t,e,n){const{props:s}=t;if(t.current instanceof HTMLButtonElement&&t.current.disabled)return;t.animationState&&s.whileTap&&t.animationState.setActive(\"whileTap\",\"Start\"===n);const i=s[\"onTap\"+(\"End\"===n?\"\":n)];i&&ce.postRender(()=>i(e,Wr(e)))}const jl=new WeakMap,Ml=new WeakMap,El=t=>{const e=jl.get(t.target);e&&e(t)},Al=t=>{t.forEach(El)};function Cl(t,e,n){const s=function({root:t,...e}){const n=t||document;Ml.has(n)||Ml.set(n,{});const s=Ml.get(n),i=JSON.stringify(e);return s[i]||(s[i]=new IntersectionObserver(Al,{root:t,...e})),s[i]}(e);return jl.set(t,n),s.observe(t),()=>{jl.delete(t),s.unobserve(t)}}const Vl={some:0,all:1};const Dl=Fo({...zr,...{inView:{Feature:class extends Or{constructor(){super(...arguments),this.hasEnteredView=!1,this.isInView=!1}startObserver(){this.unmount();const{viewport:t={}}=this.node.getProps(),{root:e,margin:n,amount:s=\"some\",once:i}=t,o={root:e?e.current:void 0,rootMargin:n,threshold:\"number\"==typeof s?s:Vl[s]};return Cl(this.node.current,o,t=>{const{isIntersecting:e}=t;if(this.isInView===e)return;if(this.isInView=e,i&&!e&&this.hasEnteredView)return;e&&(this.hasEnteredView=!0),this.node.animationState&&this.node.animationState.setActive(\"whileInView\",e);const{onViewportEnter:n,onViewportLeave:s}=this.node.getProps(),o=e?n:s;o&&o(t)})}mount(){this.startObserver()}update(){if(\"undefined\"==typeof IntersectionObserver)return;const{props:t,prevProps:e}=this.node;[\"amount\",\"margin\",\"root\"].some(function({viewport:t={}},{viewport:e={}}={}){return n=>t[n]!==e[n]}(t,e))&&this.startObserver()}unmount(){}}},tap:{Feature:class extends Or{mount(){const{current:t}=this.node;t&&(this.unmount=xi(t,(t,e)=>(kl(this.node,e,\"Start\"),(t,{success:e})=>kl(this.node,t,e?\"End\":\"Cancel\")),{useGlobalTarget:this.node.props.globalTapTarget}))}unmount(){}}},focus:{Feature:class extends Or{constructor(){super(...arguments),this.isActive=!1}onFocus(){let t=!1;try{t=this.node.current.matches(\":focus-visible\")}catch(e){t=!0}t&&this.node.animationState&&(this.node.animationState.setActive(\"whileFocus\",!0),this.isActive=!0)}onBlur(){this.isActive&&this.node.animationState&&(this.node.animationState.setActive(\"whileFocus\",!1),this.isActive=!1)}mount(){this.unmount=Ot(_r(this.node.current,\"focus\",()=>this.onFocus()),_r(this.node.current,\"blur\",()=>this.onBlur()))}unmount(){}}},hover:{Feature:class extends Or{mount(){const{current:t}=this.node;t&&(this.unmount=function(t,e,n={}){const[s,i,o]=ui(t,n),r=t=>{if(!ci(t))return;const{target:n}=t,s=e(n,t);if(\"function\"!=typeof s||!n)return;const o=t=>{ci(t)&&(s(t),n.removeEventListener(\"pointerleave\",o))};n.addEventListener(\"pointerleave\",o,i)};return s.forEach(t=>{t.addEventListener(\"pointerenter\",r,i)}),o}(t,(t,e)=>(Pl(this.node,e,\"Start\"),t=>Pl(this.node,t,\"End\"))))}unmount(){}}}},...Tl,...{layout:{ProjectionNode:Sl,MeasureLayout:wa}}},dr);function Rl({icon:t,label:s,isActive:i,onClick:o,isPlanned:r=!1,tooltipSide:a=\"right\",tooltipDelay:l=500,showDot:u=!1}){const[d,m]=e.useState(!1),p=e.useRef(null),f=e.useRef(null),[g,y]=e.useState({top:0,left:0}),v=St();e.useEffect(()=>{if(d&&f.current){const t=f.current.getBoundingClientRect();y(\"right\"===a?{top:t.top+t.height/2,left:t.right+8}:{top:t.top-8,left:t.left+t.width/2})}},[d,a]);e.useEffect(()=>()=>{p.current&&window.clearTimeout(p.current)},[]);const x=d&&!v?n.jsx(\"div\",{className:c(\"fixed z-[9999] px-2 py-1 text-xs font-medium whitespace-nowrap\",\"bg-popover text-popover-foreground\",\"rounded-md shadow-md border border-border\",\"animate-in fade-in-0 zoom-in-95 duration-200\"),style:{top:g.top,left:g.left,transform:\"top\"===a?\"translate(-50%, -100%)\":\"translateY(-50%)\"},role:\"tooltip\",children:s}):null;return n.jsxs(\"div\",{className:\"relative flex-shrink-0\",children:[n.jsxs(Dl.button,{ref:f,onClick:o,onMouseEnter:()=>{v||(p.current=window.setTimeout(()=>{m(!0)},l))},onMouseLeave:()=>{p.current&&(window.clearTimeout(p.current),p.current=null),m(!1)},className:c(\"relative p-3 rounded-xl transition-colors duration-200\",\"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",i?\"bg-accent text-accent-foreground\":\"text-muted-foreground hover:bg-accent/50 hover:text-foreground\"),whileTap:{scale:.95},transition:{type:\"spring\",stiffness:400,damping:25},\"aria-label\":s,\"aria-current\":i?\"page\":void 0,children:[n.jsx(t,{className:\"size-5\",strokeWidth:2}),r&&n.jsx(\"span\",{className:\"absolute -top-1 -right-1 size-2 bg-ring rounded-full\"}),u&&n.jsx(\"span\",{className:\"absolute top-1 right-1 size-2 bg-red-500 rounded-full border border-background\"})]}),\"undefined\"!=typeof document&&h.createPortal(x,document.body)]})}function Ll({isAdmin:t,className:s}){const{t:i}=a(),{currentModule:o,setModule:r,versionInfo:l}=bt(),u=St(),h=e.useRef(null),[m,p]=e.useState(!1),[f,g]=e.useState(!1),[y,v]=e.useState(!1),[x,w]=e.useState(!1),b=e.useCallback(()=>{if(!h.current)return;const{scrollTop:t,scrollHeight:e,clientHeight:n,scrollLeft:s,scrollWidth:i,clientWidth:o}=h.current;p(t>5),g(e-t-n>5),v(s>5),w(i-s-o>5)},[]);e.useEffect(()=>{const t=h.current;if(!t)return;b(),t.addEventListener(\"scroll\",b),window.addEventListener(\"resize\",b);const e=new ResizeObserver(b);return e.observe(t),()=>{t.removeEventListener(\"scroll\",b),window.removeEventListener(\"resize\",b),e.disconnect()}},[b]);const S=[{id:\"dashboard\",icon:ut,labelKey:\"ui.nav.menuDashboard\"},{id:\"vaults\",icon:ct,labelKey:\"ui.nav.menuVaults\"},{id:\"notes\",icon:mt,labelKey:\"ui.nav.menuNotes\"},{id:\"files\",icon:pt,labelKey:\"ui.nav.menuFiles\"},{id:\"trash\",icon:Q,labelKey:\"ui.nav.menuTrash\"},{id:\"settings\",icon:at,labelKey:\"ui.nav.menuSettingsBrowser\"},{id:\"sync-logs\",icon:dt,labelKey:\"ui.nav.menuSyncLogs\"},{id:\"sync\",icon:ot,labelKey:\"ui.nav.menuSync\"},{id:\"git\",icon:lt,labelKey:\"ui.nav.menuGit\"}].filter(e=>!e.adminOnly||t);return n.jsxs(\"div\",{className:c(\"fixed bottom-1 left-1/2 -translate-x-1/2 z-50 max-w-[calc(100vw-3rem)] w-auto\",\"md:relative md:bottom-auto md:left-auto md:translate-x-0 md:pt-6 md:pb-10 md:pl-4 md:max-w-none group/nav\",s),children:[!u&&m&&n.jsx(\"div\",{className:\"hidden md:flex absolute top-4 left-[25px] z-10 text-primary pointer-events-none w-[44px] justify-center animate-bounce\",children:n.jsx(st,{className:\"h-4 w-4\"})}),u&&y&&n.jsx(\"div\",{className:\"flex md:hidden absolute left-[-4px] top-1/2 -translate-y-1/2 z-10 text-primary pointer-events-none animate-bounce [animation-direction:alternate] [transform-origin:center]\",style:{animationName:\"bounce\",transform:\"translateY(-50%)\"},children:n.jsx(nt,{className:\"h-5 w-5\"})}),n.jsxs(\"nav\",{ref:h,\"aria-label\":i(\"ui.nav.mainNavigation\"),className:c(\"flex items-center gap-1 p-2 overflow-x-auto no-scrollbar\",\"md:flex-col md:gap-1 md:p-2 md:overflow-y-auto md:max-h-[calc(100vh-6rem)] no-scrollbar\",\"bg-sidebar text-sidebar-foreground border border-sidebar-border rounded-lg\",\"custom-shadow backdrop-blur-sm relative\"),children:[S.map(t=>n.jsxs(e.Fragment,{children:[n.jsx(Rl,{icon:t.icon,label:i(t.labelKey),isActive:o===t.id,onClick:()=>r(t.id),tooltipSide:\"right\",showDot:\"config\"===t.id&&!!(null==l?void 0:l.versionIsNew)}),\"dashboard\"===t.id&&n.jsx(\"div\",{className:\"hidden md:block w-8 h-px bg-border/50 my-1 flex-shrink-0\"})]},t.id)),t&&n.jsxs(e.Fragment,{children:[n.jsx(\"div\",{className:\"hidden md:block w-8 h-px bg-border/50 my-1 flex-shrink-0\"}),n.jsx(Rl,{icon:ft,label:i(\"ui.nav.menuSettings\"),isActive:\"config\"===o,onClick:()=>r(\"config\"),tooltipSide:\"right\",showDot:!!(null==l?void 0:l.versionIsNew)},\"config\")]},\"settings-group\")]}),u&&x&&n.jsx(\"div\",{className:\"flex md:hidden absolute right-[-4px] top-1/2 -translate-y-1/2 z-10 text-primary pointer-events-none animate-bounce [animation-direction:alternate]\",style:{transform:\"translateY(-50%)\"},children:n.jsx(d,{className:\"h-5 w-5\"})}),!u&&f&&n.jsx(\"div\",{className:\"hidden md:flex absolute bottom-2 left-[25px] z-10 text-primary pointer-events-none w-[44px] justify-center animate-bounce\",children:n.jsx(et,{className:\"h-4 w-4\"})})]})}const Nl=w,Il=p,Bl=e.forwardRef(({className:t,...e},s)=>n.jsx(m,{ref:s,className:c(\"fixed inset-0 z-50 bg-black/80  data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",t),...e}));Bl.displayName=m.displayName;const Fl=e.forwardRef(({className:t,children:e,...s},i)=>{const{t:o}=a();return n.jsxs(Il,{children:[n.jsx(Bl,{}),n.jsxs(f,{ref:i,className:c(\"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-xl duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-xl\",t),...s,children:[e,n.jsxs(g,{className:\"absolute right-4 top-4 rounded-sm opacity-70 transition-all hover:opacity-100 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground\",children:[n.jsx(y,{className:\"h-4 w-4\"}),n.jsx(\"span\",{className:\"sr-only\",children:o(\"ui.common.close\")})]})]})]})});Fl.displayName=f.displayName;const Ol=({className:t,...e})=>n.jsx(\"div\",{className:c(\"flex flex-col space-y-1.5 text-center sm:text-left\",t),...e});Ol.displayName=\"DialogHeader\";const Ul=e.forwardRef(({className:t,...e},s)=>n.jsx(v,{ref:s,className:c(\"text-lg font-semibold leading-none tracking-tight\",t),...e}));Ul.displayName=v.displayName;const zl=e.forwardRef(({className:t,...e},s)=>n.jsx(x,{ref:s,className:c(\"text-sm text-muted-foreground\",t),...e}));zl.displayName=x.displayName;var _l=[\"a\",\"button\",\"div\",\"form\",\"h2\",\"h3\",\"img\",\"input\",\"label\",\"li\",\"nav\",\"ol\",\"p\",\"select\",\"span\",\"svg\",\"ul\"].reduce((t,s)=>{const i=b(`Primitive.${s}`),o=e.forwardRef((t,e)=>{const{asChild:o,...r}=t,a=o?i:s;return\"undefined\"!=typeof window&&(window[Symbol.for(\"radix-ui\")]=!0),n.jsx(a,{...r,ref:e})});return o.displayName=`Primitive.${s}`,{...t,[s]:o}},{}),Wl=e.forwardRef((t,e)=>n.jsx(_l.label,{...t,ref:e,onMouseDown:e=>{var n;e.target.closest(\"button, input, select, textarea\")||(null==(n=t.onMouseDown)||n.call(t,e),!e.defaultPrevented&&e.detail>1&&e.preventDefault())}}));Wl.displayName=\"Label\";var $l=Wl;const Yl=S(\"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\"),Kl=e.forwardRef(({className:t,...e},s)=>n.jsx($l,{ref:s,className:c(Yl(),t),...e}));function Xl({close:t}){const{t:l}=a(),[u,c]=e.useState(\"\"),[h,d]=e.useState(\"\"),[m,p]=e.useState(\"\"),[f,g]=e.useState(!1);return n.jsxs(\"form\",{onSubmit:async e=>{if(e.preventDefault(),h===m){g(!0);try{const e=localStorage.getItem(\"token\"),n=new FormData;n.append(\"oldPassword\",u),n.append(\"password\",h),n.append(\"confirmPassword\",m);const a=await fetch(s(r.API_URL+\"/api/user/change_password\"),{method:\"POST\",headers:i({token:e,includeDomain:!1,includeContentType:!1}),body:n}),c=await a.json();!0===c.status?(o.success(c.message||l(\"ui.auth.passwordChangedSuccess\")),t()):o.error(c.details||c.message||l(\"ui.auth.passwordChangeFailed\"))}catch{o.error(l(\"ui.auth.passwordChangeFailed\"))}finally{g(!1)}}else o.error(l(\"ui.auth.passwordMismatch\"))},className:\"space-y-4\",children:[n.jsxs(\"div\",{className:\"space-y-2\",children:[n.jsx(Kl,{htmlFor:\"oldPassword\",children:l(\"ui.auth.currentPassword\")}),n.jsx(T,{id:\"oldPassword\",type:\"password\",value:u,onChange:t=>c(t.target.value),required:!0})]}),n.jsxs(\"div\",{className:\"space-y-2\",children:[n.jsx(Kl,{htmlFor:\"newPassword\",children:l(\"ui.auth.newPassword\")}),n.jsx(T,{id:\"newPassword\",type:\"password\",value:h,onChange:t=>d(t.target.value),required:!0})]}),n.jsxs(\"div\",{className:\"space-y-2\",children:[n.jsx(Kl,{htmlFor:\"confirmPassword\",children:l(\"ui.auth.confirmNewPassword\")}),n.jsx(T,{id:\"confirmPassword\",type:\"password\",value:m,onChange:t=>p(t.target.value),required:!0})]}),n.jsxs(\"div\",{className:\"flex justify-end space-x-2 pt-4\",children:[n.jsx(P,{type:\"button\",variant:\"outline\",onClick:t,children:l(\"ui.common.cancel\")}),n.jsx(P,{type:\"submit\",disabled:f,children:l(f?\"ui.auth.submitting\":\"ui.auth.changePassword\")})]})]})}function Hl({onLogout:t,className:s}){var i,l,u,h;const{t:d}=a(),[m,p]=e.useState(!1),[f,g]=e.useState(!1),[y,v]=e.useState(!1),[x,w]=e.useState(!1),{toastPosition:b,setToastPosition:S}=k(),T=localStorage.getItem(\"uid\"),O=localStorage.getItem(\"username\"),U=e.useCallback(()=>JSON.stringify({api:r.API_URL,apiToken:localStorage.getItem(\"token\")||\"\"},null,2),[]),z=e.useCallback(()=>{const t=r.API_URL,e=localStorage.getItem(\"token\")||\"\";return`obsidian://fast-note-sync/sso?pushApi=${encodeURIComponent(t)}&pushApiToken=${encodeURIComponent(e)}`},[]);return n.jsxs(j,{open:m,onOpenChange:p,children:[n.jsx(M,{asChild:!0,children:n.jsx(\"button\",{className:c(\"size-9 rounded-full bg-muted flex items-center justify-center\",\"transition-all duration-200\",\"ring-2 ring-ring/30\",\"hover:ring-ring/50\",\"focus-visible:outline-none focus-visible:ring-ring\",m&&\"ring-ring\",s),\"aria-label\":d(\"ui.auth.userUid\",{uid:T}),children:n.jsx(\"span\",{className:\"text-sm font-medium text-muted-foreground\",children:(null==(i=null==O?void 0:O.charAt(0))?void 0:i.toUpperCase())||(null==(l=null==T?void 0:T.charAt(0))?void 0:l.toUpperCase())||\"U\"})})}),n.jsxs(E,{align:\"end\",className:\"w-56 rounded-xl shadow-lg p-2\",sideOffset:8,children:[n.jsxs(\"div\",{className:\"flex items-center gap-3 px-2 py-3 mb-2 bg-muted/30 rounded-lg\",children:[n.jsx(\"div\",{className:\"size-10 rounded-full bg-primary/10 flex items-center justify-center text-primary font-bold text-lg ring-2 ring-background shadow-sm\",children:(null==(u=null==O?void 0:O.charAt(0))?void 0:u.toUpperCase())||(null==(h=null==T?void 0:T.charAt(0))?void 0:h.toUpperCase())||\"U\"}),n.jsxs(\"div\",{className:\"flex flex-col min-w-0\",children:[n.jsx(\"span\",{className:\"font-semibold text-sm truncate\",children:O||d(\"ui.auth.unknownUser\")}),n.jsxs(\"span\",{className:\"text-xs text-muted-foreground truncate font-mono\",children:[\"UID: \",T]})]})]}),n.jsx(A,{className:\"-mx-2 mb-2\"}),n.jsxs(C,{children:[n.jsxs(V,{children:[n.jsxs(D,{className:\"rounded-lg\",children:[n.jsx(tt,{className:\"mr-2 size-4 text-muted-foreground\"}),n.jsx(\"span\",{children:d(\"ui.settings.toastPosition\")})]}),n.jsx(R,{children:n.jsx(L,{className:\"rounded-xl\",children:n.jsx(N,{value:b,onValueChange:t=>S(t),children:[\"top-left\",\"top-center\",\"top-right\",\"bottom-left\",\"bottom-center\",\"bottom-right\"].map(t=>n.jsx(I,{value:t,className:\"rounded-lg cursor-pointer\",children:d(`ui.settings.position.${t}`)},t))})})})]}),n.jsxs(B,{onClick:()=>{p(!1),v(!0)},className:\"rounded-lg cursor-pointer\",children:[n.jsx(F,{className:\"mr-2 size-4 text-muted-foreground\"}),d(\"ui.auth.changePassword\")]})]}),n.jsx(A,{className:\"my-1\"}),n.jsx(C,{children:n.jsxs(B,{onClick:()=>{w(!1),g(!0),p(!1)},className:\"rounded-lg cursor-pointer\",children:[n.jsx(it,{className:\"mr-2 size-4 text-muted-foreground\"}),d(\"ui.vault.authTokenConfig\")]})}),n.jsx(A,{className:\"my-1\"}),n.jsxs(B,{onClick:()=>{p(!1),t()},className:\"text-destructive focus:text-destructive rounded-lg cursor-pointer focus:bg-destructive/10\",children:[n.jsx(ht,{className:\"mr-2 size-4\"}),d(\"ui.auth.logout\")]})]}),n.jsx(Nl,{open:f,onOpenChange:g,children:n.jsxs(Fl,{className:\"w-[calc(100vw-2rem)] max-w-2xl mx-auto rounded-lg sm:rounded-xl\",children:[n.jsx(Ol,{children:n.jsx(Ul,{className:\"text-base sm:text-lg truncate pr-8\",children:d(x?\"ui.obsidian.copyConfigError\":\"ui.vault.authTokenConfig\")})}),n.jsxs(\"div\",{className:\"space-y-4\",children:[n.jsx(\"pre\",{className:\"p-3 sm:p-4 rounded-xl bg-muted text-xs sm:text-sm overflow-x-auto max-h-48 sm:max-h-64 font-mono whitespace-pre-wrap break-all\",children:U()}),n.jsxs(\"div\",{className:\"flex flex-col-reverse sm:flex-row justify-end gap-2 text-nowrap\",children:[n.jsx(P,{variant:\"outline\",onClick:()=>g(!1),className:\"w-full sm:w-auto rounded-xl\",children:d(\"ui.common.close\")}),n.jsxs(P,{className:\"w-full sm:w-auto rounded-xl bg-sky-700 hover:bg-sky-900 text-white transition-colors border-none shadow-sm\",onClick:()=>{window.location.href=z()},children:[n.jsx(rt,{className:\"h-4 w-4 mr-2\"}),d(\"ui.obsidian.oneClickImport\")]}),n.jsxs(P,{onClick:()=>{navigator.clipboard.writeText(U()).then(()=>o.success(d(\"ui.obsidian.copyConfigSuccess\"))).catch(t=>o.error(d(\"ui.common.error\")+t))},className:\"w-full sm:w-auto rounded-xl\",children:[n.jsx(it,{className:\"h-4 w-4 mr-2\"}),d(\"ui.vault.copyConfig\")]})]})]})]})}),n.jsx(Nl,{open:y,onOpenChange:v,children:n.jsxs(Fl,{className:\"max-w-md rounded-xl\",children:[n.jsx(Ol,{children:n.jsx(Ul,{children:d(\"ui.auth.changePassword\")})}),n.jsx(Xl,{close:()=>v(!1)})]})})]})}function ql({className:t}){const{t:e}=a(),{theme:s,resolvedTheme:i,setTheme:o}=O();return n.jsxs(\"div\",{className:c(\"flex items-center gap-1\",t),children:[n.jsx(P,{variant:\"ghost\",size:\"icon\",className:\"size-9\",onClick:()=>{o(\"light\"===s?\"dark\":\"dark\"===s?\"auto\":\"light\")},\"aria-label\":e(\"ui.common.toggleTheme\"),title:e(\"auto\"===s?\"ui.settings.themeAuto\":\"dark\"===i?\"ui.settings.themeDark\":\"ui.settings.themeLight\"),children:\"auto\"===s?n.jsx(U,{className:\"size-5 text-primary\"}):\"dark\"===i?n.jsx(z,{className:\"size-5\"}):n.jsx(_,{className:\"size-5\"})}),n.jsx(W,{className:\"size-9\"}),n.jsx($,{className:\"size-9\"})]})}function Gl({className:t,...e}){return n.jsx(\"div\",{className:c(\"flex items-center justify-center rounded-lg bg-primary text-primary-foreground transition-colors duration-300\",t),...e,children:n.jsx(gt,{className:\"size-3/5\"})})}function Zl({onLogout:t,className:e}){const{t:s}=a(),{currentModule:i}=bt(),o={dashboard:s(\"ui.nav.menuDashboard\"),vaults:s(\"ui.nav.menuVaults\"),notes:s(\"ui.nav.menuNotes\"),trash:s(\"ui.nav.menuTrash\"),settings:s(\"ui.nav.menuSettingsBrowser\"),sync:s(\"ui.nav.menuSync\"),git:s(\"ui.nav.menuGit\"),config:s(\"ui.nav.menuSettings\"),files:s(\"ui.nav.menuFiles\"),\"sync-logs\":s(\"ui.nav.menuSyncLogs\")};return n.jsxs(\"header\",{className:c(\"h-14 flex items-center justify-between px-4 md:px-6\",\"sticky top-0 z-50\",\"bg-background/80 backdrop-blur-md\",\"border-b border-sidebar-border shadow-md\",e),children:[n.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[n.jsx(Gl,{className:\"size-8 shrink-0\"}),n.jsx(Ri,{mode:\"wait\",children:n.jsx(Dl.h1,{initial:{opacity:0,y:-10},animate:{opacity:1,y:0},exit:{opacity:0,y:10},transition:{duration:.2},className:\"text-xl font-semibold text-foreground\",children:o[i]},i)})]}),n.jsxs(\"div\",{className:\"flex items-center gap-1\",children:[n.jsx(ql,{}),n.jsx(Hl,{onLogout:t})]})]})}function Jl({children:t,isAdmin:e=!1,onLogout:s,className:i}){const{zenMode:o}=bt(),{colorScheme:r}=k();return St(),n.jsxs(\"div\",{className:c(\"h-dvh md:h-screen overflow-x-hidden md:overflow-hidden bg-background\",i),children:[\"default\"===r&&n.jsx(Y,{}),n.jsxs(\"div\",{className:\"flex flex-col h-full relative z-10\",children:[!o&&n.jsx(Zl,{onLogout:s}),n.jsxs(\"div\",{className:\"flex-1 flex min-h-0 overflow-hidden\",children:[!o&&n.jsx(Ll,{isAdmin:e}),n.jsx(Tt,{children:t})]})]})]})}Kl.displayName=$l.displayName;const Ql=e.lazy(()=>K(()=>import(\"./note-manager-DjJcxkCE.js\"),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28])).then(t=>({default:t.NoteManager}))),tu=e.lazy(()=>K(()=>import(\"./file-manager-Bz0QGSbU.js\"),__vite__mapDeps([29,1,2,11,8,12,13,14,15,16,17,18,7,6,30,31,22,23,25,26,27,28])).then(t=>({default:t.FileManager}))),eu=e.lazy(()=>K(()=>import(\"./system-settings-DSUsRYMo.js\"),__vite__mapDeps([32,1,2,4,7,6,33,23,34,9,35,8,26,36,37,21,38,27,25,20,14,31])).then(t=>({default:t.SystemSettings}))),nu=e.lazy(()=>K(()=>import(\"./vault-list-BzYzvdPK.js\"),__vite__mapDeps([39,1,2,8,22,23,24,25,15,27])).then(t=>({default:t.VaultList}))),su=e.lazy(()=>K(()=>import(\"./auth-form-BjZ9qVzL.js\"),__vite__mapDeps([40,1,2,41,37])).then(t=>({default:t.AuthForm}))),iu=e.lazy(()=>K(()=>import(\"./sync-backup-Bp7n2yHp.js\"),__vite__mapDeps([42,1,2,5,19,7,41,6,20,43,8,23,24,10,15,27,34,25,38,36,21])).then(t=>({default:t.SyncBackup}))),ou=e.lazy(()=>K(()=>import(\"./git-automation-tBJ0Wppw.js\"),__vite__mapDeps([44,1,2,5,25,34,7,41,6,20,43,23,24,10,15,27,33])).then(t=>({default:t.GitAutomation}))),ru=e.lazy(()=>K(()=>import(\"./setting-manager-DaP9o-yD.js\"),__vite__mapDeps([45,1,2,13,8,5,46,17,25,28,15,27,30,7,22,23,24])).then(t=>({default:t.SettingManager}))),au=e.lazy(()=>K(()=>import(\"./sync-log-manager-Zjq-lA99.js\"),__vite__mapDeps([47,1,2,5,9,8,25,38,46,35,17,7,23])).then(t=>({default:t.SyncLogManager}))),lu=()=>n.jsx(\"div\",{className:\"flex items-center justify-center h-64\",children:n.jsx(\"div\",{className:\"animate-spin rounded-full h-8 w-8 border-b-2 border-primary\"})});function uu(){const{t:t}=a(),{isLoggedIn:l,login:u,logout:c}=(()=>{const t=e.useContext(yt);if(!t)throw new Error(\"useAuth must be used within an AuthProvider\");return t})(),{handleVaultList:h}=xt(),{handleUserInfo:d}=function(){const{t:t}=a(),n=localStorage.getItem(\"token\"),l=e.useCallback(async e=>{try{const a=await fetch(s(r.API_URL+\"/api/user/info\"),{method:\"GET\",headers:i({token:n})});if(!a.ok)return;(await a.json()).data||(o.error(t(\"sessionExpired\")),e())}catch(a){}},[n,t]),u=e.useCallback(async(t,e)=>{const a={...t},l=await fetch(s(r.API_URL+\"/api/user/change_password\"),{method:\"POST\",body:JSON.stringify(a),headers:i({token:n})});if(!l.ok)throw new Error(\"Network response was not ok\");const u=await l.json();u.code<100&&u.code>0?(o.success(u.message),e(t)):o.error(u.message+\": \"+u.details)},[n]);return e.useMemo(()=>({handleUserChangePassword:u,handleUserInfo:l}),[u,l])}(),m=bt(t=>t.currentModule),p=bt(t=>t.setModule),f=bt(t=>t.zenMode),g=bt(t=>t.setZenMode),y=bt(t=>t.resetState),v=bt(t=>t.trashType),[x,w]=e.useState(null),b=e.useRef(null);e.useEffect(()=>{b.current=x},[x]),function(t,n){const{currentModule:s,setModule:i,trashType:o}=bt(),r=e.useRef(!1),a=e.useRef(t),l=e.useRef(s);e.useEffect(()=>{a.current=t},[t]),e.useEffect(()=>{l.current=s},[s]),e.useEffect(()=>{const t=()=>{r.current=!0;const t=new URLSearchParams(window.location.search),e=t.get(\"vault\"),s=t.get(\"type\");e&&e!==a.current&&n(e);let o=\"dashboard\";t.has(\"dashboard\")?o=\"dashboard\":t.has(\"vaults\")?o=\"vaults\":t.has(\"config\")?o=\"config\":t.has(\"notes\")?o=\"notes\":t.has(\"files\")?o=\"files\":t.has(\"trash\")?o=\"trash\":t.has(\"sync\")?o=\"sync\":t.has(\"git\")?o=\"git\":t.has(\"settings\")?o=\"settings\":t.has(\"sync-logs\")&&(o=\"sync-logs\"),(l.current!==o||\"trash\"===o&&s)&&(\"trash\"===o&&s?i(o,s):i(o)),setTimeout(()=>{r.current=!1},0)};return t(),window.addEventListener(\"popstate\",t),()=>{window.removeEventListener(\"popstate\",t)}},[i,n]),e.useEffect(()=>{if(r.current)return;const e=new URLSearchParams;e.set(s,\"\"),t&&e.set(\"vault\",t),\"trash\"===s&&o&&e.set(\"type\",o);const n=e.toString().replace(/=(?=&|$)/g,\"\"),i=window.location.pathname+(n?`?${n}`:\"\");window.location.search!==(n?`?${n}`:\"\")&&window.history.pushState(null,\"\",i)},[s,t,o])}(x,w);const[S,T]=e.useState(!1),[P,k]=e.useState(!0),[j,M]=e.useState(null),[E,A]=e.useState(!1),C=localStorage.getItem(\"uid\")?parseInt(localStorage.getItem(\"uid\")):null,V=null!==j&&null!==C&&(0===j||j===C);e.useEffect(()=>{l&&d(c)},[l,d,c]),e.useEffect(()=>{if(\"notes\"!==m&&\"files\"!==m&&\"trash\"!==m&&\"settings\"!==m||!l)return;let e=!0;T(!1);return(async()=>{try{await h(n=>{if(e){if(n.length>0){const t=b.current;return t&&n.some(e=>e.vault===t)||w(n[0].vault),void T(!0)}o.warning(t(\"ui.vault.pleaseCreateVault\")),p(\"vaults\"),T(!0)}})}catch(n){if(!e)return;o.error(n instanceof Error?n.message:String(n)),T(!0)}})(),()=>{e=!1}},[m,l,h,t,p]),e.useEffect(()=>{l&&\"config\"===m&&E&&!V&&(o.warning(t(\"ui.settings.onlyAdminAccess\")),p(\"vaults\"))},[l,m,E,V,p,t]);const D=e.useCallback(t=>{X(t)},[]);e.useEffect(()=>{let t=!0;return(async()=>{try{const e=r.API_URL.endsWith(\"/\")?r.API_URL.slice(0,-1):r.API_URL,n=await fetch(s(`${e}/api/webgui/config`),{headers:i({token:null,includeContentType:!1,includeDomain:!1})});if(n.ok&&t){const t=await n.json();t.code>0&&t.data&&(D(t.data.fontSet||t.data.FontSet||\"\"),void 0!==t.data.registerIsEnable&&k(t.data.registerIsEnable),void 0!==t.data.adminUid&&M(t.data.adminUid))}}catch(e){t&&o.error(e instanceof Error?e.message:String(e))}finally{t&&A(!0)}})(),()=>{t=!1}},[D]);const R=e.useCallback(()=>{u()},[u]),L=e.useCallback(()=>{c(),y()},[c,y]),N=e.useCallback(()=>{g(!f)},[f,g]),I=e.useCallback(()=>{p(\"vaults\")},[p]);if(!l)return n.jsx(\"div\",{className:\"w-full min-h-screen\",children:n.jsx(e.Suspense,{fallback:n.jsx(lu,{}),children:n.jsx(su,{onSuccess:R,registerIsEnable:P})})});return n.jsx(Jl,{isAdmin:V,onLogout:L,children:n.jsx(e.Suspense,{fallback:n.jsx(lu,{}),children:(()=>{switch(m){case\"dashboard\":return n.jsx(eu,{isDashboard:!0,isAdmin:V,onBack:I});case\"notes\":return S&&x?n.jsx(Ql,{vault:x,onVaultChange:w,onNavigateToVaults:()=>p(\"vaults\"),isMaximized:f,onToggleMaximize:N},\"notes\"):n.jsx(\"div\",{className:\"flex items-center justify-center h-64\",children:n.jsx(\"div\",{className:\"animate-spin rounded-full h-8 w-8 border-b-2 border-primary\"})});case\"files\":return S&&x?n.jsx(tu,{vault:x,onVaultChange:w,onNavigateToVaults:()=>p(\"vaults\")}):n.jsx(\"div\",{className:\"flex items-center justify-center h-64\",children:n.jsx(\"div\",{className:\"animate-spin rounded-full h-8 w-8 border-b-2 border-primary\"})});case\"trash\":return S&&x?\"files\"===v?n.jsx(tu,{vault:x,onVaultChange:w,onNavigateToVaults:()=>p(\"vaults\"),isRecycle:!0}):n.jsx(Ql,{vault:x,onVaultChange:w,onNavigateToVaults:()=>p(\"vaults\"),isMaximized:f,onToggleMaximize:N,isRecycle:!0},\"trash\"):n.jsx(\"div\",{className:\"flex items-center justify-center h-64\",children:n.jsx(\"div\",{className:\"animate-spin rounded-full h-8 w-8 border-b-2 border-primary\"})});case\"config\":return E?V?n.jsx(eu,{onBack:I}):null:n.jsx(\"div\",{className:\"flex items-center justify-center h-64\",children:n.jsx(\"div\",{className:\"animate-spin rounded-full h-8 w-8 border-b-2 border-primary\"})});case\"sync\":return n.jsx(iu,{});case\"git\":return n.jsx(ou,{});case\"settings\":return n.jsx(ru,{vault:x||\"\",onVaultChange:w,onNavigateToVaults:()=>p(\"vaults\")});case\"sync-logs\":return n.jsx(au,{vault:x,onVaultChange:w});default:return n.jsx(nu,{onNavigateToNotes:t=>{w(t),p(\"notes\")},onNavigateToAttachments:t=>{w(t),p(\"files\")}})}})()})})}(()=>{const t=localStorage.getItem(\"app-settings\");if(t)try{const{state:e}=JSON.parse(t);(null==e?void 0:e.colorScheme)&&document.documentElement.setAttribute(\"data-color-scheme\",e.colorScheme)}catch{}})();(()=>{const t=window.matchMedia(\"(prefers-color-scheme: dark)\"),e=t=>{const e=t?\"/static/images/icon.svg\":\"/static/images/icon-black.svg\";[\"link[rel='icon']\",\"link[rel='shortcut icon']\",\"link[rel='apple-touch-icon']\",\"link[rel='apple-touch-icon-precomposed']\"].forEach(t=>{document.querySelectorAll(t).forEach(t=>{var n;const s=t.cloneNode(!0);s.href=e,null==(n=t.parentNode)||n.replaceChild(s,t)})})};e(t.matches);try{t.addEventListener(\"change\",t=>e(t.matches))}catch(n){t.addListener(t=>e(t.matches))}})(),H.createRoot(document.getElementById(\"root\")).render(n.jsx(q.StrictMode,{children:n.jsx(G,{defaultTheme:\"auto\",children:n.jsx(vt,{children:n.jsxs(Z,{children:[n.jsx(uu,{}),n.jsx(J,{})]})})})}));export{Ri as A,nt as C,Nl as D,rt as E,at as F,lt as G,Kl as L,mt as N,pt as P,gt as W,Fl as a,Ol as b,Ul as c,zl as d,et as e,xt as f,ct as g,it as h,ot as i,dt as j,st as k,Dl as m,bt as u};\n"
  },
  {
    "path": "frontend/assets/markdown-editor-CX5kQlgI.js",
    "content": "var e,t;import{c as n,a9 as r,j as a,aa as i,ab as s,r as o,G as l,u as c,t as u,f as d,a8 as p,e as h,X as f,ac as m,ad as g,a5 as b,a6 as E,ae as y}from\"./font-loader-CIrh3KnA.js\";import{m as _,E as k,p as T,R as S}from\"./index-JfsWWBj_.js\";import{Z as v}from\"./zap-CLLhzk_y.js\";import{P as A}from\"./pencil-DqQhr35g.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const N=n(\"Bug\",[[\"path\",{d:\"m8 2 1.88 1.88\",key:\"fmnt4t\"}],[\"path\",{d:\"M14.12 3.88 16 2\",key:\"qol33r\"}],[\"path\",{d:\"M9 7.13v-1a3.003 3.003 0 1 1 6 0v1\",key:\"d7y7pr\"}],[\"path\",{d:\"M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6\",key:\"xs1cw7\"}],[\"path\",{d:\"M12 20v-9\",key:\"1qisl0\"}],[\"path\",{d:\"M6.53 9C4.6 8.8 3 7.1 3 5\",key:\"32zzws\"}],[\"path\",{d:\"M6 13H2\",key:\"82j7cp\"}],[\"path\",{d:\"M3 21c0-2.1 1.7-3.9 3.8-4\",key:\"4p0ekp\"}],[\"path\",{d:\"M20.97 5c0 2.1-1.6 3.8-3.5 4\",key:\"18gb23\"}],[\"path\",{d:\"M22 13h-4\",key:\"1jl80f\"}],[\"path\",{d:\"M17.2 17c2.1.1 3.8 1.9 3.8 4\",key:\"k3fwyw\"}]]),C=n(\"Calendar\",[[\"path\",{d:\"M8 2v4\",key:\"1cmpym\"}],[\"path\",{d:\"M16 2v4\",key:\"4m81vk\"}],[\"rect\",{width:\"18\",height:\"18\",x:\"3\",y:\"4\",rx:\"2\",key:\"1hopcy\"}],[\"path\",{d:\"M3 10h18\",key:\"8toen8\"}]]),x=n(\"ClipboardList\",[[\"rect\",{width:\"8\",height:\"4\",x:\"8\",y:\"2\",rx:\"1\",ry:\"1\",key:\"tgr4d6\"}],[\"path\",{d:\"M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2\",key:\"116196\"}],[\"path\",{d:\"M12 11h4\",key:\"1jrz19\"}],[\"path\",{d:\"M12 16h4\",key:\"n85exb\"}],[\"path\",{d:\"M8 11h.01\",key:\"1dfujw\"}],[\"path\",{d:\"M8 16h.01\",key:\"18s6g9\"}]]),w=n(\"Flame\",[[\"path\",{d:\"M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z\",key:\"96xj49\"}]]),I=n(\"List\",[[\"path\",{d:\"M3 12h.01\",key:\"nlz23k\"}],[\"path\",{d:\"M3 18h.01\",key:\"1tta3j\"}],[\"path\",{d:\"M3 6h.01\",key:\"1rqtza\"}],[\"path\",{d:\"M8 12h13\",key:\"1za7za\"}],[\"path\",{d:\"M8 18h13\",key:\"1lx6n3\"}],[\"path\",{d:\"M8 6h13\",key:\"ik3vkj\"}]]),O=n(\"Quote\",[[\"path\",{d:\"M16 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2 1 1 0 0 1 1 1v1a2 2 0 0 1-2 2 1 1 0 0 0-1 1v2a1 1 0 0 0 1 1 6 6 0 0 0 6-6V5a2 2 0 0 0-2-2z\",key:\"rib7q0\"}],[\"path\",{d:\"M5 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2 1 1 0 0 1 1 1v1a2 2 0 0 1-2 2 1 1 0 0 0-1 1v2a1 1 0 0 0 1 1 6 6 0 0 0 6-6V5a2 2 0 0 0-2-2z\",key:\"1ymkrd\"}]]);\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */function R(){}function D(){}function M(e){const t=[],n=String(e||\"\");let r=n.indexOf(\",\"),a=0,i=!1;for(;!i;){-1===r&&(r=n.length,i=!0);const e=n.slice(a,r).trim();!e&&i||t.push(e),a=r+1,r=n.indexOf(\",\",a)}return t}function P(e,t){const n={};return(\"\"===e[e.length-1]?[...e,\"\"]:e).join((n.padRight?\" \":\"\")+\",\"+(!1===n.padLeft?\"\":\" \")).trim()}const L=/^[$_\\p{ID_Start}][$_\\u{200C}\\u{200D}\\p{ID_Continue}]*$/u,F=/^[$_\\p{ID_Start}][-$_\\u{200C}\\u{200D}\\p{ID_Continue}]*$/u,B={};function U(e,t){return(B.jsx?F:L).test(e)}const H=/[ \\t\\n\\f\\r]/g;function z(e){return\"\"===e.replace(H,\"\")}class G{constructor(e,t,n){this.normal=t,this.property=e,n&&(this.space=n)}}function j(e,t){const n={},r={};for(const a of e)Object.assign(n,a.property),Object.assign(r,a.normal);return new G(n,r,t)}function $(e){return e.toLowerCase()}G.prototype.normal={},G.prototype.property={},G.prototype.space=void 0;class q{constructor(e,t){this.attribute=t,this.property=e}}q.prototype.attribute=\"\",q.prototype.booleanish=!1,q.prototype.boolean=!1,q.prototype.commaOrSpaceSeparated=!1,q.prototype.commaSeparated=!1,q.prototype.defined=!1,q.prototype.mustUseProperty=!1,q.prototype.number=!1,q.prototype.overloadedBoolean=!1,q.prototype.property=\"\",q.prototype.spaceSeparated=!1,q.prototype.space=void 0;let Y=0;const W=ee(),V=ee(),K=ee(),Q=ee(),X=ee(),Z=ee(),J=ee();function ee(){return 2**++Y}const te=Object.freeze(Object.defineProperty({__proto__:null,boolean:W,booleanish:V,commaOrSpaceSeparated:J,commaSeparated:Z,number:Q,overloadedBoolean:K,spaceSeparated:X},Symbol.toStringTag,{value:\"Module\"})),ne=Object.keys(te);class re extends q{constructor(e,t,n,r){let a=-1;if(super(e,t),ae(this,\"space\",r),\"number\"==typeof n)for(;++a<ne.length;){const e=ne[a];ae(this,ne[a],(n&te[e])===te[e])}}}function ae(e,t,n){n&&(e[t]=n)}function ie(e){const t={},n={};for(const[r,a]of Object.entries(e.properties)){const i=new re(r,e.transform(e.attributes||{},r),a,e.space);e.mustUseProperty&&e.mustUseProperty.includes(r)&&(i.mustUseProperty=!0),t[r]=i,n[$(r)]=r,n[$(i.attribute)]=r}return new G(t,n,e.space)}re.prototype.defined=!0;const se=ie({properties:{ariaActiveDescendant:null,ariaAtomic:V,ariaAutoComplete:null,ariaBusy:V,ariaChecked:V,ariaColCount:Q,ariaColIndex:Q,ariaColSpan:Q,ariaControls:X,ariaCurrent:null,ariaDescribedBy:X,ariaDetails:null,ariaDisabled:V,ariaDropEffect:X,ariaErrorMessage:null,ariaExpanded:V,ariaFlowTo:X,ariaGrabbed:V,ariaHasPopup:null,ariaHidden:V,ariaInvalid:null,ariaKeyShortcuts:null,ariaLabel:null,ariaLabelledBy:X,ariaLevel:Q,ariaLive:null,ariaModal:V,ariaMultiLine:V,ariaMultiSelectable:V,ariaOrientation:null,ariaOwns:X,ariaPlaceholder:null,ariaPosInSet:Q,ariaPressed:V,ariaReadOnly:V,ariaRelevant:null,ariaRequired:V,ariaRoleDescription:X,ariaRowCount:Q,ariaRowIndex:Q,ariaRowSpan:Q,ariaSelected:V,ariaSetSize:Q,ariaSort:null,ariaValueMax:Q,ariaValueMin:Q,ariaValueNow:Q,ariaValueText:null,role:null},transform:(e,t)=>\"role\"===t?t:\"aria-\"+t.slice(4).toLowerCase()});function oe(e,t){return t in e?e[t]:t}function le(e,t){return oe(e,t.toLowerCase())}const ce=ie({attributes:{acceptcharset:\"accept-charset\",classname:\"class\",htmlfor:\"for\",httpequiv:\"http-equiv\"},mustUseProperty:[\"checked\",\"multiple\",\"muted\",\"selected\"],properties:{abbr:null,accept:Z,acceptCharset:X,accessKey:X,action:null,allow:null,allowFullScreen:W,allowPaymentRequest:W,allowUserMedia:W,alt:null,as:null,async:W,autoCapitalize:null,autoComplete:X,autoFocus:W,autoPlay:W,blocking:X,capture:null,charSet:null,checked:W,cite:null,className:X,cols:Q,colSpan:null,content:null,contentEditable:V,controls:W,controlsList:X,coords:Q|Z,crossOrigin:null,data:null,dateTime:null,decoding:null,default:W,defer:W,dir:null,dirName:null,disabled:W,download:K,draggable:V,encType:null,enterKeyHint:null,fetchPriority:null,form:null,formAction:null,formEncType:null,formMethod:null,formNoValidate:W,formTarget:null,headers:X,height:Q,hidden:K,high:Q,href:null,hrefLang:null,htmlFor:X,httpEquiv:X,id:null,imageSizes:null,imageSrcSet:null,inert:W,inputMode:null,integrity:null,is:null,isMap:W,itemId:null,itemProp:X,itemRef:X,itemScope:W,itemType:X,kind:null,label:null,lang:null,language:null,list:null,loading:null,loop:W,low:Q,manifest:null,max:null,maxLength:Q,media:null,method:null,min:null,minLength:Q,multiple:W,muted:W,name:null,nonce:null,noModule:W,noValidate:W,onAbort:null,onAfterPrint:null,onAuxClick:null,onBeforeMatch:null,onBeforePrint:null,onBeforeToggle:null,onBeforeUnload:null,onBlur:null,onCancel:null,onCanPlay:null,onCanPlayThrough:null,onChange:null,onClick:null,onClose:null,onContextLost:null,onContextMenu:null,onContextRestored:null,onCopy:null,onCueChange:null,onCut:null,onDblClick:null,onDrag:null,onDragEnd:null,onDragEnter:null,onDragExit:null,onDragLeave:null,onDragOver:null,onDragStart:null,onDrop:null,onDurationChange:null,onEmptied:null,onEnded:null,onError:null,onFocus:null,onFormData:null,onHashChange:null,onInput:null,onInvalid:null,onKeyDown:null,onKeyPress:null,onKeyUp:null,onLanguageChange:null,onLoad:null,onLoadedData:null,onLoadedMetadata:null,onLoadEnd:null,onLoadStart:null,onMessage:null,onMessageError:null,onMouseDown:null,onMouseEnter:null,onMouseLeave:null,onMouseMove:null,onMouseOut:null,onMouseOver:null,onMouseUp:null,onOffline:null,onOnline:null,onPageHide:null,onPageShow:null,onPaste:null,onPause:null,onPlay:null,onPlaying:null,onPopState:null,onProgress:null,onRateChange:null,onRejectionHandled:null,onReset:null,onResize:null,onScroll:null,onScrollEnd:null,onSecurityPolicyViolation:null,onSeeked:null,onSeeking:null,onSelect:null,onSlotChange:null,onStalled:null,onStorage:null,onSubmit:null,onSuspend:null,onTimeUpdate:null,onToggle:null,onUnhandledRejection:null,onUnload:null,onVolumeChange:null,onWaiting:null,onWheel:null,open:W,optimum:Q,pattern:null,ping:X,placeholder:null,playsInline:W,popover:null,popoverTarget:null,popoverTargetAction:null,poster:null,preload:null,readOnly:W,referrerPolicy:null,rel:X,required:W,reversed:W,rows:Q,rowSpan:Q,sandbox:X,scope:null,scoped:W,seamless:W,selected:W,shadowRootClonable:W,shadowRootDelegatesFocus:W,shadowRootMode:null,shape:null,size:Q,sizes:null,slot:null,span:Q,spellCheck:V,src:null,srcDoc:null,srcLang:null,srcSet:null,start:Q,step:null,style:null,tabIndex:Q,target:null,title:null,translate:null,type:null,typeMustMatch:W,useMap:null,value:V,width:Q,wrap:null,writingSuggestions:null,align:null,aLink:null,archive:X,axis:null,background:null,bgColor:null,border:Q,borderColor:null,bottomMargin:Q,cellPadding:null,cellSpacing:null,char:null,charOff:null,classId:null,clear:null,code:null,codeBase:null,codeType:null,color:null,compact:W,declare:W,event:null,face:null,frame:null,frameBorder:null,hSpace:Q,leftMargin:Q,link:null,longDesc:null,lowSrc:null,marginHeight:Q,marginWidth:Q,noResize:W,noHref:W,noShade:W,noWrap:W,object:null,profile:null,prompt:null,rev:null,rightMargin:Q,rules:null,scheme:null,scrolling:V,standby:null,summary:null,text:null,topMargin:Q,valueType:null,version:null,vAlign:null,vLink:null,vSpace:Q,allowTransparency:null,autoCorrect:null,autoSave:null,disablePictureInPicture:W,disableRemotePlayback:W,prefix:null,property:null,results:Q,security:null,unselectable:null},space:\"html\",transform:le}),ue=ie({attributes:{accentHeight:\"accent-height\",alignmentBaseline:\"alignment-baseline\",arabicForm:\"arabic-form\",baselineShift:\"baseline-shift\",capHeight:\"cap-height\",className:\"class\",clipPath:\"clip-path\",clipRule:\"clip-rule\",colorInterpolation:\"color-interpolation\",colorInterpolationFilters:\"color-interpolation-filters\",colorProfile:\"color-profile\",colorRendering:\"color-rendering\",crossOrigin:\"crossorigin\",dataType:\"datatype\",dominantBaseline:\"dominant-baseline\",enableBackground:\"enable-background\",fillOpacity:\"fill-opacity\",fillRule:\"fill-rule\",floodColor:\"flood-color\",floodOpacity:\"flood-opacity\",fontFamily:\"font-family\",fontSize:\"font-size\",fontSizeAdjust:\"font-size-adjust\",fontStretch:\"font-stretch\",fontStyle:\"font-style\",fontVariant:\"font-variant\",fontWeight:\"font-weight\",glyphName:\"glyph-name\",glyphOrientationHorizontal:\"glyph-orientation-horizontal\",glyphOrientationVertical:\"glyph-orientation-vertical\",hrefLang:\"hreflang\",horizAdvX:\"horiz-adv-x\",horizOriginX:\"horiz-origin-x\",horizOriginY:\"horiz-origin-y\",imageRendering:\"image-rendering\",letterSpacing:\"letter-spacing\",lightingColor:\"lighting-color\",markerEnd:\"marker-end\",markerMid:\"marker-mid\",markerStart:\"marker-start\",navDown:\"nav-down\",navDownLeft:\"nav-down-left\",navDownRight:\"nav-down-right\",navLeft:\"nav-left\",navNext:\"nav-next\",navPrev:\"nav-prev\",navRight:\"nav-right\",navUp:\"nav-up\",navUpLeft:\"nav-up-left\",navUpRight:\"nav-up-right\",onAbort:\"onabort\",onActivate:\"onactivate\",onAfterPrint:\"onafterprint\",onBeforePrint:\"onbeforeprint\",onBegin:\"onbegin\",onCancel:\"oncancel\",onCanPlay:\"oncanplay\",onCanPlayThrough:\"oncanplaythrough\",onChange:\"onchange\",onClick:\"onclick\",onClose:\"onclose\",onCopy:\"oncopy\",onCueChange:\"oncuechange\",onCut:\"oncut\",onDblClick:\"ondblclick\",onDrag:\"ondrag\",onDragEnd:\"ondragend\",onDragEnter:\"ondragenter\",onDragExit:\"ondragexit\",onDragLeave:\"ondragleave\",onDragOver:\"ondragover\",onDragStart:\"ondragstart\",onDrop:\"ondrop\",onDurationChange:\"ondurationchange\",onEmptied:\"onemptied\",onEnd:\"onend\",onEnded:\"onended\",onError:\"onerror\",onFocus:\"onfocus\",onFocusIn:\"onfocusin\",onFocusOut:\"onfocusout\",onHashChange:\"onhashchange\",onInput:\"oninput\",onInvalid:\"oninvalid\",onKeyDown:\"onkeydown\",onKeyPress:\"onkeypress\",onKeyUp:\"onkeyup\",onLoad:\"onload\",onLoadedData:\"onloadeddata\",onLoadedMetadata:\"onloadedmetadata\",onLoadStart:\"onloadstart\",onMessage:\"onmessage\",onMouseDown:\"onmousedown\",onMouseEnter:\"onmouseenter\",onMouseLeave:\"onmouseleave\",onMouseMove:\"onmousemove\",onMouseOut:\"onmouseout\",onMouseOver:\"onmouseover\",onMouseUp:\"onmouseup\",onMouseWheel:\"onmousewheel\",onOffline:\"onoffline\",onOnline:\"ononline\",onPageHide:\"onpagehide\",onPageShow:\"onpageshow\",onPaste:\"onpaste\",onPause:\"onpause\",onPlay:\"onplay\",onPlaying:\"onplaying\",onPopState:\"onpopstate\",onProgress:\"onprogress\",onRateChange:\"onratechange\",onRepeat:\"onrepeat\",onReset:\"onreset\",onResize:\"onresize\",onScroll:\"onscroll\",onSeeked:\"onseeked\",onSeeking:\"onseeking\",onSelect:\"onselect\",onShow:\"onshow\",onStalled:\"onstalled\",onStorage:\"onstorage\",onSubmit:\"onsubmit\",onSuspend:\"onsuspend\",onTimeUpdate:\"ontimeupdate\",onToggle:\"ontoggle\",onUnload:\"onunload\",onVolumeChange:\"onvolumechange\",onWaiting:\"onwaiting\",onZoom:\"onzoom\",overlinePosition:\"overline-position\",overlineThickness:\"overline-thickness\",paintOrder:\"paint-order\",panose1:\"panose-1\",pointerEvents:\"pointer-events\",referrerPolicy:\"referrerpolicy\",renderingIntent:\"rendering-intent\",shapeRendering:\"shape-rendering\",stopColor:\"stop-color\",stopOpacity:\"stop-opacity\",strikethroughPosition:\"strikethrough-position\",strikethroughThickness:\"strikethrough-thickness\",strokeDashArray:\"stroke-dasharray\",strokeDashOffset:\"stroke-dashoffset\",strokeLineCap:\"stroke-linecap\",strokeLineJoin:\"stroke-linejoin\",strokeMiterLimit:\"stroke-miterlimit\",strokeOpacity:\"stroke-opacity\",strokeWidth:\"stroke-width\",tabIndex:\"tabindex\",textAnchor:\"text-anchor\",textDecoration:\"text-decoration\",textRendering:\"text-rendering\",transformOrigin:\"transform-origin\",typeOf:\"typeof\",underlinePosition:\"underline-position\",underlineThickness:\"underline-thickness\",unicodeBidi:\"unicode-bidi\",unicodeRange:\"unicode-range\",unitsPerEm:\"units-per-em\",vAlphabetic:\"v-alphabetic\",vHanging:\"v-hanging\",vIdeographic:\"v-ideographic\",vMathematical:\"v-mathematical\",vectorEffect:\"vector-effect\",vertAdvY:\"vert-adv-y\",vertOriginX:\"vert-origin-x\",vertOriginY:\"vert-origin-y\",wordSpacing:\"word-spacing\",writingMode:\"writing-mode\",xHeight:\"x-height\",playbackOrder:\"playbackorder\",timelineBegin:\"timelinebegin\"},properties:{about:J,accentHeight:Q,accumulate:null,additive:null,alignmentBaseline:null,alphabetic:Q,amplitude:Q,arabicForm:null,ascent:Q,attributeName:null,attributeType:null,azimuth:Q,bandwidth:null,baselineShift:null,baseFrequency:null,baseProfile:null,bbox:null,begin:null,bias:Q,by:null,calcMode:null,capHeight:Q,className:X,clip:null,clipPath:null,clipPathUnits:null,clipRule:null,color:null,colorInterpolation:null,colorInterpolationFilters:null,colorProfile:null,colorRendering:null,content:null,contentScriptType:null,contentStyleType:null,crossOrigin:null,cursor:null,cx:null,cy:null,d:null,dataType:null,defaultAction:null,descent:Q,diffuseConstant:Q,direction:null,display:null,dur:null,divisor:Q,dominantBaseline:null,download:W,dx:null,dy:null,edgeMode:null,editable:null,elevation:Q,enableBackground:null,end:null,event:null,exponent:Q,externalResourcesRequired:null,fill:null,fillOpacity:Q,fillRule:null,filter:null,filterRes:null,filterUnits:null,floodColor:null,floodOpacity:null,focusable:null,focusHighlight:null,fontFamily:null,fontSize:null,fontSizeAdjust:null,fontStretch:null,fontStyle:null,fontVariant:null,fontWeight:null,format:null,fr:null,from:null,fx:null,fy:null,g1:Z,g2:Z,glyphName:Z,glyphOrientationHorizontal:null,glyphOrientationVertical:null,glyphRef:null,gradientTransform:null,gradientUnits:null,handler:null,hanging:Q,hatchContentUnits:null,hatchUnits:null,height:null,href:null,hrefLang:null,horizAdvX:Q,horizOriginX:Q,horizOriginY:Q,id:null,ideographic:Q,imageRendering:null,initialVisibility:null,in:null,in2:null,intercept:Q,k:Q,k1:Q,k2:Q,k3:Q,k4:Q,kernelMatrix:J,kernelUnitLength:null,keyPoints:null,keySplines:null,keyTimes:null,kerning:null,lang:null,lengthAdjust:null,letterSpacing:null,lightingColor:null,limitingConeAngle:Q,local:null,markerEnd:null,markerMid:null,markerStart:null,markerHeight:null,markerUnits:null,markerWidth:null,mask:null,maskContentUnits:null,maskUnits:null,mathematical:null,max:null,media:null,mediaCharacterEncoding:null,mediaContentEncodings:null,mediaSize:Q,mediaTime:null,method:null,min:null,mode:null,name:null,navDown:null,navDownLeft:null,navDownRight:null,navLeft:null,navNext:null,navPrev:null,navRight:null,navUp:null,navUpLeft:null,navUpRight:null,numOctaves:null,observer:null,offset:null,onAbort:null,onActivate:null,onAfterPrint:null,onBeforePrint:null,onBegin:null,onCancel:null,onCanPlay:null,onCanPlayThrough:null,onChange:null,onClick:null,onClose:null,onCopy:null,onCueChange:null,onCut:null,onDblClick:null,onDrag:null,onDragEnd:null,onDragEnter:null,onDragExit:null,onDragLeave:null,onDragOver:null,onDragStart:null,onDrop:null,onDurationChange:null,onEmptied:null,onEnd:null,onEnded:null,onError:null,onFocus:null,onFocusIn:null,onFocusOut:null,onHashChange:null,onInput:null,onInvalid:null,onKeyDown:null,onKeyPress:null,onKeyUp:null,onLoad:null,onLoadedData:null,onLoadedMetadata:null,onLoadStart:null,onMessage:null,onMouseDown:null,onMouseEnter:null,onMouseLeave:null,onMouseMove:null,onMouseOut:null,onMouseOver:null,onMouseUp:null,onMouseWheel:null,onOffline:null,onOnline:null,onPageHide:null,onPageShow:null,onPaste:null,onPause:null,onPlay:null,onPlaying:null,onPopState:null,onProgress:null,onRateChange:null,onRepeat:null,onReset:null,onResize:null,onScroll:null,onSeeked:null,onSeeking:null,onSelect:null,onShow:null,onStalled:null,onStorage:null,onSubmit:null,onSuspend:null,onTimeUpdate:null,onToggle:null,onUnload:null,onVolumeChange:null,onWaiting:null,onZoom:null,opacity:null,operator:null,order:null,orient:null,orientation:null,origin:null,overflow:null,overlay:null,overlinePosition:Q,overlineThickness:Q,paintOrder:null,panose1:null,path:null,pathLength:Q,patternContentUnits:null,patternTransform:null,patternUnits:null,phase:null,ping:X,pitch:null,playbackOrder:null,pointerEvents:null,points:null,pointsAtX:Q,pointsAtY:Q,pointsAtZ:Q,preserveAlpha:null,preserveAspectRatio:null,primitiveUnits:null,propagate:null,property:J,r:null,radius:null,referrerPolicy:null,refX:null,refY:null,rel:J,rev:J,renderingIntent:null,repeatCount:null,repeatDur:null,requiredExtensions:J,requiredFeatures:J,requiredFonts:J,requiredFormats:J,resource:null,restart:null,result:null,rotate:null,rx:null,ry:null,scale:null,seed:null,shapeRendering:null,side:null,slope:null,snapshotTime:null,specularConstant:Q,specularExponent:Q,spreadMethod:null,spacing:null,startOffset:null,stdDeviation:null,stemh:null,stemv:null,stitchTiles:null,stopColor:null,stopOpacity:null,strikethroughPosition:Q,strikethroughThickness:Q,string:null,stroke:null,strokeDashArray:J,strokeDashOffset:null,strokeLineCap:null,strokeLineJoin:null,strokeMiterLimit:Q,strokeOpacity:Q,strokeWidth:null,style:null,surfaceScale:Q,syncBehavior:null,syncBehaviorDefault:null,syncMaster:null,syncTolerance:null,syncToleranceDefault:null,systemLanguage:J,tabIndex:Q,tableValues:null,target:null,targetX:Q,targetY:Q,textAnchor:null,textDecoration:null,textRendering:null,textLength:null,timelineBegin:null,title:null,transformBehavior:null,type:null,typeOf:J,to:null,transform:null,transformOrigin:null,u1:null,u2:null,underlinePosition:Q,underlineThickness:Q,unicode:null,unicodeBidi:null,unicodeRange:null,unitsPerEm:Q,values:null,vAlphabetic:Q,vMathematical:Q,vectorEffect:null,vHanging:Q,vIdeographic:Q,version:null,vertAdvY:Q,vertOriginX:Q,vertOriginY:Q,viewBox:null,viewTarget:null,visibility:null,width:null,widths:null,wordSpacing:null,writingMode:null,x:null,x1:null,x2:null,xChannelSelector:null,xHeight:Q,y:null,y1:null,y2:null,yChannelSelector:null,z:null,zoomAndPan:null},space:\"svg\",transform:oe}),de=ie({properties:{xLinkActuate:null,xLinkArcRole:null,xLinkHref:null,xLinkRole:null,xLinkShow:null,xLinkTitle:null,xLinkType:null},space:\"xlink\",transform:(e,t)=>\"xlink:\"+t.slice(5).toLowerCase()}),pe=ie({attributes:{xmlnsxlink:\"xmlns:xlink\"},properties:{xmlnsXLink:null,xmlns:null},space:\"xmlns\",transform:le}),he=ie({properties:{xmlBase:null,xmlLang:null,xmlSpace:null},space:\"xml\",transform:(e,t)=>\"xml:\"+t.slice(3).toLowerCase()}),fe={classId:\"classID\",dataType:\"datatype\",itemId:\"itemID\",strokeDashArray:\"strokeDasharray\",strokeDashOffset:\"strokeDashoffset\",strokeLineCap:\"strokeLinecap\",strokeLineJoin:\"strokeLinejoin\",strokeMiterLimit:\"strokeMiterlimit\",typeOf:\"typeof\",xLinkActuate:\"xlinkActuate\",xLinkArcRole:\"xlinkArcrole\",xLinkHref:\"xlinkHref\",xLinkRole:\"xlinkRole\",xLinkShow:\"xlinkShow\",xLinkTitle:\"xlinkTitle\",xLinkType:\"xlinkType\",xmlnsXLink:\"xmlnsXlink\"},me=/[A-Z]/g,ge=/-[a-z]/g,be=/^data[-\\w.:]+$/i;function Ee(e,t){const n=$(t);let r=t,a=q;if(n in e.normal)return e.property[e.normal[n]];if(n.length>4&&\"data\"===n.slice(0,4)&&be.test(t)){if(\"-\"===t.charAt(4)){const e=t.slice(5).replace(ge,_e);r=\"data\"+e.charAt(0).toUpperCase()+e.slice(1)}else{const e=t.slice(4);if(!ge.test(e)){let n=e.replace(me,ye);\"-\"!==n.charAt(0)&&(n=\"-\"+n),t=\"data\"+n}}a=re}return new a(r,t)}function ye(e){return\"-\"+e.toLowerCase()}function _e(e){return e.charAt(1).toUpperCase()}const ke=j([se,ce,de,pe,he],\"html\"),Te=j([se,ue,de,pe,he],\"svg\");function Se(e){const t=String(e||\"\").trim();return t?t.split(/[ \\t\\n\\r\\f]+/g):[]}function ve(e){return e.join(\" \").trim()}var Ae,Ne,Ce,xe={};function we(){if(Ce)return xe;Ce=1;var e=xe&&xe.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(xe,\"__esModule\",{value:!0}),xe.default=function(e,n){let r=null;if(!e||\"string\"!=typeof e)return r;const a=(0,t.default)(e),i=\"function\"==typeof n;return a.forEach(e=>{if(\"declaration\"!==e.type)return;const{property:t,value:a}=e;i?n(t,a,e):a&&(r=r||{},r[t]=a)}),r};const t=e(function(){if(Ne)return Ae;Ne=1;var e=/\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\//g,t=/\\n/g,n=/^\\s*/,r=/^(\\*?[-#/*\\\\\\w]+(\\[[0-9a-z_-]+\\])?)\\s*/,a=/^:\\s*/,i=/^((?:'(?:\\\\'|.)*?'|\"(?:\\\\\"|.)*?\"|\\([^)]*?\\)|[^};])+)/,s=/^[;\\s]*/,o=/^\\s+|\\s+$/g,l=\"\";function c(e){return e?e.replace(o,l):l}return Ae=function(o,u){if(\"string\"!=typeof o)throw new TypeError(\"First argument must be a string\");if(!o)return[];u=u||{};var d=1,p=1;function h(e){var n=e.match(t);n&&(d+=n.length);var r=e.lastIndexOf(\"\\n\");p=~r?e.length-r:p+e.length}function f(){var e={line:d,column:p};return function(t){return t.position=new m(e),E(),t}}function m(e){this.start=e,this.end={line:d,column:p},this.source=u.source}function g(e){var t=new Error(u.source+\":\"+d+\":\"+p+\": \"+e);if(t.reason=e,t.filename=u.source,t.line=d,t.column=p,t.source=o,!u.silent)throw t}function b(e){var t=e.exec(o);if(t){var n=t[0];return h(n),o=o.slice(n.length),t}}function E(){b(n)}function y(e){var t;for(e=e||[];t=_();)!1!==t&&e.push(t);return e}function _(){var e=f();if(\"/\"==o.charAt(0)&&\"*\"==o.charAt(1)){for(var t=2;l!=o.charAt(t)&&(\"*\"!=o.charAt(t)||\"/\"!=o.charAt(t+1));)++t;if(t+=2,l===o.charAt(t-1))return g(\"End of comment missing\");var n=o.slice(2,t-2);return p+=2,h(n),o=o.slice(t),p+=2,e({type:\"comment\",comment:n})}}function k(){var t=f(),n=b(r);if(n){if(_(),!b(a))return g(\"property missing ':'\");var o=b(i),u=t({type:\"declaration\",property:c(n[0].replace(e,l)),value:o?c(o[0].replace(e,l)):l});return b(s),u}}return m.prototype.content=o,E(),function(){var e,t=[];for(y(t);e=k();)!1!==e&&(t.push(e),y(t));return t}()}}());return xe}var Ie,Oe,Re,De={};function Me(){if(Ie)return De;Ie=1,Object.defineProperty(De,\"__esModule\",{value:!0}),De.camelCase=void 0;var e=/^--[a-zA-Z0-9_-]+$/,t=/-([a-z])/g,n=/^[^-]+$/,r=/^-(webkit|moz|ms|o|khtml)-/,a=/^-(ms)-/,i=function(e,t){return t.toUpperCase()},s=function(e,t){return\"\".concat(t,\"-\")};return De.camelCase=function(o,l){return void 0===l&&(l={}),function(t){return!t||n.test(t)||e.test(t)}(o)?o:(o=o.toLowerCase(),(o=l.reactCompat?o.replace(a,s):o.replace(r,s)).replace(t,i))},De}const Pe=r(function(){if(Re)return Oe;Re=1;var e=(Oe&&Oe.__importDefault||function(e){return e&&e.__esModule?e:{default:e}})(we()),t=Me();function n(n,r){var a={};return n&&\"string\"==typeof n?((0,e.default)(n,function(e,n){e&&n&&(a[(0,t.camelCase)(e,r)]=n)}),a):a}return n.default=n,Oe=n}()),Le=Be(\"end\"),Fe=Be(\"start\");function Be(e){return function(t){const n=t&&t.position&&t.position[e]||{};if(\"number\"==typeof n.line&&n.line>0&&\"number\"==typeof n.column&&n.column>0)return{line:n.line,column:n.column,offset:\"number\"==typeof n.offset&&n.offset>-1?n.offset:void 0}}}function Ue(e){const t=Fe(e),n=Le(e);if(t&&n)return{start:t,end:n}}function He(e){return e&&\"object\"==typeof e?\"position\"in e||\"type\"in e?Ge(e.position):\"start\"in e||\"end\"in e?Ge(e):\"line\"in e||\"column\"in e?ze(e):\"\":\"\"}function ze(e){return je(e&&e.line)+\":\"+je(e&&e.column)}function Ge(e){return ze(e&&e.start)+\"-\"+ze(e&&e.end)}function je(e){return e&&\"number\"==typeof e?e:1}class $e extends Error{constructor(e,t,n){super(),\"string\"==typeof t&&(n=t,t=void 0);let r=\"\",a={},i=!1;if(t&&(a=\"line\"in t&&\"column\"in t||\"start\"in t&&\"end\"in t?{place:t}:\"type\"in t?{ancestors:[t],place:t.position}:{...t}),\"string\"==typeof e?r=e:!a.cause&&e&&(i=!0,r=e.message,a.cause=e),!a.ruleId&&!a.source&&\"string\"==typeof n){const e=n.indexOf(\":\");-1===e?a.ruleId=n:(a.source=n.slice(0,e),a.ruleId=n.slice(e+1))}if(!a.place&&a.ancestors&&a.ancestors){const e=a.ancestors[a.ancestors.length-1];e&&(a.place=e.position)}const s=a.place&&\"start\"in a.place?a.place.start:a.place;this.ancestors=a.ancestors||void 0,this.cause=a.cause||void 0,this.column=s?s.column:void 0,this.fatal=void 0,this.file=\"\",this.message=r,this.line=s?s.line:void 0,this.name=He(a.place)||\"1:1\",this.place=a.place||void 0,this.reason=this.message,this.ruleId=a.ruleId||void 0,this.source=a.source||void 0,this.stack=i&&a.cause&&\"string\"==typeof a.cause.stack?a.cause.stack:\"\",this.actual=void 0,this.expected=void 0,this.note=void 0,this.url=void 0}}$e.prototype.file=\"\",$e.prototype.name=\"\",$e.prototype.reason=\"\",$e.prototype.message=\"\",$e.prototype.stack=\"\",$e.prototype.column=void 0,$e.prototype.line=void 0,$e.prototype.ancestors=void 0,$e.prototype.cause=void 0,$e.prototype.fatal=void 0,$e.prototype.place=void 0,$e.prototype.ruleId=void 0,$e.prototype.source=void 0;const qe={}.hasOwnProperty,Ye=new Map,We=/[A-Z]/g,Ve=new Set([\"table\",\"tbody\",\"thead\",\"tfoot\",\"tr\"]),Ke=new Set([\"td\",\"th\"]),Qe=\"https://github.com/syntax-tree/hast-util-to-jsx-runtime\";function Xe(e,t){if(!t||void 0===t.Fragment)throw new TypeError(\"Expected `Fragment` in options\");const n=t.filePath||void 0;let r;if(t.development){if(\"function\"!=typeof t.jsxDEV)throw new TypeError(\"Expected `jsxDEV` in options when `development: true`\");r=function(e,t){return n;function n(n,r,a,i){const s=Array.isArray(a.children),o=Fe(n);return t(r,a,i,s,{columnNumber:o?o.column-1:void 0,fileName:e,lineNumber:o?o.line:void 0},void 0)}}(n,t.jsxDEV)}else{if(\"function\"!=typeof t.jsx)throw new TypeError(\"Expected `jsx` in production options\");if(\"function\"!=typeof t.jsxs)throw new TypeError(\"Expected `jsxs` in production options\");r=function(e,t,n){return r;function r(e,r,a,i){const s=Array.isArray(a.children)?n:t;return i?s(r,a,i):s(r,a)}}(0,t.jsx,t.jsxs)}const a={Fragment:t.Fragment,ancestors:[],components:t.components||{},create:r,elementAttributeNameCase:t.elementAttributeNameCase||\"react\",evaluater:t.createEvaluater?t.createEvaluater():void 0,filePath:n,ignoreInvalidStyle:t.ignoreInvalidStyle||!1,passKeys:!1!==t.passKeys,passNode:t.passNode||!1,schema:\"svg\"===t.space?Te:ke,stylePropertyNameCase:t.stylePropertyNameCase||\"dom\",tableCellAlignToStyle:!1!==t.tableCellAlignToStyle},i=Ze(a,e,void 0);return i&&\"string\"!=typeof i?i:a.create(e,a.Fragment,{children:i||void 0},void 0)}function Ze(e,t,n){return\"element\"===t.type?function(e,t,n){const r=e.schema;let a=r;\"svg\"===t.tagName.toLowerCase()&&\"html\"===r.space&&(a=Te,e.schema=a);e.ancestors.push(t);const i=rt(e,t.tagName,!1),s=function(e,t){const n={};let r,a;for(a in t.properties)if(\"children\"!==a&&qe.call(t.properties,a)){const i=nt(e,a,t.properties[a]);if(i){const[a,s]=i;e.tableCellAlignToStyle&&\"align\"===a&&\"string\"==typeof s&&Ke.has(t.tagName)?r=s:n[a]=s}}if(r){(n.style||(n.style={}))[\"css\"===e.stylePropertyNameCase?\"text-align\":\"textAlign\"]=r}return n}(e,t);let o=tt(e,t);Ve.has(t.tagName)&&(o=o.filter(function(e){return\"string\"!=typeof e||!(\"object\"==typeof(t=e)?\"text\"===t.type&&z(t.value):z(t));var t}));return Je(e,s,i,t),et(s,o),e.ancestors.pop(),e.schema=r,e.create(t,i,s,n)}(e,t,n):\"mdxFlowExpression\"===t.type||\"mdxTextExpression\"===t.type?function(e,t){if(t.data&&t.data.estree&&e.evaluater){const n=t.data.estree.body[0];return n.type,e.evaluater.evaluateExpression(n.expression)}at(e,t.position)}(e,t):\"mdxJsxFlowElement\"===t.type||\"mdxJsxTextElement\"===t.type?function(e,t,n){const r=e.schema;let a=r;\"svg\"===t.name&&\"html\"===r.space&&(a=Te,e.schema=a);e.ancestors.push(t);const i=null===t.name?e.Fragment:rt(e,t.name,!0),s=function(e,t){const n={};for(const r of t.attributes)if(\"mdxJsxExpressionAttribute\"===r.type)if(r.data&&r.data.estree&&e.evaluater){const t=r.data.estree.body[0];R(t.type);const a=t.expression;R(a.type);const i=a.properties[0];R(i.type),Object.assign(n,e.evaluater.evaluateExpression(i.argument))}else at(e,t.position);else{const a=r.name;let i;if(r.value&&\"object\"==typeof r.value)if(r.value.data&&r.value.data.estree&&e.evaluater){const t=r.value.data.estree.body[0];R(t.type),i=e.evaluater.evaluateExpression(t.expression)}else at(e,t.position);else i=null===r.value||r.value;n[a]=i}return n}(e,t),o=tt(e,t);return Je(e,s,i,t),et(s,o),e.ancestors.pop(),e.schema=r,e.create(t,i,s,n)}(e,t,n):\"mdxjsEsm\"===t.type?function(e,t){if(t.data&&t.data.estree&&e.evaluater)return e.evaluater.evaluateProgram(t.data.estree);at(e,t.position)}(e,t):\"root\"===t.type?function(e,t,n){const r={};return et(r,tt(e,t)),e.create(t,e.Fragment,r,n)}(e,t,n):\"text\"===t.type?function(e,t){return t.value}(0,t):void 0}function Je(e,t,n,r){\"string\"!=typeof n&&n!==e.Fragment&&e.passNode&&(t.node=r)}function et(e,t){if(t.length>0){const n=t.length>1?t:t[0];n&&(e.children=n)}}function tt(e,t){const n=[];let r=-1;const a=e.passKeys?new Map:Ye;for(;++r<t.children.length;){const i=t.children[r];let s;if(e.passKeys){const e=\"element\"===i.type?i.tagName:\"mdxJsxFlowElement\"===i.type||\"mdxJsxTextElement\"===i.type?i.name:void 0;if(e){const t=a.get(e)||0;s=e+\"-\"+t,a.set(e,t+1)}}const o=Ze(e,i,s);void 0!==o&&n.push(o)}return n}function nt(e,t,n){const r=Ee(e.schema,t);if(!(null==n||\"number\"==typeof n&&Number.isNaN(n))){if(Array.isArray(n)&&(n=r.commaSeparated?P(n):ve(n)),\"style\"===r.property){let t=\"object\"==typeof n?n:function(e,t){try{return Pe(t,{reactCompat:!0})}catch(n){if(e.ignoreInvalidStyle)return{};const t=n,r=new $e(\"Cannot parse `style` attribute\",{ancestors:e.ancestors,cause:t,ruleId:\"style\",source:\"hast-util-to-jsx-runtime\"});throw r.file=e.filePath||void 0,r.url=Qe+\"#cannot-parse-style-attribute\",r}}(e,String(n));return\"css\"===e.stylePropertyNameCase&&(t=function(e){const t={};let n;for(n in e)qe.call(e,n)&&(t[it(n)]=e[n]);return t}(t)),[\"style\",t]}return[\"react\"===e.elementAttributeNameCase&&r.space?fe[r.property]||r.property:r.attribute,n]}}function rt(e,t,n){let r;if(n)if(t.includes(\".\")){const e=t.split(\".\");let n,a=-1;for(;++a<e.length;){const t=U(e[a])?{type:\"Identifier\",name:e[a]}:{type:\"Literal\",value:e[a]};n=n?{type:\"MemberExpression\",object:n,property:t,computed:Boolean(a&&\"Literal\"===t.type),optional:!1}:t}r=n}else r=U(t)&&!/^[a-z]/.test(t)?{type:\"Identifier\",name:t}:{type:\"Literal\",value:t};else r={type:\"Literal\",value:t};if(\"Literal\"===r.type){const t=r.value;return qe.call(e.components,t)?e.components[t]:t}if(e.evaluater)return e.evaluater.evaluateExpression(r);at(e)}function at(e,t){const n=new $e(\"Cannot handle MDX estrees without `createEvaluater`\",{ancestors:e.ancestors,place:t,ruleId:\"mdx-estree\",source:\"hast-util-to-jsx-runtime\"});throw n.file=e.filePath||void 0,n.url=Qe+\"#cannot-handle-mdx-estrees-without-createevaluater\",n}function it(e){let t=e.replace(We,st);return\"ms-\"===t.slice(0,3)&&(t=\"-\"+t),t}function st(e){return\"-\"+e.toLowerCase()}const ot={action:[\"form\"],cite:[\"blockquote\",\"del\",\"ins\",\"q\"],data:[\"object\"],formAction:[\"button\",\"input\"],href:[\"a\",\"area\",\"base\",\"link\"],icon:[\"menuitem\"],itemId:null,manifest:[\"html\"],ping:[\"a\",\"area\"],poster:[\"video\"],src:[\"audio\",\"embed\",\"iframe\",\"img\",\"input\",\"script\",\"source\",\"track\",\"video\"]},lt={};function ct(e,t){return ut(e,\"boolean\"!=typeof lt.includeImageAlt||lt.includeImageAlt,\"boolean\"!=typeof lt.includeHtml||lt.includeHtml)}function ut(e,t,n){if(function(e){return Boolean(e&&\"object\"==typeof e)}(e)){if(\"value\"in e)return\"html\"!==e.type||n?e.value:\"\";if(t&&\"alt\"in e&&e.alt)return e.alt;if(\"children\"in e)return dt(e.children,t,n)}return Array.isArray(e)?dt(e,t,n):\"\"}function dt(e,t,n){const r=[];let a=-1;for(;++a<e.length;)r[a]=ut(e[a],t,n);return r.join(\"\")}const pt=document.createElement(\"i\");function ht(e){const t=\"&\"+e+\";\";pt.innerHTML=t;const n=pt.textContent;return(59!==n.charCodeAt(n.length-1)||\"semi\"===e)&&(n!==t&&n)}function ft(e,t,n,r){const a=e.length;let i,s=0;if(t=t<0?-t>a?0:a+t:t>a?a:t,n=n>0?n:0,r.length<1e4)i=Array.from(r),i.unshift(t,n),e.splice(...i);else for(n&&e.splice(t,n);s<r.length;)i=r.slice(s,s+1e4),i.unshift(t,0),e.splice(...i),s+=1e4,t+=1e4}function mt(e,t){return e.length>0?(ft(e,e.length,0,t),e):t}const gt={}.hasOwnProperty;function bt(e){const t={};let n=-1;for(;++n<e.length;)Et(t,e[n]);return t}function Et(e,t){let n;for(n in t){const r=(gt.call(e,n)?e[n]:void 0)||(e[n]={}),a=t[n];let i;if(a)for(i in a){gt.call(r,i)||(r[i]=[]);const e=a[i];yt(r[i],Array.isArray(e)?e:e?[e]:[])}}}function yt(e,t){let n=-1;const r=[];for(;++n<t.length;)(\"after\"===t[n].add?e:r).push(t[n]);ft(e,0,0,r)}function _t(e,t){const n=Number.parseInt(e,t);return n<9||11===n||n>13&&n<32||n>126&&n<160||n>55295&&n<57344||n>64975&&n<65008||!(65535&~n)||65534==(65535&n)||n>1114111?\"�\":String.fromCodePoint(n)}function kt(e){return e.replace(/[\\t\\n\\r ]+/g,\" \").replace(/^ | $/g,\"\").toLowerCase().toUpperCase()}const Tt=Mt(/[A-Za-z]/),St=Mt(/[\\dA-Za-z]/),vt=Mt(/[#-'*+\\--9=?A-Z^-~]/);function At(e){return null!==e&&(e<32||127===e)}const Nt=Mt(/\\d/),Ct=Mt(/[\\dA-Fa-f]/),xt=Mt(/[!-/:-@[-`{-~]/);function wt(e){return null!==e&&e<-2}function It(e){return null!==e&&(e<0||32===e)}function Ot(e){return-2===e||-1===e||32===e}const Rt=Mt(new RegExp(\"\\\\p{P}|\\\\p{S}\",\"u\")),Dt=Mt(/\\s/);function Mt(e){return function(t){return null!==t&&t>-1&&e.test(String.fromCharCode(t))}}function Pt(e){const t=[];let n=-1,r=0,a=0;for(;++n<e.length;){const i=e.charCodeAt(n);let s=\"\";if(37===i&&St(e.charCodeAt(n+1))&&St(e.charCodeAt(n+2)))a=2;else if(i<128)/[!#$&-;=?-Z_a-z~]/.test(String.fromCharCode(i))||(s=String.fromCharCode(i));else if(i>55295&&i<57344){const t=e.charCodeAt(n+1);i<56320&&t>56319&&t<57344?(s=String.fromCharCode(i,t),a=1):s=\"�\"}else s=String.fromCharCode(i);s&&(t.push(e.slice(r,n),encodeURIComponent(s)),r=n+a+1,s=\"\"),a&&(n+=a,a=0)}return t.join(\"\")+e.slice(r)}function Lt(e,t,n,r){const a=r?r-1:Number.POSITIVE_INFINITY;let i=0;return function(r){if(Ot(r))return e.enter(n),s(r);return t(r)};function s(r){return Ot(r)&&i++<a?(e.consume(r),s):(e.exit(n),t(r))}}const Ft={tokenize:function(e){const t=e.attempt(this.parser.constructs.contentInitial,function(n){if(null===n)return void e.consume(n);return e.enter(\"lineEnding\"),e.consume(n),e.exit(\"lineEnding\"),Lt(e,t,\"linePrefix\")},function(t){return e.enter(\"paragraph\"),r(t)});let n;return t;function r(t){const r=e.enter(\"chunkText\",{contentType:\"text\",previous:n});return n&&(n.next=r),n=r,a(t)}function a(t){return null===t?(e.exit(\"chunkText\"),e.exit(\"paragraph\"),void e.consume(t)):wt(t)?(e.consume(t),e.exit(\"chunkText\"),r):(e.consume(t),a)}}};const Bt={tokenize:function(e){const t=this,n=[];let r,a,i,s=0;return o;function o(r){if(s<n.length){const a=n[s];return t.containerState=a[1],e.attempt(a[0].continuation,l,c)(r)}return c(r)}function l(e){if(s++,t.containerState._closeFlow){t.containerState._closeFlow=void 0,r&&E();const n=t.events.length;let a,i=n;for(;i--;)if(\"exit\"===t.events[i][0]&&\"chunkFlow\"===t.events[i][1].type){a=t.events[i][1].end;break}b(s);let o=n;for(;o<t.events.length;)t.events[o][1].end={...a},o++;return ft(t.events,i+1,0,t.events.slice(n)),t.events.length=o,c(e)}return o(e)}function c(a){if(s===n.length){if(!r)return p(a);if(r.currentConstruct&&r.currentConstruct.concrete)return f(a);t.interrupt=Boolean(r.currentConstruct&&!r._gfmTableDynamicInterruptHack)}return t.containerState={},e.check(Ut,u,d)(a)}function u(e){return r&&E(),b(s),p(e)}function d(e){return t.parser.lazy[t.now().line]=s!==n.length,i=t.now().offset,f(e)}function p(n){return t.containerState={},e.attempt(Ut,h,f)(n)}function h(e){return s++,n.push([t.currentConstruct,t.containerState]),p(e)}function f(n){return null===n?(r&&E(),b(0),void e.consume(n)):(r=r||t.parser.flow(t.now()),e.enter(\"chunkFlow\",{_tokenizer:r,contentType:\"flow\",previous:a}),m(n))}function m(n){return null===n?(g(e.exit(\"chunkFlow\"),!0),b(0),void e.consume(n)):wt(n)?(e.consume(n),g(e.exit(\"chunkFlow\")),s=0,t.interrupt=void 0,o):(e.consume(n),m)}function g(e,n){const o=t.sliceStream(e);if(n&&o.push(null),e.previous=a,a&&(a.next=e),a=e,r.defineSkip(e.start),r.write(o),t.parser.lazy[e.start.line]){let e=r.events.length;for(;e--;)if(r.events[e][1].start.offset<i&&(!r.events[e][1].end||r.events[e][1].end.offset>i))return;const n=t.events.length;let a,o,l=n;for(;l--;)if(\"exit\"===t.events[l][0]&&\"chunkFlow\"===t.events[l][1].type){if(a){o=t.events[l][1].end;break}a=!0}for(b(s),e=n;e<t.events.length;)t.events[e][1].end={...o},e++;ft(t.events,l+1,0,t.events.slice(n)),t.events.length=e}}function b(r){let a=n.length;for(;a-- >r;){const r=n[a];t.containerState=r[1],r[0].exit.call(t,e)}n.length=r}function E(){r.write([null]),a=void 0,r=void 0,t.containerState._closeFlow=void 0}}},Ut={tokenize:function(e,t,n){return Lt(e,e.attempt(this.parser.constructs.document,t,n),\"linePrefix\",this.parser.constructs.disable.null.includes(\"codeIndented\")?void 0:4)}};function Ht(e){return null===e||It(e)||Dt(e)?1:Rt(e)?2:void 0}function zt(e,t,n){const r=[];let a=-1;for(;++a<e.length;){const i=e[a].resolveAll;i&&!r.includes(i)&&(t=i(t,n),r.push(i))}return t}const Gt={name:\"attention\",resolveAll:function(e,t){let n,r,a,i,s,o,l,c,u=-1;for(;++u<e.length;)if(\"enter\"===e[u][0]&&\"attentionSequence\"===e[u][1].type&&e[u][1]._close)for(n=u;n--;)if(\"exit\"===e[n][0]&&\"attentionSequence\"===e[n][1].type&&e[n][1]._open&&t.sliceSerialize(e[n][1]).charCodeAt(0)===t.sliceSerialize(e[u][1]).charCodeAt(0)){if((e[n][1]._close||e[u][1]._open)&&(e[u][1].end.offset-e[u][1].start.offset)%3&&!((e[n][1].end.offset-e[n][1].start.offset+e[u][1].end.offset-e[u][1].start.offset)%3))continue;o=e[n][1].end.offset-e[n][1].start.offset>1&&e[u][1].end.offset-e[u][1].start.offset>1?2:1;const d={...e[n][1].end},p={...e[u][1].start};jt(d,-o),jt(p,o),i={type:o>1?\"strongSequence\":\"emphasisSequence\",start:d,end:{...e[n][1].end}},s={type:o>1?\"strongSequence\":\"emphasisSequence\",start:{...e[u][1].start},end:p},a={type:o>1?\"strongText\":\"emphasisText\",start:{...e[n][1].end},end:{...e[u][1].start}},r={type:o>1?\"strong\":\"emphasis\",start:{...i.start},end:{...s.end}},e[n][1].end={...i.start},e[u][1].start={...s.end},l=[],e[n][1].end.offset-e[n][1].start.offset&&(l=mt(l,[[\"enter\",e[n][1],t],[\"exit\",e[n][1],t]])),l=mt(l,[[\"enter\",r,t],[\"enter\",i,t],[\"exit\",i,t],[\"enter\",a,t]]),l=mt(l,zt(t.parser.constructs.insideSpan.null,e.slice(n+1,u),t)),l=mt(l,[[\"exit\",a,t],[\"enter\",s,t],[\"exit\",s,t],[\"exit\",r,t]]),e[u][1].end.offset-e[u][1].start.offset?(c=2,l=mt(l,[[\"enter\",e[u][1],t],[\"exit\",e[u][1],t]])):c=0,ft(e,n-1,u-n+3,l),u=n+l.length-c-2;break}u=-1;for(;++u<e.length;)\"attentionSequence\"===e[u][1].type&&(e[u][1].type=\"data\");return e},tokenize:function(e,t){const n=this.parser.constructs.attentionMarkers.null,r=this.previous,a=Ht(r);let i;return function(t){return i=t,e.enter(\"attentionSequence\"),s(t)};function s(o){if(o===i)return e.consume(o),s;const l=e.exit(\"attentionSequence\"),c=Ht(o),u=!c||2===c&&a||n.includes(o),d=!a||2===a&&c||n.includes(r);return l._open=Boolean(42===i?u:u&&(a||!d)),l._close=Boolean(42===i?d:d&&(c||!u)),t(o)}}};function jt(e,t){e.column+=t,e.offset+=t,e._bufferIndex+=t}const $t={name:\"autolink\",tokenize:function(e,t,n){let r=0;return function(t){return e.enter(\"autolink\"),e.enter(\"autolinkMarker\"),e.consume(t),e.exit(\"autolinkMarker\"),e.enter(\"autolinkProtocol\"),a};function a(t){return Tt(t)?(e.consume(t),i):64===t?n(t):l(t)}function i(e){return 43===e||45===e||46===e||St(e)?(r=1,s(e)):l(e)}function s(t){return 58===t?(e.consume(t),r=0,o):(43===t||45===t||46===t||St(t))&&r++<32?(e.consume(t),s):(r=0,l(t))}function o(r){return 62===r?(e.exit(\"autolinkProtocol\"),e.enter(\"autolinkMarker\"),e.consume(r),e.exit(\"autolinkMarker\"),e.exit(\"autolink\"),t):null===r||32===r||60===r||At(r)?n(r):(e.consume(r),o)}function l(t){return 64===t?(e.consume(t),c):vt(t)?(e.consume(t),l):n(t)}function c(e){return St(e)?u(e):n(e)}function u(n){return 46===n?(e.consume(n),r=0,c):62===n?(e.exit(\"autolinkProtocol\").type=\"autolinkEmail\",e.enter(\"autolinkMarker\"),e.consume(n),e.exit(\"autolinkMarker\"),e.exit(\"autolink\"),t):d(n)}function d(t){if((45===t||St(t))&&r++<63){const n=45===t?d:u;return e.consume(t),n}return n(t)}}};const qt={partial:!0,tokenize:function(e,t,n){return function(t){return Ot(t)?Lt(e,r,\"linePrefix\")(t):r(t)};function r(e){return null===e||wt(e)?t(e):n(e)}}};const Yt={continuation:{tokenize:function(e,t,n){const r=this;return function(t){if(Ot(t))return Lt(e,a,\"linePrefix\",r.parser.constructs.disable.null.includes(\"codeIndented\")?void 0:4)(t);return a(t)};function a(r){return e.attempt(Yt,t,n)(r)}}},exit:function(e){e.exit(\"blockQuote\")},name:\"blockQuote\",tokenize:function(e,t,n){const r=this;return function(t){if(62===t){const n=r.containerState;return n.open||(e.enter(\"blockQuote\",{_container:!0}),n.open=!0),e.enter(\"blockQuotePrefix\"),e.enter(\"blockQuoteMarker\"),e.consume(t),e.exit(\"blockQuoteMarker\"),a}return n(t)};function a(n){return Ot(n)?(e.enter(\"blockQuotePrefixWhitespace\"),e.consume(n),e.exit(\"blockQuotePrefixWhitespace\"),e.exit(\"blockQuotePrefix\"),t):(e.exit(\"blockQuotePrefix\"),t(n))}}};const Wt={name:\"characterEscape\",tokenize:function(e,t,n){return function(t){return e.enter(\"characterEscape\"),e.enter(\"escapeMarker\"),e.consume(t),e.exit(\"escapeMarker\"),r};function r(r){return xt(r)?(e.enter(\"characterEscapeValue\"),e.consume(r),e.exit(\"characterEscapeValue\"),e.exit(\"characterEscape\"),t):n(r)}}};const Vt={name:\"characterReference\",tokenize:function(e,t,n){const r=this;let a,i,s=0;return function(t){return e.enter(\"characterReference\"),e.enter(\"characterReferenceMarker\"),e.consume(t),e.exit(\"characterReferenceMarker\"),o};function o(t){return 35===t?(e.enter(\"characterReferenceMarkerNumeric\"),e.consume(t),e.exit(\"characterReferenceMarkerNumeric\"),l):(e.enter(\"characterReferenceValue\"),a=31,i=St,c(t))}function l(t){return 88===t||120===t?(e.enter(\"characterReferenceMarkerHexadecimal\"),e.consume(t),e.exit(\"characterReferenceMarkerHexadecimal\"),e.enter(\"characterReferenceValue\"),a=6,i=Ct,c):(e.enter(\"characterReferenceValue\"),a=7,i=Nt,c(t))}function c(o){if(59===o&&s){const a=e.exit(\"characterReferenceValue\");return i!==St||ht(r.sliceSerialize(a))?(e.enter(\"characterReferenceMarker\"),e.consume(o),e.exit(\"characterReferenceMarker\"),e.exit(\"characterReference\"),t):n(o)}return i(o)&&s++<a?(e.consume(o),c):n(o)}}};const Kt={partial:!0,tokenize:function(e,t,n){const r=this;return function(t){if(null===t)return n(t);return e.enter(\"lineEnding\"),e.consume(t),e.exit(\"lineEnding\"),a};function a(e){return r.parser.lazy[r.now().line]?n(e):t(e)}}},Qt={concrete:!0,name:\"codeFenced\",tokenize:function(e,t,n){const r=this,a={partial:!0,tokenize:function(e,t,n){let a=0;return s;function s(t){return e.enter(\"lineEnding\"),e.consume(t),e.exit(\"lineEnding\"),l}function l(t){return e.enter(\"codeFencedFence\"),Ot(t)?Lt(e,c,\"linePrefix\",r.parser.constructs.disable.null.includes(\"codeIndented\")?void 0:4)(t):c(t)}function c(t){return t===i?(e.enter(\"codeFencedFenceSequence\"),u(t)):n(t)}function u(t){return t===i?(a++,e.consume(t),u):a>=o?(e.exit(\"codeFencedFenceSequence\"),Ot(t)?Lt(e,d,\"whitespace\")(t):d(t)):n(t)}function d(r){return null===r||wt(r)?(e.exit(\"codeFencedFence\"),t(r)):n(r)}}};let i,s=0,o=0;return function(t){return function(t){const n=r.events[r.events.length-1];return s=n&&\"linePrefix\"===n[1].type?n[2].sliceSerialize(n[1],!0).length:0,i=t,e.enter(\"codeFenced\"),e.enter(\"codeFencedFence\"),e.enter(\"codeFencedFenceSequence\"),l(t)}(t)};function l(t){return t===i?(o++,e.consume(t),l):o<3?n(t):(e.exit(\"codeFencedFenceSequence\"),Ot(t)?Lt(e,c,\"whitespace\")(t):c(t))}function c(n){return null===n||wt(n)?(e.exit(\"codeFencedFence\"),r.interrupt?t(n):e.check(Kt,h,E)(n)):(e.enter(\"codeFencedFenceInfo\"),e.enter(\"chunkString\",{contentType:\"string\"}),u(n))}function u(t){return null===t||wt(t)?(e.exit(\"chunkString\"),e.exit(\"codeFencedFenceInfo\"),c(t)):Ot(t)?(e.exit(\"chunkString\"),e.exit(\"codeFencedFenceInfo\"),Lt(e,d,\"whitespace\")(t)):96===t&&t===i?n(t):(e.consume(t),u)}function d(t){return null===t||wt(t)?c(t):(e.enter(\"codeFencedFenceMeta\"),e.enter(\"chunkString\",{contentType:\"string\"}),p(t))}function p(t){return null===t||wt(t)?(e.exit(\"chunkString\"),e.exit(\"codeFencedFenceMeta\"),c(t)):96===t&&t===i?n(t):(e.consume(t),p)}function h(t){return e.attempt(a,E,f)(t)}function f(t){return e.enter(\"lineEnding\"),e.consume(t),e.exit(\"lineEnding\"),m}function m(t){return s>0&&Ot(t)?Lt(e,g,\"linePrefix\",s+1)(t):g(t)}function g(t){return null===t||wt(t)?e.check(Kt,h,E)(t):(e.enter(\"codeFlowValue\"),b(t))}function b(t){return null===t||wt(t)?(e.exit(\"codeFlowValue\"),g(t)):(e.consume(t),b)}function E(n){return e.exit(\"codeFenced\"),t(n)}}};const Xt={name:\"codeIndented\",tokenize:function(e,t,n){const r=this;return function(t){return e.enter(\"codeIndented\"),Lt(e,a,\"linePrefix\",5)(t)};function a(e){const t=r.events[r.events.length-1];return t&&\"linePrefix\"===t[1].type&&t[2].sliceSerialize(t[1],!0).length>=4?i(e):n(e)}function i(t){return null===t?o(t):wt(t)?e.attempt(Zt,i,o)(t):(e.enter(\"codeFlowValue\"),s(t))}function s(t){return null===t||wt(t)?(e.exit(\"codeFlowValue\"),i(t)):(e.consume(t),s)}function o(n){return e.exit(\"codeIndented\"),t(n)}}},Zt={partial:!0,tokenize:function(e,t,n){const r=this;return a;function a(t){return r.parser.lazy[r.now().line]?n(t):wt(t)?(e.enter(\"lineEnding\"),e.consume(t),e.exit(\"lineEnding\"),a):Lt(e,i,\"linePrefix\",5)(t)}function i(e){const i=r.events[r.events.length-1];return i&&\"linePrefix\"===i[1].type&&i[2].sliceSerialize(i[1],!0).length>=4?t(e):wt(e)?a(e):n(e)}}};const Jt={name:\"codeText\",previous:function(e){return 96!==e||\"characterEscape\"===this.events[this.events.length-1][1].type},resolve:function(e){let t,n,r=e.length-4,a=3;if(!(\"lineEnding\"!==e[a][1].type&&\"space\"!==e[a][1].type||\"lineEnding\"!==e[r][1].type&&\"space\"!==e[r][1].type))for(t=a;++t<r;)if(\"codeTextData\"===e[t][1].type){e[a][1].type=\"codeTextPadding\",e[r][1].type=\"codeTextPadding\",a+=2,r-=2;break}t=a-1,r++;for(;++t<=r;)void 0===n?t!==r&&\"lineEnding\"!==e[t][1].type&&(n=t):t!==r&&\"lineEnding\"!==e[t][1].type||(e[n][1].type=\"codeTextData\",t!==n+2&&(e[n][1].end=e[t-1][1].end,e.splice(n+2,t-n-2),r-=t-n-2,t=n+2),n=void 0);return e},tokenize:function(e,t,n){let r,a,i=0;return function(t){return e.enter(\"codeText\"),e.enter(\"codeTextSequence\"),s(t)};function s(t){return 96===t?(e.consume(t),i++,s):(e.exit(\"codeTextSequence\"),o(t))}function o(t){return null===t?n(t):32===t?(e.enter(\"space\"),e.consume(t),e.exit(\"space\"),o):96===t?(a=e.enter(\"codeTextSequence\"),r=0,c(t)):wt(t)?(e.enter(\"lineEnding\"),e.consume(t),e.exit(\"lineEnding\"),o):(e.enter(\"codeTextData\"),l(t))}function l(t){return null===t||32===t||96===t||wt(t)?(e.exit(\"codeTextData\"),o(t)):(e.consume(t),l)}function c(n){return 96===n?(e.consume(n),r++,c):r===i?(e.exit(\"codeTextSequence\"),e.exit(\"codeText\"),t(n)):(a.type=\"codeTextData\",l(n))}}};class en{constructor(e){this.left=e?[...e]:[],this.right=[]}get(e){if(e<0||e>=this.left.length+this.right.length)throw new RangeError(\"Cannot access index `\"+e+\"` in a splice buffer of size `\"+(this.left.length+this.right.length)+\"`\");return e<this.left.length?this.left[e]:this.right[this.right.length-e+this.left.length-1]}get length(){return this.left.length+this.right.length}shift(){return this.setCursor(0),this.right.pop()}slice(e,t){const n=null==t?Number.POSITIVE_INFINITY:t;return n<this.left.length?this.left.slice(e,n):e>this.left.length?this.right.slice(this.right.length-n+this.left.length,this.right.length-e+this.left.length).reverse():this.left.slice(e).concat(this.right.slice(this.right.length-n+this.left.length).reverse())}splice(e,t,n){const r=t||0;this.setCursor(Math.trunc(e));const a=this.right.splice(this.right.length-r,Number.POSITIVE_INFINITY);return n&&tn(this.left,n),a.reverse()}pop(){return this.setCursor(Number.POSITIVE_INFINITY),this.left.pop()}push(e){this.setCursor(Number.POSITIVE_INFINITY),this.left.push(e)}pushMany(e){this.setCursor(Number.POSITIVE_INFINITY),tn(this.left,e)}unshift(e){this.setCursor(0),this.right.push(e)}unshiftMany(e){this.setCursor(0),tn(this.right,e.reverse())}setCursor(e){if(!(e===this.left.length||e>this.left.length&&0===this.right.length||e<0&&0===this.left.length))if(e<this.left.length){const t=this.left.splice(e,Number.POSITIVE_INFINITY);tn(this.right,t.reverse())}else{const t=this.right.splice(this.left.length+this.right.length-e,Number.POSITIVE_INFINITY);tn(this.left,t.reverse())}}}function tn(e,t){let n=0;if(t.length<1e4)e.push(...t);else for(;n<t.length;)e.push(...t.slice(n,n+1e4)),n+=1e4}function nn(e){const t={};let n,r,a,i,s,o,l,c=-1;const u=new en(e);for(;++c<u.length;){for(;c in t;)c=t[c];if(n=u.get(c),c&&\"chunkFlow\"===n[1].type&&\"listItemPrefix\"===u.get(c-1)[1].type&&(o=n[1]._tokenizer.events,a=0,a<o.length&&\"lineEndingBlank\"===o[a][1].type&&(a+=2),a<o.length&&\"content\"===o[a][1].type))for(;++a<o.length&&\"content\"!==o[a][1].type;)\"chunkText\"===o[a][1].type&&(o[a][1]._isInFirstContentOfListItem=!0,a++);if(\"enter\"===n[0])n[1].contentType&&(Object.assign(t,rn(u,c)),c=t[c],l=!0);else if(n[1]._container){for(a=c,r=void 0;a--;)if(i=u.get(a),\"lineEnding\"===i[1].type||\"lineEndingBlank\"===i[1].type)\"enter\"===i[0]&&(r&&(u.get(r)[1].type=\"lineEndingBlank\"),i[1].type=\"lineEnding\",r=a);else if(\"linePrefix\"!==i[1].type&&\"listItemIndent\"!==i[1].type)break;r&&(n[1].end={...u.get(r)[1].start},s=u.slice(r,c),s.unshift(n),u.splice(r,c-r+1,s))}}return ft(e,0,Number.POSITIVE_INFINITY,u.slice(0)),!l}function rn(e,t){const n=e.get(t)[1],r=e.get(t)[2];let a=t-1;const i=[];let s=n._tokenizer;s||(s=r.parser[n.contentType](n.start),n._contentTypeTextTrailing&&(s._contentTypeTextTrailing=!0));const o=s.events,l=[],c={};let u,d,p=-1,h=n,f=0,m=0;const g=[m];for(;h;){for(;e.get(++a)[1]!==h;);i.push(a),h._tokenizer||(u=r.sliceStream(h),h.next||u.push(null),d&&s.defineSkip(h.start),h._isInFirstContentOfListItem&&(s._gfmTasklistFirstContentOfListItem=!0),s.write(u),h._isInFirstContentOfListItem&&(s._gfmTasklistFirstContentOfListItem=void 0)),d=h,h=h.next}for(h=n;++p<o.length;)\"exit\"===o[p][0]&&\"enter\"===o[p-1][0]&&o[p][1].type===o[p-1][1].type&&o[p][1].start.line!==o[p][1].end.line&&(m=p+1,g.push(m),h._tokenizer=void 0,h.previous=void 0,h=h.next);for(s.events=[],h?(h._tokenizer=void 0,h.previous=void 0):g.pop(),p=g.length;p--;){const t=o.slice(g[p],g[p+1]),n=i.pop();l.push([n,n+t.length-1]),e.splice(n,2,t)}for(l.reverse(),p=-1;++p<l.length;)c[f+l[p][0]]=f+l[p][1],f+=l[p][1]-l[p][0]-1;return c}const an={resolve:function(e){return nn(e),e},tokenize:function(e,t){let n;return function(t){return e.enter(\"content\"),n=e.enter(\"chunkContent\",{contentType:\"content\"}),r(t)};function r(t){return null===t?a(t):wt(t)?e.check(sn,i,a)(t):(e.consume(t),r)}function a(n){return e.exit(\"chunkContent\"),e.exit(\"content\"),t(n)}function i(t){return e.consume(t),e.exit(\"chunkContent\"),n.next=e.enter(\"chunkContent\",{contentType:\"content\",previous:n}),n=n.next,r}}},sn={partial:!0,tokenize:function(e,t,n){const r=this;return function(t){return e.exit(\"chunkContent\"),e.enter(\"lineEnding\"),e.consume(t),e.exit(\"lineEnding\"),Lt(e,a,\"linePrefix\")};function a(a){if(null===a||wt(a))return n(a);const i=r.events[r.events.length-1];return!r.parser.constructs.disable.null.includes(\"codeIndented\")&&i&&\"linePrefix\"===i[1].type&&i[2].sliceSerialize(i[1],!0).length>=4?t(a):e.interrupt(r.parser.constructs.flow,n,t)(a)}}};function on(e,t,n,r,a,i,s,o,l){const c=l||Number.POSITIVE_INFINITY;let u=0;return function(t){if(60===t)return e.enter(r),e.enter(a),e.enter(i),e.consume(t),e.exit(i),d;if(null===t||32===t||41===t||At(t))return n(t);return e.enter(r),e.enter(s),e.enter(o),e.enter(\"chunkString\",{contentType:\"string\"}),f(t)};function d(n){return 62===n?(e.enter(i),e.consume(n),e.exit(i),e.exit(a),e.exit(r),t):(e.enter(o),e.enter(\"chunkString\",{contentType:\"string\"}),p(n))}function p(t){return 62===t?(e.exit(\"chunkString\"),e.exit(o),d(t)):null===t||60===t||wt(t)?n(t):(e.consume(t),92===t?h:p)}function h(t){return 60===t||62===t||92===t?(e.consume(t),p):p(t)}function f(a){return u||null!==a&&41!==a&&!It(a)?u<c&&40===a?(e.consume(a),u++,f):41===a?(e.consume(a),u--,f):null===a||32===a||40===a||At(a)?n(a):(e.consume(a),92===a?m:f):(e.exit(\"chunkString\"),e.exit(o),e.exit(s),e.exit(r),t(a))}function m(t){return 40===t||41===t||92===t?(e.consume(t),f):f(t)}}function ln(e,t,n,r,a,i){const s=this;let o,l=0;return function(t){return e.enter(r),e.enter(a),e.consume(t),e.exit(a),e.enter(i),c};function c(d){return l>999||null===d||91===d||93===d&&!o||94===d&&!l&&\"_hiddenFootnoteSupport\"in s.parser.constructs?n(d):93===d?(e.exit(i),e.enter(a),e.consume(d),e.exit(a),e.exit(r),t):wt(d)?(e.enter(\"lineEnding\"),e.consume(d),e.exit(\"lineEnding\"),c):(e.enter(\"chunkString\",{contentType:\"string\"}),u(d))}function u(t){return null===t||91===t||93===t||wt(t)||l++>999?(e.exit(\"chunkString\"),c(t)):(e.consume(t),o||(o=!Ot(t)),92===t?d:u)}function d(t){return 91===t||92===t||93===t?(e.consume(t),l++,u):u(t)}}function cn(e,t,n,r,a,i){let s;return function(t){if(34===t||39===t||40===t)return e.enter(r),e.enter(a),e.consume(t),e.exit(a),s=40===t?41:t,o;return n(t)};function o(n){return n===s?(e.enter(a),e.consume(n),e.exit(a),e.exit(r),t):(e.enter(i),l(n))}function l(t){return t===s?(e.exit(i),o(s)):null===t?n(t):wt(t)?(e.enter(\"lineEnding\"),e.consume(t),e.exit(\"lineEnding\"),Lt(e,l,\"linePrefix\")):(e.enter(\"chunkString\",{contentType:\"string\"}),c(t))}function c(t){return t===s||null===t||wt(t)?(e.exit(\"chunkString\"),l(t)):(e.consume(t),92===t?u:c)}function u(t){return t===s||92===t?(e.consume(t),c):c(t)}}function un(e,t){let n;return function r(a){if(wt(a))return e.enter(\"lineEnding\"),e.consume(a),e.exit(\"lineEnding\"),n=!0,r;if(Ot(a))return Lt(e,r,n?\"linePrefix\":\"lineSuffix\")(a);return t(a)}}const dn={name:\"definition\",tokenize:function(e,t,n){const r=this;let a;return function(t){return e.enter(\"definition\"),function(t){return ln.call(r,e,i,n,\"definitionLabel\",\"definitionLabelMarker\",\"definitionLabelString\")(t)}(t)};function i(t){return a=kt(r.sliceSerialize(r.events[r.events.length-1][1]).slice(1,-1)),58===t?(e.enter(\"definitionMarker\"),e.consume(t),e.exit(\"definitionMarker\"),s):n(t)}function s(t){return It(t)?un(e,o)(t):o(t)}function o(t){return on(e,l,n,\"definitionDestination\",\"definitionDestinationLiteral\",\"definitionDestinationLiteralMarker\",\"definitionDestinationRaw\",\"definitionDestinationString\")(t)}function l(t){return e.attempt(pn,c,c)(t)}function c(t){return Ot(t)?Lt(e,u,\"whitespace\")(t):u(t)}function u(i){return null===i||wt(i)?(e.exit(\"definition\"),r.parser.defined.push(a),t(i)):n(i)}}},pn={partial:!0,tokenize:function(e,t,n){return function(t){return It(t)?un(e,r)(t):n(t)};function r(t){return cn(e,a,n,\"definitionTitle\",\"definitionTitleMarker\",\"definitionTitleString\")(t)}function a(t){return Ot(t)?Lt(e,i,\"whitespace\")(t):i(t)}function i(e){return null===e||wt(e)?t(e):n(e)}}};const hn={name:\"hardBreakEscape\",tokenize:function(e,t,n){return function(t){return e.enter(\"hardBreakEscape\"),e.consume(t),r};function r(r){return wt(r)?(e.exit(\"hardBreakEscape\"),t(r)):n(r)}}};const fn={name:\"headingAtx\",resolve:function(e,t){let n,r,a=e.length-2,i=3;\"whitespace\"===e[i][1].type&&(i+=2);a-2>i&&\"whitespace\"===e[a][1].type&&(a-=2);\"atxHeadingSequence\"===e[a][1].type&&(i===a-1||a-4>i&&\"whitespace\"===e[a-2][1].type)&&(a-=i+1===a?2:4);a>i&&(n={type:\"atxHeadingText\",start:e[i][1].start,end:e[a][1].end},r={type:\"chunkText\",start:e[i][1].start,end:e[a][1].end,contentType:\"text\"},ft(e,i,a-i+1,[[\"enter\",n,t],[\"enter\",r,t],[\"exit\",r,t],[\"exit\",n,t]]));return e},tokenize:function(e,t,n){let r=0;return function(t){return e.enter(\"atxHeading\"),function(t){return e.enter(\"atxHeadingSequence\"),a(t)}(t)};function a(t){return 35===t&&r++<6?(e.consume(t),a):null===t||It(t)?(e.exit(\"atxHeadingSequence\"),i(t)):n(t)}function i(n){return 35===n?(e.enter(\"atxHeadingSequence\"),s(n)):null===n||wt(n)?(e.exit(\"atxHeading\"),t(n)):Ot(n)?Lt(e,i,\"whitespace\")(n):(e.enter(\"atxHeadingText\"),o(n))}function s(t){return 35===t?(e.consume(t),s):(e.exit(\"atxHeadingSequence\"),i(t))}function o(t){return null===t||35===t||It(t)?(e.exit(\"atxHeadingText\"),i(t)):(e.consume(t),o)}}};const mn=[\"address\",\"article\",\"aside\",\"base\",\"basefont\",\"blockquote\",\"body\",\"caption\",\"center\",\"col\",\"colgroup\",\"dd\",\"details\",\"dialog\",\"dir\",\"div\",\"dl\",\"dt\",\"fieldset\",\"figcaption\",\"figure\",\"footer\",\"form\",\"frame\",\"frameset\",\"h1\",\"h2\",\"h3\",\"h4\",\"h5\",\"h6\",\"head\",\"header\",\"hr\",\"html\",\"iframe\",\"legend\",\"li\",\"link\",\"main\",\"menu\",\"menuitem\",\"nav\",\"noframes\",\"ol\",\"optgroup\",\"option\",\"p\",\"param\",\"search\",\"section\",\"summary\",\"table\",\"tbody\",\"td\",\"tfoot\",\"th\",\"thead\",\"title\",\"tr\",\"track\",\"ul\"],gn=[\"pre\",\"script\",\"style\",\"textarea\"],bn={concrete:!0,name:\"htmlFlow\",resolveTo:function(e){let t=e.length;for(;t--&&(\"enter\"!==e[t][0]||\"htmlFlow\"!==e[t][1].type););t>1&&\"linePrefix\"===e[t-2][1].type&&(e[t][1].start=e[t-2][1].start,e[t+1][1].start=e[t-2][1].start,e.splice(t-2,2));return e},tokenize:function(e,t,n){const r=this;let a,i,s,o,l;return function(t){return function(t){return e.enter(\"htmlFlow\"),e.enter(\"htmlFlowData\"),e.consume(t),c}(t)};function c(o){return 33===o?(e.consume(o),u):47===o?(e.consume(o),i=!0,h):63===o?(e.consume(o),a=3,r.interrupt?t:M):Tt(o)?(e.consume(o),s=String.fromCharCode(o),f):n(o)}function u(i){return 45===i?(e.consume(i),a=2,d):91===i?(e.consume(i),a=5,o=0,p):Tt(i)?(e.consume(i),a=4,r.interrupt?t:M):n(i)}function d(a){return 45===a?(e.consume(a),r.interrupt?t:M):n(a)}function p(a){const i=\"CDATA[\";return a===i.charCodeAt(o++)?(e.consume(a),6===o?r.interrupt?t:N:p):n(a)}function h(t){return Tt(t)?(e.consume(t),s=String.fromCharCode(t),f):n(t)}function f(o){if(null===o||47===o||62===o||It(o)){const l=47===o,c=s.toLowerCase();return l||i||!gn.includes(c)?mn.includes(s.toLowerCase())?(a=6,l?(e.consume(o),m):r.interrupt?t(o):N(o)):(a=7,r.interrupt&&!r.parser.lazy[r.now().line]?n(o):i?g(o):b(o)):(a=1,r.interrupt?t(o):N(o))}return 45===o||St(o)?(e.consume(o),s+=String.fromCharCode(o),f):n(o)}function m(a){return 62===a?(e.consume(a),r.interrupt?t:N):n(a)}function g(t){return Ot(t)?(e.consume(t),g):v(t)}function b(t){return 47===t?(e.consume(t),v):58===t||95===t||Tt(t)?(e.consume(t),E):Ot(t)?(e.consume(t),b):v(t)}function E(t){return 45===t||46===t||58===t||95===t||St(t)?(e.consume(t),E):y(t)}function y(t){return 61===t?(e.consume(t),_):Ot(t)?(e.consume(t),y):b(t)}function _(t){return null===t||60===t||61===t||62===t||96===t?n(t):34===t||39===t?(e.consume(t),l=t,k):Ot(t)?(e.consume(t),_):T(t)}function k(t){return t===l?(e.consume(t),l=null,S):null===t||wt(t)?n(t):(e.consume(t),k)}function T(t){return null===t||34===t||39===t||47===t||60===t||61===t||62===t||96===t||It(t)?y(t):(e.consume(t),T)}function S(e){return 47===e||62===e||Ot(e)?b(e):n(e)}function v(t){return 62===t?(e.consume(t),A):n(t)}function A(t){return null===t||wt(t)?N(t):Ot(t)?(e.consume(t),A):n(t)}function N(t){return 45===t&&2===a?(e.consume(t),I):60===t&&1===a?(e.consume(t),O):62===t&&4===a?(e.consume(t),P):63===t&&3===a?(e.consume(t),M):93===t&&5===a?(e.consume(t),D):!wt(t)||6!==a&&7!==a?null===t||wt(t)?(e.exit(\"htmlFlowData\"),C(t)):(e.consume(t),N):(e.exit(\"htmlFlowData\"),e.check(En,L,C)(t))}function C(t){return e.check(yn,x,L)(t)}function x(t){return e.enter(\"lineEnding\"),e.consume(t),e.exit(\"lineEnding\"),w}function w(t){return null===t||wt(t)?C(t):(e.enter(\"htmlFlowData\"),N(t))}function I(t){return 45===t?(e.consume(t),M):N(t)}function O(t){return 47===t?(e.consume(t),s=\"\",R):N(t)}function R(t){if(62===t){const n=s.toLowerCase();return gn.includes(n)?(e.consume(t),P):N(t)}return Tt(t)&&s.length<8?(e.consume(t),s+=String.fromCharCode(t),R):N(t)}function D(t){return 93===t?(e.consume(t),M):N(t)}function M(t){return 62===t?(e.consume(t),P):45===t&&2===a?(e.consume(t),M):N(t)}function P(t){return null===t||wt(t)?(e.exit(\"htmlFlowData\"),L(t)):(e.consume(t),P)}function L(n){return e.exit(\"htmlFlow\"),t(n)}}},En={partial:!0,tokenize:function(e,t,n){return function(r){return e.enter(\"lineEnding\"),e.consume(r),e.exit(\"lineEnding\"),e.attempt(qt,t,n)}}},yn={partial:!0,tokenize:function(e,t,n){const r=this;return function(t){if(wt(t))return e.enter(\"lineEnding\"),e.consume(t),e.exit(\"lineEnding\"),a;return n(t)};function a(e){return r.parser.lazy[r.now().line]?n(e):t(e)}}};const _n={name:\"htmlText\",tokenize:function(e,t,n){const r=this;let a,i,s;return function(t){return e.enter(\"htmlText\"),e.enter(\"htmlTextData\"),e.consume(t),o};function o(t){return 33===t?(e.consume(t),l):47===t?(e.consume(t),_):63===t?(e.consume(t),E):Tt(t)?(e.consume(t),S):n(t)}function l(t){return 45===t?(e.consume(t),c):91===t?(e.consume(t),i=0,h):Tt(t)?(e.consume(t),b):n(t)}function c(t){return 45===t?(e.consume(t),p):n(t)}function u(t){return null===t?n(t):45===t?(e.consume(t),d):wt(t)?(s=u,R(t)):(e.consume(t),u)}function d(t){return 45===t?(e.consume(t),p):u(t)}function p(e){return 62===e?O(e):45===e?d(e):u(e)}function h(t){const r=\"CDATA[\";return t===r.charCodeAt(i++)?(e.consume(t),6===i?f:h):n(t)}function f(t){return null===t?n(t):93===t?(e.consume(t),m):wt(t)?(s=f,R(t)):(e.consume(t),f)}function m(t){return 93===t?(e.consume(t),g):f(t)}function g(t){return 62===t?O(t):93===t?(e.consume(t),g):f(t)}function b(t){return null===t||62===t?O(t):wt(t)?(s=b,R(t)):(e.consume(t),b)}function E(t){return null===t?n(t):63===t?(e.consume(t),y):wt(t)?(s=E,R(t)):(e.consume(t),E)}function y(e){return 62===e?O(e):E(e)}function _(t){return Tt(t)?(e.consume(t),k):n(t)}function k(t){return 45===t||St(t)?(e.consume(t),k):T(t)}function T(t){return wt(t)?(s=T,R(t)):Ot(t)?(e.consume(t),T):O(t)}function S(t){return 45===t||St(t)?(e.consume(t),S):47===t||62===t||It(t)?v(t):n(t)}function v(t){return 47===t?(e.consume(t),O):58===t||95===t||Tt(t)?(e.consume(t),A):wt(t)?(s=v,R(t)):Ot(t)?(e.consume(t),v):O(t)}function A(t){return 45===t||46===t||58===t||95===t||St(t)?(e.consume(t),A):N(t)}function N(t){return 61===t?(e.consume(t),C):wt(t)?(s=N,R(t)):Ot(t)?(e.consume(t),N):v(t)}function C(t){return null===t||60===t||61===t||62===t||96===t?n(t):34===t||39===t?(e.consume(t),a=t,x):wt(t)?(s=C,R(t)):Ot(t)?(e.consume(t),C):(e.consume(t),w)}function x(t){return t===a?(e.consume(t),a=void 0,I):null===t?n(t):wt(t)?(s=x,R(t)):(e.consume(t),x)}function w(t){return null===t||34===t||39===t||60===t||61===t||96===t?n(t):47===t||62===t||It(t)?v(t):(e.consume(t),w)}function I(e){return 47===e||62===e||It(e)?v(e):n(e)}function O(r){return 62===r?(e.consume(r),e.exit(\"htmlTextData\"),e.exit(\"htmlText\"),t):n(r)}function R(t){return e.exit(\"htmlTextData\"),e.enter(\"lineEnding\"),e.consume(t),e.exit(\"lineEnding\"),D}function D(t){return Ot(t)?Lt(e,M,\"linePrefix\",r.parser.constructs.disable.null.includes(\"codeIndented\")?void 0:4)(t):M(t)}function M(t){return e.enter(\"htmlTextData\"),s(t)}}};const kn={name:\"labelEnd\",resolveAll:function(e){let t=-1;const n=[];for(;++t<e.length;){const r=e[t][1];if(n.push(e[t]),\"labelImage\"===r.type||\"labelLink\"===r.type||\"labelEnd\"===r.type){const e=\"labelImage\"===r.type?4:2;r.type=\"data\",t+=e}}e.length!==n.length&&ft(e,0,e.length,n);return e},resolveTo:function(e,t){let n,r,a,i,s=e.length,o=0;for(;s--;)if(n=e[s][1],r){if(\"link\"===n.type||\"labelLink\"===n.type&&n._inactive)break;\"enter\"===e[s][0]&&\"labelLink\"===n.type&&(n._inactive=!0)}else if(a){if(\"enter\"===e[s][0]&&(\"labelImage\"===n.type||\"labelLink\"===n.type)&&!n._balanced&&(r=s,\"labelLink\"!==n.type)){o=2;break}}else\"labelEnd\"===n.type&&(a=s);const l={type:\"labelLink\"===e[r][1].type?\"link\":\"image\",start:{...e[r][1].start},end:{...e[e.length-1][1].end}},c={type:\"label\",start:{...e[r][1].start},end:{...e[a][1].end}},u={type:\"labelText\",start:{...e[r+o+2][1].end},end:{...e[a-2][1].start}};return i=[[\"enter\",l,t],[\"enter\",c,t]],i=mt(i,e.slice(r+1,r+o+3)),i=mt(i,[[\"enter\",u,t]]),i=mt(i,zt(t.parser.constructs.insideSpan.null,e.slice(r+o+4,a-3),t)),i=mt(i,[[\"exit\",u,t],e[a-2],e[a-1],[\"exit\",c,t]]),i=mt(i,e.slice(a+1)),i=mt(i,[[\"exit\",l,t]]),ft(e,r,e.length,i),e},tokenize:function(e,t,n){const r=this;let a,i,s=r.events.length;for(;s--;)if((\"labelImage\"===r.events[s][1].type||\"labelLink\"===r.events[s][1].type)&&!r.events[s][1]._balanced){a=r.events[s][1];break}return function(t){if(!a)return n(t);if(a._inactive)return u(t);return i=r.parser.defined.includes(kt(r.sliceSerialize({start:a.end,end:r.now()}))),e.enter(\"labelEnd\"),e.enter(\"labelMarker\"),e.consume(t),e.exit(\"labelMarker\"),e.exit(\"labelEnd\"),o};function o(t){return 40===t?e.attempt(Tn,c,i?c:u)(t):91===t?e.attempt(Sn,c,i?l:u)(t):i?c(t):u(t)}function l(t){return e.attempt(vn,c,u)(t)}function c(e){return t(e)}function u(e){return a._balanced=!0,n(e)}}},Tn={tokenize:function(e,t,n){return function(t){return e.enter(\"resource\"),e.enter(\"resourceMarker\"),e.consume(t),e.exit(\"resourceMarker\"),r};function r(t){return It(t)?un(e,a)(t):a(t)}function a(t){return 41===t?c(t):on(e,i,s,\"resourceDestination\",\"resourceDestinationLiteral\",\"resourceDestinationLiteralMarker\",\"resourceDestinationRaw\",\"resourceDestinationString\",32)(t)}function i(t){return It(t)?un(e,o)(t):c(t)}function s(e){return n(e)}function o(t){return 34===t||39===t||40===t?cn(e,l,n,\"resourceTitle\",\"resourceTitleMarker\",\"resourceTitleString\")(t):c(t)}function l(t){return It(t)?un(e,c)(t):c(t)}function c(r){return 41===r?(e.enter(\"resourceMarker\"),e.consume(r),e.exit(\"resourceMarker\"),e.exit(\"resource\"),t):n(r)}}},Sn={tokenize:function(e,t,n){const r=this;return function(t){return ln.call(r,e,a,i,\"reference\",\"referenceMarker\",\"referenceString\")(t)};function a(e){return r.parser.defined.includes(kt(r.sliceSerialize(r.events[r.events.length-1][1]).slice(1,-1)))?t(e):n(e)}function i(e){return n(e)}}},vn={tokenize:function(e,t,n){return function(t){return e.enter(\"reference\"),e.enter(\"referenceMarker\"),e.consume(t),e.exit(\"referenceMarker\"),r};function r(r){return 93===r?(e.enter(\"referenceMarker\"),e.consume(r),e.exit(\"referenceMarker\"),e.exit(\"reference\"),t):n(r)}}};const An={name:\"labelStartImage\",resolveAll:kn.resolveAll,tokenize:function(e,t,n){const r=this;return function(t){return e.enter(\"labelImage\"),e.enter(\"labelImageMarker\"),e.consume(t),e.exit(\"labelImageMarker\"),a};function a(t){return 91===t?(e.enter(\"labelMarker\"),e.consume(t),e.exit(\"labelMarker\"),e.exit(\"labelImage\"),i):n(t)}function i(e){return 94===e&&\"_hiddenFootnoteSupport\"in r.parser.constructs?n(e):t(e)}}};const Nn={name:\"labelStartLink\",resolveAll:kn.resolveAll,tokenize:function(e,t,n){const r=this;return function(t){return e.enter(\"labelLink\"),e.enter(\"labelMarker\"),e.consume(t),e.exit(\"labelMarker\"),e.exit(\"labelLink\"),a};function a(e){return 94===e&&\"_hiddenFootnoteSupport\"in r.parser.constructs?n(e):t(e)}}};const Cn={name:\"lineEnding\",tokenize:function(e,t){return function(n){return e.enter(\"lineEnding\"),e.consume(n),e.exit(\"lineEnding\"),Lt(e,t,\"linePrefix\")}}};const xn={name:\"thematicBreak\",tokenize:function(e,t,n){let r,a=0;return function(t){return e.enter(\"thematicBreak\"),function(e){return r=e,i(e)}(t)};function i(i){return i===r?(e.enter(\"thematicBreakSequence\"),s(i)):a>=3&&(null===i||wt(i))?(e.exit(\"thematicBreak\"),t(i)):n(i)}function s(t){return t===r?(e.consume(t),a++,s):(e.exit(\"thematicBreakSequence\"),Ot(t)?Lt(e,i,\"whitespace\")(t):i(t))}}};const wn={continuation:{tokenize:function(e,t,n){const r=this;return r.containerState._closeFlow=void 0,e.check(qt,function(n){return r.containerState.furtherBlankLines=r.containerState.furtherBlankLines||r.containerState.initialBlankLine,Lt(e,t,\"listItemIndent\",r.containerState.size+1)(n)},function(n){if(r.containerState.furtherBlankLines||!Ot(n))return r.containerState.furtherBlankLines=void 0,r.containerState.initialBlankLine=void 0,a(n);return r.containerState.furtherBlankLines=void 0,r.containerState.initialBlankLine=void 0,e.attempt(On,t,a)(n)});function a(a){return r.containerState._closeFlow=!0,r.interrupt=void 0,Lt(e,e.attempt(wn,t,n),\"linePrefix\",r.parser.constructs.disable.null.includes(\"codeIndented\")?void 0:4)(a)}}},exit:function(e){e.exit(this.containerState.type)},name:\"list\",tokenize:function(e,t,n){const r=this,a=r.events[r.events.length-1];let i=a&&\"linePrefix\"===a[1].type?a[2].sliceSerialize(a[1],!0).length:0,s=0;return function(t){const a=r.containerState.type||(42===t||43===t||45===t?\"listUnordered\":\"listOrdered\");if(\"listUnordered\"===a?!r.containerState.marker||t===r.containerState.marker:Nt(t)){if(r.containerState.type||(r.containerState.type=a,e.enter(a,{_container:!0})),\"listUnordered\"===a)return e.enter(\"listItemPrefix\"),42===t||45===t?e.check(xn,n,l)(t):l(t);if(!r.interrupt||49===t)return e.enter(\"listItemPrefix\"),e.enter(\"listItemValue\"),o(t)}return n(t)};function o(t){return Nt(t)&&++s<10?(e.consume(t),o):(!r.interrupt||s<2)&&(r.containerState.marker?t===r.containerState.marker:41===t||46===t)?(e.exit(\"listItemValue\"),l(t)):n(t)}function l(t){return e.enter(\"listItemMarker\"),e.consume(t),e.exit(\"listItemMarker\"),r.containerState.marker=r.containerState.marker||t,e.check(qt,r.interrupt?n:c,e.attempt(In,d,u))}function c(e){return r.containerState.initialBlankLine=!0,i++,d(e)}function u(t){return Ot(t)?(e.enter(\"listItemPrefixWhitespace\"),e.consume(t),e.exit(\"listItemPrefixWhitespace\"),d):n(t)}function d(n){return r.containerState.size=i+r.sliceSerialize(e.exit(\"listItemPrefix\"),!0).length,t(n)}}},In={partial:!0,tokenize:function(e,t,n){const r=this;return Lt(e,function(e){const a=r.events[r.events.length-1];return!Ot(e)&&a&&\"listItemPrefixWhitespace\"===a[1].type?t(e):n(e)},\"listItemPrefixWhitespace\",r.parser.constructs.disable.null.includes(\"codeIndented\")?void 0:5)}},On={partial:!0,tokenize:function(e,t,n){const r=this;return Lt(e,function(e){const a=r.events[r.events.length-1];return a&&\"listItemIndent\"===a[1].type&&a[2].sliceSerialize(a[1],!0).length===r.containerState.size?t(e):n(e)},\"listItemIndent\",r.containerState.size+1)}};const Rn={name:\"setextUnderline\",resolveTo:function(e,t){let n,r,a,i=e.length;for(;i--;)if(\"enter\"===e[i][0]){if(\"content\"===e[i][1].type){n=i;break}\"paragraph\"===e[i][1].type&&(r=i)}else\"content\"===e[i][1].type&&e.splice(i,1),a||\"definition\"!==e[i][1].type||(a=i);const s={type:\"setextHeading\",start:{...e[n][1].start},end:{...e[e.length-1][1].end}};e[r][1].type=\"setextHeadingText\",a?(e.splice(r,0,[\"enter\",s,t]),e.splice(a+1,0,[\"exit\",e[n][1],t]),e[n][1].end={...e[a][1].end}):e[n][1]=s;return e.push([\"exit\",s,t]),e},tokenize:function(e,t,n){const r=this;let a;return function(t){let s,o=r.events.length;for(;o--;)if(\"lineEnding\"!==r.events[o][1].type&&\"linePrefix\"!==r.events[o][1].type&&\"content\"!==r.events[o][1].type){s=\"paragraph\"===r.events[o][1].type;break}if(!r.parser.lazy[r.now().line]&&(r.interrupt||s))return e.enter(\"setextHeadingLine\"),a=t,function(t){return e.enter(\"setextHeadingLineSequence\"),i(t)}(t);return n(t)};function i(t){return t===a?(e.consume(t),i):(e.exit(\"setextHeadingLineSequence\"),Ot(t)?Lt(e,s,\"lineSuffix\")(t):s(t))}function s(r){return null===r||wt(r)?(e.exit(\"setextHeadingLine\"),t(r)):n(r)}}};const Dn={tokenize:function(e){const t=this,n=e.attempt(qt,function(r){if(null===r)return void e.consume(r);return e.enter(\"lineEndingBlank\"),e.consume(r),e.exit(\"lineEndingBlank\"),t.currentConstruct=void 0,n},e.attempt(this.parser.constructs.flowInitial,r,Lt(e,e.attempt(this.parser.constructs.flow,r,e.attempt(an,r)),\"linePrefix\")));return n;function r(r){if(null!==r)return e.enter(\"lineEnding\"),e.consume(r),e.exit(\"lineEnding\"),t.currentConstruct=void 0,n;e.consume(r)}}};const Mn={resolveAll:Bn()},Pn=Fn(\"string\"),Ln=Fn(\"text\");function Fn(e){return{resolveAll:Bn(\"text\"===e?Un:void 0),tokenize:function(t){const n=this,r=this.parser.constructs[e],a=t.attempt(r,i,s);return i;function i(e){return l(e)?a(e):s(e)}function s(e){if(null!==e)return t.enter(\"data\"),t.consume(e),o;t.consume(e)}function o(e){return l(e)?(t.exit(\"data\"),a(e)):(t.consume(e),o)}function l(e){if(null===e)return!0;const t=r[e];let a=-1;if(t)for(;++a<t.length;){const e=t[a];if(!e.previous||e.previous.call(n,n.previous))return!0}return!1}}}}function Bn(e){return function(t,n){let r,a=-1;for(;++a<=t.length;)void 0===r?t[a]&&\"data\"===t[a][1].type&&(r=a,a++):t[a]&&\"data\"===t[a][1].type||(a!==r+2&&(t[r][1].end=t[a-1][1].end,t.splice(r+2,a-r-2),a=r+2),r=void 0);return e?e(t,n):t}}function Un(e,t){let n=0;for(;++n<=e.length;)if((n===e.length||\"lineEnding\"===e[n][1].type)&&\"data\"===e[n-1][1].type){const r=e[n-1][1],a=t.sliceStream(r);let i,s=a.length,o=-1,l=0;for(;s--;){const e=a[s];if(\"string\"==typeof e){for(o=e.length;32===e.charCodeAt(o-1);)l++,o--;if(o)break;o=-1}else if(-2===e)i=!0,l++;else if(-1!==e){s++;break}}if(t._contentTypeTextTrailing&&n===e.length&&(l=0),l){const a={type:n===e.length||i||l<2?\"lineSuffix\":\"hardBreakTrailing\",start:{_bufferIndex:s?o:r.start._bufferIndex+o,_index:r.start._index+s,line:r.end.line,column:r.end.column-l,offset:r.end.offset-l},end:{...r.end}};r.end={...a.start},r.start.offset===r.end.offset?Object.assign(r,a):(e.splice(n,0,[\"enter\",a,t],[\"exit\",a,t]),n+=2)}n++}return e}const Hn={42:wn,43:wn,45:wn,48:wn,49:wn,50:wn,51:wn,52:wn,53:wn,54:wn,55:wn,56:wn,57:wn,62:Yt},zn={91:dn},Gn={[-2]:Xt,[-1]:Xt,32:Xt},jn={35:fn,42:xn,45:[Rn,xn],60:bn,61:Rn,95:xn,96:Qt,126:Qt},$n={38:Vt,92:Wt},qn={[-5]:Cn,[-4]:Cn,[-3]:Cn,33:An,38:Vt,42:Gt,60:[$t,_n],91:Nn,92:[hn,Wt],93:kn,95:Gt,96:Jt},Yn={null:[Gt,Mn]},Wn=Object.freeze(Object.defineProperty({__proto__:null,attentionMarkers:{null:[42,95]},contentInitial:zn,disable:{null:[]},document:Hn,flow:jn,flowInitial:Gn,insideSpan:Yn,string:$n,text:qn},Symbol.toStringTag,{value:\"Module\"}));function Vn(e,t,n){let r={_bufferIndex:-1,_index:0,line:n&&n.line||1,column:n&&n.column||1,offset:n&&n.offset||0};const a={},i=[];let s=[],o=[];const l={attempt:g(function(e,t){b(e,t.from)}),check:g(m),consume:function(e){wt(e)?(r.line++,r.column=1,r.offset+=-3===e?2:1,E()):-1!==e&&(r.column++,r.offset++);r._bufferIndex<0?r._index++:(r._bufferIndex++,r._bufferIndex===s[r._index].length&&(r._bufferIndex=-1,r._index++));c.previous=e},enter:function(e,t){const n=t||{};return n.type=e,n.start=p(),c.events.push([\"enter\",n,c]),o.push(n),n},exit:function(e){const t=o.pop();return t.end=p(),c.events.push([\"exit\",t,c]),t},interrupt:g(m,{interrupt:!0})},c={code:null,containerState:{},defineSkip:function(e){a[e.line]=e.column,E()},events:[],now:p,parser:e,previous:null,sliceSerialize:function(e,t){return function(e,t){let n=-1;const r=[];let a;for(;++n<e.length;){const i=e[n];let s;if(\"string\"==typeof i)s=i;else switch(i){case-5:s=\"\\r\";break;case-4:s=\"\\n\";break;case-3:s=\"\\r\\n\";break;case-2:s=t?\" \":\"\\t\";break;case-1:if(!t&&a)continue;s=\" \";break;default:s=String.fromCharCode(i)}a=-2===i,r.push(s)}return r.join(\"\")}(d(e),t)},sliceStream:d,write:function(e){if(s=mt(s,e),h(),null!==s[s.length-1])return[];return b(t,0),c.events=zt(i,c.events,c),c.events}};let u=t.tokenize.call(c,l);return t.resolveAll&&i.push(t),c;function d(e){return function(e,t){const n=t.start._index,r=t.start._bufferIndex,a=t.end._index,i=t.end._bufferIndex;let s;if(n===a)s=[e[n].slice(r,i)];else{if(s=e.slice(n,a),r>-1){const e=s[0];\"string\"==typeof e?s[0]=e.slice(r):s.shift()}i>0&&s.push(e[a].slice(0,i))}return s}(s,e)}function p(){const{_bufferIndex:e,_index:t,line:n,column:a,offset:i}=r;return{_bufferIndex:e,_index:t,line:n,column:a,offset:i}}function h(){let e;for(;r._index<s.length;){const t=s[r._index];if(\"string\"==typeof t)for(e=r._index,r._bufferIndex<0&&(r._bufferIndex=0);r._index===e&&r._bufferIndex<t.length;)f(t.charCodeAt(r._bufferIndex));else f(t)}}function f(e){u=u(e)}function m(e,t){t.restore()}function g(e,t){return function(n,a,i){let s,u,d,h;return Array.isArray(n)?f(n):\"tokenize\"in n?f([n]):function(e){return t;function t(t){const n=null!==t&&e[t],r=null!==t&&e.null;return f([...Array.isArray(n)?n:n?[n]:[],...Array.isArray(r)?r:r?[r]:[]])(t)}}(n);function f(e){return s=e,u=0,0===e.length?i:m(e[u])}function m(e){return function(n){h=function(){const e=p(),t=c.previous,n=c.currentConstruct,a=c.events.length,i=Array.from(o);return{from:a,restore:s};function s(){r=e,c.previous=t,c.currentConstruct=n,c.events.length=a,o=i,E()}}(),d=e,e.partial||(c.currentConstruct=e);if(e.name&&c.parser.constructs.disable.null.includes(e.name))return b();return e.tokenize.call(t?Object.assign(Object.create(c),t):c,l,g,b)(n)}}function g(t){return e(d,h),a}function b(e){return h.restore(),++u<s.length?m(s[u]):i}}}function b(e,t){e.resolveAll&&!i.includes(e)&&i.push(e),e.resolve&&ft(c.events,t,c.events.length-t,e.resolve(c.events.slice(t),c)),e.resolveTo&&(c.events=e.resolveTo(c.events,c))}function E(){r.line in a&&r.column<2&&(r.column=a[r.line],r.offset+=a[r.line]-1)}}const Kn=/[\\0\\t\\n\\r]/g;const Qn=/\\\\([!-/:-@[-`{-~])|&(#(?:\\d{1,7}|x[\\da-f]{1,6})|[\\da-z]{1,31});/gi;function Xn(e,t,n){if(t)return t;if(35===n.charCodeAt(0)){const e=n.charCodeAt(1),t=120===e||88===e;return _t(n.slice(t?2:1),t?16:10)}return ht(n)||e}const Zn={}.hasOwnProperty;function Jn(e,t,n){return t&&\"object\"==typeof t&&(n=t,t=void 0),function(e){const t={transforms:[],canContainEols:[\"emphasis\",\"fragment\",\"heading\",\"paragraph\",\"strong\"],enter:{autolink:i(te),autolinkProtocol:A,autolinkEmail:A,atxHeading:i(X),blockQuote:i(Y),characterEscape:A,characterReference:A,codeFenced:i(W),codeFencedFenceInfo:s,codeFencedFenceMeta:s,codeIndented:i(W,s),codeText:i(V,s),codeTextData:A,data:A,codeFlowValue:A,definition:i(K),definitionDestinationString:s,definitionLabelString:s,definitionTitleString:s,emphasis:i(Q),hardBreakEscape:i(Z),hardBreakTrailing:i(Z),htmlFlow:i(J,s),htmlFlowData:A,htmlText:i(J,s),htmlTextData:A,image:i(ee),label:s,link:i(te),listItem:i(re),listItemValue:p,listOrdered:i(ne,d),listUnordered:i(ne),paragraph:i(ae),reference:U,referenceString:s,resourceDestinationString:s,resourceTitleString:s,setextHeading:i(X),strong:i(ie),thematicBreak:i(oe)},exit:{atxHeading:l(),atxHeadingSequence:k,autolink:l(),autolinkEmail:q,autolinkProtocol:$,blockQuote:l(),characterEscapeValue:N,characterReferenceMarkerHexadecimal:z,characterReferenceMarkerNumeric:z,characterReferenceValue:G,characterReference:j,codeFenced:l(g),codeFencedFence:m,codeFencedFenceInfo:h,codeFencedFenceMeta:f,codeFlowValue:N,codeIndented:l(b),codeText:l(O),codeTextData:N,data:N,definition:l(),definitionDestinationString:_,definitionLabelString:E,definitionTitleString:y,emphasis:l(),hardBreakEscape:l(x),hardBreakTrailing:l(x),htmlFlow:l(w),htmlFlowData:N,htmlText:l(I),htmlTextData:N,image:l(D),label:P,labelText:M,lineEnding:C,link:l(R),listItem:l(),listOrdered:l(),listUnordered:l(),paragraph:l(),referenceString:H,resourceDestinationString:L,resourceTitleString:F,resource:B,setextHeading:l(v),setextHeadingLineSequence:S,setextHeadingText:T,strong:l(),thematicBreak:l()}};tr(t,(e||{}).mdastExtensions||[]);const n={};return r;function r(e){let r={type:\"root\",children:[]};const i={stack:[r],tokenStack:[],config:t,enter:o,exit:c,buffer:s,resume:u,data:n},l=[];let d=-1;for(;++d<e.length;)if(\"listOrdered\"===e[d][1].type||\"listUnordered\"===e[d][1].type)if(\"enter\"===e[d][0])l.push(d);else{d=a(e,l.pop(),d)}for(d=-1;++d<e.length;){const n=t[e[d][0]];Zn.call(n,e[d][1].type)&&n[e[d][1].type].call(Object.assign({sliceSerialize:e[d][2].sliceSerialize},i),e[d][1])}if(i.tokenStack.length>0){const e=i.tokenStack[i.tokenStack.length-1];(e[1]||rr).call(i,void 0,e[0])}for(r.position={start:er(e.length>0?e[0][1].start:{line:1,column:1,offset:0}),end:er(e.length>0?e[e.length-2][1].end:{line:1,column:1,offset:0})},d=-1;++d<t.transforms.length;)r=t.transforms[d](r)||r;return r}function a(e,t,n){let r,a,i,s,o=t-1,l=-1,c=!1;for(;++o<=n;){const t=e[o];switch(t[1].type){case\"listUnordered\":case\"listOrdered\":case\"blockQuote\":\"enter\"===t[0]?l++:l--,s=void 0;break;case\"lineEndingBlank\":\"enter\"===t[0]&&(!r||s||l||i||(i=o),s=void 0);break;case\"linePrefix\":case\"listItemValue\":case\"listItemMarker\":case\"listItemPrefix\":case\"listItemPrefixWhitespace\":break;default:s=void 0}if(!l&&\"enter\"===t[0]&&\"listItemPrefix\"===t[1].type||-1===l&&\"exit\"===t[0]&&(\"listUnordered\"===t[1].type||\"listOrdered\"===t[1].type)){if(r){let s=o;for(a=void 0;s--;){const t=e[s];if(\"lineEnding\"===t[1].type||\"lineEndingBlank\"===t[1].type){if(\"exit\"===t[0])continue;a&&(e[a][1].type=\"lineEndingBlank\",c=!0),t[1].type=\"lineEnding\",a=s}else if(\"linePrefix\"!==t[1].type&&\"blockQuotePrefix\"!==t[1].type&&\"blockQuotePrefixWhitespace\"!==t[1].type&&\"blockQuoteMarker\"!==t[1].type&&\"listItemIndent\"!==t[1].type)break}i&&(!a||i<a)&&(r._spread=!0),r.end=Object.assign({},a?e[a][1].start:t[1].end),e.splice(a||o,0,[\"exit\",r,t[2]]),o++,n++}if(\"listItemPrefix\"===t[1].type){const a={type:\"listItem\",_spread:!1,start:Object.assign({},t[1].start),end:void 0};r=a,e.splice(o,0,[\"enter\",a,t[2]]),o++,n++,i=void 0,s=!0}}}return e[t][1]._spread=c,n}function i(e,t){return n;function n(n){o.call(this,e(n),n),t&&t.call(this,n)}}function s(){this.stack.push({type:\"fragment\",children:[]})}function o(e,t,n){this.stack[this.stack.length-1].children.push(e),this.stack.push(e),this.tokenStack.push([t,n||void 0]),e.position={start:er(t.start),end:void 0}}function l(e){return t;function t(t){e&&e.call(this,t),c.call(this,t)}}function c(e,t){const n=this.stack.pop(),r=this.tokenStack.pop();if(!r)throw new Error(\"Cannot close `\"+e.type+\"` (\"+He({start:e.start,end:e.end})+\"): it’s not open\");if(r[0].type!==e.type)if(t)t.call(this,e,r[0]);else{(r[1]||rr).call(this,e,r[0])}n.position.end=er(e.end)}function u(){return ct(this.stack.pop())}function d(){this.data.expectingFirstListItemValue=!0}function p(e){if(this.data.expectingFirstListItemValue){this.stack[this.stack.length-2].start=Number.parseInt(this.sliceSerialize(e),10),this.data.expectingFirstListItemValue=void 0}}function h(){const e=this.resume();this.stack[this.stack.length-1].lang=e}function f(){const e=this.resume();this.stack[this.stack.length-1].meta=e}function m(){this.data.flowCodeInside||(this.buffer(),this.data.flowCodeInside=!0)}function g(){const e=this.resume();this.stack[this.stack.length-1].value=e.replace(/^(\\r?\\n|\\r)|(\\r?\\n|\\r)$/g,\"\"),this.data.flowCodeInside=void 0}function b(){const e=this.resume();this.stack[this.stack.length-1].value=e.replace(/(\\r?\\n|\\r)$/g,\"\")}function E(e){const t=this.resume(),n=this.stack[this.stack.length-1];n.label=t,n.identifier=kt(this.sliceSerialize(e)).toLowerCase()}function y(){const e=this.resume();this.stack[this.stack.length-1].title=e}function _(){const e=this.resume();this.stack[this.stack.length-1].url=e}function k(e){const t=this.stack[this.stack.length-1];if(!t.depth){const n=this.sliceSerialize(e).length;t.depth=n}}function T(){this.data.setextHeadingSlurpLineEnding=!0}function S(e){this.stack[this.stack.length-1].depth=61===this.sliceSerialize(e).codePointAt(0)?1:2}function v(){this.data.setextHeadingSlurpLineEnding=void 0}function A(e){const t=this.stack[this.stack.length-1].children;let n=t[t.length-1];n&&\"text\"===n.type||(n=se(),n.position={start:er(e.start),end:void 0},t.push(n)),this.stack.push(n)}function N(e){const t=this.stack.pop();t.value+=this.sliceSerialize(e),t.position.end=er(e.end)}function C(e){const n=this.stack[this.stack.length-1];if(this.data.atHardBreak){return n.children[n.children.length-1].position.end=er(e.end),void(this.data.atHardBreak=void 0)}!this.data.setextHeadingSlurpLineEnding&&t.canContainEols.includes(n.type)&&(A.call(this,e),N.call(this,e))}function x(){this.data.atHardBreak=!0}function w(){const e=this.resume();this.stack[this.stack.length-1].value=e}function I(){const e=this.resume();this.stack[this.stack.length-1].value=e}function O(){const e=this.resume();this.stack[this.stack.length-1].value=e}function R(){const e=this.stack[this.stack.length-1];if(this.data.inReference){const t=this.data.referenceType||\"shortcut\";e.type+=\"Reference\",e.referenceType=t,delete e.url,delete e.title}else delete e.identifier,delete e.label;this.data.referenceType=void 0}function D(){const e=this.stack[this.stack.length-1];if(this.data.inReference){const t=this.data.referenceType||\"shortcut\";e.type+=\"Reference\",e.referenceType=t,delete e.url,delete e.title}else delete e.identifier,delete e.label;this.data.referenceType=void 0}function M(e){const t=this.sliceSerialize(e),n=this.stack[this.stack.length-2];n.label=function(e){return e.replace(Qn,Xn)}(t),n.identifier=kt(t).toLowerCase()}function P(){const e=this.stack[this.stack.length-1],t=this.resume(),n=this.stack[this.stack.length-1];if(this.data.inReference=!0,\"link\"===n.type){const t=e.children;n.children=t}else n.alt=t}function L(){const e=this.resume();this.stack[this.stack.length-1].url=e}function F(){const e=this.resume();this.stack[this.stack.length-1].title=e}function B(){this.data.inReference=void 0}function U(){this.data.referenceType=\"collapsed\"}function H(e){const t=this.resume(),n=this.stack[this.stack.length-1];n.label=t,n.identifier=kt(this.sliceSerialize(e)).toLowerCase(),this.data.referenceType=\"full\"}function z(e){this.data.characterReferenceType=e.type}function G(e){const t=this.sliceSerialize(e),n=this.data.characterReferenceType;let r;if(n)r=_t(t,\"characterReferenceMarkerNumeric\"===n?10:16),this.data.characterReferenceType=void 0;else{r=ht(t)}this.stack[this.stack.length-1].value+=r}function j(e){this.stack.pop().position.end=er(e.end)}function $(e){N.call(this,e);this.stack[this.stack.length-1].url=this.sliceSerialize(e)}function q(e){N.call(this,e);this.stack[this.stack.length-1].url=\"mailto:\"+this.sliceSerialize(e)}function Y(){return{type:\"blockquote\",children:[]}}function W(){return{type:\"code\",lang:null,meta:null,value:\"\"}}function V(){return{type:\"inlineCode\",value:\"\"}}function K(){return{type:\"definition\",identifier:\"\",label:null,title:null,url:\"\"}}function Q(){return{type:\"emphasis\",children:[]}}function X(){return{type:\"heading\",depth:0,children:[]}}function Z(){return{type:\"break\"}}function J(){return{type:\"html\",value:\"\"}}function ee(){return{type:\"image\",title:null,url:\"\",alt:null}}function te(){return{type:\"link\",title:null,url:\"\",children:[]}}function ne(e){return{type:\"list\",ordered:\"listOrdered\"===e.type,start:null,spread:e._spread,children:[]}}function re(e){return{type:\"listItem\",spread:e._spread,checked:null,children:[]}}function ae(){return{type:\"paragraph\",children:[]}}function ie(){return{type:\"strong\",children:[]}}function se(){return{type:\"text\",value:\"\"}}function oe(){return{type:\"thematicBreak\"}}}(n)(function(e){for(;!nn(e););return e}(function(e){const t={constructs:bt([Wn,...(e||{}).extensions||[]]),content:n(Ft),defined:[],document:n(Bt),flow:n(Dn),lazy:{},string:n(Pn),text:n(Ln)};return t;function n(e){return function(n){return Vn(t,e,n)}}}(n).document().write(function(){let e,t=1,n=\"\",r=!0;return function(a,i,s){const o=[];let l,c,u,d,p;for(a=n+(\"string\"==typeof a?a.toString():new TextDecoder(i||void 0).decode(a)),u=0,n=\"\",r&&(65279===a.charCodeAt(0)&&u++,r=void 0);u<a.length;){if(Kn.lastIndex=u,l=Kn.exec(a),d=l&&void 0!==l.index?l.index:a.length,p=a.charCodeAt(d),!l){n=a.slice(u);break}if(10===p&&u===d&&e)o.push(-3),e=void 0;else switch(e&&(o.push(-5),e=void 0),u<d&&(o.push(a.slice(u,d)),t+=d-u),p){case 0:o.push(65533),t++;break;case 9:for(c=4*Math.ceil(t/4),o.push(-2);t++<c;)o.push(-1);break;case 10:o.push(-4),t=1;break;default:e=!0,t=1}u=d+1}return s&&(e&&o.push(-5),n&&o.push(n),o.push(null)),o}}()(e,t,!0))))}function er(e){return{line:e.line,column:e.column,offset:e.offset}}function tr(e,t){let n=-1;for(;++n<t.length;){const r=t[n];Array.isArray(r)?tr(e,r):nr(e,r)}}function nr(e,t){let n;for(n in t)if(Zn.call(t,n))switch(n){case\"canContainEols\":{const r=t[n];r&&e[n].push(...r);break}case\"transforms\":{const r=t[n];r&&e[n].push(...r);break}case\"enter\":case\"exit\":{const r=t[n];r&&Object.assign(e[n],r);break}}}function rr(e,t){throw e?new Error(\"Cannot close `\"+e.type+\"` (\"+He({start:e.start,end:e.end})+\"): a different token (`\"+t.type+\"`, \"+He({start:t.start,end:t.end})+\") is open\"):new Error(\"Cannot close document, a token (`\"+t.type+\"`, \"+He({start:t.start,end:t.end})+\") is still open\")}function ar(e){const t=this;t.parser=function(n){return Jn(n,{...t.data(\"settings\"),...e,extensions:t.data(\"micromarkExtensions\")||[],mdastExtensions:t.data(\"fromMarkdownExtensions\")||[]})}}function ir(e,t){const n=t.referenceType;let r=\"]\";if(\"collapsed\"===n?r+=\"[]\":\"full\"===n&&(r+=\"[\"+(t.label||t.identifier)+\"]\"),\"imageReference\"===t.type)return[{type:\"text\",value:\"![\"+t.alt+r}];const a=e.all(t),i=a[0];i&&\"text\"===i.type?i.value=\"[\"+i.value:a.unshift({type:\"text\",value:\"[\"});const s=a[a.length-1];return s&&\"text\"===s.type?s.value+=r:a.push({type:\"text\",value:r}),a}function sr(e){const t=e.spread;return null==t?e.children.length>1:t}function or(e){const t=String(e),n=/\\r?\\n|\\r/g;let r=n.exec(t),a=0;const i=[];for(;r;)i.push(lr(t.slice(a,r.index),a>0,!0),r[0]),a=r.index+r[0].length,r=n.exec(t);return i.push(lr(t.slice(a),a>0,!1)),i.join(\"\")}function lr(e,t,n){let r=0,a=e.length;if(t){let t=e.codePointAt(r);for(;9===t||32===t;)r++,t=e.codePointAt(r)}if(n){let t=e.codePointAt(a-1);for(;9===t||32===t;)a--,t=e.codePointAt(a-1)}return a>r?e.slice(r,a):\"\"}const cr={blockquote:function(e,t){const n={type:\"element\",tagName:\"blockquote\",properties:{},children:e.wrap(e.all(t),!0)};return e.patch(t,n),e.applyData(t,n)},break:function(e,t){const n={type:\"element\",tagName:\"br\",properties:{},children:[]};return e.patch(t,n),[e.applyData(t,n),{type:\"text\",value:\"\\n\"}]},code:function(e,t){const n=t.value?t.value+\"\\n\":\"\",r={},a=t.lang?t.lang.split(/\\s+/):[];a.length>0&&(r.className=[\"language-\"+a[0]]);let i={type:\"element\",tagName:\"code\",properties:r,children:[{type:\"text\",value:n}]};return t.meta&&(i.data={meta:t.meta}),e.patch(t,i),i=e.applyData(t,i),i={type:\"element\",tagName:\"pre\",properties:{},children:[i]},e.patch(t,i),i},delete:function(e,t){const n={type:\"element\",tagName:\"del\",properties:{},children:e.all(t)};return e.patch(t,n),e.applyData(t,n)},emphasis:function(e,t){const n={type:\"element\",tagName:\"em\",properties:{},children:e.all(t)};return e.patch(t,n),e.applyData(t,n)},footnoteReference:function(e,t){const n=\"string\"==typeof e.options.clobberPrefix?e.options.clobberPrefix:\"user-content-\",r=String(t.identifier).toUpperCase(),a=Pt(r.toLowerCase()),i=e.footnoteOrder.indexOf(r);let s,o=e.footnoteCounts.get(r);void 0===o?(o=0,e.footnoteOrder.push(r),s=e.footnoteOrder.length):s=i+1,o+=1,e.footnoteCounts.set(r,o);const l={type:\"element\",tagName:\"a\",properties:{href:\"#\"+n+\"fn-\"+a,id:n+\"fnref-\"+a+(o>1?\"-\"+o:\"\"),dataFootnoteRef:!0,ariaDescribedBy:[\"footnote-label\"]},children:[{type:\"text\",value:String(s)}]};e.patch(t,l);const c={type:\"element\",tagName:\"sup\",properties:{},children:[l]};return e.patch(t,c),e.applyData(t,c)},heading:function(e,t){const n={type:\"element\",tagName:\"h\"+t.depth,properties:{},children:e.all(t)};return e.patch(t,n),e.applyData(t,n)},html:function(e,t){if(e.options.allowDangerousHtml){const n={type:\"raw\",value:t.value};return e.patch(t,n),e.applyData(t,n)}},imageReference:function(e,t){const n=String(t.identifier).toUpperCase(),r=e.definitionById.get(n);if(!r)return ir(e,t);const a={src:Pt(r.url||\"\"),alt:t.alt};null!==r.title&&void 0!==r.title&&(a.title=r.title);const i={type:\"element\",tagName:\"img\",properties:a,children:[]};return e.patch(t,i),e.applyData(t,i)},image:function(e,t){const n={src:Pt(t.url)};null!==t.alt&&void 0!==t.alt&&(n.alt=t.alt),null!==t.title&&void 0!==t.title&&(n.title=t.title);const r={type:\"element\",tagName:\"img\",properties:n,children:[]};return e.patch(t,r),e.applyData(t,r)},inlineCode:function(e,t){const n={type:\"text\",value:t.value.replace(/\\r?\\n|\\r/g,\" \")};e.patch(t,n);const r={type:\"element\",tagName:\"code\",properties:{},children:[n]};return e.patch(t,r),e.applyData(t,r)},linkReference:function(e,t){const n=String(t.identifier).toUpperCase(),r=e.definitionById.get(n);if(!r)return ir(e,t);const a={href:Pt(r.url||\"\")};null!==r.title&&void 0!==r.title&&(a.title=r.title);const i={type:\"element\",tagName:\"a\",properties:a,children:e.all(t)};return e.patch(t,i),e.applyData(t,i)},link:function(e,t){const n={href:Pt(t.url)};null!==t.title&&void 0!==t.title&&(n.title=t.title);const r={type:\"element\",tagName:\"a\",properties:n,children:e.all(t)};return e.patch(t,r),e.applyData(t,r)},listItem:function(e,t,n){const r=e.all(t),a=n?function(e){let t=!1;if(\"list\"===e.type){t=e.spread||!1;const n=e.children;let r=-1;for(;!t&&++r<n.length;)t=sr(n[r])}return t}(n):sr(t),i={},s=[];if(\"boolean\"==typeof t.checked){const e=r[0];let n;e&&\"element\"===e.type&&\"p\"===e.tagName?n=e:(n={type:\"element\",tagName:\"p\",properties:{},children:[]},r.unshift(n)),n.children.length>0&&n.children.unshift({type:\"text\",value:\" \"}),n.children.unshift({type:\"element\",tagName:\"input\",properties:{type:\"checkbox\",checked:t.checked,disabled:!0},children:[]}),i.className=[\"task-list-item\"]}let o=-1;for(;++o<r.length;){const e=r[o];(a||0!==o||\"element\"!==e.type||\"p\"!==e.tagName)&&s.push({type:\"text\",value:\"\\n\"}),\"element\"!==e.type||\"p\"!==e.tagName||a?s.push(e):s.push(...e.children)}const l=r[r.length-1];l&&(a||\"element\"!==l.type||\"p\"!==l.tagName)&&s.push({type:\"text\",value:\"\\n\"});const c={type:\"element\",tagName:\"li\",properties:i,children:s};return e.patch(t,c),e.applyData(t,c)},list:function(e,t){const n={},r=e.all(t);let a=-1;for(\"number\"==typeof t.start&&1!==t.start&&(n.start=t.start);++a<r.length;){const e=r[a];if(\"element\"===e.type&&\"li\"===e.tagName&&e.properties&&Array.isArray(e.properties.className)&&e.properties.className.includes(\"task-list-item\")){n.className=[\"contains-task-list\"];break}}const i={type:\"element\",tagName:t.ordered?\"ol\":\"ul\",properties:n,children:e.wrap(r,!0)};return e.patch(t,i),e.applyData(t,i)},paragraph:function(e,t){const n={type:\"element\",tagName:\"p\",properties:{},children:e.all(t)};return e.patch(t,n),e.applyData(t,n)},root:function(e,t){const n={type:\"root\",children:e.wrap(e.all(t))};return e.patch(t,n),e.applyData(t,n)},strong:function(e,t){const n={type:\"element\",tagName:\"strong\",properties:{},children:e.all(t)};return e.patch(t,n),e.applyData(t,n)},table:function(e,t){const n=e.all(t),r=n.shift(),a=[];if(r){const n={type:\"element\",tagName:\"thead\",properties:{},children:e.wrap([r],!0)};e.patch(t.children[0],n),a.push(n)}if(n.length>0){const r={type:\"element\",tagName:\"tbody\",properties:{},children:e.wrap(n,!0)},i=Fe(t.children[1]),s=Le(t.children[t.children.length-1]);i&&s&&(r.position={start:i,end:s}),a.push(r)}const i={type:\"element\",tagName:\"table\",properties:{},children:e.wrap(a,!0)};return e.patch(t,i),e.applyData(t,i)},tableCell:function(e,t){const n={type:\"element\",tagName:\"td\",properties:{},children:e.all(t)};return e.patch(t,n),e.applyData(t,n)},tableRow:function(e,t,n){const r=n?n.children:void 0,a=0===(r?r.indexOf(t):1)?\"th\":\"td\",i=n&&\"table\"===n.type?n.align:void 0,s=i?i.length:t.children.length;let o=-1;const l=[];for(;++o<s;){const n=t.children[o],r={},s=i?i[o]:void 0;s&&(r.align=s);let c={type:\"element\",tagName:a,properties:r,children:[]};n&&(c.children=e.all(n),e.patch(n,c),c=e.applyData(n,c)),l.push(c)}const c={type:\"element\",tagName:\"tr\",properties:{},children:e.wrap(l,!0)};return e.patch(t,c),e.applyData(t,c)},text:function(e,t){const n={type:\"text\",value:or(String(t.value))};return e.patch(t,n),e.applyData(t,n)},thematicBreak:function(e,t){const n={type:\"element\",tagName:\"hr\",properties:{},children:[]};return e.patch(t,n),e.applyData(t,n)},toml:ur,yaml:ur,definition:ur,footnoteDefinition:ur};function ur(){}const dr=\"object\"==typeof self?self:globalThis,pr=e=>((e,t)=>{const n=(t,n)=>(e.set(n,t),t),r=a=>{if(e.has(a))return e.get(a);const[i,s]=t[a];switch(i){case 0:case-1:return n(s,a);case 1:{const e=n([],a);for(const t of s)e.push(r(t));return e}case 2:{const e=n({},a);for(const[t,n]of s)e[r(t)]=r(n);return e}case 3:return n(new Date(s),a);case 4:{const{source:e,flags:t}=s;return n(new RegExp(e,t),a)}case 5:{const e=n(new Map,a);for(const[t,n]of s)e.set(r(t),r(n));return e}case 6:{const e=n(new Set,a);for(const t of s)e.add(r(t));return e}case 7:{const{name:e,message:t}=s;return n(new dr[e](t),a)}case 8:return n(BigInt(s),a);case\"BigInt\":return n(Object(BigInt(s)),a);case\"ArrayBuffer\":return n(new Uint8Array(s).buffer,s);case\"DataView\":{const{buffer:e}=new Uint8Array(s);return n(new DataView(e),s)}}return n(new dr[i](s),a)};return r})(new Map,e)(0),hr=\"\",{toString:fr}={},{keys:mr}=Object,gr=e=>{const t=typeof e;if(\"object\"!==t||!e)return[0,t];const n=fr.call(e).slice(8,-1);switch(n){case\"Array\":return[1,hr];case\"Object\":return[2,hr];case\"Date\":return[3,hr];case\"RegExp\":return[4,hr];case\"Map\":return[5,hr];case\"Set\":return[6,hr];case\"DataView\":return[1,n]}return n.includes(\"Array\")?[1,n]:n.includes(\"Error\")?[7,n]:[2,n]},br=([e,t])=>0===e&&(\"function\"===t||\"symbol\"===t),Er=(e,{json:t,lossy:n}={})=>{const r=[];return((e,t,n,r)=>{const a=(e,t)=>{const a=r.push(e)-1;return n.set(t,a),a},i=r=>{if(n.has(r))return n.get(r);let[s,o]=gr(r);switch(s){case 0:{let t=r;switch(o){case\"bigint\":s=8,t=r.toString();break;case\"function\":case\"symbol\":if(e)throw new TypeError(\"unable to serialize \"+o);t=null;break;case\"undefined\":return a([-1],r)}return a([s,t],r)}case 1:{if(o){let e=r;return\"DataView\"===o?e=new Uint8Array(r.buffer):\"ArrayBuffer\"===o&&(e=new Uint8Array(r)),a([o,[...e]],r)}const e=[],t=a([s,e],r);for(const n of r)e.push(i(n));return t}case 2:{if(o)switch(o){case\"BigInt\":return a([o,r.toString()],r);case\"Boolean\":case\"Number\":case\"String\":return a([o,r.valueOf()],r)}if(t&&\"toJSON\"in r)return i(r.toJSON());const n=[],l=a([s,n],r);for(const t of mr(r))!e&&br(gr(r[t]))||n.push([i(t),i(r[t])]);return l}case 3:return a([s,r.toISOString()],r);case 4:{const{source:e,flags:t}=r;return a([s,{source:e,flags:t}],r)}case 5:{const t=[],n=a([s,t],r);for(const[a,s]of r)(e||!br(gr(a))&&!br(gr(s)))&&t.push([i(a),i(s)]);return n}case 6:{const t=[],n=a([s,t],r);for(const a of r)!e&&br(gr(a))||t.push(i(a));return n}}const{message:l}=r;return a([s,{name:o,message:l}],r)};return i})(!(t||n),!!t,new Map,r)(e),r},yr=\"function\"==typeof structuredClone?(e,t)=>t&&(\"json\"in t||\"lossy\"in t)?pr(Er(e,t)):structuredClone(e):(e,t)=>pr(Er(e,t));function _r(e,t){const n=[{type:\"text\",value:\"↩\"}];return t>1&&n.push({type:\"element\",tagName:\"sup\",properties:{},children:[{type:\"text\",value:String(t)}]}),n}function kr(e,t){return\"Back to reference \"+(e+1)+(t>1?\"-\"+t:\"\")}const Tr=function(e){if(null==e)return vr;if(\"function\"==typeof e)return Sr(e);if(\"object\"==typeof e)return Array.isArray(e)?function(e){const t=[];let n=-1;for(;++n<e.length;)t[n]=Tr(e[n]);return Sr(r);function r(...e){let n=-1;for(;++n<t.length;)if(t[n].apply(this,e))return!0;return!1}}(e):function(e){const t=e;return Sr(n);function n(n){const r=n;let a;for(a in e)if(r[a]!==t[a])return!1;return!0}}(e);if(\"string\"==typeof e)return function(e){return Sr(t);function t(t){return t&&t.type===e}}(e);throw new Error(\"Expected function, string, or object as test\")};function Sr(e){return function(t,n,r){return Boolean(function(e){return null!==e&&\"object\"==typeof e&&\"type\"in e}(t)&&e.call(this,t,\"number\"==typeof n?n:void 0,r||void 0))}}function vr(){return!0}const Ar=[],Nr=!0,Cr=!1;function xr(e,t,n,r){let a;\"function\"==typeof t&&\"function\"!=typeof n?(r=n,n=t):a=t;const i=Tr(a),s=r?-1:1;!function e(a,o,l){const c=a&&\"object\"==typeof a?a:{};if(\"string\"==typeof c.type){const e=\"string\"==typeof c.tagName?c.tagName:\"string\"==typeof c.name?c.name:void 0;Object.defineProperty(u,\"name\",{value:\"node (\"+a.type+(e?\"<\"+e+\">\":\"\")+\")\"})}return u;function u(){let c,u,d,p=Ar;if((!t||i(a,o,l[l.length-1]||void 0))&&(p=function(e){if(Array.isArray(e))return e;if(\"number\"==typeof e)return[Nr,e];return null==e?Ar:[e]}(n(a,l)),p[0]===Cr))return p;if(\"children\"in a&&a.children){const t=a;if(t.children&&\"skip\"!==p[0])for(u=(r?t.children.length:-1)+s,d=l.concat(t);u>-1&&u<t.children.length;){const n=t.children[u];if(c=e(n,u,d)(),c[0]===Cr)return c;u=\"number\"==typeof c[1]?c[1]:u+s}}return p}}(e,void 0,[])()}function wr(e,t,n,r){let a,i,s;\"function\"==typeof t&&\"function\"!=typeof n?(i=void 0,s=t,a=n):(i=t,s=n,a=r),xr(e,i,function(e,t){const n=t[t.length-1],r=n?n.children.indexOf(e):void 0;return s(e,r,n)},a)}const Ir={}.hasOwnProperty,Or={};function Rr(e,t){e.position&&(t.position=Ue(e))}function Dr(e,t){let n=t;if(e&&e.data){const t=e.data.hName,r=e.data.hChildren,a=e.data.hProperties;if(\"string\"==typeof t)if(\"element\"===n.type)n.tagName=t;else{n={type:\"element\",tagName:t,properties:{},children:\"children\"in n?n.children:[n]}}\"element\"===n.type&&a&&Object.assign(n.properties,yr(a)),\"children\"in n&&n.children&&null!=r&&(n.children=r)}return n}function Mr(e,t){const n=t.data||{},r=!(\"value\"in t)||Ir.call(n,\"hProperties\")||Ir.call(n,\"hChildren\")?{type:\"element\",tagName:\"div\",properties:{},children:e.all(t)}:{type:\"text\",value:t.value};return e.patch(t,r),e.applyData(t,r)}function Pr(e,t){const n=[];let r=-1;for(t&&n.push({type:\"text\",value:\"\\n\"});++r<e.length;)r&&n.push({type:\"text\",value:\"\\n\"}),n.push(e[r]);return t&&e.length>0&&n.push({type:\"text\",value:\"\\n\"}),n}function Lr(e){let t=0,n=e.charCodeAt(t);for(;9===n||32===n;)t++,n=e.charCodeAt(t);return e.slice(t)}function Fr(e,t){const n=function(e,t){const n=t||Or,r=new Map,a=new Map,i=new Map,s={...cr,...n.handlers},o={all:function(e){const t=[];if(\"children\"in e){const n=e.children;let r=-1;for(;++r<n.length;){const a=o.one(n[r],e);if(a){if(r&&\"break\"===n[r-1].type&&(Array.isArray(a)||\"text\"!==a.type||(a.value=Lr(a.value)),!Array.isArray(a)&&\"element\"===a.type)){const e=a.children[0];e&&\"text\"===e.type&&(e.value=Lr(e.value))}Array.isArray(a)?t.push(...a):t.push(a)}}}return t},applyData:Dr,definitionById:r,footnoteById:a,footnoteCounts:i,footnoteOrder:[],handlers:s,one:function(e,t){const n=e.type,r=o.handlers[n];if(Ir.call(o.handlers,n)&&r)return r(o,e,t);if(o.options.passThrough&&o.options.passThrough.includes(n)){if(\"children\"in e){const{children:t,...n}=e,r=yr(n);return r.children=o.all(e),r}return yr(e)}return(o.options.unknownHandler||Mr)(o,e,t)},options:n,patch:Rr,wrap:Pr};return wr(e,function(e){if(\"definition\"===e.type||\"footnoteDefinition\"===e.type){const t=\"definition\"===e.type?r:a,n=String(e.identifier).toUpperCase();t.has(n)||t.set(n,e)}}),o}(e,t),r=n.one(e,void 0),a=function(e){const t=\"string\"==typeof e.options.clobberPrefix?e.options.clobberPrefix:\"user-content-\",n=e.options.footnoteBackContent||_r,r=e.options.footnoteBackLabel||kr,a=e.options.footnoteLabel||\"Footnotes\",i=e.options.footnoteLabelTagName||\"h2\",s=e.options.footnoteLabelProperties||{className:[\"sr-only\"]},o=[];let l=-1;for(;++l<e.footnoteOrder.length;){const a=e.footnoteById.get(e.footnoteOrder[l]);if(!a)continue;const i=e.all(a),s=String(a.identifier).toUpperCase(),c=Pt(s.toLowerCase());let u=0;const d=[],p=e.footnoteCounts.get(s);for(;void 0!==p&&++u<=p;){d.length>0&&d.push({type:\"text\",value:\" \"});let e=\"string\"==typeof n?n:n(l,u);\"string\"==typeof e&&(e={type:\"text\",value:e}),d.push({type:\"element\",tagName:\"a\",properties:{href:\"#\"+t+\"fnref-\"+c+(u>1?\"-\"+u:\"\"),dataFootnoteBackref:\"\",ariaLabel:\"string\"==typeof r?r:r(l,u),className:[\"data-footnote-backref\"]},children:Array.isArray(e)?e:[e]})}const h=i[i.length-1];if(h&&\"element\"===h.type&&\"p\"===h.tagName){const e=h.children[h.children.length-1];e&&\"text\"===e.type?e.value+=\" \":h.children.push({type:\"text\",value:\" \"}),h.children.push(...d)}else i.push(...d);const f={type:\"element\",tagName:\"li\",properties:{id:t+\"fn-\"+c},children:e.wrap(i,!0)};e.patch(a,f),o.push(f)}if(0!==o.length)return{type:\"element\",tagName:\"section\",properties:{dataFootnotes:!0,className:[\"footnotes\"]},children:[{type:\"element\",tagName:i,properties:{...yr(s),id:\"footnote-label\"},children:[{type:\"text\",value:a}]},{type:\"text\",value:\"\\n\"},{type:\"element\",tagName:\"ol\",properties:{},children:e.wrap(o,!0)},{type:\"text\",value:\"\\n\"}]}}(n),i=Array.isArray(r)?{type:\"root\",children:r}:r||{type:\"root\",children:[]};return a&&i.children.push({type:\"text\",value:\"\\n\"},a),i}function Br(e,t){return e&&\"run\"in e?async function(n,r){const a=Fr(n,{file:r,...t});await e.run(a,r)}:function(n,r){return Fr(n,{file:r,...e||t})}}function Ur(e){if(e)throw e}var Hr,zr;const Gr=r(function(){if(zr)return Hr;zr=1;var e=Object.prototype.hasOwnProperty,t=Object.prototype.toString,n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,a=function(e){return\"function\"==typeof Array.isArray?Array.isArray(e):\"[object Array]\"===t.call(e)},i=function(n){if(!n||\"[object Object]\"!==t.call(n))return!1;var r,a=e.call(n,\"constructor\"),i=n.constructor&&n.constructor.prototype&&e.call(n.constructor.prototype,\"isPrototypeOf\");if(n.constructor&&!a&&!i)return!1;for(r in n);return void 0===r||e.call(n,r)},s=function(e,t){n&&\"__proto__\"===t.name?n(e,t.name,{enumerable:!0,configurable:!0,value:t.newValue,writable:!0}):e[t.name]=t.newValue},o=function(t,n){if(\"__proto__\"===n){if(!e.call(t,n))return;if(r)return r(t,n).value}return t[n]};return Hr=function e(){var t,n,r,l,c,u,d=arguments[0],p=1,h=arguments.length,f=!1;for(\"boolean\"==typeof d&&(f=d,d=arguments[1]||{},p=2),(null==d||\"object\"!=typeof d&&\"function\"!=typeof d)&&(d={});p<h;++p)if(null!=(t=arguments[p]))for(n in t)r=o(d,n),d!==(l=o(t,n))&&(f&&l&&(i(l)||(c=a(l)))?(c?(c=!1,u=r&&a(r)?r:[]):u=r&&i(r)?r:{},s(d,{name:n,newValue:e(f,u,l)})):void 0!==l&&s(d,{name:n,newValue:l}));return d}}());function jr(e){if(\"object\"!=typeof e||null===e)return!1;const t=Object.getPrototypeOf(e);return!(null!==t&&t!==Object.prototype&&null!==Object.getPrototypeOf(t)||Symbol.toStringTag in e||Symbol.iterator in e)}function $r(){const e=[],t={run:function(...t){let n=-1;const r=t.pop();if(\"function\"!=typeof r)throw new TypeError(\"Expected function as last argument, not \"+r);!function a(i,...s){const o=e[++n];let l=-1;if(i)r(i);else{for(;++l<t.length;)null!==s[l]&&void 0!==s[l]||(s[l]=t[l]);t=s,o?function(e,t){let n;return r;function r(...t){const r=e.length>t.length;let o;r&&t.push(a);try{o=e.apply(this,t)}catch(i){if(r&&n)throw i;return a(i)}r||(o&&o.then&&\"function\"==typeof o.then?o.then(s,a):o instanceof Error?a(o):s(o))}function a(e,...r){n||(n=!0,t(e,...r))}function s(e){a(null,e)}}(o,a)(...s):r(null,...s)}}(null,...t)},use:function(n){if(\"function\"!=typeof n)throw new TypeError(\"Expected `middelware` to be a function, not \"+n);return e.push(n),t}};return t}const qr={basename:function(e,t){if(void 0!==t&&\"string\"!=typeof t)throw new TypeError('\"ext\" argument must be a string');Yr(e);let n,r=0,a=-1,i=e.length;if(void 0===t||0===t.length||t.length>e.length){for(;i--;)if(47===e.codePointAt(i)){if(n){r=i+1;break}}else a<0&&(n=!0,a=i+1);return a<0?\"\":e.slice(r,a)}if(t===e)return\"\";let s=-1,o=t.length-1;for(;i--;)if(47===e.codePointAt(i)){if(n){r=i+1;break}}else s<0&&(n=!0,s=i+1),o>-1&&(e.codePointAt(i)===t.codePointAt(o--)?o<0&&(a=i):(o=-1,a=s));r===a?a=s:a<0&&(a=e.length);return e.slice(r,a)},dirname:function(e){if(Yr(e),0===e.length)return\".\";let t,n=-1,r=e.length;for(;--r;)if(47===e.codePointAt(r)){if(t){n=r;break}}else t||(t=!0);return n<0?47===e.codePointAt(0)?\"/\":\".\":1===n&&47===e.codePointAt(0)?\"//\":e.slice(0,n)},extname:function(e){Yr(e);let t,n=e.length,r=-1,a=0,i=-1,s=0;for(;n--;){const o=e.codePointAt(n);if(47!==o)r<0&&(t=!0,r=n+1),46===o?i<0?i=n:1!==s&&(s=1):i>-1&&(s=-1);else if(t){a=n+1;break}}if(i<0||r<0||0===s||1===s&&i===r-1&&i===a+1)return\"\";return e.slice(i,r)},join:function(...e){let t,n=-1;for(;++n<e.length;)Yr(e[n]),e[n]&&(t=void 0===t?e[n]:t+\"/\"+e[n]);return void 0===t?\".\":function(e){Yr(e);const t=47===e.codePointAt(0);let n=function(e,t){let n,r,a=\"\",i=0,s=-1,o=0,l=-1;for(;++l<=e.length;){if(l<e.length)n=e.codePointAt(l);else{if(47===n)break;n=47}if(47===n){if(s===l-1||1===o);else if(s!==l-1&&2===o){if(a.length<2||2!==i||46!==a.codePointAt(a.length-1)||46!==a.codePointAt(a.length-2))if(a.length>2){if(r=a.lastIndexOf(\"/\"),r!==a.length-1){r<0?(a=\"\",i=0):(a=a.slice(0,r),i=a.length-1-a.lastIndexOf(\"/\")),s=l,o=0;continue}}else if(a.length>0){a=\"\",i=0,s=l,o=0;continue}t&&(a=a.length>0?a+\"/..\":\"..\",i=2)}else a.length>0?a+=\"/\"+e.slice(s+1,l):a=e.slice(s+1,l),i=l-s-1;s=l,o=0}else 46===n&&o>-1?o++:o=-1}return a}(e,!t);0!==n.length||t||(n=\".\");n.length>0&&47===e.codePointAt(e.length-1)&&(n+=\"/\");return t?\"/\"+n:n}(t)},sep:\"/\"};function Yr(e){if(\"string\"!=typeof e)throw new TypeError(\"Path must be a string. Received \"+JSON.stringify(e))}const Wr={cwd:function(){return\"/\"}};function Vr(e){return Boolean(null!==e&&\"object\"==typeof e&&\"href\"in e&&e.href&&\"protocol\"in e&&e.protocol&&void 0===e.auth)}function Kr(e){if(\"string\"==typeof e)e=new URL(e);else if(!Vr(e)){const t=new TypeError('The \"path\" argument must be of type string or an instance of URL. Received `'+e+\"`\");throw t.code=\"ERR_INVALID_ARG_TYPE\",t}if(\"file:\"!==e.protocol){const e=new TypeError(\"The URL must be of scheme file\");throw e.code=\"ERR_INVALID_URL_SCHEME\",e}return function(e){if(\"\"!==e.hostname){const e=new TypeError('File URL host must be \"localhost\" or empty on darwin');throw e.code=\"ERR_INVALID_FILE_URL_HOST\",e}const t=e.pathname;let n=-1;for(;++n<t.length;)if(37===t.codePointAt(n)&&50===t.codePointAt(n+1)){const e=t.codePointAt(n+2);if(70===e||102===e){const e=new TypeError(\"File URL path must not include encoded / characters\");throw e.code=\"ERR_INVALID_FILE_URL_PATH\",e}}return decodeURIComponent(t)}(e)}const Qr=[\"history\",\"path\",\"basename\",\"stem\",\"extname\",\"dirname\"];class Xr{constructor(e){let t;t=e?Vr(e)?{path:e}:\"string\"==typeof e||function(e){return Boolean(e&&\"object\"==typeof e&&\"byteLength\"in e&&\"byteOffset\"in e)}(e)?{value:e}:e:{},this.cwd=\"cwd\"in t?\"\":Wr.cwd(),this.data={},this.history=[],this.messages=[],this.value,this.map,this.result,this.stored;let n,r=-1;for(;++r<Qr.length;){const e=Qr[r];e in t&&void 0!==t[e]&&null!==t[e]&&(this[e]=\"history\"===e?[...t[e]]:t[e])}for(n in t)Qr.includes(n)||(this[n]=t[n])}get basename(){return\"string\"==typeof this.path?qr.basename(this.path):void 0}set basename(e){Jr(e,\"basename\"),Zr(e,\"basename\"),this.path=qr.join(this.dirname||\"\",e)}get dirname(){return\"string\"==typeof this.path?qr.dirname(this.path):void 0}set dirname(e){ea(this.basename,\"dirname\"),this.path=qr.join(e||\"\",this.basename)}get extname(){return\"string\"==typeof this.path?qr.extname(this.path):void 0}set extname(e){if(Zr(e,\"extname\"),ea(this.dirname,\"extname\"),e){if(46!==e.codePointAt(0))throw new Error(\"`extname` must start with `.`\");if(e.includes(\".\",1))throw new Error(\"`extname` cannot contain multiple dots\")}this.path=qr.join(this.dirname,this.stem+(e||\"\"))}get path(){return this.history[this.history.length-1]}set path(e){Vr(e)&&(e=Kr(e)),Jr(e,\"path\"),this.path!==e&&this.history.push(e)}get stem(){return\"string\"==typeof this.path?qr.basename(this.path,this.extname):void 0}set stem(e){Jr(e,\"stem\"),Zr(e,\"stem\"),this.path=qr.join(this.dirname||\"\",e+(this.extname||\"\"))}fail(e,t,n){const r=this.message(e,t,n);throw r.fatal=!0,r}info(e,t,n){const r=this.message(e,t,n);return r.fatal=void 0,r}message(e,t,n){const r=new $e(e,t,n);return this.path&&(r.name=this.path+\":\"+r.name,r.file=this.path),r.fatal=!1,this.messages.push(r),r}toString(e){if(void 0===this.value)return\"\";if(\"string\"==typeof this.value)return this.value;return new TextDecoder(e||void 0).decode(this.value)}}function Zr(e,t){if(e&&e.includes(qr.sep))throw new Error(\"`\"+t+\"` cannot be a path: did not expect `\"+qr.sep+\"`\")}function Jr(e,t){if(!e)throw new Error(\"`\"+t+\"` cannot be empty\")}function ea(e,t){if(!e)throw new Error(\"Setting `\"+t+\"` requires `path` to be set too\")}const ta=function(e){const t=this.constructor.prototype,n=t[e],r=function(){return n.apply(r,arguments)};return Object.setPrototypeOf(r,t),r},na={}.hasOwnProperty;class ra extends ta{constructor(){super(\"copy\"),this.Compiler=void 0,this.Parser=void 0,this.attachers=[],this.compiler=void 0,this.freezeIndex=-1,this.frozen=void 0,this.namespace={},this.parser=void 0,this.transformers=$r()}copy(){const e=new ra;let t=-1;for(;++t<this.attachers.length;){const n=this.attachers[t];e.use(...n)}return e.data(Gr(!0,{},this.namespace)),e}data(e,t){return\"string\"==typeof e?2===arguments.length?(oa(\"data\",this.frozen),this.namespace[e]=t,this):na.call(this.namespace,e)&&this.namespace[e]||void 0:e?(oa(\"data\",this.frozen),this.namespace=e,this):this.namespace}freeze(){if(this.frozen)return this;const e=this;for(;++this.freezeIndex<this.attachers.length;){const[t,...n]=this.attachers[this.freezeIndex];if(!1===n[0])continue;!0===n[0]&&(n[0]=void 0);const r=t.call(e,...n);\"function\"==typeof r&&this.transformers.use(r)}return this.frozen=!0,this.freezeIndex=Number.POSITIVE_INFINITY,this}parse(e){this.freeze();const t=ua(e),n=this.parser||this.Parser;return ia(\"parse\",n),n(String(t),t)}process(e,t){const n=this;return this.freeze(),ia(\"process\",this.parser||this.Parser),sa(\"process\",this.compiler||this.Compiler),t?r(void 0,t):new Promise(r);function r(r,a){const i=ua(e),s=n.parse(i);function o(e,n){e||!n?a(e):r?r(n):t(void 0,n)}n.run(s,i,function(e,t,r){if(e||!t||!r)return o(e);const a=t,i=n.stringify(a,r);var s;\"string\"==typeof(s=i)||function(e){return Boolean(e&&\"object\"==typeof e&&\"byteLength\"in e&&\"byteOffset\"in e)}(s)?r.value=i:r.result=i,o(e,r)})}}processSync(e){let t,n=!1;return this.freeze(),ia(\"processSync\",this.parser||this.Parser),sa(\"processSync\",this.compiler||this.Compiler),this.process(e,function(e,r){n=!0,Ur(e),t=r}),ca(\"processSync\",\"process\",n),t}run(e,t,n){la(e),this.freeze();const r=this.transformers;return n||\"function\"!=typeof t||(n=t,t=void 0),n?a(void 0,n):new Promise(a);function a(a,i){const s=ua(t);r.run(e,s,function(t,r,s){const o=r||e;t?i(t):a?a(o):n(void 0,o,s)})}}runSync(e,t){let n,r=!1;return this.run(e,t,function(e,t){Ur(e),n=t,r=!0}),ca(\"runSync\",\"run\",r),n}stringify(e,t){this.freeze();const n=ua(t),r=this.compiler||this.Compiler;return sa(\"stringify\",r),la(e),r(e,n)}use(e,...t){const n=this.attachers,r=this.namespace;if(oa(\"use\",this.frozen),null==e);else if(\"function\"==typeof e)o(e,t);else{if(\"object\"!=typeof e)throw new TypeError(\"Expected usable value, not `\"+e+\"`\");Array.isArray(e)?s(e):i(e)}return this;function a(e){if(\"function\"==typeof e)o(e,[]);else{if(\"object\"!=typeof e)throw new TypeError(\"Expected usable value, not `\"+e+\"`\");if(Array.isArray(e)){const[t,...n]=e;o(t,n)}else i(e)}}function i(e){if(!(\"plugins\"in e)&&!(\"settings\"in e))throw new Error(\"Expected usable value but received an empty preset, which is probably a mistake: presets typically come with `plugins` and sometimes with `settings`, but this has neither\");s(e.plugins),e.settings&&(r.settings=Gr(!0,r.settings,e.settings))}function s(e){let t=-1;if(null==e);else{if(!Array.isArray(e))throw new TypeError(\"Expected a list of plugins, not `\"+e+\"`\");for(;++t<e.length;){a(e[t])}}}function o(e,t){let r=-1,a=-1;for(;++r<n.length;)if(n[r][0]===e){a=r;break}if(-1===a)n.push([e,...t]);else if(t.length>0){let[r,...i]=t;const s=n[a][1];jr(s)&&jr(r)&&(r=Gr(!0,s,r)),n[a]=[e,r,...i]}}}}const aa=(new ra).freeze();function ia(e,t){if(\"function\"!=typeof t)throw new TypeError(\"Cannot `\"+e+\"` without `parser`\")}function sa(e,t){if(\"function\"!=typeof t)throw new TypeError(\"Cannot `\"+e+\"` without `compiler`\")}function oa(e,t){if(t)throw new Error(\"Cannot call `\"+e+\"` on a frozen processor.\\nCreate a new processor first, by calling it: use `processor()` instead of `processor`.\")}function la(e){if(!jr(e)||\"string\"!=typeof e.type)throw new TypeError(\"Expected node, got `\"+e+\"`\")}function ca(e,t,n){if(!n)throw new Error(\"`\"+e+\"` finished async. Use `\"+t+\"` instead\")}function ua(e){return function(e){return Boolean(e&&\"object\"==typeof e&&\"message\"in e&&\"messages\"in e)}(e)?e:new Xr(e)}const da=[],pa={allowDangerousHtml:!0},ha=/^(https?|ircs?|mailto|xmpp)$/i,fa=[{from:\"astPlugins\",id:\"remove-buggy-html-in-markdown-parser\"},{from:\"allowDangerousHtml\",id:\"remove-buggy-html-in-markdown-parser\"},{from:\"allowNode\",id:\"replace-allownode-allowedtypes-and-disallowedtypes\",to:\"allowElement\"},{from:\"allowedTypes\",id:\"replace-allownode-allowedtypes-and-disallowedtypes\",to:\"allowedElements\"},{from:\"className\",id:\"remove-classname\"},{from:\"disallowedTypes\",id:\"replace-allownode-allowedtypes-and-disallowedtypes\",to:\"disallowedElements\"},{from:\"escapeHtml\",id:\"remove-buggy-html-in-markdown-parser\"},{from:\"includeElementIndex\",id:\"#remove-includeelementindex\"},{from:\"includeNodeIndex\",id:\"change-includenodeindex-to-includeelementindex\"},{from:\"linkTarget\",id:\"remove-linktarget\"},{from:\"plugins\",id:\"change-plugins-to-remarkplugins\",to:\"remarkPlugins\"},{from:\"rawSourcePos\",id:\"#remove-rawsourcepos\"},{from:\"renderers\",id:\"change-renderers-to-components\",to:\"components\"},{from:\"source\",id:\"change-source-to-children\",to:\"children\"},{from:\"sourcePos\",id:\"#remove-sourcepos\"},{from:\"transformImageUri\",id:\"#add-urltransform\",to:\"urlTransform\"},{from:\"transformLinkUri\",id:\"#add-urltransform\",to:\"urlTransform\"}];function ma(e){const t=function(e){const t=e.rehypePlugins||da,n=e.remarkPlugins||da,r=e.remarkRehypeOptions?{...e.remarkRehypeOptions,...pa}:pa,a=aa().use(ar).use(n).use(Br,r).use(t);return a}(e),n=function(e){const t=e.children||\"\",n=new Xr;\"string\"==typeof t&&(n.value=t);return n}(e);return function(e,t){const n=t.allowedElements,r=t.allowElement,i=t.components,s=t.disallowedElements,o=t.skipHtml,l=t.unwrapDisallowed,c=t.urlTransform||ga;for(const a of fa)Object.hasOwn(t,a.from)&&D((a.from,a.to&&a.to,a.id));return wr(e,u),Xe(e,{Fragment:a.Fragment,components:i,ignoreInvalidStyle:!0,jsx:a.jsx,jsxs:a.jsxs,passKeys:!0,passNode:!0});function u(e,t,a){if(\"raw\"===e.type&&a&&\"number\"==typeof t)return o?a.children.splice(t,1):a.children[t]={type:\"text\",value:e.value},t;if(\"element\"===e.type){let t;for(t in ot)if(Object.hasOwn(ot,t)&&Object.hasOwn(e.properties,t)){const n=e.properties[t],r=ot[t];(null===r||r.includes(e.tagName))&&(e.properties[t]=c(String(n||\"\"),t,e))}}if(\"element\"===e.type){let i=n?!n.includes(e.tagName):!!s&&s.includes(e.tagName);if(!i&&r&&\"number\"==typeof t&&(i=!r(e,t,a)),i&&a&&\"number\"==typeof t)return l&&e.children?a.children.splice(t,1,...e.children):a.children.splice(t,1),t}}}(t.runSync(t.parse(n),n),e)}function ga(e){const t=e.indexOf(\":\"),n=e.indexOf(\"?\"),r=e.indexOf(\"#\"),a=e.indexOf(\"/\");return-1===t||-1!==a&&t>a||-1!==n&&t>n||-1!==r&&t>r||ha.test(e.slice(0,t))?e:\"\"}var ba,Ea={},ya={};function _a(){if(ba)return ya;ba=1;var e=i(),t=s();function n(e){var t=\"https://react.dev/errors/\"+e;if(1<arguments.length){t+=\"?args[]=\"+encodeURIComponent(arguments[1]);for(var n=2;n<arguments.length;n++)t+=\"&args[]=\"+encodeURIComponent(arguments[n])}return\"Minified React error #\"+e+\"; visit \"+t+\" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\"}var r=Symbol.for(\"react.transitional.element\"),a=Symbol.for(\"react.portal\"),o=Symbol.for(\"react.fragment\"),l=Symbol.for(\"react.strict_mode\"),c=Symbol.for(\"react.profiler\"),u=Symbol.for(\"react.consumer\"),d=Symbol.for(\"react.context\"),p=Symbol.for(\"react.forward_ref\"),h=Symbol.for(\"react.suspense\"),f=Symbol.for(\"react.suspense_list\"),m=Symbol.for(\"react.memo\"),g=Symbol.for(\"react.lazy\"),b=Symbol.for(\"react.scope\"),E=Symbol.for(\"react.activity\"),y=Symbol.for(\"react.legacy_hidden\"),_=Symbol.for(\"react.memo_cache_sentinel\"),k=Symbol.for(\"react.view_transition\"),T=Symbol.iterator;function S(e){return null===e||\"object\"!=typeof e?null:\"function\"==typeof(e=T&&e[T]||e[\"@@iterator\"])?e:null}var v=Array.isArray;function A(e,t){var n=3&e.length,r=e.length-n,a=t;for(t=0;t<r;){var i=255&e.charCodeAt(t)|(255&e.charCodeAt(++t))<<8|(255&e.charCodeAt(++t))<<16|(255&e.charCodeAt(++t))<<24;++t,a=27492+(65535&(a=5*(65535&(a=(a^=i=461845907*(65535&(i=(i=3432918353*(65535&i)+((3432918353*(i>>>16)&65535)<<16)&4294967295)<<15|i>>>17))+((461845907*(i>>>16)&65535)<<16)&4294967295)<<13|a>>>19))+((5*(a>>>16)&65535)<<16)&4294967295))+(((a>>>16)+58964&65535)<<16)}switch(i=0,n){case 3:i^=(255&e.charCodeAt(t+2))<<16;case 2:i^=(255&e.charCodeAt(t+1))<<8;case 1:a^=461845907*(65535&(i=(i=3432918353*(65535&(i^=255&e.charCodeAt(t)))+((3432918353*(i>>>16)&65535)<<16)&4294967295)<<15|i>>>17))+((461845907*(i>>>16)&65535)<<16)&4294967295}return a^=e.length,a=2246822507*(65535&(a^=a>>>16))+((2246822507*(a>>>16)&65535)<<16)&4294967295,((a=3266489909*(65535&(a^=a>>>13))+((3266489909*(a>>>16)&65535)<<16)&4294967295)^a>>>16)>>>0}var N=Object.assign,C=Object.prototype.hasOwnProperty,x=RegExp(\"^[:A-Z_a-z\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD][:A-Z_a-z\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD\\\\-.0-9\\\\u00B7\\\\u0300-\\\\u036F\\\\u203F-\\\\u2040]*$\"),w={},I={};function O(e){return!!C.call(I,e)||!C.call(w,e)&&(x.test(e)?I[e]=!0:(w[e]=!0,!1))}var R=new Set(\"animationIterationCount aspectRatio borderImageOutset borderImageSlice borderImageWidth boxFlex boxFlexGroup boxOrdinalGroup columnCount columns flex flexGrow flexPositive flexShrink flexNegative flexOrder gridArea gridRow gridRowEnd gridRowSpan gridRowStart gridColumn gridColumnEnd gridColumnSpan gridColumnStart fontWeight lineClamp lineHeight opacity order orphans scale tabSize widows zIndex zoom fillOpacity floodOpacity stopOpacity strokeDasharray strokeDashoffset strokeMiterlimit strokeOpacity strokeWidth MozAnimationIterationCount MozBoxFlex MozBoxFlexGroup MozLineClamp msAnimationIterationCount msFlex msZoom msFlexGrow msFlexNegative msFlexOrder msFlexPositive msFlexShrink msGridColumn msGridColumnSpan msGridRow msGridRowSpan WebkitAnimationIterationCount WebkitBoxFlex WebKitBoxFlexGroup WebkitBoxOrdinalGroup WebkitColumnCount WebkitColumns WebkitFlex WebkitFlexGrow WebkitFlexPositive WebkitFlexShrink WebkitLineClamp\".split(\" \")),D=new Map([[\"acceptCharset\",\"accept-charset\"],[\"htmlFor\",\"for\"],[\"httpEquiv\",\"http-equiv\"],[\"crossOrigin\",\"crossorigin\"],[\"accentHeight\",\"accent-height\"],[\"alignmentBaseline\",\"alignment-baseline\"],[\"arabicForm\",\"arabic-form\"],[\"baselineShift\",\"baseline-shift\"],[\"capHeight\",\"cap-height\"],[\"clipPath\",\"clip-path\"],[\"clipRule\",\"clip-rule\"],[\"colorInterpolation\",\"color-interpolation\"],[\"colorInterpolationFilters\",\"color-interpolation-filters\"],[\"colorProfile\",\"color-profile\"],[\"colorRendering\",\"color-rendering\"],[\"dominantBaseline\",\"dominant-baseline\"],[\"enableBackground\",\"enable-background\"],[\"fillOpacity\",\"fill-opacity\"],[\"fillRule\",\"fill-rule\"],[\"floodColor\",\"flood-color\"],[\"floodOpacity\",\"flood-opacity\"],[\"fontFamily\",\"font-family\"],[\"fontSize\",\"font-size\"],[\"fontSizeAdjust\",\"font-size-adjust\"],[\"fontStretch\",\"font-stretch\"],[\"fontStyle\",\"font-style\"],[\"fontVariant\",\"font-variant\"],[\"fontWeight\",\"font-weight\"],[\"glyphName\",\"glyph-name\"],[\"glyphOrientationHorizontal\",\"glyph-orientation-horizontal\"],[\"glyphOrientationVertical\",\"glyph-orientation-vertical\"],[\"horizAdvX\",\"horiz-adv-x\"],[\"horizOriginX\",\"horiz-origin-x\"],[\"imageRendering\",\"image-rendering\"],[\"letterSpacing\",\"letter-spacing\"],[\"lightingColor\",\"lighting-color\"],[\"markerEnd\",\"marker-end\"],[\"markerMid\",\"marker-mid\"],[\"markerStart\",\"marker-start\"],[\"overlinePosition\",\"overline-position\"],[\"overlineThickness\",\"overline-thickness\"],[\"paintOrder\",\"paint-order\"],[\"panose-1\",\"panose-1\"],[\"pointerEvents\",\"pointer-events\"],[\"renderingIntent\",\"rendering-intent\"],[\"shapeRendering\",\"shape-rendering\"],[\"stopColor\",\"stop-color\"],[\"stopOpacity\",\"stop-opacity\"],[\"strikethroughPosition\",\"strikethrough-position\"],[\"strikethroughThickness\",\"strikethrough-thickness\"],[\"strokeDasharray\",\"stroke-dasharray\"],[\"strokeDashoffset\",\"stroke-dashoffset\"],[\"strokeLinecap\",\"stroke-linecap\"],[\"strokeLinejoin\",\"stroke-linejoin\"],[\"strokeMiterlimit\",\"stroke-miterlimit\"],[\"strokeOpacity\",\"stroke-opacity\"],[\"strokeWidth\",\"stroke-width\"],[\"textAnchor\",\"text-anchor\"],[\"textDecoration\",\"text-decoration\"],[\"textRendering\",\"text-rendering\"],[\"transformOrigin\",\"transform-origin\"],[\"underlinePosition\",\"underline-position\"],[\"underlineThickness\",\"underline-thickness\"],[\"unicodeBidi\",\"unicode-bidi\"],[\"unicodeRange\",\"unicode-range\"],[\"unitsPerEm\",\"units-per-em\"],[\"vAlphabetic\",\"v-alphabetic\"],[\"vHanging\",\"v-hanging\"],[\"vIdeographic\",\"v-ideographic\"],[\"vMathematical\",\"v-mathematical\"],[\"vectorEffect\",\"vector-effect\"],[\"vertAdvY\",\"vert-adv-y\"],[\"vertOriginX\",\"vert-origin-x\"],[\"vertOriginY\",\"vert-origin-y\"],[\"wordSpacing\",\"word-spacing\"],[\"writingMode\",\"writing-mode\"],[\"xmlnsXlink\",\"xmlns:xlink\"],[\"xHeight\",\"x-height\"]]),M=/[\"'&<>]/;function P(e){if(\"boolean\"==typeof e||\"number\"==typeof e||\"bigint\"==typeof e)return\"\"+e;e=\"\"+e;var t=M.exec(e);if(t){var n,r=\"\",a=0;for(n=t.index;n<e.length;n++){switch(e.charCodeAt(n)){case 34:t=\"&quot;\";break;case 38:t=\"&amp;\";break;case 39:t=\"&#x27;\";break;case 60:t=\"&lt;\";break;case 62:t=\"&gt;\";break;default:continue}a!==n&&(r+=e.slice(a,n)),a=n+1,r+=t}e=a!==n?r+e.slice(a,n):r}return e}var L=/([A-Z])/g,F=/^ms-/,B=/^[\\u0000-\\u001F ]*j[\\r\\n\\t]*a[\\r\\n\\t]*v[\\r\\n\\t]*a[\\r\\n\\t]*s[\\r\\n\\t]*c[\\r\\n\\t]*r[\\r\\n\\t]*i[\\r\\n\\t]*p[\\r\\n\\t]*t[\\r\\n\\t]*:/i;function U(e){return B.test(\"\"+e)?\"javascript:throw new Error('React has blocked a javascript: URL as a security precaution.')\":e}var H=e.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,z=t.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,G={pending:!1,data:null,method:null,action:null},j=z.d;z.d={f:j.f,r:j.r,D:function(e){var t=fn||null;if(t){var n=t.resumableState,r=t.renderState;if(\"string\"==typeof e&&e){var a,i;if(!n.dnsResources.hasOwnProperty(e))n.dnsResources[e]=null,(i=(n=r.headers)&&0<n.remainingCapacity)&&(a=\"<\"+(\"\"+e).replace(Ve,Ke)+\">; rel=dns-prefetch\",i=0<=(n.remainingCapacity-=a.length+2)),i?(r.resets.dns[e]=null,n.preconnects&&(n.preconnects+=\", \"),n.preconnects+=a):(de(a=[],{href:e,rel:\"dns-prefetch\"}),r.preconnects.add(a));cr(t)}}else j.D(e)},C:function(e,t){var n=fn||null;if(n){var r=n.resumableState,a=n.renderState;if(\"string\"==typeof e&&e){var i=\"use-credentials\"===t?\"credentials\":\"string\"==typeof t?\"anonymous\":\"default\";if(!r.connectResources[i].hasOwnProperty(e)){var s,o;if(r.connectResources[i][e]=null,o=(r=a.headers)&&0<r.remainingCapacity){if(o=\"<\"+(\"\"+e).replace(Ve,Ke)+\">; rel=preconnect\",\"string\"==typeof t)o+='; crossorigin=\"'+(\"\"+t).replace(Qe,Xe)+'\"';s=o,o=0<=(r.remainingCapacity-=s.length+2)}o?(a.resets.connect[i][e]=null,r.preconnects&&(r.preconnects+=\", \"),r.preconnects+=s):(de(i=[],{rel:\"preconnect\",href:e,crossOrigin:t}),a.preconnects.add(i))}cr(n)}}else j.C(e,t)},L:function(e,t,n){var r=fn||null;if(r){var a=r.resumableState,i=r.renderState;if(t&&e){switch(t){case\"image\":if(n)var s=n.imageSrcSet,o=n.imageSizes,l=n.fetchPriority;var c,u=s?s+\"\\n\"+(o||\"\"):e;if(a.imageResources.hasOwnProperty(u))return;a.imageResources[u]=$,(a=i.headers)&&0<a.remainingCapacity&&\"string\"!=typeof s&&\"high\"===l&&(c=We(e,t,n),0<=(a.remainingCapacity-=c.length+2))?(i.resets.image[u]=$,a.highImagePreloads&&(a.highImagePreloads+=\", \"),a.highImagePreloads+=c):(de(a=[],N({rel:\"preload\",href:s?void 0:e,as:t},n)),\"high\"===l?i.highImagePreloads.add(a):(i.bulkPreloads.add(a),i.preloads.images.set(u,a)));break;case\"style\":if(a.styleResources.hasOwnProperty(e))return;de(s=[],N({rel:\"preload\",href:e,as:t},n)),a.styleResources[e]=!n||\"string\"!=typeof n.crossOrigin&&\"string\"!=typeof n.integrity?$:[n.crossOrigin,n.integrity],i.preloads.stylesheets.set(e,s),i.bulkPreloads.add(s);break;case\"script\":if(a.scriptResources.hasOwnProperty(e))return;s=[],i.preloads.scripts.set(e,s),i.bulkPreloads.add(s),de(s,N({rel:\"preload\",href:e,as:t},n)),a.scriptResources[e]=!n||\"string\"!=typeof n.crossOrigin&&\"string\"!=typeof n.integrity?$:[n.crossOrigin,n.integrity];break;default:if(a.unknownResources.hasOwnProperty(t)){if((s=a.unknownResources[t]).hasOwnProperty(e))return}else s={},a.unknownResources[t]=s;if(s[e]=$,(a=i.headers)&&0<a.remainingCapacity&&\"font\"===t&&(u=We(e,t,n),0<=(a.remainingCapacity-=u.length+2)))i.resets.font[e]=$,a.fontPreloads&&(a.fontPreloads+=\", \"),a.fontPreloads+=u;else if(\"font\"===(de(a=[],e=N({rel:\"preload\",href:e,as:t},n)),t))i.fontPreloads.add(a);else i.bulkPreloads.add(a)}cr(r)}}else j.L(e,t,n)},m:function(e,t){var n=fn||null;if(n){var r=n.resumableState,a=n.renderState;if(e){var i=t&&\"string\"==typeof t.as?t.as:\"script\";if(\"script\"===i){if(r.moduleScriptResources.hasOwnProperty(e))return;i=[],r.moduleScriptResources[e]=!t||\"string\"!=typeof t.crossOrigin&&\"string\"!=typeof t.integrity?$:[t.crossOrigin,t.integrity],a.preloads.moduleScripts.set(e,i)}else{if(r.moduleUnknownResources.hasOwnProperty(i)){var s=r.unknownResources[i];if(s.hasOwnProperty(e))return}else s={},r.moduleUnknownResources[i]=s;i=[],s[e]=$}de(i,N({rel:\"modulepreload\",href:e},t)),a.bulkPreloads.add(i),cr(n)}}else j.m(e,t)},X:function(e,t){var n=fn||null;if(n){var r=n.resumableState,a=n.renderState;if(e){var i=r.scriptResources.hasOwnProperty(e)?r.scriptResources[e]:void 0;null!==i&&(r.scriptResources[e]=null,t=N({src:e,async:!0},t),i&&(2===i.length&&Ye(t,i),e=a.preloads.scripts.get(e))&&(e.length=0),e=[],a.scripts.add(e),ge(e,t),cr(n))}}else j.X(e,t)},S:function(e,t,n){var r=fn||null;if(r){var a=r.resumableState,i=r.renderState;if(e){t=t||\"default\";var s=i.styles.get(t),o=a.styleResources.hasOwnProperty(e)?a.styleResources[e]:void 0;null!==o&&(a.styleResources[e]=null,s||(s={precedence:P(t),rules:[],hrefs:[],sheets:new Map},i.styles.set(t,s)),t={state:0,props:N({rel:\"stylesheet\",href:e,\"data-precedence\":t},n)},o&&(2===o.length&&Ye(t.props,o),(i=i.preloads.stylesheets.get(e))&&0<i.length?i.length=0:t.state=1),s.sheets.set(e,t),cr(r))}}else j.S(e,t,n)},M:function(e,t){var n=fn||null;if(n){var r=n.resumableState,a=n.renderState;if(e){var i=r.moduleScriptResources.hasOwnProperty(e)?r.moduleScriptResources[e]:void 0;null!==i&&(r.moduleScriptResources[e]=null,t=N({src:e,type:\"module\",async:!0},t),i&&(2===i.length&&Ye(t,i),e=a.preloads.moduleScripts.get(e))&&(e.length=0),e=[],a.scripts.add(e),ge(e,t),cr(n))}}else j.M(e,t)}};var $=[],q=null,Y=/(<\\/|<)(s)(cript)/gi;function W(e,t,n,r){return t+(\"s\"===n?\"\\\\u0073\":\"\\\\u0053\")+r}function V(e,t,n,r){return{insertionMode:e,selectedValue:t,tagScope:n,viewTransition:r}}function K(e,t,n){var r=-25&e.tagScope;switch(t){case\"noscript\":return V(2,null,1|r,null);case\"select\":return V(2,null!=n.value?n.value:n.defaultValue,r,null);case\"svg\":return V(4,null,r,null);case\"picture\":return V(2,null,2|r,null);case\"math\":return V(5,null,r,null);case\"foreignObject\":return V(2,null,r,null);case\"table\":return V(6,null,r,null);case\"thead\":case\"tbody\":case\"tfoot\":return V(7,null,r,null);case\"colgroup\":return V(9,null,r,null);case\"tr\":return V(8,null,r,null);case\"head\":if(2>e.insertionMode)return V(3,null,r,null);break;case\"html\":if(0===e.insertionMode)return V(1,null,r,null)}return 6<=e.insertionMode||2>e.insertionMode?V(2,null,r,null):e.tagScope!==r?V(e.insertionMode,e.selectedValue,r,null):e}function Q(e){return null===e?null:{update:e.update,enter:\"none\",exit:\"none\",share:e.update,name:e.autoName,autoName:e.autoName,nameIdx:0}}function X(e,t){return 32&t.tagScope&&(e.instructions|=128),V(t.insertionMode,t.selectedValue,12|t.tagScope,Q(t.viewTransition))}function Z(e,t){e=Q(t.viewTransition);var n=16|t.tagScope;return null!==e&&\"none\"!==e.share&&(n|=64),V(t.insertionMode,t.selectedValue,n,e)}var J=new Map;function ee(e,t){if(\"object\"!=typeof t)throw Error(n(62));var r,a=!0;for(r in t)if(C.call(t,r)){var i=t[r];if(null!=i&&\"boolean\"!=typeof i&&\"\"!==i){if(0===r.indexOf(\"--\")){var s=P(r);i=P((\"\"+i).trim())}else void 0===(s=J.get(r))&&(s=P(r.replace(L,\"-$1\").toLowerCase().replace(F,\"-ms-\")),J.set(r,s)),i=\"number\"==typeof i?0===i||R.has(r)?\"\"+i:i+\"px\":P((\"\"+i).trim());a?(a=!1,e.push(' style=\"',s,\":\",i)):e.push(\";\",s,\":\",i)}}a||e.push('\"')}function te(e,t,n){n&&\"function\"!=typeof n&&\"symbol\"!=typeof n&&e.push(\" \",t,'=\"\"')}function ne(e,t,n){\"function\"!=typeof n&&\"symbol\"!=typeof n&&\"boolean\"!=typeof n&&e.push(\" \",t,'=\"',P(n),'\"')}var re=P(\"javascript:throw new Error('React form unexpectedly submitted.')\");function ae(e,t){this.push('<input type=\"hidden\"'),ie(e),ne(this,\"name\",t),ne(this,\"value\",e),this.push(\"/>\")}function ie(e){if(\"string\"!=typeof e)throw Error(n(480))}function se(e,t){if(\"function\"==typeof t.$$FORM_ACTION){var n=e.nextFormID++;e=e.idPrefix+n;try{var r=t.$$FORM_ACTION(e);if(r){var a=r.data;null!=a&&a.forEach(ie)}return r}catch(i){if(\"object\"==typeof i&&null!==i&&\"function\"==typeof i.then)throw i}}return null}function oe(e,t,n,r,a,i,s,o){var l=null;if(\"function\"==typeof r){var c=se(t,r);null!==c?(o=c.name,r=c.action||\"\",a=c.encType,i=c.method,s=c.target,l=c.data):(e.push(\" \",\"formAction\",'=\"',re,'\"'),s=i=a=r=o=null,ue(t,n))}return null!=o&&le(e,\"name\",o),null!=r&&le(e,\"formAction\",r),null!=a&&le(e,\"formEncType\",a),null!=i&&le(e,\"formMethod\",i),null!=s&&le(e,\"formTarget\",s),l}function le(e,t,n){switch(t){case\"className\":ne(e,\"class\",n);break;case\"tabIndex\":ne(e,\"tabindex\",n);break;case\"dir\":case\"role\":case\"viewBox\":case\"width\":case\"height\":ne(e,t,n);break;case\"style\":ee(e,n);break;case\"src\":case\"href\":if(\"\"===n)break;case\"action\":case\"formAction\":if(null==n||\"function\"==typeof n||\"symbol\"==typeof n||\"boolean\"==typeof n)break;n=U(\"\"+n),e.push(\" \",t,'=\"',P(n),'\"');break;case\"defaultValue\":case\"defaultChecked\":case\"innerHTML\":case\"suppressContentEditableWarning\":case\"suppressHydrationWarning\":case\"ref\":break;case\"autoFocus\":case\"multiple\":case\"muted\":te(e,t.toLowerCase(),n);break;case\"xlinkHref\":if(\"function\"==typeof n||\"symbol\"==typeof n||\"boolean\"==typeof n)break;n=U(\"\"+n),e.push(\" \",\"xlink:href\",'=\"',P(n),'\"');break;case\"contentEditable\":case\"spellCheck\":case\"draggable\":case\"value\":case\"autoReverse\":case\"externalResourcesRequired\":case\"focusable\":case\"preserveAlpha\":\"function\"!=typeof n&&\"symbol\"!=typeof n&&e.push(\" \",t,'=\"',P(n),'\"');break;case\"inert\":case\"allowFullScreen\":case\"async\":case\"autoPlay\":case\"controls\":case\"default\":case\"defer\":case\"disabled\":case\"disablePictureInPicture\":case\"disableRemotePlayback\":case\"formNoValidate\":case\"hidden\":case\"loop\":case\"noModule\":case\"noValidate\":case\"open\":case\"playsInline\":case\"readOnly\":case\"required\":case\"reversed\":case\"scoped\":case\"seamless\":case\"itemScope\":n&&\"function\"!=typeof n&&\"symbol\"!=typeof n&&e.push(\" \",t,'=\"\"');break;case\"capture\":case\"download\":!0===n?e.push(\" \",t,'=\"\"'):!1!==n&&\"function\"!=typeof n&&\"symbol\"!=typeof n&&e.push(\" \",t,'=\"',P(n),'\"');break;case\"cols\":case\"rows\":case\"size\":case\"span\":\"function\"!=typeof n&&\"symbol\"!=typeof n&&!isNaN(n)&&1<=n&&e.push(\" \",t,'=\"',P(n),'\"');break;case\"rowSpan\":case\"start\":\"function\"==typeof n||\"symbol\"==typeof n||isNaN(n)||e.push(\" \",t,'=\"',P(n),'\"');break;case\"xlinkActuate\":ne(e,\"xlink:actuate\",n);break;case\"xlinkArcrole\":ne(e,\"xlink:arcrole\",n);break;case\"xlinkRole\":ne(e,\"xlink:role\",n);break;case\"xlinkShow\":ne(e,\"xlink:show\",n);break;case\"xlinkTitle\":ne(e,\"xlink:title\",n);break;case\"xlinkType\":ne(e,\"xlink:type\",n);break;case\"xmlBase\":ne(e,\"xml:base\",n);break;case\"xmlLang\":ne(e,\"xml:lang\",n);break;case\"xmlSpace\":ne(e,\"xml:space\",n);break;default:if((!(2<t.length)||\"o\"!==t[0]&&\"O\"!==t[0]||\"n\"!==t[1]&&\"N\"!==t[1])&&O(t=D.get(t)||t)){switch(typeof n){case\"function\":case\"symbol\":return;case\"boolean\":var r=t.toLowerCase().slice(0,5);if(\"data-\"!==r&&\"aria-\"!==r)return}e.push(\" \",t,'=\"',P(n),'\"')}}}function ce(e,t,r){if(null!=t){if(null!=r)throw Error(n(60));if(\"object\"!=typeof t||!(\"__html\"in t))throw Error(n(61));null!=(t=t.__html)&&e.push(\"\"+t)}}function ue(e,t){if(!(16&e.instructions)){e.instructions|=16;var n=t.preamble,r=t.bootstrapChunks;(n.htmlChunks||n.headChunks)&&0===r.length?(r.push(t.startInlineScript),je(r,e),r.push(\">\",'addEventListener(\"submit\",function(a){if(!a.defaultPrevented){var c=a.target,d=a.submitter,e=c.action,b=d;if(d){var f=d.getAttribute(\"formAction\");null!=f&&(e=f,b=null)}\"javascript:throw new Error(\\'React form unexpectedly submitted.\\')\"===e&&(a.preventDefault(),b?(a=document.createElement(\"input\"),a.name=b.name,a.value=b.value,b.parentNode.insertBefore(a,b),b=new FormData(c),a.parentNode.removeChild(a)):b=new FormData(c),a=c.ownerDocument||c,(a.$$reactFormReplay=a.$$reactFormReplay||[]).push(c,d,b))}});',\"<\\/script>\")):r.unshift(t.startInlineScript,\">\",'addEventListener(\"submit\",function(a){if(!a.defaultPrevented){var c=a.target,d=a.submitter,e=c.action,b=d;if(d){var f=d.getAttribute(\"formAction\");null!=f&&(e=f,b=null)}\"javascript:throw new Error(\\'React form unexpectedly submitted.\\')\"===e&&(a.preventDefault(),b?(a=document.createElement(\"input\"),a.name=b.name,a.value=b.value,b.parentNode.insertBefore(a,b),b=new FormData(c),a.parentNode.removeChild(a)):b=new FormData(c),a=c.ownerDocument||c,(a.$$reactFormReplay=a.$$reactFormReplay||[]).push(c,d,b))}});',\"<\\/script>\")}}function de(e,t){for(var r in e.push(ke(\"link\")),t)if(C.call(t,r)){var a=t[r];if(null!=a)switch(r){case\"children\":case\"dangerouslySetInnerHTML\":throw Error(n(399,\"link\"));default:le(e,r,a)}}return e.push(\"/>\"),null}var pe=/(<\\/|<)(s)(tyle)/gi;function he(e,t,n,r){return t+(\"s\"===n?\"\\\\73 \":\"\\\\53 \")+r}function fe(e,t,r){for(var a in e.push(ke(r)),t)if(C.call(t,a)){var i=t[a];if(null!=i)switch(a){case\"children\":case\"dangerouslySetInnerHTML\":throw Error(n(399,r));default:le(e,a,i)}}return e.push(\"/>\"),null}function me(e,t){e.push(ke(\"title\"));var n,r=null,a=null;for(n in t)if(C.call(t,n)){var i=t[n];if(null!=i)switch(n){case\"children\":r=i;break;case\"dangerouslySetInnerHTML\":a=i;break;default:le(e,n,i)}}return e.push(\">\"),\"function\"!=typeof(t=Array.isArray(r)?2>r.length?r[0]:null:r)&&\"symbol\"!=typeof t&&null!=t&&e.push(P(\"\"+t)),ce(e,a,r),e.push(ve(\"title\")),null}function ge(e,t){e.push(ke(\"script\"));var n,r=null,a=null;for(n in t)if(C.call(t,n)){var i=t[n];if(null!=i)switch(n){case\"children\":r=i;break;case\"dangerouslySetInnerHTML\":a=i;break;default:le(e,n,i)}}return e.push(\">\"),ce(e,a,r),\"string\"==typeof r&&e.push((\"\"+r).replace(Y,W)),e.push(ve(\"script\")),null}function be(e,t,n){e.push(ke(n));var r,a=n=null;for(r in t)if(C.call(t,r)){var i=t[r];if(null!=i)switch(r){case\"children\":n=i;break;case\"dangerouslySetInnerHTML\":a=i;break;default:le(e,r,i)}}return e.push(\">\"),ce(e,a,n),n}function Ee(e,t,n){e.push(ke(n));var r,a=n=null;for(r in t)if(C.call(t,r)){var i=t[r];if(null!=i)switch(r){case\"children\":n=i;break;case\"dangerouslySetInnerHTML\":a=i;break;default:le(e,r,i)}}return e.push(\">\"),ce(e,a,n),\"string\"==typeof n?(e.push(P(n)),null):n}var ye=/^[a-zA-Z][a-zA-Z:_\\.\\-\\d]*$/,_e=new Map;function ke(e){var t=_e.get(e);if(void 0===t){if(!ye.test(e))throw Error(n(65,e));t=\"<\"+e,_e.set(e,t)}return t}function Te(t,r,a,i,s,o,l,c,u){switch(r){case\"div\":case\"span\":case\"svg\":case\"path\":case\"g\":case\"p\":case\"li\":case\"annotation-xml\":case\"color-profile\":case\"font-face\":case\"font-face-src\":case\"font-face-uri\":case\"font-face-format\":case\"font-face-name\":case\"missing-glyph\":break;case\"a\":t.push(ke(\"a\"));var d,p=null,h=null;for(d in a)if(C.call(a,d)){var f=a[d];if(null!=f)switch(d){case\"children\":p=f;break;case\"dangerouslySetInnerHTML\":h=f;break;case\"href\":\"\"===f?ne(t,\"href\",\"\"):le(t,d,f);break;default:le(t,d,f)}}if(t.push(\">\"),ce(t,h,p),\"string\"==typeof p){t.push(P(p));var m=null}else m=p;return m;case\"select\":t.push(ke(\"select\"));var g,b=null,E=null;for(g in a)if(C.call(a,g)){var y=a[g];if(null!=y)switch(g){case\"children\":b=y;break;case\"dangerouslySetInnerHTML\":E=y;break;case\"defaultValue\":case\"value\":break;default:le(t,g,y)}}return t.push(\">\"),ce(t,E,b),b;case\"option\":var _=c.selectedValue;t.push(ke(\"option\"));var k,T=null,S=null,A=null,x=null;for(k in a)if(C.call(a,k)){var w=a[k];if(null!=w)switch(k){case\"children\":T=w;break;case\"selected\":A=w;break;case\"dangerouslySetInnerHTML\":x=w;break;case\"value\":S=w;default:le(t,k,w)}}if(null!=_){var I=null!==S?\"\"+S:function(t){var n=\"\";return e.Children.forEach(t,function(e){null!=e&&(n+=e)}),n}(T);if(v(_)){for(var R=0;R<_.length;R++)if(\"\"+_[R]===I){t.push(' selected=\"\"');break}}else\"\"+_===I&&t.push(' selected=\"\"')}else A&&t.push(' selected=\"\"');return t.push(\">\"),ce(t,x,T),T;case\"textarea\":t.push(ke(\"textarea\"));var D,M=null,L=null,F=null;for(D in a)if(C.call(a,D)){var B=a[D];if(null!=B)switch(D){case\"children\":F=B;break;case\"value\":M=B;break;case\"defaultValue\":L=B;break;case\"dangerouslySetInnerHTML\":throw Error(n(91));default:le(t,D,B)}}if(null===M&&null!==L&&(M=L),t.push(\">\"),null!=F){if(null!=M)throw Error(n(92));if(v(F)){if(1<F.length)throw Error(n(93));M=\"\"+F[0]}M=\"\"+F}return\"string\"==typeof M&&\"\\n\"===M[0]&&t.push(\"\\n\"),null!==M&&t.push(P(\"\"+M)),null;case\"input\":t.push(ke(\"input\"));var H,z=null,G=null,j=null,q=null,Y=null,W=null,V=null,K=null,Q=null;for(H in a)if(C.call(a,H)){var X=a[H];if(null!=X)switch(H){case\"children\":case\"dangerouslySetInnerHTML\":throw Error(n(399,\"input\"));case\"name\":z=X;break;case\"formAction\":G=X;break;case\"formEncType\":j=X;break;case\"formMethod\":q=X;break;case\"formTarget\":Y=X;break;case\"defaultChecked\":Q=X;break;case\"defaultValue\":V=X;break;case\"checked\":K=X;break;case\"value\":W=X;break;default:le(t,H,X)}}var Z=oe(t,i,s,G,j,q,Y,z);return null!==K?te(t,\"checked\",K):null!==Q&&te(t,\"checked\",Q),null!==W?le(t,\"value\",W):null!==V&&le(t,\"value\",V),t.push(\"/>\"),null!=Z&&Z.forEach(ae,t),null;case\"button\":t.push(ke(\"button\"));var J,ie=null,ye=null,_e=null,Te=null,Se=null,Ae=null,Ne=null;for(J in a)if(C.call(a,J)){var Ce=a[J];if(null!=Ce)switch(J){case\"children\":ie=Ce;break;case\"dangerouslySetInnerHTML\":ye=Ce;break;case\"name\":_e=Ce;break;case\"formAction\":Te=Ce;break;case\"formEncType\":Se=Ce;break;case\"formMethod\":Ae=Ce;break;case\"formTarget\":Ne=Ce;break;default:le(t,J,Ce)}}var xe=oe(t,i,s,Te,Se,Ae,Ne,_e);if(t.push(\">\"),null!=xe&&xe.forEach(ae,t),ce(t,ye,ie),\"string\"==typeof ie){t.push(P(ie));var we=null}else we=ie;return we;case\"form\":t.push(ke(\"form\"));var Ie,Oe=null,Re=null,De=null,Me=null,Pe=null,Le=null;for(Ie in a)if(C.call(a,Ie)){var Fe=a[Ie];if(null!=Fe)switch(Ie){case\"children\":Oe=Fe;break;case\"dangerouslySetInnerHTML\":Re=Fe;break;case\"action\":De=Fe;break;case\"encType\":Me=Fe;break;case\"method\":Pe=Fe;break;case\"target\":Le=Fe;break;default:le(t,Ie,Fe)}}var Be=null,Ue=null;if(\"function\"==typeof De){var He=se(i,De);null!==He?(De=He.action||\"\",Me=He.encType,Pe=He.method,Le=He.target,Be=He.data,Ue=He.name):(t.push(\" \",\"action\",'=\"',re,'\"'),Le=Pe=Me=De=null,ue(i,s))}if(null!=De&&le(t,\"action\",De),null!=Me&&le(t,\"encType\",Me),null!=Pe&&le(t,\"method\",Pe),null!=Le&&le(t,\"target\",Le),t.push(\">\"),null!==Ue&&(t.push('<input type=\"hidden\"'),ne(t,\"name\",Ue),t.push(\"/>\"),null!=Be&&Be.forEach(ae,t)),ce(t,Re,Oe),\"string\"==typeof Oe){t.push(P(Oe));var ze=null}else ze=Oe;return ze;case\"menuitem\":for(var Ge in t.push(ke(\"menuitem\")),a)if(C.call(a,Ge)){var je=a[Ge];if(null!=je)switch(Ge){case\"children\":case\"dangerouslySetInnerHTML\":throw Error(n(400));default:le(t,Ge,je)}}return t.push(\">\"),null;case\"object\":t.push(ke(\"object\"));var $e,qe=null,Ve=null;for($e in a)if(C.call(a,$e)){var Ke=a[$e];if(null!=Ke)switch($e){case\"children\":qe=Ke;break;case\"dangerouslySetInnerHTML\":Ve=Ke;break;case\"data\":var Qe=U(\"\"+Ke);if(\"\"===Qe)break;t.push(\" \",\"data\",'=\"',P(Qe),'\"');break;default:le(t,$e,Ke)}}if(t.push(\">\"),ce(t,Ve,qe),\"string\"==typeof qe){t.push(P(qe));var Xe=null}else Xe=qe;return Xe;case\"title\":var Ze=1&c.tagScope,Je=4&c.tagScope;if(4===c.insertionMode||Ze||null!=a.itemProp)var et=me(t,a);else Je?et=null:(me(s.hoistableChunks,a),et=void 0);return et;case\"link\":var tt=1&c.tagScope,nt=4&c.tagScope,rt=a.rel,at=a.href,it=a.precedence;if(4===c.insertionMode||tt||null!=a.itemProp||\"string\"!=typeof rt||\"string\"!=typeof at||\"\"===at){de(t,a);var st=null}else if(\"stylesheet\"===a.rel)if(\"string\"!=typeof it||null!=a.disabled||a.onLoad||a.onError)st=de(t,a);else{var ot=s.styles.get(it),lt=i.styleResources.hasOwnProperty(at)?i.styleResources[at]:void 0;if(null!==lt){i.styleResources[at]=null,ot||(ot={precedence:P(it),rules:[],hrefs:[],sheets:new Map},s.styles.set(it,ot));var ct={state:0,props:N({},a,{\"data-precedence\":a.precedence,precedence:null})};if(lt){2===lt.length&&Ye(ct.props,lt);var ut=s.preloads.stylesheets.get(at);ut&&0<ut.length?ut.length=0:ct.state=1}ot.sheets.set(at,ct),l&&l.stylesheets.add(ct)}else if(ot){var dt=ot.sheets.get(at);dt&&l&&l.stylesheets.add(dt)}u&&t.push(\"\\x3c!-- --\\x3e\"),st=null}else a.onLoad||a.onError?st=de(t,a):(u&&t.push(\"\\x3c!-- --\\x3e\"),st=nt?null:de(s.hoistableChunks,a));return st;case\"script\":var pt=1&c.tagScope,ht=a.async;if(\"string\"!=typeof a.src||!a.src||!ht||\"function\"==typeof ht||\"symbol\"==typeof ht||a.onLoad||a.onError||4===c.insertionMode||pt||null!=a.itemProp)var ft=ge(t,a);else{var mt=a.src;if(\"module\"===a.type)var gt=i.moduleScriptResources,bt=s.preloads.moduleScripts;else gt=i.scriptResources,bt=s.preloads.scripts;var Et=gt.hasOwnProperty(mt)?gt[mt]:void 0;if(null!==Et){gt[mt]=null;var yt=a;if(Et){2===Et.length&&Ye(yt=N({},a),Et);var _t=bt.get(mt);_t&&(_t.length=0)}var kt=[];s.scripts.add(kt),ge(kt,yt)}u&&t.push(\"\\x3c!-- --\\x3e\"),ft=null}return ft;case\"style\":var Tt=1&c.tagScope,St=a.precedence,vt=a.href,At=a.nonce;if(4===c.insertionMode||Tt||null!=a.itemProp||\"string\"!=typeof St||\"string\"!=typeof vt||\"\"===vt){t.push(ke(\"style\"));var Nt,Ct=null,xt=null;for(Nt in a)if(C.call(a,Nt)){var wt=a[Nt];if(null!=wt)switch(Nt){case\"children\":Ct=wt;break;case\"dangerouslySetInnerHTML\":xt=wt;break;default:le(t,Nt,wt)}}t.push(\">\");var It=Array.isArray(Ct)?2>Ct.length?Ct[0]:null:Ct;\"function\"!=typeof It&&\"symbol\"!=typeof It&&null!=It&&t.push((\"\"+It).replace(pe,he)),ce(t,xt,Ct),t.push(ve(\"style\"));var Ot=null}else{var Rt=s.styles.get(St);if(null!==(i.styleResources.hasOwnProperty(vt)?i.styleResources[vt]:void 0)){i.styleResources[vt]=null,Rt||(Rt={precedence:P(St),rules:[],hrefs:[],sheets:new Map},s.styles.set(St,Rt));var Dt=s.nonce.style;if(!Dt||Dt===At){Rt.hrefs.push(P(vt));var Mt,Pt=Rt.rules,Lt=null,Ft=null;for(Mt in a)if(C.call(a,Mt)){var Bt=a[Mt];if(null!=Bt)switch(Mt){case\"children\":Lt=Bt;break;case\"dangerouslySetInnerHTML\":Ft=Bt}}var Ut=Array.isArray(Lt)?2>Lt.length?Lt[0]:null:Lt;\"function\"!=typeof Ut&&\"symbol\"!=typeof Ut&&null!=Ut&&Pt.push((\"\"+Ut).replace(pe,he)),ce(Pt,Ft,Lt)}}Rt&&l&&l.styles.add(Rt),u&&t.push(\"\\x3c!-- --\\x3e\"),Ot=void 0}return Ot;case\"meta\":var Ht=1&c.tagScope,zt=4&c.tagScope;if(4===c.insertionMode||Ht||null!=a.itemProp)var Gt=fe(t,a,\"meta\");else u&&t.push(\"\\x3c!-- --\\x3e\"),Gt=zt?null:\"string\"==typeof a.charSet?fe(s.charsetChunks,a,\"meta\"):\"viewport\"===a.name?fe(s.viewportChunks,a,\"meta\"):fe(s.hoistableChunks,a,\"meta\");return Gt;case\"listing\":case\"pre\":t.push(ke(r));var jt,$t=null,qt=null;for(jt in a)if(C.call(a,jt)){var Yt=a[jt];if(null!=Yt)switch(jt){case\"children\":$t=Yt;break;case\"dangerouslySetInnerHTML\":qt=Yt;break;default:le(t,jt,Yt)}}if(t.push(\">\"),null!=qt){if(null!=$t)throw Error(n(60));if(\"object\"!=typeof qt||!(\"__html\"in qt))throw Error(n(61));var Wt=qt.__html;null!=Wt&&(\"string\"==typeof Wt&&0<Wt.length&&\"\\n\"===Wt[0]?t.push(\"\\n\",Wt):t.push(\"\"+Wt))}return\"string\"==typeof $t&&\"\\n\"===$t[0]&&t.push(\"\\n\"),$t;case\"img\":var Vt=3&c.tagScope,Kt=a.src,Qt=a.srcSet;if(!(\"lazy\"===a.loading||!Kt&&!Qt||\"string\"!=typeof Kt&&null!=Kt||\"string\"!=typeof Qt&&null!=Qt||\"low\"===a.fetchPriority||Vt)&&(\"string\"!=typeof Kt||\":\"!==Kt[4]||\"d\"!==Kt[0]&&\"D\"!==Kt[0]||\"a\"!==Kt[1]&&\"A\"!==Kt[1]||\"t\"!==Kt[2]&&\"T\"!==Kt[2]||\"a\"!==Kt[3]&&\"A\"!==Kt[3])&&(\"string\"!=typeof Qt||\":\"!==Qt[4]||\"d\"!==Qt[0]&&\"D\"!==Qt[0]||\"a\"!==Qt[1]&&\"A\"!==Qt[1]||\"t\"!==Qt[2]&&\"T\"!==Qt[2]||\"a\"!==Qt[3]&&\"A\"!==Qt[3])){null!==l&&64&c.tagScope&&(l.suspenseyImages=!0);var Xt=\"string\"==typeof a.sizes?a.sizes:void 0,Zt=Qt?Qt+\"\\n\"+(Xt||\"\"):Kt,Jt=s.preloads.images,en=Jt.get(Zt);if(en)(\"high\"===a.fetchPriority||10>s.highImagePreloads.size)&&(Jt.delete(Zt),s.highImagePreloads.add(en));else if(!i.imageResources.hasOwnProperty(Zt)){i.imageResources[Zt]=$;var tn,nn=a.crossOrigin,rn=\"string\"==typeof nn?\"use-credentials\"===nn?nn:\"\":void 0,an=s.headers;an&&0<an.remainingCapacity&&\"string\"!=typeof a.srcSet&&(\"high\"===a.fetchPriority||500>an.highImagePreloads.length)&&(tn=We(Kt,\"image\",{imageSrcSet:a.srcSet,imageSizes:a.sizes,crossOrigin:rn,integrity:a.integrity,nonce:a.nonce,type:a.type,fetchPriority:a.fetchPriority,referrerPolicy:a.refererPolicy}),0<=(an.remainingCapacity-=tn.length+2))?(s.resets.image[Zt]=$,an.highImagePreloads&&(an.highImagePreloads+=\", \"),an.highImagePreloads+=tn):(de(en=[],{rel:\"preload\",as:\"image\",href:Qt?void 0:Kt,imageSrcSet:Qt,imageSizes:Xt,crossOrigin:rn,integrity:a.integrity,type:a.type,fetchPriority:a.fetchPriority,referrerPolicy:a.referrerPolicy}),\"high\"===a.fetchPriority||10>s.highImagePreloads.size?s.highImagePreloads.add(en):(s.bulkPreloads.add(en),Jt.set(Zt,en)))}}return fe(t,a,\"img\");case\"base\":case\"area\":case\"br\":case\"col\":case\"embed\":case\"hr\":case\"keygen\":case\"param\":case\"source\":case\"track\":case\"wbr\":return fe(t,a,r);case\"head\":if(2>c.insertionMode){var sn=o||s.preamble;if(sn.headChunks)throw Error(n(545,\"`<head>`\"));null!==o&&t.push(\"\\x3c!--head--\\x3e\"),sn.headChunks=[];var on=be(sn.headChunks,a,\"head\")}else on=Ee(t,a,\"head\");return on;case\"body\":if(2>c.insertionMode){var ln=o||s.preamble;if(ln.bodyChunks)throw Error(n(545,\"`<body>`\"));null!==o&&t.push(\"\\x3c!--body--\\x3e\"),ln.bodyChunks=[];var cn=be(ln.bodyChunks,a,\"body\")}else cn=Ee(t,a,\"body\");return cn;case\"html\":if(0===c.insertionMode){var un=o||s.preamble;if(un.htmlChunks)throw Error(n(545,\"`<html>`\"));null!==o&&t.push(\"\\x3c!--html--\\x3e\"),un.htmlChunks=[\"\"];var dn=be(un.htmlChunks,a,\"html\")}else dn=Ee(t,a,\"html\");return dn;default:if(-1!==r.indexOf(\"-\")){t.push(ke(r));var pn,hn=null,fn=null;for(pn in a)if(C.call(a,pn)){var mn=a[pn];if(null!=mn){var gn=pn;switch(pn){case\"children\":hn=mn;break;case\"dangerouslySetInnerHTML\":fn=mn;break;case\"style\":ee(t,mn);break;case\"suppressContentEditableWarning\":case\"suppressHydrationWarning\":case\"ref\":break;case\"className\":gn=\"class\";default:if(O(pn)&&\"function\"!=typeof mn&&\"symbol\"!=typeof mn&&!1!==mn){if(!0===mn)mn=\"\";else if(\"object\"==typeof mn)continue;t.push(\" \",gn,'=\"',P(mn),'\"')}}}}return t.push(\">\"),ce(t,fn,hn),hn}}return Ee(t,a,r)}var Se=new Map;function ve(e){var t=Se.get(e);return void 0===t&&(t=\"</\"+e+\">\",Se.set(e,t)),t}function Ae(e,t){null===(e=e.preamble).htmlChunks&&t.htmlChunks&&(e.htmlChunks=t.htmlChunks),null===e.headChunks&&t.headChunks&&(e.headChunks=t.headChunks),null===e.bodyChunks&&t.bodyChunks&&(e.bodyChunks=t.bodyChunks)}function Ne(e,t){t=t.bootstrapChunks;for(var n=0;n<t.length-1;n++)e.push(t[n]);return!(n<t.length)||(n=t[n],t.length=0,e.push(n))}function Ce(e,t,r){if(e.push('\\x3c!--$?--\\x3e<template id=\"'),null===r)throw Error(n(395));return e.push(t.boundaryPrefix),t=r.toString(16),e.push(t),e.push('\"></template>')}var xe=/[<\\u2028\\u2029]/g;function we(e){return JSON.stringify(e).replace(xe,function(e){switch(e){case\"<\":return\"\\\\u003c\";case\"\\u2028\":return\"\\\\u2028\";case\"\\u2029\":return\"\\\\u2029\";default:throw Error(\"escapeJSStringsForInstructionScripts encountered a match it does not know how to replace. this means the match regex and the replacement characters are no longer in sync. This is a bug in React\")}})}var Ie=/[&><\\u2028\\u2029]/g;function Oe(e){return JSON.stringify(e).replace(Ie,function(e){switch(e){case\"&\":return\"\\\\u0026\";case\">\":return\"\\\\u003e\";case\"<\":return\"\\\\u003c\";case\"\\u2028\":return\"\\\\u2028\";case\"\\u2029\":return\"\\\\u2029\";default:throw Error(\"escapeJSObjectForInstructionScripts encountered a match it does not know how to replace. this means the match regex and the replacement characters are no longer in sync. This is a bug in React\")}})}var Re=!1,De=!0;function Me(e){var t=e.rules,n=e.hrefs,r=0;if(n.length){for(this.push(q.startInlineStyle),this.push(' media=\"not all\" data-precedence=\"'),this.push(e.precedence),this.push('\" data-href=\"');r<n.length-1;r++)this.push(n[r]),this.push(\" \");for(this.push(n[r]),this.push('\">'),r=0;r<t.length;r++)this.push(t[r]);De=this.push(\"</style>\"),Re=!0,t.length=0,n.length=0}}function Pe(e){return 2!==e.state&&(Re=!0)}function Le(e,t,n){return Re=!1,De=!0,q=n,t.styles.forEach(Me,e),q=null,t.stylesheets.forEach(Pe),Re&&(n.stylesToHoist=!0),De}function Fe(e){for(var t=0;t<e.length;t++)this.push(e[t]);e.length=0}var Be=[];function Ue(e){de(Be,e.props);for(var t=0;t<Be.length;t++)this.push(Be[t]);Be.length=0,e.state=2}function He(e){var t=0<e.sheets.size;e.sheets.forEach(Ue,this),e.sheets.clear();var n=e.rules,r=e.hrefs;if(!t||r.length){if(this.push(q.startInlineStyle),this.push(' data-precedence=\"'),this.push(e.precedence),e=0,r.length){for(this.push('\" data-href=\"');e<r.length-1;e++)this.push(r[e]),this.push(\" \");this.push(r[e])}for(this.push('\">'),e=0;e<n.length;e++)this.push(n[e]);this.push(\"</style>\"),n.length=0,r.length=0}}function ze(e){if(0===e.state){e.state=1;var t=e.props;for(de(Be,{rel:\"preload\",as:\"style\",href:e.props.href,crossOrigin:t.crossOrigin,fetchPriority:t.fetchPriority,integrity:t.integrity,media:t.media,hrefLang:t.hrefLang,referrerPolicy:t.referrerPolicy}),e=0;e<Be.length;e++)this.push(Be[e]);Be.length=0}}function Ge(e){e.sheets.forEach(ze,this),e.sheets.clear()}function je(e,t){!(32&t.instructions)&&(t.instructions|=32,e.push(' id=\"',P(\"_\"+t.idPrefix+\"R_\"),'\"'))}function $e(e,t,n){var r=t.toLowerCase();switch(typeof n){case\"function\":case\"symbol\":return}switch(t){case\"innerHTML\":case\"dangerouslySetInnerHTML\":case\"suppressContentEditableWarning\":case\"suppressHydrationWarning\":case\"style\":case\"ref\":return;case\"className\":r=\"class\",t=\"\"+n;break;case\"hidden\":if(!1===n)return;t=\"\";break;case\"src\":case\"href\":t=\"\"+(n=U(n));break;default:if(2<t.length&&(\"o\"===t[0]||\"O\"===t[0])&&(\"n\"===t[1]||\"N\"===t[1])||!O(t))return;t=\"\"+n}e.push(\",\"),r=Oe(r),e.push(r),e.push(\",\"),r=Oe(t),e.push(r)}function qe(){return{styles:new Set,stylesheets:new Set,suspenseyImages:!1}}function Ye(e,t){null==e.crossOrigin&&(e.crossOrigin=t[0]),null==e.integrity&&(e.integrity=t[1])}function We(e,t,n){for(var r in t=\"<\"+(e=(\"\"+e).replace(Ve,Ke))+'>; rel=preload; as=\"'+(t=(\"\"+t).replace(Qe,Xe))+'\"',n)C.call(n,r)&&(\"string\"==typeof(e=n[r])&&(t+=\"; \"+r.toLowerCase()+'=\"'+(\"\"+e).replace(Qe,Xe)+'\"'));return t}var Ve=/[<>\\r\\n]/g;function Ke(e){switch(e){case\"<\":return\"%3C\";case\">\":return\"%3E\";case\"\\n\":return\"%0A\";case\"\\r\":return\"%0D\";default:throw Error(\"escapeLinkHrefForHeaderContextReplacer encountered a match it does not know how to replace. this means the match regex and the replacement characters are no longer in sync. This is a bug in React\")}}var Qe=/[\"';,\\r\\n]/g;function Xe(e){switch(e){case'\"':return\"%22\";case\"'\":return\"%27\";case\";\":return\"%3B\";case\",\":return\"%2C\";case\"\\n\":return\"%0A\";case\"\\r\":return\"%0D\";default:throw Error(\"escapeStringForLinkHeaderQuotedParamValueContextReplacer encountered a match it does not know how to replace. this means the match regex and the replacement characters are no longer in sync. This is a bug in React\")}}function Ze(e){this.styles.add(e)}function Je(e){this.stylesheets.add(e)}function et(e,t){t.styles.forEach(Ze,e),t.stylesheets.forEach(Je,e),t.suspenseyImages&&(e.suspenseyImages=!0)}function tt(e,t,n,r){return n.generateStaticMarkup?(e.push(P(t)),!1):(\"\"===t?e=r:(r&&e.push(\"\\x3c!-- --\\x3e\"),e.push(P(t)),e=!0),e)}function nt(e,t,n,r){t.generateStaticMarkup||n&&r&&e.push(\"\\x3c!-- --\\x3e\")}var rt=Function.prototype.bind,at=Symbol.for(\"react.client.reference\");function it(e){if(null==e)return null;if(\"function\"==typeof e)return e.$$typeof===at?null:e.displayName||e.name||null;if(\"string\"==typeof e)return e;switch(e){case o:return\"Fragment\";case c:return\"Profiler\";case l:return\"StrictMode\";case h:return\"Suspense\";case f:return\"SuspenseList\";case E:return\"Activity\"}if(\"object\"==typeof e)switch(e.$$typeof){case a:return\"Portal\";case d:return e.displayName||\"Context\";case u:return(e._context.displayName||\"Context\")+\".Consumer\";case p:var t=e.render;return(e=e.displayName)||(e=\"\"!==(e=t.displayName||t.name||\"\")?\"ForwardRef(\"+e+\")\":\"ForwardRef\"),e;case m:return null!==(t=e.displayName||null)?t:it(e.type)||\"Memo\";case g:t=e._payload,e=e._init;try{return it(e(t))}catch(n){}}return null}var st={},ot=null;function lt(e,t){if(e!==t){e.context._currentValue2=e.parentValue,e=e.parent;var r=t.parent;if(null===e){if(null!==r)throw Error(n(401))}else{if(null===r)throw Error(n(401));lt(e,r)}t.context._currentValue2=t.value}}function ct(e){e.context._currentValue2=e.parentValue,null!==(e=e.parent)&&ct(e)}function ut(e){var t=e.parent;null!==t&&ut(t),e.context._currentValue2=e.value}function dt(e,t){if(e.context._currentValue2=e.parentValue,null===(e=e.parent))throw Error(n(402));e.depth===t.depth?lt(e,t):dt(e,t)}function pt(e,t){var r=t.parent;if(null===r)throw Error(n(402));e.depth===r.depth?lt(e,r):pt(e,r),t.context._currentValue2=t.value}function ht(e){var t=ot;t!==e&&(null===t?ut(e):null===e?ct(t):t.depth===e.depth?lt(t,e):t.depth>e.depth?dt(t,e):pt(t,e),ot=e)}var ft={enqueueSetState:function(e,t){null!==(e=e._reactInternals).queue&&e.queue.push(t)},enqueueReplaceState:function(e,t){(e=e._reactInternals).replace=!0,e.queue=[t]},enqueueForceUpdate:function(){}},mt={id:1,overflow:\"\"};function gt(e,t,n){var r=e.id;e=e.overflow;var a=32-bt(r)-1;r&=~(1<<a),n+=1;var i=32-bt(t)+a;if(30<i){var s=a-a%5;return i=(r&(1<<s)-1).toString(32),r>>=s,a-=s,{id:1<<32-bt(t)+a|n<<a|r,overflow:i+e}}return{id:1<<i|n<<a|r,overflow:e}}var bt=Math.clz32?Math.clz32:function(e){return 0===(e>>>=0)?32:31-(Et(e)/yt|0)|0},Et=Math.log,yt=Math.LN2;function _t(){}var kt=Error(n(460));var Tt=null;function St(){if(null===Tt)throw Error(n(459));var e=Tt;return Tt=null,e}var vt=\"function\"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},At=null,Nt=null,Ct=null,xt=null,wt=null,It=null,Ot=!1,Rt=!1,Dt=0,Mt=0,Pt=-1,Lt=0,Ft=null,Bt=null,Ut=0;function Ht(){if(null===At)throw Error(n(321));return At}function zt(){if(0<Ut)throw Error(n(312));return{memoizedState:null,queue:null,next:null}}function Gt(){return null===It?null===wt?(Ot=!1,wt=It=zt()):(Ot=!0,It=wt):null===It.next?(Ot=!1,It=It.next=zt()):(Ot=!0,It=It.next),It}function jt(){var e=Ft;return Ft=null,e}function $t(){xt=Ct=Nt=At=null,Rt=!1,wt=null,Ut=0,It=Bt=null}function qt(e,t){return\"function\"==typeof t?t(e):t}function Yt(e,t,n){if(At=Ht(),It=Gt(),Ot){var r=It.queue;if(t=r.dispatch,null!==Bt&&void 0!==(n=Bt.get(r))){Bt.delete(r),r=It.memoizedState;do{r=e(r,n.action),n=n.next}while(null!==n);return It.memoizedState=r,[r,t]}return[It.memoizedState,t]}return e=e===qt?\"function\"==typeof t?t():t:void 0!==n?n(t):t,It.memoizedState=e,e=(e=It.queue={last:null,dispatch:null}).dispatch=Vt.bind(null,At,e),[It.memoizedState,e]}function Wt(e,t){if(At=Ht(),t=void 0===t?null:t,null!==(It=Gt())){var n=It.memoizedState;if(null!==n&&null!==t){var r=n[1];e:if(null===r)r=!1;else{for(var a=0;a<r.length&&a<t.length;a++)if(!vt(t[a],r[a])){r=!1;break e}r=!0}if(r)return n[0]}}return e=e(),It.memoizedState=[e,t],e}function Vt(e,t,r){if(25<=Ut)throw Error(n(301));if(e===At)if(Rt=!0,e={action:r,next:null},null===Bt&&(Bt=new Map),void 0===(r=Bt.get(t)))Bt.set(t,e);else{for(t=r;null!==t.next;)t=t.next;t.next=e}}function Kt(){throw Error(n(440))}function Qt(){throw Error(n(394))}function Xt(){throw Error(n(479))}function Zt(e,t,n){Ht();var r=Mt++,a=Ct;if(\"function\"==typeof e.$$FORM_ACTION){var i=null,s=xt;a=a.formState;var o=e.$$IS_SIGNATURE_EQUAL;if(null!==a&&\"function\"==typeof o){var l=a[1];o.call(e,a[2],a[3])&&(l===(i=void 0!==n?\"p\"+n:\"k\"+A(JSON.stringify([s,null,r]),0))&&(Pt=r,t=a[0]))}var c=e.bind(null,t);return e=function(e){c(e)},\"function\"==typeof c.$$FORM_ACTION&&(e.$$FORM_ACTION=function(e){e=c.$$FORM_ACTION(e),void 0!==n&&(n+=\"\",e.action=n);var t=e.data;return t&&(null===i&&(i=void 0!==n?\"p\"+n:\"k\"+A(JSON.stringify([s,null,r]),0)),t.append(\"$ACTION_KEY\",i)),e}),[t,e,!1]}var u=e.bind(null,t);return[t,function(e){u(e)},!1]}function Jt(e){var t=Lt;return Lt+=1,null===Ft&&(Ft=[]),function(e,t,n){switch(void 0===(n=e[n])?e.push(t):n!==t&&(t.then(_t,_t),t=n),t.status){case\"fulfilled\":return t.value;case\"rejected\":throw t.reason;default:switch(\"string\"==typeof t.status?t.then(_t,_t):((e=t).status=\"pending\",e.then(function(e){if(\"pending\"===t.status){var n=t;n.status=\"fulfilled\",n.value=e}},function(e){if(\"pending\"===t.status){var n=t;n.status=\"rejected\",n.reason=e}})),t.status){case\"fulfilled\":return t.value;case\"rejected\":throw t.reason}throw Tt=t,kt}}(Ft,e,t)}function en(){throw Error(n(393))}var tn,nn,rn={readContext:function(e){return e._currentValue2},use:function(e){if(null!==e&&\"object\"==typeof e){if(\"function\"==typeof e.then)return Jt(e);if(e.$$typeof===d)return e._currentValue2}throw Error(n(438,String(e)))},useContext:function(e){return Ht(),e._currentValue2},useMemo:Wt,useReducer:Yt,useRef:function(e){At=Ht();var t=(It=Gt()).memoizedState;return null===t?(e={current:e},It.memoizedState=e):t},useState:function(e){return Yt(qt,e)},useInsertionEffect:_t,useLayoutEffect:_t,useCallback:function(e,t){return Wt(function(){return e},t)},useImperativeHandle:_t,useEffect:_t,useDebugValue:_t,useDeferredValue:function(e,t){return Ht(),void 0!==t?t:e},useTransition:function(){return Ht(),[!1,Qt]},useId:function(){var e=Nt.treeContext,t=e.overflow;e=((e=e.id)&~(1<<32-bt(e)-1)).toString(32)+t;var r=an;if(null===r)throw Error(n(404));return t=Dt++,e=\"_\"+r.idPrefix+\"R_\"+e,0<t&&(e+=\"H\"+t.toString(32)),e+\"_\"},useSyncExternalStore:function(e,t,r){if(void 0===r)throw Error(n(407));return r()},useOptimistic:function(e){return Ht(),[e,Xt]},useActionState:Zt,useFormState:Zt,useHostTransitionStatus:function(){return Ht(),G},useMemoCache:function(e){for(var t=Array(e),n=0;n<e;n++)t[n]=_;return t},useCacheRefresh:function(){return en},useEffectEvent:function(){return Kt}},an=null,sn={getCacheForType:function(){throw Error(n(248))},cacheSignal:function(){throw Error(n(248))}};function on(e){if(void 0===tn)try{throw Error()}catch(n){var t=n.stack.trim().match(/\\n( *(at )?)/);tn=t&&t[1]||\"\",nn=-1<n.stack.indexOf(\"\\n    at\")?\" (<anonymous>)\":-1<n.stack.indexOf(\"@\")?\"@unknown:0:0\":\"\"}return\"\\n\"+tn+e+nn}var ln=!1;function cn(e,t){if(!e||ln)return\"\";ln=!0;var n=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var r={DetermineComponentFrameRoot:function(){try{if(t){var n=function(){throw Error()};if(Object.defineProperty(n.prototype,\"props\",{set:function(){throw Error()}}),\"object\"==typeof Reflect&&Reflect.construct){try{Reflect.construct(n,[])}catch(a){var r=a}Reflect.construct(e,[],n)}else{try{n.call()}catch(i){r=i}e.call(n.prototype)}}else{try{throw Error()}catch(s){r=s}(n=e())&&\"function\"==typeof n.catch&&n.catch(function(){})}}catch(o){if(o&&r&&\"string\"==typeof o.stack)return[o.stack,r.stack]}return[null,null]}};r.DetermineComponentFrameRoot.displayName=\"DetermineComponentFrameRoot\";var a=Object.getOwnPropertyDescriptor(r.DetermineComponentFrameRoot,\"name\");a&&a.configurable&&Object.defineProperty(r.DetermineComponentFrameRoot,\"name\",{value:\"DetermineComponentFrameRoot\"});var i=r.DetermineComponentFrameRoot(),s=i[0],o=i[1];if(s&&o){var l=s.split(\"\\n\"),c=o.split(\"\\n\");for(a=r=0;r<l.length&&!l[r].includes(\"DetermineComponentFrameRoot\");)r++;for(;a<c.length&&!c[a].includes(\"DetermineComponentFrameRoot\");)a++;if(r===l.length||a===c.length)for(r=l.length-1,a=c.length-1;1<=r&&0<=a&&l[r]!==c[a];)a--;for(;1<=r&&0<=a;r--,a--)if(l[r]!==c[a]){if(1!==r||1!==a)do{if(r--,0>--a||l[r]!==c[a]){var u=\"\\n\"+l[r].replace(\" at new \",\" at \");return e.displayName&&u.includes(\"<anonymous>\")&&(u=u.replace(\"<anonymous>\",e.displayName)),u}}while(1<=r&&0<=a);break}}}finally{ln=!1,Error.prepareStackTrace=n}return(n=e?e.displayName||e.name:\"\")?on(n):\"\"}function un(e){if(\"string\"==typeof e)return on(e);if(\"function\"==typeof e)return e.prototype&&e.prototype.isReactComponent?cn(e,!0):cn(e,!1);if(\"object\"==typeof e&&null!==e){switch(e.$$typeof){case p:return cn(e.render,!1);case m:return cn(e.type,!1);case g:var t=e,n=t._payload;t=t._init;try{e=t(n)}catch(a){return on(\"Lazy\")}return un(e)}if(\"string\"==typeof e.name){n=e.name,t=e.env;var r=e.debugLocation;return n=null==r||(e=Error.prepareStackTrace,Error.prepareStackTrace=void 0,r=r.stack,Error.prepareStackTrace=e,r.startsWith(\"Error: react-stack-top-frame\\n\")&&(r=r.slice(29)),-1!==(e=r.indexOf(\"\\n\"))&&(r=r.slice(e+1)),-1!==(e=r.indexOf(\"react_stack_bottom_frame\"))&&(e=r.lastIndexOf(\"\\n\",e)),e=-1!==e?r=r.slice(0,e):\"\",r=e.lastIndexOf(\"\\n\"),-1===(e=-1===r?e:e.slice(r+1)).indexOf(n))?on(n+(t?\" [\"+t+\"]\":\"\")):\"\\n\"+e}}switch(e){case f:return on(\"SuspenseList\");case h:return on(\"Suspense\")}return\"\"}function dn(e,t){return 500<t.byteSize&&null===t.contentPreamble}function pn(e){if(\"object\"==typeof e&&null!==e&&\"string\"==typeof e.environmentName){var t=e.environmentName;\"string\"==typeof(e=[e].slice(0))[0]?e.splice(0,1,\"[%s] \"+e[0],\" \"+t+\" \"):e.splice(0,0,\"[%s]\",\" \"+t+\" \"),e.unshift(console),(t=rt.apply(console.error,e))()}return null}function hn(e,t,n,r,a,i,s,o,l,c,u){var d=new Set;this.destination=null,this.flushScheduled=!1,this.resumableState=e,this.renderState=t,this.rootFormatContext=n,this.progressiveChunkSize=void 0===r?12800:r,this.status=10,this.fatalError=null,this.pendingRootTasks=this.allPendingTasks=this.nextSegmentId=0,this.completedPreambleSegments=this.completedRootSegment=null,this.byteSize=0,this.abortableTasks=d,this.pingedTasks=[],this.clientRenderedBoundaries=[],this.completedBoundaries=[],this.partialBoundaries=[],this.trackedPostpones=null,this.onError=void 0===a?pn:a,this.onPostpone=void 0===c?_t:c,this.onAllReady=void 0===i?_t:i,this.onShellReady=void 0===s?_t:s,this.onShellError=void 0===o?_t:o,this.onFatalError=void 0===l?_t:l,this.formState=void 0===u?null:u}var fn=null;function mn(e,t){e.pingedTasks.push(t),1===e.pingedTasks.length&&(e.flushScheduled=null!==e.destination,Xn(e))}function gn(e,t,n,r,a){return n={status:0,rootSegmentID:-1,parentFlushed:!1,pendingTasks:0,row:t,completedSegments:[],byteSize:0,fallbackAbortableTasks:n,errorDigest:null,contentState:qe(),fallbackState:qe(),contentPreamble:r,fallbackPreamble:a,trackedContentKeyPath:null,trackedFallbackNode:null},null!==t&&(t.pendingTasks++,null!==(r=t.boundaries)&&(e.allPendingTasks++,n.pendingTasks++,r.push(n)),null!==(e=t.inheritedHoistables)&&et(n.contentState,e)),n}function bn(e,t,n,r,a,i,s,o,l,c,u,d,p,h,f){e.allPendingTasks++,null===a?e.pendingRootTasks++:a.pendingTasks++,null!==h&&h.pendingTasks++;var m={replay:null,node:n,childIndex:r,ping:function(){return mn(e,m)},blockedBoundary:a,blockedSegment:i,blockedPreamble:s,hoistableState:o,abortSet:l,keyPath:c,formatContext:u,context:d,treeContext:p,row:h,componentStack:f,thenableState:t};return l.add(m),m}function En(e,t,n,r,a,i,s,o,l,c,u,d,p,h){e.allPendingTasks++,null===i?e.pendingRootTasks++:i.pendingTasks++,null!==p&&p.pendingTasks++,n.pendingTasks++;var f={replay:n,node:r,childIndex:a,ping:function(){return mn(e,f)},blockedBoundary:i,blockedSegment:null,blockedPreamble:null,hoistableState:s,abortSet:o,keyPath:l,formatContext:c,context:u,treeContext:d,row:p,componentStack:h,thenableState:t};return o.add(f),f}function yn(e,t,n,r,a,i){return{status:0,parentFlushed:!1,id:-1,index:t,chunks:[],children:[],preambleChildren:[],parentFormatContext:r,boundary:n,lastPushedText:a,textEmbedded:i}}function _n(e){var t=e.node;if(\"object\"==typeof t&&null!==t&&t.$$typeof===r)e.componentStack={parent:e.componentStack,type:t.type}}function kn(e){return null===e?null:{parent:e.parent,type:\"Suspense Fallback\"}}function Tn(e){var t={};return e&&Object.defineProperty(t,\"componentStack\",{configurable:!0,enumerable:!0,get:function(){try{var n=\"\",r=e;do{n+=un(r.type),r=r.parent}while(r);var a=n}catch(i){a=\"\\nError generating stack: \"+i.message+\"\\n\"+i.stack}return Object.defineProperty(t,\"componentStack\",{value:a}),a}}),t}function Sn(e,t,n){if(null==(t=(e=e.onError)(t,n))||\"string\"==typeof t)return t}function vn(e,t){var n=e.onShellError,r=e.onFatalError;n(t),r(t),null!==e.destination?(e.status=14,e.destination.destroy(t)):(e.status=13,e.fatalError=t)}function An(e,t){Nn(e,t.next,t.hoistables)}function Nn(e,t,n){for(;null!==t;){null!==n&&(et(t.hoistables,n),t.inheritedHoistables=n);var r=t.boundaries;if(null!==r){t.boundaries=null;for(var a=0;a<r.length;a++){var i=r[a];null!==n&&et(i.contentState,n),Qn(e,i,null,null)}}if(t.pendingTasks--,0<t.pendingTasks)break;n=t.hoistables,t=t.next}}function Cn(e,t){var n=t.boundaries;if(null!==n&&t.pendingTasks===n.length){for(var r=!0,a=0;a<n.length;a++){var i=n[a];if(1!==i.pendingTasks||i.parentFlushed||dn(0,i)){r=!1;break}}r&&Nn(e,t,t.hoistables)}}function xn(e){var t={pendingTasks:1,boundaries:null,hoistables:qe(),inheritedHoistables:null,together:!1,next:null};return null!==e&&0<e.pendingTasks&&(t.pendingTasks++,t.boundaries=[],e.next=t),t}function wn(e,t,n,r,a){var i=t.keyPath,s=t.treeContext,o=t.row;t.keyPath=n,n=r.length;var l=null;if(null!==t.replay){var c=t.replay.slots;if(null!==c&&\"object\"==typeof c)for(var u=0;u<n;u++){var d=\"backwards\"!==a&&\"unstable_legacy-backwards\"!==a?u:n-1-u,p=r[d];t.row=l=xn(l),t.treeContext=gt(s,n,d);var h=c[d];\"number\"==typeof h?(Dn(e,t,h,p,d),delete c[d]):Gn(e,t,p,d),0===--l.pendingTasks&&An(e,l)}else for(c=0;c<n;c++)d=r[u=\"backwards\"!==a&&\"unstable_legacy-backwards\"!==a?c:n-1-c],t.row=l=xn(l),t.treeContext=gt(s,n,u),Gn(e,t,d,u),0===--l.pendingTasks&&An(e,l)}else if(\"backwards\"!==a&&\"unstable_legacy-backwards\"!==a)for(a=0;a<n;a++)c=r[a],t.row=l=xn(l),t.treeContext=gt(s,n,a),Gn(e,t,c,a),0===--l.pendingTasks&&An(e,l);else{for(c=(a=t.blockedSegment).children.length,u=a.chunks.length,d=n-1;0<=d;d--){p=r[d],t.row=l=xn(l),t.treeContext=gt(s,n,d),h=yn(0,u,null,t.formatContext,0!==d||a.lastPushedText,!0),a.children.splice(c,0,h),t.blockedSegment=h;try{Gn(e,t,p,d),nt(h.chunks,e.renderState,h.lastPushedText,h.textEmbedded),h.status=1,0===--l.pendingTasks&&An(e,l)}catch(f){throw h.status=12===e.status?3:4,f}}t.blockedSegment=a,a.lastPushedText=!1}null!==o&&null!==l&&0<l.pendingTasks&&(o.pendingTasks++,l.next=o),t.treeContext=s,t.row=o,t.keyPath=i}function In(e,t,n,r,a,i){var s=t.thenableState;for(t.thenableState=null,At={},Nt=t,Ct=e,xt=n,Mt=Dt=0,Pt=-1,Lt=0,Ft=s,e=r(a,i);Rt;)Rt=!1,Mt=Dt=0,Pt=-1,Lt=0,Ut+=1,It=null,e=r(a,i);return $t(),e}function On(e,t,n,r,a,i,s){var o=!1;if(0!==i&&null!==e.formState){var l=t.blockedSegment;if(null!==l){o=!0,l=l.chunks;for(var c=0;c<i;c++)c===s?l.push(\"\\x3c!--F!--\\x3e\"):l.push(\"\\x3c!--F--\\x3e\")}}i=t.keyPath,t.keyPath=n,a?(n=t.treeContext,t.treeContext=gt(n,1,0),Gn(e,t,r,-1),t.treeContext=n):o?Gn(e,t,r,-1):Mn(e,t,r,-1),t.keyPath=i}function Rn(e,t,r,a,i,s){if(\"function\"==typeof a)if(a.prototype&&a.prototype.isReactComponent){var _=i;if(\"ref\"in i)for(var T in _={},i)\"ref\"!==T&&(_[T]=i[T]);var A=a.defaultProps;if(A)for(var C in _===i&&(_=N({},_,i)),A)void 0===_[C]&&(_[C]=A[C]);i=_,_=st,\"object\"==typeof(A=a.contextType)&&null!==A&&(_=A._currentValue2);var x=void 0!==(_=new a(i,_)).state?_.state:null;if(_.updater=ft,_.props=i,_.state=x,A={queue:[],replace:!1},_._reactInternals=A,s=a.contextType,_.context=\"object\"==typeof s&&null!==s?s._currentValue2:st,\"function\"==typeof(s=a.getDerivedStateFromProps)&&(x=null==(s=s(i,x))?x:N({},x,s),_.state=x),\"function\"!=typeof a.getDerivedStateFromProps&&\"function\"!=typeof _.getSnapshotBeforeUpdate&&(\"function\"==typeof _.UNSAFE_componentWillMount||\"function\"==typeof _.componentWillMount))if(a=_.state,\"function\"==typeof _.componentWillMount&&_.componentWillMount(),\"function\"==typeof _.UNSAFE_componentWillMount&&_.UNSAFE_componentWillMount(),a!==_.state&&ft.enqueueReplaceState(_,_.state,null),null!==A.queue&&0<A.queue.length)if(a=A.queue,s=A.replace,A.queue=null,A.replace=!1,s&&1===a.length)_.state=a[0];else{for(A=s?a[0]:_.state,x=!0,s=s?1:0;s<a.length;s++)null!=(C=\"function\"==typeof(C=a[s])?C.call(_,A,i,void 0):C)&&(x?(x=!1,A=N({},A,C)):N(A,C));_.state=A}else A.queue=null;if(a=_.render(),12===e.status)throw null;i=t.keyPath,t.keyPath=r,Mn(e,t,a,-1),t.keyPath=i}else{if(a=In(e,t,r,a,i,void 0),12===e.status)throw null;On(e,t,r,a,0!==Dt,Mt,Pt)}else{if(\"string\"!=typeof a){switch(a){case y:case l:case c:case o:return a=t.keyPath,t.keyPath=r,Mn(e,t,i.children,-1),void(t.keyPath=a);case E:return void(null===(a=t.blockedSegment)?\"hidden\"!==i.mode&&(a=t.keyPath,t.keyPath=r,Gn(e,t,i.children,-1),t.keyPath=a):\"hidden\"!==i.mode&&(e.renderState.generateStaticMarkup||a.chunks.push(\"\\x3c!--&--\\x3e\"),a.lastPushedText=!1,_=t.keyPath,t.keyPath=r,Gn(e,t,i.children,-1),t.keyPath=_,e.renderState.generateStaticMarkup||a.chunks.push(\"\\x3c!--/&--\\x3e\"),a.lastPushedText=!1));case f:e:{if(a=i.children,\"forwards\"===(i=i.revealOrder)||\"backwards\"===i||\"unstable_legacy-backwards\"===i){if(v(a)){wn(e,t,r,a,i);break e}if((_=S(a))&&(_=_.call(a))){if(!(A=_.next()).done){do{A=_.next()}while(!A.done);wn(e,t,r,a,i)}break e}}\"together\"===i?(i=t.keyPath,_=t.row,(A=t.row=xn(null)).boundaries=[],A.together=!0,t.keyPath=r,Mn(e,t,a,-1),0===--A.pendingTasks&&An(e,A),t.keyPath=i,t.row=_,null!==_&&0<A.pendingTasks&&(_.pendingTasks++,A.next=_)):(i=t.keyPath,t.keyPath=r,Mn(e,t,a,-1),t.keyPath=i)}return;case k:case b:throw Error(n(343));case h:e:if(null!==t.replay){a=t.keyPath,_=t.formatContext,A=t.row,t.keyPath=r,t.formatContext=Z(e.resumableState,_),t.row=null,r=i.children;try{Gn(e,t,r,-1)}finally{t.keyPath=a,t.formatContext=_,t.row=A}}else{a=t.keyPath,s=t.formatContext;var w=t.row,I=t.blockedBoundary;C=t.blockedPreamble;var O=t.hoistableState;T=t.blockedSegment;var R=i.fallback;i=i.children;var D=new Set,M=gn(e,t.row,D,null,null);null!==e.trackedPostpones&&(M.trackedContentKeyPath=r);var P=yn(0,T.chunks.length,M,t.formatContext,!1,!1);T.children.push(P),T.lastPushedText=!1;var L=yn(0,0,null,t.formatContext,!1,!1);if(L.parentFlushed=!0,null!==e.trackedPostpones){_=t.componentStack,x=[(A=[r[0],\"Suspense Fallback\",r[2]])[1],A[2],[],null],e.trackedPostpones.workingMap.set(A,x),M.trackedFallbackNode=x,t.blockedSegment=P,t.blockedPreamble=M.fallbackPreamble,t.keyPath=A,t.formatContext=X(e.resumableState,s),t.componentStack=kn(_),P.status=6;try{Gn(e,t,R,-1),nt(P.chunks,e.renderState,P.lastPushedText,P.textEmbedded),P.status=1}catch(F){throw P.status=12===e.status?3:4,F}finally{t.blockedSegment=T,t.blockedPreamble=C,t.keyPath=a,t.formatContext=s}_n(t=bn(e,null,i,-1,M,L,M.contentPreamble,M.contentState,t.abortSet,r,Z(e.resumableState,t.formatContext),t.context,t.treeContext,null,_)),e.pingedTasks.push(t)}else{t.blockedBoundary=M,t.blockedPreamble=M.contentPreamble,t.hoistableState=M.contentState,t.blockedSegment=L,t.keyPath=r,t.formatContext=Z(e.resumableState,s),t.row=null,L.status=6;try{if(Gn(e,t,i,-1),nt(L.chunks,e.renderState,L.lastPushedText,L.textEmbedded),L.status=1,Kn(M,L),0===M.pendingTasks&&0===M.status){if(M.status=1,!dn(0,M)){null!==w&&0===--w.pendingTasks&&An(e,w),0===e.pendingRootTasks&&t.blockedPreamble&&er(e);break e}}else null!==w&&w.together&&Cn(e,w)}catch(B){M.status=4,12===e.status?(L.status=3,_=e.fatalError):(L.status=4,_=B),x=Sn(e,_,A=Tn(t.componentStack)),M.errorDigest=x,Un(e,M)}finally{t.blockedBoundary=I,t.blockedPreamble=C,t.hoistableState=O,t.blockedSegment=T,t.keyPath=a,t.formatContext=s,t.row=w}_n(t=bn(e,null,R,-1,I,P,M.fallbackPreamble,M.fallbackState,D,[r[0],\"Suspense Fallback\",r[2]],X(e.resumableState,t.formatContext),t.context,t.treeContext,t.row,kn(t.componentStack))),e.pingedTasks.push(t)}}return}if(\"object\"==typeof a&&null!==a)switch(a.$$typeof){case p:if(\"ref\"in i)for(R in _={},i)\"ref\"!==R&&(_[R]=i[R]);else _=i;return void On(e,t,r,a=In(e,t,r,a.render,_,s),0!==Dt,Mt,Pt);case m:return void Rn(e,t,r,a.type,i,s);case d:if(A=i.children,_=t.keyPath,i=i.value,x=a._currentValue2,a._currentValue2=i,ot=a={parent:s=ot,depth:null===s?0:s.depth+1,context:a,parentValue:x,value:i},t.context=a,t.keyPath=r,Mn(e,t,A,-1),null===(e=ot))throw Error(n(403));return e.context._currentValue2=e.parentValue,e=ot=e.parent,t.context=e,void(t.keyPath=_);case u:return a=(i=i.children)(a._context._currentValue2),i=t.keyPath,t.keyPath=r,Mn(e,t,a,-1),void(t.keyPath=i);case g:if(a=(_=a._init)(a._payload),12===e.status)throw null;return void Rn(e,t,r,a,i,s)}throw Error(n(130,null==a?a:typeof a,\"\"))}if(null===(_=t.blockedSegment))_=i.children,A=t.formatContext,x=t.keyPath,t.formatContext=K(A,a,i),t.keyPath=r,Gn(e,t,_,-1),t.formatContext=A,t.keyPath=x;else{if(x=Te(_.chunks,a,i,e.resumableState,e.renderState,t.blockedPreamble,t.hoistableState,t.formatContext,_.lastPushedText),_.lastPushedText=!1,A=t.formatContext,s=t.keyPath,t.keyPath=r,3===(t.formatContext=K(A,a,i)).insertionMode){r=yn(0,0,null,t.formatContext,!1,!1),_.preambleChildren.push(r),t.blockedSegment=r;try{r.status=6,Gn(e,t,x,-1),nt(r.chunks,e.renderState,r.lastPushedText,r.textEmbedded),r.status=1}finally{t.blockedSegment=_}}else Gn(e,t,x,-1);t.formatContext=A,t.keyPath=s;e:{switch(t=_.chunks,e=e.resumableState,a){case\"title\":case\"style\":case\"script\":case\"area\":case\"base\":case\"br\":case\"col\":case\"embed\":case\"hr\":case\"img\":case\"input\":case\"keygen\":case\"link\":case\"meta\":case\"param\":case\"source\":case\"track\":case\"wbr\":break e;case\"body\":if(1>=A.insertionMode){e.hasBody=!0;break e}break;case\"html\":if(0===A.insertionMode){e.hasHtml=!0;break e}break;case\"head\":if(1>=A.insertionMode)break e}t.push(ve(a))}_.lastPushedText=!1}}}function Dn(e,t,n,r,a){var i=t.replay,s=t.blockedBoundary,o=yn(0,0,null,t.formatContext,!1,!1);o.id=n,o.parentFlushed=!0;try{t.replay=null,t.blockedSegment=o,Gn(e,t,r,a),o.status=1,null===s?e.completedRootSegment=o:(Kn(s,o),s.parentFlushed&&e.partialBoundaries.push(s))}finally{t.replay=i,t.blockedSegment=null}}function Mn(e,t,n,r){null!==t.replay&&\"number\"==typeof t.replay.slots?Dn(e,t,t.replay.slots,n,r):(t.node=n,t.childIndex=r,n=t.componentStack,_n(t),Pn(e,t),t.componentStack=n)}function Pn(e,t){var i=t.node,s=t.childIndex;if(null!==i){if(\"object\"==typeof i){switch(i.$$typeof){case r:var o=i.type,l=i.key,c=i.props,u=void 0!==(i=c.ref)?i:null,p=it(o),f=null==l?-1===s?0:s:l;if(l=[t.keyPath,p,f],null!==t.replay)e:{var m=t.replay;for(s=m.nodes,i=0;i<s.length;i++){var b=s[i];if(f===b[1]){if(4===b.length){if(null!==p&&p!==b[0])throw Error(n(490,b[0],p));var E=b[2];p=b[3],f=t.node,t.replay={nodes:E,slots:p,pendingTasks:1};try{if(Rn(e,t,l,o,c,u),1===t.replay.pendingTasks&&0<t.replay.nodes.length)throw Error(n(488));t.replay.pendingTasks--}catch(I){if(\"object\"==typeof I&&null!==I&&(I===kt||\"function\"==typeof I.then))throw t.node===f?t.replay=m:s.splice(i,1),I;t.replay.pendingTasks--,c=Tn(t.componentStack),$n(l=e,e=t.blockedBoundary,E,p,o=I,c=Sn(l,o,c))}t.replay=m}else{if(o!==h)throw Error(n(490,\"Suspense\",it(o)||\"Unknown\"));t:{m=void 0,o=b[5],u=b[2],p=b[3],f=null===b[4]?[]:b[4][2],b=null===b[4]?null:b[4][3];var y=t.keyPath,_=t.formatContext,k=t.row,T=t.replay,A=t.blockedBoundary,N=t.hoistableState,C=c.children,x=c.fallback,w=new Set;(c=gn(e,t.row,w,null,null)).parentFlushed=!0,c.rootSegmentID=o,t.blockedBoundary=c,t.hoistableState=c.contentState,t.keyPath=l,t.formatContext=Z(e.resumableState,_),t.row=null,t.replay={nodes:u,slots:p,pendingTasks:1};try{if(Gn(e,t,C,-1),1===t.replay.pendingTasks&&0<t.replay.nodes.length)throw Error(n(488));if(t.replay.pendingTasks--,0===c.pendingTasks&&0===c.status){c.status=1,e.completedBoundaries.push(c);break t}}catch(O){c.status=4,m=Sn(e,O,E=Tn(t.componentStack)),c.errorDigest=m,t.replay.pendingTasks--,e.clientRenderedBoundaries.push(c)}finally{t.blockedBoundary=A,t.hoistableState=N,t.replay=T,t.keyPath=y,t.formatContext=_,t.row=k}_n(E=En(e,null,{nodes:f,slots:b,pendingTasks:0},x,-1,A,c.fallbackState,w,[l[0],\"Suspense Fallback\",l[2]],X(e.resumableState,t.formatContext),t.context,t.treeContext,t.row,kn(t.componentStack))),e.pingedTasks.push(E)}}s.splice(i,1);break e}}}else Rn(e,t,l,o,c,u);return;case a:throw Error(n(257));case g:if(i=(E=i._init)(i._payload),12===e.status)throw null;return void Mn(e,t,i,s)}if(v(i))return void Ln(e,t,i,s);if((E=S(i))&&(E=E.call(i))){if(!(i=E.next()).done){c=[];do{c.push(i.value),i=E.next()}while(!i.done);Ln(e,t,c,s)}return}if(\"function\"==typeof i.then)return t.thenableState=null,Mn(e,t,Jt(i),s);if(i.$$typeof===d)return Mn(e,t,i._currentValue2,s);throw s=Object.prototype.toString.call(i),Error(n(31,\"[object Object]\"===s?\"object with keys {\"+Object.keys(i).join(\", \")+\"}\":s))}\"string\"==typeof i?null!==(s=t.blockedSegment)&&(s.lastPushedText=tt(s.chunks,i,e.renderState,s.lastPushedText)):\"number\"!=typeof i&&\"bigint\"!=typeof i||null!==(s=t.blockedSegment)&&(s.lastPushedText=tt(s.chunks,\"\"+i,e.renderState,s.lastPushedText))}}function Ln(e,t,r,a){var i=t.keyPath;if(-1===a||(t.keyPath=[t.keyPath,\"Fragment\",a],null===t.replay)){if(s=t.treeContext,o=r.length,null!==t.replay&&(null!==(l=t.replay.slots)&&\"object\"==typeof l)){for(a=0;a<o;a++)c=r[a],t.treeContext=gt(s,o,a),\"number\"==typeof(u=l[a])?(Dn(e,t,u,c,a),delete l[a]):Gn(e,t,c,a);return t.treeContext=s,void(t.keyPath=i)}for(l=0;l<o;l++)a=r[l],t.treeContext=gt(s,o,l),Gn(e,t,a,l);t.treeContext=s,t.keyPath=i}else{for(var s=t.replay,o=s.nodes,l=0;l<o.length;l++){var c=o[l];if(c[1]===a){a=c[2],c=c[3],t.replay={nodes:a,slots:c,pendingTasks:1};try{if(Ln(e,t,r,-1),1===t.replay.pendingTasks&&0<t.replay.nodes.length)throw Error(n(488));t.replay.pendingTasks--}catch(d){if(\"object\"==typeof d&&null!==d&&(d===kt||\"function\"==typeof d.then))throw d;t.replay.pendingTasks--,r=Tn(t.componentStack);var u=t.blockedBoundary;$n(e,u,a,c,d,r=Sn(e,d,r))}t.replay=s,o.splice(l,1);break}}t.keyPath=i}}function Fn(e,t,r){if(r.status=5,r.rootSegmentID=e.nextSegmentId++,null===(e=r.trackedContentKeyPath))throw Error(n(486));var a=r.trackedFallbackNode,i=t.workingMap.get(e);return void 0===i?(r=[e[1],e[2],[],null,a,r.rootSegmentID],t.workingMap.set(e,r),dr(r,e[0],t),r):(i[4]=a,i[5]=r.rootSegmentID,i)}function Bn(e,t,r,a){a.status=5;var i=r.keyPath,s=r.blockedBoundary;if(null===s)a.id=e.nextSegmentId++,t.rootSlots=a.id,null!==e.completedRootSegment&&(e.completedRootSegment.status=5);else{if(null!==s&&0===s.status){var o=Fn(e,t,s);if(s.trackedContentKeyPath===i&&-1===r.childIndex)return-1===a.id&&(a.id=a.parentFlushed?s.rootSegmentID:e.nextSegmentId++),void(o[3]=a.id)}if(-1===a.id&&(a.id=a.parentFlushed&&null!==s?s.rootSegmentID:e.nextSegmentId++),-1===r.childIndex)null===i?t.rootSlots=a.id:void 0===(r=t.workingMap.get(i))?dr(r=[i[1],i[2],[],a.id],i[0],t):r[3]=a.id;else{if(null===i){if(null===(e=t.rootSlots))e=t.rootSlots={};else if(\"number\"==typeof e)throw Error(n(491))}else if(void 0===(o=(s=t.workingMap).get(i)))e={},o=[i[1],i[2],[],e],s.set(i,o),dr(o,i[0],t);else if(null===(e=o[3]))e=o[3]={};else if(\"number\"==typeof e)throw Error(n(491));e[r.childIndex]=a.id}}}function Un(e,t){null!==(e=e.trackedPostpones)&&(null!==(t=t.trackedContentKeyPath)&&(void 0!==(t=e.workingMap.get(t))&&(t.length=4,t[2]=[],t[3]=null)))}function Hn(e,t,n){return En(e,n,t.replay,t.node,t.childIndex,t.blockedBoundary,t.hoistableState,t.abortSet,t.keyPath,t.formatContext,t.context,t.treeContext,t.row,t.componentStack)}function zn(e,t,n){var r=t.blockedSegment,a=yn(0,r.chunks.length,null,t.formatContext,r.lastPushedText,!0);return r.children.push(a),r.lastPushedText=!1,bn(e,n,t.node,t.childIndex,t.blockedBoundary,a,t.blockedPreamble,t.hoistableState,t.abortSet,t.keyPath,t.formatContext,t.context,t.treeContext,t.row,t.componentStack)}function Gn(e,t,n,r){var a=t.formatContext,i=t.context,s=t.keyPath,o=t.treeContext,l=t.componentStack,c=t.blockedSegment;if(null===c){c=t.replay;try{return Mn(e,t,n,r)}catch(p){if($t(),n=p===kt?St():p,12!==e.status&&\"object\"==typeof n&&null!==n){if(\"function\"==typeof n.then)return e=Hn(e,t,r=p===kt?jt():null).ping,n.then(e,e),t.formatContext=a,t.context=i,t.keyPath=s,t.treeContext=o,t.componentStack=l,t.replay=c,void ht(i);if(\"Maximum call stack size exceeded\"===n.message)return n=Hn(e,t,n=p===kt?jt():null),e.pingedTasks.push(n),t.formatContext=a,t.context=i,t.keyPath=s,t.treeContext=o,t.componentStack=l,t.replay=c,void ht(i)}}}else{var u=c.children.length,d=c.chunks.length;try{return Mn(e,t,n,r)}catch(h){if($t(),c.children.length=u,c.chunks.length=d,n=h===kt?St():h,12!==e.status&&\"object\"==typeof n&&null!==n){if(\"function\"==typeof n.then)return c=n,e=zn(e,t,n=h===kt?jt():null).ping,c.then(e,e),t.formatContext=a,t.context=i,t.keyPath=s,t.treeContext=o,t.componentStack=l,void ht(i);if(\"Maximum call stack size exceeded\"===n.message)return c=zn(e,t,c=h===kt?jt():null),e.pingedTasks.push(c),t.formatContext=a,t.context=i,t.keyPath=s,t.treeContext=o,t.componentStack=l,void ht(i)}}}throw t.formatContext=a,t.context=i,t.keyPath=s,t.treeContext=o,ht(i),n}function jn(e){var t=e.blockedBoundary,n=e.blockedSegment;null!==n&&(n.status=3,Qn(this,t,e.row,n))}function $n(e,t,r,a,i,s){for(var o=0;o<r.length;o++){var l=r[o];if(4===l.length)$n(e,t,l[2],l[3],i,s);else{l=l[5];var c=e,u=s,d=gn(c,null,new Set,null,null);d.parentFlushed=!0,d.rootSegmentID=l,d.status=4,d.errorDigest=u,d.parentFlushed&&c.clientRenderedBoundaries.push(d)}}if(r.length=0,null!==a){if(null===t)throw Error(n(487));if(4!==t.status&&(t.status=4,t.errorDigest=s,t.parentFlushed&&e.clientRenderedBoundaries.push(t)),\"object\"==typeof a)for(var p in a)delete a[p]}}function qn(e,t,n){var r=e.blockedBoundary,a=e.blockedSegment;if(null!==a){if(6===a.status)return;a.status=3}var i=Tn(e.componentStack);if(null===r){if(13!==t.status&&14!==t.status){if(null===(r=e.replay))return void(null!==t.trackedPostpones&&null!==a?(r=t.trackedPostpones,Sn(t,n,i),Bn(t,r,e,a),Qn(t,null,e.row,a)):(Sn(t,n,i),vn(t,n)));r.pendingTasks--,0===r.pendingTasks&&0<r.nodes.length&&(a=Sn(t,n,i),$n(t,null,r.nodes,r.slots,n,a)),t.pendingRootTasks--,0===t.pendingRootTasks&&Wn(t)}}else{var s=t.trackedPostpones;if(4!==r.status){if(null!==s&&null!==a)return Sn(t,n,i),Bn(t,s,e,a),r.fallbackAbortableTasks.forEach(function(e){return qn(e,t,n)}),r.fallbackAbortableTasks.clear(),Qn(t,r,e.row,a);r.status=4,a=Sn(t,n,i),r.status=4,r.errorDigest=a,Un(t,r),r.parentFlushed&&t.clientRenderedBoundaries.push(r)}r.pendingTasks--,null!==(a=r.row)&&0===--a.pendingTasks&&An(t,a),r.fallbackAbortableTasks.forEach(function(e){return qn(e,t,n)}),r.fallbackAbortableTasks.clear()}null!==(e=e.row)&&0===--e.pendingTasks&&An(t,e),t.allPendingTasks--,0===t.allPendingTasks&&Vn(t)}function Yn(e,t){try{var n=e.renderState,r=n.onHeaders;if(r){var a=n.headers;if(a){n.headers=null;var i=a.preconnects;if(a.fontPreloads&&(i&&(i+=\", \"),i+=a.fontPreloads),a.highImagePreloads&&(i&&(i+=\", \"),i+=a.highImagePreloads),!t){var s=n.styles.values(),o=s.next();e:for(;0<a.remainingCapacity&&!o.done;o=s.next())for(var l=o.value.sheets.values(),c=l.next();0<a.remainingCapacity&&!c.done;c=l.next()){var u=c.value,d=u.props,p=d.href,h=u.props,f=We(h.href,\"style\",{crossOrigin:h.crossOrigin,integrity:h.integrity,nonce:h.nonce,type:h.type,fetchPriority:h.fetchPriority,referrerPolicy:h.referrerPolicy,media:h.media});if(!(0<=(a.remainingCapacity-=f.length+2)))break e;n.resets.style[p]=$,i&&(i+=\", \"),i+=f,n.resets.style[p]=\"string\"==typeof d.crossOrigin||\"string\"==typeof d.integrity?[d.crossOrigin,d.integrity]:$}}r(i?{Link:i}:{})}}}catch(m){Sn(e,m,{})}}function Wn(e){null===e.trackedPostpones&&Yn(e,!0),null===e.trackedPostpones&&er(e),e.onShellError=_t,(e=e.onShellReady)()}function Vn(e){Yn(e,null===e.trackedPostpones||(null===e.completedRootSegment||5!==e.completedRootSegment.status)),er(e),(e=e.onAllReady)()}function Kn(e,t){if(0===t.chunks.length&&1===t.children.length&&null===t.children[0].boundary&&-1===t.children[0].id){var n=t.children[0];n.id=t.id,n.parentFlushed=!0,1!==n.status&&3!==n.status&&4!==n.status||Kn(e,n)}else e.completedSegments.push(t)}function Qn(e,t,r,a){if(null!==r&&(0===--r.pendingTasks?An(e,r):r.together&&Cn(e,r)),e.allPendingTasks--,null===t){if(null!==a&&a.parentFlushed){if(null!==e.completedRootSegment)throw Error(n(389));e.completedRootSegment=a}e.pendingRootTasks--,0===e.pendingRootTasks&&Wn(e)}else if(t.pendingTasks--,4!==t.status)if(0===t.pendingTasks){if(0===t.status&&(t.status=1),null!==a&&a.parentFlushed&&(1===a.status||3===a.status)&&Kn(t,a),t.parentFlushed&&e.completedBoundaries.push(t),1===t.status)null!==(r=t.row)&&et(r.hoistables,t.contentState),dn(0,t)||(t.fallbackAbortableTasks.forEach(jn,e),t.fallbackAbortableTasks.clear(),null!==r&&0===--r.pendingTasks&&An(e,r)),0===e.pendingRootTasks&&null===e.trackedPostpones&&null!==t.contentPreamble&&er(e);else if(5===t.status&&null!==(t=t.row)){if(null!==e.trackedPostpones){r=e.trackedPostpones;var i=t.next;if(null!==i&&null!==(a=i.boundaries))for(i.boundaries=null,i=0;i<a.length;i++){var s=a[i];Fn(e,r,s),Qn(e,s,null,null)}}0===--t.pendingTasks&&An(e,t)}}else null===a||!a.parentFlushed||1!==a.status&&3!==a.status||(Kn(t,a),1===t.completedSegments.length&&t.parentFlushed&&e.partialBoundaries.push(t)),null!==(t=t.row)&&t.together&&Cn(e,t);0===e.allPendingTasks&&Vn(e)}function Xn(e){if(14!==e.status&&13!==e.status){var t=ot,r=H.H;H.H=rn;var a=H.A;H.A=sn;var i=fn;fn=e;var s=an;an=e.resumableState;try{var o,l=e.pingedTasks;for(o=0;o<l.length;o++){var c=l[o],u=e,d=c.blockedSegment;if(null===d){var p=u;if(0!==c.replay.pendingTasks){ht(c.context);try{if(\"number\"==typeof c.replay.slots?Dn(p,c,c.replay.slots,c.node,c.childIndex):Pn(p,c),1===c.replay.pendingTasks&&0<c.replay.nodes.length)throw Error(n(488));c.replay.pendingTasks--,c.abortSet.delete(c),Qn(p,c.blockedBoundary,c.row,null)}catch(w){$t();var h=w===kt?St():w;if(\"object\"==typeof h&&null!==h&&\"function\"==typeof h.then){var f=c.ping;h.then(f,f),c.thenableState=w===kt?jt():null}else{c.replay.pendingTasks--,c.abortSet.delete(c);var m=Tn(c.componentStack);u=void 0;var g=p,b=c.blockedBoundary,E=12===p.status?p.fatalError:h;$n(g,b,c.replay.nodes,c.replay.slots,E,u=Sn(g,E,m)),p.pendingRootTasks--,0===p.pendingRootTasks&&Wn(p),p.allPendingTasks--,0===p.allPendingTasks&&Vn(p)}}}}else if(p=void 0,0===(g=d).status){g.status=6,ht(c.context);var y=g.children.length,_=g.chunks.length;try{Pn(u,c),nt(g.chunks,u.renderState,g.lastPushedText,g.textEmbedded),c.abortSet.delete(c),g.status=1,Qn(u,c.blockedBoundary,c.row,g)}catch(w){$t(),g.children.length=y,g.chunks.length=_;var k=w===kt?St():12===u.status?u.fatalError:w;if(12===u.status&&null!==u.trackedPostpones){var T=u.trackedPostpones,S=Tn(c.componentStack);c.abortSet.delete(c),Sn(u,k,S),Bn(u,T,c,g),Qn(u,c.blockedBoundary,c.row,g)}else if(\"object\"==typeof k&&null!==k&&\"function\"==typeof k.then){g.status=0,c.thenableState=w===kt?jt():null;var v=c.ping;k.then(v,v)}else{var A=Tn(c.componentStack);c.abortSet.delete(c),g.status=4;var N=c.blockedBoundary,C=c.row;if(null!==C&&0===--C.pendingTasks&&An(u,C),u.allPendingTasks--,p=Sn(u,k,A),null===N)vn(u,k);else if(N.pendingTasks--,4!==N.status){N.status=4,N.errorDigest=p,Un(u,N);var x=N.row;null!==x&&0===--x.pendingTasks&&An(u,x),N.parentFlushed&&u.clientRenderedBoundaries.push(N),0===u.pendingRootTasks&&null===u.trackedPostpones&&null!==N.contentPreamble&&er(u)}0===u.allPendingTasks&&Vn(u)}}}}l.splice(0,o),null!==e.destination&&lr(e,e.destination)}catch(I){Sn(e,I,{}),vn(e,I)}finally{an=s,H.H=r,H.A=a,r===rn&&ht(t),fn=i}}}function Zn(e,t,n){t.preambleChildren.length&&n.push(t.preambleChildren);for(var r=!1,a=0;a<t.children.length;a++)r=Jn(e,t.children[a],n)||r;return r}function Jn(e,t,r){var a=t.boundary;if(null===a)return Zn(e,t,r);var i=a.contentPreamble,s=a.fallbackPreamble;if(null===i||null===s)return!1;switch(a.status){case 1:if(Ae(e.renderState,i),e.byteSize+=a.byteSize,!(t=a.completedSegments[0]))throw Error(n(391));return Zn(e,t,r);case 5:if(null!==e.trackedPostpones)return!0;case 4:if(1===t.status)return Ae(e.renderState,s),Zn(e,t,r);default:return!0}}function er(e){if(e.completedRootSegment&&null===e.completedPreambleSegments){var t=[],n=e.byteSize,r=Jn(e,e.completedRootSegment,t),a=e.renderState.preamble;!1===r||a.headChunks&&a.bodyChunks?e.completedPreambleSegments=t:e.byteSize=n}}function tr(e,t,r,a){switch(r.parentFlushed=!0,r.status){case 0:r.id=e.nextSegmentId++;case 5:return a=r.id,r.lastPushedText=!1,r.textEmbedded=!1,e=e.renderState,t.push('<template id=\"'),t.push(e.placeholderPrefix),e=a.toString(16),t.push(e),t.push('\"></template>');case 1:r.status=2;var i=!0,s=r.chunks,o=0;r=r.children;for(var l=0;l<r.length;l++){for(i=r[l];o<i.index;o++)t.push(s[o]);i=rr(e,t,i,a)}for(;o<s.length-1;o++)t.push(s[o]);return o<s.length&&(i=t.push(s[o])),i;case 3:return!0;default:throw Error(n(390))}}var nr=0;function rr(e,t,r,a){var i=r.boundary;if(null===i)return tr(e,t,r,a);if(i.parentFlushed=!0,4===i.status){var s=i.row;return null!==s&&0===--s.pendingTasks&&An(e,s),e.renderState.generateStaticMarkup||(i=i.errorDigest,t.push(\"\\x3c!--$!--\\x3e\"),t.push(\"<template\"),i&&(t.push(' data-dgst=\"'),i=P(i),t.push(i),t.push('\"')),t.push(\"></template>\")),tr(e,t,r,a),e=!!e.renderState.generateStaticMarkup||t.push(\"\\x3c!--/$--\\x3e\")}if(1!==i.status)return 0===i.status&&(i.rootSegmentID=e.nextSegmentId++),0<i.completedSegments.length&&e.partialBoundaries.push(i),Ce(t,e.renderState,i.rootSegmentID),a&&et(a,i.fallbackState),tr(e,t,r,a),t.push(\"\\x3c!--/$--\\x3e\");if(!or&&dn(0,i)&&nr+i.byteSize>e.progressiveChunkSize)return i.rootSegmentID=e.nextSegmentId++,e.completedBoundaries.push(i),Ce(t,e.renderState,i.rootSegmentID),tr(e,t,r,a),t.push(\"\\x3c!--/$--\\x3e\");if(nr+=i.byteSize,a&&et(a,i.contentState),null!==(r=i.row)&&dn(0,i)&&0===--r.pendingTasks&&An(e,r),e.renderState.generateStaticMarkup||t.push(\"\\x3c!--$--\\x3e\"),1!==(r=i.completedSegments).length)throw Error(n(391));return rr(e,t,r[0],a),e=!!e.renderState.generateStaticMarkup||t.push(\"\\x3c!--/$--\\x3e\")}function ar(e,t,r,a){return function(e,t,r,a){switch(r.insertionMode){case 0:case 1:case 3:case 2:return e.push('<div hidden id=\"'),e.push(t.segmentPrefix),t=a.toString(16),e.push(t),e.push('\">');case 4:return e.push('<svg aria-hidden=\"true\" style=\"display:none\" id=\"'),e.push(t.segmentPrefix),t=a.toString(16),e.push(t),e.push('\">');case 5:return e.push('<math aria-hidden=\"true\" style=\"display:none\" id=\"'),e.push(t.segmentPrefix),t=a.toString(16),e.push(t),e.push('\">');case 6:return e.push('<table hidden id=\"'),e.push(t.segmentPrefix),t=a.toString(16),e.push(t),e.push('\">');case 7:return e.push('<table hidden><tbody id=\"'),e.push(t.segmentPrefix),t=a.toString(16),e.push(t),e.push('\">');case 8:return e.push('<table hidden><tr id=\"'),e.push(t.segmentPrefix),t=a.toString(16),e.push(t),e.push('\">');case 9:return e.push('<table hidden><colgroup id=\"'),e.push(t.segmentPrefix),t=a.toString(16),e.push(t),e.push('\">');default:throw Error(n(397))}}(t,e.renderState,r.parentFormatContext,r.id),rr(e,t,r,a),function(e,t){switch(t.insertionMode){case 0:case 1:case 3:case 2:return e.push(\"</div>\");case 4:return e.push(\"</svg>\");case 5:return e.push(\"</math>\");case 6:return e.push(\"</table>\");case 7:return e.push(\"</tbody></table>\");case 8:return e.push(\"</tr></table>\");case 9:return e.push(\"</colgroup></table>\");default:throw Error(n(397))}}(t,r.parentFormatContext)}function ir(e,t,r){nr=r.byteSize;for(var a=r.completedSegments,i=0;i<a.length;i++)sr(e,t,r,a[i]);a.length=0,null!==(a=r.row)&&dn(0,r)&&0===--a.pendingTasks&&An(e,a),Le(t,r.contentState,e.renderState),a=e.resumableState,e=e.renderState,i=r.rootSegmentID,r=r.contentState;var s=e.stylesToHoist;return e.stylesToHoist=!1,t.push(e.startInlineScript),t.push(\">\"),s?(!(4&a.instructions)&&(a.instructions|=4,t.push('$RX=function(b,c,d,e,f){var a=document.getElementById(b);a&&(b=a.previousSibling,b.data=\"$!\",a=a.dataset,c&&(a.dgst=c),d&&(a.msg=d),e&&(a.stck=e),f&&(a.cstck=f),b._reactRetry&&b._reactRetry())};')),!(2&a.instructions)&&(a.instructions|=2,t.push('$RB=[];$RV=function(a){$RT=performance.now();for(var b=0;b<a.length;b+=2){var c=a[b],e=a[b+1];null!==e.parentNode&&e.parentNode.removeChild(e);var f=c.parentNode;if(f){var g=c.previousSibling,h=0;do{if(c&&8===c.nodeType){var d=c.data;if(\"/$\"===d||\"/&\"===d)if(0===h)break;else h--;else\"$\"!==d&&\"$?\"!==d&&\"$~\"!==d&&\"$!\"!==d&&\"&\"!==d||h++}d=c.nextSibling;f.removeChild(c);c=d}while(c);for(;e.firstChild;)f.insertBefore(e.firstChild,c);g.data=\"$\";g._reactRetry&&requestAnimationFrame(g._reactRetry)}}a.length=0};\\n$RC=function(a,b){if(b=document.getElementById(b))(a=document.getElementById(a))?(a.previousSibling.data=\"$~\",$RB.push(a,b),2===$RB.length&&(\"number\"!==typeof $RT?requestAnimationFrame($RV.bind(null,$RB)):(a=performance.now(),setTimeout($RV.bind(null,$RB),2300>a&&2E3<a?2300-a:$RT+300-a)))):b.parentNode.removeChild(b)};')),8&a.instructions?t.push('$RR(\"'):(a.instructions|=8,t.push('$RM=new Map;$RR=function(n,w,p){function u(q){this._p=null;q()}for(var r=new Map,t=document,h,b,e=t.querySelectorAll(\"link[data-precedence],style[data-precedence]\"),v=[],k=0;b=e[k++];)\"not all\"===b.getAttribute(\"media\")?v.push(b):(\"LINK\"===b.tagName&&$RM.set(b.getAttribute(\"href\"),b),r.set(b.dataset.precedence,h=b));e=0;b=[];var l,a;for(k=!0;;){if(k){var f=p[e++];if(!f){k=!1;e=0;continue}var c=!1,m=0;var d=f[m++];if(a=$RM.get(d)){var g=a._p;c=!0}else{a=t.createElement(\"link\");a.href=d;a.rel=\\n\"stylesheet\";for(a.dataset.precedence=l=f[m++];g=f[m++];)a.setAttribute(g,f[m++]);g=a._p=new Promise(function(q,x){a.onload=u.bind(a,q);a.onerror=u.bind(a,x)});$RM.set(d,a)}d=a.getAttribute(\"media\");!g||d&&!matchMedia(d).matches||b.push(g);if(c)continue}else{a=v[e++];if(!a)break;l=a.getAttribute(\"data-precedence\");a.removeAttribute(\"media\")}c=r.get(l)||h;c===h&&(h=a);r.set(l,a);c?c.parentNode.insertBefore(a,c.nextSibling):(c=t.head,c.insertBefore(a,c.firstChild))}if(p=document.getElementById(n))p.previousSibling.data=\\n\"$~\";Promise.all(b).then($RC.bind(null,n,w),$RX.bind(null,n,\"CSS failed to load\"))};$RR(\"'))):(!(2&a.instructions)&&(a.instructions|=2,t.push('$RB=[];$RV=function(a){$RT=performance.now();for(var b=0;b<a.length;b+=2){var c=a[b],e=a[b+1];null!==e.parentNode&&e.parentNode.removeChild(e);var f=c.parentNode;if(f){var g=c.previousSibling,h=0;do{if(c&&8===c.nodeType){var d=c.data;if(\"/$\"===d||\"/&\"===d)if(0===h)break;else h--;else\"$\"!==d&&\"$?\"!==d&&\"$~\"!==d&&\"$!\"!==d&&\"&\"!==d||h++}d=c.nextSibling;f.removeChild(c);c=d}while(c);for(;e.firstChild;)f.insertBefore(e.firstChild,c);g.data=\"$\";g._reactRetry&&requestAnimationFrame(g._reactRetry)}}a.length=0};\\n$RC=function(a,b){if(b=document.getElementById(b))(a=document.getElementById(a))?(a.previousSibling.data=\"$~\",$RB.push(a,b),2===$RB.length&&(\"number\"!==typeof $RT?requestAnimationFrame($RV.bind(null,$RB)):(a=performance.now(),setTimeout($RV.bind(null,$RB),2300>a&&2E3<a?2300-a:$RT+300-a)))):b.parentNode.removeChild(b)};')),t.push('$RC(\"')),a=i.toString(16),t.push(e.boundaryPrefix),t.push(a),t.push('\",\"'),t.push(e.segmentPrefix),t.push(a),s?(t.push('\",'),function(e,t){e.push(\"[\");var r=\"[\";t.stylesheets.forEach(function(t){if(2!==t.state)if(3===t.state)e.push(r),t=Oe(\"\"+t.props.href),e.push(t),e.push(\"]\"),r=\",[\";else{e.push(r);var a=t.props[\"data-precedence\"],i=t.props,s=U(\"\"+t.props.href);for(var o in s=Oe(s),e.push(s),a=\"\"+a,e.push(\",\"),a=Oe(a),e.push(a),i)if(C.call(i,o)&&null!=(a=i[o]))switch(o){case\"href\":case\"rel\":case\"precedence\":case\"data-precedence\":break;case\"children\":case\"dangerouslySetInnerHTML\":throw Error(n(399,\"link\"));default:$e(e,o,a)}e.push(\"]\"),r=\",[\",t.state=3}}),e.push(\"]\")}(t,r)):t.push('\"'),r=t.push(\")<\\/script>\"),Ne(t,e)&&r}function sr(e,t,r,a){if(2===a.status)return!0;var i=r.contentState,s=a.id;if(-1===s){if(-1===(a.id=r.rootSegmentID))throw Error(n(392));return ar(e,t,a,i)}return s===r.rootSegmentID?ar(e,t,a,i):(ar(e,t,a,i),r=e.resumableState,e=e.renderState,t.push(e.startInlineScript),t.push(\">\"),1&r.instructions?t.push('$RS(\"'):(r.instructions|=1,t.push('$RS=function(a,b){a=document.getElementById(a);b=document.getElementById(b);for(a.parentNode.removeChild(a);a.firstChild;)b.parentNode.insertBefore(a.firstChild,b);b.parentNode.removeChild(b)};$RS(\"')),t.push(e.segmentPrefix),s=s.toString(16),t.push(s),t.push('\",\"'),t.push(e.placeholderPrefix),t.push(s),t=t.push('\")<\\/script>'))}var or=!1;function lr(e,t){try{if(!(0<e.pendingRootTasks)){var n,r=e.completedRootSegment;if(null!==r){if(5===r.status)return;var a=e.completedPreambleSegments;if(null===a)return;nr=e.byteSize;var i,s=e.resumableState,o=e.renderState,l=o.preamble,c=l.htmlChunks,u=l.headChunks;if(c){for(i=0;i<c.length;i++)t.push(c[i]);if(u)for(i=0;i<u.length;i++)t.push(u[i]);else{var d=ke(\"head\");t.push(d),t.push(\">\")}}else if(u)for(i=0;i<u.length;i++)t.push(u[i]);var p=o.charsetChunks;for(i=0;i<p.length;i++)t.push(p[i]);p.length=0,o.preconnects.forEach(Fe,t),o.preconnects.clear();var h=o.viewportChunks;for(i=0;i<h.length;i++)t.push(h[i]);h.length=0,o.fontPreloads.forEach(Fe,t),o.fontPreloads.clear(),o.highImagePreloads.forEach(Fe,t),o.highImagePreloads.clear(),q=o,o.styles.forEach(He,t),q=null;var f=o.importMapChunks;for(i=0;i<f.length;i++)t.push(f[i]);f.length=0,o.bootstrapScripts.forEach(Fe,t),o.scripts.forEach(Fe,t),o.scripts.clear(),o.bulkPreloads.forEach(Fe,t),o.bulkPreloads.clear(),s.instructions|=32;var m=o.hoistableChunks;for(i=0;i<m.length;i++)t.push(m[i]);for(s=m.length=0;s<a.length;s++){var g=a[s];for(o=0;o<g.length;o++)rr(e,t,g[o],null)}var b=e.renderState.preamble,E=b.headChunks;if(b.htmlChunks||E){var y=ve(\"head\");t.push(y)}var _=b.bodyChunks;if(_)for(a=0;a<_.length;a++)t.push(_[a]);rr(e,t,r,null),e.completedRootSegment=null;var k=e.renderState;if(0!==e.allPendingTasks||0!==e.clientRenderedBoundaries.length||0!==e.completedBoundaries.length||null!==e.trackedPostpones&&(0!==e.trackedPostpones.rootNodes.length||null!==e.trackedPostpones.rootSlots)){var T=e.resumableState;if(!(64&T.instructions)){if(T.instructions|=64,t.push(k.startInlineScript),!(32&T.instructions)){T.instructions|=32;var S=\"_\"+T.idPrefix+\"R_\";t.push(' id=\"');var v=P(S);t.push(v),t.push('\"')}t.push(\">\"),t.push(\"requestAnimationFrame(function(){$RT=performance.now()});\"),t.push(\"<\\/script>\")}}Ne(t,k)}var A=e.renderState;r=0;var N=A.viewportChunks;for(r=0;r<N.length;r++)t.push(N[r]);N.length=0,A.preconnects.forEach(Fe,t),A.preconnects.clear(),A.fontPreloads.forEach(Fe,t),A.fontPreloads.clear(),A.highImagePreloads.forEach(Fe,t),A.highImagePreloads.clear(),A.styles.forEach(Ge,t),A.scripts.forEach(Fe,t),A.scripts.clear(),A.bulkPreloads.forEach(Fe,t),A.bulkPreloads.clear();var C=A.hoistableChunks;for(r=0;r<C.length;r++)t.push(C[r]);C.length=0;var x=e.clientRenderedBoundaries;for(n=0;n<x.length;n++){var w=x[n];A=t;var I=e.resumableState,O=e.renderState,R=w.rootSegmentID,D=w.errorDigest;A.push(O.startInlineScript),A.push(\">\"),4&I.instructions?A.push('$RX(\"'):(I.instructions|=4,A.push('$RX=function(b,c,d,e,f){var a=document.getElementById(b);a&&(b=a.previousSibling,b.data=\"$!\",a=a.dataset,c&&(a.dgst=c),d&&(a.msg=d),e&&(a.stck=e),f&&(a.cstck=f),b._reactRetry&&b._reactRetry())};;$RX(\"')),A.push(O.boundaryPrefix);var M=R.toString(16);if(A.push(M),A.push('\"'),D){A.push(\",\");var L=we(D||\"\");A.push(L)}var F=A.push(\")<\\/script>\");if(!F)return e.destination=null,n++,void x.splice(0,n)}x.splice(0,n);var B=e.completedBoundaries;for(n=0;n<B.length;n++)if(!ir(e,t,B[n]))return e.destination=null,n++,void B.splice(0,n);B.splice(0,n),or=!0;var U=e.partialBoundaries;for(n=0;n<U.length;n++){var H=U[n];e:{x=e,w=t,nr=H.byteSize;var z=H.completedSegments;for(F=0;F<z.length;F++)if(!sr(x,w,H,z[F])){F++,z.splice(0,F);var G=!1;break e}z.splice(0,F);var j=H.row;null!==j&&j.together&&1===H.pendingTasks&&(1===j.pendingTasks?Nn(x,j,j.hoistables):j.pendingTasks--),G=Le(w,H.contentState,x.renderState)}if(!G)return e.destination=null,n++,void U.splice(0,n)}U.splice(0,n),or=!1;var $=e.completedBoundaries;for(n=0;n<$.length;n++)if(!ir(e,t,$[n]))return e.destination=null,n++,void $.splice(0,n);$.splice(0,n)}}finally{or=!1,0===e.allPendingTasks&&0===e.clientRenderedBoundaries.length&&0===e.completedBoundaries.length&&(e.flushScheduled=!1,(n=e.resumableState).hasBody&&(U=ve(\"body\"),t.push(U)),n.hasHtml&&(n=ve(\"html\"),t.push(n)),e.status=14,t.push(null),e.destination=null)}}function cr(e){if(!1===e.flushScheduled&&0===e.pingedTasks.length&&null!==e.destination){e.flushScheduled=!0;var t=e.destination;t?lr(e,t):e.flushScheduled=!1}}function ur(e,t){11!==e.status&&10!==e.status||(e.status=12);try{var r=e.abortableTasks;if(0<r.size){var a=void 0===t?Error(n(432)):\"object\"==typeof t&&null!==t&&\"function\"==typeof t.then?Error(n(530)):t;e.fatalError=a,r.forEach(function(t){return qn(t,e,a)}),r.clear()}null!==e.destination&&lr(e,e.destination)}catch(i){Sn(e,i,{}),vn(e,i)}}function dr(e,t,n){if(null===t)n.rootNodes.push(e);else{var r=n.workingMap,a=r.get(t);void 0===a&&(a=[t[1],t[2],[],null],r.set(t,a),dr(a,t[0],n)),a[2].push(e)}}function pr(){}function hr(e,t,r,a){var i,s,o,l,c=!1,u=null,d=\"\",p=!1;if(e=function(e,t,n,r,a,i,s,o,l,c,u,d){return(n=yn(t=new hn(t,n,r,a,i,s,o,l,c,u,d),0,null,r,!1,!1)).parentFlushed=!0,_n(e=bn(t,null,e,-1,null,n,null,null,t.abortableTasks,null,r,null,mt,null,null)),t.pingedTasks.push(e),t}(e,t={idPrefix:void 0===(i=t?t.identifierPrefix:void 0)?\"\":i,nextFormID:0,streamingFormat:0,bootstrapScriptContent:s,bootstrapScripts:o,bootstrapModules:l,instructions:0,hasBody:!1,hasHtml:!1,unknownResources:{},dnsResources:{},connectResources:{default:{},anonymous:{},credentials:{}},imageResources:{},styleResources:{},scriptResources:{},moduleUnknownResources:{},moduleScriptResources:{}},function(e,t){var n=e.idPrefix,r=[],a=e.bootstrapScriptContent,i=e.bootstrapScripts,s=e.bootstrapModules;void 0!==a&&(r.push(\"<script\"),je(r,e),r.push(\">\",(\"\"+a).replace(Y,W),\"<\\/script>\")),a=n+\"P:\";var o=n+\"S:\";n+=\"B:\";var l=new Set,c=new Set,u=new Set,d=new Map,p=new Set,h=new Set,f=new Set,m={images:new Map,stylesheets:new Map,scripts:new Map,moduleScripts:new Map};if(void 0!==i)for(var g=0;g<i.length;g++){var b,E=i[g],y=void 0,_=void 0,k={rel:\"preload\",as:\"script\",fetchPriority:\"low\",nonce:void 0};\"string\"==typeof E?k.href=b=E:(k.href=b=E.src,k.integrity=_=\"string\"==typeof E.integrity?E.integrity:void 0,k.crossOrigin=y=\"string\"==typeof E||null==E.crossOrigin?void 0:\"use-credentials\"===E.crossOrigin?\"use-credentials\":\"\");var T=b;(E=e).scriptResources[T]=null,E.moduleScriptResources[T]=null,de(E=[],k),p.add(E),r.push('<script src=\"',P(b),'\"'),\"string\"==typeof _&&r.push(' integrity=\"',P(_),'\"'),\"string\"==typeof y&&r.push(' crossorigin=\"',P(y),'\"'),je(r,e),r.push(' async=\"\"><\\/script>')}if(void 0!==s)for(i=0;i<s.length;i++)y=b=void 0,_={rel:\"modulepreload\",fetchPriority:\"low\",nonce:void 0},\"string\"==typeof(k=s[i])?_.href=g=k:(_.href=g=k.src,_.integrity=y=\"string\"==typeof k.integrity?k.integrity:void 0,_.crossOrigin=b=\"string\"==typeof k||null==k.crossOrigin?void 0:\"use-credentials\"===k.crossOrigin?\"use-credentials\":\"\"),E=g,(k=e).scriptResources[E]=null,k.moduleScriptResources[E]=null,de(k=[],_),p.add(k),r.push('<script type=\"module\" src=\"',P(g),'\"'),\"string\"==typeof y&&r.push(' integrity=\"',P(y),'\"'),\"string\"==typeof b&&r.push(' crossorigin=\"',P(b),'\"'),je(r,e),r.push(' async=\"\"><\\/script>');return{placeholderPrefix:a,segmentPrefix:o,boundaryPrefix:n,startInlineScript:\"<script\",startInlineStyle:\"<style\",preamble:{htmlChunks:null,headChunks:null,bodyChunks:null},externalRuntimeScript:null,bootstrapChunks:r,importMapChunks:[],onHeaders:void 0,headers:null,resets:{font:{},dns:{},connect:{default:{},anonymous:{},credentials:{}},image:{},style:{}},charsetChunks:[],viewportChunks:[],hoistableChunks:[],preconnects:l,fontPreloads:c,highImagePreloads:u,styles:d,bootstrapScripts:p,scripts:h,bulkPreloads:f,preloads:m,nonce:{script:void 0,style:void 0},stylesToHoist:!1,generateStaticMarkup:t}}(t,r),V(0,null,0,null),1/0,pr,void 0,function(){p=!0},void 0,void 0,void 0),e.flushScheduled=null!==e.destination,Xn(e),10===e.status&&(e.status=11),null===e.trackedPostpones&&Yn(e,0===e.pendingRootTasks),ur(e,a),function(e,t){if(13===e.status)e.status=14,t.destroy(e.fatalError);else if(14!==e.status&&null===e.destination){e.destination=t;try{lr(e,t)}catch(n){Sn(e,n,{}),vn(e,n)}}}(e,{push:function(e){return null!==e&&(d+=e),!0},destroy:function(e){c=!0,u=e}}),c&&u!==a)throw u;if(!p)throw Error(n(426));return d}return ya.renderToStaticMarkup=function(e,t){return hr(e,t,!0,'The server used \"renderToStaticMarkup\" which does not support Suspense. If you intended to have the server wait for the suspended component please switch to \"renderToReadableStream\" which supports Suspense on the server')},ya.renderToString=function(e,t){return hr(e,t,!1,'The server used \"renderToString\" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to \"renderToReadableStream\" which supports Suspense on the server')},ya.version=\"19.2.3\",ya}var ka,Ta,Sa={};\n/**\n * @license React\n * react-dom-server.browser.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */function va(){if(ka)return Sa;ka=1;var e=i(),t=s();function n(e){var t=\"https://react.dev/errors/\"+e;if(1<arguments.length){t+=\"?args[]=\"+encodeURIComponent(arguments[1]);for(var n=2;n<arguments.length;n++)t+=\"&args[]=\"+encodeURIComponent(arguments[n])}return\"Minified React error #\"+e+\"; visit \"+t+\" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\"}var r=Symbol.for(\"react.transitional.element\"),a=Symbol.for(\"react.portal\"),o=Symbol.for(\"react.fragment\"),l=Symbol.for(\"react.strict_mode\"),c=Symbol.for(\"react.profiler\"),u=Symbol.for(\"react.consumer\"),d=Symbol.for(\"react.context\"),p=Symbol.for(\"react.forward_ref\"),h=Symbol.for(\"react.suspense\"),f=Symbol.for(\"react.suspense_list\"),m=Symbol.for(\"react.memo\"),g=Symbol.for(\"react.lazy\"),b=Symbol.for(\"react.scope\"),E=Symbol.for(\"react.activity\"),y=Symbol.for(\"react.legacy_hidden\"),_=Symbol.for(\"react.memo_cache_sentinel\"),k=Symbol.for(\"react.view_transition\"),T=Symbol.iterator;function S(e){return null===e||\"object\"!=typeof e?null:\"function\"==typeof(e=T&&e[T]||e[\"@@iterator\"])?e:null}var v=Array.isArray;function A(e,t){var n=3&e.length,r=e.length-n,a=t;for(t=0;t<r;){var i=255&e.charCodeAt(t)|(255&e.charCodeAt(++t))<<8|(255&e.charCodeAt(++t))<<16|(255&e.charCodeAt(++t))<<24;++t,a=27492+(65535&(a=5*(65535&(a=(a^=i=461845907*(65535&(i=(i=3432918353*(65535&i)+((3432918353*(i>>>16)&65535)<<16)&4294967295)<<15|i>>>17))+((461845907*(i>>>16)&65535)<<16)&4294967295)<<13|a>>>19))+((5*(a>>>16)&65535)<<16)&4294967295))+(((a>>>16)+58964&65535)<<16)}switch(i=0,n){case 3:i^=(255&e.charCodeAt(t+2))<<16;case 2:i^=(255&e.charCodeAt(t+1))<<8;case 1:a^=461845907*(65535&(i=(i=3432918353*(65535&(i^=255&e.charCodeAt(t)))+((3432918353*(i>>>16)&65535)<<16)&4294967295)<<15|i>>>17))+((461845907*(i>>>16)&65535)<<16)&4294967295}return a^=e.length,a=2246822507*(65535&(a^=a>>>16))+((2246822507*(a>>>16)&65535)<<16)&4294967295,((a=3266489909*(65535&(a^=a>>>13))+((3266489909*(a>>>16)&65535)<<16)&4294967295)^a>>>16)>>>0}var N=new MessageChannel,C=[];function x(e){C.push(e),N.port2.postMessage(null)}function w(e){setTimeout(function(){throw e})}N.port1.onmessage=function(){var e=C.shift();e&&e()};var I=Promise,O=\"function\"==typeof queueMicrotask?queueMicrotask:function(e){I.resolve(null).then(e).catch(w)},R=null,D=0;function M(e,t){if(0!==t.byteLength)if(2048<t.byteLength)0<D&&(e.enqueue(new Uint8Array(R.buffer,0,D)),R=new Uint8Array(2048),D=0),e.enqueue(t);else{var n=R.length-D;n<t.byteLength&&(0===n?e.enqueue(R):(R.set(t.subarray(0,n),D),e.enqueue(R),t=t.subarray(n)),R=new Uint8Array(2048),D=0),R.set(t,D),D+=t.byteLength}}function P(e,t){return M(e,t),!0}function L(e){R&&0<D&&(e.enqueue(new Uint8Array(R.buffer,0,D)),R=null,D=0)}var F=new TextEncoder;function B(e){return F.encode(e)}function U(e){return F.encode(e)}function H(e){return e.byteLength}function z(e,t){\"function\"==typeof e.error?e.error(t):e.close()}var G=Object.assign,j=Object.prototype.hasOwnProperty,$=RegExp(\"^[:A-Z_a-z\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD][:A-Z_a-z\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD\\\\-.0-9\\\\u00B7\\\\u0300-\\\\u036F\\\\u203F-\\\\u2040]*$\"),q={},Y={};function W(e){return!!j.call(Y,e)||!j.call(q,e)&&($.test(e)?Y[e]=!0:(q[e]=!0,!1))}var V=new Set(\"animationIterationCount aspectRatio borderImageOutset borderImageSlice borderImageWidth boxFlex boxFlexGroup boxOrdinalGroup columnCount columns flex flexGrow flexPositive flexShrink flexNegative flexOrder gridArea gridRow gridRowEnd gridRowSpan gridRowStart gridColumn gridColumnEnd gridColumnSpan gridColumnStart fontWeight lineClamp lineHeight opacity order orphans scale tabSize widows zIndex zoom fillOpacity floodOpacity stopOpacity strokeDasharray strokeDashoffset strokeMiterlimit strokeOpacity strokeWidth MozAnimationIterationCount MozBoxFlex MozBoxFlexGroup MozLineClamp msAnimationIterationCount msFlex msZoom msFlexGrow msFlexNegative msFlexOrder msFlexPositive msFlexShrink msGridColumn msGridColumnSpan msGridRow msGridRowSpan WebkitAnimationIterationCount WebkitBoxFlex WebKitBoxFlexGroup WebkitBoxOrdinalGroup WebkitColumnCount WebkitColumns WebkitFlex WebkitFlexGrow WebkitFlexPositive WebkitFlexShrink WebkitLineClamp\".split(\" \")),K=new Map([[\"acceptCharset\",\"accept-charset\"],[\"htmlFor\",\"for\"],[\"httpEquiv\",\"http-equiv\"],[\"crossOrigin\",\"crossorigin\"],[\"accentHeight\",\"accent-height\"],[\"alignmentBaseline\",\"alignment-baseline\"],[\"arabicForm\",\"arabic-form\"],[\"baselineShift\",\"baseline-shift\"],[\"capHeight\",\"cap-height\"],[\"clipPath\",\"clip-path\"],[\"clipRule\",\"clip-rule\"],[\"colorInterpolation\",\"color-interpolation\"],[\"colorInterpolationFilters\",\"color-interpolation-filters\"],[\"colorProfile\",\"color-profile\"],[\"colorRendering\",\"color-rendering\"],[\"dominantBaseline\",\"dominant-baseline\"],[\"enableBackground\",\"enable-background\"],[\"fillOpacity\",\"fill-opacity\"],[\"fillRule\",\"fill-rule\"],[\"floodColor\",\"flood-color\"],[\"floodOpacity\",\"flood-opacity\"],[\"fontFamily\",\"font-family\"],[\"fontSize\",\"font-size\"],[\"fontSizeAdjust\",\"font-size-adjust\"],[\"fontStretch\",\"font-stretch\"],[\"fontStyle\",\"font-style\"],[\"fontVariant\",\"font-variant\"],[\"fontWeight\",\"font-weight\"],[\"glyphName\",\"glyph-name\"],[\"glyphOrientationHorizontal\",\"glyph-orientation-horizontal\"],[\"glyphOrientationVertical\",\"glyph-orientation-vertical\"],[\"horizAdvX\",\"horiz-adv-x\"],[\"horizOriginX\",\"horiz-origin-x\"],[\"imageRendering\",\"image-rendering\"],[\"letterSpacing\",\"letter-spacing\"],[\"lightingColor\",\"lighting-color\"],[\"markerEnd\",\"marker-end\"],[\"markerMid\",\"marker-mid\"],[\"markerStart\",\"marker-start\"],[\"overlinePosition\",\"overline-position\"],[\"overlineThickness\",\"overline-thickness\"],[\"paintOrder\",\"paint-order\"],[\"panose-1\",\"panose-1\"],[\"pointerEvents\",\"pointer-events\"],[\"renderingIntent\",\"rendering-intent\"],[\"shapeRendering\",\"shape-rendering\"],[\"stopColor\",\"stop-color\"],[\"stopOpacity\",\"stop-opacity\"],[\"strikethroughPosition\",\"strikethrough-position\"],[\"strikethroughThickness\",\"strikethrough-thickness\"],[\"strokeDasharray\",\"stroke-dasharray\"],[\"strokeDashoffset\",\"stroke-dashoffset\"],[\"strokeLinecap\",\"stroke-linecap\"],[\"strokeLinejoin\",\"stroke-linejoin\"],[\"strokeMiterlimit\",\"stroke-miterlimit\"],[\"strokeOpacity\",\"stroke-opacity\"],[\"strokeWidth\",\"stroke-width\"],[\"textAnchor\",\"text-anchor\"],[\"textDecoration\",\"text-decoration\"],[\"textRendering\",\"text-rendering\"],[\"transformOrigin\",\"transform-origin\"],[\"underlinePosition\",\"underline-position\"],[\"underlineThickness\",\"underline-thickness\"],[\"unicodeBidi\",\"unicode-bidi\"],[\"unicodeRange\",\"unicode-range\"],[\"unitsPerEm\",\"units-per-em\"],[\"vAlphabetic\",\"v-alphabetic\"],[\"vHanging\",\"v-hanging\"],[\"vIdeographic\",\"v-ideographic\"],[\"vMathematical\",\"v-mathematical\"],[\"vectorEffect\",\"vector-effect\"],[\"vertAdvY\",\"vert-adv-y\"],[\"vertOriginX\",\"vert-origin-x\"],[\"vertOriginY\",\"vert-origin-y\"],[\"wordSpacing\",\"word-spacing\"],[\"writingMode\",\"writing-mode\"],[\"xmlnsXlink\",\"xmlns:xlink\"],[\"xHeight\",\"x-height\"]]),Q=/[\"'&<>]/;function X(e){if(\"boolean\"==typeof e||\"number\"==typeof e||\"bigint\"==typeof e)return\"\"+e;e=\"\"+e;var t=Q.exec(e);if(t){var n,r=\"\",a=0;for(n=t.index;n<e.length;n++){switch(e.charCodeAt(n)){case 34:t=\"&quot;\";break;case 38:t=\"&amp;\";break;case 39:t=\"&#x27;\";break;case 60:t=\"&lt;\";break;case 62:t=\"&gt;\";break;default:continue}a!==n&&(r+=e.slice(a,n)),a=n+1,r+=t}e=a!==n?r+e.slice(a,n):r}return e}var Z=/([A-Z])/g,J=/^ms-/,ee=/^[\\u0000-\\u001F ]*j[\\r\\n\\t]*a[\\r\\n\\t]*v[\\r\\n\\t]*a[\\r\\n\\t]*s[\\r\\n\\t]*c[\\r\\n\\t]*r[\\r\\n\\t]*i[\\r\\n\\t]*p[\\r\\n\\t]*t[\\r\\n\\t]*:/i;function te(e){return ee.test(\"\"+e)?\"javascript:throw new Error('React has blocked a javascript: URL as a security precaution.')\":e}var ne=e.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,re=t.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,ae={pending:!1,data:null,method:null,action:null},ie=re.d;re.d={f:ie.f,r:ie.r,D:function(e){var t=xa||null;if(t){var n=t.resumableState,r=t.renderState;if(\"string\"==typeof e&&e){var a,i;if(!n.dnsResources.hasOwnProperty(e))n.dnsResources[e]=null,(i=(n=r.headers)&&0<n.remainingCapacity)&&(a=\"<\"+(\"\"+e).replace(sr,or)+\">; rel=dns-prefetch\",i=0<=(n.remainingCapacity-=a.length+2)),i?(r.resets.dns[e]=null,n.preconnects&&(n.preconnects+=\", \"),n.preconnects+=a):(at(a=[],{href:e,rel:\"dns-prefetch\"}),r.preconnects.add(a));Ai(t)}}else ie.D(e)},C:function(e,t){var n=xa||null;if(n){var r=n.resumableState,a=n.renderState;if(\"string\"==typeof e&&e){var i=\"use-credentials\"===t?\"credentials\":\"string\"==typeof t?\"anonymous\":\"default\";if(!r.connectResources[i].hasOwnProperty(e)){var s,o;if(r.connectResources[i][e]=null,o=(r=a.headers)&&0<r.remainingCapacity){if(o=\"<\"+(\"\"+e).replace(sr,or)+\">; rel=preconnect\",\"string\"==typeof t)o+='; crossorigin=\"'+(\"\"+t).replace(lr,cr)+'\"';s=o,o=0<=(r.remainingCapacity-=s.length+2)}o?(a.resets.connect[i][e]=null,r.preconnects&&(r.preconnects+=\", \"),r.preconnects+=s):(at(i=[],{rel:\"preconnect\",href:e,crossOrigin:t}),a.preconnects.add(i))}Ai(n)}}else ie.C(e,t)},L:function(e,t,n){var r=xa||null;if(r){var a=r.resumableState,i=r.renderState;if(t&&e){switch(t){case\"image\":if(n)var s=n.imageSrcSet,o=n.imageSizes,l=n.fetchPriority;var c,u=s?s+\"\\n\"+(o||\"\"):e;if(a.imageResources.hasOwnProperty(u))return;a.imageResources[u]=se,(a=i.headers)&&0<a.remainingCapacity&&\"string\"!=typeof s&&\"high\"===l&&(c=ir(e,t,n),0<=(a.remainingCapacity-=c.length+2))?(i.resets.image[u]=se,a.highImagePreloads&&(a.highImagePreloads+=\", \"),a.highImagePreloads+=c):(at(a=[],G({rel:\"preload\",href:s?void 0:e,as:t},n)),\"high\"===l?i.highImagePreloads.add(a):(i.bulkPreloads.add(a),i.preloads.images.set(u,a)));break;case\"style\":if(a.styleResources.hasOwnProperty(e))return;at(s=[],G({rel:\"preload\",href:e,as:t},n)),a.styleResources[e]=!n||\"string\"!=typeof n.crossOrigin&&\"string\"!=typeof n.integrity?se:[n.crossOrigin,n.integrity],i.preloads.stylesheets.set(e,s),i.bulkPreloads.add(s);break;case\"script\":if(a.scriptResources.hasOwnProperty(e))return;s=[],i.preloads.scripts.set(e,s),i.bulkPreloads.add(s),at(s,G({rel:\"preload\",href:e,as:t},n)),a.scriptResources[e]=!n||\"string\"!=typeof n.crossOrigin&&\"string\"!=typeof n.integrity?se:[n.crossOrigin,n.integrity];break;default:if(a.unknownResources.hasOwnProperty(t)){if((s=a.unknownResources[t]).hasOwnProperty(e))return}else s={},a.unknownResources[t]=s;if(s[e]=se,(a=i.headers)&&0<a.remainingCapacity&&\"font\"===t&&(u=ir(e,t,n),0<=(a.remainingCapacity-=u.length+2)))i.resets.font[e]=se,a.fontPreloads&&(a.fontPreloads+=\", \"),a.fontPreloads+=u;else if(\"font\"===(at(a=[],e=G({rel:\"preload\",href:e,as:t},n)),t))i.fontPreloads.add(a);else i.bulkPreloads.add(a)}Ai(r)}}else ie.L(e,t,n)},m:function(e,t){var n=xa||null;if(n){var r=n.resumableState,a=n.renderState;if(e){var i=t&&\"string\"==typeof t.as?t.as:\"script\";if(\"script\"===i){if(r.moduleScriptResources.hasOwnProperty(e))return;i=[],r.moduleScriptResources[e]=!t||\"string\"!=typeof t.crossOrigin&&\"string\"!=typeof t.integrity?se:[t.crossOrigin,t.integrity],a.preloads.moduleScripts.set(e,i)}else{if(r.moduleUnknownResources.hasOwnProperty(i)){var s=r.unknownResources[i];if(s.hasOwnProperty(e))return}else s={},r.moduleUnknownResources[i]=s;i=[],s[e]=se}at(i,G({rel:\"modulepreload\",href:e},t)),a.bulkPreloads.add(i),Ai(n)}}else ie.m(e,t)},X:function(e,t){var n=xa||null;if(n){var r=n.resumableState,a=n.renderState;if(e){var i=r.scriptResources.hasOwnProperty(e)?r.scriptResources[e]:void 0;null!==i&&(r.scriptResources[e]=null,t=G({src:e,async:!0},t),i&&(2===i.length&&ar(t,i),e=a.preloads.scripts.get(e))&&(e.length=0),e=[],a.scripts.add(e),pt(e,t),Ai(n))}}else ie.X(e,t)},S:function(e,t,n){var r=xa||null;if(r){var a=r.resumableState,i=r.renderState;if(e){t=t||\"default\";var s=i.styles.get(t),o=a.styleResources.hasOwnProperty(e)?a.styleResources[e]:void 0;null!==o&&(a.styleResources[e]=null,s||(s={precedence:B(X(t)),rules:[],hrefs:[],sheets:new Map},i.styles.set(t,s)),t={state:0,props:G({rel:\"stylesheet\",href:e,\"data-precedence\":t},n)},o&&(2===o.length&&ar(t.props,o),(i=i.preloads.stylesheets.get(e))&&0<i.length?i.length=0:t.state=1),s.sheets.set(e,t),Ai(r))}}else ie.S(e,t,n)},M:function(e,t){var n=xa||null;if(n){var r=n.resumableState,a=n.renderState;if(e){var i=r.moduleScriptResources.hasOwnProperty(e)?r.moduleScriptResources[e]:void 0;null!==i&&(r.moduleScriptResources[e]=null,t=G({src:e,type:\"module\",async:!0},t),i&&(2===i.length&&ar(t,i),e=a.preloads.moduleScripts.get(e))&&(e.length=0),e=[],a.scripts.add(e),pt(e,t),Ai(n))}}else ie.M(e,t)}};var se=[],oe=null;U('\"></template>');var le=U(\"<script\"),ce=U(\"<\\/script>\"),ue=U('<script src=\"'),de=U('<script type=\"module\" src=\"'),pe=U(' nonce=\"'),he=U(' integrity=\"'),fe=U(' crossorigin=\"'),me=U(' async=\"\"><\\/script>'),ge=U(\"<style\"),be=/(<\\/|<)(s)(cript)/gi;function Ee(e,t,n,r){return t+(\"s\"===n?\"\\\\u0073\":\"\\\\u0053\")+r}var ye=U('<script type=\"importmap\">'),_e=U(\"<\\/script>\");function ke(e,t,n,r,a,i){var s=void 0===(n=\"string\"==typeof t?t:t&&t.script)?le:U('<script nonce=\"'+X(n)+'\"'),o=\"string\"==typeof t?void 0:t&&t.style,l=void 0===o?ge:U('<style nonce=\"'+X(o)+'\"'),c=e.idPrefix,u=[],d=e.bootstrapScriptContent,p=e.bootstrapScripts,h=e.bootstrapModules;if(void 0!==d&&(u.push(s),Xn(u,e),u.push(Qe,B((\"\"+d).replace(be,Ee)),ce)),d=[],void 0!==r&&(d.push(ye),d.push(B((\"\"+JSON.stringify(r)).replace(be,Ee))),d.push(_e)),r=a?{preconnects:\"\",fontPreloads:\"\",highImagePreloads:\"\",remainingCapacity:2+(\"number\"==typeof i?i:2e3)}:null,a={placeholderPrefix:U(c+\"P:\"),segmentPrefix:U(c+\"S:\"),boundaryPrefix:U(c+\"B:\"),startInlineScript:s,startInlineStyle:l,preamble:{htmlChunks:null,headChunks:null,bodyChunks:null},externalRuntimeScript:null,bootstrapChunks:u,importMapChunks:d,onHeaders:a,headers:r,resets:{font:{},dns:{},connect:{default:{},anonymous:{},credentials:{}},image:{},style:{}},charsetChunks:[],viewportChunks:[],hoistableChunks:[],preconnects:new Set,fontPreloads:new Set,highImagePreloads:new Set,styles:new Map,bootstrapScripts:new Set,scripts:new Set,bulkPreloads:new Set,preloads:{images:new Map,stylesheets:new Map,scripts:new Map,moduleScripts:new Map},nonce:{script:n,style:o},hoistableState:null,stylesToHoist:!1},void 0!==p)for(r=0;r<p.length;r++)o=s=void 0,l={rel:\"preload\",as:\"script\",fetchPriority:\"low\",nonce:t},\"string\"==typeof(c=p[r])?l.href=i=c:(l.href=i=c.src,l.integrity=o=\"string\"==typeof c.integrity?c.integrity:void 0,l.crossOrigin=s=\"string\"==typeof c||null==c.crossOrigin?void 0:\"use-credentials\"===c.crossOrigin?\"use-credentials\":\"\"),d=i,(c=e).scriptResources[d]=null,c.moduleScriptResources[d]=null,at(c=[],l),a.bootstrapScripts.add(c),u.push(ue,B(X(i)),Ue),n&&u.push(pe,B(X(n)),Ue),\"string\"==typeof o&&u.push(he,B(X(o)),Ue),\"string\"==typeof s&&u.push(fe,B(X(s)),Ue),Xn(u,e),u.push(me);if(void 0!==h)for(t=0;t<h.length;t++)i=r=void 0,s={rel:\"modulepreload\",fetchPriority:\"low\",nonce:n},\"string\"==typeof(o=h[t])?s.href=p=o:(s.href=p=o.src,s.integrity=i=\"string\"==typeof o.integrity?o.integrity:void 0,s.crossOrigin=r=\"string\"==typeof o||null==o.crossOrigin?void 0:\"use-credentials\"===o.crossOrigin?\"use-credentials\":\"\"),l=p,(o=e).scriptResources[l]=null,o.moduleScriptResources[l]=null,at(o=[],s),a.bootstrapScripts.add(o),u.push(de,B(X(p)),Ue),n&&u.push(pe,B(X(n)),Ue),\"string\"==typeof i&&u.push(he,B(X(i)),Ue),\"string\"==typeof r&&u.push(fe,B(X(r)),Ue),Xn(u,e),u.push(me);return a}function Te(e,t,n,r,a){return{idPrefix:void 0===e?\"\":e,nextFormID:0,streamingFormat:0,bootstrapScriptContent:n,bootstrapScripts:r,bootstrapModules:a,instructions:0,hasBody:!1,hasHtml:!1,unknownResources:{},dnsResources:{},connectResources:{default:{},anonymous:{},credentials:{}},imageResources:{},styleResources:{},scriptResources:{},moduleUnknownResources:{},moduleScriptResources:{}}}function Se(){return{htmlChunks:null,headChunks:null,bodyChunks:null}}function ve(e,t,n,r){return{insertionMode:e,selectedValue:t,tagScope:n,viewTransition:r}}function Ae(e){return ve(\"http://www.w3.org/2000/svg\"===e?4:\"http://www.w3.org/1998/Math/MathML\"===e?5:0,null,0,null)}function Ne(e,t,n){var r=-25&e.tagScope;switch(t){case\"noscript\":return ve(2,null,1|r,null);case\"select\":return ve(2,null!=n.value?n.value:n.defaultValue,r,null);case\"svg\":return ve(4,null,r,null);case\"picture\":return ve(2,null,2|r,null);case\"math\":return ve(5,null,r,null);case\"foreignObject\":return ve(2,null,r,null);case\"table\":return ve(6,null,r,null);case\"thead\":case\"tbody\":case\"tfoot\":return ve(7,null,r,null);case\"colgroup\":return ve(9,null,r,null);case\"tr\":return ve(8,null,r,null);case\"head\":if(2>e.insertionMode)return ve(3,null,r,null);break;case\"html\":if(0===e.insertionMode)return ve(1,null,r,null)}return 6<=e.insertionMode||2>e.insertionMode?ve(2,null,r,null):e.tagScope!==r?ve(e.insertionMode,e.selectedValue,r,null):e}function Ce(e){return null===e?null:{update:e.update,enter:\"none\",exit:\"none\",share:e.update,name:e.autoName,autoName:e.autoName,nameIdx:0}}function xe(e,t){return 32&t.tagScope&&(e.instructions|=128),ve(t.insertionMode,t.selectedValue,12|t.tagScope,Ce(t.viewTransition))}function we(e,t){e=Ce(t.viewTransition);var n=16|t.tagScope;return null!==e&&\"none\"!==e.share&&(n|=64),ve(t.insertionMode,t.selectedValue,n,e)}var Ie=U(\"\\x3c!-- --\\x3e\");function Oe(e,t,n,r){return\"\"===t?r:(r&&e.push(Ie),e.push(B(X(t))),!0)}var Re=new Map,De=U(' style=\"'),Me=U(\":\"),Pe=U(\";\");function Le(e,t){if(\"object\"!=typeof t)throw Error(n(62));var r,a=!0;for(r in t)if(j.call(t,r)){var i=t[r];if(null!=i&&\"boolean\"!=typeof i&&\"\"!==i){if(0===r.indexOf(\"--\")){var s=B(X(r));i=B(X((\"\"+i).trim()))}else void 0===(s=Re.get(r))&&(s=U(X(r.replace(Z,\"-$1\").toLowerCase().replace(J,\"-ms-\"))),Re.set(r,s)),i=\"number\"==typeof i?0===i||V.has(r)?B(\"\"+i):B(i+\"px\"):B(X((\"\"+i).trim()));a?(a=!1,e.push(De,s,Me,i)):e.push(Pe,s,Me,i)}}a||e.push(Ue)}var Fe=U(\" \"),Be=U('=\"'),Ue=U('\"'),He=U('=\"\"');function ze(e,t,n){n&&\"function\"!=typeof n&&\"symbol\"!=typeof n&&e.push(Fe,B(t),He)}function Ge(e,t,n){\"function\"!=typeof n&&\"symbol\"!=typeof n&&\"boolean\"!=typeof n&&e.push(Fe,B(t),Be,B(X(n)),Ue)}var je=U(X(\"javascript:throw new Error('React form unexpectedly submitted.')\")),$e=U('<input type=\"hidden\"');function qe(e,t){this.push($e),Ye(e),Ge(this,\"name\",t),Ge(this,\"value\",e),this.push(Xe)}function Ye(e){if(\"string\"!=typeof e)throw Error(n(480))}function We(e,t){if(\"function\"==typeof t.$$FORM_ACTION){var n=e.nextFormID++;e=e.idPrefix+n;try{var r=t.$$FORM_ACTION(e);if(r){var a=r.data;null!=a&&a.forEach(Ye)}return r}catch(i){if(\"object\"==typeof i&&null!==i&&\"function\"==typeof i.then)throw i}}return null}function Ve(e,t,n,r,a,i,s,o){var l=null;if(\"function\"==typeof r){var c=We(t,r);null!==c?(o=c.name,r=c.action||\"\",a=c.encType,i=c.method,s=c.target,l=c.data):(e.push(Fe,B(\"formAction\"),Be,je,Ue),s=i=a=r=o=null,tt(t,n))}return null!=o&&Ke(e,\"name\",o),null!=r&&Ke(e,\"formAction\",r),null!=a&&Ke(e,\"formEncType\",a),null!=i&&Ke(e,\"formMethod\",i),null!=s&&Ke(e,\"formTarget\",s),l}function Ke(e,t,n){switch(t){case\"className\":Ge(e,\"class\",n);break;case\"tabIndex\":Ge(e,\"tabindex\",n);break;case\"dir\":case\"role\":case\"viewBox\":case\"width\":case\"height\":Ge(e,t,n);break;case\"style\":Le(e,n);break;case\"src\":case\"href\":if(\"\"===n)break;case\"action\":case\"formAction\":if(null==n||\"function\"==typeof n||\"symbol\"==typeof n||\"boolean\"==typeof n)break;n=te(\"\"+n),e.push(Fe,B(t),Be,B(X(n)),Ue);break;case\"defaultValue\":case\"defaultChecked\":case\"innerHTML\":case\"suppressContentEditableWarning\":case\"suppressHydrationWarning\":case\"ref\":break;case\"autoFocus\":case\"multiple\":case\"muted\":ze(e,t.toLowerCase(),n);break;case\"xlinkHref\":if(\"function\"==typeof n||\"symbol\"==typeof n||\"boolean\"==typeof n)break;n=te(\"\"+n),e.push(Fe,B(\"xlink:href\"),Be,B(X(n)),Ue);break;case\"contentEditable\":case\"spellCheck\":case\"draggable\":case\"value\":case\"autoReverse\":case\"externalResourcesRequired\":case\"focusable\":case\"preserveAlpha\":\"function\"!=typeof n&&\"symbol\"!=typeof n&&e.push(Fe,B(t),Be,B(X(n)),Ue);break;case\"inert\":case\"allowFullScreen\":case\"async\":case\"autoPlay\":case\"controls\":case\"default\":case\"defer\":case\"disabled\":case\"disablePictureInPicture\":case\"disableRemotePlayback\":case\"formNoValidate\":case\"hidden\":case\"loop\":case\"noModule\":case\"noValidate\":case\"open\":case\"playsInline\":case\"readOnly\":case\"required\":case\"reversed\":case\"scoped\":case\"seamless\":case\"itemScope\":n&&\"function\"!=typeof n&&\"symbol\"!=typeof n&&e.push(Fe,B(t),He);break;case\"capture\":case\"download\":!0===n?e.push(Fe,B(t),He):!1!==n&&\"function\"!=typeof n&&\"symbol\"!=typeof n&&e.push(Fe,B(t),Be,B(X(n)),Ue);break;case\"cols\":case\"rows\":case\"size\":case\"span\":\"function\"!=typeof n&&\"symbol\"!=typeof n&&!isNaN(n)&&1<=n&&e.push(Fe,B(t),Be,B(X(n)),Ue);break;case\"rowSpan\":case\"start\":\"function\"==typeof n||\"symbol\"==typeof n||isNaN(n)||e.push(Fe,B(t),Be,B(X(n)),Ue);break;case\"xlinkActuate\":Ge(e,\"xlink:actuate\",n);break;case\"xlinkArcrole\":Ge(e,\"xlink:arcrole\",n);break;case\"xlinkRole\":Ge(e,\"xlink:role\",n);break;case\"xlinkShow\":Ge(e,\"xlink:show\",n);break;case\"xlinkTitle\":Ge(e,\"xlink:title\",n);break;case\"xlinkType\":Ge(e,\"xlink:type\",n);break;case\"xmlBase\":Ge(e,\"xml:base\",n);break;case\"xmlLang\":Ge(e,\"xml:lang\",n);break;case\"xmlSpace\":Ge(e,\"xml:space\",n);break;default:if((!(2<t.length)||\"o\"!==t[0]&&\"O\"!==t[0]||\"n\"!==t[1]&&\"N\"!==t[1])&&W(t=K.get(t)||t)){switch(typeof n){case\"function\":case\"symbol\":return;case\"boolean\":var r=t.toLowerCase().slice(0,5);if(\"data-\"!==r&&\"aria-\"!==r)return}e.push(Fe,B(t),Be,B(X(n)),Ue)}}}var Qe=U(\">\"),Xe=U(\"/>\");function Ze(e,t,r){if(null!=t){if(null!=r)throw Error(n(60));if(\"object\"!=typeof t||!(\"__html\"in t))throw Error(n(61));null!=(t=t.__html)&&e.push(B(\"\"+t))}}var Je=U(' selected=\"\"'),et=U('addEventListener(\"submit\",function(a){if(!a.defaultPrevented){var c=a.target,d=a.submitter,e=c.action,b=d;if(d){var f=d.getAttribute(\"formAction\");null!=f&&(e=f,b=null)}\"javascript:throw new Error(\\'React form unexpectedly submitted.\\')\"===e&&(a.preventDefault(),b?(a=document.createElement(\"input\"),a.name=b.name,a.value=b.value,b.parentNode.insertBefore(a,b),b=new FormData(c),a.parentNode.removeChild(a)):b=new FormData(c),a=c.ownerDocument||c,(a.$$reactFormReplay=a.$$reactFormReplay||[]).push(c,d,b))}});');function tt(e,t){if(!(16&e.instructions)){e.instructions|=16;var n=t.preamble,r=t.bootstrapChunks;(n.htmlChunks||n.headChunks)&&0===r.length?(r.push(t.startInlineScript),Xn(r,e),r.push(Qe,et,ce)):r.unshift(t.startInlineScript,Qe,et,ce)}}var nt=U(\"\\x3c!--F!--\\x3e\"),rt=U(\"\\x3c!--F--\\x3e\");function at(e,t){for(var r in e.push(Et(\"link\")),t)if(j.call(t,r)){var a=t[r];if(null!=a)switch(r){case\"children\":case\"dangerouslySetInnerHTML\":throw Error(n(399,\"link\"));default:Ke(e,r,a)}}return e.push(Xe),null}var it=/(<\\/|<)(s)(tyle)/gi;function st(e,t,n,r){return t+(\"s\"===n?\"\\\\73 \":\"\\\\53 \")+r}function ot(e,t,r){for(var a in e.push(Et(r)),t)if(j.call(t,a)){var i=t[a];if(null!=i)switch(a){case\"children\":case\"dangerouslySetInnerHTML\":throw Error(n(399,r));default:Ke(e,a,i)}}return e.push(Xe),null}function lt(e,t){e.push(Et(\"title\"));var n,r=null,a=null;for(n in t)if(j.call(t,n)){var i=t[n];if(null!=i)switch(n){case\"children\":r=i;break;case\"dangerouslySetInnerHTML\":a=i;break;default:Ke(e,n,i)}}return e.push(Qe),\"function\"!=typeof(t=Array.isArray(r)?2>r.length?r[0]:null:r)&&\"symbol\"!=typeof t&&null!=t&&e.push(B(X(\"\"+t))),Ze(e,a,r),e.push(Tt(\"title\")),null}var ct=U(\"\\x3c!--head--\\x3e\"),ut=U(\"\\x3c!--body--\\x3e\"),dt=U(\"\\x3c!--html--\\x3e\");function pt(e,t){e.push(Et(\"script\"));var n,r=null,a=null;for(n in t)if(j.call(t,n)){var i=t[n];if(null!=i)switch(n){case\"children\":r=i;break;case\"dangerouslySetInnerHTML\":a=i;break;default:Ke(e,n,i)}}return e.push(Qe),Ze(e,a,r),\"string\"==typeof r&&e.push(B((\"\"+r).replace(be,Ee))),e.push(Tt(\"script\")),null}function ht(e,t,n){e.push(Et(n));var r,a=n=null;for(r in t)if(j.call(t,r)){var i=t[r];if(null!=i)switch(r){case\"children\":n=i;break;case\"dangerouslySetInnerHTML\":a=i;break;default:Ke(e,r,i)}}return e.push(Qe),Ze(e,a,n),n}function ft(e,t,n){e.push(Et(n));var r,a=n=null;for(r in t)if(j.call(t,r)){var i=t[r];if(null!=i)switch(r){case\"children\":n=i;break;case\"dangerouslySetInnerHTML\":a=i;break;default:Ke(e,r,i)}}return e.push(Qe),Ze(e,a,n),\"string\"==typeof n?(e.push(B(X(n))),null):n}var mt=U(\"\\n\"),gt=/^[a-zA-Z][a-zA-Z:_\\.\\-\\d]*$/,bt=new Map;function Et(e){var t=bt.get(e);if(void 0===t){if(!gt.test(e))throw Error(n(65,e));t=U(\"<\"+e),bt.set(e,t)}return t}var yt=U(\"<!DOCTYPE html>\");function _t(t,r,a,i,s,o,l,c,u){switch(r){case\"div\":case\"span\":case\"svg\":case\"path\":case\"g\":case\"p\":case\"li\":case\"annotation-xml\":case\"color-profile\":case\"font-face\":case\"font-face-src\":case\"font-face-uri\":case\"font-face-format\":case\"font-face-name\":case\"missing-glyph\":break;case\"a\":t.push(Et(\"a\"));var d,p=null,h=null;for(d in a)if(j.call(a,d)){var f=a[d];if(null!=f)switch(d){case\"children\":p=f;break;case\"dangerouslySetInnerHTML\":h=f;break;case\"href\":\"\"===f?Ge(t,\"href\",\"\"):Ke(t,d,f);break;default:Ke(t,d,f)}}if(t.push(Qe),Ze(t,h,p),\"string\"==typeof p){t.push(B(X(p)));var m=null}else m=p;return m;case\"select\":t.push(Et(\"select\"));var g,b=null,E=null;for(g in a)if(j.call(a,g)){var y=a[g];if(null!=y)switch(g){case\"children\":b=y;break;case\"dangerouslySetInnerHTML\":E=y;break;case\"defaultValue\":case\"value\":break;default:Ke(t,g,y)}}return t.push(Qe),Ze(t,E,b),b;case\"option\":var _=c.selectedValue;t.push(Et(\"option\"));var k,T=null,S=null,A=null,N=null;for(k in a)if(j.call(a,k)){var C=a[k];if(null!=C)switch(k){case\"children\":T=C;break;case\"selected\":A=C;break;case\"dangerouslySetInnerHTML\":N=C;break;case\"value\":S=C;default:Ke(t,k,C)}}if(null!=_){var x=null!==S?\"\"+S:function(t){var n=\"\";return e.Children.forEach(t,function(e){null!=e&&(n+=e)}),n}(T);if(v(_)){for(var w=0;w<_.length;w++)if(\"\"+_[w]===x){t.push(Je);break}}else\"\"+_===x&&t.push(Je)}else A&&t.push(Je);return t.push(Qe),Ze(t,N,T),T;case\"textarea\":t.push(Et(\"textarea\"));var I,O=null,R=null,D=null;for(I in a)if(j.call(a,I)){var M=a[I];if(null!=M)switch(I){case\"children\":D=M;break;case\"value\":O=M;break;case\"defaultValue\":R=M;break;case\"dangerouslySetInnerHTML\":throw Error(n(91));default:Ke(t,I,M)}}if(null===O&&null!==R&&(O=R),t.push(Qe),null!=D){if(null!=O)throw Error(n(92));if(v(D)){if(1<D.length)throw Error(n(93));O=\"\"+D[0]}O=\"\"+D}return\"string\"==typeof O&&\"\\n\"===O[0]&&t.push(mt),null!==O&&t.push(B(X(\"\"+O))),null;case\"input\":t.push(Et(\"input\"));var P,L=null,F=null,U=null,H=null,z=null,$=null,q=null,Y=null,V=null;for(P in a)if(j.call(a,P)){var K=a[P];if(null!=K)switch(P){case\"children\":case\"dangerouslySetInnerHTML\":throw Error(n(399,\"input\"));case\"name\":L=K;break;case\"formAction\":F=K;break;case\"formEncType\":U=K;break;case\"formMethod\":H=K;break;case\"formTarget\":z=K;break;case\"defaultChecked\":V=K;break;case\"defaultValue\":q=K;break;case\"checked\":Y=K;break;case\"value\":$=K;break;default:Ke(t,P,K)}}var Q=Ve(t,i,s,F,U,H,z,L);return null!==Y?ze(t,\"checked\",Y):null!==V&&ze(t,\"checked\",V),null!==$?Ke(t,\"value\",$):null!==q&&Ke(t,\"value\",q),t.push(Xe),null!=Q&&Q.forEach(qe,t),null;case\"button\":t.push(Et(\"button\"));var Z,J=null,ee=null,ne=null,re=null,ae=null,ie=null,oe=null;for(Z in a)if(j.call(a,Z)){var le=a[Z];if(null!=le)switch(Z){case\"children\":J=le;break;case\"dangerouslySetInnerHTML\":ee=le;break;case\"name\":ne=le;break;case\"formAction\":re=le;break;case\"formEncType\":ae=le;break;case\"formMethod\":ie=le;break;case\"formTarget\":oe=le;break;default:Ke(t,Z,le)}}var ce=Ve(t,i,s,re,ae,ie,oe,ne);if(t.push(Qe),null!=ce&&ce.forEach(qe,t),Ze(t,ee,J),\"string\"==typeof J){t.push(B(X(J)));var ue=null}else ue=J;return ue;case\"form\":t.push(Et(\"form\"));var de,pe=null,he=null,fe=null,me=null,ge=null,be=null;for(de in a)if(j.call(a,de)){var Ee=a[de];if(null!=Ee)switch(de){case\"children\":pe=Ee;break;case\"dangerouslySetInnerHTML\":he=Ee;break;case\"action\":fe=Ee;break;case\"encType\":me=Ee;break;case\"method\":ge=Ee;break;case\"target\":be=Ee;break;default:Ke(t,de,Ee)}}var ye=null,_e=null;if(\"function\"==typeof fe){var ke=We(i,fe);null!==ke?(fe=ke.action||\"\",me=ke.encType,ge=ke.method,be=ke.target,ye=ke.data,_e=ke.name):(t.push(Fe,B(\"action\"),Be,je,Ue),be=ge=me=fe=null,tt(i,s))}if(null!=fe&&Ke(t,\"action\",fe),null!=me&&Ke(t,\"encType\",me),null!=ge&&Ke(t,\"method\",ge),null!=be&&Ke(t,\"target\",be),t.push(Qe),null!==_e&&(t.push($e),Ge(t,\"name\",_e),t.push(Xe),null!=ye&&ye.forEach(qe,t)),Ze(t,he,pe),\"string\"==typeof pe){t.push(B(X(pe)));var Te=null}else Te=pe;return Te;case\"menuitem\":for(var Se in t.push(Et(\"menuitem\")),a)if(j.call(a,Se)){var ve=a[Se];if(null!=ve)switch(Se){case\"children\":case\"dangerouslySetInnerHTML\":throw Error(n(400));default:Ke(t,Se,ve)}}return t.push(Qe),null;case\"object\":t.push(Et(\"object\"));var Ae,Ne=null,Ce=null;for(Ae in a)if(j.call(a,Ae)){var xe=a[Ae];if(null!=xe)switch(Ae){case\"children\":Ne=xe;break;case\"dangerouslySetInnerHTML\":Ce=xe;break;case\"data\":var we=te(\"\"+xe);if(\"\"===we)break;t.push(Fe,B(\"data\"),Be,B(X(we)),Ue);break;default:Ke(t,Ae,xe)}}if(t.push(Qe),Ze(t,Ce,Ne),\"string\"==typeof Ne){t.push(B(X(Ne)));var Oe=null}else Oe=Ne;return Oe;case\"title\":var Re=1&c.tagScope,De=4&c.tagScope;if(4===c.insertionMode||Re||null!=a.itemProp)var Me=lt(t,a);else De?Me=null:(lt(s.hoistableChunks,a),Me=void 0);return Me;case\"link\":var Pe=1&c.tagScope,He=4&c.tagScope,Ye=a.rel,et=a.href,nt=a.precedence;if(4===c.insertionMode||Pe||null!=a.itemProp||\"string\"!=typeof Ye||\"string\"!=typeof et||\"\"===et){at(t,a);var rt=null}else if(\"stylesheet\"===a.rel)if(\"string\"!=typeof nt||null!=a.disabled||a.onLoad||a.onError)rt=at(t,a);else{var gt=s.styles.get(nt),bt=i.styleResources.hasOwnProperty(et)?i.styleResources[et]:void 0;if(null!==bt){i.styleResources[et]=null,gt||(gt={precedence:B(X(nt)),rules:[],hrefs:[],sheets:new Map},s.styles.set(nt,gt));var _t={state:0,props:G({},a,{\"data-precedence\":a.precedence,precedence:null})};if(bt){2===bt.length&&ar(_t.props,bt);var kt=s.preloads.stylesheets.get(et);kt&&0<kt.length?kt.length=0:_t.state=1}gt.sheets.set(et,_t),l&&l.stylesheets.add(_t)}else if(gt){var St=gt.sheets.get(et);St&&l&&l.stylesheets.add(St)}u&&t.push(Ie),rt=null}else a.onLoad||a.onError?rt=at(t,a):(u&&t.push(Ie),rt=He?null:at(s.hoistableChunks,a));return rt;case\"script\":var vt=1&c.tagScope,At=a.async;if(\"string\"!=typeof a.src||!a.src||!At||\"function\"==typeof At||\"symbol\"==typeof At||a.onLoad||a.onError||4===c.insertionMode||vt||null!=a.itemProp)var Nt=pt(t,a);else{var Ct=a.src;if(\"module\"===a.type)var xt=i.moduleScriptResources,wt=s.preloads.moduleScripts;else xt=i.scriptResources,wt=s.preloads.scripts;var It=xt.hasOwnProperty(Ct)?xt[Ct]:void 0;if(null!==It){xt[Ct]=null;var Ot=a;if(It){2===It.length&&ar(Ot=G({},a),It);var Rt=wt.get(Ct);Rt&&(Rt.length=0)}var Dt=[];s.scripts.add(Dt),pt(Dt,Ot)}u&&t.push(Ie),Nt=null}return Nt;case\"style\":var Mt=1&c.tagScope,Pt=a.precedence,Lt=a.href,Ft=a.nonce;if(4===c.insertionMode||Mt||null!=a.itemProp||\"string\"!=typeof Pt||\"string\"!=typeof Lt||\"\"===Lt){t.push(Et(\"style\"));var Bt,Ut=null,Ht=null;for(Bt in a)if(j.call(a,Bt)){var zt=a[Bt];if(null!=zt)switch(Bt){case\"children\":Ut=zt;break;case\"dangerouslySetInnerHTML\":Ht=zt;break;default:Ke(t,Bt,zt)}}t.push(Qe);var Gt=Array.isArray(Ut)?2>Ut.length?Ut[0]:null:Ut;\"function\"!=typeof Gt&&\"symbol\"!=typeof Gt&&null!=Gt&&t.push(B((\"\"+Gt).replace(it,st))),Ze(t,Ht,Ut),t.push(Tt(\"style\"));var jt=null}else{var $t=s.styles.get(Pt);if(null!==(i.styleResources.hasOwnProperty(Lt)?i.styleResources[Lt]:void 0)){i.styleResources[Lt]=null,$t||($t={precedence:B(X(Pt)),rules:[],hrefs:[],sheets:new Map},s.styles.set(Pt,$t));var qt=s.nonce.style;if(!qt||qt===Ft){$t.hrefs.push(B(X(Lt)));var Yt,Wt=$t.rules,Vt=null,Kt=null;for(Yt in a)if(j.call(a,Yt)){var Qt=a[Yt];if(null!=Qt)switch(Yt){case\"children\":Vt=Qt;break;case\"dangerouslySetInnerHTML\":Kt=Qt}}var Xt=Array.isArray(Vt)?2>Vt.length?Vt[0]:null:Vt;\"function\"!=typeof Xt&&\"symbol\"!=typeof Xt&&null!=Xt&&Wt.push(B((\"\"+Xt).replace(it,st))),Ze(Wt,Kt,Vt)}}$t&&l&&l.styles.add($t),u&&t.push(Ie),jt=void 0}return jt;case\"meta\":var Zt=1&c.tagScope,Jt=4&c.tagScope;if(4===c.insertionMode||Zt||null!=a.itemProp)var en=ot(t,a,\"meta\");else u&&t.push(Ie),en=Jt?null:\"string\"==typeof a.charSet?ot(s.charsetChunks,a,\"meta\"):\"viewport\"===a.name?ot(s.viewportChunks,a,\"meta\"):ot(s.hoistableChunks,a,\"meta\");return en;case\"listing\":case\"pre\":t.push(Et(r));var tn,nn=null,rn=null;for(tn in a)if(j.call(a,tn)){var an=a[tn];if(null!=an)switch(tn){case\"children\":nn=an;break;case\"dangerouslySetInnerHTML\":rn=an;break;default:Ke(t,tn,an)}}if(t.push(Qe),null!=rn){if(null!=nn)throw Error(n(60));if(\"object\"!=typeof rn||!(\"__html\"in rn))throw Error(n(61));var sn=rn.__html;null!=sn&&(\"string\"==typeof sn&&0<sn.length&&\"\\n\"===sn[0]?t.push(mt,B(sn)):t.push(B(\"\"+sn)))}return\"string\"==typeof nn&&\"\\n\"===nn[0]&&t.push(mt),nn;case\"img\":var on=3&c.tagScope,ln=a.src,cn=a.srcSet;if(!(\"lazy\"===a.loading||!ln&&!cn||\"string\"!=typeof ln&&null!=ln||\"string\"!=typeof cn&&null!=cn||\"low\"===a.fetchPriority||on)&&(\"string\"!=typeof ln||\":\"!==ln[4]||\"d\"!==ln[0]&&\"D\"!==ln[0]||\"a\"!==ln[1]&&\"A\"!==ln[1]||\"t\"!==ln[2]&&\"T\"!==ln[2]||\"a\"!==ln[3]&&\"A\"!==ln[3])&&(\"string\"!=typeof cn||\":\"!==cn[4]||\"d\"!==cn[0]&&\"D\"!==cn[0]||\"a\"!==cn[1]&&\"A\"!==cn[1]||\"t\"!==cn[2]&&\"T\"!==cn[2]||\"a\"!==cn[3]&&\"A\"!==cn[3])){null!==l&&64&c.tagScope&&(l.suspenseyImages=!0);var un=\"string\"==typeof a.sizes?a.sizes:void 0,dn=cn?cn+\"\\n\"+(un||\"\"):ln,pn=s.preloads.images,hn=pn.get(dn);if(hn)(\"high\"===a.fetchPriority||10>s.highImagePreloads.size)&&(pn.delete(dn),s.highImagePreloads.add(hn));else if(!i.imageResources.hasOwnProperty(dn)){i.imageResources[dn]=se;var fn,mn=a.crossOrigin,gn=\"string\"==typeof mn?\"use-credentials\"===mn?mn:\"\":void 0,bn=s.headers;bn&&0<bn.remainingCapacity&&\"string\"!=typeof a.srcSet&&(\"high\"===a.fetchPriority||500>bn.highImagePreloads.length)&&(fn=ir(ln,\"image\",{imageSrcSet:a.srcSet,imageSizes:a.sizes,crossOrigin:gn,integrity:a.integrity,nonce:a.nonce,type:a.type,fetchPriority:a.fetchPriority,referrerPolicy:a.refererPolicy}),0<=(bn.remainingCapacity-=fn.length+2))?(s.resets.image[dn]=se,bn.highImagePreloads&&(bn.highImagePreloads+=\", \"),bn.highImagePreloads+=fn):(at(hn=[],{rel:\"preload\",as:\"image\",href:cn?void 0:ln,imageSrcSet:cn,imageSizes:un,crossOrigin:gn,integrity:a.integrity,type:a.type,fetchPriority:a.fetchPriority,referrerPolicy:a.referrerPolicy}),\"high\"===a.fetchPriority||10>s.highImagePreloads.size?s.highImagePreloads.add(hn):(s.bulkPreloads.add(hn),pn.set(dn,hn)))}}return ot(t,a,\"img\");case\"base\":case\"area\":case\"br\":case\"col\":case\"embed\":case\"hr\":case\"keygen\":case\"param\":case\"source\":case\"track\":case\"wbr\":return ot(t,a,r);case\"head\":if(2>c.insertionMode){var En=o||s.preamble;if(En.headChunks)throw Error(n(545,\"`<head>`\"));null!==o&&t.push(ct),En.headChunks=[];var yn=ht(En.headChunks,a,\"head\")}else yn=ft(t,a,\"head\");return yn;case\"body\":if(2>c.insertionMode){var _n=o||s.preamble;if(_n.bodyChunks)throw Error(n(545,\"`<body>`\"));null!==o&&t.push(ut),_n.bodyChunks=[];var kn=ht(_n.bodyChunks,a,\"body\")}else kn=ft(t,a,\"body\");return kn;case\"html\":if(0===c.insertionMode){var Tn=o||s.preamble;if(Tn.htmlChunks)throw Error(n(545,\"`<html>`\"));null!==o&&t.push(dt),Tn.htmlChunks=[yt];var Sn=ht(Tn.htmlChunks,a,\"html\")}else Sn=ft(t,a,\"html\");return Sn;default:if(-1!==r.indexOf(\"-\")){t.push(Et(r));var vn,An=null,Nn=null;for(vn in a)if(j.call(a,vn)){var Cn=a[vn];if(null!=Cn){var xn=vn;switch(vn){case\"children\":An=Cn;break;case\"dangerouslySetInnerHTML\":Nn=Cn;break;case\"style\":Le(t,Cn);break;case\"suppressContentEditableWarning\":case\"suppressHydrationWarning\":case\"ref\":break;case\"className\":xn=\"class\";default:if(W(vn)&&\"function\"!=typeof Cn&&\"symbol\"!=typeof Cn&&!1!==Cn){if(!0===Cn)Cn=\"\";else if(\"object\"==typeof Cn)continue;t.push(Fe,B(xn),Be,B(X(Cn)),Ue)}}}}return t.push(Qe),Ze(t,Nn,An),An}}return ft(t,a,r)}var kt=new Map;function Tt(e){var t=kt.get(e);return void 0===t&&(t=U(\"</\"+e+\">\"),kt.set(e,t)),t}function St(e,t){null===(e=e.preamble).htmlChunks&&t.htmlChunks&&(e.htmlChunks=t.htmlChunks),null===e.headChunks&&t.headChunks&&(e.headChunks=t.headChunks),null===e.bodyChunks&&t.bodyChunks&&(e.bodyChunks=t.bodyChunks)}function vt(e,t){t=t.bootstrapChunks;for(var n=0;n<t.length-1;n++)M(e,t[n]);return!(n<t.length)||(n=t[n],t.length=0,P(e,n))}var At=U(\"requestAnimationFrame(function(){$RT=performance.now()});\"),Nt=U('<template id=\"'),Ct=U('\"></template>'),xt=U(\"\\x3c!--&--\\x3e\"),wt=U(\"\\x3c!--/&--\\x3e\"),It=U(\"\\x3c!--$--\\x3e\"),Ot=U('\\x3c!--$?--\\x3e<template id=\"'),Rt=U('\"></template>'),Dt=U(\"\\x3c!--$!--\\x3e\"),Mt=U(\"\\x3c!--/$--\\x3e\"),Pt=U(\"<template\"),Lt=U('\"'),Ft=U(' data-dgst=\"');U(' data-msg=\"'),U(' data-stck=\"'),U(' data-cstck=\"');var Bt=U(\"></template>\");function Ut(e,t,r){if(M(e,Ot),null===r)throw Error(n(395));return M(e,t.boundaryPrefix),M(e,B(r.toString(16))),P(e,Rt)}var Ht=U('<div hidden id=\"'),zt=U('\">'),Gt=U(\"</div>\"),jt=U('<svg aria-hidden=\"true\" style=\"display:none\" id=\"'),$t=U('\">'),qt=U(\"</svg>\"),Yt=U('<math aria-hidden=\"true\" style=\"display:none\" id=\"'),Wt=U('\">'),Vt=U(\"</math>\"),Kt=U('<table hidden id=\"'),Qt=U('\">'),Xt=U(\"</table>\"),Zt=U('<table hidden><tbody id=\"'),Jt=U('\">'),en=U(\"</tbody></table>\"),tn=U('<table hidden><tr id=\"'),nn=U('\">'),rn=U(\"</tr></table>\"),an=U('<table hidden><colgroup id=\"'),sn=U('\">'),on=U(\"</colgroup></table>\");var ln=U('$RS=function(a,b){a=document.getElementById(a);b=document.getElementById(b);for(a.parentNode.removeChild(a);a.firstChild;)b.parentNode.insertBefore(a.firstChild,b);b.parentNode.removeChild(b)};$RS(\"'),cn=U('$RS(\"'),un=U('\",\"'),dn=U('\")<\\/script>');U('<template data-rsi=\"\" data-sid=\"'),U('\" data-pid=\"');var pn=U('$RB=[];$RV=function(a){$RT=performance.now();for(var b=0;b<a.length;b+=2){var c=a[b],e=a[b+1];null!==e.parentNode&&e.parentNode.removeChild(e);var f=c.parentNode;if(f){var g=c.previousSibling,h=0;do{if(c&&8===c.nodeType){var d=c.data;if(\"/$\"===d||\"/&\"===d)if(0===h)break;else h--;else\"$\"!==d&&\"$?\"!==d&&\"$~\"!==d&&\"$!\"!==d&&\"&\"!==d||h++}d=c.nextSibling;f.removeChild(c);c=d}while(c);for(;e.firstChild;)f.insertBefore(e.firstChild,c);g.data=\"$\";g._reactRetry&&requestAnimationFrame(g._reactRetry)}}a.length=0};\\n$RC=function(a,b){if(b=document.getElementById(b))(a=document.getElementById(a))?(a.previousSibling.data=\"$~\",$RB.push(a,b),2===$RB.length&&(\"number\"!==typeof $RT?requestAnimationFrame($RV.bind(null,$RB)):(a=performance.now(),setTimeout($RV.bind(null,$RB),2300>a&&2E3<a?2300-a:$RT+300-a)))):b.parentNode.removeChild(b)};');B('$RV=function(A,g){function k(a,b){var e=a.getAttribute(b);e&&(b=a.style,l.push(a,b.viewTransitionName,b.viewTransitionClass),\"auto\"!==e&&(b.viewTransitionClass=e),(a=a.getAttribute(\"vt-name\"))||(a=\"_T_\"+K++ +\"_\"),b.viewTransitionName=a,B=!0)}var B=!1,K=0,l=[];try{var f=document.__reactViewTransition;if(f){f.finished.finally($RV.bind(null,g));return}var m=new Map;for(f=1;f<g.length;f+=2)for(var h=g[f].querySelectorAll(\"[vt-share]\"),d=0;d<h.length;d++){var c=h[d];m.set(c.getAttribute(\"vt-name\"),c)}var u=[];for(h=0;h<g.length;h+=2){var C=g[h],x=C.parentNode;if(x){var v=x.getBoundingClientRect();if(v.left||v.top||v.width||v.height){c=C;for(f=0;c;){if(8===c.nodeType){var r=c.data;if(\"/$\"===r)if(0===f)break;else f--;else\"$\"!==r&&\"$?\"!==r&&\"$~\"!==r&&\"$!\"!==r||f++}else if(1===c.nodeType){d=c;var D=d.getAttribute(\"vt-name\"),y=m.get(D);k(d,y?\"vt-share\":\"vt-exit\");y&&(k(y,\"vt-share\"),m.set(D,null));var E=d.querySelectorAll(\"[vt-share]\");for(d=0;d<E.length;d++){var F=E[d],G=F.getAttribute(\"vt-name\"),\\nH=m.get(G);H&&(k(F,\"vt-share\"),k(H,\"vt-share\"),m.set(G,null))}}c=c.nextSibling}for(var I=g[h+1],t=I.firstElementChild;t;)null!==m.get(t.getAttribute(\"vt-name\"))&&k(t,\"vt-enter\"),t=t.nextElementSibling;c=x;do for(var n=c.firstElementChild;n;){var J=n.getAttribute(\"vt-update\");J&&\"none\"!==J&&!l.includes(n)&&k(n,\"vt-update\");n=n.nextElementSibling}while((c=c.parentNode)&&1===c.nodeType&&\"none\"!==c.getAttribute(\"vt-update\"));u.push.apply(u,I.querySelectorAll(\\'img[src]:not([loading=\"lazy\"])\\'))}}}if(B){var z=\\ndocument.__reactViewTransition=document.startViewTransition({update:function(){A(g);for(var a=[document.documentElement.clientHeight,document.fonts.ready],b={},e=0;e<u.length;b={g:b.g},e++)if(b.g=u[e],!b.g.complete){var p=b.g.getBoundingClientRect();0<p.bottom&&0<p.right&&p.top<window.innerHeight&&p.left<window.innerWidth&&(p=new Promise(function(w){return function(q){w.g.addEventListener(\"load\",q);w.g.addEventListener(\"error\",q)}}(b)),a.push(p))}return Promise.race([Promise.all(a),new Promise(function(w){var q=\\nperformance.now();setTimeout(w,2300>q&&2E3<q?2300-q:500)})])},types:[]});z.ready.finally(function(){for(var a=l.length-3;0<=a;a-=3){var b=l[a],e=b.style;e.viewTransitionName=l[a+1];e.viewTransitionClass=l[a+1];\"\"===b.getAttribute(\"style\")&&b.removeAttribute(\"style\")}});z.finished.finally(function(){document.__reactViewTransition===z&&(document.__reactViewTransition=null)});$RB=[];return}}catch(a){}A(g)}.bind(null,$RV);');var hn=U('$RC(\"'),fn=U('$RM=new Map;$RR=function(n,w,p){function u(q){this._p=null;q()}for(var r=new Map,t=document,h,b,e=t.querySelectorAll(\"link[data-precedence],style[data-precedence]\"),v=[],k=0;b=e[k++];)\"not all\"===b.getAttribute(\"media\")?v.push(b):(\"LINK\"===b.tagName&&$RM.set(b.getAttribute(\"href\"),b),r.set(b.dataset.precedence,h=b));e=0;b=[];var l,a;for(k=!0;;){if(k){var f=p[e++];if(!f){k=!1;e=0;continue}var c=!1,m=0;var d=f[m++];if(a=$RM.get(d)){var g=a._p;c=!0}else{a=t.createElement(\"link\");a.href=d;a.rel=\\n\"stylesheet\";for(a.dataset.precedence=l=f[m++];g=f[m++];)a.setAttribute(g,f[m++]);g=a._p=new Promise(function(q,x){a.onload=u.bind(a,q);a.onerror=u.bind(a,x)});$RM.set(d,a)}d=a.getAttribute(\"media\");!g||d&&!matchMedia(d).matches||b.push(g);if(c)continue}else{a=v[e++];if(!a)break;l=a.getAttribute(\"data-precedence\");a.removeAttribute(\"media\")}c=r.get(l)||h;c===h&&(h=a);r.set(l,a);c?c.parentNode.insertBefore(a,c.nextSibling):(c=t.head,c.insertBefore(a,c.firstChild))}if(p=document.getElementById(n))p.previousSibling.data=\\n\"$~\";Promise.all(b).then($RC.bind(null,n,w),$RX.bind(null,n,\"CSS failed to load\"))};$RR(\"'),mn=U('$RR(\"'),gn=U('\",\"'),bn=U('\",'),En=U('\"'),yn=U(\")<\\/script>\");U('<template data-rci=\"\" data-bid=\"'),U('<template data-rri=\"\" data-bid=\"'),U('\" data-sid=\"'),U('\" data-sty=\"');var _n=U('$RX=function(b,c,d,e,f){var a=document.getElementById(b);a&&(b=a.previousSibling,b.data=\"$!\",a=a.dataset,c&&(a.dgst=c),d&&(a.msg=d),e&&(a.stck=e),f&&(a.cstck=f),b._reactRetry&&b._reactRetry())};'),kn=U('$RX=function(b,c,d,e,f){var a=document.getElementById(b);a&&(b=a.previousSibling,b.data=\"$!\",a=a.dataset,c&&(a.dgst=c),d&&(a.msg=d),e&&(a.stck=e),f&&(a.cstck=f),b._reactRetry&&b._reactRetry())};;$RX(\"'),Tn=U('$RX(\"'),Sn=U('\"'),vn=U(\",\"),An=U(\")<\\/script>\");U('<template data-rxi=\"\" data-bid=\"'),U('\" data-dgst=\"'),U('\" data-msg=\"'),U('\" data-stck=\"'),U('\" data-cstck=\"');var Nn=/[<\\u2028\\u2029]/g;function Cn(e){return JSON.stringify(e).replace(Nn,function(e){switch(e){case\"<\":return\"\\\\u003c\";case\"\\u2028\":return\"\\\\u2028\";case\"\\u2029\":return\"\\\\u2029\";default:throw Error(\"escapeJSStringsForInstructionScripts encountered a match it does not know how to replace. this means the match regex and the replacement characters are no longer in sync. This is a bug in React\")}})}var xn=/[&><\\u2028\\u2029]/g;function wn(e){return JSON.stringify(e).replace(xn,function(e){switch(e){case\"&\":return\"\\\\u0026\";case\">\":return\"\\\\u003e\";case\"<\":return\"\\\\u003c\";case\"\\u2028\":return\"\\\\u2028\";case\"\\u2029\":return\"\\\\u2029\";default:throw Error(\"escapeJSObjectForInstructionScripts encountered a match it does not know how to replace. this means the match regex and the replacement characters are no longer in sync. This is a bug in React\")}})}var In=U(' media=\"not all\" data-precedence=\"'),On=U('\" data-href=\"'),Rn=U('\">'),Dn=U(\"</style>\"),Mn=!1,Pn=!0;function Ln(e){var t=e.rules,n=e.hrefs,r=0;if(n.length){for(M(this,oe.startInlineStyle),M(this,In),M(this,e.precedence),M(this,On);r<n.length-1;r++)M(this,n[r]),M(this,$n);for(M(this,n[r]),M(this,Rn),r=0;r<t.length;r++)M(this,t[r]);Pn=P(this,Dn),Mn=!0,t.length=0,n.length=0}}function Fn(e){return 2!==e.state&&(Mn=!0)}function Bn(e,t,n){return Mn=!1,Pn=!0,oe=n,t.styles.forEach(Ln,e),oe=null,t.stylesheets.forEach(Fn),Mn&&(n.stylesToHoist=!0),Pn}function Un(e){for(var t=0;t<e.length;t++)M(this,e[t]);e.length=0}var Hn=[];function zn(e){at(Hn,e.props);for(var t=0;t<Hn.length;t++)M(this,Hn[t]);Hn.length=0,e.state=2}var Gn=U(' data-precedence=\"'),jn=U('\" data-href=\"'),$n=U(\" \"),qn=U('\">'),Yn=U(\"</style>\");function Wn(e){var t=0<e.sheets.size;e.sheets.forEach(zn,this),e.sheets.clear();var n=e.rules,r=e.hrefs;if(!t||r.length){if(M(this,oe.startInlineStyle),M(this,Gn),M(this,e.precedence),e=0,r.length){for(M(this,jn);e<r.length-1;e++)M(this,r[e]),M(this,$n);M(this,r[e])}for(M(this,qn),e=0;e<n.length;e++)M(this,n[e]);M(this,Yn),n.length=0,r.length=0}}function Vn(e){if(0===e.state){e.state=1;var t=e.props;for(at(Hn,{rel:\"preload\",as:\"style\",href:e.props.href,crossOrigin:t.crossOrigin,fetchPriority:t.fetchPriority,integrity:t.integrity,media:t.media,hrefLang:t.hrefLang,referrerPolicy:t.referrerPolicy}),e=0;e<Hn.length;e++)M(this,Hn[e]);Hn.length=0}}function Kn(e){e.sheets.forEach(Vn,this),e.sheets.clear()}U('<link rel=\"expect\" href=\"#'),U('\" blocking=\"render\"/>');var Qn=U(' id=\"');function Xn(e,t){!(32&t.instructions)&&(t.instructions|=32,e.push(Qn,B(X(\"_\"+t.idPrefix+\"R_\")),Ue))}var Zn=U(\"[\"),Jn=U(\",[\"),er=U(\",\"),tr=U(\"]\");function nr(e,t,n){var r=t.toLowerCase();switch(typeof n){case\"function\":case\"symbol\":return}switch(t){case\"innerHTML\":case\"dangerouslySetInnerHTML\":case\"suppressContentEditableWarning\":case\"suppressHydrationWarning\":case\"style\":case\"ref\":return;case\"className\":r=\"class\",t=\"\"+n;break;case\"hidden\":if(!1===n)return;t=\"\";break;case\"src\":case\"href\":t=\"\"+(n=te(n));break;default:if(2<t.length&&(\"o\"===t[0]||\"O\"===t[0])&&(\"n\"===t[1]||\"N\"===t[1])||!W(t))return;t=\"\"+n}M(e,er),M(e,B(wn(r))),M(e,er),M(e,B(wn(t)))}function rr(){return{styles:new Set,stylesheets:new Set,suspenseyImages:!1}}function ar(e,t){null==e.crossOrigin&&(e.crossOrigin=t[0]),null==e.integrity&&(e.integrity=t[1])}function ir(e,t,n){for(var r in t=\"<\"+(e=(\"\"+e).replace(sr,or))+'>; rel=preload; as=\"'+(t=(\"\"+t).replace(lr,cr))+'\"',n)j.call(n,r)&&(\"string\"==typeof(e=n[r])&&(t+=\"; \"+r.toLowerCase()+'=\"'+(\"\"+e).replace(lr,cr)+'\"'));return t}var sr=/[<>\\r\\n]/g;function or(e){switch(e){case\"<\":return\"%3C\";case\">\":return\"%3E\";case\"\\n\":return\"%0A\";case\"\\r\":return\"%0D\";default:throw Error(\"escapeLinkHrefForHeaderContextReplacer encountered a match it does not know how to replace. this means the match regex and the replacement characters are no longer in sync. This is a bug in React\")}}var lr=/[\"';,\\r\\n]/g;function cr(e){switch(e){case'\"':return\"%22\";case\"'\":return\"%27\";case\";\":return\"%3B\";case\",\":return\"%2C\";case\"\\n\":return\"%0A\";case\"\\r\":return\"%0D\";default:throw Error(\"escapeStringForLinkHeaderQuotedParamValueContextReplacer encountered a match it does not know how to replace. this means the match regex and the replacement characters are no longer in sync. This is a bug in React\")}}function ur(e){this.styles.add(e)}function dr(e){this.stylesheets.add(e)}function pr(e,t){t.styles.forEach(ur,e),t.stylesheets.forEach(dr,e),t.suspenseyImages&&(e.suspenseyImages=!0)}function hr(e){return 0<e.stylesheets.size||e.suspenseyImages}var fr=Function.prototype.bind,mr=Symbol.for(\"react.client.reference\");function gr(e){if(null==e)return null;if(\"function\"==typeof e)return e.$$typeof===mr?null:e.displayName||e.name||null;if(\"string\"==typeof e)return e;switch(e){case o:return\"Fragment\";case c:return\"Profiler\";case l:return\"StrictMode\";case h:return\"Suspense\";case f:return\"SuspenseList\";case E:return\"Activity\"}if(\"object\"==typeof e)switch(e.$$typeof){case a:return\"Portal\";case d:return e.displayName||\"Context\";case u:return(e._context.displayName||\"Context\")+\".Consumer\";case p:var t=e.render;return(e=e.displayName)||(e=\"\"!==(e=t.displayName||t.name||\"\")?\"ForwardRef(\"+e+\")\":\"ForwardRef\"),e;case m:return null!==(t=e.displayName||null)?t:gr(e.type)||\"Memo\";case g:t=e._payload,e=e._init;try{return gr(e(t))}catch(n){}}return null}var br={},Er=null;function yr(e,t){if(e!==t){e.context._currentValue=e.parentValue,e=e.parent;var r=t.parent;if(null===e){if(null!==r)throw Error(n(401))}else{if(null===r)throw Error(n(401));yr(e,r)}t.context._currentValue=t.value}}function _r(e){e.context._currentValue=e.parentValue,null!==(e=e.parent)&&_r(e)}function kr(e){var t=e.parent;null!==t&&kr(t),e.context._currentValue=e.value}function Tr(e,t){if(e.context._currentValue=e.parentValue,null===(e=e.parent))throw Error(n(402));e.depth===t.depth?yr(e,t):Tr(e,t)}function Sr(e,t){var r=t.parent;if(null===r)throw Error(n(402));e.depth===r.depth?yr(e,r):Sr(e,r),t.context._currentValue=t.value}function vr(e){var t=Er;t!==e&&(null===t?kr(e):null===e?_r(t):t.depth===e.depth?yr(t,e):t.depth>e.depth?Tr(t,e):Sr(t,e),Er=e)}var Ar={enqueueSetState:function(e,t){null!==(e=e._reactInternals).queue&&e.queue.push(t)},enqueueReplaceState:function(e,t){(e=e._reactInternals).replace=!0,e.queue=[t]},enqueueForceUpdate:function(){}},Nr={id:1,overflow:\"\"};function Cr(e,t,n){var r=e.id;e=e.overflow;var a=32-xr(r)-1;r&=~(1<<a),n+=1;var i=32-xr(t)+a;if(30<i){var s=a-a%5;return i=(r&(1<<s)-1).toString(32),r>>=s,a-=s,{id:1<<32-xr(t)+a|n<<a|r,overflow:i+e}}return{id:1<<i|n<<a|r,overflow:e}}var xr=Math.clz32?Math.clz32:function(e){return 0===(e>>>=0)?32:31-(wr(e)/Ir|0)|0},wr=Math.log,Ir=Math.LN2;function Or(){}var Rr=Error(n(460));var Dr=null;function Mr(){if(null===Dr)throw Error(n(459));var e=Dr;return Dr=null,e}var Pr=\"function\"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},Lr=null,Fr=null,Br=null,Ur=null,Hr=null,zr=null,Gr=!1,jr=!1,$r=0,qr=0,Yr=-1,Wr=0,Vr=null,Kr=null,Qr=0;function Xr(){if(null===Lr)throw Error(n(321));return Lr}function Zr(){if(0<Qr)throw Error(n(312));return{memoizedState:null,queue:null,next:null}}function Jr(){return null===zr?null===Hr?(Gr=!1,Hr=zr=Zr()):(Gr=!0,zr=Hr):null===zr.next?(Gr=!1,zr=zr.next=Zr()):(Gr=!0,zr=zr.next),zr}function ea(){var e=Vr;return Vr=null,e}function ta(){Ur=Br=Fr=Lr=null,jr=!1,Hr=null,Qr=0,zr=Kr=null}function na(e,t){return\"function\"==typeof t?t(e):t}function ra(e,t,n){if(Lr=Xr(),zr=Jr(),Gr){var r=zr.queue;if(t=r.dispatch,null!==Kr&&void 0!==(n=Kr.get(r))){Kr.delete(r),r=zr.memoizedState;do{r=e(r,n.action),n=n.next}while(null!==n);return zr.memoizedState=r,[r,t]}return[zr.memoizedState,t]}return e=e===na?\"function\"==typeof t?t():t:void 0!==n?n(t):t,zr.memoizedState=e,e=(e=zr.queue={last:null,dispatch:null}).dispatch=ia.bind(null,Lr,e),[zr.memoizedState,e]}function aa(e,t){if(Lr=Xr(),t=void 0===t?null:t,null!==(zr=Jr())){var n=zr.memoizedState;if(null!==n&&null!==t){var r=n[1];e:if(null===r)r=!1;else{for(var a=0;a<r.length&&a<t.length;a++)if(!Pr(t[a],r[a])){r=!1;break e}r=!0}if(r)return n[0]}}return e=e(),zr.memoizedState=[e,t],e}function ia(e,t,r){if(25<=Qr)throw Error(n(301));if(e===Lr)if(jr=!0,e={action:r,next:null},null===Kr&&(Kr=new Map),void 0===(r=Kr.get(t)))Kr.set(t,e);else{for(t=r;null!==t.next;)t=t.next;t.next=e}}function sa(){throw Error(n(440))}function oa(){throw Error(n(394))}function la(){throw Error(n(479))}function ca(e,t,n){Xr();var r=qr++,a=Br;if(\"function\"==typeof e.$$FORM_ACTION){var i=null,s=Ur;a=a.formState;var o=e.$$IS_SIGNATURE_EQUAL;if(null!==a&&\"function\"==typeof o){var l=a[1];o.call(e,a[2],a[3])&&(l===(i=void 0!==n?\"p\"+n:\"k\"+A(JSON.stringify([s,null,r]),0))&&(Yr=r,t=a[0]))}var c=e.bind(null,t);return e=function(e){c(e)},\"function\"==typeof c.$$FORM_ACTION&&(e.$$FORM_ACTION=function(e){e=c.$$FORM_ACTION(e),void 0!==n&&(n+=\"\",e.action=n);var t=e.data;return t&&(null===i&&(i=void 0!==n?\"p\"+n:\"k\"+A(JSON.stringify([s,null,r]),0)),t.append(\"$ACTION_KEY\",i)),e}),[t,e,!1]}var u=e.bind(null,t);return[t,function(e){u(e)},!1]}function ua(e){var t=Wr;return Wr+=1,null===Vr&&(Vr=[]),function(e,t,n){switch(void 0===(n=e[n])?e.push(t):n!==t&&(t.then(Or,Or),t=n),t.status){case\"fulfilled\":return t.value;case\"rejected\":throw t.reason;default:switch(\"string\"==typeof t.status?t.then(Or,Or):((e=t).status=\"pending\",e.then(function(e){if(\"pending\"===t.status){var n=t;n.status=\"fulfilled\",n.value=e}},function(e){if(\"pending\"===t.status){var n=t;n.status=\"rejected\",n.reason=e}})),t.status){case\"fulfilled\":return t.value;case\"rejected\":throw t.reason}throw Dr=t,Rr}}(Vr,e,t)}function da(){throw Error(n(393))}var pa,ha,fa={readContext:function(e){return e._currentValue},use:function(e){if(null!==e&&\"object\"==typeof e){if(\"function\"==typeof e.then)return ua(e);if(e.$$typeof===d)return e._currentValue}throw Error(n(438,String(e)))},useContext:function(e){return Xr(),e._currentValue},useMemo:aa,useReducer:ra,useRef:function(e){Lr=Xr();var t=(zr=Jr()).memoizedState;return null===t?(e={current:e},zr.memoizedState=e):t},useState:function(e){return ra(na,e)},useInsertionEffect:Or,useLayoutEffect:Or,useCallback:function(e,t){return aa(function(){return e},t)},useImperativeHandle:Or,useEffect:Or,useDebugValue:Or,useDeferredValue:function(e,t){return Xr(),void 0!==t?t:e},useTransition:function(){return Xr(),[!1,oa]},useId:function(){var e=Fr.treeContext,t=e.overflow;e=((e=e.id)&~(1<<32-xr(e)-1)).toString(32)+t;var r=ma;if(null===r)throw Error(n(404));return t=$r++,e=\"_\"+r.idPrefix+\"R_\"+e,0<t&&(e+=\"H\"+t.toString(32)),e+\"_\"},useSyncExternalStore:function(e,t,r){if(void 0===r)throw Error(n(407));return r()},useOptimistic:function(e){return Xr(),[e,la]},useActionState:ca,useFormState:ca,useHostTransitionStatus:function(){return Xr(),ae},useMemoCache:function(e){for(var t=Array(e),n=0;n<e;n++)t[n]=_;return t},useCacheRefresh:function(){return da},useEffectEvent:function(){return sa}},ma=null,ga={getCacheForType:function(){throw Error(n(248))},cacheSignal:function(){throw Error(n(248))}};function ba(e){if(void 0===pa)try{throw Error()}catch(n){var t=n.stack.trim().match(/\\n( *(at )?)/);pa=t&&t[1]||\"\",ha=-1<n.stack.indexOf(\"\\n    at\")?\" (<anonymous>)\":-1<n.stack.indexOf(\"@\")?\"@unknown:0:0\":\"\"}return\"\\n\"+pa+e+ha}var Ea=!1;function ya(e,t){if(!e||Ea)return\"\";Ea=!0;var n=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var r={DetermineComponentFrameRoot:function(){try{if(t){var n=function(){throw Error()};if(Object.defineProperty(n.prototype,\"props\",{set:function(){throw Error()}}),\"object\"==typeof Reflect&&Reflect.construct){try{Reflect.construct(n,[])}catch(a){var r=a}Reflect.construct(e,[],n)}else{try{n.call()}catch(i){r=i}e.call(n.prototype)}}else{try{throw Error()}catch(s){r=s}(n=e())&&\"function\"==typeof n.catch&&n.catch(function(){})}}catch(o){if(o&&r&&\"string\"==typeof o.stack)return[o.stack,r.stack]}return[null,null]}};r.DetermineComponentFrameRoot.displayName=\"DetermineComponentFrameRoot\";var a=Object.getOwnPropertyDescriptor(r.DetermineComponentFrameRoot,\"name\");a&&a.configurable&&Object.defineProperty(r.DetermineComponentFrameRoot,\"name\",{value:\"DetermineComponentFrameRoot\"});var i=r.DetermineComponentFrameRoot(),s=i[0],o=i[1];if(s&&o){var l=s.split(\"\\n\"),c=o.split(\"\\n\");for(a=r=0;r<l.length&&!l[r].includes(\"DetermineComponentFrameRoot\");)r++;for(;a<c.length&&!c[a].includes(\"DetermineComponentFrameRoot\");)a++;if(r===l.length||a===c.length)for(r=l.length-1,a=c.length-1;1<=r&&0<=a&&l[r]!==c[a];)a--;for(;1<=r&&0<=a;r--,a--)if(l[r]!==c[a]){if(1!==r||1!==a)do{if(r--,0>--a||l[r]!==c[a]){var u=\"\\n\"+l[r].replace(\" at new \",\" at \");return e.displayName&&u.includes(\"<anonymous>\")&&(u=u.replace(\"<anonymous>\",e.displayName)),u}}while(1<=r&&0<=a);break}}}finally{Ea=!1,Error.prepareStackTrace=n}return(n=e?e.displayName||e.name:\"\")?ba(n):\"\"}function _a(e){if(\"string\"==typeof e)return ba(e);if(\"function\"==typeof e)return e.prototype&&e.prototype.isReactComponent?ya(e,!0):ya(e,!1);if(\"object\"==typeof e&&null!==e){switch(e.$$typeof){case p:return ya(e.render,!1);case m:return ya(e.type,!1);case g:var t=e,n=t._payload;t=t._init;try{e=t(n)}catch(a){return ba(\"Lazy\")}return _a(e)}if(\"string\"==typeof e.name){n=e.name,t=e.env;var r=e.debugLocation;return n=null==r||(e=Error.prepareStackTrace,Error.prepareStackTrace=void 0,r=r.stack,Error.prepareStackTrace=e,r.startsWith(\"Error: react-stack-top-frame\\n\")&&(r=r.slice(29)),-1!==(e=r.indexOf(\"\\n\"))&&(r=r.slice(e+1)),-1!==(e=r.indexOf(\"react_stack_bottom_frame\"))&&(e=r.lastIndexOf(\"\\n\",e)),e=-1!==e?r=r.slice(0,e):\"\",r=e.lastIndexOf(\"\\n\"),-1===(e=-1===r?e:e.slice(r+1)).indexOf(n))?ba(n+(t?\" [\"+t+\"]\":\"\")):\"\\n\"+e}}switch(e){case f:return ba(\"SuspenseList\");case h:return ba(\"Suspense\")}return\"\"}function Ta(e,t){return(500<t.byteSize||hr(t.contentState))&&null===t.contentPreamble}function va(e){if(\"object\"==typeof e&&null!==e&&\"string\"==typeof e.environmentName){var t=e.environmentName;\"string\"==typeof(e=[e].slice(0))[0]?e.splice(0,1,\"%c%s%c \"+e[0],\"background: #e6e6e6;background: light-dark(rgba(0,0,0,0.1), rgba(255,255,255,0.25));color: #000000;color: light-dark(#000000, #ffffff);border-radius: 2px\",\" \"+t+\" \",\"\"):e.splice(0,0,\"%c%s%c\",\"background: #e6e6e6;background: light-dark(rgba(0,0,0,0.1), rgba(255,255,255,0.25));color: #000000;color: light-dark(#000000, #ffffff);border-radius: 2px\",\" \"+t+\" \",\"\"),e.unshift(console),(t=fr.apply(console.error,e))()}return null}function Aa(e,t,n,r,a,i,s,o,l,c,u){var d=new Set;this.destination=null,this.flushScheduled=!1,this.resumableState=e,this.renderState=t,this.rootFormatContext=n,this.progressiveChunkSize=void 0===r?12800:r,this.status=10,this.fatalError=null,this.pendingRootTasks=this.allPendingTasks=this.nextSegmentId=0,this.completedPreambleSegments=this.completedRootSegment=null,this.byteSize=0,this.abortableTasks=d,this.pingedTasks=[],this.clientRenderedBoundaries=[],this.completedBoundaries=[],this.partialBoundaries=[],this.trackedPostpones=null,this.onError=void 0===a?va:a,this.onPostpone=void 0===c?Or:c,this.onAllReady=void 0===i?Or:i,this.onShellReady=void 0===s?Or:s,this.onShellError=void 0===o?Or:o,this.onFatalError=void 0===l?Or:l,this.formState=void 0===u?null:u}function Na(e,t,n,r,a,i,s,o,l,c,u,d){return(n=Da(t=new Aa(t,n,r,a,i,s,o,l,c,u,d),0,null,r,!1,!1)).parentFlushed=!0,Ma(e=Oa(t,null,e,-1,null,n,null,null,t.abortableTasks,null,r,null,Nr,null,null)),t.pingedTasks.push(e),t}function Ca(e,t,n,r,a,i,s,o,l){return(n=new Aa(t.resumableState,n,t.rootFormatContext,t.progressiveChunkSize,r,a,i,s,o,l,null)).nextSegmentId=t.nextSegmentId,\"number\"==typeof t.replaySlots?((r=Da(n,0,null,t.rootFormatContext,!1,!1)).parentFlushed=!0,Ma(e=Oa(n,null,e,-1,null,r,null,null,n.abortableTasks,null,t.rootFormatContext,null,Nr,null,null)),n.pingedTasks.push(e),n):(Ma(e=Ra(n,null,{nodes:t.replayNodes,slots:t.replaySlots,pendingTasks:0},e,-1,null,null,n.abortableTasks,null,t.rootFormatContext,null,Nr,null,null)),n.pingedTasks.push(e),n)}var xa=null;function wa(e,t){e.pingedTasks.push(t),1===e.pingedTasks.length&&(e.flushScheduled=null!==e.destination,null!==e.trackedPostpones||10===e.status?O(function(){return pi(e)}):x(function(){return pi(e)}))}function Ia(e,t,n,r,a){return n={status:0,rootSegmentID:-1,parentFlushed:!1,pendingTasks:0,row:t,completedSegments:[],byteSize:0,fallbackAbortableTasks:n,errorDigest:null,contentState:rr(),fallbackState:rr(),contentPreamble:r,fallbackPreamble:a,trackedContentKeyPath:null,trackedFallbackNode:null},null!==t&&(t.pendingTasks++,null!==(r=t.boundaries)&&(e.allPendingTasks++,n.pendingTasks++,r.push(n)),null!==(e=t.inheritedHoistables)&&pr(n.contentState,e)),n}function Oa(e,t,n,r,a,i,s,o,l,c,u,d,p,h,f){e.allPendingTasks++,null===a?e.pendingRootTasks++:a.pendingTasks++,null!==h&&h.pendingTasks++;var m={replay:null,node:n,childIndex:r,ping:function(){return wa(e,m)},blockedBoundary:a,blockedSegment:i,blockedPreamble:s,hoistableState:o,abortSet:l,keyPath:c,formatContext:u,context:d,treeContext:p,row:h,componentStack:f,thenableState:t};return l.add(m),m}function Ra(e,t,n,r,a,i,s,o,l,c,u,d,p,h){e.allPendingTasks++,null===i?e.pendingRootTasks++:i.pendingTasks++,null!==p&&p.pendingTasks++,n.pendingTasks++;var f={replay:n,node:r,childIndex:a,ping:function(){return wa(e,f)},blockedBoundary:i,blockedSegment:null,blockedPreamble:null,hoistableState:s,abortSet:o,keyPath:l,formatContext:c,context:u,treeContext:d,row:p,componentStack:h,thenableState:t};return o.add(f),f}function Da(e,t,n,r,a,i){return{status:0,parentFlushed:!1,id:-1,index:t,chunks:[],children:[],preambleChildren:[],parentFormatContext:r,boundary:n,lastPushedText:a,textEmbedded:i}}function Ma(e){var t=e.node;if(\"object\"==typeof t&&null!==t&&t.$$typeof===r)e.componentStack={parent:e.componentStack,type:t.type}}function Pa(e){return null===e?null:{parent:e.parent,type:\"Suspense Fallback\"}}function La(e){var t={};return e&&Object.defineProperty(t,\"componentStack\",{configurable:!0,enumerable:!0,get:function(){try{var n=\"\",r=e;do{n+=_a(r.type),r=r.parent}while(r);var a=n}catch(i){a=\"\\nError generating stack: \"+i.message+\"\\n\"+i.stack}return Object.defineProperty(t,\"componentStack\",{value:a}),a}}),t}function Fa(e,t,n){if(null==(t=(e=e.onError)(t,n))||\"string\"==typeof t)return t}function Ba(e,t){var n=e.onShellError,r=e.onFatalError;n(t),r(t),null!==e.destination?(e.status=14,z(e.destination,t)):(e.status=13,e.fatalError=t)}function Ua(e,t){Ha(e,t.next,t.hoistables)}function Ha(e,t,n){for(;null!==t;){null!==n&&(pr(t.hoistables,n),t.inheritedHoistables=n);var r=t.boundaries;if(null!==r){t.boundaries=null;for(var a=0;a<r.length;a++){var i=r[a];null!==n&&pr(i.contentState,n),di(e,i,null,null)}}if(t.pendingTasks--,0<t.pendingTasks)break;n=t.hoistables,t=t.next}}function za(e,t){var n=t.boundaries;if(null!==n&&t.pendingTasks===n.length){for(var r=!0,a=0;a<n.length;a++){var i=n[a];if(1!==i.pendingTasks||i.parentFlushed||Ta(0,i)){r=!1;break}}r&&Ha(e,t,t.hoistables)}}function Ga(e){var t={pendingTasks:1,boundaries:null,hoistables:rr(),inheritedHoistables:null,together:!1,next:null};return null!==e&&0<e.pendingTasks&&(t.pendingTasks++,t.boundaries=[],e.next=t),t}function ja(e,t,n,r,a){var i=t.keyPath,s=t.treeContext,o=t.row;t.keyPath=n,n=r.length;var l=null;if(null!==t.replay){var c=t.replay.slots;if(null!==c&&\"object\"==typeof c)for(var u=0;u<n;u++){var d=\"backwards\"!==a&&\"unstable_legacy-backwards\"!==a?u:n-1-u,p=r[d];t.row=l=Ga(l),t.treeContext=Cr(s,n,d);var h=c[d];\"number\"==typeof h?(Wa(e,t,h,p,d),delete c[d]):ni(e,t,p,d),0===--l.pendingTasks&&Ua(e,l)}else for(c=0;c<n;c++)d=r[u=\"backwards\"!==a&&\"unstable_legacy-backwards\"!==a?c:n-1-c],t.row=l=Ga(l),t.treeContext=Cr(s,n,u),ni(e,t,d,u),0===--l.pendingTasks&&Ua(e,l)}else if(\"backwards\"!==a&&\"unstable_legacy-backwards\"!==a)for(a=0;a<n;a++)c=r[a],t.row=l=Ga(l),t.treeContext=Cr(s,n,a),ni(e,t,c,a),0===--l.pendingTasks&&Ua(e,l);else{for(c=(a=t.blockedSegment).children.length,u=a.chunks.length,d=n-1;0<=d;d--){p=r[d],t.row=l=Ga(l),t.treeContext=Cr(s,n,d),h=Da(0,u,null,t.formatContext,0!==d||a.lastPushedText,!0),a.children.splice(c,0,h),t.blockedSegment=h;try{ni(e,t,p,d),h.lastPushedText&&h.textEmbedded&&h.chunks.push(Ie),h.status=1,ui(e,t.blockedBoundary,h),0===--l.pendingTasks&&Ua(e,l)}catch(f){throw h.status=12===e.status?3:4,f}}t.blockedSegment=a,a.lastPushedText=!1}null!==o&&null!==l&&0<l.pendingTasks&&(o.pendingTasks++,l.next=o),t.treeContext=s,t.row=o,t.keyPath=i}function $a(e,t,n,r,a,i){var s=t.thenableState;for(t.thenableState=null,Lr={},Fr=t,Br=e,Ur=n,qr=$r=0,Yr=-1,Wr=0,Vr=s,e=r(a,i);jr;)jr=!1,qr=$r=0,Yr=-1,Wr=0,Qr+=1,zr=null,e=r(a,i);return ta(),e}function qa(e,t,n,r,a,i,s){var o=!1;if(0!==i&&null!==e.formState){var l=t.blockedSegment;if(null!==l){o=!0,l=l.chunks;for(var c=0;c<i;c++)c===s?l.push(nt):l.push(rt)}}i=t.keyPath,t.keyPath=n,a?(n=t.treeContext,t.treeContext=Cr(n,1,0),ni(e,t,r,-1),t.treeContext=n):o?ni(e,t,r,-1):Va(e,t,r,-1),t.keyPath=i}function Ya(e,t,r,a,i,s){if(\"function\"==typeof a)if(a.prototype&&a.prototype.isReactComponent){var _=i;if(\"ref\"in i)for(var T in _={},i)\"ref\"!==T&&(_[T]=i[T]);var A=a.defaultProps;if(A)for(var N in _===i&&(_=G({},_,i)),A)void 0===_[N]&&(_[N]=A[N]);i=_,_=br,\"object\"==typeof(A=a.contextType)&&null!==A&&(_=A._currentValue);var C=void 0!==(_=new a(i,_)).state?_.state:null;if(_.updater=Ar,_.props=i,_.state=C,A={queue:[],replace:!1},_._reactInternals=A,s=a.contextType,_.context=\"object\"==typeof s&&null!==s?s._currentValue:br,\"function\"==typeof(s=a.getDerivedStateFromProps)&&(C=null==(s=s(i,C))?C:G({},C,s),_.state=C),\"function\"!=typeof a.getDerivedStateFromProps&&\"function\"!=typeof _.getSnapshotBeforeUpdate&&(\"function\"==typeof _.UNSAFE_componentWillMount||\"function\"==typeof _.componentWillMount))if(a=_.state,\"function\"==typeof _.componentWillMount&&_.componentWillMount(),\"function\"==typeof _.UNSAFE_componentWillMount&&_.UNSAFE_componentWillMount(),a!==_.state&&Ar.enqueueReplaceState(_,_.state,null),null!==A.queue&&0<A.queue.length)if(a=A.queue,s=A.replace,A.queue=null,A.replace=!1,s&&1===a.length)_.state=a[0];else{for(A=s?a[0]:_.state,C=!0,s=s?1:0;s<a.length;s++)null!=(N=\"function\"==typeof(N=a[s])?N.call(_,A,i,void 0):N)&&(C?(C=!1,A=G({},A,N)):G(A,N));_.state=A}else A.queue=null;if(a=_.render(),12===e.status)throw null;i=t.keyPath,t.keyPath=r,Va(e,t,a,-1),t.keyPath=i}else{if(a=$a(e,t,r,a,i,void 0),12===e.status)throw null;qa(e,t,r,a,0!==$r,qr,Yr)}else{if(\"string\"!=typeof a){switch(a){case y:case l:case c:case o:return a=t.keyPath,t.keyPath=r,Va(e,t,i.children,-1),void(t.keyPath=a);case E:return void(null===(a=t.blockedSegment)?\"hidden\"!==i.mode&&(a=t.keyPath,t.keyPath=r,ni(e,t,i.children,-1),t.keyPath=a):\"hidden\"!==i.mode&&(a.chunks.push(xt),a.lastPushedText=!1,_=t.keyPath,t.keyPath=r,ni(e,t,i.children,-1),t.keyPath=_,a.chunks.push(wt),a.lastPushedText=!1));case f:e:{if(a=i.children,\"forwards\"===(i=i.revealOrder)||\"backwards\"===i||\"unstable_legacy-backwards\"===i){if(v(a)){ja(e,t,r,a,i);break e}if((_=S(a))&&(_=_.call(a))){if(!(A=_.next()).done){do{A=_.next()}while(!A.done);ja(e,t,r,a,i)}break e}}\"together\"===i?(i=t.keyPath,_=t.row,(A=t.row=Ga(null)).boundaries=[],A.together=!0,t.keyPath=r,Va(e,t,a,-1),0===--A.pendingTasks&&Ua(e,A),t.keyPath=i,t.row=_,null!==_&&0<A.pendingTasks&&(_.pendingTasks++,A.next=_)):(i=t.keyPath,t.keyPath=r,Va(e,t,a,-1),t.keyPath=i)}return;case k:case b:throw Error(n(343));case h:e:if(null!==t.replay){a=t.keyPath,_=t.formatContext,A=t.row,t.keyPath=r,t.formatContext=we(e.resumableState,_),t.row=null,r=i.children;try{ni(e,t,r,-1)}finally{t.keyPath=a,t.formatContext=_,t.row=A}}else{a=t.keyPath,s=t.formatContext;var x=t.row;N=t.blockedBoundary,T=t.blockedPreamble;var w=t.hoistableState,I=t.blockedSegment,O=i.fallback;i=i.children;var R=new Set,D=2>t.formatContext.insertionMode?Ia(e,t.row,R,{htmlChunks:null,headChunks:null,bodyChunks:null},{htmlChunks:null,headChunks:null,bodyChunks:null}):Ia(e,t.row,R,null,null);null!==e.trackedPostpones&&(D.trackedContentKeyPath=r);var M=Da(0,I.chunks.length,D,t.formatContext,!1,!1);I.children.push(M),I.lastPushedText=!1;var P=Da(0,0,null,t.formatContext,!1,!1);if(P.parentFlushed=!0,null!==e.trackedPostpones){_=t.componentStack,C=[(A=[r[0],\"Suspense Fallback\",r[2]])[1],A[2],[],null],e.trackedPostpones.workingMap.set(A,C),D.trackedFallbackNode=C,t.blockedSegment=M,t.blockedPreamble=D.fallbackPreamble,t.keyPath=A,t.formatContext=xe(e.resumableState,s),t.componentStack=Pa(_),M.status=6;try{ni(e,t,O,-1),M.lastPushedText&&M.textEmbedded&&M.chunks.push(Ie),M.status=1,ui(e,N,M)}catch(L){throw M.status=12===e.status?3:4,L}finally{t.blockedSegment=I,t.blockedPreamble=T,t.keyPath=a,t.formatContext=s}Ma(t=Oa(e,null,i,-1,D,P,D.contentPreamble,D.contentState,t.abortSet,r,we(e.resumableState,t.formatContext),t.context,t.treeContext,null,_)),e.pingedTasks.push(t)}else{t.blockedBoundary=D,t.blockedPreamble=D.contentPreamble,t.hoistableState=D.contentState,t.blockedSegment=P,t.keyPath=r,t.formatContext=we(e.resumableState,s),t.row=null,P.status=6;try{if(ni(e,t,i,-1),P.lastPushedText&&P.textEmbedded&&P.chunks.push(Ie),P.status=1,ui(e,D,P),ci(D,P),0===D.pendingTasks&&0===D.status){if(D.status=1,!Ta(0,D)){null!==x&&0===--x.pendingTasks&&Ua(e,x),0===e.pendingRootTasks&&t.blockedPreamble&&mi(e);break e}}else null!==x&&x.together&&za(e,x)}catch(F){D.status=4,12===e.status?(P.status=3,_=e.fatalError):(P.status=4,_=F),C=Fa(e,_,A=La(t.componentStack)),D.errorDigest=C,Ja(e,D)}finally{t.blockedBoundary=N,t.blockedPreamble=T,t.hoistableState=w,t.blockedSegment=I,t.keyPath=a,t.formatContext=s,t.row=x}Ma(t=Oa(e,null,O,-1,N,M,D.fallbackPreamble,D.fallbackState,R,[r[0],\"Suspense Fallback\",r[2]],xe(e.resumableState,t.formatContext),t.context,t.treeContext,t.row,Pa(t.componentStack))),e.pingedTasks.push(t)}}return}if(\"object\"==typeof a&&null!==a)switch(a.$$typeof){case p:if(\"ref\"in i)for(I in _={},i)\"ref\"!==I&&(_[I]=i[I]);else _=i;return void qa(e,t,r,a=$a(e,t,r,a.render,_,s),0!==$r,qr,Yr);case m:return void Ya(e,t,r,a.type,i,s);case d:if(A=i.children,_=t.keyPath,i=i.value,C=a._currentValue,a._currentValue=i,Er=a={parent:s=Er,depth:null===s?0:s.depth+1,context:a,parentValue:C,value:i},t.context=a,t.keyPath=r,Va(e,t,A,-1),null===(e=Er))throw Error(n(403));return e.context._currentValue=e.parentValue,e=Er=e.parent,t.context=e,void(t.keyPath=_);case u:return a=(i=i.children)(a._context._currentValue),i=t.keyPath,t.keyPath=r,Va(e,t,a,-1),void(t.keyPath=i);case g:if(a=(_=a._init)(a._payload),12===e.status)throw null;return void Ya(e,t,r,a,i,s)}throw Error(n(130,null==a?a:typeof a,\"\"))}if(null===(_=t.blockedSegment))_=i.children,A=t.formatContext,C=t.keyPath,t.formatContext=Ne(A,a,i),t.keyPath=r,ni(e,t,_,-1),t.formatContext=A,t.keyPath=C;else{if(C=_t(_.chunks,a,i,e.resumableState,e.renderState,t.blockedPreamble,t.hoistableState,t.formatContext,_.lastPushedText),_.lastPushedText=!1,A=t.formatContext,s=t.keyPath,t.keyPath=r,3===(t.formatContext=Ne(A,a,i)).insertionMode){r=Da(0,0,null,t.formatContext,!1,!1),_.preambleChildren.push(r),t.blockedSegment=r;try{r.status=6,ni(e,t,C,-1),r.lastPushedText&&r.textEmbedded&&r.chunks.push(Ie),r.status=1,ui(e,t.blockedBoundary,r)}finally{t.blockedSegment=_}}else ni(e,t,C,-1);t.formatContext=A,t.keyPath=s;e:{switch(t=_.chunks,e=e.resumableState,a){case\"title\":case\"style\":case\"script\":case\"area\":case\"base\":case\"br\":case\"col\":case\"embed\":case\"hr\":case\"img\":case\"input\":case\"keygen\":case\"link\":case\"meta\":case\"param\":case\"source\":case\"track\":case\"wbr\":break e;case\"body\":if(1>=A.insertionMode){e.hasBody=!0;break e}break;case\"html\":if(0===A.insertionMode){e.hasHtml=!0;break e}break;case\"head\":if(1>=A.insertionMode)break e}t.push(Tt(a))}_.lastPushedText=!1}}}function Wa(e,t,n,r,a){var i=t.replay,s=t.blockedBoundary,o=Da(0,0,null,t.formatContext,!1,!1);o.id=n,o.parentFlushed=!0;try{t.replay=null,t.blockedSegment=o,ni(e,t,r,a),o.status=1,ui(e,s,o),null===s?e.completedRootSegment=o:(ci(s,o),s.parentFlushed&&e.partialBoundaries.push(s))}finally{t.replay=i,t.blockedSegment=null}}function Va(e,t,n,r){null!==t.replay&&\"number\"==typeof t.replay.slots?Wa(e,t,t.replay.slots,n,r):(t.node=n,t.childIndex=r,n=t.componentStack,Ma(t),Ka(e,t),t.componentStack=n)}function Ka(e,t){var i=t.node,s=t.childIndex;if(null!==i){if(\"object\"==typeof i){switch(i.$$typeof){case r:var o=i.type,l=i.key,c=i.props,u=void 0!==(i=c.ref)?i:null,p=gr(o),f=null==l?-1===s?0:s:l;if(l=[t.keyPath,p,f],null!==t.replay)e:{var m=t.replay;for(s=m.nodes,i=0;i<s.length;i++){var b=s[i];if(f===b[1]){if(4===b.length){if(null!==p&&p!==b[0])throw Error(n(490,b[0],p));var E=b[2];p=b[3],f=t.node,t.replay={nodes:E,slots:p,pendingTasks:1};try{if(Ya(e,t,l,o,c,u),1===t.replay.pendingTasks&&0<t.replay.nodes.length)throw Error(n(488));t.replay.pendingTasks--}catch(I){if(\"object\"==typeof I&&null!==I&&(I===Rr||\"function\"==typeof I.then))throw t.node===f?t.replay=m:s.splice(i,1),I;t.replay.pendingTasks--,c=La(t.componentStack),ai(l=e,e=t.blockedBoundary,E,p,o=I,c=Fa(l,o,c))}t.replay=m}else{if(o!==h)throw Error(n(490,\"Suspense\",gr(o)||\"Unknown\"));t:{m=void 0,o=b[5],u=b[2],p=b[3],f=null===b[4]?[]:b[4][2],b=null===b[4]?null:b[4][3];var y=t.keyPath,_=t.formatContext,k=t.row,T=t.replay,A=t.blockedBoundary,N=t.hoistableState,C=c.children,x=c.fallback,w=new Set;(c=2>t.formatContext.insertionMode?Ia(e,t.row,w,Se(),Se()):Ia(e,t.row,w,null,null)).parentFlushed=!0,c.rootSegmentID=o,t.blockedBoundary=c,t.hoistableState=c.contentState,t.keyPath=l,t.formatContext=we(e.resumableState,_),t.row=null,t.replay={nodes:u,slots:p,pendingTasks:1};try{if(ni(e,t,C,-1),1===t.replay.pendingTasks&&0<t.replay.nodes.length)throw Error(n(488));if(t.replay.pendingTasks--,0===c.pendingTasks&&0===c.status){c.status=1,e.completedBoundaries.push(c);break t}}catch(O){c.status=4,m=Fa(e,O,E=La(t.componentStack)),c.errorDigest=m,t.replay.pendingTasks--,e.clientRenderedBoundaries.push(c)}finally{t.blockedBoundary=A,t.hoistableState=N,t.replay=T,t.keyPath=y,t.formatContext=_,t.row=k}Ma(E=Ra(e,null,{nodes:f,slots:b,pendingTasks:0},x,-1,A,c.fallbackState,w,[l[0],\"Suspense Fallback\",l[2]],xe(e.resumableState,t.formatContext),t.context,t.treeContext,t.row,Pa(t.componentStack))),e.pingedTasks.push(E)}}s.splice(i,1);break e}}}else Ya(e,t,l,o,c,u);return;case a:throw Error(n(257));case g:if(i=(E=i._init)(i._payload),12===e.status)throw null;return void Va(e,t,i,s)}if(v(i))return void Qa(e,t,i,s);if((E=S(i))&&(E=E.call(i))){if(!(i=E.next()).done){c=[];do{c.push(i.value),i=E.next()}while(!i.done);Qa(e,t,c,s)}return}if(\"function\"==typeof i.then)return t.thenableState=null,Va(e,t,ua(i),s);if(i.$$typeof===d)return Va(e,t,i._currentValue,s);throw s=Object.prototype.toString.call(i),Error(n(31,\"[object Object]\"===s?\"object with keys {\"+Object.keys(i).join(\", \")+\"}\":s))}\"string\"==typeof i?null!==(s=t.blockedSegment)&&(s.lastPushedText=Oe(s.chunks,i,e.renderState,s.lastPushedText)):\"number\"!=typeof i&&\"bigint\"!=typeof i||null!==(s=t.blockedSegment)&&(s.lastPushedText=Oe(s.chunks,\"\"+i,e.renderState,s.lastPushedText))}}function Qa(e,t,r,a){var i=t.keyPath;if(-1===a||(t.keyPath=[t.keyPath,\"Fragment\",a],null===t.replay)){if(s=t.treeContext,o=r.length,null!==t.replay&&(null!==(l=t.replay.slots)&&\"object\"==typeof l)){for(a=0;a<o;a++)c=r[a],t.treeContext=Cr(s,o,a),\"number\"==typeof(u=l[a])?(Wa(e,t,u,c,a),delete l[a]):ni(e,t,c,a);return t.treeContext=s,void(t.keyPath=i)}for(l=0;l<o;l++)a=r[l],t.treeContext=Cr(s,o,l),ni(e,t,a,l);t.treeContext=s,t.keyPath=i}else{for(var s=t.replay,o=s.nodes,l=0;l<o.length;l++){var c=o[l];if(c[1]===a){a=c[2],c=c[3],t.replay={nodes:a,slots:c,pendingTasks:1};try{if(Qa(e,t,r,-1),1===t.replay.pendingTasks&&0<t.replay.nodes.length)throw Error(n(488));t.replay.pendingTasks--}catch(d){if(\"object\"==typeof d&&null!==d&&(d===Rr||\"function\"==typeof d.then))throw d;t.replay.pendingTasks--,r=La(t.componentStack);var u=t.blockedBoundary;ai(e,u,a,c,d,r=Fa(e,d,r))}t.replay=s,o.splice(l,1);break}}t.keyPath=i}}function Xa(e,t,r){if(r.status=5,r.rootSegmentID=e.nextSegmentId++,null===(e=r.trackedContentKeyPath))throw Error(n(486));var a=r.trackedFallbackNode,i=t.workingMap.get(e);return void 0===i?(r=[e[1],e[2],[],null,a,r.rootSegmentID],t.workingMap.set(e,r),xi(r,e[0],t),r):(i[4]=a,i[5]=r.rootSegmentID,i)}function Za(e,t,r,a){a.status=5;var i=r.keyPath,s=r.blockedBoundary;if(null===s)a.id=e.nextSegmentId++,t.rootSlots=a.id,null!==e.completedRootSegment&&(e.completedRootSegment.status=5);else{if(null!==s&&0===s.status){var o=Xa(e,t,s);if(s.trackedContentKeyPath===i&&-1===r.childIndex)return-1===a.id&&(a.id=a.parentFlushed?s.rootSegmentID:e.nextSegmentId++),void(o[3]=a.id)}if(-1===a.id&&(a.id=a.parentFlushed&&null!==s?s.rootSegmentID:e.nextSegmentId++),-1===r.childIndex)null===i?t.rootSlots=a.id:void 0===(r=t.workingMap.get(i))?xi(r=[i[1],i[2],[],a.id],i[0],t):r[3]=a.id;else{if(null===i){if(null===(e=t.rootSlots))e=t.rootSlots={};else if(\"number\"==typeof e)throw Error(n(491))}else if(void 0===(o=(s=t.workingMap).get(i)))e={},o=[i[1],i[2],[],e],s.set(i,o),xi(o,i[0],t);else if(null===(e=o[3]))e=o[3]={};else if(\"number\"==typeof e)throw Error(n(491));e[r.childIndex]=a.id}}}function Ja(e,t){null!==(e=e.trackedPostpones)&&(null!==(t=t.trackedContentKeyPath)&&(void 0!==(t=e.workingMap.get(t))&&(t.length=4,t[2]=[],t[3]=null)))}function ei(e,t,n){return Ra(e,n,t.replay,t.node,t.childIndex,t.blockedBoundary,t.hoistableState,t.abortSet,t.keyPath,t.formatContext,t.context,t.treeContext,t.row,t.componentStack)}function ti(e,t,n){var r=t.blockedSegment,a=Da(0,r.chunks.length,null,t.formatContext,r.lastPushedText,!0);return r.children.push(a),r.lastPushedText=!1,Oa(e,n,t.node,t.childIndex,t.blockedBoundary,a,t.blockedPreamble,t.hoistableState,t.abortSet,t.keyPath,t.formatContext,t.context,t.treeContext,t.row,t.componentStack)}function ni(e,t,n,r){var a=t.formatContext,i=t.context,s=t.keyPath,o=t.treeContext,l=t.componentStack,c=t.blockedSegment;if(null===c){c=t.replay;try{return Va(e,t,n,r)}catch(p){if(ta(),n=p===Rr?Mr():p,12!==e.status&&\"object\"==typeof n&&null!==n){if(\"function\"==typeof n.then)return e=ei(e,t,r=p===Rr?ea():null).ping,n.then(e,e),t.formatContext=a,t.context=i,t.keyPath=s,t.treeContext=o,t.componentStack=l,t.replay=c,void vr(i);if(\"Maximum call stack size exceeded\"===n.message)return n=ei(e,t,n=p===Rr?ea():null),e.pingedTasks.push(n),t.formatContext=a,t.context=i,t.keyPath=s,t.treeContext=o,t.componentStack=l,t.replay=c,void vr(i)}}}else{var u=c.children.length,d=c.chunks.length;try{return Va(e,t,n,r)}catch(h){if(ta(),c.children.length=u,c.chunks.length=d,n=h===Rr?Mr():h,12!==e.status&&\"object\"==typeof n&&null!==n){if(\"function\"==typeof n.then)return c=n,e=ti(e,t,n=h===Rr?ea():null).ping,c.then(e,e),t.formatContext=a,t.context=i,t.keyPath=s,t.treeContext=o,t.componentStack=l,void vr(i);if(\"Maximum call stack size exceeded\"===n.message)return c=ti(e,t,c=h===Rr?ea():null),e.pingedTasks.push(c),t.formatContext=a,t.context=i,t.keyPath=s,t.treeContext=o,t.componentStack=l,void vr(i)}}}throw t.formatContext=a,t.context=i,t.keyPath=s,t.treeContext=o,vr(i),n}function ri(e){var t=e.blockedBoundary,n=e.blockedSegment;null!==n&&(n.status=3,di(this,t,e.row,n))}function ai(e,t,r,a,i,s){for(var o=0;o<r.length;o++){var l=r[o];if(4===l.length)ai(e,t,l[2],l[3],i,s);else{l=l[5];var c=e,u=s,d=Ia(c,null,new Set,null,null);d.parentFlushed=!0,d.rootSegmentID=l,d.status=4,d.errorDigest=u,d.parentFlushed&&c.clientRenderedBoundaries.push(d)}}if(r.length=0,null!==a){if(null===t)throw Error(n(487));if(4!==t.status&&(t.status=4,t.errorDigest=s,t.parentFlushed&&e.clientRenderedBoundaries.push(t)),\"object\"==typeof a)for(var p in a)delete a[p]}}function ii(e,t,n){var r=e.blockedBoundary,a=e.blockedSegment;if(null!==a){if(6===a.status)return;a.status=3}var i=La(e.componentStack);if(null===r){if(13!==t.status&&14!==t.status){if(null===(r=e.replay))return void(null!==t.trackedPostpones&&null!==a?(r=t.trackedPostpones,Fa(t,n,i),Za(t,r,e,a),di(t,null,e.row,a)):(Fa(t,n,i),Ba(t,n)));r.pendingTasks--,0===r.pendingTasks&&0<r.nodes.length&&(a=Fa(t,n,i),ai(t,null,r.nodes,r.slots,n,a)),t.pendingRootTasks--,0===t.pendingRootTasks&&oi(t)}}else{var s=t.trackedPostpones;if(4!==r.status){if(null!==s&&null!==a)return Fa(t,n,i),Za(t,s,e,a),r.fallbackAbortableTasks.forEach(function(e){return ii(e,t,n)}),r.fallbackAbortableTasks.clear(),di(t,r,e.row,a);r.status=4,a=Fa(t,n,i),r.status=4,r.errorDigest=a,Ja(t,r),r.parentFlushed&&t.clientRenderedBoundaries.push(r)}r.pendingTasks--,null!==(a=r.row)&&0===--a.pendingTasks&&Ua(t,a),r.fallbackAbortableTasks.forEach(function(e){return ii(e,t,n)}),r.fallbackAbortableTasks.clear()}null!==(e=e.row)&&0===--e.pendingTasks&&Ua(t,e),t.allPendingTasks--,0===t.allPendingTasks&&li(t)}function si(e,t){try{var n=e.renderState,r=n.onHeaders;if(r){var a=n.headers;if(a){n.headers=null;var i=a.preconnects;if(a.fontPreloads&&(i&&(i+=\", \"),i+=a.fontPreloads),a.highImagePreloads&&(i&&(i+=\", \"),i+=a.highImagePreloads),!t){var s=n.styles.values(),o=s.next();e:for(;0<a.remainingCapacity&&!o.done;o=s.next())for(var l=o.value.sheets.values(),c=l.next();0<a.remainingCapacity&&!c.done;c=l.next()){var u=c.value,d=u.props,p=d.href,h=u.props,f=ir(h.href,\"style\",{crossOrigin:h.crossOrigin,integrity:h.integrity,nonce:h.nonce,type:h.type,fetchPriority:h.fetchPriority,referrerPolicy:h.referrerPolicy,media:h.media});if(!(0<=(a.remainingCapacity-=f.length+2)))break e;n.resets.style[p]=se,i&&(i+=\", \"),i+=f,n.resets.style[p]=\"string\"==typeof d.crossOrigin||\"string\"==typeof d.integrity?[d.crossOrigin,d.integrity]:se}}r(i?{Link:i}:{})}}}catch(m){Fa(e,m,{})}}function oi(e){null===e.trackedPostpones&&si(e,!0),null===e.trackedPostpones&&mi(e),e.onShellError=Or,(e=e.onShellReady)()}function li(e){si(e,null===e.trackedPostpones||(null===e.completedRootSegment||5!==e.completedRootSegment.status)),mi(e),(e=e.onAllReady)()}function ci(e,t){if(0===t.chunks.length&&1===t.children.length&&null===t.children[0].boundary&&-1===t.children[0].id){var n=t.children[0];n.id=t.id,n.parentFlushed=!0,1!==n.status&&3!==n.status&&4!==n.status||ci(e,n)}else e.completedSegments.push(t)}function ui(e,t,n){if(null!==H){n=n.chunks;for(var r=0,a=0;a<n.length;a++)r+=n[a].byteLength;null===t?e.byteSize+=r:t.byteSize+=r}}function di(e,t,r,a){if(null!==r&&(0===--r.pendingTasks?Ua(e,r):r.together&&za(e,r)),e.allPendingTasks--,null===t){if(null!==a&&a.parentFlushed){if(null!==e.completedRootSegment)throw Error(n(389));e.completedRootSegment=a}e.pendingRootTasks--,0===e.pendingRootTasks&&oi(e)}else if(t.pendingTasks--,4!==t.status)if(0===t.pendingTasks){if(0===t.status&&(t.status=1),null!==a&&a.parentFlushed&&(1===a.status||3===a.status)&&ci(t,a),t.parentFlushed&&e.completedBoundaries.push(t),1===t.status)null!==(r=t.row)&&pr(r.hoistables,t.contentState),Ta(0,t)||(t.fallbackAbortableTasks.forEach(ri,e),t.fallbackAbortableTasks.clear(),null!==r&&0===--r.pendingTasks&&Ua(e,r)),0===e.pendingRootTasks&&null===e.trackedPostpones&&null!==t.contentPreamble&&mi(e);else if(5===t.status&&null!==(t=t.row)){if(null!==e.trackedPostpones){r=e.trackedPostpones;var i=t.next;if(null!==i&&null!==(a=i.boundaries))for(i.boundaries=null,i=0;i<a.length;i++){var s=a[i];Xa(e,r,s),di(e,s,null,null)}}0===--t.pendingTasks&&Ua(e,t)}}else null===a||!a.parentFlushed||1!==a.status&&3!==a.status||(ci(t,a),1===t.completedSegments.length&&t.parentFlushed&&e.partialBoundaries.push(t)),null!==(t=t.row)&&t.together&&za(e,t);0===e.allPendingTasks&&li(e)}function pi(e){if(14!==e.status&&13!==e.status){var t=Er,r=ne.H;ne.H=fa;var a=ne.A;ne.A=ga;var i=xa;xa=e;var s=ma;ma=e.resumableState;try{var o,l=e.pingedTasks;for(o=0;o<l.length;o++){var c=l[o],u=e,d=c.blockedSegment;if(null===d){var p=u;if(0!==c.replay.pendingTasks){vr(c.context);try{if(\"number\"==typeof c.replay.slots?Wa(p,c,c.replay.slots,c.node,c.childIndex):Ka(p,c),1===c.replay.pendingTasks&&0<c.replay.nodes.length)throw Error(n(488));c.replay.pendingTasks--,c.abortSet.delete(c),di(p,c.blockedBoundary,c.row,null)}catch(w){ta();var h=w===Rr?Mr():w;if(\"object\"==typeof h&&null!==h&&\"function\"==typeof h.then){var f=c.ping;h.then(f,f),c.thenableState=w===Rr?ea():null}else{c.replay.pendingTasks--,c.abortSet.delete(c);var m=La(c.componentStack);u=void 0;var g=p,b=c.blockedBoundary,E=12===p.status?p.fatalError:h;ai(g,b,c.replay.nodes,c.replay.slots,E,u=Fa(g,E,m)),p.pendingRootTasks--,0===p.pendingRootTasks&&oi(p),p.allPendingTasks--,0===p.allPendingTasks&&li(p)}}}}else if(p=void 0,0===(g=d).status){g.status=6,vr(c.context);var y=g.children.length,_=g.chunks.length;try{Ka(u,c),g.lastPushedText&&g.textEmbedded&&g.chunks.push(Ie),c.abortSet.delete(c),g.status=1,ui(u,c.blockedBoundary,g),di(u,c.blockedBoundary,c.row,g)}catch(w){ta(),g.children.length=y,g.chunks.length=_;var k=w===Rr?Mr():12===u.status?u.fatalError:w;if(12===u.status&&null!==u.trackedPostpones){var T=u.trackedPostpones,S=La(c.componentStack);c.abortSet.delete(c),Fa(u,k,S),Za(u,T,c,g),di(u,c.blockedBoundary,c.row,g)}else if(\"object\"==typeof k&&null!==k&&\"function\"==typeof k.then){g.status=0,c.thenableState=w===Rr?ea():null;var v=c.ping;k.then(v,v)}else{var A=La(c.componentStack);c.abortSet.delete(c),g.status=4;var N=c.blockedBoundary,C=c.row;if(null!==C&&0===--C.pendingTasks&&Ua(u,C),u.allPendingTasks--,p=Fa(u,k,A),null===N)Ba(u,k);else if(N.pendingTasks--,4!==N.status){N.status=4,N.errorDigest=p,Ja(u,N);var x=N.row;null!==x&&0===--x.pendingTasks&&Ua(u,x),N.parentFlushed&&u.clientRenderedBoundaries.push(N),0===u.pendingRootTasks&&null===u.trackedPostpones&&null!==N.contentPreamble&&mi(u)}0===u.allPendingTasks&&li(u)}}}}l.splice(0,o),null!==e.destination&&Si(e,e.destination)}catch(I){Fa(e,I,{}),Ba(e,I)}finally{ma=s,ne.H=r,ne.A=a,r===fa&&vr(t),xa=i}}}function hi(e,t,n){t.preambleChildren.length&&n.push(t.preambleChildren);for(var r=!1,a=0;a<t.children.length;a++)r=fi(e,t.children[a],n)||r;return r}function fi(e,t,r){var a=t.boundary;if(null===a)return hi(e,t,r);var i=a.contentPreamble,s=a.fallbackPreamble;if(null===i||null===s)return!1;switch(a.status){case 1:if(St(e.renderState,i),e.byteSize+=a.byteSize,!(t=a.completedSegments[0]))throw Error(n(391));return hi(e,t,r);case 5:if(null!==e.trackedPostpones)return!0;case 4:if(1===t.status)return St(e.renderState,s),hi(e,t,r);default:return!0}}function mi(e){if(e.completedRootSegment&&null===e.completedPreambleSegments){var t=[],n=e.byteSize,r=fi(e,e.completedRootSegment,t),a=e.renderState.preamble;!1===r||a.headChunks&&a.bodyChunks?e.completedPreambleSegments=t:e.byteSize=n}}function gi(e,t,r,a){switch(r.parentFlushed=!0,r.status){case 0:r.id=e.nextSegmentId++;case 5:return a=r.id,r.lastPushedText=!1,r.textEmbedded=!1,e=e.renderState,M(t,Nt),M(t,e.placeholderPrefix),M(t,e=B(a.toString(16))),P(t,Ct);case 1:r.status=2;var i=!0,s=r.chunks,o=0;r=r.children;for(var l=0;l<r.length;l++){for(i=r[l];o<i.index;o++)M(t,s[o]);i=Ei(e,t,i,a)}for(;o<s.length-1;o++)M(t,s[o]);return o<s.length&&(i=P(t,s[o])),i;case 3:return!0;default:throw Error(n(390))}}var bi=0;function Ei(e,t,r,a){var i=r.boundary;if(null===i)return gi(e,t,r,a);if(i.parentFlushed=!0,4===i.status){var s=i.row;null!==s&&0===--s.pendingTasks&&Ua(e,s),i=i.errorDigest,P(t,Dt),M(t,Pt),i&&(M(t,Ft),M(t,B(X(i))),M(t,Lt)),P(t,Bt),gi(e,t,r,a)}else if(1!==i.status)0===i.status&&(i.rootSegmentID=e.nextSegmentId++),0<i.completedSegments.length&&e.partialBoundaries.push(i),Ut(t,e.renderState,i.rootSegmentID),a&&pr(a,i.fallbackState),gi(e,t,r,a);else if(!Ti&&Ta(0,i)&&(bi+i.byteSize>e.progressiveChunkSize||hr(i.contentState)))i.rootSegmentID=e.nextSegmentId++,e.completedBoundaries.push(i),Ut(t,e.renderState,i.rootSegmentID),gi(e,t,r,a);else{if(bi+=i.byteSize,a&&pr(a,i.contentState),null!==(r=i.row)&&Ta(0,i)&&0===--r.pendingTasks&&Ua(e,r),P(t,It),1!==(r=i.completedSegments).length)throw Error(n(391));Ei(e,t,r[0],a)}return P(t,Mt)}function yi(e,t,r,a){return function(e,t,r,a){switch(r.insertionMode){case 0:case 1:case 3:case 2:return M(e,Ht),M(e,t.segmentPrefix),M(e,B(a.toString(16))),P(e,zt);case 4:return M(e,jt),M(e,t.segmentPrefix),M(e,B(a.toString(16))),P(e,$t);case 5:return M(e,Yt),M(e,t.segmentPrefix),M(e,B(a.toString(16))),P(e,Wt);case 6:return M(e,Kt),M(e,t.segmentPrefix),M(e,B(a.toString(16))),P(e,Qt);case 7:return M(e,Zt),M(e,t.segmentPrefix),M(e,B(a.toString(16))),P(e,Jt);case 8:return M(e,tn),M(e,t.segmentPrefix),M(e,B(a.toString(16))),P(e,nn);case 9:return M(e,an),M(e,t.segmentPrefix),M(e,B(a.toString(16))),P(e,sn);default:throw Error(n(397))}}(t,e.renderState,r.parentFormatContext,r.id),Ei(e,t,r,a),function(e,t){switch(t.insertionMode){case 0:case 1:case 3:case 2:return P(e,Gt);case 4:return P(e,qt);case 5:return P(e,Vt);case 6:return P(e,Xt);case 7:return P(e,en);case 8:return P(e,rn);case 9:return P(e,on);default:throw Error(n(397))}}(t,r.parentFormatContext)}function _i(e,t,r){bi=r.byteSize;for(var a=r.completedSegments,i=0;i<a.length;i++)ki(e,t,r,a[i]);a.length=0,null!==(a=r.row)&&Ta(0,r)&&0===--a.pendingTasks&&Ua(e,a),Bn(t,r.contentState,e.renderState),a=e.resumableState,e=e.renderState,i=r.rootSegmentID,r=r.contentState;var s=e.stylesToHoist;return e.stylesToHoist=!1,M(t,e.startInlineScript),M(t,Qe),s?(!(4&a.instructions)&&(a.instructions|=4,M(t,_n)),!(2&a.instructions)&&(a.instructions|=2,M(t,pn)),8&a.instructions?M(t,mn):(a.instructions|=8,M(t,fn))):(!(2&a.instructions)&&(a.instructions|=2,M(t,pn)),M(t,hn)),a=B(i.toString(16)),M(t,e.boundaryPrefix),M(t,a),M(t,gn),M(t,e.segmentPrefix),M(t,a),s?(M(t,bn),function(e,t){M(e,Zn);var r=Zn;t.stylesheets.forEach(function(t){if(2!==t.state)if(3===t.state)M(e,r),M(e,B(wn(\"\"+t.props.href))),M(e,tr),r=Jn;else{M(e,r);var a=t.props[\"data-precedence\"],i=t.props,s=te(\"\"+t.props.href);for(var o in M(e,B(wn(s))),a=\"\"+a,M(e,er),M(e,B(wn(a))),i)if(j.call(i,o)&&null!=(a=i[o]))switch(o){case\"href\":case\"rel\":case\"precedence\":case\"data-precedence\":break;case\"children\":case\"dangerouslySetInnerHTML\":throw Error(n(399,\"link\"));default:nr(e,o,a)}M(e,tr),r=Jn,t.state=3}}),M(e,tr)}(t,r)):M(t,En),r=P(t,yn),vt(t,e)&&r}function ki(e,t,r,a){if(2===a.status)return!0;var i=r.contentState,s=a.id;if(-1===s){if(-1===(a.id=r.rootSegmentID))throw Error(n(392));return yi(e,t,a,i)}return s===r.rootSegmentID?yi(e,t,a,i):(yi(e,t,a,i),r=e.resumableState,M(t,(e=e.renderState).startInlineScript),M(t,Qe),1&r.instructions?M(t,cn):(r.instructions|=1,M(t,ln)),M(t,e.segmentPrefix),M(t,s=B(s.toString(16))),M(t,un),M(t,e.placeholderPrefix),M(t,s),t=P(t,dn))}var Ti=!1;function Si(e,t){R=new Uint8Array(2048),D=0;try{if(!(0<e.pendingRootTasks)){var n,r=e.completedRootSegment;if(null!==r){if(5===r.status)return;var a=e.completedPreambleSegments;if(null===a)return;bi=e.byteSize;var i,s=e.resumableState,o=e.renderState,l=o.preamble,c=l.htmlChunks,u=l.headChunks;if(c){for(i=0;i<c.length;i++)M(t,c[i]);if(u)for(i=0;i<u.length;i++)M(t,u[i]);else M(t,Et(\"head\")),M(t,Qe)}else if(u)for(i=0;i<u.length;i++)M(t,u[i]);var d=o.charsetChunks;for(i=0;i<d.length;i++)M(t,d[i]);d.length=0,o.preconnects.forEach(Un,t),o.preconnects.clear();var p=o.viewportChunks;for(i=0;i<p.length;i++)M(t,p[i]);p.length=0,o.fontPreloads.forEach(Un,t),o.fontPreloads.clear(),o.highImagePreloads.forEach(Un,t),o.highImagePreloads.clear(),oe=o,o.styles.forEach(Wn,t),oe=null;var h=o.importMapChunks;for(i=0;i<h.length;i++)M(t,h[i]);h.length=0,o.bootstrapScripts.forEach(Un,t),o.scripts.forEach(Un,t),o.scripts.clear(),o.bulkPreloads.forEach(Un,t),o.bulkPreloads.clear(),c||u||(s.instructions|=32);var f=o.hoistableChunks;for(i=0;i<f.length;i++)M(t,f[i]);for(s=f.length=0;s<a.length;s++){var m=a[s];for(o=0;o<m.length;o++)Ei(e,t,m[o],null)}var g=e.renderState.preamble,b=g.headChunks;(g.htmlChunks||b)&&M(t,Tt(\"head\"));var E=g.bodyChunks;if(E)for(a=0;a<E.length;a++)M(t,E[a]);Ei(e,t,r,null),e.completedRootSegment=null;var y=e.renderState;if(0!==e.allPendingTasks||0!==e.clientRenderedBoundaries.length||0!==e.completedBoundaries.length||null!==e.trackedPostpones&&(0!==e.trackedPostpones.rootNodes.length||null!==e.trackedPostpones.rootSlots)){var _=e.resumableState;if(!(64&_.instructions)){if(_.instructions|=64,M(t,y.startInlineScript),!(32&_.instructions)){_.instructions|=32;var k=\"_\"+_.idPrefix+\"R_\";M(t,Qn),M(t,B(X(k))),M(t,Ue)}M(t,Qe),M(t,At),P(t,ce)}}vt(t,y)}var T=e.renderState;r=0;var S=T.viewportChunks;for(r=0;r<S.length;r++)M(t,S[r]);S.length=0,T.preconnects.forEach(Un,t),T.preconnects.clear(),T.fontPreloads.forEach(Un,t),T.fontPreloads.clear(),T.highImagePreloads.forEach(Un,t),T.highImagePreloads.clear(),T.styles.forEach(Kn,t),T.scripts.forEach(Un,t),T.scripts.clear(),T.bulkPreloads.forEach(Un,t),T.bulkPreloads.clear();var v=T.hoistableChunks;for(r=0;r<v.length;r++)M(t,v[r]);v.length=0;var A=e.clientRenderedBoundaries;for(n=0;n<A.length;n++){var N=A[n];T=t;var C=e.resumableState,x=e.renderState,w=N.rootSegmentID,I=N.errorDigest;M(T,x.startInlineScript),M(T,Qe),4&C.instructions?M(T,Tn):(C.instructions|=4,M(T,kn)),M(T,x.boundaryPrefix),M(T,B(w.toString(16))),M(T,Sn),I&&(M(T,vn),M(T,B(Cn(I||\"\"))));var O=P(T,An);if(!O)return e.destination=null,n++,void A.splice(0,n)}A.splice(0,n);var F=e.completedBoundaries;for(n=0;n<F.length;n++)if(!_i(e,t,F[n]))return e.destination=null,n++,void F.splice(0,n);F.splice(0,n),L(t),R=new Uint8Array(2048),D=0,Ti=!0;var U=e.partialBoundaries;for(n=0;n<U.length;n++){var H=U[n];e:{A=e,N=t,bi=H.byteSize;var z=H.completedSegments;for(O=0;O<z.length;O++)if(!ki(A,N,H,z[O])){O++,z.splice(0,O);var G=!1;break e}z.splice(0,O);var j=H.row;null!==j&&j.together&&1===H.pendingTasks&&(1===j.pendingTasks?Ha(A,j,j.hoistables):j.pendingTasks--),G=Bn(N,H.contentState,A.renderState)}if(!G)return e.destination=null,n++,void U.splice(0,n)}U.splice(0,n),Ti=!1;var $=e.completedBoundaries;for(n=0;n<$.length;n++)if(!_i(e,t,$[n]))return e.destination=null,n++,void $.splice(0,n);$.splice(0,n)}}finally{Ti=!1,0===e.allPendingTasks&&0===e.clientRenderedBoundaries.length&&0===e.completedBoundaries.length?(e.flushScheduled=!1,(n=e.resumableState).hasBody&&M(t,Tt(\"body\")),n.hasHtml&&M(t,Tt(\"html\")),L(t),e.status=14,t.close(),e.destination=null):L(t)}}function vi(e){e.flushScheduled=null!==e.destination,O(function(){return pi(e)}),x(function(){10===e.status&&(e.status=11),null===e.trackedPostpones&&si(e,0===e.pendingRootTasks)})}function Ai(e){!1===e.flushScheduled&&0===e.pingedTasks.length&&null!==e.destination&&(e.flushScheduled=!0,x(function(){var t=e.destination;t?Si(e,t):e.flushScheduled=!1}))}function Ni(e,t){if(13===e.status)e.status=14,z(t,e.fatalError);else if(14!==e.status&&null===e.destination){e.destination=t;try{Si(e,t)}catch(n){Fa(e,n,{}),Ba(e,n)}}}function Ci(e,t){11!==e.status&&10!==e.status||(e.status=12);try{var r=e.abortableTasks;if(0<r.size){var a=void 0===t?Error(n(432)):\"object\"==typeof t&&null!==t&&\"function\"==typeof t.then?Error(n(530)):t;e.fatalError=a,r.forEach(function(t){return ii(t,e,a)}),r.clear()}null!==e.destination&&Si(e,e.destination)}catch(i){Fa(e,i,{}),Ba(e,i)}}function xi(e,t,n){if(null===t)n.rootNodes.push(e);else{var r=n.workingMap,a=r.get(t);void 0===a&&(a=[t[1],t[2],[],null],r.set(t,a),xi(a,t[0],n)),a[2].push(e)}}function wi(e){var t=e.trackedPostpones;if(null===t||0===t.rootNodes.length&&null===t.rootSlots)return e.trackedPostpones=null;if(null===e.completedRootSegment||5!==e.completedRootSegment.status&&null!==e.completedPreambleSegments){var n=e.nextSegmentId,r=t.rootSlots,a=e.resumableState;a.bootstrapScriptContent=void 0,a.bootstrapScripts=void 0,a.bootstrapModules=void 0}else{n=0,r=-1,a=e.resumableState;var i=e.renderState;a.nextFormID=0,a.hasBody=!1,a.hasHtml=!1,a.unknownResources={font:i.resets.font},a.dnsResources=i.resets.dns,a.connectResources=i.resets.connect,a.imageResources=i.resets.image,a.styleResources=i.resets.style,a.scriptResources={},a.moduleUnknownResources={},a.moduleScriptResources={},a.instructions=0}return{nextSegmentId:n,rootFormatContext:e.rootFormatContext,progressiveChunkSize:e.progressiveChunkSize,resumableState:e.resumableState,replayNodes:t.rootNodes,replaySlots:r}}function Ii(){var t=e.version;if(\"19.2.3\"!==t)throw Error(n(527,t,\"19.2.3\"))}return Ii(),Ii(),Sa.prerender=function(e,t){return new Promise(function(n,r){var a,i=t?t.onHeaders:void 0;i&&(a=function(e){i(new Headers(e))});var s=Te(t?t.identifierPrefix:void 0,t&&t.unstable_externalRuntimeSrc,t?t.bootstrapScriptContent:void 0,t?t.bootstrapScripts:void 0,t?t.bootstrapModules:void 0),o=function(e,t,n,r,a,i,s,o,l,c,u){return(e=Na(e,t,n,r,a,i,s,o,l,c,u,void 0)).trackedPostpones={workingMap:new Map,rootNodes:[],rootSlots:null},e}(e,s,ke(s,void 0,t?t.unstable_externalRuntimeSrc:void 0,t?t.importMap:void 0,a,t?t.maxHeadersLength:void 0),Ae(t?t.namespaceURI:void 0),t?t.progressiveChunkSize:void 0,t?t.onError:void 0,function(){var e=new ReadableStream({type:\"bytes\",pull:function(e){Ni(o,e)},cancel:function(e){o.destination=null,Ci(o,e)}},{highWaterMark:0});e={postponed:wi(o),prelude:e},n(e)},void 0,void 0,r,t?t.onPostpone:void 0);if(t&&t.signal){var l=t.signal;if(l.aborted)Ci(o,l.reason);else{var c=function(){Ci(o,l.reason),l.removeEventListener(\"abort\",c)};l.addEventListener(\"abort\",c)}}vi(o)})},Sa.renderToReadableStream=function(e,t){return new Promise(function(n,r){var a,i,s,o=new Promise(function(e,t){i=e,a=t}),l=t?t.onHeaders:void 0;l&&(s=function(e){l(new Headers(e))});var c=Te(t?t.identifierPrefix:void 0,t&&t.unstable_externalRuntimeSrc,t?t.bootstrapScriptContent:void 0,t?t.bootstrapScripts:void 0,t?t.bootstrapModules:void 0),u=Na(e,c,ke(c,t?t.nonce:void 0,t?t.unstable_externalRuntimeSrc:void 0,t?t.importMap:void 0,s,t?t.maxHeadersLength:void 0),Ae(t?t.namespaceURI:void 0),t?t.progressiveChunkSize:void 0,t?t.onError:void 0,i,function(){var e=new ReadableStream({type:\"bytes\",pull:function(e){Ni(u,e)},cancel:function(e){u.destination=null,Ci(u,e)}},{highWaterMark:0});e.allReady=o,n(e)},function(e){o.catch(function(){}),r(e)},a,t?t.onPostpone:void 0,t?t.formState:void 0);if(t&&t.signal){var d=t.signal;if(d.aborted)Ci(u,d.reason);else{var p=function(){Ci(u,d.reason),d.removeEventListener(\"abort\",p)};d.addEventListener(\"abort\",p)}}vi(u)})},Sa.resume=function(e,t,n){return new Promise(function(r,a){var i,s,o=new Promise(function(e,t){s=e,i=t}),l=Ca(e,t,ke(t.resumableState,n?n.nonce:void 0,void 0,void 0,void 0,void 0),n?n.onError:void 0,s,function(){var e=new ReadableStream({type:\"bytes\",pull:function(e){Ni(l,e)},cancel:function(e){l.destination=null,Ci(l,e)}},{highWaterMark:0});e.allReady=o,r(e)},function(e){o.catch(function(){}),a(e)},i,n?n.onPostpone:void 0);if(n&&n.signal){var c=n.signal;if(c.aborted)Ci(l,c.reason);else{var u=function(){Ci(l,c.reason),c.removeEventListener(\"abort\",u)};c.addEventListener(\"abort\",u)}}vi(l)})},Sa.resumeAndPrerender=function(e,t,n){return new Promise(function(r,a){var i=function(e,t,n,r,a,i,s,o,l){return(e=Ca(e,t,n,r,a,i,s,o,l)).trackedPostpones={workingMap:new Map,rootNodes:[],rootSlots:null},e}(e,t,ke(t.resumableState,void 0,void 0,void 0,void 0,void 0),n?n.onError:void 0,function(){var e=new ReadableStream({type:\"bytes\",pull:function(e){Ni(i,e)},cancel:function(e){i.destination=null,Ci(i,e)}},{highWaterMark:0});e={postponed:wi(i),prelude:e},r(e)},void 0,void 0,a,n?n.onPostpone:void 0);if(n&&n.signal){var s=n.signal;if(s.aborted)Ci(i,s.reason);else{var o=function(){Ci(i,s.reason),s.removeEventListener(\"abort\",o)};s.addEventListener(\"abort\",o)}}vi(i)})},Sa.version=\"19.2.3\",Sa}var Aa,Na,Ca=(Ta||(Ta=1,Aa=_a(),Na=va(),Ea.version=Aa.version,Ea.renderToString=Aa.renderToString,Ea.renderToStaticMarkup=Aa.renderToStaticMarkup,Ea.renderToReadableStream=Na.renderToReadableStream,Ea.resume=Na.resume),Ea);const xa=function(e,t,n){const r=Tr(n);if(!e||!e.type||!e.children)throw new Error(\"Expected parent node\");if(\"number\"==typeof t){if(t<0||t===Number.POSITIVE_INFINITY)throw new Error(\"Expected positive finite number as index\")}else if((t=e.children.indexOf(t))<0)throw new Error(\"Expected child node or index\");for(;++t<e.children.length;)if(r(e.children[t],t,e))return e.children[t]},wa=function(e){if(null==e)return Oa;if(\"string\"==typeof e)return function(e){return Ia(t);function t(t){return t.tagName===e}}(e);if(\"object\"==typeof e)return function(e){const t=[];let n=-1;for(;++n<e.length;)t[n]=wa(e[n]);return Ia(r);function r(...e){let n=-1;for(;++n<t.length;)if(t[n].apply(this,e))return!0;return!1}}(e);if(\"function\"==typeof e)return Ia(e);throw new Error(\"Expected function, string, or array as `test`\")};function Ia(e){return function(t,n,r){return Boolean(function(e){return null!==e&&\"object\"==typeof e&&\"type\"in e&&\"tagName\"in e}(t)&&e.call(this,t,\"number\"==typeof n?n:void 0,r||void 0))}}function Oa(e){return Boolean(e&&\"object\"==typeof e&&\"type\"in e&&\"element\"===e.type&&\"tagName\"in e&&\"string\"==typeof e.tagName)}const Ra=/\\n/g,Da=/[\\t ]+/g,Ma=wa(\"br\"),Pa=wa(function(e){return\"td\"===e.tagName||\"th\"===e.tagName}),La=wa(\"p\"),Fa=wa(\"tr\"),Ba=wa([\"datalist\",\"head\",\"noembed\",\"noframes\",\"noscript\",\"rp\",\"script\",\"style\",\"template\",\"title\",function(e){return Boolean((e.properties||{}).hidden)},function(e){return\"dialog\"===e.tagName&&!(e.properties||{}).open}]),Ua=wa([\"address\",\"article\",\"aside\",\"blockquote\",\"body\",\"caption\",\"center\",\"dd\",\"dialog\",\"dir\",\"dl\",\"dt\",\"div\",\"figure\",\"figcaption\",\"footer\",\"form,\",\"h1\",\"h2\",\"h3\",\"h4\",\"h5\",\"h6\",\"header\",\"hgroup\",\"hr\",\"html\",\"legend\",\"li\",\"listing\",\"main\",\"menu\",\"nav\",\"ol\",\"p\",\"plaintext\",\"pre\",\"section\",\"ul\",\"xmp\"]);function Ha(e,t,n){return\"element\"===e.type?function(e,t,n){const r=ja(e,n),a=e.children||[];let i,s,o=-1,l=[];if(Ba(e))return l;Ma(e)||Fa(e)&&xa(t,e,Fa)?s=\"\\n\":La(e)?(i=2,s=2):Ua(e)&&(i=1,s=1);for(;++o<a.length;)l=l.concat(Ha(a[o],e,{whitespace:r,breakBefore:o?void 0:i,breakAfter:o<a.length-1?Ma(a[o+1]):s}));Pa(e)&&xa(t,e,Pa)&&l.push(\"\\t\");i&&l.unshift(i);s&&l.push(s);return l}(e,t,n):\"text\"===e.type?\"normal\"===n.whitespace?za(e,n):function(e){return[String(e.value)]}(e):[]}function za(e,t){const n=String(e.value),r=[],a=[];let i=0;for(;i<=n.length;){Ra.lastIndex=i;const e=Ra.exec(n),a=e&&\"index\"in e?e.index:n.length;r.push(Ga(n.slice(i,a).replace(/[\\u061C\\u200E\\u200F\\u202A-\\u202E\\u2066-\\u2069]/g,\"\"),0!==i||t.breakBefore,a!==n.length||t.breakAfter)),i=a+1}let s,o=-1;for(;++o<r.length;)8203===r[o].charCodeAt(r[o].length-1)||o<r.length-1&&8203===r[o+1].charCodeAt(0)?(a.push(r[o]),s=void 0):r[o]?(\"number\"==typeof s&&a.push(s),a.push(r[o]),s=0):0!==o&&o!==r.length-1||a.push(0);return a}function Ga(e,t,n){const r=[];let a,i=0;for(;i<e.length;){Da.lastIndex=i;const n=Da.exec(e);a=n?n.index:e.length,i||a||!n||t||r.push(\"\"),i!==a&&r.push(e.slice(i,a)),i=n?a+n[0].length:a}return i===a||n||r.push(\"\"),r.join(\" \")}function ja(e,t){if(\"element\"===e.type){const n=e.properties||{};switch(e.tagName){case\"listing\":case\"plaintext\":case\"xmp\":return\"pre\";case\"nobr\":return\"nowrap\";case\"pre\":return n.wrap?\"pre-wrap\":\"pre\";case\"td\":case\"th\":return n.noWrap?\"nowrap\":t.whitespace;case\"textarea\":return\"pre-wrap\"}}return t.whitespace}const $a=[\"a\",\"abbr\",\"address\",\"article\",\"aside\",\"audio\",\"b\",\"blockquote\",\"body\",\"button\",\"canvas\",\"caption\",\"cite\",\"code\",\"dd\",\"del\",\"details\",\"dfn\",\"div\",\"dl\",\"dt\",\"em\",\"fieldset\",\"figcaption\",\"figure\",\"footer\",\"form\",\"h1\",\"h2\",\"h3\",\"h4\",\"h5\",\"h6\",\"header\",\"hgroup\",\"html\",\"i\",\"iframe\",\"img\",\"input\",\"ins\",\"kbd\",\"label\",\"legend\",\"li\",\"main\",\"mark\",\"menu\",\"nav\",\"object\",\"ol\",\"optgroup\",\"option\",\"p\",\"picture\",\"q\",\"quote\",\"samp\",\"section\",\"select\",\"source\",\"span\",\"strong\",\"summary\",\"sup\",\"table\",\"tbody\",\"td\",\"textarea\",\"tfoot\",\"th\",\"thead\",\"time\",\"tr\",\"ul\",\"var\",\"video\",\"defs\",\"g\",\"marker\",\"mask\",\"pattern\",\"svg\",\"switch\",\"symbol\",\"feBlend\",\"feColorMatrix\",\"feComponentTransfer\",\"feComposite\",\"feConvolveMatrix\",\"feDiffuseLighting\",\"feDisplacementMap\",\"feFlood\",\"feGaussianBlur\",\"feImage\",\"feMerge\",\"feMorphology\",\"feOffset\",\"feSpecularLighting\",\"feTile\",\"feTurbulence\",\"linearGradient\",\"radialGradient\",\"stop\",\"circle\",\"ellipse\",\"image\",\"line\",\"path\",\"polygon\",\"polyline\",\"rect\",\"text\",\"use\",\"textPath\",\"tspan\",\"foreignObject\",\"clipPath\"],qa=[\"any-hover\",\"any-pointer\",\"aspect-ratio\",\"color\",\"color-gamut\",\"color-index\",\"device-aspect-ratio\",\"device-height\",\"device-width\",\"display-mode\",\"forced-colors\",\"grid\",\"height\",\"hover\",\"inverted-colors\",\"monochrome\",\"orientation\",\"overflow-block\",\"overflow-inline\",\"pointer\",\"prefers-color-scheme\",\"prefers-contrast\",\"prefers-reduced-motion\",\"prefers-reduced-transparency\",\"resolution\",\"scan\",\"scripting\",\"update\",\"width\",\"min-width\",\"max-width\",\"min-height\",\"max-height\"].sort().reverse(),Ya=[\"active\",\"any-link\",\"blank\",\"checked\",\"current\",\"default\",\"defined\",\"dir\",\"disabled\",\"drop\",\"empty\",\"enabled\",\"first\",\"first-child\",\"first-of-type\",\"fullscreen\",\"future\",\"focus\",\"focus-visible\",\"focus-within\",\"has\",\"host\",\"host-context\",\"hover\",\"indeterminate\",\"in-range\",\"invalid\",\"is\",\"lang\",\"last-child\",\"last-of-type\",\"left\",\"link\",\"local-link\",\"not\",\"nth-child\",\"nth-col\",\"nth-last-child\",\"nth-last-col\",\"nth-last-of-type\",\"nth-of-type\",\"only-child\",\"only-of-type\",\"optional\",\"out-of-range\",\"past\",\"placeholder-shown\",\"read-only\",\"read-write\",\"required\",\"right\",\"root\",\"scope\",\"target\",\"target-within\",\"user-invalid\",\"valid\",\"visited\",\"where\"].sort().reverse(),Wa=[\"after\",\"backdrop\",\"before\",\"cue\",\"cue-region\",\"first-letter\",\"first-line\",\"grammar-error\",\"marker\",\"part\",\"placeholder\",\"selection\",\"slotted\",\"spelling-error\"].sort().reverse(),Va=[\"accent-color\",\"align-content\",\"align-items\",\"align-self\",\"alignment-baseline\",\"all\",\"anchor-name\",\"animation\",\"animation-composition\",\"animation-delay\",\"animation-direction\",\"animation-duration\",\"animation-fill-mode\",\"animation-iteration-count\",\"animation-name\",\"animation-play-state\",\"animation-range\",\"animation-range-end\",\"animation-range-start\",\"animation-timeline\",\"animation-timing-function\",\"appearance\",\"aspect-ratio\",\"backdrop-filter\",\"backface-visibility\",\"background\",\"background-attachment\",\"background-blend-mode\",\"background-clip\",\"background-color\",\"background-image\",\"background-origin\",\"background-position\",\"background-position-x\",\"background-position-y\",\"background-repeat\",\"background-size\",\"baseline-shift\",\"block-size\",\"border\",\"border-block\",\"border-block-color\",\"border-block-end\",\"border-block-end-color\",\"border-block-end-style\",\"border-block-end-width\",\"border-block-start\",\"border-block-start-color\",\"border-block-start-style\",\"border-block-start-width\",\"border-block-style\",\"border-block-width\",\"border-bottom\",\"border-bottom-color\",\"border-bottom-left-radius\",\"border-bottom-right-radius\",\"border-bottom-style\",\"border-bottom-width\",\"border-collapse\",\"border-color\",\"border-end-end-radius\",\"border-end-start-radius\",\"border-image\",\"border-image-outset\",\"border-image-repeat\",\"border-image-slice\",\"border-image-source\",\"border-image-width\",\"border-inline\",\"border-inline-color\",\"border-inline-end\",\"border-inline-end-color\",\"border-inline-end-style\",\"border-inline-end-width\",\"border-inline-start\",\"border-inline-start-color\",\"border-inline-start-style\",\"border-inline-start-width\",\"border-inline-style\",\"border-inline-width\",\"border-left\",\"border-left-color\",\"border-left-style\",\"border-left-width\",\"border-radius\",\"border-right\",\"border-right-color\",\"border-right-style\",\"border-right-width\",\"border-spacing\",\"border-start-end-radius\",\"border-start-start-radius\",\"border-style\",\"border-top\",\"border-top-color\",\"border-top-left-radius\",\"border-top-right-radius\",\"border-top-style\",\"border-top-width\",\"border-width\",\"bottom\",\"box-align\",\"box-decoration-break\",\"box-direction\",\"box-flex\",\"box-flex-group\",\"box-lines\",\"box-ordinal-group\",\"box-orient\",\"box-pack\",\"box-shadow\",\"box-sizing\",\"break-after\",\"break-before\",\"break-inside\",\"caption-side\",\"caret-color\",\"clear\",\"clip\",\"clip-path\",\"clip-rule\",\"color\",\"color-interpolation\",\"color-interpolation-filters\",\"color-profile\",\"color-rendering\",\"color-scheme\",\"column-count\",\"column-fill\",\"column-gap\",\"column-rule\",\"column-rule-color\",\"column-rule-style\",\"column-rule-width\",\"column-span\",\"column-width\",\"columns\",\"contain\",\"contain-intrinsic-block-size\",\"contain-intrinsic-height\",\"contain-intrinsic-inline-size\",\"contain-intrinsic-size\",\"contain-intrinsic-width\",\"container\",\"container-name\",\"container-type\",\"content\",\"content-visibility\",\"counter-increment\",\"counter-reset\",\"counter-set\",\"cue\",\"cue-after\",\"cue-before\",\"cursor\",\"cx\",\"cy\",\"direction\",\"display\",\"dominant-baseline\",\"empty-cells\",\"enable-background\",\"field-sizing\",\"fill\",\"fill-opacity\",\"fill-rule\",\"filter\",\"flex\",\"flex-basis\",\"flex-direction\",\"flex-flow\",\"flex-grow\",\"flex-shrink\",\"flex-wrap\",\"float\",\"flood-color\",\"flood-opacity\",\"flow\",\"font\",\"font-display\",\"font-family\",\"font-feature-settings\",\"font-kerning\",\"font-language-override\",\"font-optical-sizing\",\"font-palette\",\"font-size\",\"font-size-adjust\",\"font-smooth\",\"font-smoothing\",\"font-stretch\",\"font-style\",\"font-synthesis\",\"font-synthesis-position\",\"font-synthesis-small-caps\",\"font-synthesis-style\",\"font-synthesis-weight\",\"font-variant\",\"font-variant-alternates\",\"font-variant-caps\",\"font-variant-east-asian\",\"font-variant-emoji\",\"font-variant-ligatures\",\"font-variant-numeric\",\"font-variant-position\",\"font-variation-settings\",\"font-weight\",\"forced-color-adjust\",\"gap\",\"glyph-orientation-horizontal\",\"glyph-orientation-vertical\",\"grid\",\"grid-area\",\"grid-auto-columns\",\"grid-auto-flow\",\"grid-auto-rows\",\"grid-column\",\"grid-column-end\",\"grid-column-start\",\"grid-gap\",\"grid-row\",\"grid-row-end\",\"grid-row-start\",\"grid-template\",\"grid-template-areas\",\"grid-template-columns\",\"grid-template-rows\",\"hanging-punctuation\",\"height\",\"hyphenate-character\",\"hyphenate-limit-chars\",\"hyphens\",\"icon\",\"image-orientation\",\"image-rendering\",\"image-resolution\",\"ime-mode\",\"initial-letter\",\"initial-letter-align\",\"inline-size\",\"inset\",\"inset-area\",\"inset-block\",\"inset-block-end\",\"inset-block-start\",\"inset-inline\",\"inset-inline-end\",\"inset-inline-start\",\"isolation\",\"justify-content\",\"justify-items\",\"justify-self\",\"kerning\",\"left\",\"letter-spacing\",\"lighting-color\",\"line-break\",\"line-height\",\"line-height-step\",\"list-style\",\"list-style-image\",\"list-style-position\",\"list-style-type\",\"margin\",\"margin-block\",\"margin-block-end\",\"margin-block-start\",\"margin-bottom\",\"margin-inline\",\"margin-inline-end\",\"margin-inline-start\",\"margin-left\",\"margin-right\",\"margin-top\",\"margin-trim\",\"marker\",\"marker-end\",\"marker-mid\",\"marker-start\",\"marks\",\"mask\",\"mask-border\",\"mask-border-mode\",\"mask-border-outset\",\"mask-border-repeat\",\"mask-border-slice\",\"mask-border-source\",\"mask-border-width\",\"mask-clip\",\"mask-composite\",\"mask-image\",\"mask-mode\",\"mask-origin\",\"mask-position\",\"mask-repeat\",\"mask-size\",\"mask-type\",\"masonry-auto-flow\",\"math-depth\",\"math-shift\",\"math-style\",\"max-block-size\",\"max-height\",\"max-inline-size\",\"max-width\",\"min-block-size\",\"min-height\",\"min-inline-size\",\"min-width\",\"mix-blend-mode\",\"nav-down\",\"nav-index\",\"nav-left\",\"nav-right\",\"nav-up\",\"none\",\"normal\",\"object-fit\",\"object-position\",\"offset\",\"offset-anchor\",\"offset-distance\",\"offset-path\",\"offset-position\",\"offset-rotate\",\"opacity\",\"order\",\"orphans\",\"outline\",\"outline-color\",\"outline-offset\",\"outline-style\",\"outline-width\",\"overflow\",\"overflow-anchor\",\"overflow-block\",\"overflow-clip-margin\",\"overflow-inline\",\"overflow-wrap\",\"overflow-x\",\"overflow-y\",\"overlay\",\"overscroll-behavior\",\"overscroll-behavior-block\",\"overscroll-behavior-inline\",\"overscroll-behavior-x\",\"overscroll-behavior-y\",\"padding\",\"padding-block\",\"padding-block-end\",\"padding-block-start\",\"padding-bottom\",\"padding-inline\",\"padding-inline-end\",\"padding-inline-start\",\"padding-left\",\"padding-right\",\"padding-top\",\"page\",\"page-break-after\",\"page-break-before\",\"page-break-inside\",\"paint-order\",\"pause\",\"pause-after\",\"pause-before\",\"perspective\",\"perspective-origin\",\"place-content\",\"place-items\",\"place-self\",\"pointer-events\",\"position\",\"position-anchor\",\"position-visibility\",\"print-color-adjust\",\"quotes\",\"r\",\"resize\",\"rest\",\"rest-after\",\"rest-before\",\"right\",\"rotate\",\"row-gap\",\"ruby-align\",\"ruby-position\",\"scale\",\"scroll-behavior\",\"scroll-margin\",\"scroll-margin-block\",\"scroll-margin-block-end\",\"scroll-margin-block-start\",\"scroll-margin-bottom\",\"scroll-margin-inline\",\"scroll-margin-inline-end\",\"scroll-margin-inline-start\",\"scroll-margin-left\",\"scroll-margin-right\",\"scroll-margin-top\",\"scroll-padding\",\"scroll-padding-block\",\"scroll-padding-block-end\",\"scroll-padding-block-start\",\"scroll-padding-bottom\",\"scroll-padding-inline\",\"scroll-padding-inline-end\",\"scroll-padding-inline-start\",\"scroll-padding-left\",\"scroll-padding-right\",\"scroll-padding-top\",\"scroll-snap-align\",\"scroll-snap-stop\",\"scroll-snap-type\",\"scroll-timeline\",\"scroll-timeline-axis\",\"scroll-timeline-name\",\"scrollbar-color\",\"scrollbar-gutter\",\"scrollbar-width\",\"shape-image-threshold\",\"shape-margin\",\"shape-outside\",\"shape-rendering\",\"speak\",\"speak-as\",\"src\",\"stop-color\",\"stop-opacity\",\"stroke\",\"stroke-dasharray\",\"stroke-dashoffset\",\"stroke-linecap\",\"stroke-linejoin\",\"stroke-miterlimit\",\"stroke-opacity\",\"stroke-width\",\"tab-size\",\"table-layout\",\"text-align\",\"text-align-all\",\"text-align-last\",\"text-anchor\",\"text-combine-upright\",\"text-decoration\",\"text-decoration-color\",\"text-decoration-line\",\"text-decoration-skip\",\"text-decoration-skip-ink\",\"text-decoration-style\",\"text-decoration-thickness\",\"text-emphasis\",\"text-emphasis-color\",\"text-emphasis-position\",\"text-emphasis-style\",\"text-indent\",\"text-justify\",\"text-orientation\",\"text-overflow\",\"text-rendering\",\"text-shadow\",\"text-size-adjust\",\"text-transform\",\"text-underline-offset\",\"text-underline-position\",\"text-wrap\",\"text-wrap-mode\",\"text-wrap-style\",\"timeline-scope\",\"top\",\"touch-action\",\"transform\",\"transform-box\",\"transform-origin\",\"transform-style\",\"transition\",\"transition-behavior\",\"transition-delay\",\"transition-duration\",\"transition-property\",\"transition-timing-function\",\"translate\",\"unicode-bidi\",\"user-modify\",\"user-select\",\"vector-effect\",\"vertical-align\",\"view-timeline\",\"view-timeline-axis\",\"view-timeline-inset\",\"view-timeline-name\",\"view-transition-name\",\"visibility\",\"voice-balance\",\"voice-duration\",\"voice-family\",\"voice-pitch\",\"voice-range\",\"voice-rate\",\"voice-stress\",\"voice-volume\",\"white-space\",\"white-space-collapse\",\"widows\",\"width\",\"will-change\",\"word-break\",\"word-spacing\",\"word-wrap\",\"writing-mode\",\"x\",\"y\",\"z-index\",\"zoom\"].sort().reverse();var Ka=\"[0-9](_*[0-9])*\",Qa=`\\\\.(${Ka})`,Xa=\"[0-9a-fA-F](_*[0-9a-fA-F])*\",Za={className:\"number\",variants:[{begin:`(\\\\b(${Ka})((${Qa})|\\\\.)?|(${Qa}))[eE][+-]?(${Ka})[fFdD]?\\\\b`},{begin:`\\\\b(${Ka})((${Qa})[fFdD]?\\\\b|\\\\.([fFdD]\\\\b)?)`},{begin:`(${Qa})[fFdD]?\\\\b`},{begin:`\\\\b(${Ka})[fFdD]\\\\b`},{begin:`\\\\b0[xX]((${Xa})\\\\.?|(${Xa})?\\\\.(${Xa}))[pP][+-]?(${Ka})[fFdD]?\\\\b`},{begin:\"\\\\b(0|[1-9](_*[0-9])*)[lL]?\\\\b\"},{begin:`\\\\b0[xX](${Xa})[lL]?\\\\b`},{begin:\"\\\\b0(_*[0-7])*[lL]?\\\\b\"},{begin:\"\\\\b0[bB][01](_*[01])*[lL]?\\\\b\"}],relevance:0};function Ja(e,t,n){return-1===n?\"\":e.replace(t,r=>Ja(e,t,n-1))}const ei=\"[A-Za-z$_][0-9A-Za-z$_]*\",ti=[\"as\",\"in\",\"of\",\"if\",\"for\",\"while\",\"finally\",\"var\",\"new\",\"function\",\"do\",\"return\",\"void\",\"else\",\"break\",\"catch\",\"instanceof\",\"with\",\"throw\",\"case\",\"default\",\"try\",\"switch\",\"continue\",\"typeof\",\"delete\",\"let\",\"yield\",\"const\",\"class\",\"debugger\",\"async\",\"await\",\"static\",\"import\",\"from\",\"export\",\"extends\",\"using\"],ni=[\"true\",\"false\",\"null\",\"undefined\",\"NaN\",\"Infinity\"],ri=[\"Object\",\"Function\",\"Boolean\",\"Symbol\",\"Math\",\"Date\",\"Number\",\"BigInt\",\"String\",\"RegExp\",\"Array\",\"Float32Array\",\"Float64Array\",\"Int8Array\",\"Uint8Array\",\"Uint8ClampedArray\",\"Int16Array\",\"Int32Array\",\"Uint16Array\",\"Uint32Array\",\"BigInt64Array\",\"BigUint64Array\",\"Set\",\"Map\",\"WeakSet\",\"WeakMap\",\"ArrayBuffer\",\"SharedArrayBuffer\",\"Atomics\",\"DataView\",\"JSON\",\"Promise\",\"Generator\",\"GeneratorFunction\",\"AsyncFunction\",\"Reflect\",\"Proxy\",\"Intl\",\"WebAssembly\"],ai=[\"Error\",\"EvalError\",\"InternalError\",\"RangeError\",\"ReferenceError\",\"SyntaxError\",\"TypeError\",\"URIError\"],ii=[\"setInterval\",\"setTimeout\",\"clearInterval\",\"clearTimeout\",\"require\",\"exports\",\"eval\",\"isFinite\",\"isNaN\",\"parseFloat\",\"parseInt\",\"decodeURI\",\"decodeURIComponent\",\"encodeURI\",\"encodeURIComponent\",\"escape\",\"unescape\"],si=[\"arguments\",\"this\",\"super\",\"console\",\"window\",\"document\",\"localStorage\",\"sessionStorage\",\"module\",\"global\"],oi=[].concat(ii,ri,ai);var li=\"[0-9](_*[0-9])*\",ci=`\\\\.(${li})`,ui=\"[0-9a-fA-F](_*[0-9a-fA-F])*\",di={className:\"number\",variants:[{begin:`(\\\\b(${li})((${ci})|\\\\.)?|(${ci}))[eE][+-]?(${li})[fFdD]?\\\\b`},{begin:`\\\\b(${li})((${ci})[fFdD]?\\\\b|\\\\.([fFdD]\\\\b)?)`},{begin:`(${ci})[fFdD]?\\\\b`},{begin:`\\\\b(${li})[fFdD]\\\\b`},{begin:`\\\\b0[xX]((${ui})\\\\.?|(${ui})?\\\\.(${ui}))[pP][+-]?(${li})[fFdD]?\\\\b`},{begin:\"\\\\b(0|[1-9](_*[0-9])*)[lL]?\\\\b\"},{begin:`\\\\b0[xX](${ui})[lL]?\\\\b`},{begin:\"\\\\b0(_*[0-7])*[lL]?\\\\b\"},{begin:\"\\\\b0[bB][01](_*[01])*[lL]?\\\\b\"}],relevance:0};const pi=[\"a\",\"abbr\",\"address\",\"article\",\"aside\",\"audio\",\"b\",\"blockquote\",\"body\",\"button\",\"canvas\",\"caption\",\"cite\",\"code\",\"dd\",\"del\",\"details\",\"dfn\",\"div\",\"dl\",\"dt\",\"em\",\"fieldset\",\"figcaption\",\"figure\",\"footer\",\"form\",\"h1\",\"h2\",\"h3\",\"h4\",\"h5\",\"h6\",\"header\",\"hgroup\",\"html\",\"i\",\"iframe\",\"img\",\"input\",\"ins\",\"kbd\",\"label\",\"legend\",\"li\",\"main\",\"mark\",\"menu\",\"nav\",\"object\",\"ol\",\"optgroup\",\"option\",\"p\",\"picture\",\"q\",\"quote\",\"samp\",\"section\",\"select\",\"source\",\"span\",\"strong\",\"summary\",\"sup\",\"table\",\"tbody\",\"td\",\"textarea\",\"tfoot\",\"th\",\"thead\",\"time\",\"tr\",\"ul\",\"var\",\"video\",\"defs\",\"g\",\"marker\",\"mask\",\"pattern\",\"svg\",\"switch\",\"symbol\",\"feBlend\",\"feColorMatrix\",\"feComponentTransfer\",\"feComposite\",\"feConvolveMatrix\",\"feDiffuseLighting\",\"feDisplacementMap\",\"feFlood\",\"feGaussianBlur\",\"feImage\",\"feMerge\",\"feMorphology\",\"feOffset\",\"feSpecularLighting\",\"feTile\",\"feTurbulence\",\"linearGradient\",\"radialGradient\",\"stop\",\"circle\",\"ellipse\",\"image\",\"line\",\"path\",\"polygon\",\"polyline\",\"rect\",\"text\",\"use\",\"textPath\",\"tspan\",\"foreignObject\",\"clipPath\"],hi=[\"any-hover\",\"any-pointer\",\"aspect-ratio\",\"color\",\"color-gamut\",\"color-index\",\"device-aspect-ratio\",\"device-height\",\"device-width\",\"display-mode\",\"forced-colors\",\"grid\",\"height\",\"hover\",\"inverted-colors\",\"monochrome\",\"orientation\",\"overflow-block\",\"overflow-inline\",\"pointer\",\"prefers-color-scheme\",\"prefers-contrast\",\"prefers-reduced-motion\",\"prefers-reduced-transparency\",\"resolution\",\"scan\",\"scripting\",\"update\",\"width\",\"min-width\",\"max-width\",\"min-height\",\"max-height\"].sort().reverse(),fi=[\"active\",\"any-link\",\"blank\",\"checked\",\"current\",\"default\",\"defined\",\"dir\",\"disabled\",\"drop\",\"empty\",\"enabled\",\"first\",\"first-child\",\"first-of-type\",\"fullscreen\",\"future\",\"focus\",\"focus-visible\",\"focus-within\",\"has\",\"host\",\"host-context\",\"hover\",\"indeterminate\",\"in-range\",\"invalid\",\"is\",\"lang\",\"last-child\",\"last-of-type\",\"left\",\"link\",\"local-link\",\"not\",\"nth-child\",\"nth-col\",\"nth-last-child\",\"nth-last-col\",\"nth-last-of-type\",\"nth-of-type\",\"only-child\",\"only-of-type\",\"optional\",\"out-of-range\",\"past\",\"placeholder-shown\",\"read-only\",\"read-write\",\"required\",\"right\",\"root\",\"scope\",\"target\",\"target-within\",\"user-invalid\",\"valid\",\"visited\",\"where\"].sort().reverse(),mi=[\"after\",\"backdrop\",\"before\",\"cue\",\"cue-region\",\"first-letter\",\"first-line\",\"grammar-error\",\"marker\",\"part\",\"placeholder\",\"selection\",\"slotted\",\"spelling-error\"].sort().reverse(),gi=[\"accent-color\",\"align-content\",\"align-items\",\"align-self\",\"alignment-baseline\",\"all\",\"anchor-name\",\"animation\",\"animation-composition\",\"animation-delay\",\"animation-direction\",\"animation-duration\",\"animation-fill-mode\",\"animation-iteration-count\",\"animation-name\",\"animation-play-state\",\"animation-range\",\"animation-range-end\",\"animation-range-start\",\"animation-timeline\",\"animation-timing-function\",\"appearance\",\"aspect-ratio\",\"backdrop-filter\",\"backface-visibility\",\"background\",\"background-attachment\",\"background-blend-mode\",\"background-clip\",\"background-color\",\"background-image\",\"background-origin\",\"background-position\",\"background-position-x\",\"background-position-y\",\"background-repeat\",\"background-size\",\"baseline-shift\",\"block-size\",\"border\",\"border-block\",\"border-block-color\",\"border-block-end\",\"border-block-end-color\",\"border-block-end-style\",\"border-block-end-width\",\"border-block-start\",\"border-block-start-color\",\"border-block-start-style\",\"border-block-start-width\",\"border-block-style\",\"border-block-width\",\"border-bottom\",\"border-bottom-color\",\"border-bottom-left-radius\",\"border-bottom-right-radius\",\"border-bottom-style\",\"border-bottom-width\",\"border-collapse\",\"border-color\",\"border-end-end-radius\",\"border-end-start-radius\",\"border-image\",\"border-image-outset\",\"border-image-repeat\",\"border-image-slice\",\"border-image-source\",\"border-image-width\",\"border-inline\",\"border-inline-color\",\"border-inline-end\",\"border-inline-end-color\",\"border-inline-end-style\",\"border-inline-end-width\",\"border-inline-start\",\"border-inline-start-color\",\"border-inline-start-style\",\"border-inline-start-width\",\"border-inline-style\",\"border-inline-width\",\"border-left\",\"border-left-color\",\"border-left-style\",\"border-left-width\",\"border-radius\",\"border-right\",\"border-right-color\",\"border-right-style\",\"border-right-width\",\"border-spacing\",\"border-start-end-radius\",\"border-start-start-radius\",\"border-style\",\"border-top\",\"border-top-color\",\"border-top-left-radius\",\"border-top-right-radius\",\"border-top-style\",\"border-top-width\",\"border-width\",\"bottom\",\"box-align\",\"box-decoration-break\",\"box-direction\",\"box-flex\",\"box-flex-group\",\"box-lines\",\"box-ordinal-group\",\"box-orient\",\"box-pack\",\"box-shadow\",\"box-sizing\",\"break-after\",\"break-before\",\"break-inside\",\"caption-side\",\"caret-color\",\"clear\",\"clip\",\"clip-path\",\"clip-rule\",\"color\",\"color-interpolation\",\"color-interpolation-filters\",\"color-profile\",\"color-rendering\",\"color-scheme\",\"column-count\",\"column-fill\",\"column-gap\",\"column-rule\",\"column-rule-color\",\"column-rule-style\",\"column-rule-width\",\"column-span\",\"column-width\",\"columns\",\"contain\",\"contain-intrinsic-block-size\",\"contain-intrinsic-height\",\"contain-intrinsic-inline-size\",\"contain-intrinsic-size\",\"contain-intrinsic-width\",\"container\",\"container-name\",\"container-type\",\"content\",\"content-visibility\",\"counter-increment\",\"counter-reset\",\"counter-set\",\"cue\",\"cue-after\",\"cue-before\",\"cursor\",\"cx\",\"cy\",\"direction\",\"display\",\"dominant-baseline\",\"empty-cells\",\"enable-background\",\"field-sizing\",\"fill\",\"fill-opacity\",\"fill-rule\",\"filter\",\"flex\",\"flex-basis\",\"flex-direction\",\"flex-flow\",\"flex-grow\",\"flex-shrink\",\"flex-wrap\",\"float\",\"flood-color\",\"flood-opacity\",\"flow\",\"font\",\"font-display\",\"font-family\",\"font-feature-settings\",\"font-kerning\",\"font-language-override\",\"font-optical-sizing\",\"font-palette\",\"font-size\",\"font-size-adjust\",\"font-smooth\",\"font-smoothing\",\"font-stretch\",\"font-style\",\"font-synthesis\",\"font-synthesis-position\",\"font-synthesis-small-caps\",\"font-synthesis-style\",\"font-synthesis-weight\",\"font-variant\",\"font-variant-alternates\",\"font-variant-caps\",\"font-variant-east-asian\",\"font-variant-emoji\",\"font-variant-ligatures\",\"font-variant-numeric\",\"font-variant-position\",\"font-variation-settings\",\"font-weight\",\"forced-color-adjust\",\"gap\",\"glyph-orientation-horizontal\",\"glyph-orientation-vertical\",\"grid\",\"grid-area\",\"grid-auto-columns\",\"grid-auto-flow\",\"grid-auto-rows\",\"grid-column\",\"grid-column-end\",\"grid-column-start\",\"grid-gap\",\"grid-row\",\"grid-row-end\",\"grid-row-start\",\"grid-template\",\"grid-template-areas\",\"grid-template-columns\",\"grid-template-rows\",\"hanging-punctuation\",\"height\",\"hyphenate-character\",\"hyphenate-limit-chars\",\"hyphens\",\"icon\",\"image-orientation\",\"image-rendering\",\"image-resolution\",\"ime-mode\",\"initial-letter\",\"initial-letter-align\",\"inline-size\",\"inset\",\"inset-area\",\"inset-block\",\"inset-block-end\",\"inset-block-start\",\"inset-inline\",\"inset-inline-end\",\"inset-inline-start\",\"isolation\",\"justify-content\",\"justify-items\",\"justify-self\",\"kerning\",\"left\",\"letter-spacing\",\"lighting-color\",\"line-break\",\"line-height\",\"line-height-step\",\"list-style\",\"list-style-image\",\"list-style-position\",\"list-style-type\",\"margin\",\"margin-block\",\"margin-block-end\",\"margin-block-start\",\"margin-bottom\",\"margin-inline\",\"margin-inline-end\",\"margin-inline-start\",\"margin-left\",\"margin-right\",\"margin-top\",\"margin-trim\",\"marker\",\"marker-end\",\"marker-mid\",\"marker-start\",\"marks\",\"mask\",\"mask-border\",\"mask-border-mode\",\"mask-border-outset\",\"mask-border-repeat\",\"mask-border-slice\",\"mask-border-source\",\"mask-border-width\",\"mask-clip\",\"mask-composite\",\"mask-image\",\"mask-mode\",\"mask-origin\",\"mask-position\",\"mask-repeat\",\"mask-size\",\"mask-type\",\"masonry-auto-flow\",\"math-depth\",\"math-shift\",\"math-style\",\"max-block-size\",\"max-height\",\"max-inline-size\",\"max-width\",\"min-block-size\",\"min-height\",\"min-inline-size\",\"min-width\",\"mix-blend-mode\",\"nav-down\",\"nav-index\",\"nav-left\",\"nav-right\",\"nav-up\",\"none\",\"normal\",\"object-fit\",\"object-position\",\"offset\",\"offset-anchor\",\"offset-distance\",\"offset-path\",\"offset-position\",\"offset-rotate\",\"opacity\",\"order\",\"orphans\",\"outline\",\"outline-color\",\"outline-offset\",\"outline-style\",\"outline-width\",\"overflow\",\"overflow-anchor\",\"overflow-block\",\"overflow-clip-margin\",\"overflow-inline\",\"overflow-wrap\",\"overflow-x\",\"overflow-y\",\"overlay\",\"overscroll-behavior\",\"overscroll-behavior-block\",\"overscroll-behavior-inline\",\"overscroll-behavior-x\",\"overscroll-behavior-y\",\"padding\",\"padding-block\",\"padding-block-end\",\"padding-block-start\",\"padding-bottom\",\"padding-inline\",\"padding-inline-end\",\"padding-inline-start\",\"padding-left\",\"padding-right\",\"padding-top\",\"page\",\"page-break-after\",\"page-break-before\",\"page-break-inside\",\"paint-order\",\"pause\",\"pause-after\",\"pause-before\",\"perspective\",\"perspective-origin\",\"place-content\",\"place-items\",\"place-self\",\"pointer-events\",\"position\",\"position-anchor\",\"position-visibility\",\"print-color-adjust\",\"quotes\",\"r\",\"resize\",\"rest\",\"rest-after\",\"rest-before\",\"right\",\"rotate\",\"row-gap\",\"ruby-align\",\"ruby-position\",\"scale\",\"scroll-behavior\",\"scroll-margin\",\"scroll-margin-block\",\"scroll-margin-block-end\",\"scroll-margin-block-start\",\"scroll-margin-bottom\",\"scroll-margin-inline\",\"scroll-margin-inline-end\",\"scroll-margin-inline-start\",\"scroll-margin-left\",\"scroll-margin-right\",\"scroll-margin-top\",\"scroll-padding\",\"scroll-padding-block\",\"scroll-padding-block-end\",\"scroll-padding-block-start\",\"scroll-padding-bottom\",\"scroll-padding-inline\",\"scroll-padding-inline-end\",\"scroll-padding-inline-start\",\"scroll-padding-left\",\"scroll-padding-right\",\"scroll-padding-top\",\"scroll-snap-align\",\"scroll-snap-stop\",\"scroll-snap-type\",\"scroll-timeline\",\"scroll-timeline-axis\",\"scroll-timeline-name\",\"scrollbar-color\",\"scrollbar-gutter\",\"scrollbar-width\",\"shape-image-threshold\",\"shape-margin\",\"shape-outside\",\"shape-rendering\",\"speak\",\"speak-as\",\"src\",\"stop-color\",\"stop-opacity\",\"stroke\",\"stroke-dasharray\",\"stroke-dashoffset\",\"stroke-linecap\",\"stroke-linejoin\",\"stroke-miterlimit\",\"stroke-opacity\",\"stroke-width\",\"tab-size\",\"table-layout\",\"text-align\",\"text-align-all\",\"text-align-last\",\"text-anchor\",\"text-combine-upright\",\"text-decoration\",\"text-decoration-color\",\"text-decoration-line\",\"text-decoration-skip\",\"text-decoration-skip-ink\",\"text-decoration-style\",\"text-decoration-thickness\",\"text-emphasis\",\"text-emphasis-color\",\"text-emphasis-position\",\"text-emphasis-style\",\"text-indent\",\"text-justify\",\"text-orientation\",\"text-overflow\",\"text-rendering\",\"text-shadow\",\"text-size-adjust\",\"text-transform\",\"text-underline-offset\",\"text-underline-position\",\"text-wrap\",\"text-wrap-mode\",\"text-wrap-style\",\"timeline-scope\",\"top\",\"touch-action\",\"transform\",\"transform-box\",\"transform-origin\",\"transform-style\",\"transition\",\"transition-behavior\",\"transition-delay\",\"transition-duration\",\"transition-property\",\"transition-timing-function\",\"translate\",\"unicode-bidi\",\"user-modify\",\"user-select\",\"vector-effect\",\"vertical-align\",\"view-timeline\",\"view-timeline-axis\",\"view-timeline-inset\",\"view-timeline-name\",\"view-transition-name\",\"visibility\",\"voice-balance\",\"voice-duration\",\"voice-family\",\"voice-pitch\",\"voice-range\",\"voice-rate\",\"voice-stress\",\"voice-volume\",\"white-space\",\"white-space-collapse\",\"widows\",\"width\",\"will-change\",\"word-break\",\"word-spacing\",\"word-wrap\",\"writing-mode\",\"x\",\"y\",\"z-index\",\"zoom\"].sort().reverse(),bi=fi.concat(mi).sort().reverse();const Ei=[\"a\",\"abbr\",\"address\",\"article\",\"aside\",\"audio\",\"b\",\"blockquote\",\"body\",\"button\",\"canvas\",\"caption\",\"cite\",\"code\",\"dd\",\"del\",\"details\",\"dfn\",\"div\",\"dl\",\"dt\",\"em\",\"fieldset\",\"figcaption\",\"figure\",\"footer\",\"form\",\"h1\",\"h2\",\"h3\",\"h4\",\"h5\",\"h6\",\"header\",\"hgroup\",\"html\",\"i\",\"iframe\",\"img\",\"input\",\"ins\",\"kbd\",\"label\",\"legend\",\"li\",\"main\",\"mark\",\"menu\",\"nav\",\"object\",\"ol\",\"optgroup\",\"option\",\"p\",\"picture\",\"q\",\"quote\",\"samp\",\"section\",\"select\",\"source\",\"span\",\"strong\",\"summary\",\"sup\",\"table\",\"tbody\",\"td\",\"textarea\",\"tfoot\",\"th\",\"thead\",\"time\",\"tr\",\"ul\",\"var\",\"video\",\"defs\",\"g\",\"marker\",\"mask\",\"pattern\",\"svg\",\"switch\",\"symbol\",\"feBlend\",\"feColorMatrix\",\"feComponentTransfer\",\"feComposite\",\"feConvolveMatrix\",\"feDiffuseLighting\",\"feDisplacementMap\",\"feFlood\",\"feGaussianBlur\",\"feImage\",\"feMerge\",\"feMorphology\",\"feOffset\",\"feSpecularLighting\",\"feTile\",\"feTurbulence\",\"linearGradient\",\"radialGradient\",\"stop\",\"circle\",\"ellipse\",\"image\",\"line\",\"path\",\"polygon\",\"polyline\",\"rect\",\"text\",\"use\",\"textPath\",\"tspan\",\"foreignObject\",\"clipPath\"],yi=[\"any-hover\",\"any-pointer\",\"aspect-ratio\",\"color\",\"color-gamut\",\"color-index\",\"device-aspect-ratio\",\"device-height\",\"device-width\",\"display-mode\",\"forced-colors\",\"grid\",\"height\",\"hover\",\"inverted-colors\",\"monochrome\",\"orientation\",\"overflow-block\",\"overflow-inline\",\"pointer\",\"prefers-color-scheme\",\"prefers-contrast\",\"prefers-reduced-motion\",\"prefers-reduced-transparency\",\"resolution\",\"scan\",\"scripting\",\"update\",\"width\",\"min-width\",\"max-width\",\"min-height\",\"max-height\"].sort().reverse(),_i=[\"active\",\"any-link\",\"blank\",\"checked\",\"current\",\"default\",\"defined\",\"dir\",\"disabled\",\"drop\",\"empty\",\"enabled\",\"first\",\"first-child\",\"first-of-type\",\"fullscreen\",\"future\",\"focus\",\"focus-visible\",\"focus-within\",\"has\",\"host\",\"host-context\",\"hover\",\"indeterminate\",\"in-range\",\"invalid\",\"is\",\"lang\",\"last-child\",\"last-of-type\",\"left\",\"link\",\"local-link\",\"not\",\"nth-child\",\"nth-col\",\"nth-last-child\",\"nth-last-col\",\"nth-last-of-type\",\"nth-of-type\",\"only-child\",\"only-of-type\",\"optional\",\"out-of-range\",\"past\",\"placeholder-shown\",\"read-only\",\"read-write\",\"required\",\"right\",\"root\",\"scope\",\"target\",\"target-within\",\"user-invalid\",\"valid\",\"visited\",\"where\"].sort().reverse(),ki=[\"after\",\"backdrop\",\"before\",\"cue\",\"cue-region\",\"first-letter\",\"first-line\",\"grammar-error\",\"marker\",\"part\",\"placeholder\",\"selection\",\"slotted\",\"spelling-error\"].sort().reverse(),Ti=[\"accent-color\",\"align-content\",\"align-items\",\"align-self\",\"alignment-baseline\",\"all\",\"anchor-name\",\"animation\",\"animation-composition\",\"animation-delay\",\"animation-direction\",\"animation-duration\",\"animation-fill-mode\",\"animation-iteration-count\",\"animation-name\",\"animation-play-state\",\"animation-range\",\"animation-range-end\",\"animation-range-start\",\"animation-timeline\",\"animation-timing-function\",\"appearance\",\"aspect-ratio\",\"backdrop-filter\",\"backface-visibility\",\"background\",\"background-attachment\",\"background-blend-mode\",\"background-clip\",\"background-color\",\"background-image\",\"background-origin\",\"background-position\",\"background-position-x\",\"background-position-y\",\"background-repeat\",\"background-size\",\"baseline-shift\",\"block-size\",\"border\",\"border-block\",\"border-block-color\",\"border-block-end\",\"border-block-end-color\",\"border-block-end-style\",\"border-block-end-width\",\"border-block-start\",\"border-block-start-color\",\"border-block-start-style\",\"border-block-start-width\",\"border-block-style\",\"border-block-width\",\"border-bottom\",\"border-bottom-color\",\"border-bottom-left-radius\",\"border-bottom-right-radius\",\"border-bottom-style\",\"border-bottom-width\",\"border-collapse\",\"border-color\",\"border-end-end-radius\",\"border-end-start-radius\",\"border-image\",\"border-image-outset\",\"border-image-repeat\",\"border-image-slice\",\"border-image-source\",\"border-image-width\",\"border-inline\",\"border-inline-color\",\"border-inline-end\",\"border-inline-end-color\",\"border-inline-end-style\",\"border-inline-end-width\",\"border-inline-start\",\"border-inline-start-color\",\"border-inline-start-style\",\"border-inline-start-width\",\"border-inline-style\",\"border-inline-width\",\"border-left\",\"border-left-color\",\"border-left-style\",\"border-left-width\",\"border-radius\",\"border-right\",\"border-right-color\",\"border-right-style\",\"border-right-width\",\"border-spacing\",\"border-start-end-radius\",\"border-start-start-radius\",\"border-style\",\"border-top\",\"border-top-color\",\"border-top-left-radius\",\"border-top-right-radius\",\"border-top-style\",\"border-top-width\",\"border-width\",\"bottom\",\"box-align\",\"box-decoration-break\",\"box-direction\",\"box-flex\",\"box-flex-group\",\"box-lines\",\"box-ordinal-group\",\"box-orient\",\"box-pack\",\"box-shadow\",\"box-sizing\",\"break-after\",\"break-before\",\"break-inside\",\"caption-side\",\"caret-color\",\"clear\",\"clip\",\"clip-path\",\"clip-rule\",\"color\",\"color-interpolation\",\"color-interpolation-filters\",\"color-profile\",\"color-rendering\",\"color-scheme\",\"column-count\",\"column-fill\",\"column-gap\",\"column-rule\",\"column-rule-color\",\"column-rule-style\",\"column-rule-width\",\"column-span\",\"column-width\",\"columns\",\"contain\",\"contain-intrinsic-block-size\",\"contain-intrinsic-height\",\"contain-intrinsic-inline-size\",\"contain-intrinsic-size\",\"contain-intrinsic-width\",\"container\",\"container-name\",\"container-type\",\"content\",\"content-visibility\",\"counter-increment\",\"counter-reset\",\"counter-set\",\"cue\",\"cue-after\",\"cue-before\",\"cursor\",\"cx\",\"cy\",\"direction\",\"display\",\"dominant-baseline\",\"empty-cells\",\"enable-background\",\"field-sizing\",\"fill\",\"fill-opacity\",\"fill-rule\",\"filter\",\"flex\",\"flex-basis\",\"flex-direction\",\"flex-flow\",\"flex-grow\",\"flex-shrink\",\"flex-wrap\",\"float\",\"flood-color\",\"flood-opacity\",\"flow\",\"font\",\"font-display\",\"font-family\",\"font-feature-settings\",\"font-kerning\",\"font-language-override\",\"font-optical-sizing\",\"font-palette\",\"font-size\",\"font-size-adjust\",\"font-smooth\",\"font-smoothing\",\"font-stretch\",\"font-style\",\"font-synthesis\",\"font-synthesis-position\",\"font-synthesis-small-caps\",\"font-synthesis-style\",\"font-synthesis-weight\",\"font-variant\",\"font-variant-alternates\",\"font-variant-caps\",\"font-variant-east-asian\",\"font-variant-emoji\",\"font-variant-ligatures\",\"font-variant-numeric\",\"font-variant-position\",\"font-variation-settings\",\"font-weight\",\"forced-color-adjust\",\"gap\",\"glyph-orientation-horizontal\",\"glyph-orientation-vertical\",\"grid\",\"grid-area\",\"grid-auto-columns\",\"grid-auto-flow\",\"grid-auto-rows\",\"grid-column\",\"grid-column-end\",\"grid-column-start\",\"grid-gap\",\"grid-row\",\"grid-row-end\",\"grid-row-start\",\"grid-template\",\"grid-template-areas\",\"grid-template-columns\",\"grid-template-rows\",\"hanging-punctuation\",\"height\",\"hyphenate-character\",\"hyphenate-limit-chars\",\"hyphens\",\"icon\",\"image-orientation\",\"image-rendering\",\"image-resolution\",\"ime-mode\",\"initial-letter\",\"initial-letter-align\",\"inline-size\",\"inset\",\"inset-area\",\"inset-block\",\"inset-block-end\",\"inset-block-start\",\"inset-inline\",\"inset-inline-end\",\"inset-inline-start\",\"isolation\",\"justify-content\",\"justify-items\",\"justify-self\",\"kerning\",\"left\",\"letter-spacing\",\"lighting-color\",\"line-break\",\"line-height\",\"line-height-step\",\"list-style\",\"list-style-image\",\"list-style-position\",\"list-style-type\",\"margin\",\"margin-block\",\"margin-block-end\",\"margin-block-start\",\"margin-bottom\",\"margin-inline\",\"margin-inline-end\",\"margin-inline-start\",\"margin-left\",\"margin-right\",\"margin-top\",\"margin-trim\",\"marker\",\"marker-end\",\"marker-mid\",\"marker-start\",\"marks\",\"mask\",\"mask-border\",\"mask-border-mode\",\"mask-border-outset\",\"mask-border-repeat\",\"mask-border-slice\",\"mask-border-source\",\"mask-border-width\",\"mask-clip\",\"mask-composite\",\"mask-image\",\"mask-mode\",\"mask-origin\",\"mask-position\",\"mask-repeat\",\"mask-size\",\"mask-type\",\"masonry-auto-flow\",\"math-depth\",\"math-shift\",\"math-style\",\"max-block-size\",\"max-height\",\"max-inline-size\",\"max-width\",\"min-block-size\",\"min-height\",\"min-inline-size\",\"min-width\",\"mix-blend-mode\",\"nav-down\",\"nav-index\",\"nav-left\",\"nav-right\",\"nav-up\",\"none\",\"normal\",\"object-fit\",\"object-position\",\"offset\",\"offset-anchor\",\"offset-distance\",\"offset-path\",\"offset-position\",\"offset-rotate\",\"opacity\",\"order\",\"orphans\",\"outline\",\"outline-color\",\"outline-offset\",\"outline-style\",\"outline-width\",\"overflow\",\"overflow-anchor\",\"overflow-block\",\"overflow-clip-margin\",\"overflow-inline\",\"overflow-wrap\",\"overflow-x\",\"overflow-y\",\"overlay\",\"overscroll-behavior\",\"overscroll-behavior-block\",\"overscroll-behavior-inline\",\"overscroll-behavior-x\",\"overscroll-behavior-y\",\"padding\",\"padding-block\",\"padding-block-end\",\"padding-block-start\",\"padding-bottom\",\"padding-inline\",\"padding-inline-end\",\"padding-inline-start\",\"padding-left\",\"padding-right\",\"padding-top\",\"page\",\"page-break-after\",\"page-break-before\",\"page-break-inside\",\"paint-order\",\"pause\",\"pause-after\",\"pause-before\",\"perspective\",\"perspective-origin\",\"place-content\",\"place-items\",\"place-self\",\"pointer-events\",\"position\",\"position-anchor\",\"position-visibility\",\"print-color-adjust\",\"quotes\",\"r\",\"resize\",\"rest\",\"rest-after\",\"rest-before\",\"right\",\"rotate\",\"row-gap\",\"ruby-align\",\"ruby-position\",\"scale\",\"scroll-behavior\",\"scroll-margin\",\"scroll-margin-block\",\"scroll-margin-block-end\",\"scroll-margin-block-start\",\"scroll-margin-bottom\",\"scroll-margin-inline\",\"scroll-margin-inline-end\",\"scroll-margin-inline-start\",\"scroll-margin-left\",\"scroll-margin-right\",\"scroll-margin-top\",\"scroll-padding\",\"scroll-padding-block\",\"scroll-padding-block-end\",\"scroll-padding-block-start\",\"scroll-padding-bottom\",\"scroll-padding-inline\",\"scroll-padding-inline-end\",\"scroll-padding-inline-start\",\"scroll-padding-left\",\"scroll-padding-right\",\"scroll-padding-top\",\"scroll-snap-align\",\"scroll-snap-stop\",\"scroll-snap-type\",\"scroll-timeline\",\"scroll-timeline-axis\",\"scroll-timeline-name\",\"scrollbar-color\",\"scrollbar-gutter\",\"scrollbar-width\",\"shape-image-threshold\",\"shape-margin\",\"shape-outside\",\"shape-rendering\",\"speak\",\"speak-as\",\"src\",\"stop-color\",\"stop-opacity\",\"stroke\",\"stroke-dasharray\",\"stroke-dashoffset\",\"stroke-linecap\",\"stroke-linejoin\",\"stroke-miterlimit\",\"stroke-opacity\",\"stroke-width\",\"tab-size\",\"table-layout\",\"text-align\",\"text-align-all\",\"text-align-last\",\"text-anchor\",\"text-combine-upright\",\"text-decoration\",\"text-decoration-color\",\"text-decoration-line\",\"text-decoration-skip\",\"text-decoration-skip-ink\",\"text-decoration-style\",\"text-decoration-thickness\",\"text-emphasis\",\"text-emphasis-color\",\"text-emphasis-position\",\"text-emphasis-style\",\"text-indent\",\"text-justify\",\"text-orientation\",\"text-overflow\",\"text-rendering\",\"text-shadow\",\"text-size-adjust\",\"text-transform\",\"text-underline-offset\",\"text-underline-position\",\"text-wrap\",\"text-wrap-mode\",\"text-wrap-style\",\"timeline-scope\",\"top\",\"touch-action\",\"transform\",\"transform-box\",\"transform-origin\",\"transform-style\",\"transition\",\"transition-behavior\",\"transition-delay\",\"transition-duration\",\"transition-property\",\"transition-timing-function\",\"translate\",\"unicode-bidi\",\"user-modify\",\"user-select\",\"vector-effect\",\"vertical-align\",\"view-timeline\",\"view-timeline-axis\",\"view-timeline-inset\",\"view-timeline-name\",\"view-transition-name\",\"visibility\",\"voice-balance\",\"voice-duration\",\"voice-family\",\"voice-pitch\",\"voice-range\",\"voice-rate\",\"voice-stress\",\"voice-volume\",\"white-space\",\"white-space-collapse\",\"widows\",\"width\",\"will-change\",\"word-break\",\"word-spacing\",\"word-wrap\",\"writing-mode\",\"x\",\"y\",\"z-index\",\"zoom\"].sort().reverse();function Si(e){return e?\"string\"==typeof e?e:e.source:null}function vi(e){return Ai(\"(?=\",e,\")\")}function Ai(...e){return e.map(e=>Si(e)).join(\"\")}function Ni(...e){const t=function(e){const t=e[e.length-1];return\"object\"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{}}(e);return\"(\"+(t.capture?\"\":\"?:\")+e.map(e=>Si(e)).join(\"|\")+\")\"}const Ci=e=>Ai(/\\b/,e,/\\w$/.test(e)?/\\b/:/\\B/),xi=[\"Protocol\",\"Type\"].map(Ci),wi=[\"init\",\"self\"].map(Ci),Ii=[\"Any\",\"Self\"],Oi=[\"actor\",\"any\",\"associatedtype\",\"async\",\"await\",/as\\?/,/as!/,\"as\",\"borrowing\",\"break\",\"case\",\"catch\",\"class\",\"consume\",\"consuming\",\"continue\",\"convenience\",\"copy\",\"default\",\"defer\",\"deinit\",\"didSet\",\"distributed\",\"do\",\"dynamic\",\"each\",\"else\",\"enum\",\"extension\",\"fallthrough\",/fileprivate\\(set\\)/,\"fileprivate\",\"final\",\"for\",\"func\",\"get\",\"guard\",\"if\",\"import\",\"indirect\",\"infix\",/init\\?/,/init!/,\"inout\",/internal\\(set\\)/,\"internal\",\"in\",\"is\",\"isolated\",\"nonisolated\",\"lazy\",\"let\",\"macro\",\"mutating\",\"nonmutating\",/open\\(set\\)/,\"open\",\"operator\",\"optional\",\"override\",\"package\",\"postfix\",\"precedencegroup\",\"prefix\",/private\\(set\\)/,\"private\",\"protocol\",/public\\(set\\)/,\"public\",\"repeat\",\"required\",\"rethrows\",\"return\",\"set\",\"some\",\"static\",\"struct\",\"subscript\",\"super\",\"switch\",\"throws\",\"throw\",/try\\?/,/try!/,\"try\",\"typealias\",/unowned\\(safe\\)/,/unowned\\(unsafe\\)/,\"unowned\",\"var\",\"weak\",\"where\",\"while\",\"willSet\"],Ri=[\"false\",\"nil\",\"true\"],Di=[\"assignment\",\"associativity\",\"higherThan\",\"left\",\"lowerThan\",\"none\",\"right\"],Mi=[\"#colorLiteral\",\"#column\",\"#dsohandle\",\"#else\",\"#elseif\",\"#endif\",\"#error\",\"#file\",\"#fileID\",\"#fileLiteral\",\"#filePath\",\"#function\",\"#if\",\"#imageLiteral\",\"#keyPath\",\"#line\",\"#selector\",\"#sourceLocation\",\"#warning\"],Pi=[\"abs\",\"all\",\"any\",\"assert\",\"assertionFailure\",\"debugPrint\",\"dump\",\"fatalError\",\"getVaList\",\"isKnownUniquelyReferenced\",\"max\",\"min\",\"numericCast\",\"pointwiseMax\",\"pointwiseMin\",\"precondition\",\"preconditionFailure\",\"print\",\"readLine\",\"repeatElement\",\"sequence\",\"stride\",\"swap\",\"swift_unboxFromSwiftValueWithType\",\"transcode\",\"type\",\"unsafeBitCast\",\"unsafeDowncast\",\"withExtendedLifetime\",\"withUnsafeMutablePointer\",\"withUnsafePointer\",\"withVaList\",\"withoutActuallyEscaping\",\"zip\"],Li=Ni(/[/=\\-+!*%<>&|^~?]/,/[\\u00A1-\\u00A7]/,/[\\u00A9\\u00AB]/,/[\\u00AC\\u00AE]/,/[\\u00B0\\u00B1]/,/[\\u00B6\\u00BB\\u00BF\\u00D7\\u00F7]/,/[\\u2016-\\u2017]/,/[\\u2020-\\u2027]/,/[\\u2030-\\u203E]/,/[\\u2041-\\u2053]/,/[\\u2055-\\u205E]/,/[\\u2190-\\u23FF]/,/[\\u2500-\\u2775]/,/[\\u2794-\\u2BFF]/,/[\\u2E00-\\u2E7F]/,/[\\u3001-\\u3003]/,/[\\u3008-\\u3020]/,/[\\u3030]/),Fi=Ni(Li,/[\\u0300-\\u036F]/,/[\\u1DC0-\\u1DFF]/,/[\\u20D0-\\u20FF]/,/[\\uFE00-\\uFE0F]/,/[\\uFE20-\\uFE2F]/),Bi=Ai(Li,Fi,\"*\"),Ui=Ni(/[a-zA-Z_]/,/[\\u00A8\\u00AA\\u00AD\\u00AF\\u00B2-\\u00B5\\u00B7-\\u00BA]/,/[\\u00BC-\\u00BE\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u00FF]/,/[\\u0100-\\u02FF\\u0370-\\u167F\\u1681-\\u180D\\u180F-\\u1DBF]/,/[\\u1E00-\\u1FFF]/,/[\\u200B-\\u200D\\u202A-\\u202E\\u203F-\\u2040\\u2054\\u2060-\\u206F]/,/[\\u2070-\\u20CF\\u2100-\\u218F\\u2460-\\u24FF\\u2776-\\u2793]/,/[\\u2C00-\\u2DFF\\u2E80-\\u2FFF]/,/[\\u3004-\\u3007\\u3021-\\u302F\\u3031-\\u303F\\u3040-\\uD7FF]/,/[\\uF900-\\uFD3D\\uFD40-\\uFDCF\\uFDF0-\\uFE1F\\uFE30-\\uFE44]/,/[\\uFE47-\\uFEFE\\uFF00-\\uFFFD]/),Hi=Ni(Ui,/\\d/,/[\\u0300-\\u036F\\u1DC0-\\u1DFF\\u20D0-\\u20FF\\uFE20-\\uFE2F]/),zi=Ai(Ui,Hi,\"*\"),Gi=Ai(/[A-Z]/,Hi,\"*\"),ji=[\"attached\",\"autoclosure\",Ai(/convention\\(/,Ni(\"swift\",\"block\",\"c\"),/\\)/),\"discardableResult\",\"dynamicCallable\",\"dynamicMemberLookup\",\"escaping\",\"freestanding\",\"frozen\",\"GKInspectable\",\"IBAction\",\"IBDesignable\",\"IBInspectable\",\"IBOutlet\",\"IBSegueAction\",\"inlinable\",\"main\",\"nonobjc\",\"NSApplicationMain\",\"NSCopying\",\"NSManaged\",Ai(/objc\\(/,zi,/\\)/),\"objc\",\"objcMembers\",\"propertyWrapper\",\"requires_stored_property_inits\",\"resultBuilder\",\"Sendable\",\"testable\",\"UIApplicationMain\",\"unchecked\",\"unknown\",\"usableFromInline\",\"warn_unqualified_access\"],$i=[\"iOS\",\"iOSApplicationExtension\",\"macOS\",\"macOSApplicationExtension\",\"macCatalyst\",\"macCatalystApplicationExtension\",\"watchOS\",\"watchOSApplicationExtension\",\"tvOS\",\"tvOSApplicationExtension\",\"swift\"];const qi=\"[A-Za-z$_][0-9A-Za-z$_]*\",Yi=[\"as\",\"in\",\"of\",\"if\",\"for\",\"while\",\"finally\",\"var\",\"new\",\"function\",\"do\",\"return\",\"void\",\"else\",\"break\",\"catch\",\"instanceof\",\"with\",\"throw\",\"case\",\"default\",\"try\",\"switch\",\"continue\",\"typeof\",\"delete\",\"let\",\"yield\",\"const\",\"class\",\"debugger\",\"async\",\"await\",\"static\",\"import\",\"from\",\"export\",\"extends\",\"using\"],Wi=[\"true\",\"false\",\"null\",\"undefined\",\"NaN\",\"Infinity\"],Vi=[\"Object\",\"Function\",\"Boolean\",\"Symbol\",\"Math\",\"Date\",\"Number\",\"BigInt\",\"String\",\"RegExp\",\"Array\",\"Float32Array\",\"Float64Array\",\"Int8Array\",\"Uint8Array\",\"Uint8ClampedArray\",\"Int16Array\",\"Int32Array\",\"Uint16Array\",\"Uint32Array\",\"BigInt64Array\",\"BigUint64Array\",\"Set\",\"Map\",\"WeakSet\",\"WeakMap\",\"ArrayBuffer\",\"SharedArrayBuffer\",\"Atomics\",\"DataView\",\"JSON\",\"Promise\",\"Generator\",\"GeneratorFunction\",\"AsyncFunction\",\"Reflect\",\"Proxy\",\"Intl\",\"WebAssembly\"],Ki=[\"Error\",\"EvalError\",\"InternalError\",\"RangeError\",\"ReferenceError\",\"SyntaxError\",\"TypeError\",\"URIError\"],Qi=[\"setInterval\",\"setTimeout\",\"clearInterval\",\"clearTimeout\",\"require\",\"exports\",\"eval\",\"isFinite\",\"isNaN\",\"parseFloat\",\"parseInt\",\"decodeURI\",\"decodeURIComponent\",\"encodeURI\",\"encodeURIComponent\",\"escape\",\"unescape\"],Xi=[\"arguments\",\"this\",\"super\",\"console\",\"window\",\"document\",\"localStorage\",\"sessionStorage\",\"module\",\"global\"],Zi=[].concat(Qi,Vi,Ki);const Ji={arduino:function(e){const t={type:[\"boolean\",\"byte\",\"word\",\"String\"],built_in:[\"KeyboardController\",\"MouseController\",\"SoftwareSerial\",\"EthernetServer\",\"EthernetClient\",\"LiquidCrystal\",\"RobotControl\",\"GSMVoiceCall\",\"EthernetUDP\",\"EsploraTFT\",\"HttpClient\",\"RobotMotor\",\"WiFiClient\",\"GSMScanner\",\"FileSystem\",\"Scheduler\",\"GSMServer\",\"YunClient\",\"YunServer\",\"IPAddress\",\"GSMClient\",\"GSMModem\",\"Keyboard\",\"Ethernet\",\"Console\",\"GSMBand\",\"Esplora\",\"Stepper\",\"Process\",\"WiFiUDP\",\"GSM_SMS\",\"Mailbox\",\"USBHost\",\"Firmata\",\"PImage\",\"Client\",\"Server\",\"GSMPIN\",\"FileIO\",\"Bridge\",\"Serial\",\"EEPROM\",\"Stream\",\"Mouse\",\"Audio\",\"Servo\",\"File\",\"Task\",\"GPRS\",\"WiFi\",\"Wire\",\"TFT\",\"GSM\",\"SPI\",\"SD\"],_hints:[\"setup\",\"loop\",\"runShellCommandAsynchronously\",\"analogWriteResolution\",\"retrieveCallingNumber\",\"printFirmwareVersion\",\"analogReadResolution\",\"sendDigitalPortPair\",\"noListenOnLocalhost\",\"readJoystickButton\",\"setFirmwareVersion\",\"readJoystickSwitch\",\"scrollDisplayRight\",\"getVoiceCallStatus\",\"scrollDisplayLeft\",\"writeMicroseconds\",\"delayMicroseconds\",\"beginTransmission\",\"getSignalStrength\",\"runAsynchronously\",\"getAsynchronously\",\"listenOnLocalhost\",\"getCurrentCarrier\",\"readAccelerometer\",\"messageAvailable\",\"sendDigitalPorts\",\"lineFollowConfig\",\"countryNameWrite\",\"runShellCommand\",\"readStringUntil\",\"rewindDirectory\",\"readTemperature\",\"setClockDivider\",\"readLightSensor\",\"endTransmission\",\"analogReference\",\"detachInterrupt\",\"countryNameRead\",\"attachInterrupt\",\"encryptionType\",\"readBytesUntil\",\"robotNameWrite\",\"readMicrophone\",\"robotNameRead\",\"cityNameWrite\",\"userNameWrite\",\"readJoystickY\",\"readJoystickX\",\"mouseReleased\",\"openNextFile\",\"scanNetworks\",\"noInterrupts\",\"digitalWrite\",\"beginSpeaker\",\"mousePressed\",\"isActionDone\",\"mouseDragged\",\"displayLogos\",\"noAutoscroll\",\"addParameter\",\"remoteNumber\",\"getModifiers\",\"keyboardRead\",\"userNameRead\",\"waitContinue\",\"processInput\",\"parseCommand\",\"printVersion\",\"readNetworks\",\"writeMessage\",\"blinkVersion\",\"cityNameRead\",\"readMessage\",\"setDataMode\",\"parsePacket\",\"isListening\",\"setBitOrder\",\"beginPacket\",\"isDirectory\",\"motorsWrite\",\"drawCompass\",\"digitalRead\",\"clearScreen\",\"serialEvent\",\"rightToLeft\",\"setTextSize\",\"leftToRight\",\"requestFrom\",\"keyReleased\",\"compassRead\",\"analogWrite\",\"interrupts\",\"WiFiServer\",\"disconnect\",\"playMelody\",\"parseFloat\",\"autoscroll\",\"getPINUsed\",\"setPINUsed\",\"setTimeout\",\"sendAnalog\",\"readSlider\",\"analogRead\",\"beginWrite\",\"createChar\",\"motorsStop\",\"keyPressed\",\"tempoWrite\",\"readButton\",\"subnetMask\",\"debugPrint\",\"macAddress\",\"writeGreen\",\"randomSeed\",\"attachGPRS\",\"readString\",\"sendString\",\"remotePort\",\"releaseAll\",\"mouseMoved\",\"background\",\"getXChange\",\"getYChange\",\"answerCall\",\"getResult\",\"voiceCall\",\"endPacket\",\"constrain\",\"getSocket\",\"writeJSON\",\"getButton\",\"available\",\"connected\",\"findUntil\",\"readBytes\",\"exitValue\",\"readGreen\",\"writeBlue\",\"startLoop\",\"IPAddress\",\"isPressed\",\"sendSysex\",\"pauseMode\",\"gatewayIP\",\"setCursor\",\"getOemKey\",\"tuneWrite\",\"noDisplay\",\"loadImage\",\"switchPIN\",\"onRequest\",\"onReceive\",\"changePIN\",\"playFile\",\"noBuffer\",\"parseInt\",\"overflow\",\"checkPIN\",\"knobRead\",\"beginTFT\",\"bitClear\",\"updateIR\",\"bitWrite\",\"position\",\"writeRGB\",\"highByte\",\"writeRed\",\"setSpeed\",\"readBlue\",\"noStroke\",\"remoteIP\",\"transfer\",\"shutdown\",\"hangCall\",\"beginSMS\",\"endWrite\",\"attached\",\"maintain\",\"noCursor\",\"checkReg\",\"checkPUK\",\"shiftOut\",\"isValid\",\"shiftIn\",\"pulseIn\",\"connect\",\"println\",\"localIP\",\"pinMode\",\"getIMEI\",\"display\",\"noBlink\",\"process\",\"getBand\",\"running\",\"beginSD\",\"drawBMP\",\"lowByte\",\"setBand\",\"release\",\"bitRead\",\"prepare\",\"pointTo\",\"readRed\",\"setMode\",\"noFill\",\"remove\",\"listen\",\"stroke\",\"detach\",\"attach\",\"noTone\",\"exists\",\"buffer\",\"height\",\"bitSet\",\"circle\",\"config\",\"cursor\",\"random\",\"IRread\",\"setDNS\",\"endSMS\",\"getKey\",\"micros\",\"millis\",\"begin\",\"print\",\"write\",\"ready\",\"flush\",\"width\",\"isPIN\",\"blink\",\"clear\",\"press\",\"mkdir\",\"rmdir\",\"close\",\"point\",\"yield\",\"image\",\"BSSID\",\"click\",\"delay\",\"read\",\"text\",\"move\",\"peek\",\"beep\",\"rect\",\"line\",\"open\",\"seek\",\"fill\",\"size\",\"turn\",\"stop\",\"home\",\"find\",\"step\",\"tone\",\"sqrt\",\"RSSI\",\"SSID\",\"end\",\"bit\",\"tan\",\"cos\",\"sin\",\"pow\",\"map\",\"abs\",\"max\",\"min\",\"get\",\"run\",\"put\"],literal:[\"DIGITAL_MESSAGE\",\"FIRMATA_STRING\",\"ANALOG_MESSAGE\",\"REPORT_DIGITAL\",\"REPORT_ANALOG\",\"INPUT_PULLUP\",\"SET_PIN_MODE\",\"INTERNAL2V56\",\"SYSTEM_RESET\",\"LED_BUILTIN\",\"INTERNAL1V1\",\"SYSEX_START\",\"INTERNAL\",\"EXTERNAL\",\"DEFAULT\",\"OUTPUT\",\"INPUT\",\"HIGH\",\"LOW\"]},n=function(e){const t=e.regex,n=e.COMMENT(\"//\",\"$\",{contains:[{begin:/\\\\\\n/}]}),r=\"decltype\\\\(auto\\\\)\",a=\"[a-zA-Z_]\\\\w*::\",i=\"(?!struct)(\"+r+\"|\"+t.optional(a)+\"[a-zA-Z_]\\\\w*\"+t.optional(\"<[^<>]+>\")+\")\",s={className:\"type\",begin:\"\\\\b[a-z\\\\d_]*_t\\\\b\"},o={className:\"string\",variants:[{begin:'(u8?|U|L)?\"',end:'\"',illegal:\"\\\\n\",contains:[e.BACKSLASH_ESCAPE]},{begin:\"(u8?|U|L)?'(\\\\\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\\\S)|.)\",end:\"'\",illegal:\".\"},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R\"([^()\\\\ ]{0,16})\\(/,end:/\\)([^()\\\\ ]{0,16})\"/})]},l={className:\"number\",variants:[{begin:\"[+-]?(?:(?:[0-9](?:'?[0-9])*\\\\.(?:[0-9](?:'?[0-9])*)?|\\\\.[0-9](?:'?[0-9])*)(?:[Ee][+-]?[0-9](?:'?[0-9])*)?|[0-9](?:'?[0-9])*[Ee][+-]?[0-9](?:'?[0-9])*|0[Xx](?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*(?:\\\\.(?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)?)?|\\\\.[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)[Pp][+-]?[0-9](?:'?[0-9])*)(?:[Ff](?:16|32|64|128)?|(BF|bf)16|[Ll]|)\"},{begin:\"[+-]?\\\\b(?:0[Bb][01](?:'?[01])*|0[Xx][0-9A-Fa-f](?:'?[0-9A-Fa-f])*|0(?:'?[0-7])*|[1-9](?:'?[0-9])*)(?:[Uu](?:LL?|ll?)|[Uu][Zz]?|(?:LL?|ll?)[Uu]?|[Zz][Uu]|)\"}],relevance:0},c={className:\"meta\",begin:/#\\s*[a-z]+\\b/,end:/$/,keywords:{keyword:\"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include\"},contains:[{begin:/\\\\\\n/,relevance:0},e.inherit(o,{className:\"string\"}),{className:\"string\",begin:/<.*?>/},n,e.C_BLOCK_COMMENT_MODE]},u={className:\"title\",begin:t.optional(a)+e.IDENT_RE,relevance:0},d=t.optional(a)+e.IDENT_RE+\"\\\\s*\\\\(\",p={type:[\"bool\",\"char\",\"char16_t\",\"char32_t\",\"char8_t\",\"double\",\"float\",\"int\",\"long\",\"short\",\"void\",\"wchar_t\",\"unsigned\",\"signed\",\"const\",\"static\"],keyword:[\"alignas\",\"alignof\",\"and\",\"and_eq\",\"asm\",\"atomic_cancel\",\"atomic_commit\",\"atomic_noexcept\",\"auto\",\"bitand\",\"bitor\",\"break\",\"case\",\"catch\",\"class\",\"co_await\",\"co_return\",\"co_yield\",\"compl\",\"concept\",\"const_cast|10\",\"consteval\",\"constexpr\",\"constinit\",\"continue\",\"decltype\",\"default\",\"delete\",\"do\",\"dynamic_cast|10\",\"else\",\"enum\",\"explicit\",\"export\",\"extern\",\"false\",\"final\",\"for\",\"friend\",\"goto\",\"if\",\"import\",\"inline\",\"module\",\"mutable\",\"namespace\",\"new\",\"noexcept\",\"not\",\"not_eq\",\"nullptr\",\"operator\",\"or\",\"or_eq\",\"override\",\"private\",\"protected\",\"public\",\"reflexpr\",\"register\",\"reinterpret_cast|10\",\"requires\",\"return\",\"sizeof\",\"static_assert\",\"static_cast|10\",\"struct\",\"switch\",\"synchronized\",\"template\",\"this\",\"thread_local\",\"throw\",\"transaction_safe\",\"transaction_safe_dynamic\",\"true\",\"try\",\"typedef\",\"typeid\",\"typename\",\"union\",\"using\",\"virtual\",\"volatile\",\"while\",\"xor\",\"xor_eq\"],literal:[\"NULL\",\"false\",\"nullopt\",\"nullptr\",\"true\"],built_in:[\"_Pragma\"],_type_hints:[\"any\",\"auto_ptr\",\"barrier\",\"binary_semaphore\",\"bitset\",\"complex\",\"condition_variable\",\"condition_variable_any\",\"counting_semaphore\",\"deque\",\"false_type\",\"flat_map\",\"flat_set\",\"future\",\"imaginary\",\"initializer_list\",\"istringstream\",\"jthread\",\"latch\",\"lock_guard\",\"multimap\",\"multiset\",\"mutex\",\"optional\",\"ostringstream\",\"packaged_task\",\"pair\",\"promise\",\"priority_queue\",\"queue\",\"recursive_mutex\",\"recursive_timed_mutex\",\"scoped_lock\",\"set\",\"shared_future\",\"shared_lock\",\"shared_mutex\",\"shared_timed_mutex\",\"shared_ptr\",\"stack\",\"string_view\",\"stringstream\",\"timed_mutex\",\"thread\",\"true_type\",\"tuple\",\"unique_lock\",\"unique_ptr\",\"unordered_map\",\"unordered_multimap\",\"unordered_multiset\",\"unordered_set\",\"variant\",\"vector\",\"weak_ptr\",\"wstring\",\"wstring_view\"]},h={className:\"function.dispatch\",relevance:0,keywords:{_hint:[\"abort\",\"abs\",\"acos\",\"apply\",\"as_const\",\"asin\",\"atan\",\"atan2\",\"calloc\",\"ceil\",\"cerr\",\"cin\",\"clog\",\"cos\",\"cosh\",\"cout\",\"declval\",\"endl\",\"exchange\",\"exit\",\"exp\",\"fabs\",\"floor\",\"fmod\",\"forward\",\"fprintf\",\"fputs\",\"free\",\"frexp\",\"fscanf\",\"future\",\"invoke\",\"isalnum\",\"isalpha\",\"iscntrl\",\"isdigit\",\"isgraph\",\"islower\",\"isprint\",\"ispunct\",\"isspace\",\"isupper\",\"isxdigit\",\"labs\",\"launder\",\"ldexp\",\"log\",\"log10\",\"make_pair\",\"make_shared\",\"make_shared_for_overwrite\",\"make_tuple\",\"make_unique\",\"malloc\",\"memchr\",\"memcmp\",\"memcpy\",\"memset\",\"modf\",\"move\",\"pow\",\"printf\",\"putchar\",\"puts\",\"realloc\",\"scanf\",\"sin\",\"sinh\",\"snprintf\",\"sprintf\",\"sqrt\",\"sscanf\",\"std\",\"stderr\",\"stdin\",\"stdout\",\"strcat\",\"strchr\",\"strcmp\",\"strcpy\",\"strcspn\",\"strlen\",\"strncat\",\"strncmp\",\"strncpy\",\"strpbrk\",\"strrchr\",\"strspn\",\"strstr\",\"swap\",\"tan\",\"tanh\",\"terminate\",\"to_underlying\",\"tolower\",\"toupper\",\"vfprintf\",\"visit\",\"vprintf\",\"vsprintf\"]},begin:t.concat(/\\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,e.IDENT_RE,t.lookahead(/(<[^<>]+>|)\\s*\\(/))},f=[h,c,s,n,e.C_BLOCK_COMMENT_MODE,l,o],m={variants:[{begin:/=/,end:/;/},{begin:/\\(/,end:/\\)/},{beginKeywords:\"new throw return else\",end:/;/}],keywords:p,contains:f.concat([{begin:/\\(/,end:/\\)/,keywords:p,contains:f.concat([\"self\"]),relevance:0}]),relevance:0},g={className:\"function\",begin:\"(\"+i+\"[\\\\*&\\\\s]+)+\"+d,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:p,illegal:/[^\\w\\s\\*&:<>.]/,contains:[{begin:r,keywords:p,relevance:0},{begin:d,returnBegin:!0,contains:[u],relevance:0},{begin:/::/,relevance:0},{begin:/:/,endsWithParent:!0,contains:[o,l]},{relevance:0,match:/,/},{className:\"params\",begin:/\\(/,end:/\\)/,keywords:p,relevance:0,contains:[n,e.C_BLOCK_COMMENT_MODE,o,l,s,{begin:/\\(/,end:/\\)/,keywords:p,relevance:0,contains:[\"self\",n,e.C_BLOCK_COMMENT_MODE,o,l,s]}]},s,n,e.C_BLOCK_COMMENT_MODE,c]};return{name:\"C++\",aliases:[\"cc\",\"c++\",\"h++\",\"hpp\",\"hh\",\"hxx\",\"cxx\"],keywords:p,illegal:\"</\",classNameAliases:{\"function.dispatch\":\"built_in\"},contains:[].concat(m,g,h,f,[c,{begin:\"\\\\b(deque|list|queue|priority_queue|pair|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array|tuple|optional|variant|function|flat_map|flat_set)\\\\s*<(?!<)\",end:\">\",keywords:p,contains:[\"self\",s]},{begin:e.IDENT_RE+\"::\",keywords:p},{match:[/\\b(?:enum(?:\\s+(?:class|struct))?|class|struct|union)/,/\\s+/,/\\w+/],className:{1:\"keyword\",3:\"title.class\"}}])}}(e),r=n.keywords;return r.type=[...r.type,...t.type],r.literal=[...r.literal,...t.literal],r.built_in=[...r.built_in,...t.built_in],r._hints=t._hints,n.name=\"Arduino\",n.aliases=[\"ino\"],n.supersetOf=\"cpp\",n},bash:function(e){const t=e.regex,n={},r={begin:/\\$\\{/,end:/\\}/,contains:[\"self\",{begin:/:-/,contains:[n]}]};Object.assign(n,{className:\"variable\",variants:[{begin:t.concat(/\\$[\\w\\d#@][\\w\\d_]*/,\"(?![\\\\w\\\\d])(?![$])\")},r]});const a={className:\"subst\",begin:/\\$\\(/,end:/\\)/,contains:[e.BACKSLASH_ESCAPE]},i=e.inherit(e.COMMENT(),{match:[/(^|\\s)/,/#.*$/],scope:{2:\"comment\"}}),s={begin:/<<-?\\s*(?=\\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\\w+)/,end:/(\\w+)/,className:\"string\"})]}},o={className:\"string\",begin:/\"/,end:/\"/,contains:[e.BACKSLASH_ESCAPE,n,a]};a.contains.push(o);const l={begin:/\\$?\\(\\(/,end:/\\)\\)/,contains:[{begin:/\\d+#[0-9a-f]+/,className:\"number\"},e.NUMBER_MODE,n]},c=e.SHEBANG({binary:`(${[\"fish\",\"bash\",\"zsh\",\"sh\",\"csh\",\"ksh\",\"tcsh\",\"dash\",\"scsh\"].join(\"|\")})`,relevance:10}),u={className:\"function\",begin:/\\w[\\w\\d_]*\\s*\\(\\s*\\)\\s*\\{/,returnBegin:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/\\w[\\w\\d_]*/})],relevance:0};return{name:\"Bash\",aliases:[\"sh\",\"zsh\"],keywords:{$pattern:/\\b[a-z][a-z0-9._-]+\\b/,keyword:[\"if\",\"then\",\"else\",\"elif\",\"fi\",\"time\",\"for\",\"while\",\"until\",\"in\",\"do\",\"done\",\"case\",\"esac\",\"coproc\",\"function\",\"select\"],literal:[\"true\",\"false\"],built_in:[\"break\",\"cd\",\"continue\",\"eval\",\"exec\",\"exit\",\"export\",\"getopts\",\"hash\",\"pwd\",\"readonly\",\"return\",\"shift\",\"test\",\"times\",\"trap\",\"umask\",\"unset\",\"alias\",\"bind\",\"builtin\",\"caller\",\"command\",\"declare\",\"echo\",\"enable\",\"help\",\"let\",\"local\",\"logout\",\"mapfile\",\"printf\",\"read\",\"readarray\",\"source\",\"sudo\",\"type\",\"typeset\",\"ulimit\",\"unalias\",\"set\",\"shopt\",\"autoload\",\"bg\",\"bindkey\",\"bye\",\"cap\",\"chdir\",\"clone\",\"comparguments\",\"compcall\",\"compctl\",\"compdescribe\",\"compfiles\",\"compgroups\",\"compquote\",\"comptags\",\"comptry\",\"compvalues\",\"dirs\",\"disable\",\"disown\",\"echotc\",\"echoti\",\"emulate\",\"fc\",\"fg\",\"float\",\"functions\",\"getcap\",\"getln\",\"history\",\"integer\",\"jobs\",\"kill\",\"limit\",\"log\",\"noglob\",\"popd\",\"print\",\"pushd\",\"pushln\",\"rehash\",\"sched\",\"setcap\",\"setopt\",\"stat\",\"suspend\",\"ttyctl\",\"unfunction\",\"unhash\",\"unlimit\",\"unsetopt\",\"vared\",\"wait\",\"whence\",\"where\",\"which\",\"zcompile\",\"zformat\",\"zftp\",\"zle\",\"zmodload\",\"zparseopts\",\"zprof\",\"zpty\",\"zregexparse\",\"zsocket\",\"zstyle\",\"ztcp\",\"chcon\",\"chgrp\",\"chown\",\"chmod\",\"cp\",\"dd\",\"df\",\"dir\",\"dircolors\",\"ln\",\"ls\",\"mkdir\",\"mkfifo\",\"mknod\",\"mktemp\",\"mv\",\"realpath\",\"rm\",\"rmdir\",\"shred\",\"sync\",\"touch\",\"truncate\",\"vdir\",\"b2sum\",\"base32\",\"base64\",\"cat\",\"cksum\",\"comm\",\"csplit\",\"cut\",\"expand\",\"fmt\",\"fold\",\"head\",\"join\",\"md5sum\",\"nl\",\"numfmt\",\"od\",\"paste\",\"ptx\",\"pr\",\"sha1sum\",\"sha224sum\",\"sha256sum\",\"sha384sum\",\"sha512sum\",\"shuf\",\"sort\",\"split\",\"sum\",\"tac\",\"tail\",\"tr\",\"tsort\",\"unexpand\",\"uniq\",\"wc\",\"arch\",\"basename\",\"chroot\",\"date\",\"dirname\",\"du\",\"echo\",\"env\",\"expr\",\"factor\",\"groups\",\"hostid\",\"id\",\"link\",\"logname\",\"nice\",\"nohup\",\"nproc\",\"pathchk\",\"pinky\",\"printenv\",\"printf\",\"pwd\",\"readlink\",\"runcon\",\"seq\",\"sleep\",\"stat\",\"stdbuf\",\"stty\",\"tee\",\"test\",\"timeout\",\"tty\",\"uname\",\"unlink\",\"uptime\",\"users\",\"who\",\"whoami\",\"yes\"]},contains:[c,e.SHEBANG(),u,l,i,s,{match:/(\\/[a-z._-]+)+/},o,{match:/\\\\\"/},{className:\"string\",begin:/'/,end:/'/},{match:/\\\\'/},n]}},c:function(e){const t=e.regex,n=e.COMMENT(\"//\",\"$\",{contains:[{begin:/\\\\\\n/}]}),r=\"decltype\\\\(auto\\\\)\",a=\"[a-zA-Z_]\\\\w*::\",i=\"(\"+r+\"|\"+t.optional(a)+\"[a-zA-Z_]\\\\w*\"+t.optional(\"<[^<>]+>\")+\")\",s={className:\"type\",variants:[{begin:\"\\\\b[a-z\\\\d_]*_t\\\\b\"},{match:/\\batomic_[a-z]{3,6}\\b/}]},o={className:\"string\",variants:[{begin:'(u8?|U|L)?\"',end:'\"',illegal:\"\\\\n\",contains:[e.BACKSLASH_ESCAPE]},{begin:\"(u8?|U|L)?'(\\\\\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\\\S)|.)\",end:\"'\",illegal:\".\"},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R\"([^()\\\\ ]{0,16})\\(/,end:/\\)([^()\\\\ ]{0,16})\"/})]},l={className:\"number\",variants:[{match:/\\b(0b[01']+)/},{match:/(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)/},{match:/(-?)\\b(0[xX][a-fA-F0-9]+(?:'[a-fA-F0-9]+)*(?:\\.[a-fA-F0-9]*(?:'[a-fA-F0-9]*)*)?(?:[pP][-+]?[0-9]+)?(l|L)?(u|U)?)/},{match:/(-?)\\b\\d+(?:'\\d+)*(?:\\.\\d*(?:'\\d*)*)?(?:[eE][-+]?\\d+)?/}],relevance:0},c={className:\"meta\",begin:/#\\s*[a-z]+\\b/,end:/$/,keywords:{keyword:\"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef elifdef elifndef include\"},contains:[{begin:/\\\\\\n/,relevance:0},e.inherit(o,{className:\"string\"}),{className:\"string\",begin:/<.*?>/},n,e.C_BLOCK_COMMENT_MODE]},u={className:\"title\",begin:t.optional(a)+e.IDENT_RE,relevance:0},d=t.optional(a)+e.IDENT_RE+\"\\\\s*\\\\(\",p={keyword:[\"asm\",\"auto\",\"break\",\"case\",\"continue\",\"default\",\"do\",\"else\",\"enum\",\"extern\",\"for\",\"fortran\",\"goto\",\"if\",\"inline\",\"register\",\"restrict\",\"return\",\"sizeof\",\"typeof\",\"typeof_unqual\",\"struct\",\"switch\",\"typedef\",\"union\",\"volatile\",\"while\",\"_Alignas\",\"_Alignof\",\"_Atomic\",\"_Generic\",\"_Noreturn\",\"_Static_assert\",\"_Thread_local\",\"alignas\",\"alignof\",\"noreturn\",\"static_assert\",\"thread_local\",\"_Pragma\"],type:[\"float\",\"double\",\"signed\",\"unsigned\",\"int\",\"short\",\"long\",\"char\",\"void\",\"_Bool\",\"_BitInt\",\"_Complex\",\"_Imaginary\",\"_Decimal32\",\"_Decimal64\",\"_Decimal96\",\"_Decimal128\",\"_Decimal64x\",\"_Decimal128x\",\"_Float16\",\"_Float32\",\"_Float64\",\"_Float128\",\"_Float32x\",\"_Float64x\",\"_Float128x\",\"const\",\"static\",\"constexpr\",\"complex\",\"bool\",\"imaginary\"],literal:\"true false NULL\",built_in:\"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr\"},h=[c,s,n,e.C_BLOCK_COMMENT_MODE,l,o],f={variants:[{begin:/=/,end:/;/},{begin:/\\(/,end:/\\)/},{beginKeywords:\"new throw return else\",end:/;/}],keywords:p,contains:h.concat([{begin:/\\(/,end:/\\)/,keywords:p,contains:h.concat([\"self\"]),relevance:0}]),relevance:0},m={begin:\"(\"+i+\"[\\\\*&\\\\s]+)+\"+d,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:p,illegal:/[^\\w\\s\\*&:<>.]/,contains:[{begin:r,keywords:p,relevance:0},{begin:d,returnBegin:!0,contains:[e.inherit(u,{className:\"title.function\"})],relevance:0},{relevance:0,match:/,/},{className:\"params\",begin:/\\(/,end:/\\)/,keywords:p,relevance:0,contains:[n,e.C_BLOCK_COMMENT_MODE,o,l,s,{begin:/\\(/,end:/\\)/,keywords:p,relevance:0,contains:[\"self\",n,e.C_BLOCK_COMMENT_MODE,o,l,s]}]},s,n,e.C_BLOCK_COMMENT_MODE,c]};return{name:\"C\",aliases:[\"h\"],keywords:p,disableAutodetect:!0,illegal:\"</\",contains:[].concat(f,m,h,[c,{begin:e.IDENT_RE+\"::\",keywords:p},{className:\"class\",beginKeywords:\"enum class struct union\",end:/[{;:<>=]/,contains:[{beginKeywords:\"final class struct\"},e.TITLE_MODE]}]),exports:{preprocessor:c,strings:o,keywords:p}}},cpp:function(e){const t=e.regex,n=e.COMMENT(\"//\",\"$\",{contains:[{begin:/\\\\\\n/}]}),r=\"decltype\\\\(auto\\\\)\",a=\"[a-zA-Z_]\\\\w*::\",i=\"(?!struct)(\"+r+\"|\"+t.optional(a)+\"[a-zA-Z_]\\\\w*\"+t.optional(\"<[^<>]+>\")+\")\",s={className:\"type\",begin:\"\\\\b[a-z\\\\d_]*_t\\\\b\"},o={className:\"string\",variants:[{begin:'(u8?|U|L)?\"',end:'\"',illegal:\"\\\\n\",contains:[e.BACKSLASH_ESCAPE]},{begin:\"(u8?|U|L)?'(\\\\\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\\\S)|.)\",end:\"'\",illegal:\".\"},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R\"([^()\\\\ ]{0,16})\\(/,end:/\\)([^()\\\\ ]{0,16})\"/})]},l={className:\"number\",variants:[{begin:\"[+-]?(?:(?:[0-9](?:'?[0-9])*\\\\.(?:[0-9](?:'?[0-9])*)?|\\\\.[0-9](?:'?[0-9])*)(?:[Ee][+-]?[0-9](?:'?[0-9])*)?|[0-9](?:'?[0-9])*[Ee][+-]?[0-9](?:'?[0-9])*|0[Xx](?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*(?:\\\\.(?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)?)?|\\\\.[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)[Pp][+-]?[0-9](?:'?[0-9])*)(?:[Ff](?:16|32|64|128)?|(BF|bf)16|[Ll]|)\"},{begin:\"[+-]?\\\\b(?:0[Bb][01](?:'?[01])*|0[Xx][0-9A-Fa-f](?:'?[0-9A-Fa-f])*|0(?:'?[0-7])*|[1-9](?:'?[0-9])*)(?:[Uu](?:LL?|ll?)|[Uu][Zz]?|(?:LL?|ll?)[Uu]?|[Zz][Uu]|)\"}],relevance:0},c={className:\"meta\",begin:/#\\s*[a-z]+\\b/,end:/$/,keywords:{keyword:\"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include\"},contains:[{begin:/\\\\\\n/,relevance:0},e.inherit(o,{className:\"string\"}),{className:\"string\",begin:/<.*?>/},n,e.C_BLOCK_COMMENT_MODE]},u={className:\"title\",begin:t.optional(a)+e.IDENT_RE,relevance:0},d=t.optional(a)+e.IDENT_RE+\"\\\\s*\\\\(\",p={type:[\"bool\",\"char\",\"char16_t\",\"char32_t\",\"char8_t\",\"double\",\"float\",\"int\",\"long\",\"short\",\"void\",\"wchar_t\",\"unsigned\",\"signed\",\"const\",\"static\"],keyword:[\"alignas\",\"alignof\",\"and\",\"and_eq\",\"asm\",\"atomic_cancel\",\"atomic_commit\",\"atomic_noexcept\",\"auto\",\"bitand\",\"bitor\",\"break\",\"case\",\"catch\",\"class\",\"co_await\",\"co_return\",\"co_yield\",\"compl\",\"concept\",\"const_cast|10\",\"consteval\",\"constexpr\",\"constinit\",\"continue\",\"decltype\",\"default\",\"delete\",\"do\",\"dynamic_cast|10\",\"else\",\"enum\",\"explicit\",\"export\",\"extern\",\"false\",\"final\",\"for\",\"friend\",\"goto\",\"if\",\"import\",\"inline\",\"module\",\"mutable\",\"namespace\",\"new\",\"noexcept\",\"not\",\"not_eq\",\"nullptr\",\"operator\",\"or\",\"or_eq\",\"override\",\"private\",\"protected\",\"public\",\"reflexpr\",\"register\",\"reinterpret_cast|10\",\"requires\",\"return\",\"sizeof\",\"static_assert\",\"static_cast|10\",\"struct\",\"switch\",\"synchronized\",\"template\",\"this\",\"thread_local\",\"throw\",\"transaction_safe\",\"transaction_safe_dynamic\",\"true\",\"try\",\"typedef\",\"typeid\",\"typename\",\"union\",\"using\",\"virtual\",\"volatile\",\"while\",\"xor\",\"xor_eq\"],literal:[\"NULL\",\"false\",\"nullopt\",\"nullptr\",\"true\"],built_in:[\"_Pragma\"],_type_hints:[\"any\",\"auto_ptr\",\"barrier\",\"binary_semaphore\",\"bitset\",\"complex\",\"condition_variable\",\"condition_variable_any\",\"counting_semaphore\",\"deque\",\"false_type\",\"flat_map\",\"flat_set\",\"future\",\"imaginary\",\"initializer_list\",\"istringstream\",\"jthread\",\"latch\",\"lock_guard\",\"multimap\",\"multiset\",\"mutex\",\"optional\",\"ostringstream\",\"packaged_task\",\"pair\",\"promise\",\"priority_queue\",\"queue\",\"recursive_mutex\",\"recursive_timed_mutex\",\"scoped_lock\",\"set\",\"shared_future\",\"shared_lock\",\"shared_mutex\",\"shared_timed_mutex\",\"shared_ptr\",\"stack\",\"string_view\",\"stringstream\",\"timed_mutex\",\"thread\",\"true_type\",\"tuple\",\"unique_lock\",\"unique_ptr\",\"unordered_map\",\"unordered_multimap\",\"unordered_multiset\",\"unordered_set\",\"variant\",\"vector\",\"weak_ptr\",\"wstring\",\"wstring_view\"]},h={className:\"function.dispatch\",relevance:0,keywords:{_hint:[\"abort\",\"abs\",\"acos\",\"apply\",\"as_const\",\"asin\",\"atan\",\"atan2\",\"calloc\",\"ceil\",\"cerr\",\"cin\",\"clog\",\"cos\",\"cosh\",\"cout\",\"declval\",\"endl\",\"exchange\",\"exit\",\"exp\",\"fabs\",\"floor\",\"fmod\",\"forward\",\"fprintf\",\"fputs\",\"free\",\"frexp\",\"fscanf\",\"future\",\"invoke\",\"isalnum\",\"isalpha\",\"iscntrl\",\"isdigit\",\"isgraph\",\"islower\",\"isprint\",\"ispunct\",\"isspace\",\"isupper\",\"isxdigit\",\"labs\",\"launder\",\"ldexp\",\"log\",\"log10\",\"make_pair\",\"make_shared\",\"make_shared_for_overwrite\",\"make_tuple\",\"make_unique\",\"malloc\",\"memchr\",\"memcmp\",\"memcpy\",\"memset\",\"modf\",\"move\",\"pow\",\"printf\",\"putchar\",\"puts\",\"realloc\",\"scanf\",\"sin\",\"sinh\",\"snprintf\",\"sprintf\",\"sqrt\",\"sscanf\",\"std\",\"stderr\",\"stdin\",\"stdout\",\"strcat\",\"strchr\",\"strcmp\",\"strcpy\",\"strcspn\",\"strlen\",\"strncat\",\"strncmp\",\"strncpy\",\"strpbrk\",\"strrchr\",\"strspn\",\"strstr\",\"swap\",\"tan\",\"tanh\",\"terminate\",\"to_underlying\",\"tolower\",\"toupper\",\"vfprintf\",\"visit\",\"vprintf\",\"vsprintf\"]},begin:t.concat(/\\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,e.IDENT_RE,t.lookahead(/(<[^<>]+>|)\\s*\\(/))},f=[h,c,s,n,e.C_BLOCK_COMMENT_MODE,l,o],m={variants:[{begin:/=/,end:/;/},{begin:/\\(/,end:/\\)/},{beginKeywords:\"new throw return else\",end:/;/}],keywords:p,contains:f.concat([{begin:/\\(/,end:/\\)/,keywords:p,contains:f.concat([\"self\"]),relevance:0}]),relevance:0},g={className:\"function\",begin:\"(\"+i+\"[\\\\*&\\\\s]+)+\"+d,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:p,illegal:/[^\\w\\s\\*&:<>.]/,contains:[{begin:r,keywords:p,relevance:0},{begin:d,returnBegin:!0,contains:[u],relevance:0},{begin:/::/,relevance:0},{begin:/:/,endsWithParent:!0,contains:[o,l]},{relevance:0,match:/,/},{className:\"params\",begin:/\\(/,end:/\\)/,keywords:p,relevance:0,contains:[n,e.C_BLOCK_COMMENT_MODE,o,l,s,{begin:/\\(/,end:/\\)/,keywords:p,relevance:0,contains:[\"self\",n,e.C_BLOCK_COMMENT_MODE,o,l,s]}]},s,n,e.C_BLOCK_COMMENT_MODE,c]};return{name:\"C++\",aliases:[\"cc\",\"c++\",\"h++\",\"hpp\",\"hh\",\"hxx\",\"cxx\"],keywords:p,illegal:\"</\",classNameAliases:{\"function.dispatch\":\"built_in\"},contains:[].concat(m,g,h,f,[c,{begin:\"\\\\b(deque|list|queue|priority_queue|pair|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array|tuple|optional|variant|function|flat_map|flat_set)\\\\s*<(?!<)\",end:\">\",keywords:p,contains:[\"self\",s]},{begin:e.IDENT_RE+\"::\",keywords:p},{match:[/\\b(?:enum(?:\\s+(?:class|struct))?|class|struct|union)/,/\\s+/,/\\w+/],className:{1:\"keyword\",3:\"title.class\"}}])}},csharp:function(e){const t={keyword:[\"abstract\",\"as\",\"base\",\"break\",\"case\",\"catch\",\"class\",\"const\",\"continue\",\"do\",\"else\",\"event\",\"explicit\",\"extern\",\"finally\",\"fixed\",\"for\",\"foreach\",\"goto\",\"if\",\"implicit\",\"in\",\"interface\",\"internal\",\"is\",\"lock\",\"namespace\",\"new\",\"operator\",\"out\",\"override\",\"params\",\"private\",\"protected\",\"public\",\"readonly\",\"record\",\"ref\",\"return\",\"scoped\",\"sealed\",\"sizeof\",\"stackalloc\",\"static\",\"struct\",\"switch\",\"this\",\"throw\",\"try\",\"typeof\",\"unchecked\",\"unsafe\",\"using\",\"virtual\",\"void\",\"volatile\",\"while\"].concat([\"add\",\"alias\",\"and\",\"ascending\",\"args\",\"async\",\"await\",\"by\",\"descending\",\"dynamic\",\"equals\",\"file\",\"from\",\"get\",\"global\",\"group\",\"init\",\"into\",\"join\",\"let\",\"nameof\",\"not\",\"notnull\",\"on\",\"or\",\"orderby\",\"partial\",\"record\",\"remove\",\"required\",\"scoped\",\"select\",\"set\",\"unmanaged\",\"value|0\",\"var\",\"when\",\"where\",\"with\",\"yield\"]),built_in:[\"bool\",\"byte\",\"char\",\"decimal\",\"delegate\",\"double\",\"dynamic\",\"enum\",\"float\",\"int\",\"long\",\"nint\",\"nuint\",\"object\",\"sbyte\",\"short\",\"string\",\"ulong\",\"uint\",\"ushort\"],literal:[\"default\",\"false\",\"null\",\"true\"]},n=e.inherit(e.TITLE_MODE,{begin:\"[a-zA-Z](\\\\.?\\\\w)*\"}),r={className:\"number\",variants:[{begin:\"\\\\b(0b[01']+)\"},{begin:\"(-?)\\\\b([\\\\d']+(\\\\.[\\\\d']*)?|\\\\.[\\\\d']+)(u|U|l|L|ul|UL|f|F|b|B)\"},{begin:\"(-?)(\\\\b0[xX][a-fA-F0-9']+|(\\\\b[\\\\d']+(\\\\.[\\\\d']*)?|\\\\.[\\\\d']+)([eE][-+]?[\\\\d']+)?)\"}],relevance:0},a={className:\"string\",begin:'@\"',end:'\"',contains:[{begin:'\"\"'}]},i=e.inherit(a,{illegal:/\\n/}),s={className:\"subst\",begin:/\\{/,end:/\\}/,keywords:t},o=e.inherit(s,{illegal:/\\n/}),l={className:\"string\",begin:/\\$\"/,end:'\"',illegal:/\\n/,contains:[{begin:/\\{\\{/},{begin:/\\}\\}/},e.BACKSLASH_ESCAPE,o]},c={className:\"string\",begin:/\\$@\"/,end:'\"',contains:[{begin:/\\{\\{/},{begin:/\\}\\}/},{begin:'\"\"'},s]},u=e.inherit(c,{illegal:/\\n/,contains:[{begin:/\\{\\{/},{begin:/\\}\\}/},{begin:'\"\"'},o]});s.contains=[c,l,a,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,r,e.C_BLOCK_COMMENT_MODE],o.contains=[u,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,r,e.inherit(e.C_BLOCK_COMMENT_MODE,{illegal:/\\n/})];const d={variants:[{className:\"string\",begin:/\"\"\"(\"*)(?!\")(.|\\n)*?\"\"\"\\1/,relevance:1},c,l,a,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},p={begin:\"<\",end:\">\",contains:[{beginKeywords:\"in out\"},n]},h=e.IDENT_RE+\"(<\"+e.IDENT_RE+\"(\\\\s*,\\\\s*\"+e.IDENT_RE+\")*>)?(\\\\[\\\\])?\",f={begin:\"@\"+e.IDENT_RE,relevance:0};return{name:\"C#\",aliases:[\"cs\",\"c#\"],keywords:t,illegal:/::/,contains:[e.COMMENT(\"///\",\"$\",{returnBegin:!0,contains:[{className:\"doctag\",variants:[{begin:\"///\",relevance:0},{begin:\"\\x3c!--|--\\x3e\"},{begin:\"</?\",end:\">\"}]}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:\"meta\",begin:\"#\",end:\"$\",keywords:{keyword:\"if else elif endif define undef warning error line region endregion pragma checksum\"}},d,r,{beginKeywords:\"class interface\",relevance:0,end:/[{;=]/,illegal:/[^\\s:,]/,contains:[{beginKeywords:\"where class\"},n,p,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:\"namespace\",relevance:0,end:/[{;=]/,illegal:/[^\\s:]/,contains:[n,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:\"record\",relevance:0,end:/[{;=]/,illegal:/[^\\s:]/,contains:[n,p,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:\"meta\",begin:\"^\\\\s*\\\\[(?=[\\\\w])\",excludeBegin:!0,end:\"\\\\]\",excludeEnd:!0,contains:[{className:\"string\",begin:/\"/,end:/\"/}]},{beginKeywords:\"new return throw await else\",relevance:0},{className:\"function\",begin:\"(\"+h+\"\\\\s+)+\"+e.IDENT_RE+\"\\\\s*(<[^=]+>\\\\s*)?\\\\(\",returnBegin:!0,end:/\\s*[{;=]/,excludeEnd:!0,keywords:t,contains:[{beginKeywords:[\"public\",\"private\",\"protected\",\"static\",\"internal\",\"protected\",\"abstract\",\"async\",\"extern\",\"override\",\"unsafe\",\"virtual\",\"new\",\"sealed\",\"partial\"].join(\" \"),relevance:0},{begin:e.IDENT_RE+\"\\\\s*(<[^=]+>\\\\s*)?\\\\(\",returnBegin:!0,contains:[e.TITLE_MODE,p],relevance:0},{match:/\\(\\)/},{className:\"params\",begin:/\\(/,end:/\\)/,excludeBegin:!0,excludeEnd:!0,keywords:t,relevance:0,contains:[d,r,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},f]}},css:function(e){const t=e.regex,n=(e=>({IMPORTANT:{scope:\"meta\",begin:\"!important\"},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:\"number\",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\\b/},FUNCTION_DISPATCH:{className:\"built_in\",begin:/[\\w-]+(?=\\()/},ATTRIBUTE_SELECTOR_MODE:{scope:\"selector-attr\",begin:/\\[/,end:/\\]/,illegal:\"$\",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{scope:\"number\",begin:e.NUMBER_RE+\"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?\",relevance:0},CSS_VARIABLE:{className:\"attr\",begin:/--[A-Za-z_][A-Za-z0-9_-]*/}}))(e),r=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE];return{name:\"CSS\",case_insensitive:!0,illegal:/[=|'\\$]/,keywords:{keyframePosition:\"from to\"},classNameAliases:{keyframePosition:\"selector-tag\"},contains:[n.BLOCK_COMMENT,{begin:/-(webkit|moz|ms|o)-(?=[a-z])/},n.CSS_NUMBER_MODE,{className:\"selector-id\",begin:/#[A-Za-z0-9_-]+/,relevance:0},{className:\"selector-class\",begin:\"\\\\.[a-zA-Z-][a-zA-Z0-9_-]*\",relevance:0},n.ATTRIBUTE_SELECTOR_MODE,{className:\"selector-pseudo\",variants:[{begin:\":(\"+Ya.join(\"|\")+\")\"},{begin:\":(:)?(\"+Wa.join(\"|\")+\")\"}]},n.CSS_VARIABLE,{className:\"attribute\",begin:\"\\\\b(\"+Va.join(\"|\")+\")\\\\b\"},{begin:/:/,end:/[;}{]/,contains:[n.BLOCK_COMMENT,n.HEXCOLOR,n.IMPORTANT,n.CSS_NUMBER_MODE,...r,{begin:/(url|data-uri)\\(/,end:/\\)/,relevance:0,keywords:{built_in:\"url data-uri\"},contains:[...r,{className:\"string\",begin:/[^)]/,endsWithParent:!0,excludeEnd:!0}]},n.FUNCTION_DISPATCH]},{begin:t.lookahead(/@/),end:\"[{;]\",relevance:0,illegal:/:/,contains:[{className:\"keyword\",begin:/@-?\\w[\\w]*(-\\w+)*/},{begin:/\\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{$pattern:/[a-z-]+/,keyword:\"and or not only\",attribute:qa.join(\" \")},contains:[{begin:/[a-z-]+(?=:)/,className:\"attribute\"},...r,n.CSS_NUMBER_MODE]}]},{className:\"selector-tag\",begin:\"\\\\b(\"+$a.join(\"|\")+\")\\\\b\"}]}},diff:function(e){const t=e.regex;return{name:\"Diff\",aliases:[\"patch\"],contains:[{className:\"meta\",relevance:10,match:t.either(/^@@ +-\\d+,\\d+ +\\+\\d+,\\d+ +@@/,/^\\*\\*\\* +\\d+,\\d+ +\\*\\*\\*\\*$/,/^--- +\\d+,\\d+ +----$/)},{className:\"comment\",variants:[{begin:t.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\\*{3} /,/^\\+{3}/,/^diff --git/),end:/$/},{match:/^\\*{15}$/}]},{className:\"addition\",begin:/^\\+/,end:/$/},{className:\"deletion\",begin:/^-/,end:/$/},{className:\"addition\",begin:/^!/,end:/$/}]}},go:function(e){const t={keyword:[\"break\",\"case\",\"chan\",\"const\",\"continue\",\"default\",\"defer\",\"else\",\"fallthrough\",\"for\",\"func\",\"go\",\"goto\",\"if\",\"import\",\"interface\",\"map\",\"package\",\"range\",\"return\",\"select\",\"struct\",\"switch\",\"type\",\"var\"],type:[\"bool\",\"byte\",\"complex64\",\"complex128\",\"error\",\"float32\",\"float64\",\"int8\",\"int16\",\"int32\",\"int64\",\"string\",\"uint8\",\"uint16\",\"uint32\",\"uint64\",\"int\",\"uint\",\"uintptr\",\"rune\"],literal:[\"true\",\"false\",\"iota\",\"nil\"],built_in:[\"append\",\"cap\",\"close\",\"complex\",\"copy\",\"imag\",\"len\",\"make\",\"new\",\"panic\",\"print\",\"println\",\"real\",\"recover\",\"delete\"]};return{name:\"Go\",aliases:[\"golang\"],keywords:t,illegal:\"</\",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:\"string\",variants:[e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{begin:\"`\",end:\"`\"}]},{className:\"number\",variants:[{match:/-?\\b0[xX]\\.[a-fA-F0-9](_?[a-fA-F0-9])*[pP][+-]?\\d(_?\\d)*i?/,relevance:0},{match:/-?\\b0[xX](_?[a-fA-F0-9])+((\\.([a-fA-F0-9](_?[a-fA-F0-9])*)?)?[pP][+-]?\\d(_?\\d)*)?i?/,relevance:0},{match:/-?\\b0[oO](_?[0-7])*i?/,relevance:0},{match:/-?\\.\\d(_?\\d)*([eE][+-]?\\d(_?\\d)*)?i?/,relevance:0},{match:/-?\\b\\d(_?\\d)*(\\.(\\d(_?\\d)*)?)?([eE][+-]?\\d(_?\\d)*)?i?/,relevance:0}]},{begin:/:=/},{className:\"function\",beginKeywords:\"func\",end:\"\\\\s*(\\\\{|$)\",excludeEnd:!0,contains:[e.TITLE_MODE,{className:\"params\",begin:/\\(/,end:/\\)/,endsParent:!0,keywords:t,illegal:/[\"']/}]}]}},graphql:function(e){const t=e.regex;return{name:\"GraphQL\",aliases:[\"gql\"],case_insensitive:!0,disableAutodetect:!1,keywords:{keyword:[\"query\",\"mutation\",\"subscription\",\"type\",\"input\",\"schema\",\"directive\",\"interface\",\"union\",\"scalar\",\"fragment\",\"enum\",\"on\"],literal:[\"true\",\"false\",\"null\"]},contains:[e.HASH_COMMENT_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE,{scope:\"punctuation\",match:/[.]{3}/,relevance:0},{scope:\"punctuation\",begin:/[\\!\\(\\)\\:\\=\\[\\]\\{\\|\\}]{1}/,relevance:0},{scope:\"variable\",begin:/\\$/,end:/\\W/,excludeEnd:!0,relevance:0},{scope:\"meta\",match:/@\\w+/,excludeEnd:!0},{scope:\"symbol\",begin:t.concat(/[_A-Za-z][_0-9A-Za-z]*/,t.lookahead(/\\s*:/)),relevance:0}],illegal:[/[;<']/,/BEGIN/]}},ini:function(e){const t=e.regex,n={className:\"number\",relevance:0,variants:[{begin:/([+-]+)?[\\d]+_[\\d_]+/},{begin:e.NUMBER_RE}]},r=e.COMMENT();r.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];const a={className:\"variable\",variants:[{begin:/\\$[\\w\\d\"][\\w\\d_]*/},{begin:/\\$\\{(.*?)\\}/}]},i={className:\"literal\",begin:/\\bon|off|true|false|yes|no\\b/},s={className:\"string\",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:\"'''\",end:\"'''\",relevance:10},{begin:'\"\"\"',end:'\"\"\"',relevance:10},{begin:'\"',end:'\"'},{begin:\"'\",end:\"'\"}]},o={begin:/\\[/,end:/\\]/,contains:[r,i,a,s,n,\"self\"],relevance:0},l=t.either(/[A-Za-z0-9_-]+/,/\"(\\\\\"|[^\"])*\"/,/'[^']*'/);return{name:\"TOML, also INI\",aliases:[\"toml\"],case_insensitive:!0,illegal:/\\S/,contains:[r,{className:\"section\",begin:/\\[+/,end:/\\]+/},{begin:t.concat(l,\"(\\\\s*\\\\.\\\\s*\",l,\")*\",t.lookahead(/\\s*=\\s*[^#\\s]/)),className:\"attr\",starts:{end:/$/,contains:[r,o,i,a,s,n]}}]}},java:function(e){const t=e.regex,n=\"[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*\",r=n+Ja(\"(?:<\"+n+\"~~~(?:\\\\s*,\\\\s*\"+n+\"~~~)*>)?\",/~~~/g,2),a={keyword:[\"synchronized\",\"abstract\",\"private\",\"var\",\"static\",\"if\",\"const \",\"for\",\"while\",\"strictfp\",\"finally\",\"protected\",\"import\",\"native\",\"final\",\"void\",\"enum\",\"else\",\"break\",\"transient\",\"catch\",\"instanceof\",\"volatile\",\"case\",\"assert\",\"package\",\"default\",\"public\",\"try\",\"switch\",\"continue\",\"throws\",\"protected\",\"public\",\"private\",\"module\",\"requires\",\"exports\",\"do\",\"sealed\",\"yield\",\"permits\",\"goto\",\"when\"],literal:[\"false\",\"true\",\"null\"],type:[\"char\",\"boolean\",\"long\",\"float\",\"int\",\"byte\",\"short\",\"double\"],built_in:[\"super\",\"this\"]},i={className:\"meta\",begin:\"@\"+n,contains:[{begin:/\\(/,end:/\\)/,contains:[\"self\"]}]},s={className:\"params\",begin:/\\(/,end:/\\)/,keywords:a,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE],endsParent:!0};return{name:\"Java\",aliases:[\"jsp\"],keywords:a,illegal:/<\\/|#/,contains:[e.COMMENT(\"/\\\\*\\\\*\",\"\\\\*/\",{relevance:0,contains:[{begin:/\\w+@/,relevance:0},{className:\"doctag\",begin:\"@[A-Za-z]+\"}]}),{begin:/import java\\.[a-z]+\\./,keywords:\"import\",relevance:2},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{begin:/\"\"\"/,end:/\"\"\"/,className:\"string\",contains:[e.BACKSLASH_ESCAPE]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{match:[/\\b(?:class|interface|enum|extends|implements|new)/,/\\s+/,n],className:{1:\"keyword\",3:\"title.class\"}},{match:/non-sealed/,scope:\"keyword\"},{begin:[t.concat(/(?!else)/,n),/\\s+/,n,/\\s+/,/=(?!=)/],className:{1:\"type\",3:\"variable\",5:\"operator\"}},{begin:[/record/,/\\s+/,n],className:{1:\"keyword\",3:\"title.class\"},contains:[s,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:\"new throw return else\",relevance:0},{begin:[\"(?:\"+r+\"\\\\s+)\",e.UNDERSCORE_IDENT_RE,/\\s*(?=\\()/],className:{2:\"title.function\"},keywords:a,contains:[{className:\"params\",begin:/\\(/,end:/\\)/,keywords:a,relevance:0,contains:[i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,Za,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},Za,i]}},javascript:function(e){const t=e.regex,n=ei,r=\"<>\",a=\"</>\",i={begin:/<[A-Za-z0-9\\\\._:-]+/,end:/\\/[A-Za-z0-9\\\\._:-]+>|\\/>/,isTrulyOpeningTag:(e,t)=>{const n=e[0].length+e.index,r=e.input[n];if(\"<\"===r||\",\"===r)return void t.ignoreMatch();let a;\">\"===r&&(((e,{after:t})=>{const n=\"</\"+e[0].slice(1);return-1!==e.input.indexOf(n,t)})(e,{after:n})||t.ignoreMatch());const i=e.input.substring(n);((a=i.match(/^\\s*=/))||(a=i.match(/^\\s+extends\\s+/))&&0===a.index)&&t.ignoreMatch()}},s={$pattern:ei,keyword:ti,literal:ni,built_in:oi,\"variable.language\":si},o=\"[0-9](_?[0-9])*\",l=`\\\\.(${o})`,c=\"0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*\",u={className:\"number\",variants:[{begin:`(\\\\b(${c})((${l})|\\\\.)?|(${l}))[eE][+-]?(${o})\\\\b`},{begin:`\\\\b(${c})\\\\b((${l})\\\\b|\\\\.)?|(${l})\\\\b`},{begin:\"\\\\b(0|[1-9](_?[0-9])*)n\\\\b\"},{begin:\"\\\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\\\b\"},{begin:\"\\\\b0[bB][0-1](_?[0-1])*n?\\\\b\"},{begin:\"\\\\b0[oO][0-7](_?[0-7])*n?\\\\b\"},{begin:\"\\\\b0[0-7]+n?\\\\b\"}],relevance:0},d={className:\"subst\",begin:\"\\\\$\\\\{\",end:\"\\\\}\",keywords:s,contains:[]},p={begin:\".?html`\",end:\"\",starts:{end:\"`\",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,d],subLanguage:\"xml\"}},h={begin:\".?css`\",end:\"\",starts:{end:\"`\",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,d],subLanguage:\"css\"}},f={begin:\".?gql`\",end:\"\",starts:{end:\"`\",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,d],subLanguage:\"graphql\"}},m={className:\"string\",begin:\"`\",end:\"`\",contains:[e.BACKSLASH_ESCAPE,d]},g={className:\"comment\",variants:[e.COMMENT(/\\/\\*\\*(?!\\/)/,\"\\\\*/\",{relevance:0,contains:[{begin:\"(?=@[A-Za-z]+)\",relevance:0,contains:[{className:\"doctag\",begin:\"@[A-Za-z]+\"},{className:\"type\",begin:\"\\\\{\",end:\"\\\\}\",excludeEnd:!0,excludeBegin:!0,relevance:0},{className:\"variable\",begin:n+\"(?=\\\\s*(-)|$)\",endsParent:!0,relevance:0},{begin:/(?=[^\\n])\\s/,relevance:0}]}]}),e.C_BLOCK_COMMENT_MODE,e.C_LINE_COMMENT_MODE]},b=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,p,h,f,m,{match:/\\$\\d+/},u];d.contains=b.concat({begin:/\\{/,end:/\\}/,keywords:s,contains:[\"self\"].concat(b)});const E=[].concat(g,d.contains),y=E.concat([{begin:/(\\s*)\\(/,end:/\\)/,keywords:s,contains:[\"self\"].concat(E)}]),_={className:\"params\",begin:/(\\s*)\\(/,end:/\\)/,excludeBegin:!0,excludeEnd:!0,keywords:s,contains:y},k={variants:[{match:[/class/,/\\s+/,n,/\\s+/,/extends/,/\\s+/,t.concat(n,\"(\",t.concat(/\\./,n),\")*\")],scope:{1:\"keyword\",3:\"title.class\",5:\"keyword\",7:\"title.class.inherited\"}},{match:[/class/,/\\s+/,n],scope:{1:\"keyword\",3:\"title.class\"}}]},T={relevance:0,match:t.either(/\\bJSON/,/\\b[A-Z][a-z]+([A-Z][a-z]*|\\d)*/,/\\b[A-Z]{2,}([A-Z][a-z]+|\\d)+([A-Z][a-z]*)*/,/\\b[A-Z]{2,}[a-z]+([A-Z][a-z]+|\\d)*([A-Z][a-z]*)*/),className:\"title.class\",keywords:{_:[...ri,...ai]}},S={variants:[{match:[/function/,/\\s+/,n,/(?=\\s*\\()/]},{match:[/function/,/\\s*(?=\\()/]}],className:{1:\"keyword\",3:\"title.function\"},label:\"func.def\",contains:[_],illegal:/%/},v={match:t.concat(/\\b/,(A=[...ii,\"super\",\"import\"].map(e=>`${e}\\\\s*\\\\(`),t.concat(\"(?!\",A.join(\"|\"),\")\")),n,t.lookahead(/\\s*\\(/)),className:\"title.function\",relevance:0};var A;const N={begin:t.concat(/\\./,t.lookahead(t.concat(n,/(?![0-9A-Za-z$_(])/))),end:n,excludeBegin:!0,keywords:\"prototype\",className:\"property\",relevance:0},C={match:[/get|set/,/\\s+/,n,/(?=\\()/],className:{1:\"keyword\",3:\"title.function\"},contains:[{begin:/\\(\\)/},_]},x=\"(\\\\([^()]*(\\\\([^()]*(\\\\([^()]*\\\\)[^()]*)*\\\\)[^()]*)*\\\\)|\"+e.UNDERSCORE_IDENT_RE+\")\\\\s*=>\",w={match:[/const|var|let/,/\\s+/,n,/\\s*/,/=\\s*/,/(async\\s*)?/,t.lookahead(x)],keywords:\"async\",className:{1:\"keyword\",3:\"title.function\"},contains:[_]};return{name:\"JavaScript\",aliases:[\"js\",\"jsx\",\"mjs\",\"cjs\"],keywords:s,exports:{PARAMS_CONTAINS:y,CLASS_REFERENCE:T},illegal:/#(?![$_A-z])/,contains:[e.SHEBANG({label:\"shebang\",binary:\"node\",relevance:5}),{label:\"use_strict\",className:\"meta\",relevance:10,begin:/^\\s*['\"]use (strict|asm)['\"]/},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,p,h,f,m,g,{match:/\\$\\d+/},u,T,{scope:\"attr\",match:n+t.lookahead(\":\"),relevance:0},w,{begin:\"(\"+e.RE_STARTERS_RE+\"|\\\\b(case|return|throw)\\\\b)\\\\s*\",keywords:\"return throw case\",relevance:0,contains:[g,e.REGEXP_MODE,{className:\"function\",begin:x,returnBegin:!0,end:\"\\\\s*=>\",contains:[{className:\"params\",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\\(\\s*\\)/,skip:!0},{begin:/(\\s*)\\(/,end:/\\)/,excludeBegin:!0,excludeEnd:!0,keywords:s,contains:y}]}]},{begin:/,/,relevance:0},{match:/\\s+/,relevance:0},{variants:[{begin:r,end:a},{match:/<[A-Za-z0-9\\\\._:-]+\\s*\\/>/},{begin:i.begin,\"on:begin\":i.isTrulyOpeningTag,end:i.end}],subLanguage:\"xml\",contains:[{begin:i.begin,end:i.end,skip:!0,contains:[\"self\"]}]}]},S,{beginKeywords:\"while if switch catch for\"},{begin:\"\\\\b(?!function)\"+e.UNDERSCORE_IDENT_RE+\"\\\\([^()]*(\\\\([^()]*(\\\\([^()]*\\\\)[^()]*)*\\\\)[^()]*)*\\\\)\\\\s*\\\\{\",returnBegin:!0,label:\"func.def\",contains:[_,e.inherit(e.TITLE_MODE,{begin:n,className:\"title.function\"})]},{match:/\\.\\.\\./,relevance:0},N,{match:\"\\\\$\"+n,relevance:0},{match:[/\\bconstructor(?=\\s*\\()/],className:{1:\"title.function\"},contains:[_]},v,{relevance:0,match:/\\b[A-Z][A-Z_0-9]+\\b/,className:\"variable.constant\"},k,C,{match:/\\$[(.]/}]}},json:function(e){const t=[\"true\",\"false\",\"null\"],n={scope:\"literal\",beginKeywords:t.join(\" \")};return{name:\"JSON\",aliases:[\"jsonc\"],keywords:{literal:t},contains:[{className:\"attr\",begin:/\"(\\\\.|[^\\\\\"\\r\\n])*\"(?=\\s*:)/,relevance:1.01},{match:/[{}[\\],:]/,className:\"punctuation\",relevance:0},e.QUOTE_STRING_MODE,n,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE],illegal:\"\\\\S\"}},kotlin:function(e){const t={keyword:\"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual\",built_in:\"Byte Short Char Int Long Boolean Float Double Void Unit Nothing\",literal:\"true false null\"},n={className:\"symbol\",begin:e.UNDERSCORE_IDENT_RE+\"@\"},r={className:\"subst\",begin:/\\$\\{/,end:/\\}/,contains:[e.C_NUMBER_MODE]},a={className:\"variable\",begin:\"\\\\$\"+e.UNDERSCORE_IDENT_RE},i={className:\"string\",variants:[{begin:'\"\"\"',end:'\"\"\"(?=[^\"])',contains:[a,r]},{begin:\"'\",end:\"'\",illegal:/\\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'\"',end:'\"',illegal:/\\n/,contains:[e.BACKSLASH_ESCAPE,a,r]}]};r.contains.push(i);const s={className:\"meta\",begin:\"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\\\s*:(?:\\\\s*\"+e.UNDERSCORE_IDENT_RE+\")?\"},o={className:\"meta\",begin:\"@\"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\\(/,end:/\\)/,contains:[e.inherit(i,{className:\"string\"}),\"self\"]}]},l=di,c=e.COMMENT(\"/\\\\*\",\"\\\\*/\",{contains:[e.C_BLOCK_COMMENT_MODE]}),u={variants:[{className:\"type\",begin:e.UNDERSCORE_IDENT_RE},{begin:/\\(/,end:/\\)/,contains:[]}]},d=u;return d.variants[1].contains=[u],u.variants[1].contains=[d],{name:\"Kotlin\",aliases:[\"kt\",\"kts\"],keywords:t,contains:[e.COMMENT(\"/\\\\*\\\\*\",\"\\\\*/\",{relevance:0,contains:[{className:\"doctag\",begin:\"@[A-Za-z]+\"}]}),e.C_LINE_COMMENT_MODE,c,{className:\"keyword\",begin:/\\b(break|continue|return|this)\\b/,starts:{contains:[{className:\"symbol\",begin:/@\\w+/}]}},n,s,o,{className:\"function\",beginKeywords:\"fun\",end:\"[(]|$\",returnBegin:!0,excludeEnd:!0,keywords:t,relevance:5,contains:[{begin:e.UNDERSCORE_IDENT_RE+\"\\\\s*\\\\(\",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:\"type\",begin:/</,end:/>/,keywords:\"reified\",relevance:0},{className:\"params\",begin:/\\(/,end:/\\)/,endsParent:!0,keywords:t,relevance:0,contains:[{begin:/:/,end:/[=,\\/]/,endsWithParent:!0,contains:[u,e.C_LINE_COMMENT_MODE,c],relevance:0},e.C_LINE_COMMENT_MODE,c,s,o,i,e.C_NUMBER_MODE]},c]},{begin:[/class|interface|trait/,/\\s+/,e.UNDERSCORE_IDENT_RE],beginScope:{3:\"title.class\"},keywords:\"class interface trait\",end:/[:\\{(]|$/,excludeEnd:!0,illegal:\"extends implements\",contains:[{beginKeywords:\"public protected internal private constructor\"},e.UNDERSCORE_TITLE_MODE,{className:\"type\",begin:/</,end:/>/,excludeBegin:!0,excludeEnd:!0,relevance:0},{className:\"type\",begin:/[,:]\\s*/,end:/[<\\(,){\\s]|$/,excludeBegin:!0,returnEnd:!0},s,o]},i,{className:\"meta\",begin:\"^#!/usr/bin/env\",end:\"$\",illegal:\"\\n\"},l]}},less:function(e){const t=(e=>({IMPORTANT:{scope:\"meta\",begin:\"!important\"},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:\"number\",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\\b/},FUNCTION_DISPATCH:{className:\"built_in\",begin:/[\\w-]+(?=\\()/},ATTRIBUTE_SELECTOR_MODE:{scope:\"selector-attr\",begin:/\\[/,end:/\\]/,illegal:\"$\",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{scope:\"number\",begin:e.NUMBER_RE+\"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?\",relevance:0},CSS_VARIABLE:{className:\"attr\",begin:/--[A-Za-z_][A-Za-z0-9_-]*/}}))(e),n=bi,r=\"[\\\\w-]+\",a=\"(\"+r+\"|@\\\\{\"+r+\"\\\\})\",i=[],s=[],o=function(e){return{className:\"string\",begin:\"~?\"+e+\".*?\"+e}},l=function(e,t,n){return{className:e,begin:t,relevance:n}},c={$pattern:/[a-z-]+/,keyword:\"and or not only\",attribute:hi.join(\" \")},u={begin:\"\\\\(\",end:\"\\\\)\",contains:s,keywords:c,relevance:0};s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,o(\"'\"),o('\"'),t.CSS_NUMBER_MODE,{begin:\"(url|data-uri)\\\\(\",starts:{className:\"string\",end:\"[\\\\)\\\\n]\",excludeEnd:!0}},t.HEXCOLOR,u,l(\"variable\",\"@@?\"+r,10),l(\"variable\",\"@\\\\{\"+r+\"\\\\}\"),l(\"built_in\",\"~?`[^`]*?`\"),{className:\"attribute\",begin:r+\"\\\\s*:\",end:\":\",returnBegin:!0,excludeEnd:!0},t.IMPORTANT,{beginKeywords:\"and not\"},t.FUNCTION_DISPATCH);const d=s.concat({begin:/\\{/,end:/\\}/,contains:i}),p={beginKeywords:\"when\",endsWithParent:!0,contains:[{beginKeywords:\"and not\"}].concat(s)},h={begin:a+\"\\\\s*:\",returnBegin:!0,end:/[;}]/,relevance:0,contains:[{begin:/-(webkit|moz|ms|o)-/},t.CSS_VARIABLE,{className:\"attribute\",begin:\"\\\\b(\"+gi.join(\"|\")+\")\\\\b\",end:/(?=:)/,starts:{endsWithParent:!0,illegal:\"[<=$]\",relevance:0,contains:s}}]},f={className:\"keyword\",begin:\"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\\\b\",starts:{end:\"[;{}]\",keywords:c,returnEnd:!0,contains:s,relevance:0}},m={className:\"variable\",variants:[{begin:\"@\"+r+\"\\\\s*:\",relevance:15},{begin:\"@\"+r}],starts:{end:\"[;}]\",returnEnd:!0,contains:d}},g={variants:[{begin:\"[\\\\.#:&\\\\[>]\",end:\"[;{}]\"},{begin:a,end:/\\{/}],returnBegin:!0,returnEnd:!0,illegal:\"[<='$\\\"]\",relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,p,l(\"keyword\",\"all\\\\b\"),l(\"variable\",\"@\\\\{\"+r+\"\\\\}\"),{begin:\"\\\\b(\"+pi.join(\"|\")+\")\\\\b\",className:\"selector-tag\"},t.CSS_NUMBER_MODE,l(\"selector-tag\",a,0),l(\"selector-id\",\"#\"+a),l(\"selector-class\",\"\\\\.\"+a,0),l(\"selector-tag\",\"&\",0),t.ATTRIBUTE_SELECTOR_MODE,{className:\"selector-pseudo\",begin:\":(\"+fi.join(\"|\")+\")\"},{className:\"selector-pseudo\",begin:\":(:)?(\"+mi.join(\"|\")+\")\"},{begin:/\\(/,end:/\\)/,relevance:0,contains:d},{begin:\"!important\"},t.FUNCTION_DISPATCH]},b={begin:r+`:(:)?(${n.join(\"|\")})`,returnBegin:!0,contains:[g]};return i.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,f,m,b,h,g,p,t.FUNCTION_DISPATCH),{name:\"Less\",case_insensitive:!0,illegal:\"[=>'/<($\\\"]\",contains:i}},lua:function(e){const t=\"\\\\[=*\\\\[\",n=\"\\\\]=*\\\\]\",r={begin:t,end:n,contains:[\"self\"]},a=[e.COMMENT(\"--(?!\"+t+\")\",\"$\"),e.COMMENT(\"--\"+t,n,{contains:[r],relevance:10})];return{name:\"Lua\",aliases:[\"pluto\"],keywords:{$pattern:e.UNDERSCORE_IDENT_RE,literal:\"true false nil\",keyword:\"and break do else elseif end for goto if in local not or repeat return then until while\",built_in:\"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove\"},contains:a.concat([{className:\"function\",beginKeywords:\"function\",end:\"\\\\)\",contains:[e.inherit(e.TITLE_MODE,{begin:\"([_a-zA-Z]\\\\w*\\\\.)*([_a-zA-Z]\\\\w*:)?[_a-zA-Z]\\\\w*\"}),{className:\"params\",begin:\"\\\\(\",endsWithParent:!0,contains:a}].concat(a)},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:\"string\",begin:t,end:n,contains:[r],relevance:5}])}},makefile:function(e){const t={className:\"variable\",variants:[{begin:\"\\\\$\\\\(\"+e.UNDERSCORE_IDENT_RE+\"\\\\)\",contains:[e.BACKSLASH_ESCAPE]},{begin:/\\$[@%<?\\^\\+\\*]/}]},n={className:\"string\",begin:/\"/,end:/\"/,contains:[e.BACKSLASH_ESCAPE,t]},r={className:\"variable\",begin:/\\$\\([\\w-]+\\s/,end:/\\)/,keywords:{built_in:\"subst patsubst strip findstring filter filter-out sort word wordlist firstword lastword dir notdir suffix basename addsuffix addprefix join wildcard realpath abspath error warning shell origin flavor foreach if or and call eval file value\"},contains:[t,n]},a={begin:\"^\"+e.UNDERSCORE_IDENT_RE+\"\\\\s*(?=[:+?]?=)\"},i={className:\"section\",begin:/^[^\\s]+:/,end:/$/,contains:[t]};return{name:\"Makefile\",aliases:[\"mk\",\"mak\",\"make\"],keywords:{$pattern:/[\\w-]+/,keyword:\"define endef undefine ifdef ifndef ifeq ifneq else endif include -include sinclude override export unexport private vpath\"},contains:[e.HASH_COMMENT_MODE,t,n,r,a,{className:\"meta\",begin:/^\\.PHONY:/,end:/$/,keywords:{$pattern:/[\\.\\w]+/,keyword:\".PHONY\"}},i]}},markdown:function(e){const t={begin:/<\\/?[A-Za-z_]/,end:\">\",subLanguage:\"xml\",relevance:0},n={variants:[{begin:/\\[.+?\\]\\[.*?\\]/,relevance:0},{begin:/\\[.+?\\]\\(((data|javascript|mailto):|(?:http|ftp)s?:\\/\\/).*?\\)/,relevance:2},{begin:e.regex.concat(/\\[.+?\\]\\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\\/\\/.*?\\)/),relevance:2},{begin:/\\[.+?\\]\\([./?&#].*?\\)/,relevance:1},{begin:/\\[.*?\\]\\(.*?\\)/,relevance:0}],returnBegin:!0,contains:[{match:/\\[(?=\\])/},{className:\"string\",relevance:0,begin:\"\\\\[\",end:\"\\\\]\",excludeBegin:!0,returnEnd:!0},{className:\"link\",relevance:0,begin:\"\\\\]\\\\(\",end:\"\\\\)\",excludeBegin:!0,excludeEnd:!0},{className:\"symbol\",relevance:0,begin:\"\\\\]\\\\[\",end:\"\\\\]\",excludeBegin:!0,excludeEnd:!0}]},r={className:\"strong\",contains:[],variants:[{begin:/_{2}(?!\\s)/,end:/_{2}/},{begin:/\\*{2}(?!\\s)/,end:/\\*{2}/}]},a={className:\"emphasis\",contains:[],variants:[{begin:/\\*(?![*\\s])/,end:/\\*/},{begin:/_(?![_\\s])/,end:/_/,relevance:0}]},i=e.inherit(r,{contains:[]}),s=e.inherit(a,{contains:[]});r.contains.push(s),a.contains.push(i);let o=[t,n];return[r,a,i,s].forEach(e=>{e.contains=e.contains.concat(o)}),o=o.concat(r,a),{name:\"Markdown\",aliases:[\"md\",\"mkdown\",\"mkd\"],contains:[{className:\"section\",variants:[{begin:\"^#{1,6}\",end:\"$\",contains:o},{begin:\"(?=^.+?\\\\n[=-]{2,}$)\",contains:[{begin:\"^[=-]*$\"},{begin:\"^\",end:\"\\\\n\",contains:o}]}]},t,{className:\"bullet\",begin:\"^[ \\t]*([*+-]|(\\\\d+\\\\.))(?=\\\\s+)\",end:\"\\\\s+\",excludeEnd:!0},r,a,{className:\"quote\",begin:\"^>\\\\s+\",contains:o,end:\"$\"},{className:\"code\",variants:[{begin:\"(`{3,})[^`](.|\\\\n)*?\\\\1`*[ ]*\"},{begin:\"(~{3,})[^~](.|\\\\n)*?\\\\1~*[ ]*\"},{begin:\"```\",end:\"```+[ ]*$\"},{begin:\"~~~\",end:\"~~~+[ ]*$\"},{begin:\"`.+?`\"},{begin:\"(?=^( {4}|\\\\t))\",contains:[{begin:\"^( {4}|\\\\t)\",end:\"(\\\\n)$\"}],relevance:0}]},{begin:\"^[-\\\\*]{3,}\",end:\"$\"},n,{begin:/^\\[[^\\n]+\\]:/,returnBegin:!0,contains:[{className:\"symbol\",begin:/\\[/,end:/\\]/,excludeBegin:!0,excludeEnd:!0},{className:\"link\",begin:/:\\s*/,end:/$/,excludeBegin:!0}]},{scope:\"literal\",match:/&([a-zA-Z0-9]+|#[0-9]{1,7}|#[Xx][0-9a-fA-F]{1,6});/}]}},objectivec:function(e){const t=/[a-zA-Z@][a-zA-Z0-9_]*/,n={$pattern:t,keyword:[\"@interface\",\"@class\",\"@protocol\",\"@implementation\"]};return{name:\"Objective-C\",aliases:[\"mm\",\"objc\",\"obj-c\",\"obj-c++\",\"objective-c++\"],keywords:{\"variable.language\":[\"this\",\"super\"],$pattern:t,keyword:[\"while\",\"export\",\"sizeof\",\"typedef\",\"const\",\"struct\",\"for\",\"union\",\"volatile\",\"static\",\"mutable\",\"if\",\"do\",\"return\",\"goto\",\"enum\",\"else\",\"break\",\"extern\",\"asm\",\"case\",\"default\",\"register\",\"explicit\",\"typename\",\"switch\",\"continue\",\"inline\",\"readonly\",\"assign\",\"readwrite\",\"self\",\"@synchronized\",\"id\",\"typeof\",\"nonatomic\",\"IBOutlet\",\"IBAction\",\"strong\",\"weak\",\"copy\",\"in\",\"out\",\"inout\",\"bycopy\",\"byref\",\"oneway\",\"__strong\",\"__weak\",\"__block\",\"__autoreleasing\",\"@private\",\"@protected\",\"@public\",\"@try\",\"@property\",\"@end\",\"@throw\",\"@catch\",\"@finally\",\"@autoreleasepool\",\"@synthesize\",\"@dynamic\",\"@selector\",\"@optional\",\"@required\",\"@encode\",\"@package\",\"@import\",\"@defs\",\"@compatibility_alias\",\"__bridge\",\"__bridge_transfer\",\"__bridge_retained\",\"__bridge_retain\",\"__covariant\",\"__contravariant\",\"__kindof\",\"_Nonnull\",\"_Nullable\",\"_Null_unspecified\",\"__FUNCTION__\",\"__PRETTY_FUNCTION__\",\"__attribute__\",\"getter\",\"setter\",\"retain\",\"unsafe_unretained\",\"nonnull\",\"nullable\",\"null_unspecified\",\"null_resettable\",\"class\",\"instancetype\",\"NS_DESIGNATED_INITIALIZER\",\"NS_UNAVAILABLE\",\"NS_REQUIRES_SUPER\",\"NS_RETURNS_INNER_POINTER\",\"NS_INLINE\",\"NS_AVAILABLE\",\"NS_DEPRECATED\",\"NS_ENUM\",\"NS_OPTIONS\",\"NS_SWIFT_UNAVAILABLE\",\"NS_ASSUME_NONNULL_BEGIN\",\"NS_ASSUME_NONNULL_END\",\"NS_REFINED_FOR_SWIFT\",\"NS_SWIFT_NAME\",\"NS_SWIFT_NOTHROW\",\"NS_DURING\",\"NS_HANDLER\",\"NS_ENDHANDLER\",\"NS_VALUERETURN\",\"NS_VOIDRETURN\"],literal:[\"false\",\"true\",\"FALSE\",\"TRUE\",\"nil\",\"YES\",\"NO\",\"NULL\"],built_in:[\"dispatch_once_t\",\"dispatch_queue_t\",\"dispatch_sync\",\"dispatch_async\",\"dispatch_once\"],type:[\"int\",\"float\",\"char\",\"unsigned\",\"signed\",\"short\",\"long\",\"double\",\"wchar_t\",\"unichar\",\"void\",\"bool\",\"BOOL\",\"id|0\",\"_Bool\"]},illegal:\"</\",contains:[{className:\"built_in\",begin:\"\\\\b(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)\\\\w+\"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.C_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{className:\"string\",variants:[{begin:'@\"',end:'\"',illegal:\"\\\\n\",contains:[e.BACKSLASH_ESCAPE]}]},{className:\"meta\",begin:/#\\s*[a-z]+\\b/,end:/$/,keywords:{keyword:\"if else elif endif define undef warning error line pragma ifdef ifndef include\"},contains:[{begin:/\\\\\\n/,relevance:0},e.inherit(e.QUOTE_STRING_MODE,{className:\"string\"}),{className:\"string\",begin:/<.*?>/,end:/$/,illegal:\"\\\\n\"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:\"class\",begin:\"(\"+n.keyword.join(\"|\")+\")\\\\b\",end:/(\\{|$)/,excludeEnd:!0,keywords:n,contains:[e.UNDERSCORE_TITLE_MODE]},{begin:\"\\\\.\"+e.UNDERSCORE_IDENT_RE,relevance:0}]}},perl:function(e){const t=e.regex,n=/[dualxmsipngr]{0,12}/,r={$pattern:/[\\w.]+/,keyword:[\"abs\",\"accept\",\"alarm\",\"and\",\"atan2\",\"bind\",\"binmode\",\"bless\",\"break\",\"caller\",\"chdir\",\"chmod\",\"chomp\",\"chop\",\"chown\",\"chr\",\"chroot\",\"class\",\"close\",\"closedir\",\"connect\",\"continue\",\"cos\",\"crypt\",\"dbmclose\",\"dbmopen\",\"defined\",\"delete\",\"die\",\"do\",\"dump\",\"each\",\"else\",\"elsif\",\"endgrent\",\"endhostent\",\"endnetent\",\"endprotoent\",\"endpwent\",\"endservent\",\"eof\",\"eval\",\"exec\",\"exists\",\"exit\",\"exp\",\"fcntl\",\"field\",\"fileno\",\"flock\",\"for\",\"foreach\",\"fork\",\"format\",\"formline\",\"getc\",\"getgrent\",\"getgrgid\",\"getgrnam\",\"gethostbyaddr\",\"gethostbyname\",\"gethostent\",\"getlogin\",\"getnetbyaddr\",\"getnetbyname\",\"getnetent\",\"getpeername\",\"getpgrp\",\"getpriority\",\"getprotobyname\",\"getprotobynumber\",\"getprotoent\",\"getpwent\",\"getpwnam\",\"getpwuid\",\"getservbyname\",\"getservbyport\",\"getservent\",\"getsockname\",\"getsockopt\",\"given\",\"glob\",\"gmtime\",\"goto\",\"grep\",\"gt\",\"hex\",\"if\",\"index\",\"int\",\"ioctl\",\"join\",\"keys\",\"kill\",\"last\",\"lc\",\"lcfirst\",\"length\",\"link\",\"listen\",\"local\",\"localtime\",\"log\",\"lstat\",\"lt\",\"ma\",\"map\",\"method\",\"mkdir\",\"msgctl\",\"msgget\",\"msgrcv\",\"msgsnd\",\"my\",\"ne\",\"next\",\"no\",\"not\",\"oct\",\"open\",\"opendir\",\"or\",\"ord\",\"our\",\"pack\",\"package\",\"pipe\",\"pop\",\"pos\",\"print\",\"printf\",\"prototype\",\"push\",\"q|0\",\"qq\",\"quotemeta\",\"qw\",\"qx\",\"rand\",\"read\",\"readdir\",\"readline\",\"readlink\",\"readpipe\",\"recv\",\"redo\",\"ref\",\"rename\",\"require\",\"reset\",\"return\",\"reverse\",\"rewinddir\",\"rindex\",\"rmdir\",\"say\",\"scalar\",\"seek\",\"seekdir\",\"select\",\"semctl\",\"semget\",\"semop\",\"send\",\"setgrent\",\"sethostent\",\"setnetent\",\"setpgrp\",\"setpriority\",\"setprotoent\",\"setpwent\",\"setservent\",\"setsockopt\",\"shift\",\"shmctl\",\"shmget\",\"shmread\",\"shmwrite\",\"shutdown\",\"sin\",\"sleep\",\"socket\",\"socketpair\",\"sort\",\"splice\",\"split\",\"sprintf\",\"sqrt\",\"srand\",\"stat\",\"state\",\"study\",\"sub\",\"substr\",\"symlink\",\"syscall\",\"sysopen\",\"sysread\",\"sysseek\",\"system\",\"syswrite\",\"tell\",\"telldir\",\"tie\",\"tied\",\"time\",\"times\",\"tr\",\"truncate\",\"uc\",\"ucfirst\",\"umask\",\"undef\",\"unless\",\"unlink\",\"unpack\",\"unshift\",\"untie\",\"until\",\"use\",\"utime\",\"values\",\"vec\",\"wait\",\"waitpid\",\"wantarray\",\"warn\",\"when\",\"while\",\"write\",\"x|0\",\"xor\",\"y|0\"].join(\" \")},a={className:\"subst\",begin:\"[$@]\\\\{\",end:\"\\\\}\",keywords:r},i={begin:/->\\{/,end:/\\}/},s={scope:\"attr\",match:/\\s+:\\s*\\w+(\\s*\\(.*?\\))?/},o={scope:\"variable\",variants:[{begin:/\\$\\d/},{begin:t.concat(/[$%@](?!\")(\\^\\w\\b|#\\w+(::\\w+)*|\\{\\w+\\}|\\w+(::\\w*)*)/,\"(?![A-Za-z])(?![@$%])\")},{begin:/[$%@](?!\")[^\\s\\w{=]|\\$=/,relevance:0}],contains:[s]},l={className:\"number\",variants:[{match:/0?\\.[0-9][0-9_]+\\b/},{match:/\\bv?(0|[1-9][0-9_]*(\\.[0-9_]+)?|[1-9][0-9_]*)\\b/},{match:/\\b0[0-7][0-7_]*\\b/},{match:/\\b0x[0-9a-fA-F][0-9a-fA-F_]*\\b/},{match:/\\b0b[0-1][0-1_]*\\b/}],relevance:0},c=[e.BACKSLASH_ESCAPE,a,o],u=[/!/,/\\//,/\\|/,/\\?/,/'/,/\"/,/#/],d=(e,r,a=\"\\\\1\")=>{const i=\"\\\\1\"===a?a:t.concat(a,r);return t.concat(t.concat(\"(?:\",e,\")\"),r,/(?:\\\\.|[^\\\\\\/])*?/,i,/(?:\\\\.|[^\\\\\\/])*?/,a,n)},p=(e,r,a)=>t.concat(t.concat(\"(?:\",e,\")\"),r,/(?:\\\\.|[^\\\\\\/])*?/,a,n),h=[o,e.HASH_COMMENT_MODE,e.COMMENT(/^=\\w/,/=cut/,{endsWithParent:!0}),i,{className:\"string\",contains:c,variants:[{begin:\"q[qwxr]?\\\\s*\\\\(\",end:\"\\\\)\",relevance:5},{begin:\"q[qwxr]?\\\\s*\\\\[\",end:\"\\\\]\",relevance:5},{begin:\"q[qwxr]?\\\\s*\\\\{\",end:\"\\\\}\",relevance:5},{begin:\"q[qwxr]?\\\\s*\\\\|\",end:\"\\\\|\",relevance:5},{begin:\"q[qwxr]?\\\\s*<\",end:\">\",relevance:5},{begin:\"qw\\\\s+q\",end:\"q\",relevance:5},{begin:\"'\",end:\"'\",contains:[e.BACKSLASH_ESCAPE]},{begin:'\"',end:'\"'},{begin:\"`\",end:\"`\",contains:[e.BACKSLASH_ESCAPE]},{begin:/\\{\\w+\\}/,relevance:0},{begin:\"-?\\\\w+\\\\s*=>\",relevance:0}]},l,{begin:\"(\\\\/\\\\/|\"+e.RE_STARTERS_RE+\"|\\\\b(split|return|print|reverse|grep)\\\\b)\\\\s*\",keywords:\"split return print reverse grep\",relevance:0,contains:[e.HASH_COMMENT_MODE,{className:\"regexp\",variants:[{begin:d(\"s|tr|y\",t.either(...u,{capture:!0}))},{begin:d(\"s|tr|y\",\"\\\\(\",\"\\\\)\")},{begin:d(\"s|tr|y\",\"\\\\[\",\"\\\\]\")},{begin:d(\"s|tr|y\",\"\\\\{\",\"\\\\}\")}],relevance:2},{className:\"regexp\",variants:[{begin:/(m|qr)\\/\\//,relevance:0},{begin:p(\"(?:m|qr)?\",/\\//,/\\//)},{begin:p(\"m|qr\",t.either(...u,{capture:!0}),/\\1/)},{begin:p(\"m|qr\",/\\(/,/\\)/)},{begin:p(\"m|qr\",/\\[/,/\\]/)},{begin:p(\"m|qr\",/\\{/,/\\}/)}]}]},{className:\"function\",beginKeywords:\"sub method\",end:\"(\\\\s*\\\\(.*?\\\\))?[;{]\",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE,s]},{className:\"class\",beginKeywords:\"class\",end:\"[;{]\",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE,s,l]},{begin:\"-\\\\w\\\\b\",relevance:0},{begin:\"^__DATA__$\",end:\"^__END__$\",subLanguage:\"mojolicious\",contains:[{begin:\"^@@.*\",end:\"$\",className:\"comment\"}]}];return a.contains=h,i.contains=h,{name:\"Perl\",aliases:[\"pl\",\"pm\"],keywords:r,contains:h}},php:function(e){const t=e.regex,n=/(?![A-Za-z0-9])(?![$])/,r=t.concat(/[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*/,n),a=t.concat(/(\\\\?[A-Z][a-z0-9_\\x7f-\\xff]+|\\\\?[A-Z]+(?=[A-Z][a-z0-9_\\x7f-\\xff])){1,}/,n),i=t.concat(/[A-Z]+/,n),s={scope:\"variable\",match:\"\\\\$+\"+r},o={scope:\"subst\",variants:[{begin:/\\$\\w+/},{begin:/\\{\\$/,end:/\\}/}]},l=e.inherit(e.APOS_STRING_MODE,{illegal:null}),c=\"[ \\t\\n]\",u={scope:\"string\",variants:[e.inherit(e.QUOTE_STRING_MODE,{illegal:null,contains:e.QUOTE_STRING_MODE.contains.concat(o)}),l,{begin:/<<<[ \\t]*(?:(\\w+)|\"(\\w+)\")\\n/,end:/[ \\t]*(\\w+)\\b/,contains:e.QUOTE_STRING_MODE.contains.concat(o),\"on:begin\":(e,t)=>{t.data._beginMatch=e[1]||e[2]},\"on:end\":(e,t)=>{t.data._beginMatch!==e[1]&&t.ignoreMatch()}},e.END_SAME_AS_BEGIN({begin:/<<<[ \\t]*'(\\w+)'\\n/,end:/[ \\t]*(\\w+)\\b/})]},d={scope:\"number\",variants:[{begin:\"\\\\b0[bB][01]+(?:_[01]+)*\\\\b\"},{begin:\"\\\\b0[oO][0-7]+(?:_[0-7]+)*\\\\b\"},{begin:\"\\\\b0[xX][\\\\da-fA-F]+(?:_[\\\\da-fA-F]+)*\\\\b\"},{begin:\"(?:\\\\b\\\\d+(?:_\\\\d+)*(\\\\.(?:\\\\d+(?:_\\\\d+)*))?|\\\\B\\\\.\\\\d+)(?:[eE][+-]?\\\\d+)?\"}],relevance:0},p=[\"false\",\"null\",\"true\"],h=[\"__CLASS__\",\"__DIR__\",\"__FILE__\",\"__FUNCTION__\",\"__COMPILER_HALT_OFFSET__\",\"__LINE__\",\"__METHOD__\",\"__NAMESPACE__\",\"__TRAIT__\",\"die\",\"echo\",\"exit\",\"include\",\"include_once\",\"print\",\"require\",\"require_once\",\"array\",\"abstract\",\"and\",\"as\",\"binary\",\"bool\",\"boolean\",\"break\",\"callable\",\"case\",\"catch\",\"class\",\"clone\",\"const\",\"continue\",\"declare\",\"default\",\"do\",\"double\",\"else\",\"elseif\",\"empty\",\"enddeclare\",\"endfor\",\"endforeach\",\"endif\",\"endswitch\",\"endwhile\",\"enum\",\"eval\",\"extends\",\"final\",\"finally\",\"float\",\"for\",\"foreach\",\"from\",\"global\",\"goto\",\"if\",\"implements\",\"instanceof\",\"insteadof\",\"int\",\"integer\",\"interface\",\"isset\",\"iterable\",\"list\",\"match|0\",\"mixed\",\"new\",\"never\",\"object\",\"or\",\"private\",\"protected\",\"public\",\"readonly\",\"real\",\"return\",\"string\",\"switch\",\"throw\",\"trait\",\"try\",\"unset\",\"use\",\"var\",\"void\",\"while\",\"xor\",\"yield\"],f=[\"Error|0\",\"AppendIterator\",\"ArgumentCountError\",\"ArithmeticError\",\"ArrayIterator\",\"ArrayObject\",\"AssertionError\",\"BadFunctionCallException\",\"BadMethodCallException\",\"CachingIterator\",\"CallbackFilterIterator\",\"CompileError\",\"Countable\",\"DirectoryIterator\",\"DivisionByZeroError\",\"DomainException\",\"EmptyIterator\",\"ErrorException\",\"Exception\",\"FilesystemIterator\",\"FilterIterator\",\"GlobIterator\",\"InfiniteIterator\",\"InvalidArgumentException\",\"IteratorIterator\",\"LengthException\",\"LimitIterator\",\"LogicException\",\"MultipleIterator\",\"NoRewindIterator\",\"OutOfBoundsException\",\"OutOfRangeException\",\"OuterIterator\",\"OverflowException\",\"ParentIterator\",\"ParseError\",\"RangeException\",\"RecursiveArrayIterator\",\"RecursiveCachingIterator\",\"RecursiveCallbackFilterIterator\",\"RecursiveDirectoryIterator\",\"RecursiveFilterIterator\",\"RecursiveIterator\",\"RecursiveIteratorIterator\",\"RecursiveRegexIterator\",\"RecursiveTreeIterator\",\"RegexIterator\",\"RuntimeException\",\"SeekableIterator\",\"SplDoublyLinkedList\",\"SplFileInfo\",\"SplFileObject\",\"SplFixedArray\",\"SplHeap\",\"SplMaxHeap\",\"SplMinHeap\",\"SplObjectStorage\",\"SplObserver\",\"SplPriorityQueue\",\"SplQueue\",\"SplStack\",\"SplSubject\",\"SplTempFileObject\",\"TypeError\",\"UnderflowException\",\"UnexpectedValueException\",\"UnhandledMatchError\",\"ArrayAccess\",\"BackedEnum\",\"Closure\",\"Fiber\",\"Generator\",\"Iterator\",\"IteratorAggregate\",\"Serializable\",\"Stringable\",\"Throwable\",\"Traversable\",\"UnitEnum\",\"WeakReference\",\"WeakMap\",\"Directory\",\"__PHP_Incomplete_Class\",\"parent\",\"php_user_filter\",\"self\",\"static\",\"stdClass\"],m={keyword:h,literal:(e=>{const t=[];return e.forEach(e=>{t.push(e),e.toLowerCase()===e?t.push(e.toUpperCase()):t.push(e.toLowerCase())}),t})(p),built_in:f},g=e=>e.map(e=>e.replace(/\\|\\d+$/,\"\")),b={variants:[{match:[/new/,t.concat(c,\"+\"),t.concat(\"(?!\",g(f).join(\"\\\\b|\"),\"\\\\b)\"),a],scope:{1:\"keyword\",4:\"title.class\"}}]},E=t.concat(r,\"\\\\b(?!\\\\()\"),y={variants:[{match:[t.concat(/::/,t.lookahead(/(?!class\\b)/)),E],scope:{2:\"variable.constant\"}},{match:[/::/,/class/],scope:{2:\"variable.language\"}},{match:[a,t.concat(/::/,t.lookahead(/(?!class\\b)/)),E],scope:{1:\"title.class\",3:\"variable.constant\"}},{match:[a,t.concat(\"::\",t.lookahead(/(?!class\\b)/))],scope:{1:\"title.class\"}},{match:[a,/::/,/class/],scope:{1:\"title.class\",3:\"variable.language\"}}]},_={scope:\"attr\",match:t.concat(r,t.lookahead(\":\"),t.lookahead(/(?!::)/))},k={relevance:0,begin:/\\(/,end:/\\)/,keywords:m,contains:[_,s,y,e.C_BLOCK_COMMENT_MODE,u,d,b]},T={relevance:0,match:[/\\b/,t.concat(\"(?!fn\\\\b|function\\\\b|\",g(h).join(\"\\\\b|\"),\"|\",g(f).join(\"\\\\b|\"),\"\\\\b)\"),r,t.concat(c,\"*\"),t.lookahead(/(?=\\()/)],scope:{3:\"title.function.invoke\"},contains:[k]};k.contains.push(T);const S=[_,y,e.C_BLOCK_COMMENT_MODE,u,d,b],v={begin:t.concat(/#\\[\\s*\\\\?/,t.either(a,i)),beginScope:\"meta\",end:/]/,endScope:\"meta\",keywords:{literal:p,keyword:[\"new\",\"array\"]},contains:[{begin:/\\[/,end:/]/,keywords:{literal:p,keyword:[\"new\",\"array\"]},contains:[\"self\",...S]},...S,{scope:\"meta\",variants:[{match:a},{match:i}]}]};return{case_insensitive:!1,keywords:m,contains:[v,e.HASH_COMMENT_MODE,e.COMMENT(\"//\",\"$\"),e.COMMENT(\"/\\\\*\",\"\\\\*/\",{contains:[{scope:\"doctag\",match:\"@[A-Za-z]+\"}]}),{match:/__halt_compiler\\(\\);/,keywords:\"__halt_compiler\",starts:{scope:\"comment\",end:e.MATCH_NOTHING_RE,contains:[{match:/\\?>/,scope:\"meta\",endsParent:!0}]}},{scope:\"meta\",variants:[{begin:/<\\?php/,relevance:10},{begin:/<\\?=/},{begin:/<\\?/,relevance:.1},{begin:/\\?>/}]},{scope:\"variable.language\",match:/\\$this\\b/},s,T,y,{match:[/const/,/\\s/,r],scope:{1:\"keyword\",3:\"variable.constant\"}},b,{scope:\"function\",relevance:0,beginKeywords:\"fn function\",end:/[;{]/,excludeEnd:!0,illegal:\"[$%\\\\[]\",contains:[{beginKeywords:\"use\"},e.UNDERSCORE_TITLE_MODE,{begin:\"=>\",endsParent:!0},{scope:\"params\",begin:\"\\\\(\",end:\"\\\\)\",excludeBegin:!0,excludeEnd:!0,keywords:m,contains:[\"self\",v,s,y,e.C_BLOCK_COMMENT_MODE,u,d]}]},{scope:\"class\",variants:[{beginKeywords:\"enum\",illegal:/[($\"]/},{beginKeywords:\"class interface trait\",illegal:/[:($\"]/}],relevance:0,end:/\\{/,excludeEnd:!0,contains:[{beginKeywords:\"extends implements\"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:\"namespace\",relevance:0,end:\";\",illegal:/[.']/,contains:[e.inherit(e.UNDERSCORE_TITLE_MODE,{scope:\"title.class\"})]},{beginKeywords:\"use\",relevance:0,end:\";\",contains:[{match:/\\b(as|const|function)\\b/,scope:\"keyword\"},e.UNDERSCORE_TITLE_MODE]},u,d]}},\"php-template\":function(e){return{name:\"PHP template\",subLanguage:\"xml\",contains:[{begin:/<\\?(php|=)?/,end:/\\?>/,subLanguage:\"php\",contains:[{begin:\"/\\\\*\",end:\"\\\\*/\",skip:!0},{begin:'b\"',end:'\"',skip:!0},{begin:\"b'\",end:\"'\",skip:!0},e.inherit(e.APOS_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0})]}]}},plaintext:function(e){return{name:\"Plain text\",aliases:[\"text\",\"txt\"],disableAutodetect:!0}},python:function(e){const t=e.regex,n=new RegExp(\"[\\\\p{XID_Start}_]\\\\p{XID_Continue}*\",\"u\"),r=[\"and\",\"as\",\"assert\",\"async\",\"await\",\"break\",\"case\",\"class\",\"continue\",\"def\",\"del\",\"elif\",\"else\",\"except\",\"finally\",\"for\",\"from\",\"global\",\"if\",\"import\",\"in\",\"is\",\"lambda\",\"match\",\"nonlocal|10\",\"not\",\"or\",\"pass\",\"raise\",\"return\",\"try\",\"while\",\"with\",\"yield\"],a={$pattern:/[A-Za-z]\\w+|__\\w+__/,keyword:r,built_in:[\"__import__\",\"abs\",\"all\",\"any\",\"ascii\",\"bin\",\"bool\",\"breakpoint\",\"bytearray\",\"bytes\",\"callable\",\"chr\",\"classmethod\",\"compile\",\"complex\",\"delattr\",\"dict\",\"dir\",\"divmod\",\"enumerate\",\"eval\",\"exec\",\"filter\",\"float\",\"format\",\"frozenset\",\"getattr\",\"globals\",\"hasattr\",\"hash\",\"help\",\"hex\",\"id\",\"input\",\"int\",\"isinstance\",\"issubclass\",\"iter\",\"len\",\"list\",\"locals\",\"map\",\"max\",\"memoryview\",\"min\",\"next\",\"object\",\"oct\",\"open\",\"ord\",\"pow\",\"print\",\"property\",\"range\",\"repr\",\"reversed\",\"round\",\"set\",\"setattr\",\"slice\",\"sorted\",\"staticmethod\",\"str\",\"sum\",\"super\",\"tuple\",\"type\",\"vars\",\"zip\"],literal:[\"__debug__\",\"Ellipsis\",\"False\",\"None\",\"NotImplemented\",\"True\"],type:[\"Any\",\"Callable\",\"Coroutine\",\"Dict\",\"List\",\"Literal\",\"Generic\",\"Optional\",\"Sequence\",\"Set\",\"Tuple\",\"Type\",\"Union\"]},i={className:\"meta\",begin:/^(>>>|\\.\\.\\.) /},s={className:\"subst\",begin:/\\{/,end:/\\}/,keywords:a,illegal:/#/},o={begin:/\\{\\{/,relevance:0},l={className:\"string\",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,i],relevance:10},{begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?\"\"\"/,end:/\"\"\"/,contains:[e.BACKSLASH_ESCAPE,i],relevance:10},{begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,i,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])\"\"\"/,end:/\"\"\"/,contains:[e.BACKSLASH_ESCAPE,i,o,s]},{begin:/([uU]|[rR])'/,end:/'/,relevance:10},{begin:/([uU]|[rR])\"/,end:/\"/,relevance:10},{begin:/([bB]|[bB][rR]|[rR][bB])'/,end:/'/},{begin:/([bB]|[bB][rR]|[rR][bB])\"/,end:/\"/},{begin:/([fF][rR]|[rR][fF]|[fF])'/,end:/'/,contains:[e.BACKSLASH_ESCAPE,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])\"/,end:/\"/,contains:[e.BACKSLASH_ESCAPE,o,s]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},c=\"[0-9](_?[0-9])*\",u=`(\\\\b(${c}))?\\\\.(${c})|\\\\b(${c})\\\\.`,d=`\\\\b|${r.join(\"|\")}`,p={className:\"number\",relevance:0,variants:[{begin:`(\\\\b(${c})|(${u}))[eE][+-]?(${c})[jJ]?(?=${d})`},{begin:`(${u})[jJ]?`},{begin:`\\\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?(?=${d})`},{begin:`\\\\b0[bB](_?[01])+[lL]?(?=${d})`},{begin:`\\\\b0[oO](_?[0-7])+[lL]?(?=${d})`},{begin:`\\\\b0[xX](_?[0-9a-fA-F])+[lL]?(?=${d})`},{begin:`\\\\b(${c})[jJ](?=${d})`}]},h={className:\"comment\",begin:t.lookahead(/# type:/),end:/$/,keywords:a,contains:[{begin:/# type:/},{begin:/#/,end:/\\b\\B/,endsWithParent:!0}]},f={className:\"params\",variants:[{className:\"\",begin:/\\(\\s*\\)/,skip:!0},{begin:/\\(/,end:/\\)/,excludeBegin:!0,excludeEnd:!0,keywords:a,contains:[\"self\",i,p,l,e.HASH_COMMENT_MODE]}]};return s.contains=[l,p,i],{name:\"Python\",aliases:[\"py\",\"gyp\",\"ipython\"],unicodeRegex:!0,keywords:a,illegal:/(<\\/|\\?)|=>/,contains:[i,p,{scope:\"variable.language\",match:/\\bself\\b/},{beginKeywords:\"if\",relevance:0},{match:/\\bor\\b/,scope:\"keyword\"},l,h,e.HASH_COMMENT_MODE,{match:[/\\bdef/,/\\s+/,n],scope:{1:\"keyword\",3:\"title.function\"},contains:[f]},{variants:[{match:[/\\bclass/,/\\s+/,n,/\\s*/,/\\(\\s*/,n,/\\s*\\)/]},{match:[/\\bclass/,/\\s+/,n]}],scope:{1:\"keyword\",3:\"title.class\",6:\"title.class.inherited\"}},{className:\"meta\",begin:/^[\\t ]*@/,end:/(?=#)|$/,contains:[p,f,l]}]}},\"python-repl\":function(e){return{aliases:[\"pycon\"],contains:[{className:\"meta.prompt\",starts:{end:/ |$/,starts:{end:\"$\",subLanguage:\"python\"}},variants:[{begin:/^>>>(?=[ ]|$)/},{begin:/^\\.\\.\\.(?=[ ]|$)/}]}]}},r:function(e){const t=e.regex,n=/(?:(?:[a-zA-Z]|\\.[._a-zA-Z])[._a-zA-Z0-9]*)|\\.(?!\\d)/,r=t.either(/0[xX][0-9a-fA-F]+\\.[0-9a-fA-F]*[pP][+-]?\\d+i?/,/0[xX][0-9a-fA-F]+(?:[pP][+-]?\\d+)?[Li]?/,/(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?[Li]?/),a=/[=!<>:]=|\\|\\||&&|:::?|<-|<<-|->>|->|\\|>|[-+*\\/?!$&|:<=>@^~]|\\*\\*/,i=t.either(/[()]/,/[{}]/,/\\[\\[/,/[[\\]]/,/\\\\/,/,/);return{name:\"R\",keywords:{$pattern:n,keyword:\"function if in break next repeat else for while\",literal:\"NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10\",built_in:\"LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum switch tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm\"},contains:[e.COMMENT(/#'/,/$/,{contains:[{scope:\"doctag\",match:/@examples/,starts:{end:t.lookahead(t.either(/\\n^#'\\s*(?=@[a-zA-Z]+)/,/\\n^(?!#')/)),endsParent:!0}},{scope:\"doctag\",begin:\"@param\",end:/$/,contains:[{scope:\"variable\",variants:[{match:n},{match:/`(?:\\\\.|[^`\\\\])+`/}],endsParent:!0}]},{scope:\"doctag\",match:/@[a-zA-Z]+/},{scope:\"keyword\",match:/\\\\[a-zA-Z]+/}]}),e.HASH_COMMENT_MODE,{scope:\"string\",contains:[e.BACKSLASH_ESCAPE],variants:[e.END_SAME_AS_BEGIN({begin:/[rR]\"(-*)\\(/,end:/\\)(-*)\"/}),e.END_SAME_AS_BEGIN({begin:/[rR]\"(-*)\\{/,end:/\\}(-*)\"/}),e.END_SAME_AS_BEGIN({begin:/[rR]\"(-*)\\[/,end:/\\](-*)\"/}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\\(/,end:/\\)(-*)'/}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\\{/,end:/\\}(-*)'/}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\\[/,end:/\\](-*)'/}),{begin:'\"',end:'\"',relevance:0},{begin:\"'\",end:\"'\",relevance:0}]},{relevance:0,variants:[{scope:{1:\"operator\",2:\"number\"},match:[a,r]},{scope:{1:\"operator\",2:\"number\"},match:[/%[^%]*%/,r]},{scope:{1:\"punctuation\",2:\"number\"},match:[i,r]},{scope:{2:\"number\"},match:[/[^a-zA-Z0-9._]|^/,r]}]},{scope:{3:\"operator\"},match:[n,/\\s+/,/<-/,/\\s+/]},{scope:\"operator\",relevance:0,variants:[{match:a},{match:/%[^%]*%/}]},{scope:\"punctuation\",relevance:0,match:i},{begin:\"`\",end:\"`\",contains:[{begin:/\\\\./}]}]}},ruby:function(e){const t=e.regex,n=\"([a-zA-Z_]\\\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\\\*\\\\*|[-/+%^&*~`|]|\\\\[\\\\]=?)\",r=t.either(/\\b([A-Z]+[a-z0-9]+)+/,/\\b([A-Z]+[a-z0-9]+)+[A-Z]+/),a=t.concat(r,/(::\\w+)*/),i={\"variable.constant\":[\"__FILE__\",\"__LINE__\",\"__ENCODING__\"],\"variable.language\":[\"self\",\"super\"],keyword:[\"alias\",\"and\",\"begin\",\"BEGIN\",\"break\",\"case\",\"class\",\"defined\",\"do\",\"else\",\"elsif\",\"end\",\"END\",\"ensure\",\"for\",\"if\",\"in\",\"module\",\"next\",\"not\",\"or\",\"redo\",\"require\",\"rescue\",\"retry\",\"return\",\"then\",\"undef\",\"unless\",\"until\",\"when\",\"while\",\"yield\",\"include\",\"extend\",\"prepend\",\"public\",\"private\",\"protected\",\"raise\",\"throw\"],built_in:[\"proc\",\"lambda\",\"attr_accessor\",\"attr_reader\",\"attr_writer\",\"define_method\",\"private_constant\",\"module_function\"],literal:[\"true\",\"false\",\"nil\"]},s={className:\"doctag\",begin:\"@[A-Za-z]+\"},o={begin:\"#<\",end:\">\"},l=[e.COMMENT(\"#\",\"$\",{contains:[s]}),e.COMMENT(\"^=begin\",\"^=end\",{contains:[s],relevance:10}),e.COMMENT(\"^__END__\",e.MATCH_NOTHING_RE)],c={className:\"subst\",begin:/#\\{/,end:/\\}/,keywords:i},u={className:\"string\",contains:[e.BACKSLASH_ESCAPE,c],variants:[{begin:/'/,end:/'/},{begin:/\"/,end:/\"/},{begin:/`/,end:/`/},{begin:/%[qQwWx]?\\(/,end:/\\)/},{begin:/%[qQwWx]?\\[/,end:/\\]/},{begin:/%[qQwWx]?\\{/,end:/\\}/},{begin:/%[qQwWx]?</,end:/>/},{begin:/%[qQwWx]?\\//,end:/\\//},{begin:/%[qQwWx]?%/,end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{begin:/%[qQwWx]?\\|/,end:/\\|/},{begin:/\\B\\?(\\\\\\d{1,3})/},{begin:/\\B\\?(\\\\x[A-Fa-f0-9]{1,2})/},{begin:/\\B\\?(\\\\u\\{?[A-Fa-f0-9]{1,6}\\}?)/},{begin:/\\B\\?(\\\\M-\\\\C-|\\\\M-\\\\c|\\\\c\\\\M-|\\\\M-|\\\\C-\\\\M-)[\\x20-\\x7e]/},{begin:/\\B\\?\\\\(c|C-)[\\x20-\\x7e]/},{begin:/\\B\\?\\\\?\\S/},{begin:t.concat(/<<[-~]?'?/,t.lookahead(/(\\w+)(?=\\W)[^\\n]*\\n(?:[^\\n]*\\n)*?\\s*\\1\\b/)),contains:[e.END_SAME_AS_BEGIN({begin:/(\\w+)/,end:/(\\w+)/,contains:[e.BACKSLASH_ESCAPE,c]})]}]},d=\"[0-9](_?[0-9])*\",p={className:\"number\",relevance:0,variants:[{begin:`\\\\b([1-9](_?[0-9])*|0)(\\\\.(${d}))?([eE][+-]?(${d})|r)?i?\\\\b`},{begin:\"\\\\b0[dD][0-9](_?[0-9])*r?i?\\\\b\"},{begin:\"\\\\b0[bB][0-1](_?[0-1])*r?i?\\\\b\"},{begin:\"\\\\b0[oO][0-7](_?[0-7])*r?i?\\\\b\"},{begin:\"\\\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\\\b\"},{begin:\"\\\\b0(_?[0-7])+r?i?\\\\b\"}]},h={variants:[{match:/\\(\\)/},{className:\"params\",begin:/\\(/,end:/(?=\\))/,excludeBegin:!0,endsParent:!0,keywords:i}]},f=[u,{variants:[{match:[/class\\s+/,a,/\\s+<\\s+/,a]},{match:[/\\b(class|module)\\s+/,a]}],scope:{2:\"title.class\",4:\"title.class.inherited\"},keywords:i},{match:[/(include|extend)\\s+/,a],scope:{2:\"title.class\"},keywords:i},{relevance:0,match:[a,/\\.new[. (]/],scope:{1:\"title.class\"}},{relevance:0,match:/\\b[A-Z][A-Z_0-9]+\\b/,className:\"variable.constant\"},{relevance:0,match:r,scope:\"title.class\"},{match:[/def/,/\\s+/,n],scope:{1:\"keyword\",3:\"title.function\"},contains:[h]},{begin:e.IDENT_RE+\"::\"},{className:\"symbol\",begin:e.UNDERSCORE_IDENT_RE+\"(!|\\\\?)?:\",relevance:0},{className:\"symbol\",begin:\":(?!\\\\s)\",contains:[u,{begin:n}],relevance:0},p,{className:\"variable\",begin:\"(\\\\$\\\\W)|((\\\\$|@@?)(\\\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])\"},{className:\"params\",begin:/\\|(?!=)/,end:/\\|/,excludeBegin:!0,excludeEnd:!0,relevance:0,keywords:i},{begin:\"(\"+e.RE_STARTERS_RE+\"|unless)\\\\s*\",keywords:\"unless\",contains:[{className:\"regexp\",contains:[e.BACKSLASH_ESCAPE,c],illegal:/\\n/,variants:[{begin:\"/\",end:\"/[a-z]*\"},{begin:/%r\\{/,end:/\\}[a-z]*/},{begin:\"%r\\\\(\",end:\"\\\\)[a-z]*\"},{begin:\"%r!\",end:\"![a-z]*\"},{begin:\"%r\\\\[\",end:\"\\\\][a-z]*\"}]}].concat(o,l),relevance:0}].concat(o,l);c.contains=f,h.contains=f;const m=[{begin:/^\\s*=>/,starts:{end:\"$\",contains:f}},{className:\"meta.prompt\",begin:\"^([>?]>|[\\\\w#]+\\\\(\\\\w+\\\\):\\\\d+:\\\\d+[>*]|(\\\\w+-)?\\\\d+\\\\.\\\\d+\\\\.\\\\d+(p\\\\d+)?[^\\\\d][^>]+>)(?=[ ])\",starts:{end:\"$\",keywords:i,contains:f}}];return l.unshift(o),{name:\"Ruby\",aliases:[\"rb\",\"gemspec\",\"podspec\",\"thor\",\"irb\"],keywords:i,illegal:/\\/\\*/,contains:[e.SHEBANG({binary:\"ruby\"})].concat(m).concat(l).concat(f)}},rust:function(e){const t=e.regex,n=/(r#)?/,r=t.concat(n,e.UNDERSCORE_IDENT_RE),a=t.concat(n,e.IDENT_RE),i={className:\"title.function.invoke\",relevance:0,begin:t.concat(/\\b/,/(?!let|for|while|if|else|match\\b)/,a,t.lookahead(/\\s*\\(/))},s=\"([ui](8|16|32|64|128|size)|f(32|64))?\",o=[\"drop \",\"Copy\",\"Send\",\"Sized\",\"Sync\",\"Drop\",\"Fn\",\"FnMut\",\"FnOnce\",\"ToOwned\",\"Clone\",\"Debug\",\"PartialEq\",\"PartialOrd\",\"Eq\",\"Ord\",\"AsRef\",\"AsMut\",\"Into\",\"From\",\"Default\",\"Iterator\",\"Extend\",\"IntoIterator\",\"DoubleEndedIterator\",\"ExactSizeIterator\",\"SliceConcatExt\",\"ToString\",\"assert!\",\"assert_eq!\",\"bitflags!\",\"bytes!\",\"cfg!\",\"col!\",\"concat!\",\"concat_idents!\",\"debug_assert!\",\"debug_assert_eq!\",\"env!\",\"eprintln!\",\"panic!\",\"file!\",\"format!\",\"format_args!\",\"include_bytes!\",\"include_str!\",\"line!\",\"local_data_key!\",\"module_path!\",\"option_env!\",\"print!\",\"println!\",\"select!\",\"stringify!\",\"try!\",\"unimplemented!\",\"unreachable!\",\"vec!\",\"write!\",\"writeln!\",\"macro_rules!\",\"assert_ne!\",\"debug_assert_ne!\"],l=[\"i8\",\"i16\",\"i32\",\"i64\",\"i128\",\"isize\",\"u8\",\"u16\",\"u32\",\"u64\",\"u128\",\"usize\",\"f32\",\"f64\",\"str\",\"char\",\"bool\",\"Box\",\"Option\",\"Result\",\"String\",\"Vec\"];return{name:\"Rust\",aliases:[\"rs\"],keywords:{$pattern:e.IDENT_RE+\"!?\",type:l,keyword:[\"abstract\",\"as\",\"async\",\"await\",\"become\",\"box\",\"break\",\"const\",\"continue\",\"crate\",\"do\",\"dyn\",\"else\",\"enum\",\"extern\",\"false\",\"final\",\"fn\",\"for\",\"if\",\"impl\",\"in\",\"let\",\"loop\",\"macro\",\"match\",\"mod\",\"move\",\"mut\",\"override\",\"priv\",\"pub\",\"ref\",\"return\",\"self\",\"Self\",\"static\",\"struct\",\"super\",\"trait\",\"true\",\"try\",\"type\",\"typeof\",\"union\",\"unsafe\",\"unsized\",\"use\",\"virtual\",\"where\",\"while\",\"yield\"],literal:[\"true\",\"false\",\"Some\",\"None\",\"Ok\",\"Err\"],built_in:o},illegal:\"</\",contains:[e.C_LINE_COMMENT_MODE,e.COMMENT(\"/\\\\*\",\"\\\\*/\",{contains:[\"self\"]}),e.inherit(e.QUOTE_STRING_MODE,{begin:/b?\"/,illegal:null}),{className:\"symbol\",begin:/'[a-zA-Z_][a-zA-Z0-9_]*(?!')/},{scope:\"string\",variants:[{begin:/b?r(#*)\"(.|\\n)*?\"\\1(?!#)/},{begin:/b?'/,end:/'/,contains:[{scope:\"char.escape\",match:/\\\\('|\\w|x\\w{2}|u\\w{4}|U\\w{8})/}]}]},{className:\"number\",variants:[{begin:\"\\\\b0b([01_]+)\"+s},{begin:\"\\\\b0o([0-7_]+)\"+s},{begin:\"\\\\b0x([A-Fa-f0-9_]+)\"+s},{begin:\"\\\\b(\\\\d[\\\\d_]*(\\\\.[0-9_]+)?([eE][+-]?[0-9_]+)?)\"+s}],relevance:0},{begin:[/fn/,/\\s+/,r],className:{1:\"keyword\",3:\"title.function\"}},{className:\"meta\",begin:\"#!?\\\\[\",end:\"\\\\]\",contains:[{className:\"string\",begin:/\"/,end:/\"/,contains:[e.BACKSLASH_ESCAPE]}]},{begin:[/let/,/\\s+/,/(?:mut\\s+)?/,r],className:{1:\"keyword\",3:\"keyword\",4:\"variable\"}},{begin:[/for/,/\\s+/,r,/\\s+/,/in/],className:{1:\"keyword\",3:\"variable\",5:\"keyword\"}},{begin:[/type/,/\\s+/,r],className:{1:\"keyword\",3:\"title.class\"}},{begin:[/(?:trait|enum|struct|union|impl|for)/,/\\s+/,r],className:{1:\"keyword\",3:\"title.class\"}},{begin:e.IDENT_RE+\"::\",keywords:{keyword:\"Self\",built_in:o,type:l}},{className:\"punctuation\",begin:\"->\"},i]}},scss:function(e){const t=(e=>({IMPORTANT:{scope:\"meta\",begin:\"!important\"},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:\"number\",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\\b/},FUNCTION_DISPATCH:{className:\"built_in\",begin:/[\\w-]+(?=\\()/},ATTRIBUTE_SELECTOR_MODE:{scope:\"selector-attr\",begin:/\\[/,end:/\\]/,illegal:\"$\",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{scope:\"number\",begin:e.NUMBER_RE+\"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?\",relevance:0},CSS_VARIABLE:{className:\"attr\",begin:/--[A-Za-z_][A-Za-z0-9_-]*/}}))(e),n=ki,r=_i,a=\"@[a-z-]+\",i={className:\"variable\",begin:\"(\\\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\\\b\",relevance:0};return{name:\"SCSS\",case_insensitive:!0,illegal:\"[=/|']\",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,t.CSS_NUMBER_MODE,{className:\"selector-id\",begin:\"#[A-Za-z0-9_-]+\",relevance:0},{className:\"selector-class\",begin:\"\\\\.[A-Za-z0-9_-]+\",relevance:0},t.ATTRIBUTE_SELECTOR_MODE,{className:\"selector-tag\",begin:\"\\\\b(\"+Ei.join(\"|\")+\")\\\\b\",relevance:0},{className:\"selector-pseudo\",begin:\":(\"+r.join(\"|\")+\")\"},{className:\"selector-pseudo\",begin:\":(:)?(\"+n.join(\"|\")+\")\"},i,{begin:/\\(/,end:/\\)/,contains:[t.CSS_NUMBER_MODE]},t.CSS_VARIABLE,{className:\"attribute\",begin:\"\\\\b(\"+Ti.join(\"|\")+\")\\\\b\"},{begin:\"\\\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\\\b\"},{begin:/:/,end:/[;}{]/,relevance:0,contains:[t.BLOCK_COMMENT,i,t.HEXCOLOR,t.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,t.IMPORTANT,t.FUNCTION_DISPATCH]},{begin:\"@(page|font-face)\",keywords:{$pattern:a,keyword:\"@page @font-face\"}},{begin:\"@\",end:\"[{;]\",returnBegin:!0,keywords:{$pattern:/[a-z-]+/,keyword:\"and or not only\",attribute:yi.join(\" \")},contains:[{begin:a,className:\"keyword\"},{begin:/[a-z-]+(?=:)/,className:\"attribute\"},i,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,t.HEXCOLOR,t.CSS_NUMBER_MODE]},t.FUNCTION_DISPATCH]}},shell:function(e){return{name:\"Shell Session\",aliases:[\"console\",\"shellsession\"],contains:[{className:\"meta.prompt\",begin:/^\\s{0,3}[/~\\w\\d[\\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\\\](?=\\s*$)/,subLanguage:\"bash\"}}]}},sql:function(e){const t=e.regex,n=e.COMMENT(\"--\",\"$\"),r=[\"abs\",\"acos\",\"array_agg\",\"asin\",\"atan\",\"avg\",\"cast\",\"ceil\",\"ceiling\",\"coalesce\",\"corr\",\"cos\",\"cosh\",\"count\",\"covar_pop\",\"covar_samp\",\"cume_dist\",\"dense_rank\",\"deref\",\"element\",\"exp\",\"extract\",\"first_value\",\"floor\",\"json_array\",\"json_arrayagg\",\"json_exists\",\"json_object\",\"json_objectagg\",\"json_query\",\"json_table\",\"json_table_primitive\",\"json_value\",\"lag\",\"last_value\",\"lead\",\"listagg\",\"ln\",\"log\",\"log10\",\"lower\",\"max\",\"min\",\"mod\",\"nth_value\",\"ntile\",\"nullif\",\"percent_rank\",\"percentile_cont\",\"percentile_disc\",\"position\",\"position_regex\",\"power\",\"rank\",\"regr_avgx\",\"regr_avgy\",\"regr_count\",\"regr_intercept\",\"regr_r2\",\"regr_slope\",\"regr_sxx\",\"regr_sxy\",\"regr_syy\",\"row_number\",\"sin\",\"sinh\",\"sqrt\",\"stddev_pop\",\"stddev_samp\",\"substring\",\"substring_regex\",\"sum\",\"tan\",\"tanh\",\"translate\",\"translate_regex\",\"treat\",\"trim\",\"trim_array\",\"unnest\",\"upper\",\"value_of\",\"var_pop\",\"var_samp\",\"width_bucket\"],a=r,i=[\"abs\",\"acos\",\"all\",\"allocate\",\"alter\",\"and\",\"any\",\"are\",\"array\",\"array_agg\",\"array_max_cardinality\",\"as\",\"asensitive\",\"asin\",\"asymmetric\",\"at\",\"atan\",\"atomic\",\"authorization\",\"avg\",\"begin\",\"begin_frame\",\"begin_partition\",\"between\",\"bigint\",\"binary\",\"blob\",\"boolean\",\"both\",\"by\",\"call\",\"called\",\"cardinality\",\"cascaded\",\"case\",\"cast\",\"ceil\",\"ceiling\",\"char\",\"char_length\",\"character\",\"character_length\",\"check\",\"classifier\",\"clob\",\"close\",\"coalesce\",\"collate\",\"collect\",\"column\",\"commit\",\"condition\",\"connect\",\"constraint\",\"contains\",\"convert\",\"copy\",\"corr\",\"corresponding\",\"cos\",\"cosh\",\"count\",\"covar_pop\",\"covar_samp\",\"create\",\"cross\",\"cube\",\"cume_dist\",\"current\",\"current_catalog\",\"current_date\",\"current_default_transform_group\",\"current_path\",\"current_role\",\"current_row\",\"current_schema\",\"current_time\",\"current_timestamp\",\"current_path\",\"current_role\",\"current_transform_group_for_type\",\"current_user\",\"cursor\",\"cycle\",\"date\",\"day\",\"deallocate\",\"dec\",\"decimal\",\"decfloat\",\"declare\",\"default\",\"define\",\"delete\",\"dense_rank\",\"deref\",\"describe\",\"deterministic\",\"disconnect\",\"distinct\",\"double\",\"drop\",\"dynamic\",\"each\",\"element\",\"else\",\"empty\",\"end\",\"end_frame\",\"end_partition\",\"end-exec\",\"equals\",\"escape\",\"every\",\"except\",\"exec\",\"execute\",\"exists\",\"exp\",\"external\",\"extract\",\"false\",\"fetch\",\"filter\",\"first_value\",\"float\",\"floor\",\"for\",\"foreign\",\"frame_row\",\"free\",\"from\",\"full\",\"function\",\"fusion\",\"get\",\"global\",\"grant\",\"group\",\"grouping\",\"groups\",\"having\",\"hold\",\"hour\",\"identity\",\"in\",\"indicator\",\"initial\",\"inner\",\"inout\",\"insensitive\",\"insert\",\"int\",\"integer\",\"intersect\",\"intersection\",\"interval\",\"into\",\"is\",\"join\",\"json_array\",\"json_arrayagg\",\"json_exists\",\"json_object\",\"json_objectagg\",\"json_query\",\"json_table\",\"json_table_primitive\",\"json_value\",\"lag\",\"language\",\"large\",\"last_value\",\"lateral\",\"lead\",\"leading\",\"left\",\"like\",\"like_regex\",\"listagg\",\"ln\",\"local\",\"localtime\",\"localtimestamp\",\"log\",\"log10\",\"lower\",\"match\",\"match_number\",\"match_recognize\",\"matches\",\"max\",\"member\",\"merge\",\"method\",\"min\",\"minute\",\"mod\",\"modifies\",\"module\",\"month\",\"multiset\",\"national\",\"natural\",\"nchar\",\"nclob\",\"new\",\"no\",\"none\",\"normalize\",\"not\",\"nth_value\",\"ntile\",\"null\",\"nullif\",\"numeric\",\"octet_length\",\"occurrences_regex\",\"of\",\"offset\",\"old\",\"omit\",\"on\",\"one\",\"only\",\"open\",\"or\",\"order\",\"out\",\"outer\",\"over\",\"overlaps\",\"overlay\",\"parameter\",\"partition\",\"pattern\",\"per\",\"percent\",\"percent_rank\",\"percentile_cont\",\"percentile_disc\",\"period\",\"portion\",\"position\",\"position_regex\",\"power\",\"precedes\",\"precision\",\"prepare\",\"primary\",\"procedure\",\"ptf\",\"range\",\"rank\",\"reads\",\"real\",\"recursive\",\"ref\",\"references\",\"referencing\",\"regr_avgx\",\"regr_avgy\",\"regr_count\",\"regr_intercept\",\"regr_r2\",\"regr_slope\",\"regr_sxx\",\"regr_sxy\",\"regr_syy\",\"release\",\"result\",\"return\",\"returns\",\"revoke\",\"right\",\"rollback\",\"rollup\",\"row\",\"row_number\",\"rows\",\"running\",\"savepoint\",\"scope\",\"scroll\",\"search\",\"second\",\"seek\",\"select\",\"sensitive\",\"session_user\",\"set\",\"show\",\"similar\",\"sin\",\"sinh\",\"skip\",\"smallint\",\"some\",\"specific\",\"specifictype\",\"sql\",\"sqlexception\",\"sqlstate\",\"sqlwarning\",\"sqrt\",\"start\",\"static\",\"stddev_pop\",\"stddev_samp\",\"submultiset\",\"subset\",\"substring\",\"substring_regex\",\"succeeds\",\"sum\",\"symmetric\",\"system\",\"system_time\",\"system_user\",\"table\",\"tablesample\",\"tan\",\"tanh\",\"then\",\"time\",\"timestamp\",\"timezone_hour\",\"timezone_minute\",\"to\",\"trailing\",\"translate\",\"translate_regex\",\"translation\",\"treat\",\"trigger\",\"trim\",\"trim_array\",\"true\",\"truncate\",\"uescape\",\"union\",\"unique\",\"unknown\",\"unnest\",\"update\",\"upper\",\"user\",\"using\",\"value\",\"values\",\"value_of\",\"var_pop\",\"var_samp\",\"varbinary\",\"varchar\",\"varying\",\"versioning\",\"when\",\"whenever\",\"where\",\"width_bucket\",\"window\",\"with\",\"within\",\"without\",\"year\",\"add\",\"asc\",\"collation\",\"desc\",\"final\",\"first\",\"last\",\"view\"].filter(e=>!r.includes(e)),s={match:t.concat(/\\b/,t.either(...a),/\\s*\\(/),relevance:0,keywords:{built_in:a}};function o(e){return t.concat(/\\b/,t.either(...e.map(e=>e.replace(/\\s+/,\"\\\\s+\"))),/\\b/)}const l={scope:\"keyword\",match:o([\"create table\",\"insert into\",\"primary key\",\"foreign key\",\"not null\",\"alter table\",\"add constraint\",\"grouping sets\",\"on overflow\",\"character set\",\"respect nulls\",\"ignore nulls\",\"nulls first\",\"nulls last\",\"depth first\",\"breadth first\"]),relevance:0};return{name:\"SQL\",case_insensitive:!0,illegal:/[{}]|<\\//,keywords:{$pattern:/\\b[\\w\\.]+/,keyword:function(e,{exceptions:t,when:n}={}){const r=n;return t=t||[],e.map(e=>e.match(/\\|\\d+$/)||t.includes(e)?e:r(e)?`${e}|0`:e)}(i,{when:e=>e.length<3}),literal:[\"true\",\"false\",\"unknown\"],type:[\"bigint\",\"binary\",\"blob\",\"boolean\",\"char\",\"character\",\"clob\",\"date\",\"dec\",\"decfloat\",\"decimal\",\"float\",\"int\",\"integer\",\"interval\",\"nchar\",\"nclob\",\"national\",\"numeric\",\"real\",\"row\",\"smallint\",\"time\",\"timestamp\",\"varchar\",\"varying\",\"varbinary\"],built_in:[\"current_catalog\",\"current_date\",\"current_default_transform_group\",\"current_path\",\"current_role\",\"current_schema\",\"current_transform_group_for_type\",\"current_user\",\"session_user\",\"system_time\",\"system_user\",\"current_time\",\"localtime\",\"current_timestamp\",\"localtimestamp\"]},contains:[{scope:\"type\",match:o([\"double precision\",\"large object\",\"with timezone\",\"without timezone\"])},l,s,{scope:\"variable\",match:/@[a-z0-9][a-z0-9_]*/},{scope:\"string\",variants:[{begin:/'/,end:/'/,contains:[{match:/''/}]}]},{begin:/\"/,end:/\"/,contains:[{match:/\"\"/}]},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,n,{scope:\"operator\",match:/[-+*/=%^~]|&&?|\\|\\|?|!=?|<(?:=>?|<|>)?|>[>=]?/,relevance:0}]}},swift:function(e){const t={match:/\\s+/,relevance:0},n=e.COMMENT(\"/\\\\*\",\"\\\\*/\",{contains:[\"self\"]}),r=[e.C_LINE_COMMENT_MODE,n],a={match:[/\\./,Ni(...xi,...wi)],className:{2:\"keyword\"}},i={match:Ai(/\\./,Ni(...Oi)),relevance:0},s=Oi.filter(e=>\"string\"==typeof e).concat([\"_|0\"]),o={variants:[{className:\"keyword\",match:Ni(...Oi.filter(e=>\"string\"!=typeof e).concat(Ii).map(Ci),...wi)}]},l={$pattern:Ni(/\\b\\w+/,/#\\w+/),keyword:s.concat(Mi),literal:Ri},c=[a,i,o],u=[{match:Ai(/\\./,Ni(...Pi)),relevance:0},{className:\"built_in\",match:Ai(/\\b/,Ni(...Pi),/(?=\\()/)}],d={match:/->/,relevance:0},p=[d,{className:\"operator\",relevance:0,variants:[{match:Bi},{match:`\\\\.(\\\\.|${Fi})+`}]}],h=\"([0-9]_*)+\",f=\"([0-9a-fA-F]_*)+\",m={className:\"number\",relevance:0,variants:[{match:`\\\\b(${h})(\\\\.(${h}))?([eE][+-]?(${h}))?\\\\b`},{match:`\\\\b0x(${f})(\\\\.(${f}))?([pP][+-]?(${h}))?\\\\b`},{match:/\\b0o([0-7]_*)+\\b/},{match:/\\b0b([01]_*)+\\b/}]},g=(e=\"\")=>({className:\"subst\",variants:[{match:Ai(/\\\\/,e,/[0\\\\tnr\"']/)},{match:Ai(/\\\\/,e,/u\\{[0-9a-fA-F]{1,8}\\}/)}]}),b=(e=\"\")=>({className:\"subst\",match:Ai(/\\\\/,e,/[\\t ]*(?:[\\r\\n]|\\r\\n)/)}),E=(e=\"\")=>({className:\"subst\",label:\"interpol\",begin:Ai(/\\\\/,e,/\\(/),end:/\\)/}),y=(e=\"\")=>({begin:Ai(e,/\"\"\"/),end:Ai(/\"\"\"/,e),contains:[g(e),b(e),E(e)]}),_=(e=\"\")=>({begin:Ai(e,/\"/),end:Ai(/\"/,e),contains:[g(e),E(e)]}),k={className:\"string\",variants:[y(),y(\"#\"),y(\"##\"),y(\"###\"),_(),_(\"#\"),_(\"##\"),_(\"###\")]},T=[e.BACKSLASH_ESCAPE,{begin:/\\[/,end:/\\]/,relevance:0,contains:[e.BACKSLASH_ESCAPE]}],S={begin:/\\/[^\\s](?=[^/\\n]*\\/)/,end:/\\//,contains:T},v=e=>{const t=Ai(e,/\\//),n=Ai(/\\//,e);return{begin:t,end:n,contains:[...T,{scope:\"comment\",begin:`#(?!.*${n})`,end:/$/}]}},A={scope:\"regexp\",variants:[v(\"###\"),v(\"##\"),v(\"#\"),S]},N={match:Ai(/`/,zi,/`/)},C=[N,{className:\"variable\",match:/\\$\\d+/},{className:\"variable\",match:`\\\\$${Hi}+`}],x=[{match:/(@|#(un)?)available/,scope:\"keyword\",starts:{contains:[{begin:/\\(/,end:/\\)/,keywords:$i,contains:[...p,m,k]}]}},{scope:\"keyword\",match:Ai(/@/,Ni(...ji),vi(Ni(/\\(/,/\\s+/)))},{scope:\"meta\",match:Ai(/@/,zi)}],w={match:vi(/\\b[A-Z]/),relevance:0,contains:[{className:\"type\",match:Ai(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,Hi,\"+\")},{className:\"type\",match:Gi,relevance:0},{match:/[?!]+/,relevance:0},{match:/\\.\\.\\./,relevance:0},{match:Ai(/\\s+&\\s+/,vi(Gi)),relevance:0}]},I={begin:/</,end:/>/,keywords:l,contains:[...r,...c,...x,d,w]};w.contains.push(I);const O={begin:/\\(/,end:/\\)/,relevance:0,keywords:l,contains:[\"self\",{match:Ai(zi,/\\s*:/),keywords:\"_|0\",relevance:0},...r,A,...c,...u,...p,m,k,...C,...x,w]},R={begin:/</,end:/>/,keywords:\"repeat each\",contains:[...r,w]},D={begin:/\\(/,end:/\\)/,keywords:l,contains:[{begin:Ni(vi(Ai(zi,/\\s*:/)),vi(Ai(zi,/\\s+/,zi,/\\s*:/))),end:/:/,relevance:0,contains:[{className:\"keyword\",match:/\\b_\\b/},{className:\"params\",match:zi}]},...r,...c,...p,m,k,...x,w,O],endsParent:!0,illegal:/[\"']/},M={match:[/(func|macro)/,/\\s+/,Ni(N.match,zi,Bi)],className:{1:\"keyword\",3:\"title.function\"},contains:[R,D,t],illegal:[/\\[/,/%/]},P={match:[/\\b(?:subscript|init[?!]?)/,/\\s*(?=[<(])/],className:{1:\"keyword\"},contains:[R,D,t],illegal:/\\[|%/},L={match:[/operator/,/\\s+/,Bi],className:{1:\"keyword\",3:\"title\"}},F={begin:[/precedencegroup/,/\\s+/,Gi],className:{1:\"keyword\",3:\"title\"},contains:[w],keywords:[...Di,...Ri],end:/}/},B={begin:[/(struct|protocol|class|extension|enum|actor)/,/\\s+/,zi,/\\s*/],beginScope:{1:\"keyword\",3:\"title.class\"},keywords:l,contains:[R,...c,{begin:/:/,end:/\\{/,keywords:l,contains:[{scope:\"title.class.inherited\",match:Gi},...c],relevance:0}]};for(const U of k.variants){const e=U.contains.find(e=>\"interpol\"===e.label);e.keywords=l;const t=[...c,...u,...p,m,k,...C];e.contains=[...t,{begin:/\\(/,end:/\\)/,contains:[\"self\",...t]}]}return{name:\"Swift\",keywords:l,contains:[...r,M,P,{match:[/class\\b/,/\\s+/,/func\\b/,/\\s+/,/\\b[A-Za-z_][A-Za-z0-9_]*\\b/],scope:{1:\"keyword\",3:\"keyword\",5:\"title.function\"}},{match:[/class\\b/,/\\s+/,/var\\b/],scope:{1:\"keyword\",3:\"keyword\"}},B,L,F,{beginKeywords:\"import\",end:/$/,contains:[...r],relevance:0},A,...c,...u,...p,m,k,...C,...x,w,O]}},typescript:function(e){const t=e.regex,n=function(e){const t=e.regex,n=qi,r=\"<>\",a=\"</>\",i={begin:/<[A-Za-z0-9\\\\._:-]+/,end:/\\/[A-Za-z0-9\\\\._:-]+>|\\/>/,isTrulyOpeningTag:(e,t)=>{const n=e[0].length+e.index,r=e.input[n];if(\"<\"===r||\",\"===r)return void t.ignoreMatch();let a;\">\"===r&&(((e,{after:t})=>{const n=\"</\"+e[0].slice(1);return-1!==e.input.indexOf(n,t)})(e,{after:n})||t.ignoreMatch());const i=e.input.substring(n);((a=i.match(/^\\s*=/))||(a=i.match(/^\\s+extends\\s+/))&&0===a.index)&&t.ignoreMatch()}},s={$pattern:qi,keyword:Yi,literal:Wi,built_in:Zi,\"variable.language\":Xi},o=\"[0-9](_?[0-9])*\",l=`\\\\.(${o})`,c=\"0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*\",u={className:\"number\",variants:[{begin:`(\\\\b(${c})((${l})|\\\\.)?|(${l}))[eE][+-]?(${o})\\\\b`},{begin:`\\\\b(${c})\\\\b((${l})\\\\b|\\\\.)?|(${l})\\\\b`},{begin:\"\\\\b(0|[1-9](_?[0-9])*)n\\\\b\"},{begin:\"\\\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\\\b\"},{begin:\"\\\\b0[bB][0-1](_?[0-1])*n?\\\\b\"},{begin:\"\\\\b0[oO][0-7](_?[0-7])*n?\\\\b\"},{begin:\"\\\\b0[0-7]+n?\\\\b\"}],relevance:0},d={className:\"subst\",begin:\"\\\\$\\\\{\",end:\"\\\\}\",keywords:s,contains:[]},p={begin:\".?html`\",end:\"\",starts:{end:\"`\",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,d],subLanguage:\"xml\"}},h={begin:\".?css`\",end:\"\",starts:{end:\"`\",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,d],subLanguage:\"css\"}},f={begin:\".?gql`\",end:\"\",starts:{end:\"`\",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,d],subLanguage:\"graphql\"}},m={className:\"string\",begin:\"`\",end:\"`\",contains:[e.BACKSLASH_ESCAPE,d]},g={className:\"comment\",variants:[e.COMMENT(/\\/\\*\\*(?!\\/)/,\"\\\\*/\",{relevance:0,contains:[{begin:\"(?=@[A-Za-z]+)\",relevance:0,contains:[{className:\"doctag\",begin:\"@[A-Za-z]+\"},{className:\"type\",begin:\"\\\\{\",end:\"\\\\}\",excludeEnd:!0,excludeBegin:!0,relevance:0},{className:\"variable\",begin:n+\"(?=\\\\s*(-)|$)\",endsParent:!0,relevance:0},{begin:/(?=[^\\n])\\s/,relevance:0}]}]}),e.C_BLOCK_COMMENT_MODE,e.C_LINE_COMMENT_MODE]},b=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,p,h,f,m,{match:/\\$\\d+/},u];d.contains=b.concat({begin:/\\{/,end:/\\}/,keywords:s,contains:[\"self\"].concat(b)});const E=[].concat(g,d.contains),y=E.concat([{begin:/(\\s*)\\(/,end:/\\)/,keywords:s,contains:[\"self\"].concat(E)}]),_={className:\"params\",begin:/(\\s*)\\(/,end:/\\)/,excludeBegin:!0,excludeEnd:!0,keywords:s,contains:y},k={variants:[{match:[/class/,/\\s+/,n,/\\s+/,/extends/,/\\s+/,t.concat(n,\"(\",t.concat(/\\./,n),\")*\")],scope:{1:\"keyword\",3:\"title.class\",5:\"keyword\",7:\"title.class.inherited\"}},{match:[/class/,/\\s+/,n],scope:{1:\"keyword\",3:\"title.class\"}}]},T={relevance:0,match:t.either(/\\bJSON/,/\\b[A-Z][a-z]+([A-Z][a-z]*|\\d)*/,/\\b[A-Z]{2,}([A-Z][a-z]+|\\d)+([A-Z][a-z]*)*/,/\\b[A-Z]{2,}[a-z]+([A-Z][a-z]+|\\d)*([A-Z][a-z]*)*/),className:\"title.class\",keywords:{_:[...Vi,...Ki]}},S={variants:[{match:[/function/,/\\s+/,n,/(?=\\s*\\()/]},{match:[/function/,/\\s*(?=\\()/]}],className:{1:\"keyword\",3:\"title.function\"},label:\"func.def\",contains:[_],illegal:/%/},v={match:t.concat(/\\b/,(A=[...Qi,\"super\",\"import\"].map(e=>`${e}\\\\s*\\\\(`),t.concat(\"(?!\",A.join(\"|\"),\")\")),n,t.lookahead(/\\s*\\(/)),className:\"title.function\",relevance:0};var A;const N={begin:t.concat(/\\./,t.lookahead(t.concat(n,/(?![0-9A-Za-z$_(])/))),end:n,excludeBegin:!0,keywords:\"prototype\",className:\"property\",relevance:0},C={match:[/get|set/,/\\s+/,n,/(?=\\()/],className:{1:\"keyword\",3:\"title.function\"},contains:[{begin:/\\(\\)/},_]},x=\"(\\\\([^()]*(\\\\([^()]*(\\\\([^()]*\\\\)[^()]*)*\\\\)[^()]*)*\\\\)|\"+e.UNDERSCORE_IDENT_RE+\")\\\\s*=>\",w={match:[/const|var|let/,/\\s+/,n,/\\s*/,/=\\s*/,/(async\\s*)?/,t.lookahead(x)],keywords:\"async\",className:{1:\"keyword\",3:\"title.function\"},contains:[_]};return{name:\"JavaScript\",aliases:[\"js\",\"jsx\",\"mjs\",\"cjs\"],keywords:s,exports:{PARAMS_CONTAINS:y,CLASS_REFERENCE:T},illegal:/#(?![$_A-z])/,contains:[e.SHEBANG({label:\"shebang\",binary:\"node\",relevance:5}),{label:\"use_strict\",className:\"meta\",relevance:10,begin:/^\\s*['\"]use (strict|asm)['\"]/},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,p,h,f,m,g,{match:/\\$\\d+/},u,T,{scope:\"attr\",match:n+t.lookahead(\":\"),relevance:0},w,{begin:\"(\"+e.RE_STARTERS_RE+\"|\\\\b(case|return|throw)\\\\b)\\\\s*\",keywords:\"return throw case\",relevance:0,contains:[g,e.REGEXP_MODE,{className:\"function\",begin:x,returnBegin:!0,end:\"\\\\s*=>\",contains:[{className:\"params\",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\\(\\s*\\)/,skip:!0},{begin:/(\\s*)\\(/,end:/\\)/,excludeBegin:!0,excludeEnd:!0,keywords:s,contains:y}]}]},{begin:/,/,relevance:0},{match:/\\s+/,relevance:0},{variants:[{begin:r,end:a},{match:/<[A-Za-z0-9\\\\._:-]+\\s*\\/>/},{begin:i.begin,\"on:begin\":i.isTrulyOpeningTag,end:i.end}],subLanguage:\"xml\",contains:[{begin:i.begin,end:i.end,skip:!0,contains:[\"self\"]}]}]},S,{beginKeywords:\"while if switch catch for\"},{begin:\"\\\\b(?!function)\"+e.UNDERSCORE_IDENT_RE+\"\\\\([^()]*(\\\\([^()]*(\\\\([^()]*\\\\)[^()]*)*\\\\)[^()]*)*\\\\)\\\\s*\\\\{\",returnBegin:!0,label:\"func.def\",contains:[_,e.inherit(e.TITLE_MODE,{begin:n,className:\"title.function\"})]},{match:/\\.\\.\\./,relevance:0},N,{match:\"\\\\$\"+n,relevance:0},{match:[/\\bconstructor(?=\\s*\\()/],className:{1:\"title.function\"},contains:[_]},v,{relevance:0,match:/\\b[A-Z][A-Z_0-9]+\\b/,className:\"variable.constant\"},k,C,{match:/\\$[(.]/}]}}(e),r=qi,a=[\"any\",\"void\",\"number\",\"boolean\",\"string\",\"object\",\"never\",\"symbol\",\"bigint\",\"unknown\"],i={begin:[/namespace/,/\\s+/,e.IDENT_RE],beginScope:{1:\"keyword\",3:\"title.class\"}},s={beginKeywords:\"interface\",end:/\\{/,excludeEnd:!0,keywords:{keyword:\"interface extends\",built_in:a},contains:[n.exports.CLASS_REFERENCE]},o={$pattern:qi,keyword:Yi.concat([\"type\",\"interface\",\"public\",\"private\",\"protected\",\"implements\",\"declare\",\"abstract\",\"readonly\",\"enum\",\"override\",\"satisfies\"]),literal:Wi,built_in:Zi.concat(a),\"variable.language\":Xi},l={className:\"meta\",begin:\"@\"+r},c=(e,t,n)=>{const r=e.contains.findIndex(e=>e.label===t);if(-1===r)throw new Error(\"can not find mode to replace\");e.contains.splice(r,1,n)};Object.assign(n.keywords,o),n.exports.PARAMS_CONTAINS.push(l);const u=n.contains.find(e=>\"attr\"===e.scope),d=Object.assign({},u,{match:t.concat(r,t.lookahead(/\\s*\\?:/))});return n.exports.PARAMS_CONTAINS.push([n.exports.CLASS_REFERENCE,u,d]),n.contains=n.contains.concat([l,i,s,d]),c(n,\"shebang\",e.SHEBANG()),c(n,\"use_strict\",{className:\"meta\",relevance:10,begin:/^\\s*['\"]use strict['\"]/}),n.contains.find(e=>\"func.def\"===e.label).relevance=0,Object.assign(n,{name:\"TypeScript\",aliases:[\"ts\",\"tsx\",\"mts\",\"cts\"]}),n},vbnet:function(e){const t=e.regex,n=/\\d{1,2}\\/\\d{1,2}\\/\\d{4}/,r=/\\d{4}-\\d{1,2}-\\d{1,2}/,a=/(\\d|1[012])(:\\d+){0,2} *(AM|PM)/,i=/\\d{1,2}(:\\d{1,2}){1,2}/,s={className:\"literal\",variants:[{begin:t.concat(/# */,t.either(r,n),/ *#/)},{begin:t.concat(/# */,i,/ *#/)},{begin:t.concat(/# */,a,/ *#/)},{begin:t.concat(/# */,t.either(r,n),/ +/,t.either(a,i),/ *#/)}]},o=e.COMMENT(/'''/,/$/,{contains:[{className:\"doctag\",begin:/<\\/?/,end:/>/}]}),l=e.COMMENT(null,/$/,{variants:[{begin:/'/},{begin:/([\\t ]|^)REM(?=\\s)/}]});return{name:\"Visual Basic .NET\",aliases:[\"vb\"],case_insensitive:!0,classNameAliases:{label:\"symbol\"},keywords:{keyword:\"addhandler alias aggregate ansi as async assembly auto binary by byref byval call case catch class compare const continue custom declare default delegate dim distinct do each equals else elseif end enum erase error event exit explicit finally for friend from function get global goto group handles if implements imports in inherits interface into iterator join key let lib loop me mid module mustinherit mustoverride mybase myclass namespace narrowing new next notinheritable notoverridable of off on operator option optional order overloads overridable overrides paramarray partial preserve private property protected public raiseevent readonly redim removehandler resume return select set shadows shared skip static step stop structure strict sub synclock take text then throw to try unicode until using when where while widening with withevents writeonly yield\",built_in:\"addressof and andalso await directcast gettype getxmlnamespace is isfalse isnot istrue like mod nameof new not or orelse trycast typeof xor cbool cbyte cchar cdate cdbl cdec cint clng cobj csbyte cshort csng cstr cuint culng cushort\",type:\"boolean byte char date decimal double integer long object sbyte short single string uinteger ulong ushort\",literal:\"true false nothing\"},illegal:\"//|\\\\{|\\\\}|endif|gosub|variant|wend|^\\\\$ \",contains:[{className:\"string\",begin:/\"(\"\"|[^/n])\"C\\b/},{className:\"string\",begin:/\"/,end:/\"/,illegal:/\\n/,contains:[{begin:/\"\"/}]},s,{className:\"number\",relevance:0,variants:[{begin:/\\b\\d[\\d_]*((\\.[\\d_]+(E[+-]?[\\d_]+)?)|(E[+-]?[\\d_]+))[RFD@!#]?/},{begin:/\\b\\d[\\d_]*((U?[SIL])|[%&])?/},{begin:/&H[\\dA-F_]+((U?[SIL])|[%&])?/},{begin:/&O[0-7_]+((U?[SIL])|[%&])?/},{begin:/&B[01_]+((U?[SIL])|[%&])?/}]},{className:\"label\",begin:/^\\w+:/},o,l,{className:\"meta\",begin:/[\\t ]*#(const|disable|else|elseif|enable|end|externalsource|if|region)\\b/,end:/$/,keywords:{keyword:\"const disable else elseif enable end externalsource if region then\"},contains:[l]}]}},wasm:function(e){e.regex;const t=e.COMMENT(/\\(;/,/;\\)/);return t.contains.push(\"self\"),{name:\"WebAssembly\",keywords:{$pattern:/[\\w.]+/,keyword:[\"anyfunc\",\"block\",\"br\",\"br_if\",\"br_table\",\"call\",\"call_indirect\",\"data\",\"drop\",\"elem\",\"else\",\"end\",\"export\",\"func\",\"global.get\",\"global.set\",\"local.get\",\"local.set\",\"local.tee\",\"get_global\",\"get_local\",\"global\",\"if\",\"import\",\"local\",\"loop\",\"memory\",\"memory.grow\",\"memory.size\",\"module\",\"mut\",\"nop\",\"offset\",\"param\",\"result\",\"return\",\"select\",\"set_global\",\"set_local\",\"start\",\"table\",\"tee_local\",\"then\",\"type\",\"unreachable\"]},contains:[e.COMMENT(/;;/,/$/),t,{match:[/(?:offset|align)/,/\\s*/,/=/],className:{1:\"keyword\",3:\"operator\"}},{className:\"variable\",begin:/\\$[\\w_]+/},{match:/(\\((?!;)|\\))+/,className:\"punctuation\",relevance:0},{begin:[/(?:func|call|call_indirect)/,/\\s+/,/\\$[^\\s)]+/],className:{1:\"keyword\",3:\"title.function\"}},e.QUOTE_STRING_MODE,{match:/(i32|i64|f32|f64)(?!\\.)/,className:\"type\"},{className:\"keyword\",match:/\\b(f32|f64|i32|i64)(?:\\.(?:abs|add|and|ceil|clz|const|convert_[su]\\/i(?:32|64)|copysign|ctz|demote\\/f64|div(?:_[su])?|eqz?|extend_[su]\\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|nearest|neg?|or|popcnt|promote\\/f32|reinterpret\\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|store(?:8|16|32)?|sqrt|sub|trunc(?:_[su]\\/f(?:32|64))?|wrap\\/i64|xor))\\b/},{className:\"number\",relevance:0,match:/[+-]?\\b(?:\\d(?:_?\\d)*(?:\\.\\d(?:_?\\d)*)?(?:[eE][+-]?\\d(?:_?\\d)*)?|0x[\\da-fA-F](?:_?[\\da-fA-F])*(?:\\.[\\da-fA-F](?:_?[\\da-fA-D])*)?(?:[pP][+-]?\\d(?:_?\\d)*)?)\\b|\\binf\\b|\\bnan(?::0x[\\da-fA-F](?:_?[\\da-fA-D])*)?\\b/}]}},xml:function(e){const t=e.regex,n=t.concat(/[\\p{L}_]/u,t.optional(/[\\p{L}0-9_.-]*:/u),/[\\p{L}0-9_.-]*/u),r={className:\"symbol\",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},a={begin:/\\s/,contains:[{className:\"keyword\",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\\n/}]},i=e.inherit(a,{begin:/\\(/,end:/\\)/}),s=e.inherit(e.APOS_STRING_MODE,{className:\"string\"}),o=e.inherit(e.QUOTE_STRING_MODE,{className:\"string\"}),l={endsWithParent:!0,illegal:/</,relevance:0,contains:[{className:\"attr\",begin:/[\\p{L}0-9._:-]+/u,relevance:0},{begin:/=\\s*/,relevance:0,contains:[{className:\"string\",endsParent:!0,variants:[{begin:/\"/,end:/\"/,contains:[r]},{begin:/'/,end:/'/,contains:[r]},{begin:/[^\\s\"'=<>`]+/}]}]}]};return{name:\"HTML, XML\",aliases:[\"html\",\"xhtml\",\"rss\",\"atom\",\"xjb\",\"xsd\",\"xsl\",\"plist\",\"wsf\",\"svg\"],case_insensitive:!0,unicodeRegex:!0,contains:[{className:\"meta\",begin:/<![a-z]/,end:/>/,relevance:10,contains:[a,o,s,i,{begin:/\\[/,end:/\\]/,contains:[{className:\"meta\",begin:/<![a-z]/,end:/>/,contains:[a,i,o,s]}]}]},e.COMMENT(/<!--/,/-->/,{relevance:10}),{begin:/<!\\[CDATA\\[/,end:/\\]\\]>/,relevance:10},r,{className:\"meta\",end:/\\?>/,variants:[{begin:/<\\?xml/,relevance:10,contains:[o]},{begin:/<\\?[a-z][a-z0-9]+/}]},{className:\"tag\",begin:/<style(?=\\s|>)/,end:/>/,keywords:{name:\"style\"},contains:[l],starts:{end:/<\\/style>/,returnEnd:!0,subLanguage:[\"css\",\"xml\"]}},{className:\"tag\",begin:/<script(?=\\s|>)/,end:/>/,keywords:{name:\"script\"},contains:[l],starts:{end:/<\\/script>/,returnEnd:!0,subLanguage:[\"javascript\",\"handlebars\",\"xml\"]}},{className:\"tag\",begin:/<>|<\\/>/},{className:\"tag\",begin:t.concat(/</,t.lookahead(t.concat(n,t.either(/\\/>/,/>/,/\\s/)))),end:/\\/?>/,contains:[{className:\"name\",begin:n,relevance:0,starts:l}]},{className:\"tag\",begin:t.concat(/<\\//,t.lookahead(t.concat(n,/>/))),contains:[{className:\"name\",begin:n,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}},yaml:function(e){const t=\"true false yes no null\",n=\"[\\\\w#;/?:@&=+$,.~*'()[\\\\]]+\",r={className:\"string\",relevance:0,variants:[{begin:/\"/,end:/\"/},{begin:/\\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:\"template-variable\",variants:[{begin:/\\{\\{/,end:/\\}\\}/},{begin:/%\\{/,end:/\\}/}]}]},a=e.inherit(r,{variants:[{begin:/'/,end:/'/,contains:[{begin:/''/,relevance:0}]},{begin:/\"/,end:/\"/},{begin:/[^\\s,{}[\\]]+/}]}),i={className:\"number\",begin:\"\\\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\\\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\\\.[0-9]*)?([ \\\\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\\\b\"},s={end:\",\",endsWithParent:!0,excludeEnd:!0,keywords:t,relevance:0},o={begin:/\\{/,end:/\\}/,contains:[s],illegal:\"\\\\n\",relevance:0},l={begin:\"\\\\[\",end:\"\\\\]\",contains:[s],illegal:\"\\\\n\",relevance:0},c=[{className:\"attr\",variants:[{begin:/[\\w*@][\\w*@ :()\\./-]*:(?=[ \\t]|$)/},{begin:/\"[\\w*@][\\w*@ :()\\./-]*\":(?=[ \\t]|$)/},{begin:/'[\\w*@][\\w*@ :()\\./-]*':(?=[ \\t]|$)/}]},{className:\"meta\",begin:\"^---\\\\s*$\",relevance:10},{className:\"string\",begin:\"[\\\\|>]([1-9]?[+-])?[ ]*\\\\n( +)[^ ][^\\\\n]*\\\\n(\\\\2[^\\\\n]+\\\\n?)*\"},{begin:\"<%[%=-]?\",end:\"[%-]?%>\",subLanguage:\"ruby\",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:\"type\",begin:\"!\\\\w+!\"+n},{className:\"type\",begin:\"!<\"+n+\">\"},{className:\"type\",begin:\"!\"+n},{className:\"type\",begin:\"!!\"+n},{className:\"meta\",begin:\"&\"+e.UNDERSCORE_IDENT_RE+\"$\"},{className:\"meta\",begin:\"\\\\*\"+e.UNDERSCORE_IDENT_RE+\"$\"},{className:\"bullet\",begin:\"-(?=[ ]|$)\",relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:t,keywords:{literal:t}},i,{className:\"number\",begin:e.C_NUMBER_RE+\"\\\\b\",relevance:0},o,l,{className:\"string\",relevance:0,begin:/'/,end:/'/,contains:[{match:/''/,scope:\"char.escape\",relevance:0}]},r],u=[...c];return u.pop(),u.push(a),s.contains=u,{name:\"YAML\",case_insensitive:!0,aliases:[\"yml\"],contains:c}}};var es,ts;function ns(){if(ts)return es;function e(t){return t instanceof Map?t.clear=t.delete=t.set=function(){throw new Error(\"map is read-only\")}:t instanceof Set&&(t.add=t.clear=t.delete=function(){throw new Error(\"set is read-only\")}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach(n=>{const r=t[n],a=typeof r;\"object\"!==a&&\"function\"!==a||Object.isFrozen(r)||e(r)}),t}ts=1;class t{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function n(e){return e.replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\").replace(/>/g,\"&gt;\").replace(/\"/g,\"&quot;\").replace(/'/g,\"&#x27;\")}function r(e,...t){const n=Object.create(null);for(const r in e)n[r]=e[r];return t.forEach(function(e){for(const t in e)n[t]=e[t]}),n}const a=e=>!!e.scope;class i{constructor(e,t){this.buffer=\"\",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){this.buffer+=n(e)}openNode(e){if(!a(e))return;const t=((e,{prefix:t})=>{if(e.startsWith(\"language:\"))return e.replace(\"language:\",\"language-\");if(e.includes(\".\")){const n=e.split(\".\");return[`${t}${n.shift()}`,...n.map((e,t)=>`${e}${\"_\".repeat(t+1)}`)].join(\" \")}return`${t}${e}`})(e.scope,{prefix:this.classPrefix});this.span(t)}closeNode(e){a(e)&&(this.buffer+=\"</span>\")}value(){return this.buffer}span(e){this.buffer+=`<span class=\"${e}\">`}}const s=(e={})=>{const t={children:[]};return Object.assign(t,e),t};class o{constructor(){this.rootNode=s(),this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const t=s({scope:e});this.add(t),this.stack.push(t)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){return\"string\"==typeof t?e.addText(t):t.children&&(e.openNode(t),t.children.forEach(t=>this._walk(e,t)),e.closeNode(t)),e}static _collapse(e){\"string\"!=typeof e&&e.children&&(e.children.every(e=>\"string\"==typeof e)?e.children=[e.children.join(\"\")]:e.children.forEach(e=>{o._collapse(e)}))}}class l extends o{constructor(e){super(),this.options=e}addText(e){\"\"!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){this.closeNode()}__addSublanguage(e,t){const n=e.root;t&&(n.scope=`language:${t}`),this.add(n)}toHTML(){return new i(this,this.options).value()}finalize(){return this.closeAllNodes(),!0}}function c(e){return e?\"string\"==typeof e?e:e.source:null}function u(e){return h(\"(?=\",e,\")\")}function d(e){return h(\"(?:\",e,\")*\")}function p(e){return h(\"(?:\",e,\")?\")}function h(...e){return e.map(e=>c(e)).join(\"\")}function f(...e){const t=function(e){const t=e[e.length-1];return\"object\"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{}}(e);return\"(\"+(t.capture?\"\":\"?:\")+e.map(e=>c(e)).join(\"|\")+\")\"}function m(e){return new RegExp(e.toString()+\"|\").exec(\"\").length-1}const g=/\\[(?:[^\\\\\\]]|\\\\.)*\\]|\\(\\??|\\\\([1-9][0-9]*)|\\\\./;function b(e,{joinWith:t}){let n=0;return e.map(e=>{n+=1;const t=n;let r=c(e),a=\"\";for(;r.length>0;){const e=g.exec(r);if(!e){a+=r;break}a+=r.substring(0,e.index),r=r.substring(e.index+e[0].length),\"\\\\\"===e[0][0]&&e[1]?a+=\"\\\\\"+String(Number(e[1])+t):(a+=e[0],\"(\"===e[0]&&n++)}return a}).map(e=>`(${e})`).join(t)}const E=\"[a-zA-Z]\\\\w*\",y=\"[a-zA-Z_]\\\\w*\",_=\"\\\\b\\\\d+(\\\\.\\\\d+)?\",k=\"(-?)(\\\\b0[xX][a-fA-F0-9]+|(\\\\b\\\\d+(\\\\.\\\\d*)?|\\\\.\\\\d+)([eE][-+]?\\\\d+)?)\",T=\"\\\\b(0b[01]+)\",S={begin:\"\\\\\\\\[\\\\s\\\\S]\",relevance:0},v={scope:\"string\",begin:\"'\",end:\"'\",illegal:\"\\\\n\",contains:[S]},A={scope:\"string\",begin:'\"',end:'\"',illegal:\"\\\\n\",contains:[S]},N=function(e,t,n={}){const a=r({scope:\"comment\",begin:e,end:t,contains:[]},n);a.contains.push({scope:\"doctag\",begin:\"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)\",end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0});const i=f(\"I\",\"a\",\"is\",\"so\",\"us\",\"to\",\"at\",\"if\",\"in\",\"it\",\"on\",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/);return a.contains.push({begin:h(/[ ]+/,\"(\",i,/[.]?[:]?([.][ ]|[ ])/,\"){3}\")}),a},C=N(\"//\",\"$\"),x=N(\"/\\\\*\",\"\\\\*/\"),w=N(\"#\",\"$\"),I={scope:\"number\",begin:_,relevance:0},O={scope:\"number\",begin:k,relevance:0},R={scope:\"number\",begin:T,relevance:0},D={scope:\"regexp\",begin:/\\/(?=[^/\\n]*\\/)/,end:/\\/[gimuy]*/,contains:[S,{begin:/\\[/,end:/\\]/,relevance:0,contains:[S]}]},M={scope:\"title\",begin:E,relevance:0},P={scope:\"title\",begin:y,relevance:0},L={begin:\"\\\\.\\\\s*\"+y,relevance:0};var F=Object.freeze({__proto__:null,APOS_STRING_MODE:v,BACKSLASH_ESCAPE:S,BINARY_NUMBER_MODE:R,BINARY_NUMBER_RE:T,COMMENT:N,C_BLOCK_COMMENT_MODE:x,C_LINE_COMMENT_MODE:C,C_NUMBER_MODE:O,C_NUMBER_RE:k,END_SAME_AS_BEGIN:function(e){return Object.assign(e,{\"on:begin\":(e,t)=>{t.data._beginMatch=e[1]},\"on:end\":(e,t)=>{t.data._beginMatch!==e[1]&&t.ignoreMatch()}})},HASH_COMMENT_MODE:w,IDENT_RE:E,MATCH_NOTHING_RE:/\\b\\B/,METHOD_GUARD:L,NUMBER_MODE:I,NUMBER_RE:_,PHRASAL_WORDS_MODE:{begin:/\\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\\b/},QUOTE_STRING_MODE:A,REGEXP_MODE:D,RE_STARTERS_RE:\"!|!=|!==|%|%=|&|&&|&=|\\\\*|\\\\*=|\\\\+|\\\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\\\?|\\\\[|\\\\{|\\\\(|\\\\^|\\\\^=|\\\\||\\\\|=|\\\\|\\\\||~\",SHEBANG:(e={})=>{const t=/^#![ ]*\\//;return e.binary&&(e.begin=h(t,/.*\\b/,e.binary,/\\b.*/)),r({scope:\"meta\",begin:t,end:/$/,relevance:0,\"on:begin\":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)},TITLE_MODE:M,UNDERSCORE_IDENT_RE:y,UNDERSCORE_TITLE_MODE:P});function B(e,t){\".\"===e.input[e.index-1]&&t.ignoreMatch()}function U(e,t){void 0!==e.className&&(e.scope=e.className,delete e.className)}function H(e,t){t&&e.beginKeywords&&(e.begin=\"\\\\b(\"+e.beginKeywords.split(\" \").join(\"|\")+\")(?!\\\\.)(?=\\\\b|\\\\s)\",e.__beforeBegin=B,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,void 0===e.relevance&&(e.relevance=0))}function z(e,t){Array.isArray(e.illegal)&&(e.illegal=f(...e.illegal))}function G(e,t){if(e.match){if(e.begin||e.end)throw new Error(\"begin & end are not supported with match\");e.begin=e.match,delete e.match}}function j(e,t){void 0===e.relevance&&(e.relevance=1)}const $=(e,t)=>{if(!e.beforeMatch)return;if(e.starts)throw new Error(\"beforeMatch cannot be used with starts\");const n=Object.assign({},e);Object.keys(e).forEach(t=>{delete e[t]}),e.keywords=n.keywords,e.begin=h(n.beforeMatch,u(n.begin)),e.starts={relevance:0,contains:[Object.assign(n,{endsParent:!0})]},e.relevance=0,delete n.beforeMatch},q=[\"of\",\"and\",\"for\",\"in\",\"not\",\"or\",\"if\",\"then\",\"parent\",\"list\",\"value\"];function Y(e,t,n=\"keyword\"){const r=Object.create(null);return\"string\"==typeof e?a(n,e.split(\" \")):Array.isArray(e)?a(n,e):Object.keys(e).forEach(function(n){Object.assign(r,Y(e[n],t,n))}),r;function a(e,n){t&&(n=n.map(e=>e.toLowerCase())),n.forEach(function(t){const n=t.split(\"|\");r[n[0]]=[e,W(n[0],n[1])]})}}function W(e,t){return t?Number(t):function(e){return q.includes(e.toLowerCase())}(e)?0:1}const V={},K=(e,t)=>{V[`${e}/${t}`]||(V[`${e}/${t}`]=!0)},Q=new Error;function X(e,t,{key:n}){let r=0;const a=e[n],i={},s={};for(let o=1;o<=t.length;o++)s[o+r]=a[o],i[o+r]=!0,r+=m(t[o-1]);e[n]=s,e[n]._emit=i,e[n]._multi=!0}function Z(e){!function(e){e.scope&&\"object\"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope,delete e.scope)}(e),\"string\"==typeof e.beginScope&&(e.beginScope={_wrap:e.beginScope}),\"string\"==typeof e.endScope&&(e.endScope={_wrap:e.endScope}),function(e){if(Array.isArray(e.begin)){if(e.skip||e.excludeBegin||e.returnBegin)throw Q;if(\"object\"!=typeof e.beginScope||null===e.beginScope)throw Q;X(e,e.begin,{key:\"beginScope\"}),e.begin=b(e.begin,{joinWith:\"\"})}}(e),function(e){if(Array.isArray(e.end)){if(e.skip||e.excludeEnd||e.returnEnd)throw Q;if(\"object\"!=typeof e.endScope||null===e.endScope)throw Q;X(e,e.end,{key:\"endScope\"}),e.end=b(e.end,{joinWith:\"\"})}}(e)}function J(e){function t(t,n){return new RegExp(c(t),\"m\"+(e.case_insensitive?\"i\":\"\")+(e.unicodeRegex?\"u\":\"\")+(n?\"g\":\"\"))}class n{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,t){t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]),this.matchAt+=m(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map(e=>e[1]);this.matcherRe=t(b(e,{joinWith:\"|\"}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const t=this.matcherRe.exec(e);if(!t)return null;const n=t.findIndex((e,t)=>t>0&&void 0!==e),r=this.matchIndexes[n];return t.splice(0,n),Object.assign(t,r)}}class a{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n;return this.rules.slice(e).forEach(([e,n])=>t.addRule(e,n)),t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){this.rules.push([e,t]),\"begin\"===t.type&&this.count++}exec(e){const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex;let n=t.exec(e);if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)}return n&&(this.regexIndex+=n.position+1,this.regexIndex===this.count&&this.considerAll()),n}}if(e.compilerExtensions||(e.compilerExtensions=[]),e.contains&&e.contains.includes(\"self\"))throw new Error(\"ERR: contains `self` is not supported at the top-level of a language.  See documentation.\");return e.classNameAliases=r(e.classNameAliases||{}),function n(i,s){const o=i;if(i.isCompiled)return o;[U,G,Z,$].forEach(e=>e(i,s)),e.compilerExtensions.forEach(e=>e(i,s)),i.__beforeBegin=null,[H,z,j].forEach(e=>e(i,s)),i.isCompiled=!0;let l=null;return\"object\"==typeof i.keywords&&i.keywords.$pattern&&(i.keywords=Object.assign({},i.keywords),l=i.keywords.$pattern,delete i.keywords.$pattern),l=l||/\\w+/,i.keywords&&(i.keywords=Y(i.keywords,e.case_insensitive)),o.keywordPatternRe=t(l,!0),s&&(i.begin||(i.begin=/\\B|\\b/),o.beginRe=t(o.begin),i.end||i.endsWithParent||(i.end=/\\B|\\b/),i.end&&(o.endRe=t(o.end)),o.terminatorEnd=c(o.end)||\"\",i.endsWithParent&&s.terminatorEnd&&(o.terminatorEnd+=(i.end?\"|\":\"\")+s.terminatorEnd)),i.illegal&&(o.illegalRe=t(i.illegal)),i.contains||(i.contains=[]),i.contains=[].concat(...i.contains.map(function(e){return function(e){e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map(function(t){return r(e,{variants:null},t)}));if(e.cachedVariants)return e.cachedVariants;if(ee(e))return r(e,{starts:e.starts?r(e.starts):null});if(Object.isFrozen(e))return r(e);return e}(\"self\"===e?i:e)})),i.contains.forEach(function(e){n(e,o)}),i.starts&&n(i.starts,s),o.matcher=function(e){const t=new a;return e.contains.forEach(e=>t.addRule(e.begin,{rule:e,type:\"begin\"})),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:\"end\"}),e.illegal&&t.addRule(e.illegal,{type:\"illegal\"}),t}(o),o}(e)}function ee(e){return!!e&&(e.endsWithParent||ee(e.starts))}class te extends Error{constructor(e,t){super(e),this.name=\"HTMLInjectionError\",this.html=t}}const ne=n,re=r,ae=Symbol(\"nomatch\"),ie=function(n){const r=Object.create(null),a=Object.create(null),i=[];let s=!0;const o=\"Could not find the language '{}', did you forget to load/include a language module?\",c={disableAutodetect:!0,name:\"Plain text\",contains:[]};let m={ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\\blang(?:uage)?-([\\w-]+)\\b/i,classPrefix:\"hljs-\",cssSelector:\"pre code\",languages:null,__emitter:l};function g(e){return m.noHighlightRe.test(e)}function b(e,t,n){let r=\"\",a=\"\";\"object\"==typeof t?(r=e,n=t.ignoreIllegals,a=t.language):(K(\"10.7.0\",\"highlight(lang, code, ...args) has been deprecated.\"),K(\"10.7.0\",\"Please use highlight(code, options) instead.\\nhttps://github.com/highlightjs/highlight.js/issues/2277\"),a=e,r=t),void 0===n&&(n=!0);const i={code:r,language:a};N(\"before:highlight\",i);const s=i.result?i.result:E(i.language,i.code,n);return s.code=i.code,N(\"after:highlight\",s),s}function E(e,n,a,i){const l=Object.create(null);function c(e,t){return e.keywords[t]}function u(){if(!C.keywords)return void w.addText(I);let e=0;C.keywordPatternRe.lastIndex=0;let t=C.keywordPatternRe.exec(I),n=\"\";for(;t;){n+=I.substring(e,t.index);const r=v.case_insensitive?t[0].toLowerCase():t[0],a=c(C,r);if(a){const[e,i]=a;if(w.addText(n),n=\"\",l[r]=(l[r]||0)+1,l[r]<=7&&(O+=i),e.startsWith(\"_\"))n+=t[0];else{const n=v.classNameAliases[e]||e;p(t[0],n)}}else n+=t[0];e=C.keywordPatternRe.lastIndex,t=C.keywordPatternRe.exec(I)}n+=I.substring(e),w.addText(n)}function d(){null!=C.subLanguage?function(){if(\"\"===I)return;let e=null;if(\"string\"==typeof C.subLanguage){if(!r[C.subLanguage])return void w.addText(I);e=E(C.subLanguage,I,!0,x[C.subLanguage]),x[C.subLanguage]=e._top}else e=y(I,C.subLanguage.length?C.subLanguage:null);C.relevance>0&&(O+=e.relevance),w.__addSublanguage(e._emitter,e.language)}():u(),I=\"\"}function p(e,t){\"\"!==e&&(w.startScope(t),w.addText(e),w.endScope())}function h(e,t){let n=1;const r=t.length-1;for(;n<=r;){if(!e._emit[n]){n++;continue}const r=v.classNameAliases[e[n]]||e[n],a=t[n];r?p(a,r):(I=a,u(),I=\"\"),n++}}function f(e,t){return e.scope&&\"string\"==typeof e.scope&&w.openNode(v.classNameAliases[e.scope]||e.scope),e.beginScope&&(e.beginScope._wrap?(p(I,v.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap),I=\"\"):e.beginScope._multi&&(h(e.beginScope,t),I=\"\")),C=Object.create(e,{parent:{value:C}}),C}function g(e,n,r){let a=function(e,t){const n=e&&e.exec(t);return n&&0===n.index}(e.endRe,r);if(a){if(e[\"on:end\"]){const r=new t(e);e[\"on:end\"](n,r),r.isMatchIgnored&&(a=!1)}if(a){for(;e.endsParent&&e.parent;)e=e.parent;return e}}if(e.endsWithParent)return g(e.parent,n,r)}function b(e){return 0===C.matcher.regexIndex?(I+=e[0],1):(M=!0,0)}function _(e){const t=e[0],r=n.substring(e.index),a=g(C,e,r);if(!a)return ae;const i=C;C.endScope&&C.endScope._wrap?(d(),p(t,C.endScope._wrap)):C.endScope&&C.endScope._multi?(d(),h(C.endScope,e)):i.skip?I+=t:(i.returnEnd||i.excludeEnd||(I+=t),d(),i.excludeEnd&&(I=t));do{C.scope&&w.closeNode(),C.skip||C.subLanguage||(O+=C.relevance),C=C.parent}while(C!==a.parent);return a.starts&&f(a.starts,e),i.returnEnd?0:t.length}let k={};function T(r,i){const o=i&&i[0];if(I+=r,null==o)return d(),0;if(\"begin\"===k.type&&\"end\"===i.type&&k.index===i.index&&\"\"===o){if(I+=n.slice(i.index,i.index+1),!s){const t=new Error(`0 width match regex (${e})`);throw t.languageName=e,t.badRule=k.rule,t}return 1}if(k=i,\"begin\"===i.type)return function(e){const n=e[0],r=e.rule,a=new t(r),i=[r.__beforeBegin,r[\"on:begin\"]];for(const t of i)if(t&&(t(e,a),a.isMatchIgnored))return b(n);return r.skip?I+=n:(r.excludeBegin&&(I+=n),d(),r.returnBegin||r.excludeBegin||(I=n)),f(r,e),r.returnBegin?0:n.length}(i);if(\"illegal\"===i.type&&!a){const e=new Error('Illegal lexeme \"'+o+'\" for mode \"'+(C.scope||\"<unnamed>\")+'\"');throw e.mode=C,e}if(\"end\"===i.type){const e=_(i);if(e!==ae)return e}if(\"illegal\"===i.type&&\"\"===o)return I+=\"\\n\",1;if(D>1e5&&D>3*i.index){throw new Error(\"potential infinite loop, way more iterations than matches\")}return I+=o,o.length}const v=S(e);if(!v)throw o.replace(\"{}\",e),new Error('Unknown language: \"'+e+'\"');const A=J(v);let N=\"\",C=i||A;const x={},w=new m.__emitter(m);!function(){const e=[];for(let t=C;t!==v;t=t.parent)t.scope&&e.unshift(t.scope);e.forEach(e=>w.openNode(e))}();let I=\"\",O=0,R=0,D=0,M=!1;try{if(v.__emitTokens)v.__emitTokens(n,w);else{for(C.matcher.considerAll();;){D++,M?M=!1:C.matcher.considerAll(),C.matcher.lastIndex=R;const e=C.matcher.exec(n);if(!e)break;const t=T(n.substring(R,e.index),e);R=e.index+t}T(n.substring(R))}return w.finalize(),N=w.toHTML(),{language:e,value:N,relevance:O,illegal:!1,_emitter:w,_top:C}}catch(P){if(P.message&&P.message.includes(\"Illegal\"))return{language:e,value:ne(n),illegal:!0,relevance:0,_illegalBy:{message:P.message,index:R,context:n.slice(R-100,R+100),mode:P.mode,resultSoFar:N},_emitter:w};if(s)return{language:e,value:ne(n),illegal:!1,relevance:0,errorRaised:P,_emitter:w,_top:C};throw P}}function y(e,t){t=t||m.languages||Object.keys(r);const n=function(e){const t={value:ne(e),illegal:!1,relevance:0,_top:c,_emitter:new m.__emitter(m)};return t._emitter.addText(e),t}(e),a=t.filter(S).filter(A).map(t=>E(t,e,!1));a.unshift(n);const i=a.sort((e,t)=>{if(e.relevance!==t.relevance)return t.relevance-e.relevance;if(e.language&&t.language){if(S(e.language).supersetOf===t.language)return 1;if(S(t.language).supersetOf===e.language)return-1}return 0}),[s,o]=i,l=s;return l.secondBest=o,l}function _(e){let t=null;const n=function(e){let t=e.className+\" \";t+=e.parentNode?e.parentNode.className:\"\";const n=m.languageDetectRe.exec(t);if(n){const e=S(n[1]);return e||o.replace(\"{}\",n[1]),e?n[1]:\"no-highlight\"}return t.split(/\\s+/).find(e=>g(e)||S(e))}(e);if(g(n))return;if(N(\"before:highlightElement\",{el:e,language:n}),e.dataset.highlighted)return;if(e.children.length>0&&(m.ignoreUnescapedHTML,m.throwUnescapedHTML)){throw new te(\"One of your code blocks includes unescaped HTML.\",e.innerHTML)}t=e;const r=t.textContent,i=n?b(r,{language:n,ignoreIllegals:!0}):y(r);e.innerHTML=i.value,e.dataset.highlighted=\"yes\",function(e,t,n){const r=t&&a[t]||n;e.classList.add(\"hljs\"),e.classList.add(`language-${r}`)}(e,n,i.language),e.result={language:i.language,re:i.relevance,relevance:i.relevance},i.secondBest&&(e.secondBest={language:i.secondBest.language,relevance:i.secondBest.relevance}),N(\"after:highlightElement\",{el:e,result:i,text:r})}let k=!1;function T(){if(\"loading\"===document.readyState)return k||window.addEventListener(\"DOMContentLoaded\",function(){T()},!1),void(k=!0);document.querySelectorAll(m.cssSelector).forEach(_)}function S(e){return e=(e||\"\").toLowerCase(),r[e]||r[a[e]]}function v(e,{languageName:t}){\"string\"==typeof e&&(e=[e]),e.forEach(e=>{a[e.toLowerCase()]=t})}function A(e){const t=S(e);return t&&!t.disableAutodetect}function N(e,t){const n=e;i.forEach(function(e){e[n]&&e[n](t)})}Object.assign(n,{highlight:b,highlightAuto:y,highlightAll:T,highlightElement:_,highlightBlock:function(e){return K(\"10.7.0\",\"highlightBlock will be removed entirely in v12.0\"),K(\"10.7.0\",\"Please use highlightElement now.\"),_(e)},configure:function(e){m=re(m,e)},initHighlighting:()=>{T(),K(\"10.6.0\",\"initHighlighting() deprecated.  Use highlightAll() now.\")},initHighlightingOnLoad:function(){T(),K(\"10.6.0\",\"initHighlightingOnLoad() deprecated.  Use highlightAll() now.\")},registerLanguage:function(e,t){let a=null;try{a=t(n)}catch(i){if(\"Language definition for '{}' could not be registered.\".replace(\"{}\",e),!s)throw i;a=c}a.name||(a.name=e),r[e]=a,a.rawDefinition=t.bind(null,n),a.aliases&&v(a.aliases,{languageName:e})},unregisterLanguage:function(e){delete r[e];for(const t of Object.keys(a))a[t]===e&&delete a[t]},listLanguages:function(){return Object.keys(r)},getLanguage:S,registerAliases:v,autoDetection:A,inherit:re,addPlugin:function(e){!function(e){e[\"before:highlightBlock\"]&&!e[\"before:highlightElement\"]&&(e[\"before:highlightElement\"]=t=>{e[\"before:highlightBlock\"](Object.assign({block:t.el},t))}),e[\"after:highlightBlock\"]&&!e[\"after:highlightElement\"]&&(e[\"after:highlightElement\"]=t=>{e[\"after:highlightBlock\"](Object.assign({block:t.el},t))})}(e),i.push(e)},removePlugin:function(e){const t=i.indexOf(e);-1!==t&&i.splice(t,1)}}),n.debugMode=function(){s=!1},n.safeMode=function(){s=!0},n.versionString=\"11.11.1\",n.regex={concat:h,lookahead:u,either:f,optional:p,anyNumberOfTimes:d};for(const t in F)\"object\"==typeof F[t]&&e(F[t]);return Object.assign(n,F),n},se=ie({});return se.newInstance=()=>ie({}),es=se,se.HighlightJS=se,se.default=se,es}const rs=r(ns()),as={};class is{constructor(e){this.options=e,this.root={type:\"root\",children:[],data:{language:void 0,relevance:0}},this.stack=[this.root]}addText(e){if(\"\"===e)return;const t=this.stack[this.stack.length-1],n=t.children[t.children.length-1];n&&\"text\"===n.type?n.value+=e:t.children.push({type:\"text\",value:e})}startScope(e){this.openNode(String(e))}endScope(){this.closeNode()}__addSublanguage(e,t){const n=this.stack[this.stack.length-1],r=e.root.children;t?n.children.push({type:\"element\",tagName:\"span\",properties:{className:[t]},children:r}):n.children.push(...r)}openNode(e){const t=this,n={type:\"element\",tagName:\"span\",properties:{className:e.split(\".\").map(function(e,n){return n?e+\"_\".repeat(n):t.options.classPrefix+e})},children:[]};this.stack[this.stack.length-1].children.push(n),this.stack.push(n)}closeNode(){this.stack.pop()}finalize(){}toHTML(){return\"\"}}const ss={};const os=/[#.]/g;function ls(e,t,n){const r=n?function(e){const t=new Map;for(const n of e)t.set(n.toLowerCase(),n);return t}(n):void 0;return function(n,a,...i){let s;if(null==n){s={type:\"root\",children:[]};const e=a;i.unshift(e)}else{s=function(e,t){const n=e||\"\",r={};let a,i,s=0;for(;s<n.length;){os.lastIndex=s;const e=os.exec(n),t=n.slice(s,e?e.index:n.length);t&&(a?\"#\"===a?r.id=t:Array.isArray(r.className)?r.className.push(t):r.className=[t]:i=t,s+=t.length),e&&(a=e[0],s++)}return{type:\"element\",tagName:i||t||\"div\",properties:r,children:[]}}(n,t);const o=s.tagName.toLowerCase(),l=r?r.get(o):void 0;if(s.tagName=l||o,function(e){if(null===e||\"object\"!=typeof e||Array.isArray(e))return!0;if(\"string\"!=typeof e.type)return!1;const t=e,n=Object.keys(e);for(const r of n){const e=t[r];if(e&&\"object\"==typeof e){if(!Array.isArray(e))return!0;const t=e;for(const e of t)if(\"number\"!=typeof e&&\"string\"!=typeof e)return!0}}if(\"children\"in e&&Array.isArray(e.children))return!0;return!1}(a))i.unshift(a);else for(const[t,n]of Object.entries(a))cs(e,s.properties,t,n)}for(const e of i)us(s.children,e);return\"element\"===s.type&&\"template\"===s.tagName&&(s.content={type:\"root\",children:s.children},s.children=[]),s}}function cs(e,t,n,r){const a=Ee(e,n);let i;if(null!=r){if(\"number\"==typeof r){if(Number.isNaN(r))return;i=r}else i=\"boolean\"==typeof r?r:\"string\"==typeof r?a.spaceSeparated?Se(r):a.commaSeparated?M(r):a.commaOrSpaceSeparated?Se(M(r).join(\" \")):ds(a,a.property,r):Array.isArray(r)?[...r]:\"style\"===a.property?function(e){const t=[];for(const[n,r]of Object.entries(e))t.push([n,r].join(\": \"));return t.join(\"; \")}(r):String(r);if(Array.isArray(i)){const e=[];for(const t of i)e.push(ds(a,a.property,t));i=e}\"className\"===a.property&&Array.isArray(t.className)&&(i=t.className.concat(i)),t[a.property]=i}}function us(e,t){if(null==t);else if(\"number\"==typeof t||\"string\"==typeof t)e.push({type:\"text\",value:String(t)});else if(Array.isArray(t))for(const n of t)us(e,n);else{if(\"object\"!=typeof t||!(\"type\"in t))throw new Error(\"Expected node, nodes, or string, got `\"+t+\"`\");\"root\"===t.type?us(e,t.children):e.push(t)}}function ds(e,t,n){if(\"string\"==typeof n){if(e.number&&n&&!Number.isNaN(Number(n)))return Number(n);if((e.boolean||e.overloadedBoolean)&&(\"\"===n||$(n)===$(t)))return!0}return n}const ps=ls(ke,\"div\"),hs=ls(Te,\"g\",[\"altGlyph\",\"altGlyphDef\",\"altGlyphItem\",\"animateColor\",\"animateMotion\",\"animateTransform\",\"clipPath\",\"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\",\"foreignObject\",\"glyphRef\",\"linearGradient\",\"radialGradient\",\"solidColor\",\"textArea\",\"textPath\"]);function fs(e,t){const n=e.indexOf(\"\\r\",t),r=e.indexOf(\"\\n\",t);return-1===r?n:-1===n||n+1===r?r:n<r?n:r}const ms={html:\"http://www.w3.org/1999/xhtml\",mathml:\"http://www.w3.org/1998/Math/MathML\",svg:\"http://www.w3.org/2000/svg\",xlink:\"http://www.w3.org/1999/xlink\",xml:\"http://www.w3.org/XML/1998/namespace\",xmlns:\"http://www.w3.org/2000/xmlns/\"},gs={}.hasOwnProperty,bs=Object.prototype;function Es(e,t){let n;switch(t.nodeName){case\"#comment\":{const r=t;return n={type:\"comment\",value:r.data},_s(e,r,n),n}case\"#document\":case\"#document-fragment\":{const r=t,a=\"mode\"in r&&(\"quirks\"===r.mode||\"limited-quirks\"===r.mode);if(n={type:\"root\",children:ys(e,t.childNodes),data:{quirksMode:a}},e.file&&e.location){const t=String(e.file),r=function(e){const t=String(e),n=[];return{toOffset:function(e){if(e&&\"number\"==typeof e.line&&\"number\"==typeof e.column&&!Number.isNaN(e.line)&&!Number.isNaN(e.column)){for(;n.length<e.line;){const e=n[n.length-1],r=fs(t,e),a=-1===r?t.length+1:r+1;if(e===a)break;n.push(a)}const r=(e.line>1?n[e.line-2]:0)+e.column-1;if(r<n[e.line-1])return r}},toPoint:function(e){if(\"number\"==typeof e&&e>-1&&e<=t.length){let r=0;for(;;){let a=n[r];if(void 0===a){const e=fs(t,n[r-1]);a=-1===e?t.length+1:e+1,n[r]=a}if(a>e)return{line:r+1,column:e-(r>0?n[r-1]:0)+1,offset:e};r++}}}}}(t),a=r.toPoint(0),i=r.toPoint(t.length);n.position={start:a,end:i}}return n}case\"#documentType\":return n={type:\"doctype\"},_s(e,t,n),n;case\"#text\":{const r=t;return n={type:\"text\",value:r.value},_s(e,r,n),n}default:return n=function(e,t){const n=e.schema;e.schema=t.namespaceURI===ms.svg?Te:ke;let r=-1;const a={};for(;++r<t.attrs.length;){const e=t.attrs[r],n=(e.prefix?e.prefix+\":\":\"\")+e.name;gs.call(bs,n)||(a[n]=e.value)}const i=\"svg\"===e.schema.space?hs:ps,s=i(t.tagName,a,ys(e,t.childNodes));if(_s(e,t,s),\"template\"===s.tagName){const n=t,r=n.sourceCodeLocation,a=r&&r.startTag&&ks(r.startTag),i=r&&r.endTag&&ks(r.endTag),o=Es(e,n.content);a&&i&&e.file&&(o.position={start:a.end,end:i.start}),s.content=o}return e.schema=n,s}(e,t),n}}function ys(e,t){let n=-1;const r=[];for(;++n<t.length;){const a=Es(e,t[n]);r.push(a)}return r}function _s(e,t,n){if(\"sourceCodeLocation\"in t&&t.sourceCodeLocation&&e.file){const r=function(e,t,n){const r=ks(n);if(\"element\"===t.type){const a=t.children[t.children.length-1];if(r&&!n.endTag&&a&&a.position&&a.position.end&&(r.end=Object.assign({},a.position.end)),e.verbose){const r={};let a;if(n.attrs)for(a in n.attrs)gs.call(n.attrs,a)&&(r[Ee(e.schema,a).property]=ks(n.attrs[a]));n.startTag;const i=ks(n.startTag),s=n.endTag?ks(n.endTag):void 0,o={opening:i};s&&(o.closing=s),o.properties=r,t.data={position:o}}}return r}(e,n,t.sourceCodeLocation);r&&(e.location=!0,n.position=r)}}function ks(e){const t=Ts({line:e.startLine,column:e.startCol,offset:e.startOffset}),n=Ts({line:e.endLine,column:e.endCol,offset:e.endOffset});return t||n?{start:t,end:n}:void 0}function Ts(e){return e.line&&e.column?e:void 0}const Ss={}.hasOwnProperty;function vs(e,t){const n=t||{};function r(t,...n){let a=r.invalid;const i=r.handlers;if(t&&Ss.call(t,e)){const n=String(t[e]);a=Ss.call(i,n)?i[n]:r.unknown}if(a)return a.call(this,t,...n)}return r.handlers=n.handlers||{},r.invalid=n.invalid,r.unknown=n.unknown,r}const As={},Ns={}.hasOwnProperty,Cs=vs(\"type\",{handlers:{root:function(e,t){const n={nodeName:\"#document\",mode:(e.data||{}).quirksMode?\"quirks\":\"no-quirks\",childNodes:[]};return n.childNodes=ws(e.children,n,t),Is(e,n),n},element:function(e,t){const n=t;let r=n;\"element\"===e.type&&\"svg\"===e.tagName.toLowerCase()&&\"html\"===n.space&&(r=Te);const a=[];let i;if(e.properties)for(i in e.properties)if(\"children\"!==i&&Ns.call(e.properties,i)){const t=xs(r,i,e.properties[i]);t&&a.push(t)}const s=r.space,o={nodeName:e.tagName,tagName:e.tagName,attrs:a,namespaceURI:ms[s],childNodes:[],parentNode:null};o.childNodes=ws(e.children,o,r),Is(e,o),\"template\"===e.tagName&&e.content&&(o.content=function(e,t){const n={nodeName:\"#document-fragment\",childNodes:[]};return n.childNodes=ws(e.children,n,t),Is(e,n),n}(e.content,r));return o},text:function(e){const t={nodeName:\"#text\",value:e.value,parentNode:null};return Is(e,t),t},comment:function(e){const t={nodeName:\"#comment\",data:e.value,parentNode:null};return Is(e,t),t},doctype:function(e){const t={nodeName:\"#documentType\",name:\"html\",publicId:\"\",systemId:\"\",parentNode:null};return Is(e,t),t}}});function xs(e,t,n){const r=Ee(e,t);if(!1===n||null==n||\"number\"==typeof n&&Number.isNaN(n)||!n&&r.boolean)return;Array.isArray(n)&&(n=r.commaSeparated?P(n):ve(n));const a={name:r.attribute,value:!0===n?\"\":String(n)};if(r.space&&\"html\"!==r.space&&\"svg\"!==r.space){const e=a.name.indexOf(\":\");e<0?a.prefix=\"\":(a.name=a.name.slice(e+1),a.prefix=r.attribute.slice(0,e)),a.namespace=ms[r.space]}return a}function ws(e,t,n){let r=-1;const a=[];if(e)for(;++r<e.length;){const i=Cs(e[r],n);i.parentNode=t,a.push(i)}return a}function Is(e,t){const n=e.position;n&&n.start&&n.end&&(n.start.offset,n.end.offset,t.sourceCodeLocation={startLine:n.start.line,startCol:n.start.column,startOffset:n.start.offset,endLine:n.end.line,endCol:n.end.column,endOffset:n.end.offset})}const Os=[\"area\",\"base\",\"basefont\",\"bgsound\",\"br\",\"col\",\"command\",\"embed\",\"frame\",\"hr\",\"image\",\"img\",\"input\",\"keygen\",\"link\",\"meta\",\"param\",\"source\",\"track\",\"wbr\"],Rs=new Set([65534,65535,131070,131071,196606,196607,262142,262143,327678,327679,393214,393215,458750,458751,524286,524287,589822,589823,655358,655359,720894,720895,786430,786431,851966,851967,917502,917503,983038,983039,1048574,1048575,1114110,1114111]),Ds=\"�\";var Ms,Ps;(Ps=Ms||(Ms={}))[Ps.EOF=-1]=\"EOF\",Ps[Ps.NULL=0]=\"NULL\",Ps[Ps.TABULATION=9]=\"TABULATION\",Ps[Ps.CARRIAGE_RETURN=13]=\"CARRIAGE_RETURN\",Ps[Ps.LINE_FEED=10]=\"LINE_FEED\",Ps[Ps.FORM_FEED=12]=\"FORM_FEED\",Ps[Ps.SPACE=32]=\"SPACE\",Ps[Ps.EXCLAMATION_MARK=33]=\"EXCLAMATION_MARK\",Ps[Ps.QUOTATION_MARK=34]=\"QUOTATION_MARK\",Ps[Ps.AMPERSAND=38]=\"AMPERSAND\",Ps[Ps.APOSTROPHE=39]=\"APOSTROPHE\",Ps[Ps.HYPHEN_MINUS=45]=\"HYPHEN_MINUS\",Ps[Ps.SOLIDUS=47]=\"SOLIDUS\",Ps[Ps.DIGIT_0=48]=\"DIGIT_0\",Ps[Ps.DIGIT_9=57]=\"DIGIT_9\",Ps[Ps.SEMICOLON=59]=\"SEMICOLON\",Ps[Ps.LESS_THAN_SIGN=60]=\"LESS_THAN_SIGN\",Ps[Ps.EQUALS_SIGN=61]=\"EQUALS_SIGN\",Ps[Ps.GREATER_THAN_SIGN=62]=\"GREATER_THAN_SIGN\",Ps[Ps.QUESTION_MARK=63]=\"QUESTION_MARK\",Ps[Ps.LATIN_CAPITAL_A=65]=\"LATIN_CAPITAL_A\",Ps[Ps.LATIN_CAPITAL_Z=90]=\"LATIN_CAPITAL_Z\",Ps[Ps.RIGHT_SQUARE_BRACKET=93]=\"RIGHT_SQUARE_BRACKET\",Ps[Ps.GRAVE_ACCENT=96]=\"GRAVE_ACCENT\",Ps[Ps.LATIN_SMALL_A=97]=\"LATIN_SMALL_A\",Ps[Ps.LATIN_SMALL_Z=122]=\"LATIN_SMALL_Z\";const Ls=\"--\",Fs=\"[CDATA[\",Bs=\"doctype\",Us=\"script\",Hs=\"public\",zs=\"system\";function Gs(e){return e>=55296&&e<=57343}function js(e){return 32!==e&&10!==e&&13!==e&&9!==e&&12!==e&&e>=1&&e<=31||e>=127&&e<=159}function $s(e){return e>=64976&&e<=65007||Rs.has(e)}var qs,Ys;(Ys=qs||(qs={})).controlCharacterInInputStream=\"control-character-in-input-stream\",Ys.noncharacterInInputStream=\"noncharacter-in-input-stream\",Ys.surrogateInInputStream=\"surrogate-in-input-stream\",Ys.nonVoidHtmlElementStartTagWithTrailingSolidus=\"non-void-html-element-start-tag-with-trailing-solidus\",Ys.endTagWithAttributes=\"end-tag-with-attributes\",Ys.endTagWithTrailingSolidus=\"end-tag-with-trailing-solidus\",Ys.unexpectedSolidusInTag=\"unexpected-solidus-in-tag\",Ys.unexpectedNullCharacter=\"unexpected-null-character\",Ys.unexpectedQuestionMarkInsteadOfTagName=\"unexpected-question-mark-instead-of-tag-name\",Ys.invalidFirstCharacterOfTagName=\"invalid-first-character-of-tag-name\",Ys.unexpectedEqualsSignBeforeAttributeName=\"unexpected-equals-sign-before-attribute-name\",Ys.missingEndTagName=\"missing-end-tag-name\",Ys.unexpectedCharacterInAttributeName=\"unexpected-character-in-attribute-name\",Ys.unknownNamedCharacterReference=\"unknown-named-character-reference\",Ys.missingSemicolonAfterCharacterReference=\"missing-semicolon-after-character-reference\",Ys.unexpectedCharacterAfterDoctypeSystemIdentifier=\"unexpected-character-after-doctype-system-identifier\",Ys.unexpectedCharacterInUnquotedAttributeValue=\"unexpected-character-in-unquoted-attribute-value\",Ys.eofBeforeTagName=\"eof-before-tag-name\",Ys.eofInTag=\"eof-in-tag\",Ys.missingAttributeValue=\"missing-attribute-value\",Ys.missingWhitespaceBetweenAttributes=\"missing-whitespace-between-attributes\",Ys.missingWhitespaceAfterDoctypePublicKeyword=\"missing-whitespace-after-doctype-public-keyword\",Ys.missingWhitespaceBetweenDoctypePublicAndSystemIdentifiers=\"missing-whitespace-between-doctype-public-and-system-identifiers\",Ys.missingWhitespaceAfterDoctypeSystemKeyword=\"missing-whitespace-after-doctype-system-keyword\",Ys.missingQuoteBeforeDoctypePublicIdentifier=\"missing-quote-before-doctype-public-identifier\",Ys.missingQuoteBeforeDoctypeSystemIdentifier=\"missing-quote-before-doctype-system-identifier\",Ys.missingDoctypePublicIdentifier=\"missing-doctype-public-identifier\",Ys.missingDoctypeSystemIdentifier=\"missing-doctype-system-identifier\",Ys.abruptDoctypePublicIdentifier=\"abrupt-doctype-public-identifier\",Ys.abruptDoctypeSystemIdentifier=\"abrupt-doctype-system-identifier\",Ys.cdataInHtmlContent=\"cdata-in-html-content\",Ys.incorrectlyOpenedComment=\"incorrectly-opened-comment\",Ys.eofInScriptHtmlCommentLikeText=\"eof-in-script-html-comment-like-text\",Ys.eofInDoctype=\"eof-in-doctype\",Ys.nestedComment=\"nested-comment\",Ys.abruptClosingOfEmptyComment=\"abrupt-closing-of-empty-comment\",Ys.eofInComment=\"eof-in-comment\",Ys.incorrectlyClosedComment=\"incorrectly-closed-comment\",Ys.eofInCdata=\"eof-in-cdata\",Ys.absenceOfDigitsInNumericCharacterReference=\"absence-of-digits-in-numeric-character-reference\",Ys.nullCharacterReference=\"null-character-reference\",Ys.surrogateCharacterReference=\"surrogate-character-reference\",Ys.characterReferenceOutsideUnicodeRange=\"character-reference-outside-unicode-range\",Ys.controlCharacterReference=\"control-character-reference\",Ys.noncharacterCharacterReference=\"noncharacter-character-reference\",Ys.missingWhitespaceBeforeDoctypeName=\"missing-whitespace-before-doctype-name\",Ys.missingDoctypeName=\"missing-doctype-name\",Ys.invalidCharacterSequenceAfterDoctypeName=\"invalid-character-sequence-after-doctype-name\",Ys.duplicateAttribute=\"duplicate-attribute\",Ys.nonConformingDoctype=\"non-conforming-doctype\",Ys.missingDoctype=\"missing-doctype\",Ys.misplacedDoctype=\"misplaced-doctype\",Ys.endTagWithoutMatchingOpenElement=\"end-tag-without-matching-open-element\",Ys.closingOfElementWithOpenChildElements=\"closing-of-element-with-open-child-elements\",Ys.disallowedContentInNoscriptInHead=\"disallowed-content-in-noscript-in-head\",Ys.openElementsLeftAfterEof=\"open-elements-left-after-eof\",Ys.abandonedHeadElementChild=\"abandoned-head-element-child\",Ys.misplacedStartTagForHeadElement=\"misplaced-start-tag-for-head-element\",Ys.nestedNoscriptInHead=\"nested-noscript-in-head\",Ys.eofInElementThatCanContainOnlyText=\"eof-in-element-that-can-contain-only-text\";class Ws{constructor(e){this.handler=e,this.html=\"\",this.pos=-1,this.lastGapPos=-2,this.gapStack=[],this.skipNextNewLine=!1,this.lastChunkWritten=!1,this.endOfChunkHit=!1,this.bufferWaterline=65536,this.isEol=!1,this.lineStartPos=0,this.droppedBufferSize=0,this.line=1,this.lastErrOffset=-1}get col(){return this.pos-this.lineStartPos+Number(this.lastGapPos!==this.pos)}get offset(){return this.droppedBufferSize+this.pos}getError(e,t){const{line:n,col:r,offset:a}=this,i=r+t,s=a+t;return{code:e,startLine:n,endLine:n,startCol:i,endCol:i,startOffset:s,endOffset:s}}_err(e){this.handler.onParseError&&this.lastErrOffset!==this.offset&&(this.lastErrOffset=this.offset,this.handler.onParseError(this.getError(e,0)))}_addGap(){this.gapStack.push(this.lastGapPos),this.lastGapPos=this.pos}_processSurrogate(e){if(this.pos!==this.html.length-1){const t=this.html.charCodeAt(this.pos+1);if(function(e){return e>=56320&&e<=57343}(t))return this.pos++,this._addGap(),1024*(e-55296)+9216+t}else if(!this.lastChunkWritten)return this.endOfChunkHit=!0,Ms.EOF;return this._err(qs.surrogateInInputStream),e}willDropParsedChunk(){return this.pos>this.bufferWaterline}dropParsedChunk(){this.willDropParsedChunk()&&(this.html=this.html.substring(this.pos),this.lineStartPos-=this.pos,this.droppedBufferSize+=this.pos,this.pos=0,this.lastGapPos=-2,this.gapStack.length=0)}write(e,t){this.html.length>0?this.html+=e:this.html=e,this.endOfChunkHit=!1,this.lastChunkWritten=t}insertHtmlAtCurrentPos(e){this.html=this.html.substring(0,this.pos+1)+e+this.html.substring(this.pos+1),this.endOfChunkHit=!1}startsWith(e,t){if(this.pos+e.length>this.html.length)return this.endOfChunkHit=!this.lastChunkWritten,!1;if(t)return this.html.startsWith(e,this.pos);for(let n=0;n<e.length;n++){if((32|this.html.charCodeAt(this.pos+n))!==e.charCodeAt(n))return!1}return!0}peek(e){const t=this.pos+e;if(t>=this.html.length)return this.endOfChunkHit=!this.lastChunkWritten,Ms.EOF;const n=this.html.charCodeAt(t);return n===Ms.CARRIAGE_RETURN?Ms.LINE_FEED:n}advance(){if(this.pos++,this.isEol&&(this.isEol=!1,this.line++,this.lineStartPos=this.pos),this.pos>=this.html.length)return this.endOfChunkHit=!this.lastChunkWritten,Ms.EOF;let e=this.html.charCodeAt(this.pos);if(e===Ms.CARRIAGE_RETURN)return this.isEol=!0,this.skipNextNewLine=!0,Ms.LINE_FEED;if(e===Ms.LINE_FEED&&(this.isEol=!0,this.skipNextNewLine))return this.line--,this.skipNextNewLine=!1,this._addGap(),this.advance();this.skipNextNewLine=!1,Gs(e)&&(e=this._processSurrogate(e));return null===this.handler.onParseError||e>31&&e<127||e===Ms.LINE_FEED||e===Ms.CARRIAGE_RETURN||e>159&&e<64976||this._checkForProblematicCharacters(e),e}_checkForProblematicCharacters(e){js(e)?this._err(qs.controlCharacterInInputStream):$s(e)&&this._err(qs.noncharacterInInputStream)}retreat(e){for(this.pos-=e;this.pos<this.lastGapPos;)this.lastGapPos=this.gapStack.pop(),this.pos--;this.isEol=!1}}var Vs,Ks;function Qs(e,t){for(let n=e.attrs.length-1;n>=0;n--)if(e.attrs[n].name===t)return e.attrs[n].value;return null}(Ks=Vs||(Vs={}))[Ks.CHARACTER=0]=\"CHARACTER\",Ks[Ks.NULL_CHARACTER=1]=\"NULL_CHARACTER\",Ks[Ks.WHITESPACE_CHARACTER=2]=\"WHITESPACE_CHARACTER\",Ks[Ks.START_TAG=3]=\"START_TAG\",Ks[Ks.END_TAG=4]=\"END_TAG\",Ks[Ks.COMMENT=5]=\"COMMENT\",Ks[Ks.DOCTYPE=6]=\"DOCTYPE\",Ks[Ks.EOF=7]=\"EOF\",Ks[Ks.HIBERNATION=8]=\"HIBERNATION\";const Xs=new Uint16Array('ᵁ<Õıʊҝջאٵ۞ޢߖࠏ੊ઑඡ๭༉༦჊ረዡᐕᒝᓃᓟᔥ\\0\\0\\0\\0\\0\\0ᕫᛍᦍᰒᷝ὾⁠↰⊍⏀⏻⑂⠤⤒ⴈ⹈⿎〖㊺㘹㞬㣾㨨㩱㫠㬮ࠀEMabcfglmnoprstu\\\\bfms¦³¹ÈÏlig耻Æ䃆P耻&䀦cute耻Á䃁reve;䄂Āiyx}rc耻Â䃂;䐐r;쀀𝔄rave耻À䃀pha;䎑acr;䄀d;橓Āgp¡on;䄄f;쀀𝔸plyFunction;恡ing耻Å䃅Ācs¾Ãr;쀀𝒜ign;扔ilde耻Ã䃃ml耻Ä䃄ЀaceforsuåûþėĜĢħĪĀcrêòkslash;或Ŷöø;櫧ed;挆y;䐑ƀcrtąċĔause;戵noullis;愬a;䎒r;쀀𝔅pf;쀀𝔹eve;䋘còēmpeq;扎܀HOacdefhilorsuōőŖƀƞƢƵƷƺǜȕɳɸɾcy;䐧PY耻©䂩ƀcpyŝŢźute;䄆Ā;iŧŨ拒talDifferentialD;慅leys;愭ȀaeioƉƎƔƘron;䄌dil耻Ç䃇rc;䄈nint;戰ot;䄊ĀdnƧƭilla;䂸terDot;䂷òſi;䎧rcleȀDMPTǇǋǑǖot;抙inus;抖lus;投imes;抗oĀcsǢǸkwiseContourIntegral;戲eCurlyĀDQȃȏoubleQuote;思uote;怙ȀlnpuȞȨɇɕonĀ;eȥȦ户;橴ƀgitȯȶȺruent;扡nt;戯ourIntegral;戮ĀfrɌɎ;愂oduct;成nterClockwiseContourIntegral;戳oss;樯cr;쀀𝒞pĀ;Cʄʅ拓ap;才րDJSZacefiosʠʬʰʴʸˋ˗ˡ˦̳ҍĀ;oŹʥtrahd;椑cy;䐂cy;䐅cy;䐏ƀgrsʿ˄ˇger;怡r;憡hv;櫤Āayː˕ron;䄎;䐔lĀ;t˝˞戇a;䎔r;쀀𝔇Āaf˫̧Ācm˰̢riticalȀADGT̖̜̀̆cute;䂴oŴ̋̍;䋙bleAcute;䋝rave;䁠ilde;䋜ond;拄ferentialD;慆Ѱ̽\\0\\0\\0͔͂\\0Ѕf;쀀𝔻ƀ;DE͈͉͍䂨ot;惜qual;扐blèCDLRUVͣͲ΂ϏϢϸontourIntegraìȹoɴ͹\\0\\0ͻ»͉nArrow;懓Āeo·ΤftƀARTΐΖΡrrow;懐ightArrow;懔eåˊngĀLRΫτeftĀARγιrrow;柸ightArrow;柺ightArrow;柹ightĀATϘϞrrow;懒ee;抨pɁϩ\\0\\0ϯrrow;懑ownArrow;懕erticalBar;戥ǹABLRTaВЪаўѿͼrrowƀ;BUНОТ憓ar;椓pArrow;懵reve;䌑eft˒к\\0ц\\0ѐightVector;楐eeVector;楞ectorĀ;Bљњ憽ar;楖ightǔѧ\\0ѱeeVector;楟ectorĀ;BѺѻ懁ar;楗eeĀ;A҆҇护rrow;憧ĀctҒҗr;쀀𝒟rok;䄐ࠀNTacdfglmopqstuxҽӀӄӋӞӢӧӮӵԡԯԶՒ՝ՠեG;䅊H耻Ð䃐cute耻É䃉ƀaiyӒӗӜron;䄚rc耻Ê䃊;䐭ot;䄖r;쀀𝔈rave耻È䃈ement;戈ĀapӺӾcr;䄒tyɓԆ\\0\\0ԒmallSquare;旻erySmallSquare;斫ĀgpԦԪon;䄘f;쀀𝔼silon;䎕uĀaiԼՉlĀ;TՂՃ橵ilde;扂librium;懌Āci՗՚r;愰m;橳a;䎗ml耻Ë䃋Āipժկsts;戃onentialE;慇ʀcfiosօֈ֍ֲ׌y;䐤r;쀀𝔉lledɓ֗\\0\\0֣mallSquare;旼erySmallSquare;斪Ͱֺ\\0ֿ\\0\\0ׄf;쀀𝔽All;戀riertrf;愱cò׋؀JTabcdfgorstר׬ׯ׺؀ؒؖ؛؝أ٬ٲcy;䐃耻>䀾mmaĀ;d׷׸䎓;䏜reve;䄞ƀeiy؇،ؐdil;䄢rc;䄜;䐓ot;䄠r;쀀𝔊;拙pf;쀀𝔾eater̀EFGLSTصلَٖٛ٦qualĀ;Lؾؿ扥ess;招ullEqual;执reater;檢ess;扷lantEqual;橾ilde;扳cr;쀀𝒢;扫ЀAacfiosuڅڋږڛڞڪھۊRDcy;䐪Āctڐڔek;䋇;䁞irc;䄤r;愌lbertSpace;愋ǰگ\\0ڲf;愍izontalLine;攀Āctۃۅòکrok;䄦mpńېۘownHumðįqual;扏܀EJOacdfgmnostuۺ۾܃܇܎ܚܞܡܨ݄ݸދޏޕcy;䐕lig;䄲cy;䐁cute耻Í䃍Āiyܓܘrc耻Î䃎;䐘ot;䄰r;愑rave耻Ì䃌ƀ;apܠܯܿĀcgܴܷr;䄪inaryI;慈lieóϝǴ݉\\0ݢĀ;eݍݎ戬Āgrݓݘral;戫section;拂isibleĀCTݬݲomma;恣imes;恢ƀgptݿރވon;䄮f;쀀𝕀a;䎙cr;愐ilde;䄨ǫޚ\\0ޞcy;䐆l耻Ï䃏ʀcfosuެ޷޼߂ߐĀiyޱ޵rc;䄴;䐙r;쀀𝔍pf;쀀𝕁ǣ߇\\0ߌr;쀀𝒥rcy;䐈kcy;䐄΀HJacfosߤߨ߽߬߱ࠂࠈcy;䐥cy;䐌ppa;䎚Āey߶߻dil;䄶;䐚r;쀀𝔎pf;쀀𝕂cr;쀀𝒦րJTaceflmostࠥࠩࠬࡐࡣ঳সে্਷ੇcy;䐉耻<䀼ʀcmnpr࠷࠼ࡁࡄࡍute;䄹bda;䎛g;柪lacetrf;愒r;憞ƀaeyࡗ࡜ࡡron;䄽dil;䄻;䐛Āfsࡨ॰tԀACDFRTUVarࡾࢩࢱࣦ࣠ࣼयज़ΐ४Ānrࢃ࢏gleBracket;柨rowƀ;BR࢙࢚࢞憐ar;懤ightArrow;懆eiling;挈oǵࢷ\\0ࣃbleBracket;柦nǔࣈ\\0࣒eeVector;楡ectorĀ;Bࣛࣜ懃ar;楙loor;挊ightĀAV࣯ࣵrrow;憔ector;楎Āerँगeƀ;AVउऊऐ抣rrow;憤ector;楚iangleƀ;BEतथऩ抲ar;槏qual;抴pƀDTVषूौownVector;楑eeVector;楠ectorĀ;Bॖॗ憿ar;楘ectorĀ;B॥०憼ar;楒ightáΜs̀EFGLSTॾঋকঝঢভqualGreater;拚ullEqual;扦reater;扶ess;檡lantEqual;橽ilde;扲r;쀀𝔏Ā;eঽা拘ftarrow;懚idot;䄿ƀnpw৔ਖਛgȀLRlr৞৷ਂਐeftĀAR০৬rrow;柵ightArrow;柷ightArrow;柶eftĀarγਊightáοightáϊf;쀀𝕃erĀLRਢਬeftArrow;憙ightArrow;憘ƀchtਾੀੂòࡌ;憰rok;䅁;扪Ѐacefiosuਗ਼੝੠੷੼અઋ઎p;椅y;䐜Ādl੥੯iumSpace;恟lintrf;愳r;쀀𝔐nusPlus;戓pf;쀀𝕄cò੶;䎜ҀJacefostuણધભીଔଙඑ඗ඞcy;䐊cute;䅃ƀaey઴હાron;䅇dil;䅅;䐝ƀgswે૰଎ativeƀMTV૓૟૨ediumSpace;怋hiĀcn૦૘ë૙eryThiî૙tedĀGL૸ଆreaterGreateòٳessLesóੈLine;䀊r;쀀𝔑ȀBnptଢନଷ଺reak;恠BreakingSpace;䂠f;愕ڀ;CDEGHLNPRSTV୕ୖ୪୼஡௫ఄ౞಄ದ೘ൡඅ櫬Āou୛୤ngruent;扢pCap;扭oubleVerticalBar;戦ƀlqxஃஊ஛ement;戉ualĀ;Tஒஓ扠ilde;쀀≂̸ists;戄reater΀;EFGLSTஶஷ஽௉௓௘௥扯qual;扱ullEqual;쀀≧̸reater;쀀≫̸ess;批lantEqual;쀀⩾̸ilde;扵umpń௲௽ownHump;쀀≎̸qual;쀀≏̸eĀfsఊధtTriangleƀ;BEచఛడ拪ar;쀀⧏̸qual;括s̀;EGLSTవశ఼ౄోౘ扮qual;扰reater;扸ess;쀀≪̸lantEqual;쀀⩽̸ilde;扴estedĀGL౨౹reaterGreater;쀀⪢̸essLess;쀀⪡̸recedesƀ;ESಒಓಛ技qual;쀀⪯̸lantEqual;拠ĀeiಫಹverseElement;戌ghtTriangleƀ;BEೋೌ೒拫ar;쀀⧐̸qual;拭ĀquೝഌuareSuĀbp೨೹setĀ;E೰ೳ쀀⊏̸qual;拢ersetĀ;Eഃആ쀀⊐̸qual;拣ƀbcpഓതൎsetĀ;Eഛഞ쀀⊂⃒qual;抈ceedsȀ;ESTലള഻െ抁qual;쀀⪰̸lantEqual;拡ilde;쀀≿̸ersetĀ;E൘൛쀀⊃⃒qual;抉ildeȀ;EFT൮൯൵ൿ扁qual;扄ullEqual;扇ilde;扉erticalBar;戤cr;쀀𝒩ilde耻Ñ䃑;䎝܀Eacdfgmoprstuvලෂ෉෕ෛ෠෧෼ขภยา฿ไlig;䅒cute耻Ó䃓Āiy෎ීrc耻Ô䃔;䐞blac;䅐r;쀀𝔒rave耻Ò䃒ƀaei෮ෲ෶cr;䅌ga;䎩cron;䎟pf;쀀𝕆enCurlyĀDQฎบoubleQuote;怜uote;怘;橔Āclวฬr;쀀𝒪ash耻Ø䃘iŬื฼de耻Õ䃕es;樷ml耻Ö䃖erĀBP๋๠Āar๐๓r;怾acĀek๚๜;揞et;掴arenthesis;揜Ҁacfhilors๿ງຊຏຒດຝະ໼rtialD;戂y;䐟r;쀀𝔓i;䎦;䎠usMinus;䂱Āipຢອncareplanåڝf;愙Ȁ;eio຺ູ໠໤檻cedesȀ;EST່້໏໚扺qual;檯lantEqual;扼ilde;找me;怳Ādp໩໮uct;戏ortionĀ;aȥ໹l;戝Āci༁༆r;쀀𝒫;䎨ȀUfos༑༖༛༟OT耻\"䀢r;쀀𝔔pf;愚cr;쀀𝒬؀BEacefhiorsu༾གྷཇའཱིྦྷྪྭ႖ႩႴႾarr;椐G耻®䂮ƀcnrཎནབute;䅔g;柫rĀ;tཛྷཝ憠l;椖ƀaeyཧཬཱron;䅘dil;䅖;䐠Ā;vླྀཹ愜erseĀEUྂྙĀlq྇ྎement;戋uilibrium;懋pEquilibrium;楯r»ཹo;䎡ghtЀACDFTUVa࿁࿫࿳ဢဨၛႇϘĀnr࿆࿒gleBracket;柩rowƀ;BL࿜࿝࿡憒ar;懥eftArrow;懄eiling;按oǵ࿹\\0စbleBracket;柧nǔည\\0နeeVector;楝ectorĀ;Bဝသ懂ar;楕loor;挋Āerိ၃eƀ;AVဵံြ抢rrow;憦ector;楛iangleƀ;BEၐၑၕ抳ar;槐qual;抵pƀDTVၣၮၸownVector;楏eeVector;楜ectorĀ;Bႂႃ憾ar;楔ectorĀ;B႑႒懀ar;楓Āpuႛ႞f;愝ndImplies;楰ightarrow;懛ĀchႹႼr;愛;憱leDelayed;槴ڀHOacfhimoqstuფჱჷჽᄙᄞᅑᅖᅡᅧᆵᆻᆿĀCcჩხHcy;䐩y;䐨FTcy;䐬cute;䅚ʀ;aeiyᄈᄉᄎᄓᄗ檼ron;䅠dil;䅞rc;䅜;䐡r;쀀𝔖ortȀDLRUᄪᄴᄾᅉownArrow»ОeftArrow»࢚ightArrow»࿝pArrow;憑gma;䎣allCircle;战pf;쀀𝕊ɲᅭ\\0\\0ᅰt;戚areȀ;ISUᅻᅼᆉᆯ斡ntersection;抓uĀbpᆏᆞsetĀ;Eᆗᆘ抏qual;抑ersetĀ;Eᆨᆩ抐qual;抒nion;抔cr;쀀𝒮ar;拆ȀbcmpᇈᇛሉላĀ;sᇍᇎ拐etĀ;Eᇍᇕqual;抆ĀchᇠህeedsȀ;ESTᇭᇮᇴᇿ扻qual;檰lantEqual;扽ilde;承Tháྌ;我ƀ;esሒሓሣ拑rsetĀ;Eሜም抃qual;抇et»ሓրHRSacfhiorsሾቄ቉ቕ቞ቱቶኟዂወዑORN耻Þ䃞ADE;愢ĀHc቎ቒcy;䐋y;䐦Ābuቚቜ;䀉;䎤ƀaeyብቪቯron;䅤dil;䅢;䐢r;쀀𝔗Āeiቻ኉ǲኀ\\0ኇefore;戴a;䎘Ācn኎ኘkSpace;쀀  Space;怉ldeȀ;EFTካኬኲኼ戼qual;扃ullEqual;扅ilde;扈pf;쀀𝕋ipleDot;惛Āctዖዛr;쀀𝒯rok;䅦ૡዷጎጚጦ\\0ጬጱ\\0\\0\\0\\0\\0ጸጽ፷ᎅ\\0᏿ᐄᐊᐐĀcrዻጁute耻Ú䃚rĀ;oጇገ憟cir;楉rǣጓ\\0጖y;䐎ve;䅬Āiyጞጣrc耻Û䃛;䐣blac;䅰r;쀀𝔘rave耻Ù䃙acr;䅪Ādiፁ፩erĀBPፈ፝Āarፍፐr;䁟acĀekፗፙ;揟et;掵arenthesis;揝onĀ;P፰፱拃lus;抎Āgp፻፿on;䅲f;쀀𝕌ЀADETadps᎕ᎮᎸᏄϨᏒᏗᏳrrowƀ;BDᅐᎠᎤar;椒ownArrow;懅ownArrow;憕quilibrium;楮eeĀ;AᏋᏌ报rrow;憥ownáϳerĀLRᏞᏨeftArrow;憖ightArrow;憗iĀ;lᏹᏺ䏒on;䎥ing;䅮cr;쀀𝒰ilde;䅨ml耻Ü䃜ҀDbcdefosvᐧᐬᐰᐳᐾᒅᒊᒐᒖash;披ar;櫫y;䐒ashĀ;lᐻᐼ抩;櫦Āerᑃᑅ;拁ƀbtyᑌᑐᑺar;怖Ā;iᑏᑕcalȀBLSTᑡᑥᑪᑴar;戣ine;䁼eparator;杘ilde;所ThinSpace;怊r;쀀𝔙pf;쀀𝕍cr;쀀𝒱dash;抪ʀcefosᒧᒬᒱᒶᒼirc;䅴dge;拀r;쀀𝔚pf;쀀𝕎cr;쀀𝒲Ȁfiosᓋᓐᓒᓘr;쀀𝔛;䎞pf;쀀𝕏cr;쀀𝒳ҀAIUacfosuᓱᓵᓹᓽᔄᔏᔔᔚᔠcy;䐯cy;䐇cy;䐮cute耻Ý䃝Āiyᔉᔍrc;䅶;䐫r;쀀𝔜pf;쀀𝕐cr;쀀𝒴ml;䅸ЀHacdefosᔵᔹᔿᕋᕏᕝᕠᕤcy;䐖cute;䅹Āayᕄᕉron;䅽;䐗ot;䅻ǲᕔ\\0ᕛoWidtè૙a;䎖r;愨pf;愤cr;쀀𝒵௡ᖃᖊᖐ\\0ᖰᖶᖿ\\0\\0\\0\\0ᗆᗛᗫᙟ᙭\\0ᚕ᚛ᚲᚹ\\0ᚾcute耻á䃡reve;䄃̀;Ediuyᖜᖝᖡᖣᖨᖭ戾;쀀∾̳;房rc耻â䃢te肻´̆;䐰lig耻æ䃦Ā;r²ᖺ;쀀𝔞rave耻à䃠ĀepᗊᗖĀfpᗏᗔsym;愵èᗓha;䎱ĀapᗟcĀclᗤᗧr;䄁g;樿ɤᗰ\\0\\0ᘊʀ;adsvᗺᗻᗿᘁᘇ戧nd;橕;橜lope;橘;橚΀;elmrszᘘᘙᘛᘞᘿᙏᙙ戠;榤e»ᘙsdĀ;aᘥᘦ戡ѡᘰᘲᘴᘶᘸᘺᘼᘾ;榨;榩;榪;榫;榬;榭;榮;榯tĀ;vᙅᙆ戟bĀ;dᙌᙍ抾;榝Āptᙔᙗh;戢»¹arr;捼Āgpᙣᙧon;䄅f;쀀𝕒΀;Eaeiop዁ᙻᙽᚂᚄᚇᚊ;橰cir;橯;扊d;手s;䀧roxĀ;e዁ᚒñᚃing耻å䃥ƀctyᚡᚦᚨr;쀀𝒶;䀪mpĀ;e዁ᚯñʈilde耻ã䃣ml耻ä䃤Āciᛂᛈoninôɲnt;樑ࠀNabcdefiklnoprsu᛭ᛱᜰ᜼ᝃᝈ᝸᝽០៦ᠹᡐᜍ᤽᥈ᥰot;櫭Ācrᛶ᜞kȀcepsᜀᜅᜍᜓong;扌psilon;䏶rime;怵imĀ;e᜚᜛戽q;拍Ŷᜢᜦee;抽edĀ;gᜬᜭ挅e»ᜭrkĀ;t፜᜷brk;掶Āoyᜁᝁ;䐱quo;怞ʀcmprtᝓ᝛ᝡᝤᝨausĀ;eĊĉptyv;榰séᜌnoõēƀahwᝯ᝱ᝳ;䎲;愶een;扬r;쀀𝔟g΀costuvwឍឝឳេ៕៛៞ƀaiuបពរðݠrc;旯p»፱ƀdptឤឨឭot;樀lus;樁imes;樂ɱឹ\\0\\0ើcup;樆ar;昅riangleĀdu៍្own;施p;斳plus;樄eåᑄåᒭarow;植ƀako៭ᠦᠵĀcn៲ᠣkƀlst៺֫᠂ozenge;槫riangleȀ;dlr᠒᠓᠘᠝斴own;斾eft;旂ight;斸k;搣Ʊᠫ\\0ᠳƲᠯ\\0ᠱ;斒;斑4;斓ck;斈ĀeoᠾᡍĀ;qᡃᡆ쀀=⃥uiv;쀀≡⃥t;挐Ȁptwxᡙᡞᡧᡬf;쀀𝕓Ā;tᏋᡣom»Ꮜtie;拈؀DHUVbdhmptuvᢅᢖᢪᢻᣗᣛᣬ᣿ᤅᤊᤐᤡȀLRlrᢎᢐᢒᢔ;敗;敔;敖;敓ʀ;DUduᢡᢢᢤᢦᢨ敐;敦;敩;敤;敧ȀLRlrᢳᢵᢷᢹ;敝;敚;敜;教΀;HLRhlrᣊᣋᣍᣏᣑᣓᣕ救;敬;散;敠;敫;敢;敟ox;槉ȀLRlrᣤᣦᣨᣪ;敕;敒;攐;攌ʀ;DUduڽ᣷᣹᣻᣽;敥;敨;攬;攴inus;抟lus;択imes;抠ȀLRlrᤙᤛᤝ᤟;敛;敘;攘;攔΀;HLRhlrᤰᤱᤳᤵᤷ᤻᤹攂;敪;敡;敞;攼;攤;攜Āevģ᥂bar耻¦䂦Ȁceioᥑᥖᥚᥠr;쀀𝒷mi;恏mĀ;e᜚᜜lƀ;bhᥨᥩᥫ䁜;槅sub;柈Ŭᥴ᥾lĀ;e᥹᥺怢t»᥺pƀ;Eeįᦅᦇ;檮Ā;qۜۛೡᦧ\\0᧨ᨑᨕᨲ\\0ᨷᩐ\\0\\0᪴\\0\\0᫁\\0\\0ᬡᬮ᭍᭒\\0᯽\\0ᰌƀcpr᦭ᦲ᧝ute;䄇̀;abcdsᦿᧀᧄ᧊᧕᧙戩nd;橄rcup;橉Āau᧏᧒p;橋p;橇ot;橀;쀀∩︀Āeo᧢᧥t;恁îړȀaeiu᧰᧻ᨁᨅǰ᧵\\0᧸s;橍on;䄍dil耻ç䃧rc;䄉psĀ;sᨌᨍ橌m;橐ot;䄋ƀdmnᨛᨠᨦil肻¸ƭptyv;榲t脀¢;eᨭᨮ䂢räƲr;쀀𝔠ƀceiᨽᩀᩍy;䑇ckĀ;mᩇᩈ朓ark»ᩈ;䏇r΀;Ecefms᩟᩠ᩢᩫ᪤᪪᪮旋;槃ƀ;elᩩᩪᩭ䋆q;扗eɡᩴ\\0\\0᪈rrowĀlr᩼᪁eft;憺ight;憻ʀRSacd᪒᪔᪖᪚᪟»ཇ;擈st;抛irc;抚ash;抝nint;樐id;櫯cir;槂ubsĀ;u᪻᪼晣it»᪼ˬ᫇᫔᫺\\0ᬊonĀ;eᫍᫎ䀺Ā;qÇÆɭ᫙\\0\\0᫢aĀ;t᫞᫟䀬;䁀ƀ;fl᫨᫩᫫戁îᅠeĀmx᫱᫶ent»᫩eóɍǧ᫾\\0ᬇĀ;dኻᬂot;橭nôɆƀfryᬐᬔᬗ;쀀𝕔oäɔ脀©;sŕᬝr;愗Āaoᬥᬩrr;憵ss;朗Ācuᬲᬷr;쀀𝒸Ābpᬼ᭄Ā;eᭁᭂ櫏;櫑Ā;eᭉᭊ櫐;櫒dot;拯΀delprvw᭠᭬᭷ᮂᮬᯔ᯹arrĀlr᭨᭪;椸;椵ɰ᭲\\0\\0᭵r;拞c;拟arrĀ;p᭿ᮀ憶;椽̀;bcdosᮏᮐᮖᮡᮥᮨ截rcap;橈Āauᮛᮞp;橆p;橊ot;抍r;橅;쀀∪︀Ȁalrv᮵ᮿᯞᯣrrĀ;mᮼᮽ憷;椼yƀevwᯇᯔᯘqɰᯎ\\0\\0ᯒreã᭳uã᭵ee;拎edge;拏en耻¤䂤earrowĀlrᯮ᯳eft»ᮀight»ᮽeäᯝĀciᰁᰇoninôǷnt;戱lcty;挭ঀAHabcdefhijlorstuwz᰸᰻᰿ᱝᱩᱵᲊᲞᲬᲷ᳻᳿ᴍᵻᶑᶫᶻ᷆᷍rò΁ar;楥Ȁglrs᱈ᱍ᱒᱔ger;怠eth;愸òᄳhĀ;vᱚᱛ怐»ऊūᱡᱧarow;椏aã̕Āayᱮᱳron;䄏;䐴ƀ;ao̲ᱼᲄĀgrʿᲁr;懊tseq;橷ƀglmᲑᲔᲘ耻°䂰ta;䎴ptyv;榱ĀirᲣᲨsht;楿;쀀𝔡arĀlrᲳᲵ»ࣜ»သʀaegsv᳂͸᳖᳜᳠mƀ;oș᳊᳔ndĀ;ș᳑uit;晦amma;䏝in;拲ƀ;io᳧᳨᳸䃷de脀÷;o᳧ᳰntimes;拇nø᳷cy;䑒cɯᴆ\\0\\0ᴊrn;挞op;挍ʀlptuwᴘᴝᴢᵉᵕlar;䀤f;쀀𝕕ʀ;emps̋ᴭᴷᴽᵂqĀ;d͒ᴳot;扑inus;戸lus;戔quare;抡blebarwedgåúnƀadhᄮᵝᵧownarrowóᲃarpoonĀlrᵲᵶefôᲴighôᲶŢᵿᶅkaro÷གɯᶊ\\0\\0ᶎrn;挟op;挌ƀcotᶘᶣᶦĀryᶝᶡ;쀀𝒹;䑕l;槶rok;䄑Ādrᶰᶴot;拱iĀ;fᶺ᠖斿Āah᷀᷃ròЩaòྦangle;榦Āci᷒ᷕy;䑟grarr;柿ऀDacdefglmnopqrstuxḁḉḙḸոḼṉṡṾấắẽỡἪἷὄ὎὚ĀDoḆᴴoôᲉĀcsḎḔute耻é䃩ter;橮ȀaioyḢḧḱḶron;䄛rĀ;cḭḮ扖耻ê䃪lon;払;䑍ot;䄗ĀDrṁṅot;扒;쀀𝔢ƀ;rsṐṑṗ檚ave耻è䃨Ā;dṜṝ檖ot;檘Ȁ;ilsṪṫṲṴ檙nters;揧;愓Ā;dṹṺ檕ot;檗ƀapsẅẉẗcr;䄓tyƀ;svẒẓẕ戅et»ẓpĀ1;ẝẤĳạả;怄;怅怃ĀgsẪẬ;䅋p;怂ĀgpẴẸon;䄙f;쀀𝕖ƀalsỄỎỒrĀ;sỊị拕l;槣us;橱iƀ;lvỚớở䎵on»ớ;䏵ȀcsuvỪỳἋἣĀioữḱrc»Ḯɩỹ\\0\\0ỻíՈantĀglἂἆtr»ṝess»Ṻƀaeiἒ἖Ἒls;䀽st;扟vĀ;DȵἠD;橸parsl;槥ĀDaἯἳot;打rr;楱ƀcdiἾὁỸr;愯oô͒ĀahὉὋ;䎷耻ð䃰Āmrὓὗl耻ë䃫o;悬ƀcipὡὤὧl;䀡sôծĀeoὬὴctatioîՙnentialåչৡᾒ\\0ᾞ\\0ᾡᾧ\\0\\0ῆῌ\\0ΐ\\0ῦῪ \\0 ⁚llingdotseñṄy;䑄male;晀ƀilrᾭᾳ῁lig;耀ﬃɩᾹ\\0\\0᾽g;耀ﬀig;耀ﬄ;쀀𝔣lig;耀ﬁlig;쀀fjƀaltῙ῜ῡt;晭ig;耀ﬂns;斱of;䆒ǰ΅\\0ῳf;쀀𝕗ĀakֿῷĀ;vῼ´拔;櫙artint;樍Āao‌⁕Ācs‑⁒α‚‰‸⁅⁈\\0⁐β•‥‧‪‬\\0‮耻½䂽;慓耻¼䂼;慕;慙;慛Ƴ‴\\0‶;慔;慖ʴ‾⁁\\0\\0⁃耻¾䂾;慗;慜5;慘ƶ⁌\\0⁎;慚;慝8;慞l;恄wn;挢cr;쀀𝒻ࢀEabcdefgijlnorstv₂₉₟₥₰₴⃰⃵⃺⃿℃ℒℸ̗ℾ⅒↞Ā;lٍ₇;檌ƀcmpₐₕ₝ute;䇵maĀ;dₜ᳚䎳;檆reve;䄟Āiy₪₮rc;䄝;䐳ot;䄡Ȁ;lqsؾق₽⃉ƀ;qsؾٌ⃄lanô٥Ȁ;cdl٥⃒⃥⃕c;檩otĀ;o⃜⃝檀Ā;l⃢⃣檂;檄Ā;e⃪⃭쀀⋛︀s;檔r;쀀𝔤Ā;gٳ؛mel;愷cy;䑓Ȁ;Eajٚℌℎℐ;檒;檥;檤ȀEaesℛℝ℩ℴ;扩pĀ;p℣ℤ檊rox»ℤĀ;q℮ℯ檈Ā;q℮ℛim;拧pf;쀀𝕘Āci⅃ⅆr;愊mƀ;el٫ⅎ⅐;檎;檐茀>;cdlqr׮ⅠⅪⅮⅳⅹĀciⅥⅧ;檧r;橺ot;拗Par;榕uest;橼ʀadelsↄⅪ←ٖ↛ǰ↉\\0↎proø₞r;楸qĀlqؿ↖lesó₈ií٫Āen↣↭rtneqq;쀀≩︀Å↪ԀAabcefkosy⇄⇇⇱⇵⇺∘∝∯≨≽ròΠȀilmr⇐⇔⇗⇛rsðᒄf»․ilôکĀdr⇠⇤cy;䑊ƀ;cwࣴ⇫⇯ir;楈;憭ar;意irc;䄥ƀalr∁∎∓rtsĀ;u∉∊晥it»∊lip;怦con;抹r;쀀𝔥sĀew∣∩arow;椥arow;椦ʀamopr∺∾≃≞≣rr;懿tht;戻kĀlr≉≓eftarrow;憩ightarrow;憪f;쀀𝕙bar;怕ƀclt≯≴≸r;쀀𝒽asè⇴rok;䄧Ābp⊂⊇ull;恃hen»ᱛૡ⊣\\0⊪\\0⊸⋅⋎\\0⋕⋳\\0\\0⋸⌢⍧⍢⍿\\0⎆⎪⎴cute耻í䃭ƀ;iyݱ⊰⊵rc耻î䃮;䐸Ācx⊼⊿y;䐵cl耻¡䂡ĀfrΟ⋉;쀀𝔦rave耻ì䃬Ȁ;inoܾ⋝⋩⋮Āin⋢⋦nt;樌t;戭fin;槜ta;愩lig;䄳ƀaop⋾⌚⌝ƀcgt⌅⌈⌗r;䄫ƀelpܟ⌏⌓inåގarôܠh;䄱f;抷ed;䆵ʀ;cfotӴ⌬⌱⌽⍁are;愅inĀ;t⌸⌹戞ie;槝doô⌙ʀ;celpݗ⍌⍐⍛⍡al;抺Āgr⍕⍙eróᕣã⍍arhk;樗rod;樼Ȁcgpt⍯⍲⍶⍻y;䑑on;䄯f;쀀𝕚a;䎹uest耻¿䂿Āci⎊⎏r;쀀𝒾nʀ;EdsvӴ⎛⎝⎡ӳ;拹ot;拵Ā;v⎦⎧拴;拳Ā;iݷ⎮lde;䄩ǫ⎸\\0⎼cy;䑖l耻ï䃯̀cfmosu⏌⏗⏜⏡⏧⏵Āiy⏑⏕rc;䄵;䐹r;쀀𝔧ath;䈷pf;쀀𝕛ǣ⏬\\0⏱r;쀀𝒿rcy;䑘kcy;䑔Ѐacfghjos␋␖␢␧␭␱␵␻ppaĀ;v␓␔䎺;䏰Āey␛␠dil;䄷;䐺r;쀀𝔨reen;䄸cy;䑅cy;䑜pf;쀀𝕜cr;쀀𝓀஀ABEHabcdefghjlmnoprstuv⑰⒁⒆⒍⒑┎┽╚▀♎♞♥♹♽⚚⚲⛘❝❨➋⟀⠁⠒ƀart⑷⑺⑼rò৆òΕail;椛arr;椎Ā;gঔ⒋;檋ar;楢ॣ⒥\\0⒪\\0⒱\\0\\0\\0\\0\\0⒵Ⓔ\\0ⓆⓈⓍ\\0⓹ute;䄺mptyv;榴raîࡌbda;䎻gƀ;dlࢎⓁⓃ;榑åࢎ;檅uo耻«䂫rЀ;bfhlpst࢙ⓞⓦⓩ⓫⓮⓱⓵Ā;f࢝ⓣs;椟s;椝ë≒p;憫l;椹im;楳l;憢ƀ;ae⓿─┄檫il;椙Ā;s┉┊檭;쀀⪭︀ƀabr┕┙┝rr;椌rk;杲Āak┢┬cĀek┨┪;䁻;䁛Āes┱┳;榋lĀdu┹┻;榏;榍Ȁaeuy╆╋╖╘ron;䄾Ādi═╔il;䄼ìࢰâ┩;䐻Ȁcqrs╣╦╭╽a;椶uoĀ;rนᝆĀdu╲╷har;楧shar;楋h;憲ʀ;fgqs▋▌উ◳◿扤tʀahlrt▘▤▷◂◨rrowĀ;t࢙□aé⓶arpoonĀdu▯▴own»њp»०eftarrows;懇ightƀahs◍◖◞rrowĀ;sࣴࢧarpoonó྘quigarro÷⇰hreetimes;拋ƀ;qs▋ও◺lanôবʀ;cdgsব☊☍☝☨c;檨otĀ;o☔☕橿Ā;r☚☛檁;檃Ā;e☢☥쀀⋚︀s;檓ʀadegs☳☹☽♉♋pproøⓆot;拖qĀgq♃♅ôউgtò⒌ôছiíলƀilr♕࣡♚sht;楼;쀀𝔩Ā;Eজ♣;檑š♩♶rĀdu▲♮Ā;l॥♳;楪lk;斄cy;䑙ʀ;achtੈ⚈⚋⚑⚖rò◁orneòᴈard;楫ri;旺Āio⚟⚤dot;䅀ustĀ;a⚬⚭掰che»⚭ȀEaes⚻⚽⛉⛔;扨pĀ;p⛃⛄檉rox»⛄Ā;q⛎⛏檇Ā;q⛎⚻im;拦Ѐabnoptwz⛩⛴⛷✚✯❁❇❐Ānr⛮⛱g;柬r;懽rëࣁgƀlmr⛿✍✔eftĀar০✇ightá৲apsto;柼ightá৽parrowĀlr✥✩efô⓭ight;憬ƀafl✶✹✽r;榅;쀀𝕝us;樭imes;樴š❋❏st;戗áፎƀ;ef❗❘᠀旊nge»❘arĀ;l❤❥䀨t;榓ʀachmt❳❶❼➅➇ròࢨorneòᶌarĀ;d྘➃;業;怎ri;抿̀achiqt➘➝ੀ➢➮➻quo;怹r;쀀𝓁mƀ;egল➪➬;檍;檏Ābu┪➳oĀ;rฟ➹;怚rok;䅂萀<;cdhilqrࠫ⟒☹⟜⟠⟥⟪⟰Āci⟗⟙;檦r;橹reå◲mes;拉arr;楶uest;橻ĀPi⟵⟹ar;榖ƀ;ef⠀भ᠛旃rĀdu⠇⠍shar;楊har;楦Āen⠗⠡rtneqq;쀀≨︀Å⠞܀Dacdefhilnopsu⡀⡅⢂⢎⢓⢠⢥⢨⣚⣢⣤ઃ⣳⤂Dot;戺Ȁclpr⡎⡒⡣⡽r耻¯䂯Āet⡗⡙;時Ā;e⡞⡟朠se»⡟Ā;sျ⡨toȀ;dluျ⡳⡷⡻owîҌefôएðᏑker;斮Āoy⢇⢌mma;権;䐼ash;怔asuredangle»ᘦr;쀀𝔪o;愧ƀcdn⢯⢴⣉ro耻µ䂵Ȁ;acdᑤ⢽⣀⣄sôᚧir;櫰ot肻·Ƶusƀ;bd⣒ᤃ⣓戒Ā;uᴼ⣘;横ţ⣞⣡p;櫛ò−ðઁĀdp⣩⣮els;抧f;쀀𝕞Āct⣸⣽r;쀀𝓂pos»ᖝƀ;lm⤉⤊⤍䎼timap;抸ఀGLRVabcdefghijlmoprstuvw⥂⥓⥾⦉⦘⧚⧩⨕⨚⩘⩝⪃⪕⪤⪨⬄⬇⭄⭿⮮ⰴⱧⱼ⳩Āgt⥇⥋;쀀⋙̸Ā;v⥐௏쀀≫⃒ƀelt⥚⥲⥶ftĀar⥡⥧rrow;懍ightarrow;懎;쀀⋘̸Ā;v⥻ే쀀≪⃒ightarrow;懏ĀDd⦎⦓ash;抯ash;抮ʀbcnpt⦣⦧⦬⦱⧌la»˞ute;䅄g;쀀∠⃒ʀ;Eiop඄⦼⧀⧅⧈;쀀⩰̸d;쀀≋̸s;䅉roø඄urĀ;a⧓⧔普lĀ;s⧓ସǳ⧟\\0⧣p肻 ଷmpĀ;e௹ఀʀaeouy⧴⧾⨃⨐⨓ǰ⧹\\0⧻;橃on;䅈dil;䅆ngĀ;dൾ⨊ot;쀀⩭̸p;橂;䐽ash;怓΀;Aadqsxஒ⨩⨭⨻⩁⩅⩐rr;懗rĀhr⨳⨶k;椤Ā;oᏲᏰot;쀀≐̸uiöୣĀei⩊⩎ar;椨í஘istĀ;s஠டr;쀀𝔫ȀEest௅⩦⩹⩼ƀ;qs஼⩭௡ƀ;qs஼௅⩴lanô௢ií௪Ā;rஶ⪁»ஷƀAap⪊⪍⪑rò⥱rr;憮ar;櫲ƀ;svྍ⪜ྌĀ;d⪡⪢拼;拺cy;䑚΀AEadest⪷⪺⪾⫂⫅⫶⫹rò⥦;쀀≦̸rr;憚r;急Ȁ;fqs఻⫎⫣⫯tĀar⫔⫙rro÷⫁ightarro÷⪐ƀ;qs఻⪺⫪lanôౕĀ;sౕ⫴»శiíౝĀ;rవ⫾iĀ;eచథiäඐĀpt⬌⬑f;쀀𝕟膀¬;in⬙⬚⬶䂬nȀ;Edvஉ⬤⬨⬮;쀀⋹̸ot;쀀⋵̸ǡஉ⬳⬵;拷;拶iĀ;vಸ⬼ǡಸ⭁⭃;拾;拽ƀaor⭋⭣⭩rȀ;ast୻⭕⭚⭟lleì୻l;쀀⫽⃥;쀀∂̸lint;樔ƀ;ceಒ⭰⭳uåಥĀ;cಘ⭸Ā;eಒ⭽ñಘȀAait⮈⮋⮝⮧rò⦈rrƀ;cw⮔⮕⮙憛;쀀⤳̸;쀀↝̸ghtarrow»⮕riĀ;eೋೖ΀chimpqu⮽⯍⯙⬄୸⯤⯯Ȁ;cerല⯆ഷ⯉uå൅;쀀𝓃ortɭ⬅\\0\\0⯖ará⭖mĀ;e൮⯟Ā;q൴൳suĀbp⯫⯭å೸åഋƀbcp⯶ⰑⰙȀ;Ees⯿ⰀഢⰄ抄;쀀⫅̸etĀ;eഛⰋqĀ;qണⰀcĀ;eലⰗñസȀ;EesⰢⰣൟⰧ抅;쀀⫆̸etĀ;e൘ⰮqĀ;qൠⰣȀgilrⰽⰿⱅⱇìௗlde耻ñ䃱çృiangleĀlrⱒⱜeftĀ;eచⱚñదightĀ;eೋⱥñ೗Ā;mⱬⱭ䎽ƀ;esⱴⱵⱹ䀣ro;愖p;怇ҀDHadgilrsⲏⲔⲙⲞⲣⲰⲶⳓⳣash;抭arr;椄p;쀀≍⃒ash;抬ĀetⲨⲬ;쀀≥⃒;쀀>⃒nfin;槞ƀAetⲽⳁⳅrr;椂;쀀≤⃒Ā;rⳊⳍ쀀<⃒ie;쀀⊴⃒ĀAtⳘⳜrr;椃rie;쀀⊵⃒im;쀀∼⃒ƀAan⳰⳴ⴂrr;懖rĀhr⳺⳽k;椣Ā;oᏧᏥear;椧ቓ᪕\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0ⴭ\\0ⴸⵈⵠⵥ⵲ⶄᬇ\\0\\0ⶍⶫ\\0ⷈⷎ\\0ⷜ⸙⸫⸾⹃Ācsⴱ᪗ute耻ó䃳ĀiyⴼⵅrĀ;c᪞ⵂ耻ô䃴;䐾ʀabios᪠ⵒⵗǈⵚlac;䅑v;樸old;榼lig;䅓Ācr⵩⵭ir;榿;쀀𝔬ͯ⵹\\0\\0⵼\\0ⶂn;䋛ave耻ò䃲;槁Ābmⶈ෴ar;榵Ȁacitⶕ⶘ⶥⶨrò᪀Āir⶝ⶠr;榾oss;榻nå๒;槀ƀaeiⶱⶵⶹcr;䅍ga;䏉ƀcdnⷀⷅǍron;䎿;榶pf;쀀𝕠ƀaelⷔ⷗ǒr;榷rp;榹΀;adiosvⷪⷫⷮ⸈⸍⸐⸖戨rò᪆Ȁ;efmⷷⷸ⸂⸅橝rĀ;oⷾⷿ愴f»ⷿ耻ª䂪耻º䂺gof;抶r;橖lope;橗;橛ƀclo⸟⸡⸧ò⸁ash耻ø䃸l;折iŬⸯ⸴de耻õ䃵esĀ;aǛ⸺s;樶ml耻ö䃶bar;挽ૡ⹞\\0⹽\\0⺀⺝\\0⺢⺹\\0\\0⻋ຜ\\0⼓\\0\\0⼫⾼\\0⿈rȀ;astЃ⹧⹲຅脀¶;l⹭⹮䂶leìЃɩ⹸\\0\\0⹻m;櫳;櫽y;䐿rʀcimpt⺋⺏⺓ᡥ⺗nt;䀥od;䀮il;怰enk;怱r;쀀𝔭ƀimo⺨⺰⺴Ā;v⺭⺮䏆;䏕maô੶ne;明ƀ;tv⺿⻀⻈䏀chfork»´;䏖Āau⻏⻟nĀck⻕⻝kĀ;h⇴⻛;愎ö⇴sҀ;abcdemst⻳⻴ᤈ⻹⻽⼄⼆⼊⼎䀫cir;樣ir;樢Āouᵀ⼂;樥;橲n肻±ຝim;樦wo;樧ƀipu⼙⼠⼥ntint;樕f;쀀𝕡nd耻£䂣Ԁ;Eaceinosu່⼿⽁⽄⽇⾁⾉⾒⽾⾶;檳p;檷uå໙Ā;c໎⽌̀;acens່⽙⽟⽦⽨⽾pproø⽃urlyeñ໙ñ໎ƀaes⽯⽶⽺pprox;檹qq;檵im;拨iíໟmeĀ;s⾈ຮ怲ƀEas⽸⾐⽺ð⽵ƀdfp໬⾙⾯ƀals⾠⾥⾪lar;挮ine;挒urf;挓Ā;t໻⾴ï໻rel;抰Āci⿀⿅r;쀀𝓅;䏈ncsp;怈̀fiopsu⿚⋢⿟⿥⿫⿱r;쀀𝔮pf;쀀𝕢rime;恗cr;쀀𝓆ƀaeo⿸〉〓tĀei⿾々rnionóڰnt;樖stĀ;e【】䀿ñἙô༔઀ABHabcdefhilmnoprstux぀けさすムㄎㄫㅇㅢㅲㆎ㈆㈕㈤㈩㉘㉮㉲㊐㊰㊷ƀartぇおがròႳòϝail;検aròᱥar;楤΀cdenqrtとふへみわゔヌĀeuねぱ;쀀∽̱te;䅕iãᅮmptyv;榳gȀ;del࿑らるろ;榒;榥å࿑uo耻»䂻rր;abcfhlpstw࿜ガクシスゼゾダッデナp;極Ā;f࿠ゴs;椠;椳s;椞ë≝ð✮l;楅im;楴l;憣;憝Āaiパフil;椚oĀ;nホボ戶aló༞ƀabrョリヮrò៥rk;杳ĀakンヽcĀekヹ・;䁽;䁝Āes㄂㄄;榌lĀduㄊㄌ;榎;榐Ȁaeuyㄗㄜㄧㄩron;䅙Ādiㄡㄥil;䅗ì࿲âヺ;䑀Ȁclqsㄴㄷㄽㅄa;椷dhar;楩uoĀ;rȎȍh;憳ƀacgㅎㅟངlȀ;ipsླྀㅘㅛႜnåႻarôྩt;断ƀilrㅩဣㅮsht;楽;쀀𝔯ĀaoㅷㆆrĀduㅽㅿ»ѻĀ;l႑ㆄ;楬Ā;vㆋㆌ䏁;䏱ƀgns㆕ㇹㇼht̀ahlrstㆤㆰ㇂㇘㇤㇮rrowĀ;t࿜ㆭaéトarpoonĀduㆻㆿowîㅾp»႒eftĀah㇊㇐rrowó࿪arpoonóՑightarrows;應quigarro÷ニhreetimes;拌g;䋚ingdotseñἲƀahm㈍㈐㈓rò࿪aòՑ;怏oustĀ;a㈞㈟掱che»㈟mid;櫮Ȁabpt㈲㈽㉀㉒Ānr㈷㈺g;柭r;懾rëဃƀafl㉇㉊㉎r;榆;쀀𝕣us;樮imes;樵Āap㉝㉧rĀ;g㉣㉤䀩t;榔olint;樒arò㇣Ȁachq㉻㊀Ⴜ㊅quo;怺r;쀀𝓇Ābu・㊊oĀ;rȔȓƀhir㊗㊛㊠reåㇸmes;拊iȀ;efl㊪ၙᠡ㊫方tri;槎luhar;楨;愞ൡ㋕㋛㋟㌬㌸㍱\\0㍺㎤\\0\\0㏬㏰\\0㐨㑈㑚㒭㒱㓊㓱\\0㘖\\0\\0㘳cute;䅛quï➺Ԁ;Eaceinpsyᇭ㋳㋵㋿㌂㌋㌏㌟㌦㌩;檴ǰ㋺\\0㋼;檸on;䅡uåᇾĀ;dᇳ㌇il;䅟rc;䅝ƀEas㌖㌘㌛;檶p;檺im;择olint;樓iíሄ;䑁otƀ;be㌴ᵇ㌵担;橦΀Aacmstx㍆㍊㍗㍛㍞㍣㍭rr;懘rĀhr㍐㍒ë∨Ā;oਸ਼਴t耻§䂧i;䀻war;椩mĀin㍩ðnuóñt;朶rĀ;o㍶⁕쀀𝔰Ȁacoy㎂㎆㎑㎠rp;景Āhy㎋㎏cy;䑉;䑈rtɭ㎙\\0\\0㎜iäᑤaraì⹯耻­䂭Āgm㎨㎴maƀ;fv㎱㎲㎲䏃;䏂Ѐ;deglnprካ㏅㏉㏎㏖㏞㏡㏦ot;橪Ā;q኱ኰĀ;E㏓㏔檞;檠Ā;E㏛㏜檝;檟e;扆lus;樤arr;楲aròᄽȀaeit㏸㐈㐏㐗Āls㏽㐄lsetmé㍪hp;樳parsl;槤Ādlᑣ㐔e;挣Ā;e㐜㐝檪Ā;s㐢㐣檬;쀀⪬︀ƀflp㐮㐳㑂tcy;䑌Ā;b㐸㐹䀯Ā;a㐾㐿槄r;挿f;쀀𝕤aĀdr㑍ЂesĀ;u㑔㑕晠it»㑕ƀcsu㑠㑹㒟Āau㑥㑯pĀ;sᆈ㑫;쀀⊓︀pĀ;sᆴ㑵;쀀⊔︀uĀbp㑿㒏ƀ;esᆗᆜ㒆etĀ;eᆗ㒍ñᆝƀ;esᆨᆭ㒖etĀ;eᆨ㒝ñᆮƀ;afᅻ㒦ְrť㒫ֱ»ᅼaròᅈȀcemt㒹㒾㓂㓅r;쀀𝓈tmîñiì㐕aræᆾĀar㓎㓕rĀ;f㓔ឿ昆Āan㓚㓭ightĀep㓣㓪psiloîỠhé⺯s»⡒ʀbcmnp㓻㕞ሉ㖋㖎Ҁ;Edemnprs㔎㔏㔑㔕㔞㔣㔬㔱㔶抂;櫅ot;檽Ā;dᇚ㔚ot;櫃ult;櫁ĀEe㔨㔪;櫋;把lus;檿arr;楹ƀeiu㔽㕒㕕tƀ;en㔎㕅㕋qĀ;qᇚ㔏eqĀ;q㔫㔨m;櫇Ābp㕚㕜;櫕;櫓c̀;acensᇭ㕬㕲㕹㕻㌦pproø㋺urlyeñᇾñᇳƀaes㖂㖈㌛pproø㌚qñ㌗g;晪ڀ123;Edehlmnps㖩㖬㖯ሜ㖲㖴㗀㗉㗕㗚㗟㗨㗭耻¹䂹耻²䂲耻³䂳;櫆Āos㖹㖼t;檾ub;櫘Ā;dሢ㗅ot;櫄sĀou㗏㗒l;柉b;櫗arr;楻ult;櫂ĀEe㗤㗦;櫌;抋lus;櫀ƀeiu㗴㘉㘌tƀ;enሜ㗼㘂qĀ;qሢ㖲eqĀ;q㗧㗤m;櫈Ābp㘑㘓;櫔;櫖ƀAan㘜㘠㘭rr;懙rĀhr㘦㘨ë∮Ā;oਫ਩war;椪lig耻ß䃟௡㙑㙝㙠ዎ㙳㙹\\0㙾㛂\\0\\0\\0\\0\\0㛛㜃\\0㜉㝬\\0\\0\\0㞇ɲ㙖\\0\\0㙛get;挖;䏄rë๟ƀaey㙦㙫㙰ron;䅥dil;䅣;䑂lrec;挕r;쀀𝔱Ȁeiko㚆㚝㚵㚼ǲ㚋\\0㚑eĀ4fኄኁaƀ;sv㚘㚙㚛䎸ym;䏑Ācn㚢㚲kĀas㚨㚮pproø዁im»ኬsðኞĀas㚺㚮ð዁rn耻þ䃾Ǭ̟㛆⋧es膀×;bd㛏㛐㛘䃗Ā;aᤏ㛕r;樱;樰ƀeps㛡㛣㜀á⩍Ȁ;bcf҆㛬㛰㛴ot;挶ir;櫱Ā;o㛹㛼쀀𝕥rk;櫚á㍢rime;怴ƀaip㜏㜒㝤dåቈ΀adempst㜡㝍㝀㝑㝗㝜㝟ngleʀ;dlqr㜰㜱㜶㝀㝂斵own»ᶻeftĀ;e⠀㜾ñम;扜ightĀ;e㊪㝋ñၚot;旬inus;樺lus;樹b;槍ime;樻ezium;揢ƀcht㝲㝽㞁Āry㝷㝻;쀀𝓉;䑆cy;䑛rok;䅧Āio㞋㞎xô᝷headĀlr㞗㞠eftarro÷ࡏightarrow»ཝऀAHabcdfghlmoprstuw㟐㟓㟗㟤㟰㟼㠎㠜㠣㠴㡑㡝㡫㢩㣌㣒㣪㣶ròϭar;楣Ācr㟜㟢ute耻ú䃺òᅐrǣ㟪\\0㟭y;䑞ve;䅭Āiy㟵㟺rc耻û䃻;䑃ƀabh㠃㠆㠋ròᎭlac;䅱aòᏃĀir㠓㠘sht;楾;쀀𝔲rave耻ù䃹š㠧㠱rĀlr㠬㠮»ॗ»ႃlk;斀Āct㠹㡍ɯ㠿\\0\\0㡊rnĀ;e㡅㡆挜r»㡆op;挏ri;旸Āal㡖㡚cr;䅫肻¨͉Āgp㡢㡦on;䅳f;쀀𝕦̀adhlsuᅋ㡸㡽፲㢑㢠ownáᎳarpoonĀlr㢈㢌efô㠭ighô㠯iƀ;hl㢙㢚㢜䏅»ᏺon»㢚parrows;懈ƀcit㢰㣄㣈ɯ㢶\\0\\0㣁rnĀ;e㢼㢽挝r»㢽op;挎ng;䅯ri;旹cr;쀀𝓊ƀdir㣙㣝㣢ot;拰lde;䅩iĀ;f㜰㣨»᠓Āam㣯㣲rò㢨l耻ü䃼angle;榧ހABDacdeflnoprsz㤜㤟㤩㤭㦵㦸㦽㧟㧤㧨㧳㧹㧽㨁㨠ròϷarĀ;v㤦㤧櫨;櫩asèϡĀnr㤲㤷grt;榜΀eknprst㓣㥆㥋㥒㥝㥤㦖appá␕othinçẖƀhir㓫⻈㥙opô⾵Ā;hᎷ㥢ïㆍĀiu㥩㥭gmá㎳Ābp㥲㦄setneqĀ;q㥽㦀쀀⊊︀;쀀⫋︀setneqĀ;q㦏㦒쀀⊋︀;쀀⫌︀Āhr㦛㦟etá㚜iangleĀlr㦪㦯eft»थight»ၑy;䐲ash»ံƀelr㧄㧒㧗ƀ;beⷪ㧋㧏ar;抻q;扚lip;拮Ābt㧜ᑨaòᑩr;쀀𝔳tré㦮suĀbp㧯㧱»ജ»൙pf;쀀𝕧roð໻tré㦴Ācu㨆㨋r;쀀𝓋Ābp㨐㨘nĀEe㦀㨖»㥾nĀEe㦒㨞»㦐igzag;榚΀cefoprs㨶㨻㩖㩛㩔㩡㩪irc;䅵Ādi㩀㩑Ābg㩅㩉ar;機eĀ;qᗺ㩏;扙erp;愘r;쀀𝔴pf;쀀𝕨Ā;eᑹ㩦atèᑹcr;쀀𝓌ૣណ㪇\\0㪋\\0㪐㪛\\0\\0㪝㪨㪫㪯\\0\\0㫃㫎\\0㫘ៜ៟tré៑r;쀀𝔵ĀAa㪔㪗ròσrò৶;䎾ĀAa㪡㪤ròθrò৫að✓is;拻ƀdptឤ㪵㪾Āfl㪺ឩ;쀀𝕩imåឲĀAa㫇㫊ròώròਁĀcq㫒ីr;쀀𝓍Āpt៖㫜ré។Ѐacefiosu㫰㫽㬈㬌㬑㬕㬛㬡cĀuy㫶㫻te耻ý䃽;䑏Āiy㬂㬆rc;䅷;䑋n耻¥䂥r;쀀𝔶cy;䑗pf;쀀𝕪cr;쀀𝓎Ācm㬦㬩y;䑎l耻ÿ䃿Ԁacdefhiosw㭂㭈㭔㭘㭤㭩㭭㭴㭺㮀cute;䅺Āay㭍㭒ron;䅾;䐷ot;䅼Āet㭝㭡træᕟa;䎶r;쀀𝔷cy;䐶grarr;懝pf;쀀𝕫cr;쀀𝓏Ājn㮅㮇;怍j;怌'.split(\"\").map(e=>e.charCodeAt(0))),Zs=new Map([[0,65533],[128,8364],[130,8218],[131,402],[132,8222],[133,8230],[134,8224],[135,8225],[136,710],[137,8240],[138,352],[139,8249],[140,338],[142,381],[145,8216],[146,8217],[147,8220],[148,8221],[149,8226],[150,8211],[151,8212],[152,732],[153,8482],[154,353],[155,8250],[156,339],[158,382],[159,376]]);var Js,eo;(eo=Js||(Js={}))[eo.NUM=35]=\"NUM\",eo[eo.SEMI=59]=\"SEMI\",eo[eo.EQUALS=61]=\"EQUALS\",eo[eo.ZERO=48]=\"ZERO\",eo[eo.NINE=57]=\"NINE\",eo[eo.LOWER_A=97]=\"LOWER_A\",eo[eo.LOWER_F=102]=\"LOWER_F\",eo[eo.LOWER_X=120]=\"LOWER_X\",eo[eo.LOWER_Z=122]=\"LOWER_Z\",eo[eo.UPPER_A=65]=\"UPPER_A\",eo[eo.UPPER_F=70]=\"UPPER_F\",eo[eo.UPPER_Z=90]=\"UPPER_Z\";var to,no,ro,ao,io,so,oo,lo,co,uo,po,ho,fo,mo,go,bo;function Eo(e){return e>=Js.ZERO&&e<=Js.NINE}function yo(e){return e>=Js.UPPER_A&&e<=Js.UPPER_F||e>=Js.LOWER_A&&e<=Js.LOWER_F}function _o(e){return e===Js.EQUALS||function(e){return e>=Js.UPPER_A&&e<=Js.UPPER_Z||e>=Js.LOWER_A&&e<=Js.LOWER_Z||Eo(e)}(e)}(no=to||(to={}))[no.VALUE_LENGTH=49152]=\"VALUE_LENGTH\",no[no.BRANCH_LENGTH=16256]=\"BRANCH_LENGTH\",no[no.JUMP_TABLE=127]=\"JUMP_TABLE\",(ao=ro||(ro={}))[ao.EntityStart=0]=\"EntityStart\",ao[ao.NumericStart=1]=\"NumericStart\",ao[ao.NumericDecimal=2]=\"NumericDecimal\",ao[ao.NumericHex=3]=\"NumericHex\",ao[ao.NamedEntity=4]=\"NamedEntity\",(so=io||(io={}))[so.Legacy=0]=\"Legacy\",so[so.Strict=1]=\"Strict\",so[so.Attribute=2]=\"Attribute\";class ko{constructor(e,t,n){this.decodeTree=e,this.emitCodePoint=t,this.errors=n,this.state=ro.EntityStart,this.consumed=1,this.result=0,this.treeIndex=0,this.excess=1,this.decodeMode=io.Strict}startEntity(e){this.decodeMode=e,this.state=ro.EntityStart,this.result=0,this.treeIndex=0,this.excess=1,this.consumed=1}write(e,t){switch(this.state){case ro.EntityStart:return e.charCodeAt(t)===Js.NUM?(this.state=ro.NumericStart,this.consumed+=1,this.stateNumericStart(e,t+1)):(this.state=ro.NamedEntity,this.stateNamedEntity(e,t));case ro.NumericStart:return this.stateNumericStart(e,t);case ro.NumericDecimal:return this.stateNumericDecimal(e,t);case ro.NumericHex:return this.stateNumericHex(e,t);case ro.NamedEntity:return this.stateNamedEntity(e,t)}}stateNumericStart(e,t){return t>=e.length?-1:(32|e.charCodeAt(t))===Js.LOWER_X?(this.state=ro.NumericHex,this.consumed+=1,this.stateNumericHex(e,t+1)):(this.state=ro.NumericDecimal,this.stateNumericDecimal(e,t))}addToNumericResult(e,t,n,r){if(t!==n){const a=n-t;this.result=this.result*Math.pow(r,a)+Number.parseInt(e.substr(t,a),r),this.consumed+=a}}stateNumericHex(e,t){const n=t;for(;t<e.length;){const r=e.charCodeAt(t);if(!Eo(r)&&!yo(r))return this.addToNumericResult(e,n,t,16),this.emitNumericEntity(r,3);t+=1}return this.addToNumericResult(e,n,t,16),-1}stateNumericDecimal(e,t){const n=t;for(;t<e.length;){const r=e.charCodeAt(t);if(!Eo(r))return this.addToNumericResult(e,n,t,10),this.emitNumericEntity(r,2);t+=1}return this.addToNumericResult(e,n,t,10),-1}emitNumericEntity(e,t){var n;if(this.consumed<=t)return null===(n=this.errors)||void 0===n||n.absenceOfDigitsInNumericCharacterReference(this.consumed),0;if(e===Js.SEMI)this.consumed+=1;else if(this.decodeMode===io.Strict)return 0;return this.emitCodePoint(function(e){var t;return e>=55296&&e<=57343||e>1114111?65533:null!==(t=Zs.get(e))&&void 0!==t?t:e}(this.result),this.consumed),this.errors&&(e!==Js.SEMI&&this.errors.missingSemicolonAfterCharacterReference(),this.errors.validateNumericCharacterReference(this.result)),this.consumed}stateNamedEntity(e,t){const{decodeTree:n}=this;let r=n[this.treeIndex],a=(r&to.VALUE_LENGTH)>>14;for(;t<e.length;t++,this.excess++){const i=e.charCodeAt(t);if(this.treeIndex=To(n,r,this.treeIndex+Math.max(1,a),i),this.treeIndex<0)return 0===this.result||this.decodeMode===io.Attribute&&(0===a||_o(i))?0:this.emitNotTerminatedNamedEntity();if(r=n[this.treeIndex],a=(r&to.VALUE_LENGTH)>>14,0!==a){if(i===Js.SEMI)return this.emitNamedEntityData(this.treeIndex,a,this.consumed+this.excess);this.decodeMode!==io.Strict&&(this.result=this.treeIndex,this.consumed+=this.excess,this.excess=0)}}return-1}emitNotTerminatedNamedEntity(){var e;const{result:t,decodeTree:n}=this,r=(n[t]&to.VALUE_LENGTH)>>14;return this.emitNamedEntityData(t,r,this.consumed),null===(e=this.errors)||void 0===e||e.missingSemicolonAfterCharacterReference(),this.consumed}emitNamedEntityData(e,t,n){const{decodeTree:r}=this;return this.emitCodePoint(1===t?r[e]&~to.VALUE_LENGTH:r[e+1],n),3===t&&this.emitCodePoint(r[e+2],n),n}end(){var e;switch(this.state){case ro.NamedEntity:return 0===this.result||this.decodeMode===io.Attribute&&this.result!==this.treeIndex?0:this.emitNotTerminatedNamedEntity();case ro.NumericDecimal:return this.emitNumericEntity(0,2);case ro.NumericHex:return this.emitNumericEntity(0,3);case ro.NumericStart:return null===(e=this.errors)||void 0===e||e.absenceOfDigitsInNumericCharacterReference(this.consumed),0;case ro.EntityStart:return 0}}}function To(e,t,n,r){const a=(t&to.BRANCH_LENGTH)>>7,i=t&to.JUMP_TABLE;if(0===a)return 0!==i&&r===i?n:-1;if(i){const t=r-i;return t<0||t>=a?-1:e[n+t]-1}let s=n,o=s+a-1;for(;s<=o;){const t=s+o>>>1,n=e[t];if(n<r)s=t+1;else{if(!(n>r))return e[t+a];o=t-1}}return-1}(lo=oo||(oo={})).HTML=\"http://www.w3.org/1999/xhtml\",lo.MATHML=\"http://www.w3.org/1998/Math/MathML\",lo.SVG=\"http://www.w3.org/2000/svg\",lo.XLINK=\"http://www.w3.org/1999/xlink\",lo.XML=\"http://www.w3.org/XML/1998/namespace\",lo.XMLNS=\"http://www.w3.org/2000/xmlns/\",(uo=co||(co={})).TYPE=\"type\",uo.ACTION=\"action\",uo.ENCODING=\"encoding\",uo.PROMPT=\"prompt\",uo.NAME=\"name\",uo.COLOR=\"color\",uo.FACE=\"face\",uo.SIZE=\"size\",(ho=po||(po={})).NO_QUIRKS=\"no-quirks\",ho.QUIRKS=\"quirks\",ho.LIMITED_QUIRKS=\"limited-quirks\",(mo=fo||(fo={})).A=\"a\",mo.ADDRESS=\"address\",mo.ANNOTATION_XML=\"annotation-xml\",mo.APPLET=\"applet\",mo.AREA=\"area\",mo.ARTICLE=\"article\",mo.ASIDE=\"aside\",mo.B=\"b\",mo.BASE=\"base\",mo.BASEFONT=\"basefont\",mo.BGSOUND=\"bgsound\",mo.BIG=\"big\",mo.BLOCKQUOTE=\"blockquote\",mo.BODY=\"body\",mo.BR=\"br\",mo.BUTTON=\"button\",mo.CAPTION=\"caption\",mo.CENTER=\"center\",mo.CODE=\"code\",mo.COL=\"col\",mo.COLGROUP=\"colgroup\",mo.DD=\"dd\",mo.DESC=\"desc\",mo.DETAILS=\"details\",mo.DIALOG=\"dialog\",mo.DIR=\"dir\",mo.DIV=\"div\",mo.DL=\"dl\",mo.DT=\"dt\",mo.EM=\"em\",mo.EMBED=\"embed\",mo.FIELDSET=\"fieldset\",mo.FIGCAPTION=\"figcaption\",mo.FIGURE=\"figure\",mo.FONT=\"font\",mo.FOOTER=\"footer\",mo.FOREIGN_OBJECT=\"foreignObject\",mo.FORM=\"form\",mo.FRAME=\"frame\",mo.FRAMESET=\"frameset\",mo.H1=\"h1\",mo.H2=\"h2\",mo.H3=\"h3\",mo.H4=\"h4\",mo.H5=\"h5\",mo.H6=\"h6\",mo.HEAD=\"head\",mo.HEADER=\"header\",mo.HGROUP=\"hgroup\",mo.HR=\"hr\",mo.HTML=\"html\",mo.I=\"i\",mo.IMG=\"img\",mo.IMAGE=\"image\",mo.INPUT=\"input\",mo.IFRAME=\"iframe\",mo.KEYGEN=\"keygen\",mo.LABEL=\"label\",mo.LI=\"li\",mo.LINK=\"link\",mo.LISTING=\"listing\",mo.MAIN=\"main\",mo.MALIGNMARK=\"malignmark\",mo.MARQUEE=\"marquee\",mo.MATH=\"math\",mo.MENU=\"menu\",mo.META=\"meta\",mo.MGLYPH=\"mglyph\",mo.MI=\"mi\",mo.MO=\"mo\",mo.MN=\"mn\",mo.MS=\"ms\",mo.MTEXT=\"mtext\",mo.NAV=\"nav\",mo.NOBR=\"nobr\",mo.NOFRAMES=\"noframes\",mo.NOEMBED=\"noembed\",mo.NOSCRIPT=\"noscript\",mo.OBJECT=\"object\",mo.OL=\"ol\",mo.OPTGROUP=\"optgroup\",mo.OPTION=\"option\",mo.P=\"p\",mo.PARAM=\"param\",mo.PLAINTEXT=\"plaintext\",mo.PRE=\"pre\",mo.RB=\"rb\",mo.RP=\"rp\",mo.RT=\"rt\",mo.RTC=\"rtc\",mo.RUBY=\"ruby\",mo.S=\"s\",mo.SCRIPT=\"script\",mo.SEARCH=\"search\",mo.SECTION=\"section\",mo.SELECT=\"select\",mo.SOURCE=\"source\",mo.SMALL=\"small\",mo.SPAN=\"span\",mo.STRIKE=\"strike\",mo.STRONG=\"strong\",mo.STYLE=\"style\",mo.SUB=\"sub\",mo.SUMMARY=\"summary\",mo.SUP=\"sup\",mo.TABLE=\"table\",mo.TBODY=\"tbody\",mo.TEMPLATE=\"template\",mo.TEXTAREA=\"textarea\",mo.TFOOT=\"tfoot\",mo.TD=\"td\",mo.TH=\"th\",mo.THEAD=\"thead\",mo.TITLE=\"title\",mo.TR=\"tr\",mo.TRACK=\"track\",mo.TT=\"tt\",mo.U=\"u\",mo.UL=\"ul\",mo.SVG=\"svg\",mo.VAR=\"var\",mo.WBR=\"wbr\",mo.XMP=\"xmp\",(bo=go||(go={}))[bo.UNKNOWN=0]=\"UNKNOWN\",bo[bo.A=1]=\"A\",bo[bo.ADDRESS=2]=\"ADDRESS\",bo[bo.ANNOTATION_XML=3]=\"ANNOTATION_XML\",bo[bo.APPLET=4]=\"APPLET\",bo[bo.AREA=5]=\"AREA\",bo[bo.ARTICLE=6]=\"ARTICLE\",bo[bo.ASIDE=7]=\"ASIDE\",bo[bo.B=8]=\"B\",bo[bo.BASE=9]=\"BASE\",bo[bo.BASEFONT=10]=\"BASEFONT\",bo[bo.BGSOUND=11]=\"BGSOUND\",bo[bo.BIG=12]=\"BIG\",bo[bo.BLOCKQUOTE=13]=\"BLOCKQUOTE\",bo[bo.BODY=14]=\"BODY\",bo[bo.BR=15]=\"BR\",bo[bo.BUTTON=16]=\"BUTTON\",bo[bo.CAPTION=17]=\"CAPTION\",bo[bo.CENTER=18]=\"CENTER\",bo[bo.CODE=19]=\"CODE\",bo[bo.COL=20]=\"COL\",bo[bo.COLGROUP=21]=\"COLGROUP\",bo[bo.DD=22]=\"DD\",bo[bo.DESC=23]=\"DESC\",bo[bo.DETAILS=24]=\"DETAILS\",bo[bo.DIALOG=25]=\"DIALOG\",bo[bo.DIR=26]=\"DIR\",bo[bo.DIV=27]=\"DIV\",bo[bo.DL=28]=\"DL\",bo[bo.DT=29]=\"DT\",bo[bo.EM=30]=\"EM\",bo[bo.EMBED=31]=\"EMBED\",bo[bo.FIELDSET=32]=\"FIELDSET\",bo[bo.FIGCAPTION=33]=\"FIGCAPTION\",bo[bo.FIGURE=34]=\"FIGURE\",bo[bo.FONT=35]=\"FONT\",bo[bo.FOOTER=36]=\"FOOTER\",bo[bo.FOREIGN_OBJECT=37]=\"FOREIGN_OBJECT\",bo[bo.FORM=38]=\"FORM\",bo[bo.FRAME=39]=\"FRAME\",bo[bo.FRAMESET=40]=\"FRAMESET\",bo[bo.H1=41]=\"H1\",bo[bo.H2=42]=\"H2\",bo[bo.H3=43]=\"H3\",bo[bo.H4=44]=\"H4\",bo[bo.H5=45]=\"H5\",bo[bo.H6=46]=\"H6\",bo[bo.HEAD=47]=\"HEAD\",bo[bo.HEADER=48]=\"HEADER\",bo[bo.HGROUP=49]=\"HGROUP\",bo[bo.HR=50]=\"HR\",bo[bo.HTML=51]=\"HTML\",bo[bo.I=52]=\"I\",bo[bo.IMG=53]=\"IMG\",bo[bo.IMAGE=54]=\"IMAGE\",bo[bo.INPUT=55]=\"INPUT\",bo[bo.IFRAME=56]=\"IFRAME\",bo[bo.KEYGEN=57]=\"KEYGEN\",bo[bo.LABEL=58]=\"LABEL\",bo[bo.LI=59]=\"LI\",bo[bo.LINK=60]=\"LINK\",bo[bo.LISTING=61]=\"LISTING\",bo[bo.MAIN=62]=\"MAIN\",bo[bo.MALIGNMARK=63]=\"MALIGNMARK\",bo[bo.MARQUEE=64]=\"MARQUEE\",bo[bo.MATH=65]=\"MATH\",bo[bo.MENU=66]=\"MENU\",bo[bo.META=67]=\"META\",bo[bo.MGLYPH=68]=\"MGLYPH\",bo[bo.MI=69]=\"MI\",bo[bo.MO=70]=\"MO\",bo[bo.MN=71]=\"MN\",bo[bo.MS=72]=\"MS\",bo[bo.MTEXT=73]=\"MTEXT\",bo[bo.NAV=74]=\"NAV\",bo[bo.NOBR=75]=\"NOBR\",bo[bo.NOFRAMES=76]=\"NOFRAMES\",bo[bo.NOEMBED=77]=\"NOEMBED\",bo[bo.NOSCRIPT=78]=\"NOSCRIPT\",bo[bo.OBJECT=79]=\"OBJECT\",bo[bo.OL=80]=\"OL\",bo[bo.OPTGROUP=81]=\"OPTGROUP\",bo[bo.OPTION=82]=\"OPTION\",bo[bo.P=83]=\"P\",bo[bo.PARAM=84]=\"PARAM\",bo[bo.PLAINTEXT=85]=\"PLAINTEXT\",bo[bo.PRE=86]=\"PRE\",bo[bo.RB=87]=\"RB\",bo[bo.RP=88]=\"RP\",bo[bo.RT=89]=\"RT\",bo[bo.RTC=90]=\"RTC\",bo[bo.RUBY=91]=\"RUBY\",bo[bo.S=92]=\"S\",bo[bo.SCRIPT=93]=\"SCRIPT\",bo[bo.SEARCH=94]=\"SEARCH\",bo[bo.SECTION=95]=\"SECTION\",bo[bo.SELECT=96]=\"SELECT\",bo[bo.SOURCE=97]=\"SOURCE\",bo[bo.SMALL=98]=\"SMALL\",bo[bo.SPAN=99]=\"SPAN\",bo[bo.STRIKE=100]=\"STRIKE\",bo[bo.STRONG=101]=\"STRONG\",bo[bo.STYLE=102]=\"STYLE\",bo[bo.SUB=103]=\"SUB\",bo[bo.SUMMARY=104]=\"SUMMARY\",bo[bo.SUP=105]=\"SUP\",bo[bo.TABLE=106]=\"TABLE\",bo[bo.TBODY=107]=\"TBODY\",bo[bo.TEMPLATE=108]=\"TEMPLATE\",bo[bo.TEXTAREA=109]=\"TEXTAREA\",bo[bo.TFOOT=110]=\"TFOOT\",bo[bo.TD=111]=\"TD\",bo[bo.TH=112]=\"TH\",bo[bo.THEAD=113]=\"THEAD\",bo[bo.TITLE=114]=\"TITLE\",bo[bo.TR=115]=\"TR\",bo[bo.TRACK=116]=\"TRACK\",bo[bo.TT=117]=\"TT\",bo[bo.U=118]=\"U\",bo[bo.UL=119]=\"UL\",bo[bo.SVG=120]=\"SVG\",bo[bo.VAR=121]=\"VAR\",bo[bo.WBR=122]=\"WBR\",bo[bo.XMP=123]=\"XMP\";const So=new Map([[fo.A,go.A],[fo.ADDRESS,go.ADDRESS],[fo.ANNOTATION_XML,go.ANNOTATION_XML],[fo.APPLET,go.APPLET],[fo.AREA,go.AREA],[fo.ARTICLE,go.ARTICLE],[fo.ASIDE,go.ASIDE],[fo.B,go.B],[fo.BASE,go.BASE],[fo.BASEFONT,go.BASEFONT],[fo.BGSOUND,go.BGSOUND],[fo.BIG,go.BIG],[fo.BLOCKQUOTE,go.BLOCKQUOTE],[fo.BODY,go.BODY],[fo.BR,go.BR],[fo.BUTTON,go.BUTTON],[fo.CAPTION,go.CAPTION],[fo.CENTER,go.CENTER],[fo.CODE,go.CODE],[fo.COL,go.COL],[fo.COLGROUP,go.COLGROUP],[fo.DD,go.DD],[fo.DESC,go.DESC],[fo.DETAILS,go.DETAILS],[fo.DIALOG,go.DIALOG],[fo.DIR,go.DIR],[fo.DIV,go.DIV],[fo.DL,go.DL],[fo.DT,go.DT],[fo.EM,go.EM],[fo.EMBED,go.EMBED],[fo.FIELDSET,go.FIELDSET],[fo.FIGCAPTION,go.FIGCAPTION],[fo.FIGURE,go.FIGURE],[fo.FONT,go.FONT],[fo.FOOTER,go.FOOTER],[fo.FOREIGN_OBJECT,go.FOREIGN_OBJECT],[fo.FORM,go.FORM],[fo.FRAME,go.FRAME],[fo.FRAMESET,go.FRAMESET],[fo.H1,go.H1],[fo.H2,go.H2],[fo.H3,go.H3],[fo.H4,go.H4],[fo.H5,go.H5],[fo.H6,go.H6],[fo.HEAD,go.HEAD],[fo.HEADER,go.HEADER],[fo.HGROUP,go.HGROUP],[fo.HR,go.HR],[fo.HTML,go.HTML],[fo.I,go.I],[fo.IMG,go.IMG],[fo.IMAGE,go.IMAGE],[fo.INPUT,go.INPUT],[fo.IFRAME,go.IFRAME],[fo.KEYGEN,go.KEYGEN],[fo.LABEL,go.LABEL],[fo.LI,go.LI],[fo.LINK,go.LINK],[fo.LISTING,go.LISTING],[fo.MAIN,go.MAIN],[fo.MALIGNMARK,go.MALIGNMARK],[fo.MARQUEE,go.MARQUEE],[fo.MATH,go.MATH],[fo.MENU,go.MENU],[fo.META,go.META],[fo.MGLYPH,go.MGLYPH],[fo.MI,go.MI],[fo.MO,go.MO],[fo.MN,go.MN],[fo.MS,go.MS],[fo.MTEXT,go.MTEXT],[fo.NAV,go.NAV],[fo.NOBR,go.NOBR],[fo.NOFRAMES,go.NOFRAMES],[fo.NOEMBED,go.NOEMBED],[fo.NOSCRIPT,go.NOSCRIPT],[fo.OBJECT,go.OBJECT],[fo.OL,go.OL],[fo.OPTGROUP,go.OPTGROUP],[fo.OPTION,go.OPTION],[fo.P,go.P],[fo.PARAM,go.PARAM],[fo.PLAINTEXT,go.PLAINTEXT],[fo.PRE,go.PRE],[fo.RB,go.RB],[fo.RP,go.RP],[fo.RT,go.RT],[fo.RTC,go.RTC],[fo.RUBY,go.RUBY],[fo.S,go.S],[fo.SCRIPT,go.SCRIPT],[fo.SEARCH,go.SEARCH],[fo.SECTION,go.SECTION],[fo.SELECT,go.SELECT],[fo.SOURCE,go.SOURCE],[fo.SMALL,go.SMALL],[fo.SPAN,go.SPAN],[fo.STRIKE,go.STRIKE],[fo.STRONG,go.STRONG],[fo.STYLE,go.STYLE],[fo.SUB,go.SUB],[fo.SUMMARY,go.SUMMARY],[fo.SUP,go.SUP],[fo.TABLE,go.TABLE],[fo.TBODY,go.TBODY],[fo.TEMPLATE,go.TEMPLATE],[fo.TEXTAREA,go.TEXTAREA],[fo.TFOOT,go.TFOOT],[fo.TD,go.TD],[fo.TH,go.TH],[fo.THEAD,go.THEAD],[fo.TITLE,go.TITLE],[fo.TR,go.TR],[fo.TRACK,go.TRACK],[fo.TT,go.TT],[fo.U,go.U],[fo.UL,go.UL],[fo.SVG,go.SVG],[fo.VAR,go.VAR],[fo.WBR,go.WBR],[fo.XMP,go.XMP]]);function vo(e){var t;return null!==(t=So.get(e))&&void 0!==t?t:go.UNKNOWN}const Ao=go,No={[oo.HTML]:new Set([Ao.ADDRESS,Ao.APPLET,Ao.AREA,Ao.ARTICLE,Ao.ASIDE,Ao.BASE,Ao.BASEFONT,Ao.BGSOUND,Ao.BLOCKQUOTE,Ao.BODY,Ao.BR,Ao.BUTTON,Ao.CAPTION,Ao.CENTER,Ao.COL,Ao.COLGROUP,Ao.DD,Ao.DETAILS,Ao.DIR,Ao.DIV,Ao.DL,Ao.DT,Ao.EMBED,Ao.FIELDSET,Ao.FIGCAPTION,Ao.FIGURE,Ao.FOOTER,Ao.FORM,Ao.FRAME,Ao.FRAMESET,Ao.H1,Ao.H2,Ao.H3,Ao.H4,Ao.H5,Ao.H6,Ao.HEAD,Ao.HEADER,Ao.HGROUP,Ao.HR,Ao.HTML,Ao.IFRAME,Ao.IMG,Ao.INPUT,Ao.LI,Ao.LINK,Ao.LISTING,Ao.MAIN,Ao.MARQUEE,Ao.MENU,Ao.META,Ao.NAV,Ao.NOEMBED,Ao.NOFRAMES,Ao.NOSCRIPT,Ao.OBJECT,Ao.OL,Ao.P,Ao.PARAM,Ao.PLAINTEXT,Ao.PRE,Ao.SCRIPT,Ao.SECTION,Ao.SELECT,Ao.SOURCE,Ao.STYLE,Ao.SUMMARY,Ao.TABLE,Ao.TBODY,Ao.TD,Ao.TEMPLATE,Ao.TEXTAREA,Ao.TFOOT,Ao.TH,Ao.THEAD,Ao.TITLE,Ao.TR,Ao.TRACK,Ao.UL,Ao.WBR,Ao.XMP]),[oo.MATHML]:new Set([Ao.MI,Ao.MO,Ao.MN,Ao.MS,Ao.MTEXT,Ao.ANNOTATION_XML]),[oo.SVG]:new Set([Ao.TITLE,Ao.FOREIGN_OBJECT,Ao.DESC]),[oo.XLINK]:new Set,[oo.XML]:new Set,[oo.XMLNS]:new Set},Co=new Set([Ao.H1,Ao.H2,Ao.H3,Ao.H4,Ao.H5,Ao.H6]);var xo,wo;fo.STYLE,fo.SCRIPT,fo.XMP,fo.IFRAME,fo.NOEMBED,fo.NOFRAMES,fo.PLAINTEXT,(wo=xo||(xo={}))[wo.DATA=0]=\"DATA\",wo[wo.RCDATA=1]=\"RCDATA\",wo[wo.RAWTEXT=2]=\"RAWTEXT\",wo[wo.SCRIPT_DATA=3]=\"SCRIPT_DATA\",wo[wo.PLAINTEXT=4]=\"PLAINTEXT\",wo[wo.TAG_OPEN=5]=\"TAG_OPEN\",wo[wo.END_TAG_OPEN=6]=\"END_TAG_OPEN\",wo[wo.TAG_NAME=7]=\"TAG_NAME\",wo[wo.RCDATA_LESS_THAN_SIGN=8]=\"RCDATA_LESS_THAN_SIGN\",wo[wo.RCDATA_END_TAG_OPEN=9]=\"RCDATA_END_TAG_OPEN\",wo[wo.RCDATA_END_TAG_NAME=10]=\"RCDATA_END_TAG_NAME\",wo[wo.RAWTEXT_LESS_THAN_SIGN=11]=\"RAWTEXT_LESS_THAN_SIGN\",wo[wo.RAWTEXT_END_TAG_OPEN=12]=\"RAWTEXT_END_TAG_OPEN\",wo[wo.RAWTEXT_END_TAG_NAME=13]=\"RAWTEXT_END_TAG_NAME\",wo[wo.SCRIPT_DATA_LESS_THAN_SIGN=14]=\"SCRIPT_DATA_LESS_THAN_SIGN\",wo[wo.SCRIPT_DATA_END_TAG_OPEN=15]=\"SCRIPT_DATA_END_TAG_OPEN\",wo[wo.SCRIPT_DATA_END_TAG_NAME=16]=\"SCRIPT_DATA_END_TAG_NAME\",wo[wo.SCRIPT_DATA_ESCAPE_START=17]=\"SCRIPT_DATA_ESCAPE_START\",wo[wo.SCRIPT_DATA_ESCAPE_START_DASH=18]=\"SCRIPT_DATA_ESCAPE_START_DASH\",wo[wo.SCRIPT_DATA_ESCAPED=19]=\"SCRIPT_DATA_ESCAPED\",wo[wo.SCRIPT_DATA_ESCAPED_DASH=20]=\"SCRIPT_DATA_ESCAPED_DASH\",wo[wo.SCRIPT_DATA_ESCAPED_DASH_DASH=21]=\"SCRIPT_DATA_ESCAPED_DASH_DASH\",wo[wo.SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN=22]=\"SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN\",wo[wo.SCRIPT_DATA_ESCAPED_END_TAG_OPEN=23]=\"SCRIPT_DATA_ESCAPED_END_TAG_OPEN\",wo[wo.SCRIPT_DATA_ESCAPED_END_TAG_NAME=24]=\"SCRIPT_DATA_ESCAPED_END_TAG_NAME\",wo[wo.SCRIPT_DATA_DOUBLE_ESCAPE_START=25]=\"SCRIPT_DATA_DOUBLE_ESCAPE_START\",wo[wo.SCRIPT_DATA_DOUBLE_ESCAPED=26]=\"SCRIPT_DATA_DOUBLE_ESCAPED\",wo[wo.SCRIPT_DATA_DOUBLE_ESCAPED_DASH=27]=\"SCRIPT_DATA_DOUBLE_ESCAPED_DASH\",wo[wo.SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH=28]=\"SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH\",wo[wo.SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN=29]=\"SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN\",wo[wo.SCRIPT_DATA_DOUBLE_ESCAPE_END=30]=\"SCRIPT_DATA_DOUBLE_ESCAPE_END\",wo[wo.BEFORE_ATTRIBUTE_NAME=31]=\"BEFORE_ATTRIBUTE_NAME\",wo[wo.ATTRIBUTE_NAME=32]=\"ATTRIBUTE_NAME\",wo[wo.AFTER_ATTRIBUTE_NAME=33]=\"AFTER_ATTRIBUTE_NAME\",wo[wo.BEFORE_ATTRIBUTE_VALUE=34]=\"BEFORE_ATTRIBUTE_VALUE\",wo[wo.ATTRIBUTE_VALUE_DOUBLE_QUOTED=35]=\"ATTRIBUTE_VALUE_DOUBLE_QUOTED\",wo[wo.ATTRIBUTE_VALUE_SINGLE_QUOTED=36]=\"ATTRIBUTE_VALUE_SINGLE_QUOTED\",wo[wo.ATTRIBUTE_VALUE_UNQUOTED=37]=\"ATTRIBUTE_VALUE_UNQUOTED\",wo[wo.AFTER_ATTRIBUTE_VALUE_QUOTED=38]=\"AFTER_ATTRIBUTE_VALUE_QUOTED\",wo[wo.SELF_CLOSING_START_TAG=39]=\"SELF_CLOSING_START_TAG\",wo[wo.BOGUS_COMMENT=40]=\"BOGUS_COMMENT\",wo[wo.MARKUP_DECLARATION_OPEN=41]=\"MARKUP_DECLARATION_OPEN\",wo[wo.COMMENT_START=42]=\"COMMENT_START\",wo[wo.COMMENT_START_DASH=43]=\"COMMENT_START_DASH\",wo[wo.COMMENT=44]=\"COMMENT\",wo[wo.COMMENT_LESS_THAN_SIGN=45]=\"COMMENT_LESS_THAN_SIGN\",wo[wo.COMMENT_LESS_THAN_SIGN_BANG=46]=\"COMMENT_LESS_THAN_SIGN_BANG\",wo[wo.COMMENT_LESS_THAN_SIGN_BANG_DASH=47]=\"COMMENT_LESS_THAN_SIGN_BANG_DASH\",wo[wo.COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH=48]=\"COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH\",wo[wo.COMMENT_END_DASH=49]=\"COMMENT_END_DASH\",wo[wo.COMMENT_END=50]=\"COMMENT_END\",wo[wo.COMMENT_END_BANG=51]=\"COMMENT_END_BANG\",wo[wo.DOCTYPE=52]=\"DOCTYPE\",wo[wo.BEFORE_DOCTYPE_NAME=53]=\"BEFORE_DOCTYPE_NAME\",wo[wo.DOCTYPE_NAME=54]=\"DOCTYPE_NAME\",wo[wo.AFTER_DOCTYPE_NAME=55]=\"AFTER_DOCTYPE_NAME\",wo[wo.AFTER_DOCTYPE_PUBLIC_KEYWORD=56]=\"AFTER_DOCTYPE_PUBLIC_KEYWORD\",wo[wo.BEFORE_DOCTYPE_PUBLIC_IDENTIFIER=57]=\"BEFORE_DOCTYPE_PUBLIC_IDENTIFIER\",wo[wo.DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED=58]=\"DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED\",wo[wo.DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED=59]=\"DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED\",wo[wo.AFTER_DOCTYPE_PUBLIC_IDENTIFIER=60]=\"AFTER_DOCTYPE_PUBLIC_IDENTIFIER\",wo[wo.BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS=61]=\"BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS\",wo[wo.AFTER_DOCTYPE_SYSTEM_KEYWORD=62]=\"AFTER_DOCTYPE_SYSTEM_KEYWORD\",wo[wo.BEFORE_DOCTYPE_SYSTEM_IDENTIFIER=63]=\"BEFORE_DOCTYPE_SYSTEM_IDENTIFIER\",wo[wo.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED=64]=\"DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED\",wo[wo.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED=65]=\"DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED\",wo[wo.AFTER_DOCTYPE_SYSTEM_IDENTIFIER=66]=\"AFTER_DOCTYPE_SYSTEM_IDENTIFIER\",wo[wo.BOGUS_DOCTYPE=67]=\"BOGUS_DOCTYPE\",wo[wo.CDATA_SECTION=68]=\"CDATA_SECTION\",wo[wo.CDATA_SECTION_BRACKET=69]=\"CDATA_SECTION_BRACKET\",wo[wo.CDATA_SECTION_END=70]=\"CDATA_SECTION_END\",wo[wo.CHARACTER_REFERENCE=71]=\"CHARACTER_REFERENCE\",wo[wo.AMBIGUOUS_AMPERSAND=72]=\"AMBIGUOUS_AMPERSAND\";const Io={DATA:xo.DATA,RCDATA:xo.RCDATA,RAWTEXT:xo.RAWTEXT,SCRIPT_DATA:xo.SCRIPT_DATA,PLAINTEXT:xo.PLAINTEXT,CDATA_SECTION:xo.CDATA_SECTION};function Oo(e){return e>=Ms.LATIN_CAPITAL_A&&e<=Ms.LATIN_CAPITAL_Z}function Ro(e){return function(e){return e>=Ms.LATIN_SMALL_A&&e<=Ms.LATIN_SMALL_Z}(e)||Oo(e)}function Do(e){return Ro(e)||function(e){return e>=Ms.DIGIT_0&&e<=Ms.DIGIT_9}(e)}function Mo(e){return e+32}function Po(e){return e===Ms.SPACE||e===Ms.LINE_FEED||e===Ms.TABULATION||e===Ms.FORM_FEED}function Lo(e){return Po(e)||e===Ms.SOLIDUS||e===Ms.GREATER_THAN_SIGN}class Fo{constructor(e,t){this.options=e,this.handler=t,this.paused=!1,this.inLoop=!1,this.inForeignNode=!1,this.lastStartTagName=\"\",this.active=!1,this.state=xo.DATA,this.returnState=xo.DATA,this.entityStartPos=0,this.consumedAfterSnapshot=-1,this.currentCharacterToken=null,this.currentToken=null,this.currentAttr={name:\"\",value:\"\"},this.preprocessor=new Ws(t),this.currentLocation=this.getCurrentLocation(-1),this.entityDecoder=new ko(Xs,(e,t)=>{this.preprocessor.pos=this.entityStartPos+t-1,this._flushCodePointConsumedAsCharacterReference(e)},t.onParseError?{missingSemicolonAfterCharacterReference:()=>{this._err(qs.missingSemicolonAfterCharacterReference,1)},absenceOfDigitsInNumericCharacterReference:e=>{this._err(qs.absenceOfDigitsInNumericCharacterReference,this.entityStartPos-this.preprocessor.pos+e)},validateNumericCharacterReference:e=>{const t=function(e){return e===Ms.NULL?qs.nullCharacterReference:e>1114111?qs.characterReferenceOutsideUnicodeRange:Gs(e)?qs.surrogateCharacterReference:$s(e)?qs.noncharacterCharacterReference:js(e)||e===Ms.CARRIAGE_RETURN?qs.controlCharacterReference:null}(e);t&&this._err(t,1)}}:void 0)}_err(e,t=0){var n,r;null===(r=(n=this.handler).onParseError)||void 0===r||r.call(n,this.preprocessor.getError(e,t))}getCurrentLocation(e){return this.options.sourceCodeLocationInfo?{startLine:this.preprocessor.line,startCol:this.preprocessor.col-e,startOffset:this.preprocessor.offset-e,endLine:-1,endCol:-1,endOffset:-1}:null}_runParsingLoop(){if(!this.inLoop){for(this.inLoop=!0;this.active&&!this.paused;){this.consumedAfterSnapshot=0;const e=this._consume();this._ensureHibernation()||this._callState(e)}this.inLoop=!1}}pause(){this.paused=!0}resume(e){if(!this.paused)throw new Error(\"Parser was already resumed\");this.paused=!1,this.inLoop||(this._runParsingLoop(),this.paused||null==e||e())}write(e,t,n){this.active=!0,this.preprocessor.write(e,t),this._runParsingLoop(),this.paused||null==n||n()}insertHtmlAtCurrentPos(e){this.active=!0,this.preprocessor.insertHtmlAtCurrentPos(e),this._runParsingLoop()}_ensureHibernation(){return!!this.preprocessor.endOfChunkHit&&(this.preprocessor.retreat(this.consumedAfterSnapshot),this.consumedAfterSnapshot=0,this.active=!1,!0)}_consume(){return this.consumedAfterSnapshot++,this.preprocessor.advance()}_advanceBy(e){this.consumedAfterSnapshot+=e;for(let t=0;t<e;t++)this.preprocessor.advance()}_consumeSequenceIfMatch(e,t){return!!this.preprocessor.startsWith(e,t)&&(this._advanceBy(e.length-1),!0)}_createStartTagToken(){this.currentToken={type:Vs.START_TAG,tagName:\"\",tagID:go.UNKNOWN,selfClosing:!1,ackSelfClosing:!1,attrs:[],location:this.getCurrentLocation(1)}}_createEndTagToken(){this.currentToken={type:Vs.END_TAG,tagName:\"\",tagID:go.UNKNOWN,selfClosing:!1,ackSelfClosing:!1,attrs:[],location:this.getCurrentLocation(2)}}_createCommentToken(e){this.currentToken={type:Vs.COMMENT,data:\"\",location:this.getCurrentLocation(e)}}_createDoctypeToken(e){this.currentToken={type:Vs.DOCTYPE,name:e,forceQuirks:!1,publicId:null,systemId:null,location:this.currentLocation}}_createCharacterToken(e,t){this.currentCharacterToken={type:e,chars:t,location:this.currentLocation}}_createAttr(e){this.currentAttr={name:e,value:\"\"},this.currentLocation=this.getCurrentLocation(0)}_leaveAttrName(){var e,t;const n=this.currentToken;if(null===Qs(n,this.currentAttr.name)){if(n.attrs.push(this.currentAttr),n.location&&this.currentLocation){(null!==(e=(t=n.location).attrs)&&void 0!==e?e:t.attrs=Object.create(null))[this.currentAttr.name]=this.currentLocation,this._leaveAttrValue()}}else this._err(qs.duplicateAttribute)}_leaveAttrValue(){this.currentLocation&&(this.currentLocation.endLine=this.preprocessor.line,this.currentLocation.endCol=this.preprocessor.col,this.currentLocation.endOffset=this.preprocessor.offset)}prepareToken(e){this._emitCurrentCharacterToken(e.location),this.currentToken=null,e.location&&(e.location.endLine=this.preprocessor.line,e.location.endCol=this.preprocessor.col+1,e.location.endOffset=this.preprocessor.offset+1),this.currentLocation=this.getCurrentLocation(-1)}emitCurrentTagToken(){const e=this.currentToken;this.prepareToken(e),e.tagID=vo(e.tagName),e.type===Vs.START_TAG?(this.lastStartTagName=e.tagName,this.handler.onStartTag(e)):(e.attrs.length>0&&this._err(qs.endTagWithAttributes),e.selfClosing&&this._err(qs.endTagWithTrailingSolidus),this.handler.onEndTag(e)),this.preprocessor.dropParsedChunk()}emitCurrentComment(e){this.prepareToken(e),this.handler.onComment(e),this.preprocessor.dropParsedChunk()}emitCurrentDoctype(e){this.prepareToken(e),this.handler.onDoctype(e),this.preprocessor.dropParsedChunk()}_emitCurrentCharacterToken(e){if(this.currentCharacterToken){switch(e&&this.currentCharacterToken.location&&(this.currentCharacterToken.location.endLine=e.startLine,this.currentCharacterToken.location.endCol=e.startCol,this.currentCharacterToken.location.endOffset=e.startOffset),this.currentCharacterToken.type){case Vs.CHARACTER:this.handler.onCharacter(this.currentCharacterToken);break;case Vs.NULL_CHARACTER:this.handler.onNullCharacter(this.currentCharacterToken);break;case Vs.WHITESPACE_CHARACTER:this.handler.onWhitespaceCharacter(this.currentCharacterToken)}this.currentCharacterToken=null}}_emitEOFToken(){const e=this.getCurrentLocation(0);e&&(e.endLine=e.startLine,e.endCol=e.startCol,e.endOffset=e.startOffset),this._emitCurrentCharacterToken(e),this.handler.onEof({type:Vs.EOF,location:e}),this.active=!1}_appendCharToCurrentCharacterToken(e,t){if(this.currentCharacterToken){if(this.currentCharacterToken.type===e)return void(this.currentCharacterToken.chars+=t);this.currentLocation=this.getCurrentLocation(0),this._emitCurrentCharacterToken(this.currentLocation),this.preprocessor.dropParsedChunk()}this._createCharacterToken(e,t)}_emitCodePoint(e){const t=Po(e)?Vs.WHITESPACE_CHARACTER:e===Ms.NULL?Vs.NULL_CHARACTER:Vs.CHARACTER;this._appendCharToCurrentCharacterToken(t,String.fromCodePoint(e))}_emitChars(e){this._appendCharToCurrentCharacterToken(Vs.CHARACTER,e)}_startCharacterReference(){this.returnState=this.state,this.state=xo.CHARACTER_REFERENCE,this.entityStartPos=this.preprocessor.pos,this.entityDecoder.startEntity(this._isCharacterReferenceInAttribute()?io.Attribute:io.Legacy)}_isCharacterReferenceInAttribute(){return this.returnState===xo.ATTRIBUTE_VALUE_DOUBLE_QUOTED||this.returnState===xo.ATTRIBUTE_VALUE_SINGLE_QUOTED||this.returnState===xo.ATTRIBUTE_VALUE_UNQUOTED}_flushCodePointConsumedAsCharacterReference(e){this._isCharacterReferenceInAttribute()?this.currentAttr.value+=String.fromCodePoint(e):this._emitCodePoint(e)}_callState(e){switch(this.state){case xo.DATA:this._stateData(e);break;case xo.RCDATA:this._stateRcdata(e);break;case xo.RAWTEXT:this._stateRawtext(e);break;case xo.SCRIPT_DATA:this._stateScriptData(e);break;case xo.PLAINTEXT:this._statePlaintext(e);break;case xo.TAG_OPEN:this._stateTagOpen(e);break;case xo.END_TAG_OPEN:this._stateEndTagOpen(e);break;case xo.TAG_NAME:this._stateTagName(e);break;case xo.RCDATA_LESS_THAN_SIGN:this._stateRcdataLessThanSign(e);break;case xo.RCDATA_END_TAG_OPEN:this._stateRcdataEndTagOpen(e);break;case xo.RCDATA_END_TAG_NAME:this._stateRcdataEndTagName(e);break;case xo.RAWTEXT_LESS_THAN_SIGN:this._stateRawtextLessThanSign(e);break;case xo.RAWTEXT_END_TAG_OPEN:this._stateRawtextEndTagOpen(e);break;case xo.RAWTEXT_END_TAG_NAME:this._stateRawtextEndTagName(e);break;case xo.SCRIPT_DATA_LESS_THAN_SIGN:this._stateScriptDataLessThanSign(e);break;case xo.SCRIPT_DATA_END_TAG_OPEN:this._stateScriptDataEndTagOpen(e);break;case xo.SCRIPT_DATA_END_TAG_NAME:this._stateScriptDataEndTagName(e);break;case xo.SCRIPT_DATA_ESCAPE_START:this._stateScriptDataEscapeStart(e);break;case xo.SCRIPT_DATA_ESCAPE_START_DASH:this._stateScriptDataEscapeStartDash(e);break;case xo.SCRIPT_DATA_ESCAPED:this._stateScriptDataEscaped(e);break;case xo.SCRIPT_DATA_ESCAPED_DASH:this._stateScriptDataEscapedDash(e);break;case xo.SCRIPT_DATA_ESCAPED_DASH_DASH:this._stateScriptDataEscapedDashDash(e);break;case xo.SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN:this._stateScriptDataEscapedLessThanSign(e);break;case xo.SCRIPT_DATA_ESCAPED_END_TAG_OPEN:this._stateScriptDataEscapedEndTagOpen(e);break;case xo.SCRIPT_DATA_ESCAPED_END_TAG_NAME:this._stateScriptDataEscapedEndTagName(e);break;case xo.SCRIPT_DATA_DOUBLE_ESCAPE_START:this._stateScriptDataDoubleEscapeStart(e);break;case xo.SCRIPT_DATA_DOUBLE_ESCAPED:this._stateScriptDataDoubleEscaped(e);break;case xo.SCRIPT_DATA_DOUBLE_ESCAPED_DASH:this._stateScriptDataDoubleEscapedDash(e);break;case xo.SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH:this._stateScriptDataDoubleEscapedDashDash(e);break;case xo.SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN:this._stateScriptDataDoubleEscapedLessThanSign(e);break;case xo.SCRIPT_DATA_DOUBLE_ESCAPE_END:this._stateScriptDataDoubleEscapeEnd(e);break;case xo.BEFORE_ATTRIBUTE_NAME:this._stateBeforeAttributeName(e);break;case xo.ATTRIBUTE_NAME:this._stateAttributeName(e);break;case xo.AFTER_ATTRIBUTE_NAME:this._stateAfterAttributeName(e);break;case xo.BEFORE_ATTRIBUTE_VALUE:this._stateBeforeAttributeValue(e);break;case xo.ATTRIBUTE_VALUE_DOUBLE_QUOTED:this._stateAttributeValueDoubleQuoted(e);break;case xo.ATTRIBUTE_VALUE_SINGLE_QUOTED:this._stateAttributeValueSingleQuoted(e);break;case xo.ATTRIBUTE_VALUE_UNQUOTED:this._stateAttributeValueUnquoted(e);break;case xo.AFTER_ATTRIBUTE_VALUE_QUOTED:this._stateAfterAttributeValueQuoted(e);break;case xo.SELF_CLOSING_START_TAG:this._stateSelfClosingStartTag(e);break;case xo.BOGUS_COMMENT:this._stateBogusComment(e);break;case xo.MARKUP_DECLARATION_OPEN:this._stateMarkupDeclarationOpen(e);break;case xo.COMMENT_START:this._stateCommentStart(e);break;case xo.COMMENT_START_DASH:this._stateCommentStartDash(e);break;case xo.COMMENT:this._stateComment(e);break;case xo.COMMENT_LESS_THAN_SIGN:this._stateCommentLessThanSign(e);break;case xo.COMMENT_LESS_THAN_SIGN_BANG:this._stateCommentLessThanSignBang(e);break;case xo.COMMENT_LESS_THAN_SIGN_BANG_DASH:this._stateCommentLessThanSignBangDash(e);break;case xo.COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH:this._stateCommentLessThanSignBangDashDash(e);break;case xo.COMMENT_END_DASH:this._stateCommentEndDash(e);break;case xo.COMMENT_END:this._stateCommentEnd(e);break;case xo.COMMENT_END_BANG:this._stateCommentEndBang(e);break;case xo.DOCTYPE:this._stateDoctype(e);break;case xo.BEFORE_DOCTYPE_NAME:this._stateBeforeDoctypeName(e);break;case xo.DOCTYPE_NAME:this._stateDoctypeName(e);break;case xo.AFTER_DOCTYPE_NAME:this._stateAfterDoctypeName(e);break;case xo.AFTER_DOCTYPE_PUBLIC_KEYWORD:this._stateAfterDoctypePublicKeyword(e);break;case xo.BEFORE_DOCTYPE_PUBLIC_IDENTIFIER:this._stateBeforeDoctypePublicIdentifier(e);break;case xo.DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED:this._stateDoctypePublicIdentifierDoubleQuoted(e);break;case xo.DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED:this._stateDoctypePublicIdentifierSingleQuoted(e);break;case xo.AFTER_DOCTYPE_PUBLIC_IDENTIFIER:this._stateAfterDoctypePublicIdentifier(e);break;case xo.BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS:this._stateBetweenDoctypePublicAndSystemIdentifiers(e);break;case xo.AFTER_DOCTYPE_SYSTEM_KEYWORD:this._stateAfterDoctypeSystemKeyword(e);break;case xo.BEFORE_DOCTYPE_SYSTEM_IDENTIFIER:this._stateBeforeDoctypeSystemIdentifier(e);break;case xo.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED:this._stateDoctypeSystemIdentifierDoubleQuoted(e);break;case xo.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED:this._stateDoctypeSystemIdentifierSingleQuoted(e);break;case xo.AFTER_DOCTYPE_SYSTEM_IDENTIFIER:this._stateAfterDoctypeSystemIdentifier(e);break;case xo.BOGUS_DOCTYPE:this._stateBogusDoctype(e);break;case xo.CDATA_SECTION:this._stateCdataSection(e);break;case xo.CDATA_SECTION_BRACKET:this._stateCdataSectionBracket(e);break;case xo.CDATA_SECTION_END:this._stateCdataSectionEnd(e);break;case xo.CHARACTER_REFERENCE:this._stateCharacterReference();break;case xo.AMBIGUOUS_AMPERSAND:this._stateAmbiguousAmpersand(e);break;default:throw new Error(\"Unknown state\")}}_stateData(e){switch(e){case Ms.LESS_THAN_SIGN:this.state=xo.TAG_OPEN;break;case Ms.AMPERSAND:this._startCharacterReference();break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),this._emitCodePoint(e);break;case Ms.EOF:this._emitEOFToken();break;default:this._emitCodePoint(e)}}_stateRcdata(e){switch(e){case Ms.AMPERSAND:this._startCharacterReference();break;case Ms.LESS_THAN_SIGN:this.state=xo.RCDATA_LESS_THAN_SIGN;break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),this._emitChars(Ds);break;case Ms.EOF:this._emitEOFToken();break;default:this._emitCodePoint(e)}}_stateRawtext(e){switch(e){case Ms.LESS_THAN_SIGN:this.state=xo.RAWTEXT_LESS_THAN_SIGN;break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),this._emitChars(Ds);break;case Ms.EOF:this._emitEOFToken();break;default:this._emitCodePoint(e)}}_stateScriptData(e){switch(e){case Ms.LESS_THAN_SIGN:this.state=xo.SCRIPT_DATA_LESS_THAN_SIGN;break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),this._emitChars(Ds);break;case Ms.EOF:this._emitEOFToken();break;default:this._emitCodePoint(e)}}_statePlaintext(e){switch(e){case Ms.NULL:this._err(qs.unexpectedNullCharacter),this._emitChars(Ds);break;case Ms.EOF:this._emitEOFToken();break;default:this._emitCodePoint(e)}}_stateTagOpen(e){if(Ro(e))this._createStartTagToken(),this.state=xo.TAG_NAME,this._stateTagName(e);else switch(e){case Ms.EXCLAMATION_MARK:this.state=xo.MARKUP_DECLARATION_OPEN;break;case Ms.SOLIDUS:this.state=xo.END_TAG_OPEN;break;case Ms.QUESTION_MARK:this._err(qs.unexpectedQuestionMarkInsteadOfTagName),this._createCommentToken(1),this.state=xo.BOGUS_COMMENT,this._stateBogusComment(e);break;case Ms.EOF:this._err(qs.eofBeforeTagName),this._emitChars(\"<\"),this._emitEOFToken();break;default:this._err(qs.invalidFirstCharacterOfTagName),this._emitChars(\"<\"),this.state=xo.DATA,this._stateData(e)}}_stateEndTagOpen(e){if(Ro(e))this._createEndTagToken(),this.state=xo.TAG_NAME,this._stateTagName(e);else switch(e){case Ms.GREATER_THAN_SIGN:this._err(qs.missingEndTagName),this.state=xo.DATA;break;case Ms.EOF:this._err(qs.eofBeforeTagName),this._emitChars(\"</\"),this._emitEOFToken();break;default:this._err(qs.invalidFirstCharacterOfTagName),this._createCommentToken(2),this.state=xo.BOGUS_COMMENT,this._stateBogusComment(e)}}_stateTagName(e){const t=this.currentToken;switch(e){case Ms.SPACE:case Ms.LINE_FEED:case Ms.TABULATION:case Ms.FORM_FEED:this.state=xo.BEFORE_ATTRIBUTE_NAME;break;case Ms.SOLIDUS:this.state=xo.SELF_CLOSING_START_TAG;break;case Ms.GREATER_THAN_SIGN:this.state=xo.DATA,this.emitCurrentTagToken();break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),t.tagName+=Ds;break;case Ms.EOF:this._err(qs.eofInTag),this._emitEOFToken();break;default:t.tagName+=String.fromCodePoint(Oo(e)?Mo(e):e)}}_stateRcdataLessThanSign(e){e===Ms.SOLIDUS?this.state=xo.RCDATA_END_TAG_OPEN:(this._emitChars(\"<\"),this.state=xo.RCDATA,this._stateRcdata(e))}_stateRcdataEndTagOpen(e){Ro(e)?(this.state=xo.RCDATA_END_TAG_NAME,this._stateRcdataEndTagName(e)):(this._emitChars(\"</\"),this.state=xo.RCDATA,this._stateRcdata(e))}handleSpecialEndTag(e){if(!this.preprocessor.startsWith(this.lastStartTagName,!1))return!this._ensureHibernation();this._createEndTagToken();this.currentToken.tagName=this.lastStartTagName;switch(this.preprocessor.peek(this.lastStartTagName.length)){case Ms.SPACE:case Ms.LINE_FEED:case Ms.TABULATION:case Ms.FORM_FEED:return this._advanceBy(this.lastStartTagName.length),this.state=xo.BEFORE_ATTRIBUTE_NAME,!1;case Ms.SOLIDUS:return this._advanceBy(this.lastStartTagName.length),this.state=xo.SELF_CLOSING_START_TAG,!1;case Ms.GREATER_THAN_SIGN:return this._advanceBy(this.lastStartTagName.length),this.emitCurrentTagToken(),this.state=xo.DATA,!1;default:return!this._ensureHibernation()}}_stateRcdataEndTagName(e){this.handleSpecialEndTag(e)&&(this._emitChars(\"</\"),this.state=xo.RCDATA,this._stateRcdata(e))}_stateRawtextLessThanSign(e){e===Ms.SOLIDUS?this.state=xo.RAWTEXT_END_TAG_OPEN:(this._emitChars(\"<\"),this.state=xo.RAWTEXT,this._stateRawtext(e))}_stateRawtextEndTagOpen(e){Ro(e)?(this.state=xo.RAWTEXT_END_TAG_NAME,this._stateRawtextEndTagName(e)):(this._emitChars(\"</\"),this.state=xo.RAWTEXT,this._stateRawtext(e))}_stateRawtextEndTagName(e){this.handleSpecialEndTag(e)&&(this._emitChars(\"</\"),this.state=xo.RAWTEXT,this._stateRawtext(e))}_stateScriptDataLessThanSign(e){switch(e){case Ms.SOLIDUS:this.state=xo.SCRIPT_DATA_END_TAG_OPEN;break;case Ms.EXCLAMATION_MARK:this.state=xo.SCRIPT_DATA_ESCAPE_START,this._emitChars(\"<!\");break;default:this._emitChars(\"<\"),this.state=xo.SCRIPT_DATA,this._stateScriptData(e)}}_stateScriptDataEndTagOpen(e){Ro(e)?(this.state=xo.SCRIPT_DATA_END_TAG_NAME,this._stateScriptDataEndTagName(e)):(this._emitChars(\"</\"),this.state=xo.SCRIPT_DATA,this._stateScriptData(e))}_stateScriptDataEndTagName(e){this.handleSpecialEndTag(e)&&(this._emitChars(\"</\"),this.state=xo.SCRIPT_DATA,this._stateScriptData(e))}_stateScriptDataEscapeStart(e){e===Ms.HYPHEN_MINUS?(this.state=xo.SCRIPT_DATA_ESCAPE_START_DASH,this._emitChars(\"-\")):(this.state=xo.SCRIPT_DATA,this._stateScriptData(e))}_stateScriptDataEscapeStartDash(e){e===Ms.HYPHEN_MINUS?(this.state=xo.SCRIPT_DATA_ESCAPED_DASH_DASH,this._emitChars(\"-\")):(this.state=xo.SCRIPT_DATA,this._stateScriptData(e))}_stateScriptDataEscaped(e){switch(e){case Ms.HYPHEN_MINUS:this.state=xo.SCRIPT_DATA_ESCAPED_DASH,this._emitChars(\"-\");break;case Ms.LESS_THAN_SIGN:this.state=xo.SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN;break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),this._emitChars(Ds);break;case Ms.EOF:this._err(qs.eofInScriptHtmlCommentLikeText),this._emitEOFToken();break;default:this._emitCodePoint(e)}}_stateScriptDataEscapedDash(e){switch(e){case Ms.HYPHEN_MINUS:this.state=xo.SCRIPT_DATA_ESCAPED_DASH_DASH,this._emitChars(\"-\");break;case Ms.LESS_THAN_SIGN:this.state=xo.SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN;break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),this.state=xo.SCRIPT_DATA_ESCAPED,this._emitChars(Ds);break;case Ms.EOF:this._err(qs.eofInScriptHtmlCommentLikeText),this._emitEOFToken();break;default:this.state=xo.SCRIPT_DATA_ESCAPED,this._emitCodePoint(e)}}_stateScriptDataEscapedDashDash(e){switch(e){case Ms.HYPHEN_MINUS:this._emitChars(\"-\");break;case Ms.LESS_THAN_SIGN:this.state=xo.SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN;break;case Ms.GREATER_THAN_SIGN:this.state=xo.SCRIPT_DATA,this._emitChars(\">\");break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),this.state=xo.SCRIPT_DATA_ESCAPED,this._emitChars(Ds);break;case Ms.EOF:this._err(qs.eofInScriptHtmlCommentLikeText),this._emitEOFToken();break;default:this.state=xo.SCRIPT_DATA_ESCAPED,this._emitCodePoint(e)}}_stateScriptDataEscapedLessThanSign(e){e===Ms.SOLIDUS?this.state=xo.SCRIPT_DATA_ESCAPED_END_TAG_OPEN:Ro(e)?(this._emitChars(\"<\"),this.state=xo.SCRIPT_DATA_DOUBLE_ESCAPE_START,this._stateScriptDataDoubleEscapeStart(e)):(this._emitChars(\"<\"),this.state=xo.SCRIPT_DATA_ESCAPED,this._stateScriptDataEscaped(e))}_stateScriptDataEscapedEndTagOpen(e){Ro(e)?(this.state=xo.SCRIPT_DATA_ESCAPED_END_TAG_NAME,this._stateScriptDataEscapedEndTagName(e)):(this._emitChars(\"</\"),this.state=xo.SCRIPT_DATA_ESCAPED,this._stateScriptDataEscaped(e))}_stateScriptDataEscapedEndTagName(e){this.handleSpecialEndTag(e)&&(this._emitChars(\"</\"),this.state=xo.SCRIPT_DATA_ESCAPED,this._stateScriptDataEscaped(e))}_stateScriptDataDoubleEscapeStart(e){if(this.preprocessor.startsWith(Us,!1)&&Lo(this.preprocessor.peek(Us.length))){this._emitCodePoint(e);for(let e=0;e<Us.length;e++)this._emitCodePoint(this._consume());this.state=xo.SCRIPT_DATA_DOUBLE_ESCAPED}else this._ensureHibernation()||(this.state=xo.SCRIPT_DATA_ESCAPED,this._stateScriptDataEscaped(e))}_stateScriptDataDoubleEscaped(e){switch(e){case Ms.HYPHEN_MINUS:this.state=xo.SCRIPT_DATA_DOUBLE_ESCAPED_DASH,this._emitChars(\"-\");break;case Ms.LESS_THAN_SIGN:this.state=xo.SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN,this._emitChars(\"<\");break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),this._emitChars(Ds);break;case Ms.EOF:this._err(qs.eofInScriptHtmlCommentLikeText),this._emitEOFToken();break;default:this._emitCodePoint(e)}}_stateScriptDataDoubleEscapedDash(e){switch(e){case Ms.HYPHEN_MINUS:this.state=xo.SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH,this._emitChars(\"-\");break;case Ms.LESS_THAN_SIGN:this.state=xo.SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN,this._emitChars(\"<\");break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),this.state=xo.SCRIPT_DATA_DOUBLE_ESCAPED,this._emitChars(Ds);break;case Ms.EOF:this._err(qs.eofInScriptHtmlCommentLikeText),this._emitEOFToken();break;default:this.state=xo.SCRIPT_DATA_DOUBLE_ESCAPED,this._emitCodePoint(e)}}_stateScriptDataDoubleEscapedDashDash(e){switch(e){case Ms.HYPHEN_MINUS:this._emitChars(\"-\");break;case Ms.LESS_THAN_SIGN:this.state=xo.SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN,this._emitChars(\"<\");break;case Ms.GREATER_THAN_SIGN:this.state=xo.SCRIPT_DATA,this._emitChars(\">\");break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),this.state=xo.SCRIPT_DATA_DOUBLE_ESCAPED,this._emitChars(Ds);break;case Ms.EOF:this._err(qs.eofInScriptHtmlCommentLikeText),this._emitEOFToken();break;default:this.state=xo.SCRIPT_DATA_DOUBLE_ESCAPED,this._emitCodePoint(e)}}_stateScriptDataDoubleEscapedLessThanSign(e){e===Ms.SOLIDUS?(this.state=xo.SCRIPT_DATA_DOUBLE_ESCAPE_END,this._emitChars(\"/\")):(this.state=xo.SCRIPT_DATA_DOUBLE_ESCAPED,this._stateScriptDataDoubleEscaped(e))}_stateScriptDataDoubleEscapeEnd(e){if(this.preprocessor.startsWith(Us,!1)&&Lo(this.preprocessor.peek(Us.length))){this._emitCodePoint(e);for(let e=0;e<Us.length;e++)this._emitCodePoint(this._consume());this.state=xo.SCRIPT_DATA_ESCAPED}else this._ensureHibernation()||(this.state=xo.SCRIPT_DATA_DOUBLE_ESCAPED,this._stateScriptDataDoubleEscaped(e))}_stateBeforeAttributeName(e){switch(e){case Ms.SPACE:case Ms.LINE_FEED:case Ms.TABULATION:case Ms.FORM_FEED:break;case Ms.SOLIDUS:case Ms.GREATER_THAN_SIGN:case Ms.EOF:this.state=xo.AFTER_ATTRIBUTE_NAME,this._stateAfterAttributeName(e);break;case Ms.EQUALS_SIGN:this._err(qs.unexpectedEqualsSignBeforeAttributeName),this._createAttr(\"=\"),this.state=xo.ATTRIBUTE_NAME;break;default:this._createAttr(\"\"),this.state=xo.ATTRIBUTE_NAME,this._stateAttributeName(e)}}_stateAttributeName(e){switch(e){case Ms.SPACE:case Ms.LINE_FEED:case Ms.TABULATION:case Ms.FORM_FEED:case Ms.SOLIDUS:case Ms.GREATER_THAN_SIGN:case Ms.EOF:this._leaveAttrName(),this.state=xo.AFTER_ATTRIBUTE_NAME,this._stateAfterAttributeName(e);break;case Ms.EQUALS_SIGN:this._leaveAttrName(),this.state=xo.BEFORE_ATTRIBUTE_VALUE;break;case Ms.QUOTATION_MARK:case Ms.APOSTROPHE:case Ms.LESS_THAN_SIGN:this._err(qs.unexpectedCharacterInAttributeName),this.currentAttr.name+=String.fromCodePoint(e);break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),this.currentAttr.name+=Ds;break;default:this.currentAttr.name+=String.fromCodePoint(Oo(e)?Mo(e):e)}}_stateAfterAttributeName(e){switch(e){case Ms.SPACE:case Ms.LINE_FEED:case Ms.TABULATION:case Ms.FORM_FEED:break;case Ms.SOLIDUS:this.state=xo.SELF_CLOSING_START_TAG;break;case Ms.EQUALS_SIGN:this.state=xo.BEFORE_ATTRIBUTE_VALUE;break;case Ms.GREATER_THAN_SIGN:this.state=xo.DATA,this.emitCurrentTagToken();break;case Ms.EOF:this._err(qs.eofInTag),this._emitEOFToken();break;default:this._createAttr(\"\"),this.state=xo.ATTRIBUTE_NAME,this._stateAttributeName(e)}}_stateBeforeAttributeValue(e){switch(e){case Ms.SPACE:case Ms.LINE_FEED:case Ms.TABULATION:case Ms.FORM_FEED:break;case Ms.QUOTATION_MARK:this.state=xo.ATTRIBUTE_VALUE_DOUBLE_QUOTED;break;case Ms.APOSTROPHE:this.state=xo.ATTRIBUTE_VALUE_SINGLE_QUOTED;break;case Ms.GREATER_THAN_SIGN:this._err(qs.missingAttributeValue),this.state=xo.DATA,this.emitCurrentTagToken();break;default:this.state=xo.ATTRIBUTE_VALUE_UNQUOTED,this._stateAttributeValueUnquoted(e)}}_stateAttributeValueDoubleQuoted(e){switch(e){case Ms.QUOTATION_MARK:this.state=xo.AFTER_ATTRIBUTE_VALUE_QUOTED;break;case Ms.AMPERSAND:this._startCharacterReference();break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),this.currentAttr.value+=Ds;break;case Ms.EOF:this._err(qs.eofInTag),this._emitEOFToken();break;default:this.currentAttr.value+=String.fromCodePoint(e)}}_stateAttributeValueSingleQuoted(e){switch(e){case Ms.APOSTROPHE:this.state=xo.AFTER_ATTRIBUTE_VALUE_QUOTED;break;case Ms.AMPERSAND:this._startCharacterReference();break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),this.currentAttr.value+=Ds;break;case Ms.EOF:this._err(qs.eofInTag),this._emitEOFToken();break;default:this.currentAttr.value+=String.fromCodePoint(e)}}_stateAttributeValueUnquoted(e){switch(e){case Ms.SPACE:case Ms.LINE_FEED:case Ms.TABULATION:case Ms.FORM_FEED:this._leaveAttrValue(),this.state=xo.BEFORE_ATTRIBUTE_NAME;break;case Ms.AMPERSAND:this._startCharacterReference();break;case Ms.GREATER_THAN_SIGN:this._leaveAttrValue(),this.state=xo.DATA,this.emitCurrentTagToken();break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),this.currentAttr.value+=Ds;break;case Ms.QUOTATION_MARK:case Ms.APOSTROPHE:case Ms.LESS_THAN_SIGN:case Ms.EQUALS_SIGN:case Ms.GRAVE_ACCENT:this._err(qs.unexpectedCharacterInUnquotedAttributeValue),this.currentAttr.value+=String.fromCodePoint(e);break;case Ms.EOF:this._err(qs.eofInTag),this._emitEOFToken();break;default:this.currentAttr.value+=String.fromCodePoint(e)}}_stateAfterAttributeValueQuoted(e){switch(e){case Ms.SPACE:case Ms.LINE_FEED:case Ms.TABULATION:case Ms.FORM_FEED:this._leaveAttrValue(),this.state=xo.BEFORE_ATTRIBUTE_NAME;break;case Ms.SOLIDUS:this._leaveAttrValue(),this.state=xo.SELF_CLOSING_START_TAG;break;case Ms.GREATER_THAN_SIGN:this._leaveAttrValue(),this.state=xo.DATA,this.emitCurrentTagToken();break;case Ms.EOF:this._err(qs.eofInTag),this._emitEOFToken();break;default:this._err(qs.missingWhitespaceBetweenAttributes),this.state=xo.BEFORE_ATTRIBUTE_NAME,this._stateBeforeAttributeName(e)}}_stateSelfClosingStartTag(e){switch(e){case Ms.GREATER_THAN_SIGN:this.currentToken.selfClosing=!0,this.state=xo.DATA,this.emitCurrentTagToken();break;case Ms.EOF:this._err(qs.eofInTag),this._emitEOFToken();break;default:this._err(qs.unexpectedSolidusInTag),this.state=xo.BEFORE_ATTRIBUTE_NAME,this._stateBeforeAttributeName(e)}}_stateBogusComment(e){const t=this.currentToken;switch(e){case Ms.GREATER_THAN_SIGN:this.state=xo.DATA,this.emitCurrentComment(t);break;case Ms.EOF:this.emitCurrentComment(t),this._emitEOFToken();break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),t.data+=Ds;break;default:t.data+=String.fromCodePoint(e)}}_stateMarkupDeclarationOpen(e){this._consumeSequenceIfMatch(Ls,!0)?(this._createCommentToken(Ls.length+1),this.state=xo.COMMENT_START):this._consumeSequenceIfMatch(Bs,!1)?(this.currentLocation=this.getCurrentLocation(Bs.length+1),this.state=xo.DOCTYPE):this._consumeSequenceIfMatch(Fs,!0)?this.inForeignNode?this.state=xo.CDATA_SECTION:(this._err(qs.cdataInHtmlContent),this._createCommentToken(Fs.length+1),this.currentToken.data=\"[CDATA[\",this.state=xo.BOGUS_COMMENT):this._ensureHibernation()||(this._err(qs.incorrectlyOpenedComment),this._createCommentToken(2),this.state=xo.BOGUS_COMMENT,this._stateBogusComment(e))}_stateCommentStart(e){switch(e){case Ms.HYPHEN_MINUS:this.state=xo.COMMENT_START_DASH;break;case Ms.GREATER_THAN_SIGN:{this._err(qs.abruptClosingOfEmptyComment),this.state=xo.DATA;const e=this.currentToken;this.emitCurrentComment(e);break}default:this.state=xo.COMMENT,this._stateComment(e)}}_stateCommentStartDash(e){const t=this.currentToken;switch(e){case Ms.HYPHEN_MINUS:this.state=xo.COMMENT_END;break;case Ms.GREATER_THAN_SIGN:this._err(qs.abruptClosingOfEmptyComment),this.state=xo.DATA,this.emitCurrentComment(t);break;case Ms.EOF:this._err(qs.eofInComment),this.emitCurrentComment(t),this._emitEOFToken();break;default:t.data+=\"-\",this.state=xo.COMMENT,this._stateComment(e)}}_stateComment(e){const t=this.currentToken;switch(e){case Ms.HYPHEN_MINUS:this.state=xo.COMMENT_END_DASH;break;case Ms.LESS_THAN_SIGN:t.data+=\"<\",this.state=xo.COMMENT_LESS_THAN_SIGN;break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),t.data+=Ds;break;case Ms.EOF:this._err(qs.eofInComment),this.emitCurrentComment(t),this._emitEOFToken();break;default:t.data+=String.fromCodePoint(e)}}_stateCommentLessThanSign(e){const t=this.currentToken;switch(e){case Ms.EXCLAMATION_MARK:t.data+=\"!\",this.state=xo.COMMENT_LESS_THAN_SIGN_BANG;break;case Ms.LESS_THAN_SIGN:t.data+=\"<\";break;default:this.state=xo.COMMENT,this._stateComment(e)}}_stateCommentLessThanSignBang(e){e===Ms.HYPHEN_MINUS?this.state=xo.COMMENT_LESS_THAN_SIGN_BANG_DASH:(this.state=xo.COMMENT,this._stateComment(e))}_stateCommentLessThanSignBangDash(e){e===Ms.HYPHEN_MINUS?this.state=xo.COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH:(this.state=xo.COMMENT_END_DASH,this._stateCommentEndDash(e))}_stateCommentLessThanSignBangDashDash(e){e!==Ms.GREATER_THAN_SIGN&&e!==Ms.EOF&&this._err(qs.nestedComment),this.state=xo.COMMENT_END,this._stateCommentEnd(e)}_stateCommentEndDash(e){const t=this.currentToken;switch(e){case Ms.HYPHEN_MINUS:this.state=xo.COMMENT_END;break;case Ms.EOF:this._err(qs.eofInComment),this.emitCurrentComment(t),this._emitEOFToken();break;default:t.data+=\"-\",this.state=xo.COMMENT,this._stateComment(e)}}_stateCommentEnd(e){const t=this.currentToken;switch(e){case Ms.GREATER_THAN_SIGN:this.state=xo.DATA,this.emitCurrentComment(t);break;case Ms.EXCLAMATION_MARK:this.state=xo.COMMENT_END_BANG;break;case Ms.HYPHEN_MINUS:t.data+=\"-\";break;case Ms.EOF:this._err(qs.eofInComment),this.emitCurrentComment(t),this._emitEOFToken();break;default:t.data+=\"--\",this.state=xo.COMMENT,this._stateComment(e)}}_stateCommentEndBang(e){const t=this.currentToken;switch(e){case Ms.HYPHEN_MINUS:t.data+=\"--!\",this.state=xo.COMMENT_END_DASH;break;case Ms.GREATER_THAN_SIGN:this._err(qs.incorrectlyClosedComment),this.state=xo.DATA,this.emitCurrentComment(t);break;case Ms.EOF:this._err(qs.eofInComment),this.emitCurrentComment(t),this._emitEOFToken();break;default:t.data+=\"--!\",this.state=xo.COMMENT,this._stateComment(e)}}_stateDoctype(e){switch(e){case Ms.SPACE:case Ms.LINE_FEED:case Ms.TABULATION:case Ms.FORM_FEED:this.state=xo.BEFORE_DOCTYPE_NAME;break;case Ms.GREATER_THAN_SIGN:this.state=xo.BEFORE_DOCTYPE_NAME,this._stateBeforeDoctypeName(e);break;case Ms.EOF:{this._err(qs.eofInDoctype),this._createDoctypeToken(null);const e=this.currentToken;e.forceQuirks=!0,this.emitCurrentDoctype(e),this._emitEOFToken();break}default:this._err(qs.missingWhitespaceBeforeDoctypeName),this.state=xo.BEFORE_DOCTYPE_NAME,this._stateBeforeDoctypeName(e)}}_stateBeforeDoctypeName(e){if(Oo(e))this._createDoctypeToken(String.fromCharCode(Mo(e))),this.state=xo.DOCTYPE_NAME;else switch(e){case Ms.SPACE:case Ms.LINE_FEED:case Ms.TABULATION:case Ms.FORM_FEED:break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),this._createDoctypeToken(Ds),this.state=xo.DOCTYPE_NAME;break;case Ms.GREATER_THAN_SIGN:{this._err(qs.missingDoctypeName),this._createDoctypeToken(null);const e=this.currentToken;e.forceQuirks=!0,this.emitCurrentDoctype(e),this.state=xo.DATA;break}case Ms.EOF:{this._err(qs.eofInDoctype),this._createDoctypeToken(null);const e=this.currentToken;e.forceQuirks=!0,this.emitCurrentDoctype(e),this._emitEOFToken();break}default:this._createDoctypeToken(String.fromCodePoint(e)),this.state=xo.DOCTYPE_NAME}}_stateDoctypeName(e){const t=this.currentToken;switch(e){case Ms.SPACE:case Ms.LINE_FEED:case Ms.TABULATION:case Ms.FORM_FEED:this.state=xo.AFTER_DOCTYPE_NAME;break;case Ms.GREATER_THAN_SIGN:this.state=xo.DATA,this.emitCurrentDoctype(t);break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),t.name+=Ds;break;case Ms.EOF:this._err(qs.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:t.name+=String.fromCodePoint(Oo(e)?Mo(e):e)}}_stateAfterDoctypeName(e){const t=this.currentToken;switch(e){case Ms.SPACE:case Ms.LINE_FEED:case Ms.TABULATION:case Ms.FORM_FEED:break;case Ms.GREATER_THAN_SIGN:this.state=xo.DATA,this.emitCurrentDoctype(t);break;case Ms.EOF:this._err(qs.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:this._consumeSequenceIfMatch(Hs,!1)?this.state=xo.AFTER_DOCTYPE_PUBLIC_KEYWORD:this._consumeSequenceIfMatch(zs,!1)?this.state=xo.AFTER_DOCTYPE_SYSTEM_KEYWORD:this._ensureHibernation()||(this._err(qs.invalidCharacterSequenceAfterDoctypeName),t.forceQuirks=!0,this.state=xo.BOGUS_DOCTYPE,this._stateBogusDoctype(e))}}_stateAfterDoctypePublicKeyword(e){const t=this.currentToken;switch(e){case Ms.SPACE:case Ms.LINE_FEED:case Ms.TABULATION:case Ms.FORM_FEED:this.state=xo.BEFORE_DOCTYPE_PUBLIC_IDENTIFIER;break;case Ms.QUOTATION_MARK:this._err(qs.missingWhitespaceAfterDoctypePublicKeyword),t.publicId=\"\",this.state=xo.DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED;break;case Ms.APOSTROPHE:this._err(qs.missingWhitespaceAfterDoctypePublicKeyword),t.publicId=\"\",this.state=xo.DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED;break;case Ms.GREATER_THAN_SIGN:this._err(qs.missingDoctypePublicIdentifier),t.forceQuirks=!0,this.state=xo.DATA,this.emitCurrentDoctype(t);break;case Ms.EOF:this._err(qs.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:this._err(qs.missingQuoteBeforeDoctypePublicIdentifier),t.forceQuirks=!0,this.state=xo.BOGUS_DOCTYPE,this._stateBogusDoctype(e)}}_stateBeforeDoctypePublicIdentifier(e){const t=this.currentToken;switch(e){case Ms.SPACE:case Ms.LINE_FEED:case Ms.TABULATION:case Ms.FORM_FEED:break;case Ms.QUOTATION_MARK:t.publicId=\"\",this.state=xo.DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED;break;case Ms.APOSTROPHE:t.publicId=\"\",this.state=xo.DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED;break;case Ms.GREATER_THAN_SIGN:this._err(qs.missingDoctypePublicIdentifier),t.forceQuirks=!0,this.state=xo.DATA,this.emitCurrentDoctype(t);break;case Ms.EOF:this._err(qs.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:this._err(qs.missingQuoteBeforeDoctypePublicIdentifier),t.forceQuirks=!0,this.state=xo.BOGUS_DOCTYPE,this._stateBogusDoctype(e)}}_stateDoctypePublicIdentifierDoubleQuoted(e){const t=this.currentToken;switch(e){case Ms.QUOTATION_MARK:this.state=xo.AFTER_DOCTYPE_PUBLIC_IDENTIFIER;break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),t.publicId+=Ds;break;case Ms.GREATER_THAN_SIGN:this._err(qs.abruptDoctypePublicIdentifier),t.forceQuirks=!0,this.emitCurrentDoctype(t),this.state=xo.DATA;break;case Ms.EOF:this._err(qs.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:t.publicId+=String.fromCodePoint(e)}}_stateDoctypePublicIdentifierSingleQuoted(e){const t=this.currentToken;switch(e){case Ms.APOSTROPHE:this.state=xo.AFTER_DOCTYPE_PUBLIC_IDENTIFIER;break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),t.publicId+=Ds;break;case Ms.GREATER_THAN_SIGN:this._err(qs.abruptDoctypePublicIdentifier),t.forceQuirks=!0,this.emitCurrentDoctype(t),this.state=xo.DATA;break;case Ms.EOF:this._err(qs.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:t.publicId+=String.fromCodePoint(e)}}_stateAfterDoctypePublicIdentifier(e){const t=this.currentToken;switch(e){case Ms.SPACE:case Ms.LINE_FEED:case Ms.TABULATION:case Ms.FORM_FEED:this.state=xo.BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS;break;case Ms.GREATER_THAN_SIGN:this.state=xo.DATA,this.emitCurrentDoctype(t);break;case Ms.QUOTATION_MARK:this._err(qs.missingWhitespaceBetweenDoctypePublicAndSystemIdentifiers),t.systemId=\"\",this.state=xo.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED;break;case Ms.APOSTROPHE:this._err(qs.missingWhitespaceBetweenDoctypePublicAndSystemIdentifiers),t.systemId=\"\",this.state=xo.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED;break;case Ms.EOF:this._err(qs.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:this._err(qs.missingQuoteBeforeDoctypeSystemIdentifier),t.forceQuirks=!0,this.state=xo.BOGUS_DOCTYPE,this._stateBogusDoctype(e)}}_stateBetweenDoctypePublicAndSystemIdentifiers(e){const t=this.currentToken;switch(e){case Ms.SPACE:case Ms.LINE_FEED:case Ms.TABULATION:case Ms.FORM_FEED:break;case Ms.GREATER_THAN_SIGN:this.emitCurrentDoctype(t),this.state=xo.DATA;break;case Ms.QUOTATION_MARK:t.systemId=\"\",this.state=xo.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED;break;case Ms.APOSTROPHE:t.systemId=\"\",this.state=xo.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED;break;case Ms.EOF:this._err(qs.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:this._err(qs.missingQuoteBeforeDoctypeSystemIdentifier),t.forceQuirks=!0,this.state=xo.BOGUS_DOCTYPE,this._stateBogusDoctype(e)}}_stateAfterDoctypeSystemKeyword(e){const t=this.currentToken;switch(e){case Ms.SPACE:case Ms.LINE_FEED:case Ms.TABULATION:case Ms.FORM_FEED:this.state=xo.BEFORE_DOCTYPE_SYSTEM_IDENTIFIER;break;case Ms.QUOTATION_MARK:this._err(qs.missingWhitespaceAfterDoctypeSystemKeyword),t.systemId=\"\",this.state=xo.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED;break;case Ms.APOSTROPHE:this._err(qs.missingWhitespaceAfterDoctypeSystemKeyword),t.systemId=\"\",this.state=xo.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED;break;case Ms.GREATER_THAN_SIGN:this._err(qs.missingDoctypeSystemIdentifier),t.forceQuirks=!0,this.state=xo.DATA,this.emitCurrentDoctype(t);break;case Ms.EOF:this._err(qs.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:this._err(qs.missingQuoteBeforeDoctypeSystemIdentifier),t.forceQuirks=!0,this.state=xo.BOGUS_DOCTYPE,this._stateBogusDoctype(e)}}_stateBeforeDoctypeSystemIdentifier(e){const t=this.currentToken;switch(e){case Ms.SPACE:case Ms.LINE_FEED:case Ms.TABULATION:case Ms.FORM_FEED:break;case Ms.QUOTATION_MARK:t.systemId=\"\",this.state=xo.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED;break;case Ms.APOSTROPHE:t.systemId=\"\",this.state=xo.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED;break;case Ms.GREATER_THAN_SIGN:this._err(qs.missingDoctypeSystemIdentifier),t.forceQuirks=!0,this.state=xo.DATA,this.emitCurrentDoctype(t);break;case Ms.EOF:this._err(qs.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:this._err(qs.missingQuoteBeforeDoctypeSystemIdentifier),t.forceQuirks=!0,this.state=xo.BOGUS_DOCTYPE,this._stateBogusDoctype(e)}}_stateDoctypeSystemIdentifierDoubleQuoted(e){const t=this.currentToken;switch(e){case Ms.QUOTATION_MARK:this.state=xo.AFTER_DOCTYPE_SYSTEM_IDENTIFIER;break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),t.systemId+=Ds;break;case Ms.GREATER_THAN_SIGN:this._err(qs.abruptDoctypeSystemIdentifier),t.forceQuirks=!0,this.emitCurrentDoctype(t),this.state=xo.DATA;break;case Ms.EOF:this._err(qs.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:t.systemId+=String.fromCodePoint(e)}}_stateDoctypeSystemIdentifierSingleQuoted(e){const t=this.currentToken;switch(e){case Ms.APOSTROPHE:this.state=xo.AFTER_DOCTYPE_SYSTEM_IDENTIFIER;break;case Ms.NULL:this._err(qs.unexpectedNullCharacter),t.systemId+=Ds;break;case Ms.GREATER_THAN_SIGN:this._err(qs.abruptDoctypeSystemIdentifier),t.forceQuirks=!0,this.emitCurrentDoctype(t),this.state=xo.DATA;break;case Ms.EOF:this._err(qs.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:t.systemId+=String.fromCodePoint(e)}}_stateAfterDoctypeSystemIdentifier(e){const t=this.currentToken;switch(e){case Ms.SPACE:case Ms.LINE_FEED:case Ms.TABULATION:case Ms.FORM_FEED:break;case Ms.GREATER_THAN_SIGN:this.emitCurrentDoctype(t),this.state=xo.DATA;break;case Ms.EOF:this._err(qs.eofInDoctype),t.forceQuirks=!0,this.emitCurrentDoctype(t),this._emitEOFToken();break;default:this._err(qs.unexpectedCharacterAfterDoctypeSystemIdentifier),this.state=xo.BOGUS_DOCTYPE,this._stateBogusDoctype(e)}}_stateBogusDoctype(e){const t=this.currentToken;switch(e){case Ms.GREATER_THAN_SIGN:this.emitCurrentDoctype(t),this.state=xo.DATA;break;case Ms.NULL:this._err(qs.unexpectedNullCharacter);break;case Ms.EOF:this.emitCurrentDoctype(t),this._emitEOFToken()}}_stateCdataSection(e){switch(e){case Ms.RIGHT_SQUARE_BRACKET:this.state=xo.CDATA_SECTION_BRACKET;break;case Ms.EOF:this._err(qs.eofInCdata),this._emitEOFToken();break;default:this._emitCodePoint(e)}}_stateCdataSectionBracket(e){e===Ms.RIGHT_SQUARE_BRACKET?this.state=xo.CDATA_SECTION_END:(this._emitChars(\"]\"),this.state=xo.CDATA_SECTION,this._stateCdataSection(e))}_stateCdataSectionEnd(e){switch(e){case Ms.GREATER_THAN_SIGN:this.state=xo.DATA;break;case Ms.RIGHT_SQUARE_BRACKET:this._emitChars(\"]\");break;default:this._emitChars(\"]]\"),this.state=xo.CDATA_SECTION,this._stateCdataSection(e)}}_stateCharacterReference(){let e=this.entityDecoder.write(this.preprocessor.html,this.preprocessor.pos);if(e<0){if(!this.preprocessor.lastChunkWritten)return this.active=!1,this.preprocessor.pos=this.preprocessor.html.length-1,this.consumedAfterSnapshot=0,void(this.preprocessor.endOfChunkHit=!0);e=this.entityDecoder.end()}0===e?(this.preprocessor.pos=this.entityStartPos,this._flushCodePointConsumedAsCharacterReference(Ms.AMPERSAND),this.state=!this._isCharacterReferenceInAttribute()&&Do(this.preprocessor.peek(1))?xo.AMBIGUOUS_AMPERSAND:this.returnState):this.state=this.returnState}_stateAmbiguousAmpersand(e){Do(e)?this._flushCodePointConsumedAsCharacterReference(e):(e===Ms.SEMICOLON&&this._err(qs.unknownNamedCharacterReference),this.state=this.returnState,this._callState(e))}}const Bo=new Set([go.DD,go.DT,go.LI,go.OPTGROUP,go.OPTION,go.P,go.RB,go.RP,go.RT,go.RTC]),Uo=new Set([...Bo,go.CAPTION,go.COLGROUP,go.TBODY,go.TD,go.TFOOT,go.TH,go.THEAD,go.TR]),Ho=new Set([go.APPLET,go.CAPTION,go.HTML,go.MARQUEE,go.OBJECT,go.TABLE,go.TD,go.TEMPLATE,go.TH]),zo=new Set([...Ho,go.OL,go.UL]),Go=new Set([...Ho,go.BUTTON]),jo=new Set([go.ANNOTATION_XML,go.MI,go.MN,go.MO,go.MS,go.MTEXT]),$o=new Set([go.DESC,go.FOREIGN_OBJECT,go.TITLE]),qo=new Set([go.TR,go.TEMPLATE,go.HTML]),Yo=new Set([go.TBODY,go.TFOOT,go.THEAD,go.TEMPLATE,go.HTML]),Wo=new Set([go.TABLE,go.TEMPLATE,go.HTML]),Vo=new Set([go.TD,go.TH]);class Ko{get currentTmplContentOrNode(){return this._isInTemplate()?this.treeAdapter.getTemplateContent(this.current):this.current}constructor(e,t,n){this.treeAdapter=t,this.handler=n,this.items=[],this.tagIDs=[],this.stackTop=-1,this.tmplCount=0,this.currentTagId=go.UNKNOWN,this.current=e}_indexOf(e){return this.items.lastIndexOf(e,this.stackTop)}_isInTemplate(){return this.currentTagId===go.TEMPLATE&&this.treeAdapter.getNamespaceURI(this.current)===oo.HTML}_updateCurrentElement(){this.current=this.items[this.stackTop],this.currentTagId=this.tagIDs[this.stackTop]}push(e,t){this.stackTop++,this.items[this.stackTop]=e,this.current=e,this.tagIDs[this.stackTop]=t,this.currentTagId=t,this._isInTemplate()&&this.tmplCount++,this.handler.onItemPush(e,t,!0)}pop(){const e=this.current;this.tmplCount>0&&this._isInTemplate()&&this.tmplCount--,this.stackTop--,this._updateCurrentElement(),this.handler.onItemPop(e,!0)}replace(e,t){const n=this._indexOf(e);this.items[n]=t,n===this.stackTop&&(this.current=t)}insertAfter(e,t,n){const r=this._indexOf(e)+1;this.items.splice(r,0,t),this.tagIDs.splice(r,0,n),this.stackTop++,r===this.stackTop&&this._updateCurrentElement(),this.current&&void 0!==this.currentTagId&&this.handler.onItemPush(this.current,this.currentTagId,r===this.stackTop)}popUntilTagNamePopped(e){let t=this.stackTop+1;do{t=this.tagIDs.lastIndexOf(e,t-1)}while(t>0&&this.treeAdapter.getNamespaceURI(this.items[t])!==oo.HTML);this.shortenToLength(Math.max(t,0))}shortenToLength(e){for(;this.stackTop>=e;){const t=this.current;this.tmplCount>0&&this._isInTemplate()&&(this.tmplCount-=1),this.stackTop--,this._updateCurrentElement(),this.handler.onItemPop(t,this.stackTop<e)}}popUntilElementPopped(e){const t=this._indexOf(e);this.shortenToLength(Math.max(t,0))}popUntilPopped(e,t){const n=this._indexOfTagNames(e,t);this.shortenToLength(Math.max(n,0))}popUntilNumberedHeaderPopped(){this.popUntilPopped(Co,oo.HTML)}popUntilTableCellPopped(){this.popUntilPopped(Vo,oo.HTML)}popAllUpToHtmlElement(){this.tmplCount=0,this.shortenToLength(1)}_indexOfTagNames(e,t){for(let n=this.stackTop;n>=0;n--)if(e.has(this.tagIDs[n])&&this.treeAdapter.getNamespaceURI(this.items[n])===t)return n;return-1}clearBackTo(e,t){const n=this._indexOfTagNames(e,t);this.shortenToLength(n+1)}clearBackToTableContext(){this.clearBackTo(Wo,oo.HTML)}clearBackToTableBodyContext(){this.clearBackTo(Yo,oo.HTML)}clearBackToTableRowContext(){this.clearBackTo(qo,oo.HTML)}remove(e){const t=this._indexOf(e);t>=0&&(t===this.stackTop?this.pop():(this.items.splice(t,1),this.tagIDs.splice(t,1),this.stackTop--,this._updateCurrentElement(),this.handler.onItemPop(e,!1)))}tryPeekProperlyNestedBodyElement(){return this.stackTop>=1&&this.tagIDs[1]===go.BODY?this.items[1]:null}contains(e){return this._indexOf(e)>-1}getCommonAncestor(e){const t=this._indexOf(e)-1;return t>=0?this.items[t]:null}isRootHtmlElementCurrent(){return 0===this.stackTop&&this.tagIDs[0]===go.HTML}hasInDynamicScope(e,t){for(let n=this.stackTop;n>=0;n--){const r=this.tagIDs[n];switch(this.treeAdapter.getNamespaceURI(this.items[n])){case oo.HTML:if(r===e)return!0;if(t.has(r))return!1;break;case oo.SVG:if($o.has(r))return!1;break;case oo.MATHML:if(jo.has(r))return!1}}return!0}hasInScope(e){return this.hasInDynamicScope(e,Ho)}hasInListItemScope(e){return this.hasInDynamicScope(e,zo)}hasInButtonScope(e){return this.hasInDynamicScope(e,Go)}hasNumberedHeaderInScope(){for(let e=this.stackTop;e>=0;e--){const t=this.tagIDs[e];switch(this.treeAdapter.getNamespaceURI(this.items[e])){case oo.HTML:if(Co.has(t))return!0;if(Ho.has(t))return!1;break;case oo.SVG:if($o.has(t))return!1;break;case oo.MATHML:if(jo.has(t))return!1}}return!0}hasInTableScope(e){for(let t=this.stackTop;t>=0;t--)if(this.treeAdapter.getNamespaceURI(this.items[t])===oo.HTML)switch(this.tagIDs[t]){case e:return!0;case go.TABLE:case go.HTML:return!1}return!0}hasTableBodyContextInTableScope(){for(let e=this.stackTop;e>=0;e--)if(this.treeAdapter.getNamespaceURI(this.items[e])===oo.HTML)switch(this.tagIDs[e]){case go.TBODY:case go.THEAD:case go.TFOOT:return!0;case go.TABLE:case go.HTML:return!1}return!0}hasInSelectScope(e){for(let t=this.stackTop;t>=0;t--)if(this.treeAdapter.getNamespaceURI(this.items[t])===oo.HTML)switch(this.tagIDs[t]){case e:return!0;case go.OPTION:case go.OPTGROUP:break;default:return!1}return!0}generateImpliedEndTags(){for(;void 0!==this.currentTagId&&Bo.has(this.currentTagId);)this.pop()}generateImpliedEndTagsThoroughly(){for(;void 0!==this.currentTagId&&Uo.has(this.currentTagId);)this.pop()}generateImpliedEndTagsWithExclusion(e){for(;void 0!==this.currentTagId&&this.currentTagId!==e&&Uo.has(this.currentTagId);)this.pop()}}var Qo,Xo;(Xo=Qo||(Qo={}))[Xo.Marker=0]=\"Marker\",Xo[Xo.Element=1]=\"Element\";const Zo={type:Qo.Marker};class Jo{constructor(e){this.treeAdapter=e,this.entries=[],this.bookmark=null}_getNoahArkConditionCandidates(e,t){const n=[],r=t.length,a=this.treeAdapter.getTagName(e),i=this.treeAdapter.getNamespaceURI(e);for(let s=0;s<this.entries.length;s++){const e=this.entries[s];if(e.type===Qo.Marker)break;const{element:t}=e;if(this.treeAdapter.getTagName(t)===a&&this.treeAdapter.getNamespaceURI(t)===i){const e=this.treeAdapter.getAttrList(t);e.length===r&&n.push({idx:s,attrs:e})}}return n}_ensureNoahArkCondition(e){if(this.entries.length<3)return;const t=this.treeAdapter.getAttrList(e),n=this._getNoahArkConditionCandidates(e,t);if(n.length<3)return;const r=new Map(t.map(e=>[e.name,e.value]));let a=0;for(let i=0;i<n.length;i++){const e=n[i];e.attrs.every(e=>r.get(e.name)===e.value)&&(a+=1,a>=3&&this.entries.splice(e.idx,1))}}insertMarker(){this.entries.unshift(Zo)}pushElement(e,t){this._ensureNoahArkCondition(e),this.entries.unshift({type:Qo.Element,element:e,token:t})}insertElementAfterBookmark(e,t){const n=this.entries.indexOf(this.bookmark);this.entries.splice(n,0,{type:Qo.Element,element:e,token:t})}removeEntry(e){const t=this.entries.indexOf(e);-1!==t&&this.entries.splice(t,1)}clearToLastMarker(){const e=this.entries.indexOf(Zo);-1===e?this.entries.length=0:this.entries.splice(0,e+1)}getElementEntryInScopeWithTagName(e){const t=this.entries.find(t=>t.type===Qo.Marker||this.treeAdapter.getTagName(t.element)===e);return t&&t.type===Qo.Element?t:null}getElementEntry(e){return this.entries.find(t=>t.type===Qo.Element&&t.element===e)}}const el={createDocument:()=>({nodeName:\"#document\",mode:po.NO_QUIRKS,childNodes:[]}),createDocumentFragment:()=>({nodeName:\"#document-fragment\",childNodes:[]}),createElement:(e,t,n)=>({nodeName:e,tagName:e,attrs:n,namespaceURI:t,childNodes:[],parentNode:null}),createCommentNode:e=>({nodeName:\"#comment\",data:e,parentNode:null}),createTextNode:e=>({nodeName:\"#text\",value:e,parentNode:null}),appendChild(e,t){e.childNodes.push(t),t.parentNode=e},insertBefore(e,t,n){const r=e.childNodes.indexOf(n);e.childNodes.splice(r,0,t),t.parentNode=e},setTemplateContent(e,t){e.content=t},getTemplateContent:e=>e.content,setDocumentType(e,t,n,r){const a=e.childNodes.find(e=>\"#documentType\"===e.nodeName);if(a)a.name=t,a.publicId=n,a.systemId=r;else{const a={nodeName:\"#documentType\",name:t,publicId:n,systemId:r,parentNode:null};el.appendChild(e,a)}},setDocumentMode(e,t){e.mode=t},getDocumentMode:e=>e.mode,detachNode(e){if(e.parentNode){const t=e.parentNode.childNodes.indexOf(e);e.parentNode.childNodes.splice(t,1),e.parentNode=null}},insertText(e,t){if(e.childNodes.length>0){const n=e.childNodes[e.childNodes.length-1];if(el.isTextNode(n))return void(n.value+=t)}el.appendChild(e,el.createTextNode(t))},insertTextBefore(e,t,n){const r=e.childNodes[e.childNodes.indexOf(n)-1];r&&el.isTextNode(r)?r.value+=t:el.insertBefore(e,el.createTextNode(t),n)},adoptAttributes(e,t){const n=new Set(e.attrs.map(e=>e.name));for(let r=0;r<t.length;r++)n.has(t[r].name)||e.attrs.push(t[r])},getFirstChild:e=>e.childNodes[0],getChildNodes:e=>e.childNodes,getParentNode:e=>e.parentNode,getAttrList:e=>e.attrs,getTagName:e=>e.tagName,getNamespaceURI:e=>e.namespaceURI,getTextNodeContent:e=>e.value,getCommentNodeContent:e=>e.data,getDocumentTypeNodeName:e=>e.name,getDocumentTypeNodePublicId:e=>e.publicId,getDocumentTypeNodeSystemId:e=>e.systemId,isTextNode:e=>\"#text\"===e.nodeName,isCommentNode:e=>\"#comment\"===e.nodeName,isDocumentTypeNode:e=>\"#documentType\"===e.nodeName,isElementNode:e=>Object.prototype.hasOwnProperty.call(e,\"tagName\"),setNodeSourceCodeLocation(e,t){e.sourceCodeLocation=t},getNodeSourceCodeLocation:e=>e.sourceCodeLocation,updateNodeSourceCodeLocation(e,t){e.sourceCodeLocation={...e.sourceCodeLocation,...t}}},tl=\"html\",nl=[\"+//silmaril//dtd html pro v0r11 19970101//\",\"-//as//dtd html 3.0 aswedit + extensions//\",\"-//advasoft ltd//dtd html 3.0 aswedit + extensions//\",\"-//ietf//dtd html 2.0 level 1//\",\"-//ietf//dtd html 2.0 level 2//\",\"-//ietf//dtd html 2.0 strict level 1//\",\"-//ietf//dtd html 2.0 strict level 2//\",\"-//ietf//dtd html 2.0 strict//\",\"-//ietf//dtd html 2.0//\",\"-//ietf//dtd html 2.1e//\",\"-//ietf//dtd html 3.0//\",\"-//ietf//dtd html 3.2 final//\",\"-//ietf//dtd html 3.2//\",\"-//ietf//dtd html 3//\",\"-//ietf//dtd html level 0//\",\"-//ietf//dtd html level 1//\",\"-//ietf//dtd html level 2//\",\"-//ietf//dtd html level 3//\",\"-//ietf//dtd html strict level 0//\",\"-//ietf//dtd html strict level 1//\",\"-//ietf//dtd html strict level 2//\",\"-//ietf//dtd html strict level 3//\",\"-//ietf//dtd html strict//\",\"-//ietf//dtd html//\",\"-//metrius//dtd metrius presentational//\",\"-//microsoft//dtd internet explorer 2.0 html strict//\",\"-//microsoft//dtd internet explorer 2.0 html//\",\"-//microsoft//dtd internet explorer 2.0 tables//\",\"-//microsoft//dtd internet explorer 3.0 html strict//\",\"-//microsoft//dtd internet explorer 3.0 html//\",\"-//microsoft//dtd internet explorer 3.0 tables//\",\"-//netscape comm. corp.//dtd html//\",\"-//netscape comm. corp.//dtd strict html//\",\"-//o'reilly and associates//dtd html 2.0//\",\"-//o'reilly and associates//dtd html extended 1.0//\",\"-//o'reilly and associates//dtd html extended relaxed 1.0//\",\"-//sq//dtd html 2.0 hotmetal + extensions//\",\"-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//\",\"-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//\",\"-//spyglass//dtd html 2.0 extended//\",\"-//sun microsystems corp.//dtd hotjava html//\",\"-//sun microsystems corp.//dtd hotjava strict html//\",\"-//w3c//dtd html 3 1995-03-24//\",\"-//w3c//dtd html 3.2 draft//\",\"-//w3c//dtd html 3.2 final//\",\"-//w3c//dtd html 3.2//\",\"-//w3c//dtd html 3.2s draft//\",\"-//w3c//dtd html 4.0 frameset//\",\"-//w3c//dtd html 4.0 transitional//\",\"-//w3c//dtd html experimental 19960712//\",\"-//w3c//dtd html experimental 970421//\",\"-//w3c//dtd w3 html//\",\"-//w3o//dtd w3 html 3.0//\",\"-//webtechs//dtd mozilla html 2.0//\",\"-//webtechs//dtd mozilla html//\"],rl=[...nl,\"-//w3c//dtd html 4.01 frameset//\",\"-//w3c//dtd html 4.01 transitional//\"],al=new Set([\"-//w3o//dtd w3 html strict 3.0//en//\",\"-/w3c/dtd html 4.0 transitional/en\",\"html\"]),il=[\"-//w3c//dtd xhtml 1.0 frameset//\",\"-//w3c//dtd xhtml 1.0 transitional//\"],sl=[...il,\"-//w3c//dtd html 4.01 frameset//\",\"-//w3c//dtd html 4.01 transitional//\"];function ol(e,t){return t.some(t=>e.startsWith(t))}const ll=\"text/html\",cl=\"application/xhtml+xml\",ul=new Map([\"attributeName\",\"attributeType\",\"baseFrequency\",\"baseProfile\",\"calcMode\",\"clipPathUnits\",\"diffuseConstant\",\"edgeMode\",\"filterUnits\",\"glyphRef\",\"gradientTransform\",\"gradientUnits\",\"kernelMatrix\",\"kernelUnitLength\",\"keyPoints\",\"keySplines\",\"keyTimes\",\"lengthAdjust\",\"limitingConeAngle\",\"markerHeight\",\"markerUnits\",\"markerWidth\",\"maskContentUnits\",\"maskUnits\",\"numOctaves\",\"pathLength\",\"patternContentUnits\",\"patternTransform\",\"patternUnits\",\"pointsAtX\",\"pointsAtY\",\"pointsAtZ\",\"preserveAlpha\",\"preserveAspectRatio\",\"primitiveUnits\",\"refX\",\"refY\",\"repeatCount\",\"repeatDur\",\"requiredExtensions\",\"requiredFeatures\",\"specularConstant\",\"specularExponent\",\"spreadMethod\",\"startOffset\",\"stdDeviation\",\"stitchTiles\",\"surfaceScale\",\"systemLanguage\",\"tableValues\",\"targetX\",\"targetY\",\"textLength\",\"viewBox\",\"viewTarget\",\"xChannelSelector\",\"yChannelSelector\",\"zoomAndPan\"].map(e=>[e.toLowerCase(),e])),dl=new Map([[\"xlink:actuate\",{prefix:\"xlink\",name:\"actuate\",namespace:oo.XLINK}],[\"xlink:arcrole\",{prefix:\"xlink\",name:\"arcrole\",namespace:oo.XLINK}],[\"xlink:href\",{prefix:\"xlink\",name:\"href\",namespace:oo.XLINK}],[\"xlink:role\",{prefix:\"xlink\",name:\"role\",namespace:oo.XLINK}],[\"xlink:show\",{prefix:\"xlink\",name:\"show\",namespace:oo.XLINK}],[\"xlink:title\",{prefix:\"xlink\",name:\"title\",namespace:oo.XLINK}],[\"xlink:type\",{prefix:\"xlink\",name:\"type\",namespace:oo.XLINK}],[\"xml:lang\",{prefix:\"xml\",name:\"lang\",namespace:oo.XML}],[\"xml:space\",{prefix:\"xml\",name:\"space\",namespace:oo.XML}],[\"xmlns\",{prefix:\"\",name:\"xmlns\",namespace:oo.XMLNS}],[\"xmlns:xlink\",{prefix:\"xmlns\",name:\"xlink\",namespace:oo.XMLNS}]]),pl=new Map([\"altGlyph\",\"altGlyphDef\",\"altGlyphItem\",\"animateColor\",\"animateMotion\",\"animateTransform\",\"clipPath\",\"feBlend\",\"feColorMatrix\",\"feComponentTransfer\",\"feComposite\",\"feConvolveMatrix\",\"feDiffuseLighting\",\"feDisplacementMap\",\"feDistantLight\",\"feFlood\",\"feFuncA\",\"feFuncB\",\"feFuncG\",\"feFuncR\",\"feGaussianBlur\",\"feImage\",\"feMerge\",\"feMergeNode\",\"feMorphology\",\"feOffset\",\"fePointLight\",\"feSpecularLighting\",\"feSpotLight\",\"feTile\",\"feTurbulence\",\"foreignObject\",\"glyphRef\",\"linearGradient\",\"radialGradient\",\"textPath\"].map(e=>[e.toLowerCase(),e])),hl=new Set([go.B,go.BIG,go.BLOCKQUOTE,go.BODY,go.BR,go.CENTER,go.CODE,go.DD,go.DIV,go.DL,go.DT,go.EM,go.EMBED,go.H1,go.H2,go.H3,go.H4,go.H5,go.H6,go.HEAD,go.HR,go.I,go.IMG,go.LI,go.LISTING,go.MENU,go.META,go.NOBR,go.OL,go.P,go.PRE,go.RUBY,go.S,go.SMALL,go.SPAN,go.STRONG,go.STRIKE,go.SUB,go.SUP,go.TABLE,go.TT,go.U,go.UL,go.VAR]);function fl(e){for(let t=0;t<e.attrs.length;t++)if(\"definitionurl\"===e.attrs[t].name){e.attrs[t].name=\"definitionURL\";break}}function ml(e){for(let t=0;t<e.attrs.length;t++){const n=ul.get(e.attrs[t].name);null!=n&&(e.attrs[t].name=n)}}function gl(e){for(let t=0;t<e.attrs.length;t++){const n=dl.get(e.attrs[t].name);n&&(e.attrs[t].prefix=n.prefix,e.attrs[t].name=n.name,e.attrs[t].namespace=n.namespace)}}function bl(e,t,n,r){return(!r||r===oo.HTML)&&function(e,t,n){if(t===oo.MATHML&&e===go.ANNOTATION_XML)for(let r=0;r<n.length;r++)if(n[r].name===co.ENCODING){const e=n[r].value.toLowerCase();return e===ll||e===cl}return t===oo.SVG&&(e===go.FOREIGN_OBJECT||e===go.DESC||e===go.TITLE)}(e,t,n)||(!r||r===oo.MATHML)&&function(e,t){return t===oo.MATHML&&(e===go.MI||e===go.MO||e===go.MN||e===go.MS||e===go.MTEXT)}(e,t)}var El,yl;(yl=El||(El={}))[yl.INITIAL=0]=\"INITIAL\",yl[yl.BEFORE_HTML=1]=\"BEFORE_HTML\",yl[yl.BEFORE_HEAD=2]=\"BEFORE_HEAD\",yl[yl.IN_HEAD=3]=\"IN_HEAD\",yl[yl.IN_HEAD_NO_SCRIPT=4]=\"IN_HEAD_NO_SCRIPT\",yl[yl.AFTER_HEAD=5]=\"AFTER_HEAD\",yl[yl.IN_BODY=6]=\"IN_BODY\",yl[yl.TEXT=7]=\"TEXT\",yl[yl.IN_TABLE=8]=\"IN_TABLE\",yl[yl.IN_TABLE_TEXT=9]=\"IN_TABLE_TEXT\",yl[yl.IN_CAPTION=10]=\"IN_CAPTION\",yl[yl.IN_COLUMN_GROUP=11]=\"IN_COLUMN_GROUP\",yl[yl.IN_TABLE_BODY=12]=\"IN_TABLE_BODY\",yl[yl.IN_ROW=13]=\"IN_ROW\",yl[yl.IN_CELL=14]=\"IN_CELL\",yl[yl.IN_SELECT=15]=\"IN_SELECT\",yl[yl.IN_SELECT_IN_TABLE=16]=\"IN_SELECT_IN_TABLE\",yl[yl.IN_TEMPLATE=17]=\"IN_TEMPLATE\",yl[yl.AFTER_BODY=18]=\"AFTER_BODY\",yl[yl.IN_FRAMESET=19]=\"IN_FRAMESET\",yl[yl.AFTER_FRAMESET=20]=\"AFTER_FRAMESET\",yl[yl.AFTER_AFTER_BODY=21]=\"AFTER_AFTER_BODY\",yl[yl.AFTER_AFTER_FRAMESET=22]=\"AFTER_AFTER_FRAMESET\";const _l={startLine:-1,startCol:-1,startOffset:-1,endLine:-1,endCol:-1,endOffset:-1},kl=new Set([go.TABLE,go.TBODY,go.TFOOT,go.THEAD,go.TR]),Tl={scriptingEnabled:!0,sourceCodeLocationInfo:!1,treeAdapter:el,onParseError:null};class Sl{constructor(e,t,n=null,r=null){this.fragmentContext=n,this.scriptHandler=r,this.currentToken=null,this.stopped=!1,this.insertionMode=El.INITIAL,this.originalInsertionMode=El.INITIAL,this.headElement=null,this.formElement=null,this.currentNotInHTML=!1,this.tmplInsertionModeStack=[],this.pendingCharacterTokens=[],this.hasNonWhitespacePendingCharacterToken=!1,this.framesetOk=!0,this.skipNextNewLine=!1,this.fosterParentingEnabled=!1,this.options={...Tl,...e},this.treeAdapter=this.options.treeAdapter,this.onParseError=this.options.onParseError,this.onParseError&&(this.options.sourceCodeLocationInfo=!0),this.document=null!=t?t:this.treeAdapter.createDocument(),this.tokenizer=new Fo(this.options,this),this.activeFormattingElements=new Jo(this.treeAdapter),this.fragmentContextID=n?vo(this.treeAdapter.getTagName(n)):go.UNKNOWN,this._setContextModes(null!=n?n:this.document,this.fragmentContextID),this.openElements=new Ko(this.document,this.treeAdapter,this)}static parse(e,t){const n=new this(t);return n.tokenizer.write(e,!0),n.document}static getFragmentParser(e,t){const n={...Tl,...t};null!=e||(e=n.treeAdapter.createElement(fo.TEMPLATE,oo.HTML,[]));const r=n.treeAdapter.createElement(\"documentmock\",oo.HTML,[]),a=new this(n,r,e);return a.fragmentContextID===go.TEMPLATE&&a.tmplInsertionModeStack.unshift(El.IN_TEMPLATE),a._initTokenizerForFragmentParsing(),a._insertFakeRootElement(),a._resetInsertionMode(),a._findFormInFragmentContext(),a}getFragment(){const e=this.treeAdapter.getFirstChild(this.document),t=this.treeAdapter.createDocumentFragment();return this._adoptNodes(e,t),t}_err(e,t,n){var r;if(!this.onParseError)return;const a=null!==(r=e.location)&&void 0!==r?r:_l,i={code:t,startLine:a.startLine,startCol:a.startCol,startOffset:a.startOffset,endLine:n?a.startLine:a.endLine,endCol:n?a.startCol:a.endCol,endOffset:n?a.startOffset:a.endOffset};this.onParseError(i)}onItemPush(e,t,n){var r,a;null===(a=(r=this.treeAdapter).onItemPush)||void 0===a||a.call(r,e),n&&this.openElements.stackTop>0&&this._setContextModes(e,t)}onItemPop(e,t){var n,r;if(this.options.sourceCodeLocationInfo&&this._setEndLocation(e,this.currentToken),null===(r=(n=this.treeAdapter).onItemPop)||void 0===r||r.call(n,e,this.openElements.current),t){let e,t;0===this.openElements.stackTop&&this.fragmentContext?(e=this.fragmentContext,t=this.fragmentContextID):({current:e,currentTagId:t}=this.openElements),this._setContextModes(e,t)}}_setContextModes(e,t){const n=e===this.document||e&&this.treeAdapter.getNamespaceURI(e)===oo.HTML;this.currentNotInHTML=!n,this.tokenizer.inForeignNode=!n&&void 0!==e&&void 0!==t&&!this._isIntegrationPoint(t,e)}_switchToTextParsing(e,t){this._insertElement(e,oo.HTML),this.tokenizer.state=t,this.originalInsertionMode=this.insertionMode,this.insertionMode=El.TEXT}switchToPlaintextParsing(){this.insertionMode=El.TEXT,this.originalInsertionMode=El.IN_BODY,this.tokenizer.state=Io.PLAINTEXT}_getAdjustedCurrentElement(){return 0===this.openElements.stackTop&&this.fragmentContext?this.fragmentContext:this.openElements.current}_findFormInFragmentContext(){let e=this.fragmentContext;for(;e;){if(this.treeAdapter.getTagName(e)===fo.FORM){this.formElement=e;break}e=this.treeAdapter.getParentNode(e)}}_initTokenizerForFragmentParsing(){if(this.fragmentContext&&this.treeAdapter.getNamespaceURI(this.fragmentContext)===oo.HTML)switch(this.fragmentContextID){case go.TITLE:case go.TEXTAREA:this.tokenizer.state=Io.RCDATA;break;case go.STYLE:case go.XMP:case go.IFRAME:case go.NOEMBED:case go.NOFRAMES:case go.NOSCRIPT:this.tokenizer.state=Io.RAWTEXT;break;case go.SCRIPT:this.tokenizer.state=Io.SCRIPT_DATA;break;case go.PLAINTEXT:this.tokenizer.state=Io.PLAINTEXT}}_setDocumentType(e){const t=e.name||\"\",n=e.publicId||\"\",r=e.systemId||\"\";if(this.treeAdapter.setDocumentType(this.document,t,n,r),e.location){const t=this.treeAdapter.getChildNodes(this.document).find(e=>this.treeAdapter.isDocumentTypeNode(e));t&&this.treeAdapter.setNodeSourceCodeLocation(t,e.location)}}_attachElementToTree(e,t){if(this.options.sourceCodeLocationInfo){const n=t&&{...t,startTag:t};this.treeAdapter.setNodeSourceCodeLocation(e,n)}if(this._shouldFosterParentOnInsertion())this._fosterParentElement(e);else{const t=this.openElements.currentTmplContentOrNode;this.treeAdapter.appendChild(null!=t?t:this.document,e)}}_appendElement(e,t){const n=this.treeAdapter.createElement(e.tagName,t,e.attrs);this._attachElementToTree(n,e.location)}_insertElement(e,t){const n=this.treeAdapter.createElement(e.tagName,t,e.attrs);this._attachElementToTree(n,e.location),this.openElements.push(n,e.tagID)}_insertFakeElement(e,t){const n=this.treeAdapter.createElement(e,oo.HTML,[]);this._attachElementToTree(n,null),this.openElements.push(n,t)}_insertTemplate(e){const t=this.treeAdapter.createElement(e.tagName,oo.HTML,e.attrs),n=this.treeAdapter.createDocumentFragment();this.treeAdapter.setTemplateContent(t,n),this._attachElementToTree(t,e.location),this.openElements.push(t,e.tagID),this.options.sourceCodeLocationInfo&&this.treeAdapter.setNodeSourceCodeLocation(n,null)}_insertFakeRootElement(){const e=this.treeAdapter.createElement(fo.HTML,oo.HTML,[]);this.options.sourceCodeLocationInfo&&this.treeAdapter.setNodeSourceCodeLocation(e,null),this.treeAdapter.appendChild(this.openElements.current,e),this.openElements.push(e,go.HTML)}_appendCommentNode(e,t){const n=this.treeAdapter.createCommentNode(e.data);this.treeAdapter.appendChild(t,n),this.options.sourceCodeLocationInfo&&this.treeAdapter.setNodeSourceCodeLocation(n,e.location)}_insertCharacters(e){let t,n;if(this._shouldFosterParentOnInsertion()?(({parent:t,beforeElement:n}=this._findFosterParentingLocation()),n?this.treeAdapter.insertTextBefore(t,e.chars,n):this.treeAdapter.insertText(t,e.chars)):(t=this.openElements.currentTmplContentOrNode,this.treeAdapter.insertText(t,e.chars)),!e.location)return;const r=this.treeAdapter.getChildNodes(t),a=n?r.lastIndexOf(n):r.length,i=r[a-1];if(this.treeAdapter.getNodeSourceCodeLocation(i)){const{endLine:t,endCol:n,endOffset:r}=e.location;this.treeAdapter.updateNodeSourceCodeLocation(i,{endLine:t,endCol:n,endOffset:r})}else this.options.sourceCodeLocationInfo&&this.treeAdapter.setNodeSourceCodeLocation(i,e.location)}_adoptNodes(e,t){for(let n=this.treeAdapter.getFirstChild(e);n;n=this.treeAdapter.getFirstChild(e))this.treeAdapter.detachNode(n),this.treeAdapter.appendChild(t,n)}_setEndLocation(e,t){if(this.treeAdapter.getNodeSourceCodeLocation(e)&&t.location){const n=t.location,r=this.treeAdapter.getTagName(e),a=t.type===Vs.END_TAG&&r===t.tagName?{endTag:{...n},endLine:n.endLine,endCol:n.endCol,endOffset:n.endOffset}:{endLine:n.startLine,endCol:n.startCol,endOffset:n.startOffset};this.treeAdapter.updateNodeSourceCodeLocation(e,a)}}shouldProcessStartTagTokenInForeignContent(e){if(!this.currentNotInHTML)return!1;let t,n;return 0===this.openElements.stackTop&&this.fragmentContext?(t=this.fragmentContext,n=this.fragmentContextID):({current:t,currentTagId:n}=this.openElements),(e.tagID!==go.SVG||this.treeAdapter.getTagName(t)!==fo.ANNOTATION_XML||this.treeAdapter.getNamespaceURI(t)!==oo.MATHML)&&(this.tokenizer.inForeignNode||(e.tagID===go.MGLYPH||e.tagID===go.MALIGNMARK)&&void 0!==n&&!this._isIntegrationPoint(n,t,oo.HTML))}_processToken(e){switch(e.type){case Vs.CHARACTER:this.onCharacter(e);break;case Vs.NULL_CHARACTER:this.onNullCharacter(e);break;case Vs.COMMENT:this.onComment(e);break;case Vs.DOCTYPE:this.onDoctype(e);break;case Vs.START_TAG:this._processStartTag(e);break;case Vs.END_TAG:this.onEndTag(e);break;case Vs.EOF:this.onEof(e);break;case Vs.WHITESPACE_CHARACTER:this.onWhitespaceCharacter(e)}}_isIntegrationPoint(e,t,n){return bl(e,this.treeAdapter.getNamespaceURI(t),this.treeAdapter.getAttrList(t),n)}_reconstructActiveFormattingElements(){const e=this.activeFormattingElements.entries.length;if(e){const t=this.activeFormattingElements.entries.findIndex(e=>e.type===Qo.Marker||this.openElements.contains(e.element));for(let n=-1===t?e-1:t-1;n>=0;n--){const e=this.activeFormattingElements.entries[n];this._insertElement(e.token,this.treeAdapter.getNamespaceURI(e.element)),e.element=this.openElements.current}}}_closeTableCell(){this.openElements.generateImpliedEndTags(),this.openElements.popUntilTableCellPopped(),this.activeFormattingElements.clearToLastMarker(),this.insertionMode=El.IN_ROW}_closePElement(){this.openElements.generateImpliedEndTagsWithExclusion(go.P),this.openElements.popUntilTagNamePopped(go.P)}_resetInsertionMode(){for(let e=this.openElements.stackTop;e>=0;e--)switch(0===e&&this.fragmentContext?this.fragmentContextID:this.openElements.tagIDs[e]){case go.TR:return void(this.insertionMode=El.IN_ROW);case go.TBODY:case go.THEAD:case go.TFOOT:return void(this.insertionMode=El.IN_TABLE_BODY);case go.CAPTION:return void(this.insertionMode=El.IN_CAPTION);case go.COLGROUP:return void(this.insertionMode=El.IN_COLUMN_GROUP);case go.TABLE:return void(this.insertionMode=El.IN_TABLE);case go.BODY:return void(this.insertionMode=El.IN_BODY);case go.FRAMESET:return void(this.insertionMode=El.IN_FRAMESET);case go.SELECT:return void this._resetInsertionModeForSelect(e);case go.TEMPLATE:return void(this.insertionMode=this.tmplInsertionModeStack[0]);case go.HTML:return void(this.insertionMode=this.headElement?El.AFTER_HEAD:El.BEFORE_HEAD);case go.TD:case go.TH:if(e>0)return void(this.insertionMode=El.IN_CELL);break;case go.HEAD:if(e>0)return void(this.insertionMode=El.IN_HEAD)}this.insertionMode=El.IN_BODY}_resetInsertionModeForSelect(e){if(e>0)for(let t=e-1;t>0;t--){const e=this.openElements.tagIDs[t];if(e===go.TEMPLATE)break;if(e===go.TABLE)return void(this.insertionMode=El.IN_SELECT_IN_TABLE)}this.insertionMode=El.IN_SELECT}_isElementCausesFosterParenting(e){return kl.has(e)}_shouldFosterParentOnInsertion(){return this.fosterParentingEnabled&&void 0!==this.openElements.currentTagId&&this._isElementCausesFosterParenting(this.openElements.currentTagId)}_findFosterParentingLocation(){for(let e=this.openElements.stackTop;e>=0;e--){const t=this.openElements.items[e];switch(this.openElements.tagIDs[e]){case go.TEMPLATE:if(this.treeAdapter.getNamespaceURI(t)===oo.HTML)return{parent:this.treeAdapter.getTemplateContent(t),beforeElement:null};break;case go.TABLE:{const n=this.treeAdapter.getParentNode(t);return n?{parent:n,beforeElement:t}:{parent:this.openElements.items[e-1],beforeElement:null}}}}return{parent:this.openElements.items[0],beforeElement:null}}_fosterParentElement(e){const t=this._findFosterParentingLocation();t.beforeElement?this.treeAdapter.insertBefore(t.parent,e,t.beforeElement):this.treeAdapter.appendChild(t.parent,e)}_isSpecialElement(e,t){const n=this.treeAdapter.getNamespaceURI(e);return No[n].has(t)}onCharacter(e){if(this.skipNextNewLine=!1,this.tokenizer.inForeignNode)!function(e,t){e._insertCharacters(t),e.framesetOk=!1}(this,e);else switch(this.insertionMode){case El.INITIAL:Dl(this,e);break;case El.BEFORE_HTML:Ml(this,e);break;case El.BEFORE_HEAD:Pl(this,e);break;case El.IN_HEAD:Bl(this,e);break;case El.IN_HEAD_NO_SCRIPT:Ul(this,e);break;case El.AFTER_HEAD:Hl(this,e);break;case El.IN_BODY:case El.IN_CAPTION:case El.IN_CELL:case El.IN_TEMPLATE:jl(this,e);break;case El.TEXT:case El.IN_SELECT:case El.IN_SELECT_IN_TABLE:this._insertCharacters(e);break;case El.IN_TABLE:case El.IN_TABLE_BODY:case El.IN_ROW:Zl(this,e);break;case El.IN_TABLE_TEXT:rc(this,e);break;case El.IN_COLUMN_GROUP:oc(this,e);break;case El.AFTER_BODY:gc(this,e);break;case El.AFTER_AFTER_BODY:bc(this,e)}}onNullCharacter(e){if(this.skipNextNewLine=!1,this.tokenizer.inForeignNode)!function(e,t){t.chars=Ds,e._insertCharacters(t)}(this,e);else switch(this.insertionMode){case El.INITIAL:Dl(this,e);break;case El.BEFORE_HTML:Ml(this,e);break;case El.BEFORE_HEAD:Pl(this,e);break;case El.IN_HEAD:Bl(this,e);break;case El.IN_HEAD_NO_SCRIPT:Ul(this,e);break;case El.AFTER_HEAD:Hl(this,e);break;case El.TEXT:this._insertCharacters(e);break;case El.IN_TABLE:case El.IN_TABLE_BODY:case El.IN_ROW:Zl(this,e);break;case El.IN_COLUMN_GROUP:oc(this,e);break;case El.AFTER_BODY:gc(this,e);break;case El.AFTER_AFTER_BODY:bc(this,e)}}onComment(e){if(this.skipNextNewLine=!1,this.currentNotInHTML)Ol(this,e);else switch(this.insertionMode){case El.INITIAL:case El.BEFORE_HTML:case El.BEFORE_HEAD:case El.IN_HEAD:case El.IN_HEAD_NO_SCRIPT:case El.AFTER_HEAD:case El.IN_BODY:case El.IN_TABLE:case El.IN_CAPTION:case El.IN_COLUMN_GROUP:case El.IN_TABLE_BODY:case El.IN_ROW:case El.IN_CELL:case El.IN_SELECT:case El.IN_SELECT_IN_TABLE:case El.IN_TEMPLATE:case El.IN_FRAMESET:case El.AFTER_FRAMESET:Ol(this,e);break;case El.IN_TABLE_TEXT:ac(this,e);break;case El.AFTER_BODY:!function(e,t){e._appendCommentNode(t,e.openElements.items[0])}(this,e);break;case El.AFTER_AFTER_BODY:case El.AFTER_AFTER_FRAMESET:!function(e,t){e._appendCommentNode(t,e.document)}(this,e)}}onDoctype(e){switch(this.skipNextNewLine=!1,this.insertionMode){case El.INITIAL:!function(e,t){e._setDocumentType(t);const n=t.forceQuirks?po.QUIRKS:function(e){if(e.name!==tl)return po.QUIRKS;const{systemId:t}=e;if(t&&\"http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd\"===t.toLowerCase())return po.QUIRKS;let{publicId:n}=e;if(null!==n){if(n=n.toLowerCase(),al.has(n))return po.QUIRKS;let e=null===t?rl:nl;if(ol(n,e))return po.QUIRKS;if(e=null===t?il:sl,ol(n,e))return po.LIMITED_QUIRKS}return po.NO_QUIRKS}(t);(function(e){return e.name===tl&&null===e.publicId&&(null===e.systemId||\"about:legacy-compat\"===e.systemId)})(t)||e._err(t,qs.nonConformingDoctype);e.treeAdapter.setDocumentMode(e.document,n),e.insertionMode=El.BEFORE_HTML}(this,e);break;case El.BEFORE_HEAD:case El.IN_HEAD:case El.IN_HEAD_NO_SCRIPT:case El.AFTER_HEAD:this._err(e,qs.misplacedDoctype);break;case El.IN_TABLE_TEXT:ac(this,e)}}onStartTag(e){this.skipNextNewLine=!1,this.currentToken=e,this._processStartTag(e),e.selfClosing&&!e.ackSelfClosing&&this._err(e,qs.nonVoidHtmlElementStartTagWithTrailingSolidus)}_processStartTag(e){this.shouldProcessStartTagTokenInForeignContent(e)?function(e,t){if(function(e){const t=e.tagID;return t===go.FONT&&e.attrs.some(({name:e})=>e===co.COLOR||e===co.SIZE||e===co.FACE)||hl.has(t)}(t))Ec(e),e._startTagOutsideForeignContent(t);else{const n=e._getAdjustedCurrentElement(),r=e.treeAdapter.getNamespaceURI(n);r===oo.MATHML?fl(t):r===oo.SVG&&(!function(e){const t=pl.get(e.tagName);null!=t&&(e.tagName=t,e.tagID=vo(e.tagName))}(t),ml(t)),gl(t),t.selfClosing?e._appendElement(t,r):e._insertElement(t,r),t.ackSelfClosing=!0}}(this,e):this._startTagOutsideForeignContent(e)}_startTagOutsideForeignContent(e){switch(this.insertionMode){case El.INITIAL:Dl(this,e);break;case El.BEFORE_HTML:!function(e,t){t.tagID===go.HTML?(e._insertElement(t,oo.HTML),e.insertionMode=El.BEFORE_HEAD):Ml(e,t)}(this,e);break;case El.BEFORE_HEAD:!function(e,t){switch(t.tagID){case go.HTML:Vl(e,t);break;case go.HEAD:e._insertElement(t,oo.HTML),e.headElement=e.openElements.current,e.insertionMode=El.IN_HEAD;break;default:Pl(e,t)}}(this,e);break;case El.IN_HEAD:Ll(this,e);break;case El.IN_HEAD_NO_SCRIPT:!function(e,t){switch(t.tagID){case go.HTML:Vl(e,t);break;case go.BASEFONT:case go.BGSOUND:case go.HEAD:case go.LINK:case go.META:case go.NOFRAMES:case go.STYLE:Ll(e,t);break;case go.NOSCRIPT:e._err(t,qs.nestedNoscriptInHead);break;default:Ul(e,t)}}(this,e);break;case El.AFTER_HEAD:!function(e,t){switch(t.tagID){case go.HTML:Vl(e,t);break;case go.BODY:e._insertElement(t,oo.HTML),e.framesetOk=!1,e.insertionMode=El.IN_BODY;break;case go.FRAMESET:e._insertElement(t,oo.HTML),e.insertionMode=El.IN_FRAMESET;break;case go.BASE:case go.BASEFONT:case go.BGSOUND:case go.LINK:case go.META:case go.NOFRAMES:case go.SCRIPT:case go.STYLE:case go.TEMPLATE:case go.TITLE:e._err(t,qs.abandonedHeadElementChild),e.openElements.push(e.headElement,go.HEAD),Ll(e,t),e.openElements.remove(e.headElement);break;case go.HEAD:e._err(t,qs.misplacedStartTagForHeadElement);break;default:Hl(e,t)}}(this,e);break;case El.IN_BODY:Vl(this,e);break;case El.IN_TABLE:Jl(this,e);break;case El.IN_TABLE_TEXT:ac(this,e);break;case El.IN_CAPTION:!function(e,t){const n=t.tagID;ic.has(n)?e.openElements.hasInTableScope(go.CAPTION)&&(e.openElements.generateImpliedEndTags(),e.openElements.popUntilTagNamePopped(go.CAPTION),e.activeFormattingElements.clearToLastMarker(),e.insertionMode=El.IN_TABLE,Jl(e,t)):Vl(e,t)}(this,e);break;case El.IN_COLUMN_GROUP:sc(this,e);break;case El.IN_TABLE_BODY:lc(this,e);break;case El.IN_ROW:uc(this,e);break;case El.IN_CELL:!function(e,t){const n=t.tagID;ic.has(n)?(e.openElements.hasInTableScope(go.TD)||e.openElements.hasInTableScope(go.TH))&&(e._closeTableCell(),uc(e,t)):Vl(e,t)}(this,e);break;case El.IN_SELECT:pc(this,e);break;case El.IN_SELECT_IN_TABLE:!function(e,t){const n=t.tagID;n===go.CAPTION||n===go.TABLE||n===go.TBODY||n===go.TFOOT||n===go.THEAD||n===go.TR||n===go.TD||n===go.TH?(e.openElements.popUntilTagNamePopped(go.SELECT),e._resetInsertionMode(),e._processStartTag(t)):pc(e,t)}(this,e);break;case El.IN_TEMPLATE:!function(e,t){switch(t.tagID){case go.BASE:case go.BASEFONT:case go.BGSOUND:case go.LINK:case go.META:case go.NOFRAMES:case go.SCRIPT:case go.STYLE:case go.TEMPLATE:case go.TITLE:Ll(e,t);break;case go.CAPTION:case go.COLGROUP:case go.TBODY:case go.TFOOT:case go.THEAD:e.tmplInsertionModeStack[0]=El.IN_TABLE,e.insertionMode=El.IN_TABLE,Jl(e,t);break;case go.COL:e.tmplInsertionModeStack[0]=El.IN_COLUMN_GROUP,e.insertionMode=El.IN_COLUMN_GROUP,sc(e,t);break;case go.TR:e.tmplInsertionModeStack[0]=El.IN_TABLE_BODY,e.insertionMode=El.IN_TABLE_BODY,lc(e,t);break;case go.TD:case go.TH:e.tmplInsertionModeStack[0]=El.IN_ROW,e.insertionMode=El.IN_ROW,uc(e,t);break;default:e.tmplInsertionModeStack[0]=El.IN_BODY,e.insertionMode=El.IN_BODY,Vl(e,t)}}(this,e);break;case El.AFTER_BODY:!function(e,t){t.tagID===go.HTML?Vl(e,t):gc(e,t)}(this,e);break;case El.IN_FRAMESET:!function(e,t){switch(t.tagID){case go.HTML:Vl(e,t);break;case go.FRAMESET:e._insertElement(t,oo.HTML);break;case go.FRAME:e._appendElement(t,oo.HTML),t.ackSelfClosing=!0;break;case go.NOFRAMES:Ll(e,t)}}(this,e);break;case El.AFTER_FRAMESET:!function(e,t){switch(t.tagID){case go.HTML:Vl(e,t);break;case go.NOFRAMES:Ll(e,t)}}(this,e);break;case El.AFTER_AFTER_BODY:!function(e,t){t.tagID===go.HTML?Vl(e,t):bc(e,t)}(this,e);break;case El.AFTER_AFTER_FRAMESET:!function(e,t){switch(t.tagID){case go.HTML:Vl(e,t);break;case go.NOFRAMES:Ll(e,t)}}(this,e)}}onEndTag(e){this.skipNextNewLine=!1,this.currentToken=e,this.currentNotInHTML?function(e,t){if(t.tagID===go.P||t.tagID===go.BR)return Ec(e),void e._endTagOutsideForeignContent(t);for(let n=e.openElements.stackTop;n>0;n--){const r=e.openElements.items[n];if(e.treeAdapter.getNamespaceURI(r)===oo.HTML){e._endTagOutsideForeignContent(t);break}const a=e.treeAdapter.getTagName(r);if(a.toLowerCase()===t.tagName){t.tagName=a,e.openElements.shortenToLength(n);break}}}(this,e):this._endTagOutsideForeignContent(e)}_endTagOutsideForeignContent(e){switch(this.insertionMode){case El.INITIAL:Dl(this,e);break;case El.BEFORE_HTML:!function(e,t){const n=t.tagID;n!==go.HTML&&n!==go.HEAD&&n!==go.BODY&&n!==go.BR||Ml(e,t)}(this,e);break;case El.BEFORE_HEAD:!function(e,t){const n=t.tagID;n===go.HEAD||n===go.BODY||n===go.HTML||n===go.BR?Pl(e,t):e._err(t,qs.endTagWithoutMatchingOpenElement)}(this,e);break;case El.IN_HEAD:!function(e,t){switch(t.tagID){case go.HEAD:e.openElements.pop(),e.insertionMode=El.AFTER_HEAD;break;case go.BODY:case go.BR:case go.HTML:Bl(e,t);break;case go.TEMPLATE:Fl(e,t);break;default:e._err(t,qs.endTagWithoutMatchingOpenElement)}}(this,e);break;case El.IN_HEAD_NO_SCRIPT:!function(e,t){switch(t.tagID){case go.NOSCRIPT:e.openElements.pop(),e.insertionMode=El.IN_HEAD;break;case go.BR:Ul(e,t);break;default:e._err(t,qs.endTagWithoutMatchingOpenElement)}}(this,e);break;case El.AFTER_HEAD:!function(e,t){switch(t.tagID){case go.BODY:case go.HTML:case go.BR:Hl(e,t);break;case go.TEMPLATE:Fl(e,t);break;default:e._err(t,qs.endTagWithoutMatchingOpenElement)}}(this,e);break;case El.IN_BODY:Ql(this,e);break;case El.TEXT:!function(e,t){var n;t.tagID===go.SCRIPT&&(null===(n=e.scriptHandler)||void 0===n||n.call(e,e.openElements.current));e.openElements.pop(),e.insertionMode=e.originalInsertionMode}(this,e);break;case El.IN_TABLE:ec(this,e);break;case El.IN_TABLE_TEXT:ac(this,e);break;case El.IN_CAPTION:!function(e,t){const n=t.tagID;switch(n){case go.CAPTION:case go.TABLE:e.openElements.hasInTableScope(go.CAPTION)&&(e.openElements.generateImpliedEndTags(),e.openElements.popUntilTagNamePopped(go.CAPTION),e.activeFormattingElements.clearToLastMarker(),e.insertionMode=El.IN_TABLE,n===go.TABLE&&ec(e,t));break;case go.BODY:case go.COL:case go.COLGROUP:case go.HTML:case go.TBODY:case go.TD:case go.TFOOT:case go.TH:case go.THEAD:case go.TR:break;default:Ql(e,t)}}(this,e);break;case El.IN_COLUMN_GROUP:!function(e,t){switch(t.tagID){case go.COLGROUP:e.openElements.currentTagId===go.COLGROUP&&(e.openElements.pop(),e.insertionMode=El.IN_TABLE);break;case go.TEMPLATE:Fl(e,t);break;case go.COL:break;default:oc(e,t)}}(this,e);break;case El.IN_TABLE_BODY:cc(this,e);break;case El.IN_ROW:dc(this,e);break;case El.IN_CELL:!function(e,t){const n=t.tagID;switch(n){case go.TD:case go.TH:e.openElements.hasInTableScope(n)&&(e.openElements.generateImpliedEndTags(),e.openElements.popUntilTagNamePopped(n),e.activeFormattingElements.clearToLastMarker(),e.insertionMode=El.IN_ROW);break;case go.TABLE:case go.TBODY:case go.TFOOT:case go.THEAD:case go.TR:e.openElements.hasInTableScope(n)&&(e._closeTableCell(),dc(e,t));break;case go.BODY:case go.CAPTION:case go.COL:case go.COLGROUP:case go.HTML:break;default:Ql(e,t)}}(this,e);break;case El.IN_SELECT:hc(this,e);break;case El.IN_SELECT_IN_TABLE:!function(e,t){const n=t.tagID;n===go.CAPTION||n===go.TABLE||n===go.TBODY||n===go.TFOOT||n===go.THEAD||n===go.TR||n===go.TD||n===go.TH?e.openElements.hasInTableScope(n)&&(e.openElements.popUntilTagNamePopped(go.SELECT),e._resetInsertionMode(),e.onEndTag(t)):hc(e,t)}(this,e);break;case El.IN_TEMPLATE:!function(e,t){t.tagID===go.TEMPLATE&&Fl(e,t)}(this,e);break;case El.AFTER_BODY:mc(this,e);break;case El.IN_FRAMESET:!function(e,t){t.tagID!==go.FRAMESET||e.openElements.isRootHtmlElementCurrent()||(e.openElements.pop(),e.fragmentContext||e.openElements.currentTagId===go.FRAMESET||(e.insertionMode=El.AFTER_FRAMESET))}(this,e);break;case El.AFTER_FRAMESET:!function(e,t){t.tagID===go.HTML&&(e.insertionMode=El.AFTER_AFTER_FRAMESET)}(this,e);break;case El.AFTER_AFTER_BODY:bc(this,e)}}onEof(e){switch(this.insertionMode){case El.INITIAL:Dl(this,e);break;case El.BEFORE_HTML:Ml(this,e);break;case El.BEFORE_HEAD:Pl(this,e);break;case El.IN_HEAD:Bl(this,e);break;case El.IN_HEAD_NO_SCRIPT:Ul(this,e);break;case El.AFTER_HEAD:Hl(this,e);break;case El.IN_BODY:case El.IN_TABLE:case El.IN_CAPTION:case El.IN_COLUMN_GROUP:case El.IN_TABLE_BODY:case El.IN_ROW:case El.IN_CELL:case El.IN_SELECT:case El.IN_SELECT_IN_TABLE:Xl(this,e);break;case El.TEXT:!function(e,t){e._err(t,qs.eofInElementThatCanContainOnlyText),e.openElements.pop(),e.insertionMode=e.originalInsertionMode,e.onEof(t)}(this,e);break;case El.IN_TABLE_TEXT:ac(this,e);break;case El.IN_TEMPLATE:fc(this,e);break;case El.AFTER_BODY:case El.IN_FRAMESET:case El.AFTER_FRAMESET:case El.AFTER_AFTER_BODY:case El.AFTER_AFTER_FRAMESET:Rl(this,e)}}onWhitespaceCharacter(e){if(this.skipNextNewLine&&(this.skipNextNewLine=!1,e.chars.charCodeAt(0)===Ms.LINE_FEED)){if(1===e.chars.length)return;e.chars=e.chars.substr(1)}if(this.tokenizer.inForeignNode)this._insertCharacters(e);else switch(this.insertionMode){case El.IN_HEAD:case El.IN_HEAD_NO_SCRIPT:case El.AFTER_HEAD:case El.TEXT:case El.IN_COLUMN_GROUP:case El.IN_SELECT:case El.IN_SELECT_IN_TABLE:case El.IN_FRAMESET:case El.AFTER_FRAMESET:this._insertCharacters(e);break;case El.IN_BODY:case El.IN_CAPTION:case El.IN_CELL:case El.IN_TEMPLATE:case El.AFTER_BODY:case El.AFTER_AFTER_BODY:case El.AFTER_AFTER_FRAMESET:Gl(this,e);break;case El.IN_TABLE:case El.IN_TABLE_BODY:case El.IN_ROW:Zl(this,e);break;case El.IN_TABLE_TEXT:nc(this,e)}}}function vl(e,t){let n=e.activeFormattingElements.getElementEntryInScopeWithTagName(t.tagName);return n?e.openElements.contains(n.element)?e.openElements.hasInScope(t.tagID)||(n=null):(e.activeFormattingElements.removeEntry(n),n=null):Kl(e,t),n}function Al(e,t){let n=null,r=e.openElements.stackTop;for(;r>=0;r--){const a=e.openElements.items[r];if(a===t.element)break;e._isSpecialElement(a,e.openElements.tagIDs[r])&&(n=a)}return n||(e.openElements.shortenToLength(Math.max(r,0)),e.activeFormattingElements.removeEntry(t)),n}function Nl(e,t,n){let r=t,a=e.openElements.getCommonAncestor(t);for(let i=0,s=a;s!==n;i++,s=a){a=e.openElements.getCommonAncestor(s);const n=e.activeFormattingElements.getElementEntry(s),o=n&&i>=3;!n||o?(o&&e.activeFormattingElements.removeEntry(n),e.openElements.remove(s)):(s=Cl(e,n),r===t&&(e.activeFormattingElements.bookmark=n),e.treeAdapter.detachNode(r),e.treeAdapter.appendChild(s,r),r=s)}return r}function Cl(e,t){const n=e.treeAdapter.getNamespaceURI(t.element),r=e.treeAdapter.createElement(t.token.tagName,n,t.token.attrs);return e.openElements.replace(t.element,r),t.element=r,r}function xl(e,t,n){const r=vo(e.treeAdapter.getTagName(t));if(e._isElementCausesFosterParenting(r))e._fosterParentElement(n);else{const a=e.treeAdapter.getNamespaceURI(t);r===go.TEMPLATE&&a===oo.HTML&&(t=e.treeAdapter.getTemplateContent(t)),e.treeAdapter.appendChild(t,n)}}function wl(e,t,n){const r=e.treeAdapter.getNamespaceURI(n.element),{token:a}=n,i=e.treeAdapter.createElement(a.tagName,r,a.attrs);e._adoptNodes(t,i),e.treeAdapter.appendChild(t,i),e.activeFormattingElements.insertElementAfterBookmark(i,a),e.activeFormattingElements.removeEntry(n),e.openElements.remove(n.element),e.openElements.insertAfter(t,i,a.tagID)}function Il(e,t){for(let n=0;n<8;n++){const n=vl(e,t);if(!n)break;const r=Al(e,n);if(!r)break;e.activeFormattingElements.bookmark=n;const a=Nl(e,r,n.element),i=e.openElements.getCommonAncestor(n.element);e.treeAdapter.detachNode(a),i&&xl(e,i,a),wl(e,r,n)}}function Ol(e,t){e._appendCommentNode(t,e.openElements.currentTmplContentOrNode)}function Rl(e,t){if(e.stopped=!0,t.location){const n=e.fragmentContext?0:2;for(let r=e.openElements.stackTop;r>=n;r--)e._setEndLocation(e.openElements.items[r],t);if(!e.fragmentContext&&e.openElements.stackTop>=0){const n=e.openElements.items[0],r=e.treeAdapter.getNodeSourceCodeLocation(n);if(r&&!r.endTag&&(e._setEndLocation(n,t),e.openElements.stackTop>=1)){const n=e.openElements.items[1],r=e.treeAdapter.getNodeSourceCodeLocation(n);r&&!r.endTag&&e._setEndLocation(n,t)}}}}function Dl(e,t){e._err(t,qs.missingDoctype,!0),e.treeAdapter.setDocumentMode(e.document,po.QUIRKS),e.insertionMode=El.BEFORE_HTML,e._processToken(t)}function Ml(e,t){e._insertFakeRootElement(),e.insertionMode=El.BEFORE_HEAD,e._processToken(t)}function Pl(e,t){e._insertFakeElement(fo.HEAD,go.HEAD),e.headElement=e.openElements.current,e.insertionMode=El.IN_HEAD,e._processToken(t)}function Ll(e,t){switch(t.tagID){case go.HTML:Vl(e,t);break;case go.BASE:case go.BASEFONT:case go.BGSOUND:case go.LINK:case go.META:e._appendElement(t,oo.HTML),t.ackSelfClosing=!0;break;case go.TITLE:e._switchToTextParsing(t,Io.RCDATA);break;case go.NOSCRIPT:e.options.scriptingEnabled?e._switchToTextParsing(t,Io.RAWTEXT):(e._insertElement(t,oo.HTML),e.insertionMode=El.IN_HEAD_NO_SCRIPT);break;case go.NOFRAMES:case go.STYLE:e._switchToTextParsing(t,Io.RAWTEXT);break;case go.SCRIPT:e._switchToTextParsing(t,Io.SCRIPT_DATA);break;case go.TEMPLATE:e._insertTemplate(t),e.activeFormattingElements.insertMarker(),e.framesetOk=!1,e.insertionMode=El.IN_TEMPLATE,e.tmplInsertionModeStack.unshift(El.IN_TEMPLATE);break;case go.HEAD:e._err(t,qs.misplacedStartTagForHeadElement);break;default:Bl(e,t)}}function Fl(e,t){e.openElements.tmplCount>0?(e.openElements.generateImpliedEndTagsThoroughly(),e.openElements.currentTagId!==go.TEMPLATE&&e._err(t,qs.closingOfElementWithOpenChildElements),e.openElements.popUntilTagNamePopped(go.TEMPLATE),e.activeFormattingElements.clearToLastMarker(),e.tmplInsertionModeStack.shift(),e._resetInsertionMode()):e._err(t,qs.endTagWithoutMatchingOpenElement)}function Bl(e,t){e.openElements.pop(),e.insertionMode=El.AFTER_HEAD,e._processToken(t)}function Ul(e,t){const n=t.type===Vs.EOF?qs.openElementsLeftAfterEof:qs.disallowedContentInNoscriptInHead;e._err(t,n),e.openElements.pop(),e.insertionMode=El.IN_HEAD,e._processToken(t)}function Hl(e,t){e._insertFakeElement(fo.BODY,go.BODY),e.insertionMode=El.IN_BODY,zl(e,t)}function zl(e,t){switch(t.type){case Vs.CHARACTER:jl(e,t);break;case Vs.WHITESPACE_CHARACTER:Gl(e,t);break;case Vs.COMMENT:Ol(e,t);break;case Vs.START_TAG:Vl(e,t);break;case Vs.END_TAG:Ql(e,t);break;case Vs.EOF:Xl(e,t)}}function Gl(e,t){e._reconstructActiveFormattingElements(),e._insertCharacters(t)}function jl(e,t){e._reconstructActiveFormattingElements(),e._insertCharacters(t),e.framesetOk=!1}function $l(e,t){e._reconstructActiveFormattingElements(),e._appendElement(t,oo.HTML),e.framesetOk=!1,t.ackSelfClosing=!0}function ql(e){const t=Qs(e,co.TYPE);return null!=t&&\"hidden\"===t.toLowerCase()}function Yl(e,t){e._switchToTextParsing(t,Io.RAWTEXT)}function Wl(e,t){e._reconstructActiveFormattingElements(),e._insertElement(t,oo.HTML)}function Vl(e,t){switch(t.tagID){case go.I:case go.S:case go.B:case go.U:case go.EM:case go.TT:case go.BIG:case go.CODE:case go.FONT:case go.SMALL:case go.STRIKE:case go.STRONG:!function(e,t){e._reconstructActiveFormattingElements(),e._insertElement(t,oo.HTML),e.activeFormattingElements.pushElement(e.openElements.current,t)}(e,t);break;case go.A:!function(e,t){const n=e.activeFormattingElements.getElementEntryInScopeWithTagName(fo.A);n&&(Il(e,t),e.openElements.remove(n.element),e.activeFormattingElements.removeEntry(n)),e._reconstructActiveFormattingElements(),e._insertElement(t,oo.HTML),e.activeFormattingElements.pushElement(e.openElements.current,t)}(e,t);break;case go.H1:case go.H2:case go.H3:case go.H4:case go.H5:case go.H6:!function(e,t){e.openElements.hasInButtonScope(go.P)&&e._closePElement(),void 0!==e.openElements.currentTagId&&Co.has(e.openElements.currentTagId)&&e.openElements.pop(),e._insertElement(t,oo.HTML)}(e,t);break;case go.P:case go.DL:case go.OL:case go.UL:case go.DIV:case go.DIR:case go.NAV:case go.MAIN:case go.MENU:case go.ASIDE:case go.CENTER:case go.FIGURE:case go.FOOTER:case go.HEADER:case go.HGROUP:case go.DIALOG:case go.DETAILS:case go.ADDRESS:case go.ARTICLE:case go.SEARCH:case go.SECTION:case go.SUMMARY:case go.FIELDSET:case go.BLOCKQUOTE:case go.FIGCAPTION:!function(e,t){e.openElements.hasInButtonScope(go.P)&&e._closePElement(),e._insertElement(t,oo.HTML)}(e,t);break;case go.LI:case go.DD:case go.DT:!function(e,t){e.framesetOk=!1;const n=t.tagID;for(let r=e.openElements.stackTop;r>=0;r--){const t=e.openElements.tagIDs[r];if(n===go.LI&&t===go.LI||(n===go.DD||n===go.DT)&&(t===go.DD||t===go.DT)){e.openElements.generateImpliedEndTagsWithExclusion(t),e.openElements.popUntilTagNamePopped(t);break}if(t!==go.ADDRESS&&t!==go.DIV&&t!==go.P&&e._isSpecialElement(e.openElements.items[r],t))break}e.openElements.hasInButtonScope(go.P)&&e._closePElement(),e._insertElement(t,oo.HTML)}(e,t);break;case go.BR:case go.IMG:case go.WBR:case go.AREA:case go.EMBED:case go.KEYGEN:$l(e,t);break;case go.HR:!function(e,t){e.openElements.hasInButtonScope(go.P)&&e._closePElement(),e._appendElement(t,oo.HTML),e.framesetOk=!1,t.ackSelfClosing=!0}(e,t);break;case go.RB:case go.RTC:!function(e,t){e.openElements.hasInScope(go.RUBY)&&e.openElements.generateImpliedEndTags(),e._insertElement(t,oo.HTML)}(e,t);break;case go.RT:case go.RP:!function(e,t){e.openElements.hasInScope(go.RUBY)&&e.openElements.generateImpliedEndTagsWithExclusion(go.RTC),e._insertElement(t,oo.HTML)}(e,t);break;case go.PRE:case go.LISTING:!function(e,t){e.openElements.hasInButtonScope(go.P)&&e._closePElement(),e._insertElement(t,oo.HTML),e.skipNextNewLine=!0,e.framesetOk=!1}(e,t);break;case go.XMP:!function(e,t){e.openElements.hasInButtonScope(go.P)&&e._closePElement(),e._reconstructActiveFormattingElements(),e.framesetOk=!1,e._switchToTextParsing(t,Io.RAWTEXT)}(e,t);break;case go.SVG:!function(e,t){e._reconstructActiveFormattingElements(),ml(t),gl(t),t.selfClosing?e._appendElement(t,oo.SVG):e._insertElement(t,oo.SVG),t.ackSelfClosing=!0}(e,t);break;case go.HTML:!function(e,t){0===e.openElements.tmplCount&&e.treeAdapter.adoptAttributes(e.openElements.items[0],t.attrs)}(e,t);break;case go.BASE:case go.LINK:case go.META:case go.STYLE:case go.TITLE:case go.SCRIPT:case go.BGSOUND:case go.BASEFONT:case go.TEMPLATE:Ll(e,t);break;case go.BODY:!function(e,t){const n=e.openElements.tryPeekProperlyNestedBodyElement();n&&0===e.openElements.tmplCount&&(e.framesetOk=!1,e.treeAdapter.adoptAttributes(n,t.attrs))}(e,t);break;case go.FORM:!function(e,t){const n=e.openElements.tmplCount>0;e.formElement&&!n||(e.openElements.hasInButtonScope(go.P)&&e._closePElement(),e._insertElement(t,oo.HTML),n||(e.formElement=e.openElements.current))}(e,t);break;case go.NOBR:!function(e,t){e._reconstructActiveFormattingElements(),e.openElements.hasInScope(go.NOBR)&&(Il(e,t),e._reconstructActiveFormattingElements()),e._insertElement(t,oo.HTML),e.activeFormattingElements.pushElement(e.openElements.current,t)}(e,t);break;case go.MATH:!function(e,t){e._reconstructActiveFormattingElements(),fl(t),gl(t),t.selfClosing?e._appendElement(t,oo.MATHML):e._insertElement(t,oo.MATHML),t.ackSelfClosing=!0}(e,t);break;case go.TABLE:!function(e,t){e.treeAdapter.getDocumentMode(e.document)!==po.QUIRKS&&e.openElements.hasInButtonScope(go.P)&&e._closePElement(),e._insertElement(t,oo.HTML),e.framesetOk=!1,e.insertionMode=El.IN_TABLE}(e,t);break;case go.INPUT:!function(e,t){e._reconstructActiveFormattingElements(),e._appendElement(t,oo.HTML),ql(t)||(e.framesetOk=!1),t.ackSelfClosing=!0}(e,t);break;case go.PARAM:case go.TRACK:case go.SOURCE:!function(e,t){e._appendElement(t,oo.HTML),t.ackSelfClosing=!0}(e,t);break;case go.IMAGE:!function(e,t){t.tagName=fo.IMG,t.tagID=go.IMG,$l(e,t)}(e,t);break;case go.BUTTON:!function(e,t){e.openElements.hasInScope(go.BUTTON)&&(e.openElements.generateImpliedEndTags(),e.openElements.popUntilTagNamePopped(go.BUTTON)),e._reconstructActiveFormattingElements(),e._insertElement(t,oo.HTML),e.framesetOk=!1}(e,t);break;case go.APPLET:case go.OBJECT:case go.MARQUEE:!function(e,t){e._reconstructActiveFormattingElements(),e._insertElement(t,oo.HTML),e.activeFormattingElements.insertMarker(),e.framesetOk=!1}(e,t);break;case go.IFRAME:!function(e,t){e.framesetOk=!1,e._switchToTextParsing(t,Io.RAWTEXT)}(e,t);break;case go.SELECT:!function(e,t){e._reconstructActiveFormattingElements(),e._insertElement(t,oo.HTML),e.framesetOk=!1,e.insertionMode=e.insertionMode===El.IN_TABLE||e.insertionMode===El.IN_CAPTION||e.insertionMode===El.IN_TABLE_BODY||e.insertionMode===El.IN_ROW||e.insertionMode===El.IN_CELL?El.IN_SELECT_IN_TABLE:El.IN_SELECT}(e,t);break;case go.OPTION:case go.OPTGROUP:!function(e,t){e.openElements.currentTagId===go.OPTION&&e.openElements.pop(),e._reconstructActiveFormattingElements(),e._insertElement(t,oo.HTML)}(e,t);break;case go.NOEMBED:case go.NOFRAMES:Yl(e,t);break;case go.FRAMESET:!function(e,t){const n=e.openElements.tryPeekProperlyNestedBodyElement();e.framesetOk&&n&&(e.treeAdapter.detachNode(n),e.openElements.popAllUpToHtmlElement(),e._insertElement(t,oo.HTML),e.insertionMode=El.IN_FRAMESET)}(e,t);break;case go.TEXTAREA:!function(e,t){e._insertElement(t,oo.HTML),e.skipNextNewLine=!0,e.tokenizer.state=Io.RCDATA,e.originalInsertionMode=e.insertionMode,e.framesetOk=!1,e.insertionMode=El.TEXT}(e,t);break;case go.NOSCRIPT:e.options.scriptingEnabled?Yl(e,t):Wl(e,t);break;case go.PLAINTEXT:!function(e,t){e.openElements.hasInButtonScope(go.P)&&e._closePElement(),e._insertElement(t,oo.HTML),e.tokenizer.state=Io.PLAINTEXT}(e,t);break;case go.COL:case go.TH:case go.TD:case go.TR:case go.HEAD:case go.FRAME:case go.TBODY:case go.TFOOT:case go.THEAD:case go.CAPTION:case go.COLGROUP:break;default:Wl(e,t)}}function Kl(e,t){const n=t.tagName,r=t.tagID;for(let a=e.openElements.stackTop;a>0;a--){const t=e.openElements.items[a],i=e.openElements.tagIDs[a];if(r===i&&(r!==go.UNKNOWN||e.treeAdapter.getTagName(t)===n)){e.openElements.generateImpliedEndTagsWithExclusion(r),e.openElements.stackTop>=a&&e.openElements.shortenToLength(a);break}if(e._isSpecialElement(t,i))break}}function Ql(e,t){switch(t.tagID){case go.A:case go.B:case go.I:case go.S:case go.U:case go.EM:case go.TT:case go.BIG:case go.CODE:case go.FONT:case go.NOBR:case go.SMALL:case go.STRIKE:case go.STRONG:Il(e,t);break;case go.P:!function(e){e.openElements.hasInButtonScope(go.P)||e._insertFakeElement(fo.P,go.P),e._closePElement()}(e);break;case go.DL:case go.UL:case go.OL:case go.DIR:case go.DIV:case go.NAV:case go.PRE:case go.MAIN:case go.MENU:case go.ASIDE:case go.BUTTON:case go.CENTER:case go.FIGURE:case go.FOOTER:case go.HEADER:case go.HGROUP:case go.DIALOG:case go.ADDRESS:case go.ARTICLE:case go.DETAILS:case go.SEARCH:case go.SECTION:case go.SUMMARY:case go.LISTING:case go.FIELDSET:case go.BLOCKQUOTE:case go.FIGCAPTION:!function(e,t){const n=t.tagID;e.openElements.hasInScope(n)&&(e.openElements.generateImpliedEndTags(),e.openElements.popUntilTagNamePopped(n))}(e,t);break;case go.LI:!function(e){e.openElements.hasInListItemScope(go.LI)&&(e.openElements.generateImpliedEndTagsWithExclusion(go.LI),e.openElements.popUntilTagNamePopped(go.LI))}(e);break;case go.DD:case go.DT:!function(e,t){const n=t.tagID;e.openElements.hasInScope(n)&&(e.openElements.generateImpliedEndTagsWithExclusion(n),e.openElements.popUntilTagNamePopped(n))}(e,t);break;case go.H1:case go.H2:case go.H3:case go.H4:case go.H5:case go.H6:!function(e){e.openElements.hasNumberedHeaderInScope()&&(e.openElements.generateImpliedEndTags(),e.openElements.popUntilNumberedHeaderPopped())}(e);break;case go.BR:!function(e){e._reconstructActiveFormattingElements(),e._insertFakeElement(fo.BR,go.BR),e.openElements.pop(),e.framesetOk=!1}(e);break;case go.BODY:!function(e,t){if(e.openElements.hasInScope(go.BODY)&&(e.insertionMode=El.AFTER_BODY,e.options.sourceCodeLocationInfo)){const n=e.openElements.tryPeekProperlyNestedBodyElement();n&&e._setEndLocation(n,t)}}(e,t);break;case go.HTML:!function(e,t){e.openElements.hasInScope(go.BODY)&&(e.insertionMode=El.AFTER_BODY,mc(e,t))}(e,t);break;case go.FORM:!function(e){const t=e.openElements.tmplCount>0,{formElement:n}=e;t||(e.formElement=null),(n||t)&&e.openElements.hasInScope(go.FORM)&&(e.openElements.generateImpliedEndTags(),t?e.openElements.popUntilTagNamePopped(go.FORM):n&&e.openElements.remove(n))}(e);break;case go.APPLET:case go.OBJECT:case go.MARQUEE:!function(e,t){const n=t.tagID;e.openElements.hasInScope(n)&&(e.openElements.generateImpliedEndTags(),e.openElements.popUntilTagNamePopped(n),e.activeFormattingElements.clearToLastMarker())}(e,t);break;case go.TEMPLATE:Fl(e,t);break;default:Kl(e,t)}}function Xl(e,t){e.tmplInsertionModeStack.length>0?fc(e,t):Rl(e,t)}function Zl(e,t){if(void 0!==e.openElements.currentTagId&&kl.has(e.openElements.currentTagId))switch(e.pendingCharacterTokens.length=0,e.hasNonWhitespacePendingCharacterToken=!1,e.originalInsertionMode=e.insertionMode,e.insertionMode=El.IN_TABLE_TEXT,t.type){case Vs.CHARACTER:rc(e,t);break;case Vs.WHITESPACE_CHARACTER:nc(e,t)}else tc(e,t)}function Jl(e,t){switch(t.tagID){case go.TD:case go.TH:case go.TR:!function(e,t){e.openElements.clearBackToTableContext(),e._insertFakeElement(fo.TBODY,go.TBODY),e.insertionMode=El.IN_TABLE_BODY,lc(e,t)}(e,t);break;case go.STYLE:case go.SCRIPT:case go.TEMPLATE:Ll(e,t);break;case go.COL:!function(e,t){e.openElements.clearBackToTableContext(),e._insertFakeElement(fo.COLGROUP,go.COLGROUP),e.insertionMode=El.IN_COLUMN_GROUP,sc(e,t)}(e,t);break;case go.FORM:!function(e,t){e.formElement||0!==e.openElements.tmplCount||(e._insertElement(t,oo.HTML),e.formElement=e.openElements.current,e.openElements.pop())}(e,t);break;case go.TABLE:!function(e,t){e.openElements.hasInTableScope(go.TABLE)&&(e.openElements.popUntilTagNamePopped(go.TABLE),e._resetInsertionMode(),e._processStartTag(t))}(e,t);break;case go.TBODY:case go.TFOOT:case go.THEAD:!function(e,t){e.openElements.clearBackToTableContext(),e._insertElement(t,oo.HTML),e.insertionMode=El.IN_TABLE_BODY}(e,t);break;case go.INPUT:!function(e,t){ql(t)?e._appendElement(t,oo.HTML):tc(e,t),t.ackSelfClosing=!0}(e,t);break;case go.CAPTION:!function(e,t){e.openElements.clearBackToTableContext(),e.activeFormattingElements.insertMarker(),e._insertElement(t,oo.HTML),e.insertionMode=El.IN_CAPTION}(e,t);break;case go.COLGROUP:!function(e,t){e.openElements.clearBackToTableContext(),e._insertElement(t,oo.HTML),e.insertionMode=El.IN_COLUMN_GROUP}(e,t);break;default:tc(e,t)}}function ec(e,t){switch(t.tagID){case go.TABLE:e.openElements.hasInTableScope(go.TABLE)&&(e.openElements.popUntilTagNamePopped(go.TABLE),e._resetInsertionMode());break;case go.TEMPLATE:Fl(e,t);break;case go.BODY:case go.CAPTION:case go.COL:case go.COLGROUP:case go.HTML:case go.TBODY:case go.TD:case go.TFOOT:case go.TH:case go.THEAD:case go.TR:break;default:tc(e,t)}}function tc(e,t){const n=e.fosterParentingEnabled;e.fosterParentingEnabled=!0,zl(e,t),e.fosterParentingEnabled=n}function nc(e,t){e.pendingCharacterTokens.push(t)}function rc(e,t){e.pendingCharacterTokens.push(t),e.hasNonWhitespacePendingCharacterToken=!0}function ac(e,t){let n=0;if(e.hasNonWhitespacePendingCharacterToken)for(;n<e.pendingCharacterTokens.length;n++)tc(e,e.pendingCharacterTokens[n]);else for(;n<e.pendingCharacterTokens.length;n++)e._insertCharacters(e.pendingCharacterTokens[n]);e.insertionMode=e.originalInsertionMode,e._processToken(t)}const ic=new Set([go.CAPTION,go.COL,go.COLGROUP,go.TBODY,go.TD,go.TFOOT,go.TH,go.THEAD,go.TR]);function sc(e,t){switch(t.tagID){case go.HTML:Vl(e,t);break;case go.COL:e._appendElement(t,oo.HTML),t.ackSelfClosing=!0;break;case go.TEMPLATE:Ll(e,t);break;default:oc(e,t)}}function oc(e,t){e.openElements.currentTagId===go.COLGROUP&&(e.openElements.pop(),e.insertionMode=El.IN_TABLE,e._processToken(t))}function lc(e,t){switch(t.tagID){case go.TR:e.openElements.clearBackToTableBodyContext(),e._insertElement(t,oo.HTML),e.insertionMode=El.IN_ROW;break;case go.TH:case go.TD:e.openElements.clearBackToTableBodyContext(),e._insertFakeElement(fo.TR,go.TR),e.insertionMode=El.IN_ROW,uc(e,t);break;case go.CAPTION:case go.COL:case go.COLGROUP:case go.TBODY:case go.TFOOT:case go.THEAD:e.openElements.hasTableBodyContextInTableScope()&&(e.openElements.clearBackToTableBodyContext(),e.openElements.pop(),e.insertionMode=El.IN_TABLE,Jl(e,t));break;default:Jl(e,t)}}function cc(e,t){const n=t.tagID;switch(t.tagID){case go.TBODY:case go.TFOOT:case go.THEAD:e.openElements.hasInTableScope(n)&&(e.openElements.clearBackToTableBodyContext(),e.openElements.pop(),e.insertionMode=El.IN_TABLE);break;case go.TABLE:e.openElements.hasTableBodyContextInTableScope()&&(e.openElements.clearBackToTableBodyContext(),e.openElements.pop(),e.insertionMode=El.IN_TABLE,ec(e,t));break;case go.BODY:case go.CAPTION:case go.COL:case go.COLGROUP:case go.HTML:case go.TD:case go.TH:case go.TR:break;default:ec(e,t)}}function uc(e,t){switch(t.tagID){case go.TH:case go.TD:e.openElements.clearBackToTableRowContext(),e._insertElement(t,oo.HTML),e.insertionMode=El.IN_CELL,e.activeFormattingElements.insertMarker();break;case go.CAPTION:case go.COL:case go.COLGROUP:case go.TBODY:case go.TFOOT:case go.THEAD:case go.TR:e.openElements.hasInTableScope(go.TR)&&(e.openElements.clearBackToTableRowContext(),e.openElements.pop(),e.insertionMode=El.IN_TABLE_BODY,lc(e,t));break;default:Jl(e,t)}}function dc(e,t){switch(t.tagID){case go.TR:e.openElements.hasInTableScope(go.TR)&&(e.openElements.clearBackToTableRowContext(),e.openElements.pop(),e.insertionMode=El.IN_TABLE_BODY);break;case go.TABLE:e.openElements.hasInTableScope(go.TR)&&(e.openElements.clearBackToTableRowContext(),e.openElements.pop(),e.insertionMode=El.IN_TABLE_BODY,cc(e,t));break;case go.TBODY:case go.TFOOT:case go.THEAD:(e.openElements.hasInTableScope(t.tagID)||e.openElements.hasInTableScope(go.TR))&&(e.openElements.clearBackToTableRowContext(),e.openElements.pop(),e.insertionMode=El.IN_TABLE_BODY,cc(e,t));break;case go.BODY:case go.CAPTION:case go.COL:case go.COLGROUP:case go.HTML:case go.TD:case go.TH:break;default:ec(e,t)}}function pc(e,t){switch(t.tagID){case go.HTML:Vl(e,t);break;case go.OPTION:e.openElements.currentTagId===go.OPTION&&e.openElements.pop(),e._insertElement(t,oo.HTML);break;case go.OPTGROUP:e.openElements.currentTagId===go.OPTION&&e.openElements.pop(),e.openElements.currentTagId===go.OPTGROUP&&e.openElements.pop(),e._insertElement(t,oo.HTML);break;case go.HR:e.openElements.currentTagId===go.OPTION&&e.openElements.pop(),e.openElements.currentTagId===go.OPTGROUP&&e.openElements.pop(),e._appendElement(t,oo.HTML),t.ackSelfClosing=!0;break;case go.INPUT:case go.KEYGEN:case go.TEXTAREA:case go.SELECT:e.openElements.hasInSelectScope(go.SELECT)&&(e.openElements.popUntilTagNamePopped(go.SELECT),e._resetInsertionMode(),t.tagID!==go.SELECT&&e._processStartTag(t));break;case go.SCRIPT:case go.TEMPLATE:Ll(e,t)}}function hc(e,t){switch(t.tagID){case go.OPTGROUP:e.openElements.stackTop>0&&e.openElements.currentTagId===go.OPTION&&e.openElements.tagIDs[e.openElements.stackTop-1]===go.OPTGROUP&&e.openElements.pop(),e.openElements.currentTagId===go.OPTGROUP&&e.openElements.pop();break;case go.OPTION:e.openElements.currentTagId===go.OPTION&&e.openElements.pop();break;case go.SELECT:e.openElements.hasInSelectScope(go.SELECT)&&(e.openElements.popUntilTagNamePopped(go.SELECT),e._resetInsertionMode());break;case go.TEMPLATE:Fl(e,t)}}function fc(e,t){e.openElements.tmplCount>0?(e.openElements.popUntilTagNamePopped(go.TEMPLATE),e.activeFormattingElements.clearToLastMarker(),e.tmplInsertionModeStack.shift(),e._resetInsertionMode(),e.onEof(t)):Rl(e,t)}function mc(e,t){var n;if(t.tagID===go.HTML){if(e.fragmentContext||(e.insertionMode=El.AFTER_AFTER_BODY),e.options.sourceCodeLocationInfo&&e.openElements.tagIDs[0]===go.HTML){e._setEndLocation(e.openElements.items[0],t);const r=e.openElements.items[1];r&&!(null===(n=e.treeAdapter.getNodeSourceCodeLocation(r))||void 0===n?void 0:n.endTag)&&e._setEndLocation(r,t)}}else gc(e,t)}function gc(e,t){e.insertionMode=El.IN_BODY,zl(e,t)}function bc(e,t){e.insertionMode=El.IN_BODY,zl(e,t)}function Ec(e){for(;e.treeAdapter.getNamespaceURI(e.openElements.current)!==oo.HTML&&void 0!==e.openElements.currentTagId&&!e._isIntegrationPoint(e.openElements.currentTagId,e.openElements.current);)e.openElements.pop()}fo.AREA,fo.BASE,fo.BASEFONT,fo.BGSOUND,fo.BR,fo.COL,fo.EMBED,fo.FRAME,fo.HR,fo.IMG,fo.INPUT,fo.KEYGEN,fo.LINK,fo.META,fo.PARAM,fo.SOURCE,fo.TRACK,fo.WBR;const yc=/<(\\/?)(iframe|noembed|noframes|plaintext|script|style|textarea|title|xmp)(?=[\\t\\n\\f\\r />])/gi,_c=new Set([\"mdxFlowExpression\",\"mdxJsxFlowElement\",\"mdxJsxTextElement\",\"mdxTextExpression\",\"mdxjsEsm\"]),kc={sourceCodeLocationInfo:!0,scriptingEnabled:!1};function Tc(e,t){const n=function(e){const t=\"root\"===e.type?e.children[0]:e;return Boolean(t&&(\"doctype\"===t.type||\"element\"===t.type&&\"html\"===t.tagName.toLowerCase()))}(e),r=vs(\"type\",{handlers:{root:vc,element:Ac,text:Nc,comment:wc,doctype:Cc,raw:Ic},unknown:Oc}),a={parser:n?new Sl(kc):Sl.getFragmentParser(void 0,kc),handle(e){r(e,a)},stitches:!1,options:t||{}};r(e,a),Rc(a,Fe());const i=function(e,t){const n=t||{};return Es({file:n.file||void 0,location:!1,schema:\"svg\"===n.space?Te:ke,verbose:n.verbose||!1},e)}(n?a.parser.document:a.parser.getFragment(),{file:a.options.file});return a.stitches&&wr(i,\"comment\",function(e,t,n){const r=e;if(r.value.stitch&&n&&void 0!==t){return n.children[t]=r.value.stitch,t}}),\"root\"===i.type&&1===i.children.length&&i.children[0].type===e.type?i.children[0]:i}function Sc(e,t){let n=-1;if(e)for(;++n<e.length;)t.handle(e[n])}function vc(e,t){Sc(e.children,t)}function Ac(e,t){!function(e,t){const n=e.tagName.toLowerCase();if(t.parser.tokenizer.state===Io.PLAINTEXT)return;Rc(t,Fe(e));const r=t.parser.openElements.current;let a=\"namespaceURI\"in r?r.namespaceURI:ms.html;a===ms.html&&\"svg\"===n&&(a=ms.svg);const i=function(e,t){const n=(t||As).space;return Cs(e,\"svg\"===n?Te:ke)}({...e,children:[]},{space:a===ms.svg?\"svg\":\"html\"}),s={type:Vs.START_TAG,tagName:n,tagID:vo(n),selfClosing:!1,ackSelfClosing:!1,attrs:\"attrs\"in i?i.attrs:[],location:Mc(e)};t.parser.currentToken=s,t.parser._processToken(t.parser.currentToken),t.parser.tokenizer.lastStartTagName=n}(e,t),Sc(e.children,t),function(e,t){const n=e.tagName.toLowerCase();if(!t.parser.tokenizer.inForeignNode&&Os.includes(n))return;if(t.parser.tokenizer.state===Io.PLAINTEXT)return;Rc(t,Le(e));const r={type:Vs.END_TAG,tagName:n,tagID:vo(n),selfClosing:!1,ackSelfClosing:!1,attrs:[],location:Mc(e)};t.parser.currentToken=r,t.parser._processToken(t.parser.currentToken),n!==t.parser.tokenizer.lastStartTagName||t.parser.tokenizer.state!==Io.RCDATA&&t.parser.tokenizer.state!==Io.RAWTEXT&&t.parser.tokenizer.state!==Io.SCRIPT_DATA||(t.parser.tokenizer.state=Io.DATA)}(e,t)}function Nc(e,t){t.parser.tokenizer.state>4&&(t.parser.tokenizer.state=0);const n={type:Vs.CHARACTER,chars:e.value,location:Mc(e)};Rc(t,Fe(e)),t.parser.currentToken=n,t.parser._processToken(t.parser.currentToken)}function Cc(e,t){const n={type:Vs.DOCTYPE,name:\"html\",forceQuirks:!1,publicId:\"\",systemId:\"\",location:Mc(e)};Rc(t,Fe(e)),t.parser.currentToken=n,t.parser._processToken(t.parser.currentToken)}function xc(e,t){t.stitches=!0;const n=function(e){return yr(\"children\"in e?{...e,children:[]}:e)}(e);if(\"children\"in e&&\"children\"in n){const r=Tc({type:\"root\",children:e.children},t.options);n.children=r.children}wc({type:\"comment\",value:{stitch:n}},t)}function wc(e,t){const n=e.value,r={type:Vs.COMMENT,data:n,location:Mc(e)};Rc(t,Fe(e)),t.parser.currentToken=r,t.parser._processToken(t.parser.currentToken)}function Ic(e,t){if(t.parser.tokenizer.preprocessor.html=\"\",t.parser.tokenizer.preprocessor.pos=-1,t.parser.tokenizer.preprocessor.lastGapPos=-2,t.parser.tokenizer.preprocessor.gapStack=[],t.parser.tokenizer.preprocessor.skipNextNewLine=!1,t.parser.tokenizer.preprocessor.lastChunkWritten=!1,t.parser.tokenizer.preprocessor.endOfChunkHit=!1,t.parser.tokenizer.preprocessor.isEol=!1,Dc(t,Fe(e)),t.parser.tokenizer.write(t.options.tagfilter?e.value.replace(yc,\"&lt;$1$2\"):e.value,!1),t.parser.tokenizer._runParsingLoop(),72===t.parser.tokenizer.state||78===t.parser.tokenizer.state){t.parser.tokenizer.preprocessor.lastChunkWritten=!0;const e=t.parser.tokenizer._consume();t.parser.tokenizer._callState(e)}}function Oc(e,t){const n=e;if(!t.options.passThrough||!t.options.passThrough.includes(n.type)){let e=\"\";throw _c.has(n.type)&&(e=\". It looks like you are using MDX nodes with `hast-util-raw` (or `rehype-raw`). If you use this because you are using remark or rehype plugins that inject `'html'` nodes, then please raise an issue with that plugin, as its a bad and slow idea. If you use this because you are using markdown syntax, then you have to configure this utility (or plugin) to pass through these nodes (see `passThrough` in docs), but you can also migrate to use the MDX syntax\"),new Error(\"Cannot compile `\"+n.type+\"` node\"+e)}xc(n,t)}function Rc(e,t){Dc(e,t);const n=e.parser.tokenizer.currentCharacterToken;n&&n.location&&(n.location.endLine=e.parser.tokenizer.preprocessor.line,n.location.endCol=e.parser.tokenizer.preprocessor.col+1,n.location.endOffset=e.parser.tokenizer.preprocessor.offset+1,e.parser.currentToken=n,e.parser._processToken(e.parser.currentToken)),e.parser.tokenizer.paused=!1,e.parser.tokenizer.inLoop=!1,e.parser.tokenizer.active=!1,e.parser.tokenizer.returnState=Io.DATA,e.parser.tokenizer.charRefCode=-1,e.parser.tokenizer.consumedAfterSnapshot=-1,e.parser.tokenizer.currentLocation=null,e.parser.tokenizer.currentCharacterToken=null,e.parser.tokenizer.currentToken=null,e.parser.tokenizer.currentAttr={name:\"\",value:\"\"}}function Dc(e,t){if(t&&void 0!==t.offset){const n={startLine:t.line,startCol:t.column,startOffset:t.offset,endLine:-1,endCol:-1,endOffset:-1};e.parser.tokenizer.preprocessor.lineStartPos=1-t.column,e.parser.tokenizer.preprocessor.droppedBufferSize=t.offset,e.parser.tokenizer.preprocessor.line=t.line,e.parser.tokenizer.currentLocation=n}}function Mc(e){const t=Fe(e)||{line:void 0,column:void 0,offset:void 0},n=Le(e)||{line:void 0,column:void 0,offset:void 0};return{startLine:t.line,startCol:t.column,startOffset:t.offset,endLine:n.line,endCol:n.column,endOffset:n.offset}}const Pc=[\"ariaDescribedBy\",\"ariaLabel\",\"ariaLabelledBy\"],Lc={ancestors:{tbody:[\"table\"],td:[\"table\"],th:[\"table\"],thead:[\"table\"],tfoot:[\"table\"],tr:[\"table\"]},attributes:{a:[...Pc,\"dataFootnoteBackref\",\"dataFootnoteRef\",[\"className\",\"data-footnote-backref\"],\"href\"],blockquote:[\"cite\"],code:[[\"className\",/^language-./]],del:[\"cite\"],div:[\"itemScope\",\"itemType\"],dl:[...Pc],h2:[[\"className\",\"sr-only\"]],img:[...Pc,\"longDesc\",\"src\"],input:[[\"disabled\",!0],[\"type\",\"checkbox\"]],ins:[\"cite\"],li:[[\"className\",\"task-list-item\"]],ol:[...Pc,[\"className\",\"contains-task-list\"]],q:[\"cite\"],section:[\"dataFootnotes\",[\"className\",\"footnotes\"]],source:[\"srcSet\"],summary:[...Pc],table:[...Pc],ul:[...Pc,[\"className\",\"contains-task-list\"]],\"*\":[\"abbr\",\"accept\",\"acceptCharset\",\"accessKey\",\"action\",\"align\",\"alt\",\"axis\",\"border\",\"cellPadding\",\"cellSpacing\",\"char\",\"charOff\",\"charSet\",\"checked\",\"clear\",\"colSpan\",\"color\",\"cols\",\"compact\",\"coords\",\"dateTime\",\"dir\",\"encType\",\"frame\",\"hSpace\",\"headers\",\"height\",\"hrefLang\",\"htmlFor\",\"id\",\"isMap\",\"itemProp\",\"label\",\"lang\",\"maxLength\",\"media\",\"method\",\"multiple\",\"name\",\"noHref\",\"noShade\",\"noWrap\",\"open\",\"prompt\",\"readOnly\",\"rev\",\"rowSpan\",\"rows\",\"rules\",\"scope\",\"selected\",\"shape\",\"size\",\"span\",\"start\",\"summary\",\"tabIndex\",\"title\",\"useMap\",\"vAlign\",\"value\",\"width\"]},clobber:[\"ariaDescribedBy\",\"ariaLabelledBy\",\"id\",\"name\"],clobberPrefix:\"user-content-\",protocols:{cite:[\"http\",\"https\"],href:[\"http\",\"https\",\"irc\",\"ircs\",\"mailto\",\"xmpp\"],longDesc:[\"http\",\"https\"],src:[\"http\",\"https\"]},required:{input:{disabled:!0,type:\"checkbox\"}},strip:[\"script\"],tagNames:[\"a\",\"b\",\"blockquote\",\"br\",\"code\",\"dd\",\"del\",\"details\",\"div\",\"dl\",\"dt\",\"em\",\"h1\",\"h2\",\"h3\",\"h4\",\"h5\",\"h6\",\"hr\",\"i\",\"img\",\"input\",\"ins\",\"kbd\",\"li\",\"ol\",\"p\",\"picture\",\"pre\",\"q\",\"rp\",\"rt\",\"ruby\",\"s\",\"samp\",\"section\",\"source\",\"span\",\"strike\",\"strong\",\"sub\",\"summary\",\"sup\",\"table\",\"tbody\",\"td\",\"tfoot\",\"th\",\"thead\",\"tr\",\"tt\",\"ul\",\"var\"]},Fc={}.hasOwnProperty;function Bc(e,t){if(t&&\"object\"==typeof t){const n=t;switch(\"string\"==typeof n.type?n.type:\"\"){case\"comment\":return function(e,t){if(e.schema.allowComments){const e=\"string\"==typeof t.value?t.value:\"\",n=e.indexOf(\"--\\x3e\"),r={type:\"comment\",value:n<0?e:e.slice(0,n)};return Gc(r,t),r}}(e,n);case\"doctype\":return function(e,t){if(e.schema.allowDoctypes){const e={type:\"doctype\"};return Gc(e,t),e}}(e,n);case\"element\":return function(e,t){const n=\"string\"==typeof t.tagName?t.tagName:\"\";e.stack.push(n);const r=Uc(e,t.children),a=function(e,t){const n=e.stack[e.stack.length-1],r=e.schema.attributes,a=e.schema.required,i=r&&Fc.call(r,n)?r[n]:void 0,s=r&&Fc.call(r,\"*\")?r[\"*\"]:void 0,o=t&&\"object\"==typeof t?t:{},l={};let c;for(c in o)if(Fc.call(o,c)){const t=o[c];let n=Hc(e,jc(i,c),c,t);null==n&&(n=Hc(e,jc(s,c),c,t)),null!=n&&(l[c]=n)}if(a&&Fc.call(a,n)){const e=a[n];for(c in e)Fc.call(e,c)&&!Fc.call(l,c)&&(l[c]=e[c])}return l}(e,t.properties);e.stack.pop();let i=!1;if(n&&\"*\"!==n&&(!e.schema.tagNames||e.schema.tagNames.includes(n))&&(i=!0,e.schema.ancestors&&Fc.call(e.schema.ancestors,n))){const t=e.schema.ancestors[n];let r=-1;for(i=!1;++r<t.length;)e.stack.includes(t[r])&&(i=!0)}if(!i)return e.schema.strip&&!e.schema.strip.includes(n)?r:void 0;const s={type:\"element\",tagName:n,properties:a,children:r};return Gc(s,t),s}(e,n);case\"root\":return function(e,t){const n=Uc(e,t.children),r={type:\"root\",children:n};return Gc(r,t),r}(e,n);case\"text\":return function(e,t){const n=\"string\"==typeof t.value?t.value:\"\",r={type:\"text\",value:n};return Gc(r,t),r}(0,n)}}}function Uc(e,t){const n=[];if(Array.isArray(t)){const r=t;let a=-1;for(;++a<r.length;){const t=Bc(e,r[a]);t&&(Array.isArray(t)?n.push(...t):n.push(t))}}return n}function Hc(e,t,n,r){return t?Array.isArray(r)?function(e,t,n,r){let a=-1;const i=[];for(;++a<r.length;){const s=zc(e,t,n,r[a]);\"number\"!=typeof s&&\"string\"!=typeof s||i.push(s)}return i}(e,t,n,r):zc(e,t,n,r):void 0}function zc(e,t,n,r){if((\"boolean\"==typeof r||\"number\"==typeof r||\"string\"==typeof r)&&function(e,t,n){const r=e.schema.protocols&&Fc.call(e.schema.protocols,t)?e.schema.protocols[t]:void 0;if(!r||0===r.length)return!0;const a=String(n),i=a.indexOf(\":\"),s=a.indexOf(\"?\"),o=a.indexOf(\"#\"),l=a.indexOf(\"/\");if(i<0||l>-1&&i>l||s>-1&&i>s||o>-1&&i>o)return!0;let c=-1;for(;++c<r.length;){const e=r[c];if(i===e.length&&a.slice(0,e.length)===e)return!0}return!1}(e,n,r)){if(\"object\"==typeof t&&t.length>1){let e=!1,n=0;for(;++n<t.length;){const a=t[n];if(a&&\"object\"==typeof a&&\"flags\"in a){if(a.test(String(r))){e=!0;break}}else if(a===r){e=!0;break}}if(!e)return}return e.schema.clobber&&e.schema.clobberPrefix&&e.schema.clobber.includes(n)?e.schema.clobberPrefix+r:r}}function Gc(e,t){const n=Ue(t);t.data&&(e.data=yr(t.data)),n&&(e.position=n)}function jc(e,t){let n,r=-1;if(e)for(;++r<e.length;){const a=e[r],i=\"string\"==typeof a?a:a[0];if(i===t)return a;\"data*\"===i&&(n=a)}if(t.length>4&&\"data\"===t.slice(0,4).toLowerCase())return n}function $c(e,t){const n=String(e);if(\"string\"!=typeof t)throw new TypeError(\"Expected character\");let r=0,a=n.indexOf(t);for(;-1!==a;)r++,a=n.indexOf(t,a+t.length);return r}function qc(e,t,n){const r=Tr((n||{}).ignore||[]),a=function(e){const t=[];if(!Array.isArray(e))throw new TypeError(\"Expected find and replace tuple or list of tuples\");const n=!e[0]||Array.isArray(e[0])?e:[e];let r=-1;for(;++r<n.length;){const e=n[r];t.push([Yc(e[0]),Wc(e[1])])}return t}(t);let i=-1;for(;++i<a.length;)xr(e,\"text\",s);function s(e,t){let n,s=-1;for(;++s<t.length;){const e=t[s],a=n?n.children:void 0;if(r(e,a?a.indexOf(e):void 0,n))return;n=e}if(n)return function(e,t){const n=t[t.length-1],r=a[i][0],s=a[i][1];let o=0;const l=n.children.indexOf(e);let c=!1,u=[];r.lastIndex=0;let d=r.exec(e.value);for(;d;){const n=d.index,a={index:d.index,input:d.input,stack:[...t,e]};let i=s(...d,a);if(\"string\"==typeof i&&(i=i.length>0?{type:\"text\",value:i}:void 0),!1===i?r.lastIndex=n+1:(o!==n&&u.push({type:\"text\",value:e.value.slice(o,n)}),Array.isArray(i)?u.push(...i):i&&u.push(i),o=n+d[0].length,c=!0),!r.global)break;d=r.exec(e.value)}c?(o<e.value.length&&u.push({type:\"text\",value:e.value.slice(o)}),n.children.splice(l,1,...u)):u=[e];return l+u.length}(e,t)}}function Yc(e){return\"string\"==typeof e?new RegExp(function(e){if(\"string\"!=typeof e)throw new TypeError(\"Expected a string\");return e.replace(/[|\\\\{}()[\\]^$+*?.]/g,\"\\\\$&\").replace(/-/g,\"\\\\x2d\")}(e),\"g\"):e}function Wc(e){return\"function\"==typeof e?e:function(){return e}}const Vc=\"phrasing\",Kc=[\"autolink\",\"link\",\"image\",\"label\"];function Qc(e){this.enter({type:\"link\",title:null,url:\"\",children:[]},e)}function Xc(e){this.config.enter.autolinkProtocol.call(this,e)}function Zc(e){this.config.exit.autolinkProtocol.call(this,e)}function Jc(e){this.config.exit.data.call(this,e);const t=this.stack[this.stack.length-1];t.type,t.url=\"http://\"+this.sliceSerialize(e)}function eu(e){this.config.exit.autolinkEmail.call(this,e)}function tu(e){this.exit(e)}function nu(e){qc(e,[[/(https?:\\/\\/|www(?=\\.))([-.\\w]+)([^ \\t\\r\\n]*)/gi,ru],[new RegExp(\"(?<=^|\\\\s|\\\\p{P}|\\\\p{S})([-.\\\\w+]+)@([-\\\\w]+(?:\\\\.[-\\\\w]+)+)\",\"gu\"),au]],{ignore:[\"link\",\"linkReference\"]})}function ru(e,t,n,r,a){let i=\"\";if(!iu(a))return!1;if(/^w/i.test(t)&&(n=t+n,t=\"\",i=\"http://\"),!function(e){const t=e.split(\".\");if(t.length<2||t[t.length-1]&&(/_/.test(t[t.length-1])||!/[a-zA-Z\\d]/.test(t[t.length-1]))||t[t.length-2]&&(/_/.test(t[t.length-2])||!/[a-zA-Z\\d]/.test(t[t.length-2])))return!1;return!0}(n))return!1;const s=function(e){const t=/[!\"&'),.:;<>?\\]}]+$/.exec(e);if(!t)return[e,void 0];e=e.slice(0,t.index);let n=t[0],r=n.indexOf(\")\");const a=$c(e,\"(\");let i=$c(e,\")\");for(;-1!==r&&a>i;)e+=n.slice(0,r+1),n=n.slice(r+1),r=n.indexOf(\")\"),i++;return[e,n]}(n+r);if(!s[0])return!1;const o={type:\"link\",title:null,url:i+t+s[0],children:[{type:\"text\",value:t+s[0]}]};return s[1]?[o,{type:\"text\",value:s[1]}]:o}function au(e,t,n,r){return!(!iu(r,!0)||/[-\\d_]$/.test(n))&&{type:\"link\",title:null,url:\"mailto:\"+t+\"@\"+n,children:[{type:\"text\",value:t+\"@\"+n}]}}function iu(e,t){const n=e.input.charCodeAt(e.index-1);return(0===e.index||Dt(n)||Rt(n))&&(!t||47!==n)}function su(){this.buffer()}function ou(e){this.enter({type:\"footnoteReference\",identifier:\"\",label:\"\"},e)}function lu(){this.buffer()}function cu(e){this.enter({type:\"footnoteDefinition\",identifier:\"\",label:\"\",children:[]},e)}function uu(e){const t=this.resume(),n=this.stack[this.stack.length-1];n.type,n.identifier=kt(this.sliceSerialize(e)).toLowerCase(),n.label=t}function du(e){this.exit(e)}function pu(e){const t=this.resume(),n=this.stack[this.stack.length-1];n.type,n.identifier=kt(this.sliceSerialize(e)).toLowerCase(),n.label=t}function hu(e){this.exit(e)}function fu(e,t,n,r){const a=n.createTracker(r);let i=a.move(\"[^\");const s=n.enter(\"footnoteReference\"),o=n.enter(\"reference\");return i+=a.move(n.safe(n.associationId(e),{after:\"]\",before:i})),o(),s(),i+=a.move(\"]\"),i}function mu(e){let t=!1;return e&&e.firstLineBlank&&(t=!0),{handlers:{footnoteDefinition:function(e,n,r,a){const i=r.createTracker(a);let s=i.move(\"[^\");const o=r.enter(\"footnoteDefinition\"),l=r.enter(\"label\");s+=i.move(r.safe(r.associationId(e),{before:s,after:\"]\"})),l(),s+=i.move(\"]:\"),e.children&&e.children.length>0&&(i.shift(4),s+=i.move((t?\"\\n\":\" \")+r.indentLines(r.containerFlow(e,i.current()),t?bu:gu)));return o(),s},footnoteReference:fu},unsafe:[{character:\"[\",inConstruct:[\"label\",\"phrasing\",\"reference\"]}]}}function gu(e,t,n){return 0===t?e:bu(e,t,n)}function bu(e,t,n){return(n?\"\":\"    \")+e}fu.peek=function(){return\"[\"};const Eu=[\"autolink\",\"destinationLiteral\",\"destinationRaw\",\"reference\",\"titleQuote\",\"titleApostrophe\"];function yu(e){this.enter({type:\"delete\",children:[]},e)}function _u(e){this.exit(e)}function ku(e,t,n,r){const a=n.createTracker(r),i=n.enter(\"strikethrough\");let s=a.move(\"~~\");return s+=n.containerPhrasing(e,{...a.current(),before:s,after:\"~\"}),s+=a.move(\"~~\"),i(),s}function Tu(e){return e.length}function Su(e){return null==e?\"\":String(e)}function vu(e){const t=\"string\"==typeof e?e.codePointAt(0):0;return 67===t||99===t?99:76===t||108===t?108:82===t||114===t?114:0}function Au(e,t,n){return\">\"+(n?\"\":\" \")+e}function Nu(e,t){return Cu(e,t.inConstruct,!0)&&!Cu(e,t.notInConstruct,!1)}function Cu(e,t,n){if(\"string\"==typeof t&&(t=[t]),!t||0===t.length)return n;let r=-1;for(;++r<t.length;)if(e.includes(t[r]))return!0;return!1}function xu(e,t,n,r){let a=-1;for(;++a<n.unsafe.length;)if(\"\\n\"===n.unsafe[a].character&&Nu(n.stack,n.unsafe[a]))return/[ \\t]/.test(r.before)?\"\":\" \";return\"\\\\\\n\"}function wu(e,t,n){return(n?\"\":\"    \")+e}function Iu(e){const t=e.options.quote||'\"';if('\"'!==t&&\"'\"!==t)throw new Error(\"Cannot serialize title with `\"+t+\"` for `options.quote`, expected `\\\"`, or `'`\");return t}function Ou(e){return\"&#x\"+e.toString(16).toUpperCase()+\";\"}function Ru(e,t,n){const r=Ht(e),a=Ht(t);return void 0===r?void 0===a?\"_\"===n?{inside:!0,outside:!0}:{inside:!1,outside:!1}:1===a?{inside:!0,outside:!0}:{inside:!1,outside:!0}:1===r?void 0===a?{inside:!1,outside:!1}:1===a?{inside:!0,outside:!0}:{inside:!1,outside:!1}:void 0===a?{inside:!1,outside:!1}:1===a?{inside:!0,outside:!1}:{inside:!1,outside:!1}}function Du(e,t,n,r){const a=function(e){const t=e.options.emphasis||\"*\";if(\"*\"!==t&&\"_\"!==t)throw new Error(\"Cannot serialize emphasis with `\"+t+\"` for `options.emphasis`, expected `*`, or `_`\");return t}(n),i=n.enter(\"emphasis\"),s=n.createTracker(r),o=s.move(a);let l=s.move(n.containerPhrasing(e,{after:a,before:o,...s.current()}));const c=l.charCodeAt(0),u=Ru(r.before.charCodeAt(r.before.length-1),c,a);u.inside&&(l=Ou(c)+l.slice(1));const d=l.charCodeAt(l.length-1),p=Ru(r.after.charCodeAt(0),d,a);p.inside&&(l=l.slice(0,-1)+Ou(d));const h=s.move(a);return i(),n.attentionEncodeSurroundingInfo={after:p.outside,before:u.outside},o+l+h}function Mu(e){return e.value||\"\"}function Pu(e,t,n,r){const a=Iu(n),i='\"'===a?\"Quote\":\"Apostrophe\",s=n.enter(\"image\");let o=n.enter(\"label\");const l=n.createTracker(r);let c=l.move(\"![\");return c+=l.move(n.safe(e.alt,{before:c,after:\"]\",...l.current()})),c+=l.move(\"](\"),o(),!e.url&&e.title||/[\\0- \\u007F]/.test(e.url)?(o=n.enter(\"destinationLiteral\"),c+=l.move(\"<\"),c+=l.move(n.safe(e.url,{before:c,after:\">\",...l.current()})),c+=l.move(\">\")):(o=n.enter(\"destinationRaw\"),c+=l.move(n.safe(e.url,{before:c,after:e.title?\" \":\")\",...l.current()}))),o(),e.title&&(o=n.enter(`title${i}`),c+=l.move(\" \"+a),c+=l.move(n.safe(e.title,{before:c,after:a,...l.current()})),c+=l.move(a),o()),c+=l.move(\")\"),s(),c}function Lu(e,t,n,r){const a=e.referenceType,i=n.enter(\"imageReference\");let s=n.enter(\"label\");const o=n.createTracker(r);let l=o.move(\"![\");const c=n.safe(e.alt,{before:l,after:\"]\",...o.current()});l+=o.move(c+\"][\"),s();const u=n.stack;n.stack=[],s=n.enter(\"reference\");const d=n.safe(n.associationId(e),{before:l,after:\"]\",...o.current()});return s(),n.stack=u,i(),\"full\"!==a&&c&&c===d?\"shortcut\"===a?l=l.slice(0,-1):l+=o.move(\"]\"):l+=o.move(d+\"]\"),l}function Fu(e,t,n){let r=e.value||\"\",a=\"`\",i=-1;for(;new RegExp(\"(^|[^`])\"+a+\"([^`]|$)\").test(r);)a+=\"`\";for(/[^ \\r\\n]/.test(r)&&(/^[ \\r\\n]/.test(r)&&/[ \\r\\n]$/.test(r)||/^`|`$/.test(r))&&(r=\" \"+r+\" \");++i<n.unsafe.length;){const e=n.unsafe[i],t=n.compilePattern(e);let a;if(e.atBreak)for(;a=t.exec(r);){let e=a.index;10===r.charCodeAt(e)&&13===r.charCodeAt(e-1)&&e--,r=r.slice(0,e)+\" \"+r.slice(a.index+1)}}return a+r+a}function Bu(e,t){const n=ct(e);return Boolean(!t.options.resourceLink&&e.url&&!e.title&&e.children&&1===e.children.length&&\"text\"===e.children[0].type&&(n===e.url||\"mailto:\"+n===e.url)&&/^[a-z][a-z+.-]+:/i.test(e.url)&&!/[\\0- <>\\u007F]/.test(e.url))}function Uu(e,t,n,r){const a=Iu(n),i='\"'===a?\"Quote\":\"Apostrophe\",s=n.createTracker(r);let o,l;if(Bu(e,n)){const t=n.stack;n.stack=[],o=n.enter(\"autolink\");let r=s.move(\"<\");return r+=s.move(n.containerPhrasing(e,{before:r,after:\">\",...s.current()})),r+=s.move(\">\"),o(),n.stack=t,r}o=n.enter(\"link\"),l=n.enter(\"label\");let c=s.move(\"[\");return c+=s.move(n.containerPhrasing(e,{before:c,after:\"](\",...s.current()})),c+=s.move(\"](\"),l(),!e.url&&e.title||/[\\0- \\u007F]/.test(e.url)?(l=n.enter(\"destinationLiteral\"),c+=s.move(\"<\"),c+=s.move(n.safe(e.url,{before:c,after:\">\",...s.current()})),c+=s.move(\">\")):(l=n.enter(\"destinationRaw\"),c+=s.move(n.safe(e.url,{before:c,after:e.title?\" \":\")\",...s.current()}))),l(),e.title&&(l=n.enter(`title${i}`),c+=s.move(\" \"+a),c+=s.move(n.safe(e.title,{before:c,after:a,...s.current()})),c+=s.move(a),l()),c+=s.move(\")\"),o(),c}function Hu(e,t,n,r){const a=e.referenceType,i=n.enter(\"linkReference\");let s=n.enter(\"label\");const o=n.createTracker(r);let l=o.move(\"[\");const c=n.containerPhrasing(e,{before:l,after:\"]\",...o.current()});l+=o.move(c+\"][\"),s();const u=n.stack;n.stack=[],s=n.enter(\"reference\");const d=n.safe(n.associationId(e),{before:l,after:\"]\",...o.current()});return s(),n.stack=u,i(),\"full\"!==a&&c&&c===d?\"shortcut\"===a?l=l.slice(0,-1):l+=o.move(\"]\"):l+=o.move(d+\"]\"),l}function zu(e){const t=e.options.bullet||\"*\";if(\"*\"!==t&&\"+\"!==t&&\"-\"!==t)throw new Error(\"Cannot serialize items with `\"+t+\"` for `options.bullet`, expected `*`, `+`, or `-`\");return t}function Gu(e){const t=e.options.rule||\"*\";if(\"*\"!==t&&\"-\"!==t&&\"_\"!==t)throw new Error(\"Cannot serialize rules with `\"+t+\"` for `options.rule`, expected `*`, `-`, or `_`\");return t}ku.peek=function(){return\"~\"},Du.peek=function(e,t,n){return n.options.emphasis||\"*\"},Mu.peek=function(){return\"<\"},Pu.peek=function(){return\"!\"},Lu.peek=function(){return\"!\"},Fu.peek=function(){return\"`\"},Uu.peek=function(e,t,n){return Bu(e,n)?\"<\":\"[\"},Hu.peek=function(){return\"[\"};const ju=Tr([\"break\",\"delete\",\"emphasis\",\"footnote\",\"footnoteReference\",\"image\",\"imageReference\",\"inlineCode\",\"inlineMath\",\"link\",\"linkReference\",\"mdxJsxTextElement\",\"mdxTextExpression\",\"strong\",\"text\",\"textDirective\"]);function $u(e,t,n,r){const a=function(e){const t=e.options.strong||\"*\";if(\"*\"!==t&&\"_\"!==t)throw new Error(\"Cannot serialize strong with `\"+t+\"` for `options.strong`, expected `*`, or `_`\");return t}(n),i=n.enter(\"strong\"),s=n.createTracker(r),o=s.move(a+a);let l=s.move(n.containerPhrasing(e,{after:a,before:o,...s.current()}));const c=l.charCodeAt(0),u=Ru(r.before.charCodeAt(r.before.length-1),c,a);u.inside&&(l=Ou(c)+l.slice(1));const d=l.charCodeAt(l.length-1),p=Ru(r.after.charCodeAt(0),d,a);p.inside&&(l=l.slice(0,-1)+Ou(d));const h=s.move(a+a);return i(),n.attentionEncodeSurroundingInfo={after:p.outside,before:u.outside},o+l+h}$u.peek=function(e,t,n){return n.options.strong||\"*\"};const qu={blockquote:function(e,t,n,r){const a=n.enter(\"blockquote\"),i=n.createTracker(r);i.move(\"> \"),i.shift(2);const s=n.indentLines(n.containerFlow(e,i.current()),Au);return a(),s},break:xu,code:function(e,t,n,r){const a=function(e){const t=e.options.fence||\"`\";if(\"`\"!==t&&\"~\"!==t)throw new Error(\"Cannot serialize code with `\"+t+\"` for `options.fence`, expected `` ` `` or `~`\");return t}(n),i=e.value||\"\",s=\"`\"===a?\"GraveAccent\":\"Tilde\";if(function(e,t){return Boolean(!1===t.options.fences&&e.value&&!e.lang&&/[^ \\r\\n]/.test(e.value)&&!/^[\\t ]*(?:[\\r\\n]|$)|(?:^|[\\r\\n])[\\t ]*$/.test(e.value))}(e,n)){const e=n.enter(\"codeIndented\"),t=n.indentLines(i,wu);return e(),t}const o=n.createTracker(r),l=a.repeat(Math.max(function(e,t){const n=String(e);let r=n.indexOf(t),a=r,i=0,s=0;if(\"string\"!=typeof t)throw new TypeError(\"Expected substring\");for(;-1!==r;)r===a?++i>s&&(s=i):i=1,a=r+t.length,r=n.indexOf(t,a);return s}(i,a)+1,3)),c=n.enter(\"codeFenced\");let u=o.move(l);if(e.lang){const t=n.enter(`codeFencedLang${s}`);u+=o.move(n.safe(e.lang,{before:u,after:\" \",encode:[\"`\"],...o.current()})),t()}if(e.lang&&e.meta){const t=n.enter(`codeFencedMeta${s}`);u+=o.move(\" \"),u+=o.move(n.safe(e.meta,{before:u,after:\"\\n\",encode:[\"`\"],...o.current()})),t()}return u+=o.move(\"\\n\"),i&&(u+=o.move(i+\"\\n\")),u+=o.move(l),c(),u},definition:function(e,t,n,r){const a=Iu(n),i='\"'===a?\"Quote\":\"Apostrophe\",s=n.enter(\"definition\");let o=n.enter(\"label\");const l=n.createTracker(r);let c=l.move(\"[\");return c+=l.move(n.safe(n.associationId(e),{before:c,after:\"]\",...l.current()})),c+=l.move(\"]: \"),o(),!e.url||/[\\0- \\u007F]/.test(e.url)?(o=n.enter(\"destinationLiteral\"),c+=l.move(\"<\"),c+=l.move(n.safe(e.url,{before:c,after:\">\",...l.current()})),c+=l.move(\">\")):(o=n.enter(\"destinationRaw\"),c+=l.move(n.safe(e.url,{before:c,after:e.title?\" \":\"\\n\",...l.current()}))),o(),e.title&&(o=n.enter(`title${i}`),c+=l.move(\" \"+a),c+=l.move(n.safe(e.title,{before:c,after:a,...l.current()})),c+=l.move(a),o()),s(),c},emphasis:Du,hardBreak:xu,heading:function(e,t,n,r){const a=Math.max(Math.min(6,e.depth||1),1),i=n.createTracker(r);if(function(e,t){let n=!1;return wr(e,function(e){if(\"value\"in e&&/\\r?\\n|\\r/.test(e.value)||\"break\"===e.type)return n=!0,Cr}),Boolean((!e.depth||e.depth<3)&&ct(e)&&(t.options.setext||n))}(e,n)){const t=n.enter(\"headingSetext\"),r=n.enter(\"phrasing\"),s=n.containerPhrasing(e,{...i.current(),before:\"\\n\",after:\"\\n\"});return r(),t(),s+\"\\n\"+(1===a?\"=\":\"-\").repeat(s.length-(Math.max(s.lastIndexOf(\"\\r\"),s.lastIndexOf(\"\\n\"))+1))}const s=\"#\".repeat(a),o=n.enter(\"headingAtx\"),l=n.enter(\"phrasing\");i.move(s+\" \");let c=n.containerPhrasing(e,{before:\"# \",after:\"\\n\",...i.current()});return/^[\\t ]/.test(c)&&(c=Ou(c.charCodeAt(0))+c.slice(1)),c=c?s+\" \"+c:s,n.options.closeAtx&&(c+=\" \"+s),l(),o(),c},html:Mu,image:Pu,imageReference:Lu,inlineCode:Fu,link:Uu,linkReference:Hu,list:function(e,t,n,r){const a=n.enter(\"list\"),i=n.bulletCurrent;let s=e.ordered?function(e){const t=e.options.bulletOrdered||\".\";if(\".\"!==t&&\")\"!==t)throw new Error(\"Cannot serialize items with `\"+t+\"` for `options.bulletOrdered`, expected `.` or `)`\");return t}(n):zu(n);const o=e.ordered?\".\"===s?\")\":\".\":function(e){const t=zu(e),n=e.options.bulletOther;if(!n)return\"*\"===t?\"-\":\"*\";if(\"*\"!==n&&\"+\"!==n&&\"-\"!==n)throw new Error(\"Cannot serialize items with `\"+n+\"` for `options.bulletOther`, expected `*`, `+`, or `-`\");if(n===t)throw new Error(\"Expected `bullet` (`\"+t+\"`) and `bulletOther` (`\"+n+\"`) to be different\");return n}(n);let l=!(!t||!n.bulletLastUsed)&&s===n.bulletLastUsed;if(!e.ordered){const t=e.children?e.children[0]:void 0;if(\"*\"!==s&&\"-\"!==s||!t||t.children&&t.children[0]||\"list\"!==n.stack[n.stack.length-1]||\"listItem\"!==n.stack[n.stack.length-2]||\"list\"!==n.stack[n.stack.length-3]||\"listItem\"!==n.stack[n.stack.length-4]||0!==n.indexStack[n.indexStack.length-1]||0!==n.indexStack[n.indexStack.length-2]||0!==n.indexStack[n.indexStack.length-3]||(l=!0),Gu(n)===s&&t){let t=-1;for(;++t<e.children.length;){const n=e.children[t];if(n&&\"listItem\"===n.type&&n.children&&n.children[0]&&\"thematicBreak\"===n.children[0].type){l=!0;break}}}}l&&(s=o),n.bulletCurrent=s;const c=n.containerFlow(e,r);return n.bulletLastUsed=s,n.bulletCurrent=i,a(),c},listItem:function(e,t,n,r){const a=function(e){const t=e.options.listItemIndent||\"one\";if(\"tab\"!==t&&\"one\"!==t&&\"mixed\"!==t)throw new Error(\"Cannot serialize items with `\"+t+\"` for `options.listItemIndent`, expected `tab`, `one`, or `mixed`\");return t}(n);let i=n.bulletCurrent||zu(n);t&&\"list\"===t.type&&t.ordered&&(i=(\"number\"==typeof t.start&&t.start>-1?t.start:1)+(!1===n.options.incrementListMarker?0:t.children.indexOf(e))+i);let s=i.length+1;(\"tab\"===a||\"mixed\"===a&&(t&&\"list\"===t.type&&t.spread||e.spread))&&(s=4*Math.ceil(s/4));const o=n.createTracker(r);o.move(i+\" \".repeat(s-i.length)),o.shift(s);const l=n.enter(\"listItem\"),c=n.indentLines(n.containerFlow(e,o.current()),function(e,t,n){if(t)return(n?\"\":\" \".repeat(s))+e;return(n?i:i+\" \".repeat(s-i.length))+e});return l(),c},paragraph:function(e,t,n,r){const a=n.enter(\"paragraph\"),i=n.enter(\"phrasing\"),s=n.containerPhrasing(e,r);return i(),a(),s},root:function(e,t,n,r){return(e.children.some(function(e){return ju(e)})?n.containerPhrasing:n.containerFlow).call(n,e,r)},strong:$u,text:function(e,t,n,r){return n.safe(e.value,r)},thematicBreak:function(e,t,n){const r=(Gu(n)+(n.options.ruleSpaces?\" \":\"\")).repeat(function(e){const t=e.options.ruleRepetition||3;if(t<3)throw new Error(\"Cannot serialize rules with repetition `\"+t+\"` for `options.ruleRepetition`, expected `3` or more\");return t}(n));return n.options.ruleSpaces?r.slice(0,-1):r}};function Yu(e){const t=e._align;this.enter({type:\"table\",align:t.map(function(e){return\"none\"===e?null:e}),children:[]},e),this.data.inTable=!0}function Wu(e){this.exit(e),this.data.inTable=void 0}function Vu(e){this.enter({type:\"tableRow\",children:[]},e)}function Ku(e){this.exit(e)}function Qu(e){this.enter({type:\"tableCell\",children:[]},e)}function Xu(e){let t=this.resume();this.data.inTable&&(t=t.replace(/\\\\([\\\\|])/g,Zu));const n=this.stack[this.stack.length-1];n.type,n.value=t,this.exit(e)}function Zu(e,t){return\"|\"===t?t:e}function Ju(e){const t=e||{},n=t.tableCellPadding,r=t.tablePipeAlign,a=t.stringLength,i=n?\" \":\"|\";return{unsafe:[{character:\"\\r\",inConstruct:\"tableCell\"},{character:\"\\n\",inConstruct:\"tableCell\"},{atBreak:!0,character:\"|\",after:\"[\\t :-]\"},{character:\"|\",inConstruct:\"tableCell\"},{atBreak:!0,character:\":\",after:\"-\"},{atBreak:!0,character:\"-\",after:\"[:|-]\"}],handlers:{inlineCode:function(e,t,n){let r=qu.inlineCode(e,t,n);n.stack.includes(\"tableCell\")&&(r=r.replace(/\\|/g,\"\\\\$&\"));return r},table:function(e,t,n,r){return o(function(e,t,n){const r=e.children;let a=-1;const i=[],s=t.enter(\"table\");for(;++a<r.length;)i[a]=l(r[a],t,n);return s(),i}(e,n,r),e.align)},tableCell:s,tableRow:function(e,t,n,r){const a=o([l(e,n,r)]);return a.slice(0,a.indexOf(\"\\n\"))}}};function s(e,t,n,r){const a=n.enter(\"tableCell\"),s=n.enter(\"phrasing\"),o=n.containerPhrasing(e,{...r,before:i,after:i});return s(),a(),o}function o(e,t){return function(e,t){const n=t||{},r=(n.align||[]).concat(),a=n.stringLength||Tu,i=[],s=[],o=[],l=[];let c=0,u=-1;for(;++u<e.length;){const t=[],r=[];let i=-1;for(e[u].length>c&&(c=e[u].length);++i<e[u].length;){const s=Su(e[u][i]);if(!1!==n.alignDelimiters){const e=a(s);r[i]=e,(void 0===l[i]||e>l[i])&&(l[i]=e)}t.push(s)}s[u]=t,o[u]=r}let d=-1;if(\"object\"==typeof r&&\"length\"in r)for(;++d<c;)i[d]=vu(r[d]);else{const e=vu(r);for(;++d<c;)i[d]=e}d=-1;const p=[],h=[];for(;++d<c;){const e=i[d];let t=\"\",r=\"\";99===e?(t=\":\",r=\":\"):108===e?t=\":\":114===e&&(r=\":\");let a=!1===n.alignDelimiters?1:Math.max(1,l[d]-t.length-r.length);const s=t+\"-\".repeat(a)+r;!1!==n.alignDelimiters&&(a=t.length+a+r.length,a>l[d]&&(l[d]=a),h[d]=a),p[d]=s}s.splice(1,0,p),o.splice(1,0,h),u=-1;const f=[];for(;++u<s.length;){const e=s[u],t=o[u];d=-1;const r=[];for(;++d<c;){const a=e[d]||\"\";let s=\"\",o=\"\";if(!1!==n.alignDelimiters){const e=l[d]-(t[d]||0),n=i[d];114===n?s=\" \".repeat(e):99===n?e%2?(s=\" \".repeat(e/2+.5),o=\" \".repeat(e/2-.5)):(s=\" \".repeat(e/2),o=s):o=\" \".repeat(e)}!1===n.delimiterStart||d||r.push(\"|\"),!1===n.padding||!1===n.alignDelimiters&&\"\"===a||!1===n.delimiterStart&&!d||r.push(\" \"),!1!==n.alignDelimiters&&r.push(s),r.push(a),!1!==n.alignDelimiters&&r.push(o),!1!==n.padding&&r.push(\" \"),!1===n.delimiterEnd&&d===c-1||r.push(\"|\")}f.push(!1===n.delimiterEnd?r.join(\"\").replace(/ +$/,\"\"):r.join(\"\"))}return f.join(\"\\n\")}(e,{align:t,alignDelimiters:r,padding:n,stringLength:a})}function l(e,t,n){const r=e.children;let a=-1;const i=[],o=t.enter(\"tableRow\");for(;++a<r.length;)i[a]=s(r[a],0,t,n);return o(),i}}function ed(e){const t=this.stack[this.stack.length-2];t.type,t.checked=\"taskListCheckValueChecked\"===e.type}function td(e){const t=this.stack[this.stack.length-2];if(t&&\"listItem\"===t.type&&\"boolean\"==typeof t.checked){const e=this.stack[this.stack.length-1];e.type;const n=e.children[0];if(n&&\"text\"===n.type){const r=t.children;let a,i=-1;for(;++i<r.length;){const e=r[i];if(\"paragraph\"===e.type){a=e;break}}a===e&&(n.value=n.value.slice(1),0===n.value.length?e.children.shift():e.position&&n.position&&\"number\"==typeof n.position.start.offset&&(n.position.start.column++,n.position.start.offset++,e.position.start=Object.assign({},n.position.start)))}}this.exit(e)}function nd(e,t,n,r){const a=e.children[0],i=\"boolean\"==typeof e.checked&&a&&\"paragraph\"===a.type,s=\"[\"+(e.checked?\"x\":\" \")+\"] \",o=n.createTracker(r);i&&o.move(s);let l=qu.listItem(e,t,n,{...r,...o.current()});return i&&(l=l.replace(/^(?:[*+-]|\\d+\\.)([\\r\\n]| {1,3})/,function(e){return e+s})),l}const rd={tokenize:function(e,t,n){let r=0;return function t(i){if((87===i||119===i)&&r<3)return r++,e.consume(i),t;if(46===i&&3===r)return e.consume(i),a;return n(i)};function a(e){return null===e?n(e):t(e)}},partial:!0},ad={tokenize:function(e,t,n){let r,a,i;return s;function s(t){return 46===t||95===t?e.check(sd,l,o)(t):null===t||It(t)||Dt(t)||45!==t&&Rt(t)?l(t):(i=!0,e.consume(t),s)}function o(t){return 95===t?r=!0:(a=r,r=void 0),e.consume(t),s}function l(e){return a||r||!i?n(e):t(e)}},partial:!0},id={tokenize:function(e,t){let n=0,r=0;return a;function a(s){return 40===s?(n++,e.consume(s),a):41===s&&r<n?i(s):33===s||34===s||38===s||39===s||41===s||42===s||44===s||46===s||58===s||59===s||60===s||63===s||93===s||95===s||126===s?e.check(sd,t,i)(s):null===s||It(s)||Dt(s)?t(s):(e.consume(s),a)}function i(t){return 41===t&&r++,e.consume(t),a}},partial:!0},sd={tokenize:function(e,t,n){return r;function r(s){return 33===s||34===s||39===s||41===s||42===s||44===s||46===s||58===s||59===s||63===s||95===s||126===s?(e.consume(s),r):38===s?(e.consume(s),i):93===s?(e.consume(s),a):60===s||null===s||It(s)||Dt(s)?t(s):n(s)}function a(e){return null===e||40===e||91===e||It(e)||Dt(e)?t(e):r(e)}function i(e){return Tt(e)?s(e):n(e)}function s(t){return 59===t?(e.consume(t),r):Tt(t)?(e.consume(t),s):n(t)}},partial:!0},od={tokenize:function(e,t,n){return function(t){return e.consume(t),r};function r(e){return St(e)?n(e):t(e)}},partial:!0},ld={name:\"wwwAutolink\",tokenize:function(e,t,n){const r=this;return function(t){if(87!==t&&119!==t||!hd.call(r,r.previous)||bd(r.events))return n(t);return e.enter(\"literalAutolink\"),e.enter(\"literalAutolinkWww\"),e.check(rd,e.attempt(ad,e.attempt(id,a),n),n)(t)};function a(n){return e.exit(\"literalAutolinkWww\"),e.exit(\"literalAutolink\"),t(n)}},previous:hd},cd={name:\"protocolAutolink\",tokenize:function(e,t,n){const r=this;let a=\"\",i=!1;return function(t){if((72===t||104===t)&&fd.call(r,r.previous)&&!bd(r.events))return e.enter(\"literalAutolink\"),e.enter(\"literalAutolinkHttp\"),a+=String.fromCodePoint(t),e.consume(t),s;return n(t)};function s(t){if(Tt(t)&&a.length<5)return a+=String.fromCodePoint(t),e.consume(t),s;if(58===t){const n=a.toLowerCase();if(\"http\"===n||\"https\"===n)return e.consume(t),o}return n(t)}function o(t){return 47===t?(e.consume(t),i?l:(i=!0,o)):n(t)}function l(t){return null===t||At(t)||It(t)||Dt(t)||Rt(t)?n(t):e.attempt(ad,e.attempt(id,c),n)(t)}function c(n){return e.exit(\"literalAutolinkHttp\"),e.exit(\"literalAutolink\"),t(n)}},previous:fd},ud={name:\"emailAutolink\",tokenize:function(e,t,n){const r=this;let a,i;return function(t){if(!gd(t)||!md.call(r,r.previous)||bd(r.events))return n(t);return e.enter(\"literalAutolink\"),e.enter(\"literalAutolinkEmail\"),s(t)};function s(t){return gd(t)?(e.consume(t),s):64===t?(e.consume(t),o):n(t)}function o(t){return 46===t?e.check(od,c,l)(t):45===t||95===t||St(t)?(i=!0,e.consume(t),o):c(t)}function l(t){return e.consume(t),a=!0,o}function c(s){return i&&a&&Tt(r.previous)?(e.exit(\"literalAutolinkEmail\"),e.exit(\"literalAutolink\"),t(s)):n(s)}},previous:md},dd={};let pd=48;for(;pd<123;)dd[pd]=ud,pd++,58===pd?pd=65:91===pd&&(pd=97);function hd(e){return null===e||40===e||42===e||95===e||91===e||93===e||126===e||It(e)}function fd(e){return!Tt(e)}function md(e){return!(47===e||gd(e))}function gd(e){return 43===e||45===e||46===e||95===e||St(e)}function bd(e){let t=e.length,n=!1;for(;t--;){const r=e[t][1];if((\"labelLink\"===r.type||\"labelImage\"===r.type)&&!r._balanced){n=!0;break}if(r._gfmAutolinkLiteralWalkedInto){n=!1;break}}return e.length>0&&!n&&(e[e.length-1][1]._gfmAutolinkLiteralWalkedInto=!0),n}dd[43]=ud,dd[45]=ud,dd[46]=ud,dd[95]=ud,dd[72]=[ud,cd],dd[104]=[ud,cd],dd[87]=[ud,ld],dd[119]=[ud,ld];const Ed={tokenize:function(e,t,n){const r=this;return Lt(e,function(e){const a=r.events[r.events.length-1];return a&&\"gfmFootnoteDefinitionIndent\"===a[1].type&&4===a[2].sliceSerialize(a[1],!0).length?t(e):n(e)},\"gfmFootnoteDefinitionIndent\",5)},partial:!0};function yd(e,t,n){const r=this;let a=r.events.length;const i=r.parser.gfmFootnotes||(r.parser.gfmFootnotes=[]);let s;for(;a--;){const e=r.events[a][1];if(\"labelImage\"===e.type){s=e;break}if(\"gfmFootnoteCall\"===e.type||\"labelLink\"===e.type||\"label\"===e.type||\"image\"===e.type||\"link\"===e.type)break}return function(a){if(!s||!s._balanced)return n(a);const o=kt(r.sliceSerialize({start:s.end,end:r.now()}));if(94!==o.codePointAt(0)||!i.includes(o.slice(1)))return n(a);return e.enter(\"gfmFootnoteCallLabelMarker\"),e.consume(a),e.exit(\"gfmFootnoteCallLabelMarker\"),t(a)}}function _d(e,t){let n=e.length;for(;n--;)if(\"labelImage\"===e[n][1].type&&\"enter\"===e[n][0]){e[n][1];break}e[n+1][1].type=\"data\",e[n+3][1].type=\"gfmFootnoteCallLabelMarker\";const r={type:\"gfmFootnoteCall\",start:Object.assign({},e[n+3][1].start),end:Object.assign({},e[e.length-1][1].end)},a={type:\"gfmFootnoteCallMarker\",start:Object.assign({},e[n+3][1].end),end:Object.assign({},e[n+3][1].end)};a.end.column++,a.end.offset++,a.end._bufferIndex++;const i={type:\"gfmFootnoteCallString\",start:Object.assign({},a.end),end:Object.assign({},e[e.length-1][1].start)},s={type:\"chunkString\",contentType:\"string\",start:Object.assign({},i.start),end:Object.assign({},i.end)},o=[e[n+1],e[n+2],[\"enter\",r,t],e[n+3],e[n+4],[\"enter\",a,t],[\"exit\",a,t],[\"enter\",i,t],[\"enter\",s,t],[\"exit\",s,t],[\"exit\",i,t],e[e.length-2],e[e.length-1],[\"exit\",r,t]];return e.splice(n,e.length-n+1,...o),e}function kd(e,t,n){const r=this,a=r.parser.gfmFootnotes||(r.parser.gfmFootnotes=[]);let i,s=0;return function(t){return e.enter(\"gfmFootnoteCall\"),e.enter(\"gfmFootnoteCallLabelMarker\"),e.consume(t),e.exit(\"gfmFootnoteCallLabelMarker\"),o};function o(t){return 94!==t?n(t):(e.enter(\"gfmFootnoteCallMarker\"),e.consume(t),e.exit(\"gfmFootnoteCallMarker\"),e.enter(\"gfmFootnoteCallString\"),e.enter(\"chunkString\").contentType=\"string\",l)}function l(o){if(s>999||93===o&&!i||null===o||91===o||It(o))return n(o);if(93===o){e.exit(\"chunkString\");const i=e.exit(\"gfmFootnoteCallString\");return a.includes(kt(r.sliceSerialize(i)))?(e.enter(\"gfmFootnoteCallLabelMarker\"),e.consume(o),e.exit(\"gfmFootnoteCallLabelMarker\"),e.exit(\"gfmFootnoteCall\"),t):n(o)}return It(o)||(i=!0),s++,e.consume(o),92===o?c:l}function c(t){return 91===t||92===t||93===t?(e.consume(t),s++,l):l(t)}}function Td(e,t,n){const r=this,a=r.parser.gfmFootnotes||(r.parser.gfmFootnotes=[]);let i,s,o=0;return function(t){return e.enter(\"gfmFootnoteDefinition\")._container=!0,e.enter(\"gfmFootnoteDefinitionLabel\"),e.enter(\"gfmFootnoteDefinitionLabelMarker\"),e.consume(t),e.exit(\"gfmFootnoteDefinitionLabelMarker\"),l};function l(t){return 94===t?(e.enter(\"gfmFootnoteDefinitionMarker\"),e.consume(t),e.exit(\"gfmFootnoteDefinitionMarker\"),e.enter(\"gfmFootnoteDefinitionLabelString\"),e.enter(\"chunkString\").contentType=\"string\",c):n(t)}function c(t){if(o>999||93===t&&!s||null===t||91===t||It(t))return n(t);if(93===t){e.exit(\"chunkString\");const n=e.exit(\"gfmFootnoteDefinitionLabelString\");return i=kt(r.sliceSerialize(n)),e.enter(\"gfmFootnoteDefinitionLabelMarker\"),e.consume(t),e.exit(\"gfmFootnoteDefinitionLabelMarker\"),e.exit(\"gfmFootnoteDefinitionLabel\"),d}return It(t)||(s=!0),o++,e.consume(t),92===t?u:c}function u(t){return 91===t||92===t||93===t?(e.consume(t),o++,c):c(t)}function d(t){return 58===t?(e.enter(\"definitionMarker\"),e.consume(t),e.exit(\"definitionMarker\"),a.includes(i)||a.push(i),Lt(e,p,\"gfmFootnoteDefinitionWhitespace\")):n(t)}function p(e){return t(e)}}function Sd(e,t,n){return e.check(qt,t,e.attempt(Ed,t,n))}function vd(e){e.exit(\"gfmFootnoteDefinition\")}function Ad(e){let t=(e||{}).singleTilde;const n={name:\"strikethrough\",tokenize:function(e,n,r){const a=this.previous,i=this.events;let s=0;return function(t){if(126===a&&\"characterEscape\"!==i[i.length-1][1].type)return r(t);return e.enter(\"strikethroughSequenceTemporary\"),o(t)};function o(i){const l=Ht(a);if(126===i)return s>1?r(i):(e.consume(i),s++,o);if(s<2&&!t)return r(i);const c=e.exit(\"strikethroughSequenceTemporary\"),u=Ht(i);return c._open=!u||2===u&&Boolean(l),c._close=!l||2===l&&Boolean(u),n(i)}},resolveAll:function(e,t){let n=-1;for(;++n<e.length;)if(\"enter\"===e[n][0]&&\"strikethroughSequenceTemporary\"===e[n][1].type&&e[n][1]._close){let r=n;for(;r--;)if(\"exit\"===e[r][0]&&\"strikethroughSequenceTemporary\"===e[r][1].type&&e[r][1]._open&&e[n][1].end.offset-e[n][1].start.offset===e[r][1].end.offset-e[r][1].start.offset){e[n][1].type=\"strikethroughSequence\",e[r][1].type=\"strikethroughSequence\";const a={type:\"strikethrough\",start:Object.assign({},e[r][1].start),end:Object.assign({},e[n][1].end)},i={type:\"strikethroughText\",start:Object.assign({},e[r][1].end),end:Object.assign({},e[n][1].start)},s=[[\"enter\",a,t],[\"enter\",e[r][1],t],[\"exit\",e[r][1],t],[\"enter\",i,t]],o=t.parser.constructs.insideSpan.null;o&&ft(s,s.length,0,zt(o,e.slice(r+1,n),t)),ft(s,s.length,0,[[\"exit\",i,t],[\"enter\",e[n][1],t],[\"exit\",e[n][1],t],[\"exit\",a,t]]),ft(e,r-1,n-r+3,s),n=r+s.length-2;break}}n=-1;for(;++n<e.length;)\"strikethroughSequenceTemporary\"===e[n][1].type&&(e[n][1].type=\"data\");return e}};return null==t&&(t=!0),{text:{126:n},insideSpan:{null:[n]},attentionMarkers:{null:[126]}}}class Nd{constructor(){this.map=[]}add(e,t,n){!function(e,t,n,r){let a=0;if(0===n&&0===r.length)return;for(;a<e.map.length;){if(e.map[a][0]===t)return e.map[a][1]+=n,void e.map[a][2].push(...r);a+=1}e.map.push([t,n,r])}(this,e,t,n)}consume(e){if(this.map.sort(function(e,t){return e[0]-t[0]}),0===this.map.length)return;let t=this.map.length;const n=[];for(;t>0;)t-=1,n.push(e.slice(this.map[t][0]+this.map[t][1]),this.map[t][2]),e.length=this.map[t][0];n.push(e.slice()),e.length=0;let r=n.pop();for(;r;){for(const t of r)e.push(t);r=n.pop()}this.map.length=0}}function Cd(e,t){let n=!1;const r=[];for(;t<e.length;){const a=e[t];if(n){if(\"enter\"===a[0])\"tableContent\"===a[1].type&&r.push(\"tableDelimiterMarker\"===e[t+1][1].type?\"left\":\"none\");else if(\"tableContent\"===a[1].type){if(\"tableDelimiterMarker\"===e[t-1][1].type){const e=r.length-1;r[e]=\"left\"===r[e]?\"center\":\"right\"}}else if(\"tableDelimiterRow\"===a[1].type)break}else\"enter\"===a[0]&&\"tableDelimiterRow\"===a[1].type&&(n=!0);t+=1}return r}function xd(e,t,n){const r=this;let a,i=0,s=0;return function(e){let t=r.events.length-1;for(;t>-1;){const e=r.events[t][1].type;if(\"lineEnding\"!==e&&\"linePrefix\"!==e)break;t--}const a=t>-1?r.events[t][1].type:null,i=\"tableHead\"===a||\"tableRow\"===a?_:o;if(i===_&&r.parser.lazy[r.now().line])return n(e);return i(e)};function o(t){return e.enter(\"tableHead\"),e.enter(\"tableRow\"),function(e){if(124===e)return l(e);return a=!0,s+=1,l(e)}(t)}function l(t){return null===t?n(t):wt(t)?s>1?(s=0,r.interrupt=!0,e.exit(\"tableRow\"),e.enter(\"lineEnding\"),e.consume(t),e.exit(\"lineEnding\"),d):n(t):Ot(t)?Lt(e,l,\"whitespace\")(t):(s+=1,a&&(a=!1,i+=1),124===t?(e.enter(\"tableCellDivider\"),e.consume(t),e.exit(\"tableCellDivider\"),a=!0,l):(e.enter(\"data\"),c(t)))}function c(t){return null===t||124===t||It(t)?(e.exit(\"data\"),l(t)):(e.consume(t),92===t?u:c)}function u(t){return 92===t||124===t?(e.consume(t),c):c(t)}function d(t){return r.interrupt=!1,r.parser.lazy[r.now().line]?n(t):(e.enter(\"tableDelimiterRow\"),a=!1,Ot(t)?Lt(e,p,\"linePrefix\",r.parser.constructs.disable.null.includes(\"codeIndented\")?void 0:4)(t):p(t))}function p(t){return 45===t||58===t?f(t):124===t?(a=!0,e.enter(\"tableCellDivider\"),e.consume(t),e.exit(\"tableCellDivider\"),h):y(t)}function h(t){return Ot(t)?Lt(e,f,\"whitespace\")(t):f(t)}function f(t){return 58===t?(s+=1,a=!0,e.enter(\"tableDelimiterMarker\"),e.consume(t),e.exit(\"tableDelimiterMarker\"),m):45===t?(s+=1,m(t)):null===t||wt(t)?E(t):y(t)}function m(t){return 45===t?(e.enter(\"tableDelimiterFiller\"),g(t)):y(t)}function g(t){return 45===t?(e.consume(t),g):58===t?(a=!0,e.exit(\"tableDelimiterFiller\"),e.enter(\"tableDelimiterMarker\"),e.consume(t),e.exit(\"tableDelimiterMarker\"),b):(e.exit(\"tableDelimiterFiller\"),b(t))}function b(t){return Ot(t)?Lt(e,E,\"whitespace\")(t):E(t)}function E(n){return 124===n?p(n):(null===n||wt(n))&&a&&i===s?(e.exit(\"tableDelimiterRow\"),e.exit(\"tableHead\"),t(n)):y(n)}function y(e){return n(e)}function _(t){return e.enter(\"tableRow\"),k(t)}function k(n){return 124===n?(e.enter(\"tableCellDivider\"),e.consume(n),e.exit(\"tableCellDivider\"),k):null===n||wt(n)?(e.exit(\"tableRow\"),t(n)):Ot(n)?Lt(e,k,\"whitespace\")(n):(e.enter(\"data\"),T(n))}function T(t){return null===t||124===t||It(t)?(e.exit(\"data\"),k(t)):(e.consume(t),92===t?S:T)}function S(t){return 92===t||124===t?(e.consume(t),T):T(t)}}function wd(e,t){let n,r,a,i=-1,s=!0,o=0,l=[0,0,0,0],c=[0,0,0,0],u=!1,d=0;const p=new Nd;for(;++i<e.length;){const h=e[i],f=h[1];\"enter\"===h[0]?\"tableHead\"===f.type?(u=!1,0!==d&&(Od(p,t,d,n,r),r=void 0,d=0),n={type:\"table\",start:Object.assign({},f.start),end:Object.assign({},f.end)},p.add(i,0,[[\"enter\",n,t]])):\"tableRow\"===f.type||\"tableDelimiterRow\"===f.type?(s=!0,a=void 0,l=[0,0,0,0],c=[0,i+1,0,0],u&&(u=!1,r={type:\"tableBody\",start:Object.assign({},f.start),end:Object.assign({},f.end)},p.add(i,0,[[\"enter\",r,t]])),o=\"tableDelimiterRow\"===f.type?2:r?3:1):!o||\"data\"!==f.type&&\"tableDelimiterMarker\"!==f.type&&\"tableDelimiterFiller\"!==f.type?\"tableCellDivider\"===f.type&&(s?s=!1:(0!==l[1]&&(c[0]=c[1],a=Id(p,t,l,o,void 0,a)),l=c,c=[l[1],i,0,0])):(s=!1,0===c[2]&&(0!==l[1]&&(c[0]=c[1],a=Id(p,t,l,o,void 0,a),l=[0,0,0,0]),c[2]=i)):\"tableHead\"===f.type?(u=!0,d=i):\"tableRow\"===f.type||\"tableDelimiterRow\"===f.type?(d=i,0!==l[1]?(c[0]=c[1],a=Id(p,t,l,o,i,a)):0!==c[1]&&(a=Id(p,t,c,o,i,a)),o=0):!o||\"data\"!==f.type&&\"tableDelimiterMarker\"!==f.type&&\"tableDelimiterFiller\"!==f.type||(c[3]=i)}for(0!==d&&Od(p,t,d,n,r),p.consume(t.events),i=-1;++i<t.events.length;){const e=t.events[i];\"enter\"===e[0]&&\"table\"===e[1].type&&(e[1]._align=Cd(t.events,i))}return e}function Id(e,t,n,r,a,i){const s=1===r?\"tableHeader\":2===r?\"tableDelimiter\":\"tableData\";0!==n[0]&&(i.end=Object.assign({},Rd(t.events,n[0])),e.add(n[0],0,[[\"exit\",i,t]]));const o=Rd(t.events,n[1]);if(i={type:s,start:Object.assign({},o),end:Object.assign({},o)},e.add(n[1],0,[[\"enter\",i,t]]),0!==n[2]){const a=Rd(t.events,n[2]),i=Rd(t.events,n[3]),s={type:\"tableContent\",start:Object.assign({},a),end:Object.assign({},i)};if(e.add(n[2],0,[[\"enter\",s,t]]),2!==r){const r=t.events[n[2]],a=t.events[n[3]];if(r[1].end=Object.assign({},a[1].end),r[1].type=\"chunkText\",r[1].contentType=\"text\",n[3]>n[2]+1){const t=n[2]+1,r=n[3]-n[2]-1;e.add(t,r,[])}}e.add(n[3]+1,0,[[\"exit\",s,t]])}return void 0!==a&&(i.end=Object.assign({},Rd(t.events,a)),e.add(a,0,[[\"exit\",i,t]]),i=void 0),i}function Od(e,t,n,r,a){const i=[],s=Rd(t.events,n);a&&(a.end=Object.assign({},s),i.push([\"exit\",a,t])),r.end=Object.assign({},s),i.push([\"exit\",r,t]),e.add(n+1,0,i)}function Rd(e,t){const n=e[t],r=\"enter\"===n[0]?\"start\":\"end\";return n[1][r]}const Dd={name:\"tasklistCheck\",tokenize:function(e,t,n){const r=this;return function(t){if(null!==r.previous||!r._gfmTasklistFirstContentOfListItem)return n(t);return e.enter(\"taskListCheck\"),e.enter(\"taskListCheckMarker\"),e.consume(t),e.exit(\"taskListCheckMarker\"),a};function a(t){return It(t)?(e.enter(\"taskListCheckValueUnchecked\"),e.consume(t),e.exit(\"taskListCheckValueUnchecked\"),i):88===t||120===t?(e.enter(\"taskListCheckValueChecked\"),e.consume(t),e.exit(\"taskListCheckValueChecked\"),i):n(t)}function i(t){return 93===t?(e.enter(\"taskListCheckMarker\"),e.consume(t),e.exit(\"taskListCheckMarker\"),e.exit(\"taskListCheck\"),s):n(t)}function s(r){return wt(r)?t(r):Ot(r)?e.check({tokenize:Md},t,n)(r):n(r)}}};function Md(e,t,n){return Lt(e,function(e){return null===e?n(e):t(e)},\"whitespace\")}const Pd={};const Ld={pencil:A,\"clipboard-list\":x,info:y,\"check-circle-2\":E,flame:w,check:b,\"help-circle\":g,\"alert-triangle\":m,x:f,zap:v,bug:N,list:I,quote:O},Fd={},Bd=/\\.(png|jpe?g|gif|svg|webp|bmp)$/i,Ud=/\\.(mp4|webm|mov|mkv|avi|ogv)$/i,Hd=/\\.(mp3|wav|ogg|m4a|flac|aac)$/i,zd=/!\\[([^\\]]*)\\]\\(([^)]+)\\)/g,Gd=/<img\\b([^>]*?)\\bsrc\\s*=\\s*(['\"])(.*?)\\2([^>]*)>/gi,jd={note:{border:\"border-blue-400\",bg:\"bg-blue-50 dark:bg-blue-950/30\",text:\"text-blue-400\",icon:\"pencil\"},abstract:{border:\"border-cyan-400\",bg:\"bg-cyan-50 dark:bg-cyan-950/30\",text:\"text-cyan-400\",icon:\"clipboard-list\"},summary:{border:\"border-cyan-400\",bg:\"bg-cyan-50 dark:bg-cyan-950/30\",text:\"text-cyan-400\",icon:\"clipboard-list\"},tldr:{border:\"border-cyan-400\",bg:\"bg-cyan-50 dark:bg-cyan-950/30\",text:\"text-cyan-400\",icon:\"clipboard-list\"},info:{border:\"border-blue-400\",bg:\"bg-blue-50 dark:bg-blue-950/30\",text:\"text-blue-400\",icon:\"info\"},todo:{border:\"border-blue-400\",bg:\"bg-blue-50 dark:bg-blue-950/30\",text:\"text-blue-400\",icon:\"check-circle-2\"},tip:{border:\"border-teal-400\",bg:\"bg-teal-50 dark:bg-teal-950/30\",text:\"text-teal-400\",icon:\"flame\"},hint:{border:\"border-teal-400\",bg:\"bg-teal-50 dark:bg-teal-950/30\",text:\"text-teal-400\",icon:\"flame\"},important:{border:\"border-teal-400\",bg:\"bg-teal-50 dark:bg-teal-950/30\",text:\"text-teal-400\",icon:\"flame\"},success:{border:\"border-green-400\",bg:\"bg-green-50 dark:bg-green-950/30\",text:\"text-green-400\",icon:\"check\"},check:{border:\"border-green-400\",bg:\"bg-green-50 dark:bg-green-950/30\",text:\"text-green-400\",icon:\"check\"},done:{border:\"border-green-400\",bg:\"bg-green-50 dark:bg-green-950/30\",text:\"text-green-400\",icon:\"check\"},question:{border:\"border-yellow-400\",bg:\"bg-yellow-50 dark:bg-yellow-950/30\",text:\"text-yellow-400\",icon:\"help-circle\"},help:{border:\"border-yellow-400\",bg:\"bg-yellow-50 dark:bg-yellow-950/30\",text:\"text-yellow-400\",icon:\"help-circle\"},faq:{border:\"border-yellow-400\",bg:\"bg-yellow-50 dark:bg-yellow-950/30\",text:\"text-yellow-400\",icon:\"help-circle\"},warning:{border:\"border-orange-400\",bg:\"bg-orange-50 dark:bg-orange-950/30\",text:\"text-orange-400\",icon:\"alert-triangle\"},caution:{border:\"border-orange-400\",bg:\"bg-orange-50 dark:bg-orange-950/30\",text:\"text-orange-400\",icon:\"alert-triangle\"},attention:{border:\"border-orange-400\",bg:\"bg-orange-50 dark:bg-orange-950/30\",text:\"text-orange-400\",icon:\"alert-triangle\"},failure:{border:\"border-red-400\",bg:\"bg-red-50 dark:bg-red-950/30\",text:\"text-red-400\",icon:\"x\"},fail:{border:\"border-red-400\",bg:\"bg-red-50 dark:bg-red-950/30\",text:\"text-red-400\",icon:\"x\"},missing:{border:\"border-red-400\",bg:\"bg-red-50 dark:bg-red-950/30\",text:\"text-red-400\",icon:\"x\"},danger:{border:\"border-red-400\",bg:\"bg-red-50 dark:bg-red-950/30\",text:\"text-red-400\",icon:\"zap\"},error:{border:\"border-red-400\",bg:\"bg-red-50 dark:bg-red-950/30\",text:\"text-red-400\",icon:\"zap\"},bug:{border:\"border-red-400\",bg:\"bg-red-50 dark:bg-red-950/30\",text:\"text-red-400\",icon:\"bug\"},example:{border:\"border-purple-400\",bg:\"bg-purple-50 dark:bg-purple-950/30\",text:\"text-purple-400\",icon:\"list\"},quote:{border:\"border-gray-400\",bg:\"bg-gray-50 dark:bg-gray-950/30\",text:\"text-gray-400\",icon:\"quote\"},cite:{border:\"border-gray-400\",bg:\"bg-gray-50 dark:bg-gray-950/30\",text:\"text-gray-400\",icon:\"quote\"}},$d={lineNumbers:!1,foldGutter:!1,highlightActiveLineGutter:!1},qd=[[function(e){const t=e||Pd,n=this.data(),r=n.micromarkExtensions||(n.micromarkExtensions=[]),a=n.fromMarkdownExtensions||(n.fromMarkdownExtensions=[]),i=n.toMarkdownExtensions||(n.toMarkdownExtensions=[]);r.push(function(e){return bt([{text:dd},{document:{91:{name:\"gfmFootnoteDefinition\",tokenize:Td,continuation:{tokenize:Sd},exit:vd}},text:{91:{name:\"gfmFootnoteCall\",tokenize:kd},93:{name:\"gfmPotentialFootnoteCall\",add:\"after\",tokenize:yd,resolveTo:_d}}},Ad(e),{flow:{null:{name:\"table\",tokenize:xd,resolveAll:wd}}},{text:{91:Dd}}])}(t)),a.push([{transforms:[nu],enter:{literalAutolink:Qc,literalAutolinkEmail:Xc,literalAutolinkHttp:Xc,literalAutolinkWww:Xc},exit:{literalAutolink:tu,literalAutolinkEmail:eu,literalAutolinkHttp:Zc,literalAutolinkWww:Jc}},{enter:{gfmFootnoteCallString:su,gfmFootnoteCall:ou,gfmFootnoteDefinitionLabelString:lu,gfmFootnoteDefinition:cu},exit:{gfmFootnoteCallString:uu,gfmFootnoteCall:du,gfmFootnoteDefinitionLabelString:pu,gfmFootnoteDefinition:hu}},{canContainEols:[\"delete\"],enter:{strikethrough:yu},exit:{strikethrough:_u}},{enter:{table:Yu,tableData:Qu,tableHeader:Qu,tableRow:Vu},exit:{codeText:Xu,table:Wu,tableData:Ku,tableHeader:Ku,tableRow:Ku}},{exit:{taskListCheckValueChecked:ed,taskListCheckValueUnchecked:ed,paragraph:td}}]),i.push(function(e){return{extensions:[{unsafe:[{character:\"@\",before:\"[+\\\\-.\\\\w]\",after:\"[\\\\-.\\\\w]\",inConstruct:Vc,notInConstruct:Kc},{character:\".\",before:\"[Ww]\",after:\"[\\\\-.\\\\w]\",inConstruct:Vc,notInConstruct:Kc},{character:\":\",before:\"[ps]\",after:\"\\\\/\",inConstruct:Vc,notInConstruct:Kc}]},mu(e),{unsafe:[{character:\"~\",inConstruct:\"phrasing\",notInConstruct:Eu}],handlers:{delete:ku}},Ju(e),{unsafe:[{atBreak:!0,character:\"-\",after:\"[:|-]\"}],handlers:{listItem:nd}}]}}(t))},{singleTilde:!1}]],Yd={...Lc,tagNames:[...Lc.tagNames??[],\"mark\"],attributes:{...Lc.attributes??{},span:[...(null==(e=Lc.attributes)?void 0:e.span)??[],\"className\",\"title\"],img:[...(null==(t=Lc.attributes)?void 0:t.img)??[],\"width\"]}};function Wd(e,t,n){const r=new URLSearchParams({vault:e,path:t,token:n,lang:p()});return`${h.API_URL}/api/file?${r.toString()}`}function Vd(e){return Bd.test(e)?\"image\":Ud.test(e)?\"video\":Hd.test(e)?\"audio\":\"file\"}function Kd(e,t,n,r){const a=t[e.trim()];return a?function(e,t,n){return Wd(e,t,n)}(n,a,r):null}const Qd=[function(e){return function(t,n){return Tc(t,{...e,file:n})}},[function(e){return function(t){const n=function(e,t){let n={type:\"root\",children:[]};const r=Bc({schema:t?{...Lc,...t}:Lc,stack:[]},e);return r&&(Array.isArray(r)?1===r.length?n=r[0]:n.children=r:n=r),n}(t,e);return n}},Yd],function(e){const t=e||ss,n=t.aliases,r=t.detect||!1,a=t.languages||Ji,i=t.plainText,s=t.prefix,o=t.subset;let l=\"hljs\";const c=function(e){const t=rs.newInstance();return e&&a(e),{highlight:n,highlightAuto:function(e,a){const i=(a||as).subset||r();let s,o=-1,l=0;for(;++o<i.length;){const r=i[o];if(!t.getLanguage(r))continue;const c=n(r,e,a);c.data&&void 0!==c.data.relevance&&c.data.relevance>l&&(l=c.data.relevance,s=c)}return s||{type:\"root\",children:[],data:{language:void 0,relevance:l}}},listLanguages:r,register:a,registerAlias:function(e,n){if(\"string\"==typeof e)t.registerAliases(\"string\"==typeof n?n:[...n],{languageName:e});else{let n;for(n in e)if(Object.hasOwn(e,n)){const r=e[n];t.registerAliases(\"string\"==typeof r?r:[...r],{languageName:n})}}},registered:function(e){return Boolean(t.getLanguage(e))}};function n(e,n,r){const a=r||as,i=\"string\"==typeof a.prefix?a.prefix:\"hljs-\";if(!t.getLanguage(e))throw new Error(\"Unknown language: `\"+e+\"` is not registered\");t.configure({__emitter:is,classPrefix:i});const s=t.highlight(n,{ignoreIllegals:!0,language:e});if(s.errorRaised)throw new Error(\"Could not highlight with `Highlight.js`\",{cause:s.errorRaised});const o=s._emitter.root,l=o.data;return l.language=s.language,l.relevance=s.relevance,o}function r(){return t.listLanguages()}function a(e,n){if(\"string\"==typeof e)t.registerLanguage(e,n);else{let n;for(n in e)Object.hasOwn(e,n)&&t.registerLanguage(n,e[n])}}}(a);if(n&&c.registerAlias(n),s){const e=s.indexOf(\"-\");l=-1===e?s:s.slice(0,e)}return function(e,t){wr(e,\"element\",function(e,n,a){if(\"code\"!==e.tagName||!a||\"element\"!==a.type||\"pre\"!==a.tagName)return;const u=function(e){const t=e.properties.className;let n,r=-1;if(!Array.isArray(t))return;for(;++r<t.length;){const e=String(t[r]);if(\"no-highlight\"===e||\"nohighlight\"===e)return!1;n||\"lang-\"!==e.slice(0,5)||(n=e.slice(5)),n||\"language-\"!==e.slice(0,9)||(n=e.slice(9))}return n}(e);if(!1===u||!u&&!r||u&&i&&i.includes(u))return;Array.isArray(e.properties.className)||(e.properties.className=[]),e.properties.className.includes(l)||e.properties.className.unshift(l);const d=function(e,t){const n=t||{},r=\"children\"in e?e.children:[],a=Ua(e),i=ja(e,{whitespace:n.whitespace||\"normal\"}),s=[];\"text\"!==e.type&&\"comment\"!==e.type||s.push(...za(e,{breakBefore:!0,breakAfter:!0}));let o=-1;for(;++o<r.length;)s.push(...Ha(r[o],e,{whitespace:i,breakBefore:o?void 0:a,breakAfter:o<r.length-1?Ma(r[o+1]):a}));const l=[];let c;for(o=-1;++o<s.length;){const e=s[o];\"number\"==typeof e?void 0!==c&&e>c&&(c=e):e&&(void 0!==c&&c>-1&&l.push(\"\\n\".repeat(c)||\" \"),c=-1,l.push(e))}return l.join(\"\")}(e,{whitespace:\"pre\"});let p;try{p=u?c.highlight(u,d,{prefix:s}):c.highlightAuto(d,{prefix:s,subset:o})}catch(h){const n=h;if(u&&/Unknown language/.test(n.message))return void t.message(\"Cannot highlight as `\"+u+\"`, it’s not registered\",{ancestors:[a,e],cause:n,place:e.position,ruleId:\"missing-language\",source:\"rehype-highlight\"});throw n}!u&&p.data&&p.data.language&&e.properties.className.push(\"language-\"+p.data.language),p.children.length>0&&(e.children=p.children)})}}];function Xd(e,t,n,r,a,i,s){if(!e)return e;let o=e;const l=[],c=\"\\0CODE_BLOCK_\";return o=o.replace(/^(`{3,}|~{3,})[^\\n]*\\n[\\s\\S]*?\\n\\1\\s*$/gm,e=>{const t=l.length;return l.push(e),`${c}${t}\\0`}),o=o.replace(/(`+)(?!\\0)(.+?)\\1/g,e=>{const t=l.length;return l.push(e),`${c}${t}\\0`}),o=o.replace(/%%[\\s\\S]*?%%/g,\"\"),o=o.replace(/!\\[\\[([^\\]]+)\\]\\]/g,(e,o)=>{var l;const[c=\"\",...u]=o.split(\"|\"),d=c.split(\"#\")[0].trim();if(!d)return e;const f=n[d]||d,m=a&&i?function(e,t,n,r){const a=new URLSearchParams({id:e,token:t,path:n,lang:p()});return r&&a.append(\"password\",r),`${h.API_URL}/api/share/file?${a.toString()}`}(a,i,f,s):Wd(t,f,r),g=(u[0]||d).trim().replace(/\\\\/g,\"\\\\\\\\\").replace(/\\[/g,\"\\\\[\").replace(/\\]/g,\"\\\\]\").replace(/\\(/g,\"\\\\(\").replace(/\\)/g,\"\\\\)\");const b=Vd(f.toLowerCase());if(\"image\"===b){const e=null==(l=u[0])?void 0:l.match(/^(\\d+)$/);return e?`<img src=\"${m}\" alt=\"${d}\" width=\"${e[1]}\" />`:`![${g}](${m})`}return\"video\"===b?`[🎬 ${g}](${m})`:\"audio\"===b?`[🎵 ${g}](${m})`:`[📎 ${g}](${m})`}),o=function(e,t,n,r){return e.replace(zd,(e,a,i)=>{const s=function(e){let t=0;for(;t<e.length&&/\\s/.test(e[t]);)t+=1;if(t>=e.length)return null;if(\"<\"===e[t]){const n=e.indexOf(\">\",t+1);return-1===n?null:{target:e.slice(t+1,n),start:t,end:n+1}}let n=e.length,r=!1;for(let a=t;a<e.length;a+=1)if(r)r=!1;else if(\"\\\\\"!==e[a]){if(/\\s/.test(e[a])){n=a;break}}else r=!0;return{target:e.slice(t,n),start:t,end:n}}(i);if(!s)return e;const o=Kd(s.target,t,n,r);if(!o)return e;let l=o;const c=i.slice(s.start,s.end);return c.startsWith(\"<\")&&c.endsWith(\">\")&&(l=`<${l}>`),`![${a}](${i.slice(0,s.start)}${l}${i.slice(s.end)})`})}(o,n,t,r),o=function(e,t,n,r){return e.replace(Gd,(e,a,i,s,o)=>{const l=Kd(s,t,n,r);return l?`<img${a}src=${i}${l}${i}${o}>`:e})}(o,n,t,r),o=o.replace(/\\[\\[([^\\]]+)\\]\\]/g,(e,t)=>{const n=t.split(\"|\"),r=(n[1]||n[0]).trim();return`<span class=\"obsidian-wiki-link\" title=\"${n[0].trim()}\">${r}</span>`}),o=o.replace(/==(.*?)==/g,\"<mark>$1</mark>\"),o=o.replace(/(^|[\\s])#([a-zA-Z\\u4e00-\\u9fff][\\w/\\u4e00-\\u9fff-]*)/gm,(e,t,n)=>`${t}<span class=\"obsidian-tag\">#${n}</span>`),o=o.replace(new RegExp(`${c.replace(/\\0/g,\"\\\\0\")}(\\\\d+)\\\\0`,\"g\"),(e,t)=>l[parseInt(t,10)]),o}const Zd=/^\\[!(\\w+)\\]([+-])?\\s*(.*)?$/;const Jd={h1:({node:e,className:t,...n})=>a.jsx(\"h1\",{className:d(\"mt-8 mb-4 text-3xl font-bold first:mt-0\",t),...n}),h2:({node:e,className:t,...n})=>a.jsx(\"h2\",{className:d(\"mt-7 mb-3 text-2xl font-semibold first:mt-0\",t),...n}),h3:({node:e,className:t,...n})=>a.jsx(\"h3\",{className:d(\"mt-6 mb-3 text-xl font-semibold first:mt-0\",t),...n}),p:({node:e,className:t,...n})=>a.jsx(\"p\",{className:d(\"my-3 leading-7 text-foreground/90\",t),...n}),ul:({node:e,className:t,...n})=>a.jsx(\"ul\",{className:d(\"my-3 list-disc pl-6\",t),...n}),ol:({node:e,className:t,...n})=>a.jsx(\"ol\",{className:d(\"my-3 list-decimal pl-6\",t),...n}),li:({node:e,className:t,...n})=>a.jsx(\"li\",{className:d(\"my-1.5\",t),...n}),blockquote:({node:e,className:t,children:n,ref:r,...i})=>{const s=function(e){var t;const n=Array.isArray(e)?e:[e];if(0===n.length)return null;let r=-1;for(let a=0;a<n.length;a++){const e=n[a];if(e&&\"object\"==typeof e&&\"props\"in e){r=a;break}}if(-1===r)return null;const i=null==(t=n[r].props)?void 0:t.children;if(!i)return null;let s=\"\",o=[];if(\"string\"==typeof i){const e=i.split(\"\\n\");if(s=e[0].trimStart(),e.length>1){const t=e.slice(1).join(\"\\n\").trimStart();t&&(o=[t])}}else if(Array.isArray(i)){let e=-1;for(let t=0;t<i.length;t++){const n=i[t];if(\"string\"==typeof n){const r=n.trimStart();if(r){const n=r.split(\"\\n\");if(s=n[0],e=t,n.length>1){const e=n.slice(1).join(\"\\n\").trimStart();e&&o.push(e)}break}}}e>=0&&e+1<i.length&&o.push(...i.slice(e+1))}const l=s.match(Zd);if(!l)return null;const[,c,u,d]=l,p=c.toLowerCase(),h=[];return o.length>0&&h.push(a.jsx(\"p\",{children:o},\"callout-body\")),h.push(...n.slice(r+1)),{type:p,title:(null==d?void 0:d.trim())||c.charAt(0).toUpperCase()+c.slice(1),foldable:\"+\"===u||\"-\"===u,defaultOpen:\"-\"!==u,restChildren:h}}(n);if(s){const e=jd[s.type]||jd.note,n=Ld[e.icon]||y,r=e.text,i=a.jsxs(\"div\",{className:d(\"flex items-center gap-1.5 font-semibold mb-1 capitalize\",r),children:[a.jsx(n,{className:\"size-4 shrink-0\"}),s.title]});return s.foldable?a.jsxs(\"details\",{open:s.defaultOpen,className:d(\"my-4 border-l-4 rounded-r-lg px-4 py-3\",e.border,e.bg,t),children:[a.jsxs(\"summary\",{className:d(\"flex items-center gap-1.5 font-semibold cursor-pointer capitalize\",r),children:[a.jsx(n,{className:\"size-4 shrink-0\"}),s.title]}),a.jsx(\"div\",{className:\"mt-2\",children:s.restChildren})]}):a.jsxs(\"div\",{className:d(\"my-4 border-l-4 rounded-r-lg px-4 py-3\",e.border,e.bg,t),children:[i,s.restChildren]})}return a.jsx(\"blockquote\",{className:d(\"my-4 border-l-4 border-border pl-4 text-muted-foreground\",t),...i,children:n})},hr:({node:e,className:t,...n})=>a.jsx(\"hr\",{className:d(\"my-6 border-border\",t),...n}),table:({node:e,className:t,...n})=>a.jsx(\"div\",{className:\"my-4 overflow-x-auto\",children:a.jsx(\"table\",{className:d(\"w-full border-collapse text-sm\",t),...n})}),thead:({node:e,className:t,...n})=>a.jsx(\"thead\",{className:d(\"bg-muted/50\",t),...n}),th:({node:e,className:t,...n})=>a.jsx(\"th\",{className:d(\"border border-border px-3 py-2 text-left font-semibold\",t),...n}),td:({node:e,className:t,...n})=>a.jsx(\"td\",{className:d(\"border border-border px-3 py-2 align-top\",t),...n}),img:({node:e,className:t,alt:n,...r})=>a.jsx(\"img\",{className:d(\"my-4 max-w-full rounded-lg border border-border/60\",t),alt:n??\"\",loading:\"lazy\",...r}),a:({node:e,href:t,children:n,className:r,...i})=>{const s=Vd(function(e){if(!e)return\"\";try{const t=\"undefined\"==typeof window?\"http://localhost\":window.location.origin;return(new URL(e,t).searchParams.get(\"path\")??e).toLowerCase()}catch{return e.toLowerCase()}}(t));return t&&\"video\"===s?a.jsx(\"video\",{src:t,controls:!0,className:\"my-4 w-full rounded-lg border border-border/60\"}):t&&\"audio\"===s?a.jsx(\"audio\",{src:t,controls:!0,className:\"my-4 w-full\"}):a.jsx(\"a\",{href:t,target:\"_blank\",rel:\"noreferrer\",className:d(\"text-primary underline underline-offset-4 hover:opacity-80\",r),...i,children:n})},pre:({node:e,className:t,...n})=>a.jsx(\"pre\",{className:d(\"my-4 overflow-x-auto rounded-lg border border-border/60 bg-muted/40 p-4\",t),...n}),code:({node:e,className:t,children:n,...r})=>{const i=String(n);return!t&&!i.includes(\"\\n\")?a.jsx(\"code\",{className:\"rounded bg-muted px-1.5 py-0.5 text-[0.9em]\",...r,children:n}):a.jsx(\"code\",{className:d(\"text-sm\",t),...r,children:n})},input:({node:e,className:t,...n})=>\"checkbox\"===n.type?a.jsx(\"input\",{...n,disabled:!0,className:d(\"mr-2 accent-primary\",t)}):a.jsx(\"input\",{...n,className:t})},ep=o.memo(function({content:e}){return a.jsx(ma,{remarkPlugins:qd,rehypePlugins:Qd,components:Jd,allowedElements:void 0,unwrapDisallowed:!0,children:e})}),tp=o.forwardRef(({value:e,onChange:t,readOnly:n=!1,placeholder:r=\"\",vault:i=\"\",fileLinks:s=Fd,initialMode:p=\"edit\",ariaLabel:h,onWikiLinkClick:f,fullWidth:m=!1,autoHeight:g=!1,shareId:b,shareToken:E,password:y},v)=>{const{resolvedTheme:A}=l(),{t:N}=c(),C=o.useRef(null),x=o.useRef(e),w=h??N(\"ui.note.editNote\"),I=o.useRef(\"undefined\"!=typeof window?localStorage.getItem(\"token\")??\"\":\"\"),O=p,R=o.useCallback(e=>{const t=e.target.closest(\".obsidian-wiki-link\");if(!t)return;e.preventDefault();const n=t.getAttribute(\"title\");n&&f&&f(n)},[f]),D=\"dark\"===A?\"[&_.hljs-comment]:text-zinc-500 [&_.hljs-quote]:text-zinc-500 [&_.hljs-keyword]:text-sky-300 [&_.hljs-selector-tag]:text-sky-300 [&_.hljs-literal]:text-sky-300 [&_.hljs-title]:text-emerald-300 [&_.hljs-section]:text-emerald-300 [&_.hljs-name]:text-emerald-300 [&_.hljs-string]:text-amber-300 [&_.hljs-attr]:text-amber-300 [&_.hljs-template-tag]:text-amber-300 [&_.hljs-number]:text-fuchsia-300 [&_.hljs-built_in]:text-violet-300 [&_.hljs-type]:text-violet-300\":\"[&_.hljs-comment]:text-zinc-500 [&_.hljs-quote]:text-zinc-500 [&_.hljs-keyword]:text-blue-600 [&_.hljs-selector-tag]:text-blue-600 [&_.hljs-literal]:text-blue-600 [&_.hljs-title]:text-green-700 [&_.hljs-section]:text-green-700 [&_.hljs-name]:text-green-700 [&_.hljs-string]:text-amber-700 [&_.hljs-attr]:text-amber-700 [&_.hljs-template-tag]:text-amber-700 [&_.hljs-number]:text-purple-700 [&_.hljs-built_in]:text-purple-700 [&_.hljs-type]:text-purple-700\";o.useEffect(()=>{x.current=e},[e]),o.useEffect(()=>{I.current=\"undefined\"!=typeof window?localStorage.getItem(\"token\")??\"\":\"\"},[i]),o.useEffect(()=>()=>{C.current=null},[]);const M=o.useMemo(()=>{const e=[_(),k.lineWrapping,k.editable.of(!n),k.contentAttributes.of({\"aria-label\":w})];return r&&e.push(T(r)),e},[w,r,n]),P=o.useCallback(e=>{x.current=e,null==t||t(e)},[t]),L=o.useCallback(()=>{const e=C.current;return e?e.state.doc.toString():x.current},[]),F=o.useCallback(e=>{x.current=e;const t=C.current;t&&t.state.doc.toString()!==e&&t.dispatch({changes:{from:0,to:t.state.doc.length,insert:e}})},[]),[B,U]=o.useState(()=>\"preview\"===O?Xd(e,i,s,I.current,b,E,y):\"\");o.useEffect(()=>{if(\"preview\"!==O)return;const t=setTimeout(()=>{U(Xd(e,i,s,I.current,b,E,y))},150);return()=>clearTimeout(t)},[O,e,i,s,b,E,y]);const H=o.useCallback(()=>{try{const e=Xd(L(),i,s,I.current,b,E,y),t=`<!doctype html>\\n<html lang=\"en\">\\n<head>\\n<meta charset=\"utf-8\" />\\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\\n<title>Export</title>\\n<style>\\nbody {\\n  margin: 0;\\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\\n  color: #222;\\n  line-height: 1.75;\\n}\\nmain {\\n  max-width: 900px;\\n  margin: 0 auto;\\n  padding: 40px 20px;\\n}\\nimg, video {\\n  max-width: 100%;\\n  border-radius: 10px;\\n}\\npre {\\n  overflow-x: auto;\\n  background: #f6f8fa;\\n  border-radius: 10px;\\n  padding: 14px;\\n}\\ncode {\\n  font-family: \"SFMono-Regular\", Menlo, Monaco, Consolas, monospace;\\n}\\ntable {\\n  width: 100%;\\n  border-collapse: collapse;\\n}\\nth, td {\\n  border: 1px solid #e5e7eb;\\n  padding: 6px 10px;\\n  text-align: left;\\n}\\nblockquote {\\n  margin: 0;\\n  padding-left: 12px;\\n  border-left: 4px solid #e5e7eb;\\n  color: #6b7280;\\n}\\nmark {\\n  background: #fef08a;\\n  padding: 0 2px;\\n  border-radius: 2px;\\n}\\n.callout {\\n  border-left: 4px solid #60a5fa;\\n  background: #eff6ff;\\n  padding: 12px 16px;\\n  margin: 16px 0;\\n  border-radius: 0 8px 8px 0;\\n}\\n.callout-title {\\n  font-weight: 600;\\n  margin-bottom: 4px;\\n  text-transform: capitalize;\\n}\\n.hljs-comment, .hljs-quote { color: #6b7280; }\\n.hljs-keyword, .hljs-selector-tag, .hljs-literal { color: #2563eb; }\\n.hljs-title, .hljs-section, .hljs-name { color: #16a34a; }\\n.hljs-string, .hljs-attr, .hljs-template-tag { color: #b45309; }\\n.hljs-number, .hljs-built_in, .hljs-type { color: #9333ea; }\\n</style>\\n</head>\\n<body>${Ca.renderToStaticMarkup(a.jsx(\"main\",{children:a.jsx(ep,{content:e})}))}</body>\\n</html>`,n=new Blob([t],{type:\"text/html;charset=utf-8\"}),r=URL.createObjectURL(n),o=document.createElement(\"a\");o.href=r,o.download=`note-${(new Date).toISOString().replace(/[:.]/g,\"-\")}.html`,o.click(),URL.revokeObjectURL(r),u.success(N(\"ui.note.exportHtmlSuccess\",{defaultValue:\"HTML exported successfully\"}))}catch{u.error(N(\"ui.note.exportHtmlFailed\",{defaultValue:\"Failed to export HTML\"}))}},[s,L,N,i,b,E,y]),z=o.useCallback(()=>{u.info(N(\"ui.note.exportPdfPlanned\"))},[N]);return o.useImperativeHandle(v,()=>({getValue:L,setValue:F,exportPDF:z,exportHTML:H}),[L,H,z,F]),\"preview\"===O?a.jsx(\"div\",{className:d(\"markdown-preview\",!g&&\"h-full overflow-y-auto\",D),onClick:R,children:a.jsx(\"article\",{className:d(\"mx-auto px-5 py-10 transition-all duration-300 break-words\",m?\"max-w-none\":\"max-w-225\"),children:a.jsx(ep,{content:B})})}):a.jsx(\"div\",{className:\"h-full [&_.cm-editor]:h-full [&_.cm-gutters]:h-full [&_.cm-scroller]:overflow-auto\",children:a.jsx(S,{value:e,height:\"100%\",theme:\"dark\"===A?\"dark\":\"light\",extensions:M,basicSetup:$d,onChange:P,onCreateEditor:e=>{C.current=e},className:\"h-full text-sm\"})})});tp.displayName=\"MarkdownEditor\";const np=Object.freeze(Object.defineProperty({__proto__:null,MarkdownEditor:tp,MarkdownRenderer:ep,transformObsidianSyntax:Xd},Symbol.toStringTag,{value:\"Module\"}));export{C,tp as M,ep as a,np as m};\n"
  },
  {
    "path": "frontend/assets/markdown-editor-DMUawZD_.css",
    "content": ".cm-editor{background-color:transparent!important;font-family:inherit!important}.cm-editor .cm-content{font-family:inherit!important;padding:16px!important}.cm-editor .cm-scroller{scrollbar-width:thin;scrollbar-color:transparent transparent;transition:scrollbar-color .3s ease}.cm-editor .cm-scroller:hover{scrollbar-color:rgba(128,128,128,.3) transparent}.cm-editor .cm-scroller::-webkit-scrollbar{width:6px;height:6px}.cm-editor .cm-scroller::-webkit-scrollbar-track{background:transparent}.cm-editor .cm-scroller::-webkit-scrollbar-thumb{background-color:transparent;border-radius:3px;transition:background-color .3s ease}.cm-editor .cm-scroller:hover::-webkit-scrollbar-thumb{background-color:#8080804d}.cm-editor .cm-scroller::-webkit-scrollbar-thumb:hover{background-color:#80808080}.cm-editor.cm-focused{outline:none!important}.markdown-preview{scrollbar-width:thin;scrollbar-color:transparent transparent;transition:scrollbar-color .3s ease}.markdown-preview:hover{scrollbar-color:rgba(128,128,128,.3) transparent}.markdown-preview::-webkit-scrollbar{width:6px;height:6px}.markdown-preview::-webkit-scrollbar-track{background:transparent}.markdown-preview::-webkit-scrollbar-thumb{background-color:transparent;border-radius:3px;transition:background-color .3s ease}.markdown-preview:hover::-webkit-scrollbar-thumb{background-color:#8080804d}.markdown-preview::-webkit-scrollbar-thumb:hover{background-color:#80808080}.obsidian-wiki-link{color:hsl(var(--primary));background:hsl(var(--primary) / .08);padding:0 4px;border-radius:4px;font-weight:500;cursor:pointer;transition:background .15s ease}.obsidian-wiki-link:hover{background:hsl(var(--primary) / .15)}.obsidian-tag{color:hsl(var(--primary));background:hsl(var(--primary) / .08);padding:1px 6px;border-radius:4px;font-size:.9em;white-space:nowrap}mark{background:#fef08a;padding:0 2px;border-radius:2px}.dark mark{background:#854d0e;color:#fef9c3}\n"
  },
  {
    "path": "frontend/assets/monitor-BGNS5Y9j.js",
    "content": "import{c as e}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const t=e(\"Activity\",[[\"path\",{d:\"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2\",key:\"169zse\"}]]),y=e(\"Monitor\",[[\"rect\",{width:\"20\",height:\"14\",x:\"2\",y:\"3\",rx:\"2\",key:\"48i651\"}],[\"line\",{x1:\"8\",x2:\"16\",y1:\"21\",y2:\"21\",key:\"1svkeh\"}],[\"line\",{x1:\"12\",x2:\"12\",y1:\"17\",y2:\"21\",key:\"vw1qmm\"}]]);\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */export{t as A,y as M};\n"
  },
  {
    "path": "frontend/assets/note-handle-IK8dQjtF.js",
    "content": "import{r as e,b as o,e as t,a,t as s}from\"./font-loader-CIrh3KnA.js\";function r(){const r=localStorage.getItem(\"token\"),n=e.useCallback(()=>o({token:r}),[r]),c=e.useCallback(()=>{localStorage.removeItem(\"token\"),window.location.reload()},[]),i=e.useCallback(async(e,o,r,i=\"\",l=!1,d=\"path\",h=!1,g=\"mtime\",m=\"desc\",w)=>{try{const p=Math.floor(o).toString(),f=Math.floor(r).toString();let u=`${t.API_URL}/api/notes?vault=${encodeURIComponent(e)}&page=${p}&pageSize=${f}`;i&&(u+=`&keyword=${encodeURIComponent(i)}`),l&&(u+=\"&isRecycle=1\"),d&&\"path\"!==d&&(u+=`&searchMode=${d}`),h&&(u+=\"&searchContent=1\"),g&&\"mtime\"!==g&&(u+=`&sortBy=${g}`),m&&\"desc\"!==m&&(u+=`&sortOrder=${m}`);const k=await fetch(a(u),{method:\"GET\",headers:n()});if(!k.ok)return 508===k.status?c():s.error(\"Network response was not ok\"),void w(null);const y=await k.json();if(y.code>0&&y.code<=200){const e=y.data||{list:[],pager:{page:1,pageSize:r,totalRows:0}};e.list||(e.list=[]),w(e)}else 508===y.code?c():s.error(y.message),w(null)}catch(p){s.error(p instanceof Error?p.message:String(p)),w(null)}},[n,c]),l=e.useCallback(async(e,o,r,c=!1,i,l)=>{try{let l=`${t.API_URL}/api/note?vault=${encodeURIComponent(e)}&path=${encodeURIComponent(o)}`;r&&(l+=`&pathHash=${r}`),c&&(l+=\"&isRecycle=1\");const d=await fetch(a(l),{method:\"GET\",headers:n()});if(!d.ok)throw new Error(\"Network response was not ok\");const h=await d.json();h.code>0&&h.code<=200&&h.data?i(h.data):h.code>0&&h.code<=200||s.error(h.message)}catch(d){s.error(d instanceof Error?d.message:String(d))}finally{null==l||l()}},[n]),d=e.useCallback(async(e,o,r,c={},i,l,d=!1)=>{try{const{srcPath:h,srcPathHash:g,...m}=c||{},w={vault:e,path:o,content:r,...m},p=await fetch(a(`${t.API_URL}/api/note`),{method:\"POST\",body:JSON.stringify(w),headers:n()});if(!p.ok)throw new Error(\"Network response was not ok\");const f=await p.json();if(f.code>0&&f.code<=200)d||s.success(f.message),i();else{const e=f.message+(f.details?\": \"+f.details.join(\", \"):\"\");s.error(e),l&&l(e)}}catch(h){const e=h instanceof Error?h.message:String(h);s.error(e),l&&l(e)}},[n]),h=e.useCallback(async(e,o,r,c)=>{try{const i={vault:e,path:o,pathHash:r},l=await fetch(a(`${t.API_URL}/api/note`),{method:\"DELETE\",body:JSON.stringify(i),headers:n()});if(!l.ok)throw new Error(\"Network response was not ok\");const d=await l.json();d.code>0&&d.code<=200?(s.success(d.message),c()):s.error(d.message+(d.details?\": \"+d.details.join(\", \"):\"\"))}catch(i){s.error(i instanceof Error?i.message:String(i))}},[n]),g=e.useCallback(async(e,o,r,c)=>{try{const i={vault:e,path:o,pathHash:r},l=await fetch(a(`${t.API_URL}/api/note/recycle-clear`),{method:\"DELETE\",body:JSON.stringify(i),headers:n()});if(!l.ok)throw new Error(\"Network response was not ok\");const d=await l.json();d.code>0&&d.code<=200?(s.success(d.message),c()):s.error(d.message+(d.details?\": \"+d.details.join(\", \"):\"\"))}catch(i){s.error(i instanceof Error?i.message:String(i))}},[n]),m=e.useCallback(async(e,o)=>{try{const r={vault:e},c=await fetch(a(`${t.API_URL}/api/note/recycle-clear`),{method:\"DELETE\",body:JSON.stringify(r),headers:n()});if(!c.ok)throw new Error(\"Network response was not ok\");const i=await c.json();i.code>0&&i.code<=200?(s.success(i.message),o()):s.error(i.message)}catch(r){s.error(r instanceof Error?r.message:String(r))}},[n]),w=e.useCallback(async(e,o,r,c)=>{try{const i={vault:e,path:o,pathHash:r},l=await fetch(a(`${t.API_URL}/api/note/restore`),{method:\"PUT\",body:JSON.stringify(i),headers:n()});if(!l.ok)throw new Error(\"Network response was not ok\");const d=await l.json();d.code>0&&d.code<=200?(s.success(d.message),c()):s.error(d.message+(d.details?\": \"+d.details.join(\", \"):\"\"))}catch(i){s.error(i instanceof Error?i.message:String(i))}},[n]),p=e.useCallback(async(e,o)=>{try{const r=await fetch(a(`${t.API_URL}/api/note/rename`),{method:\"POST\",body:JSON.stringify(e),headers:n()});if(!r.ok)throw new Error(\"Network response was not ok\");const c=await r.json();c.code>0&&c.code<=200?(s.success(c.message),o()):s.error(c.message+(c.details?\": \"+c.details.join(\", \"):\"\"))}catch(r){s.error(r instanceof Error?r.message:String(r))}},[n]),f=e.useCallback(async(e,o,r,i,l,d=!1,h)=>{try{const g=Math.floor(i).toString(),m=Math.floor(l).toString();let w=`${t.API_URL}/api/note/histories?vault=${encodeURIComponent(e)}&path=${encodeURIComponent(o)}&page=${g}&pageSize=${m}`;r&&(w+=`&pathHash=${r}`),d&&(w+=\"&isRecycle=1\");const p=await fetch(a(w),{method:\"GET\",headers:n()});if(!p.ok)return 508===p.status?c():s.error(\"Network response was not ok\"),void h(null);const f=await p.json();if(f.code>0&&f.code<=200){const e=f.data||{list:[],pager:{page:1,pageSize:l,totalRows:0}};e.list||(e.list=[]),h(e)}else 508===f.code?c():s.error(f.message),h(null)}catch(g){s.error(g instanceof Error?g.message:String(g)),h(null)}},[n,c]),u=e.useCallback(async(e,r,n,c,i,l)=>{try{let l=`${t.API_URL}/api/share/note?id=${e}`;n&&(l+=`&password=${encodeURIComponent(n)}`);const d=await fetch(a(l),{method:\"GET\",headers:{...o({token:null}),\"Share-Token\":r}});if(!d.ok){try{const e=await d.json();if(e.code>=400)return void(null==i||i(e.code,e.message))}catch{}throw new Error(\"Network response was not ok\")}const h=await d.json();h.code>0&&h.code<=200&&h.data?null==c||c(h.data):483===h.code?i?i(483,h.message):s.error(h.message):i?i(h.code,h.message):s.error(h.message)}catch(d){const e=d instanceof Error?d.message:String(d);i?i(-1,e):s.error(e)}finally{null==l||l()}},[]),k=e.useCallback(async(e,o,r)=>{try{const c=await fetch(a(`${t.API_URL}/api/note/history?vault=${encodeURIComponent(e)}&id=${o}`),{method:\"GET\",headers:n()});if(!c.ok)throw new Error(\"Network response was not ok\");const i=await c.json();i.code>0&&i.code<=200&&i.data?r(i.data):s.error(i.message)}catch(c){s.error(c instanceof Error?c.message:String(c))}},[n]),y=e.useCallback(async(e,o,r)=>{try{const c={vault:e,historyId:o},i=await fetch(a(`${t.API_URL}/api/note/history/restore`),{method:\"PUT\",body:JSON.stringify(c),headers:n()});if(!i.ok)throw new Error(\"Network response was not ok\");const l=await i.json();l.code>0&&l.code<=200?(s.success(l.message),r()):s.error(l.message+(l.details?\": \"+l.details.join(\", \"):\"\"))}catch(c){s.error(c instanceof Error?c.message:String(c))}},[n]),$=e.useCallback(async(e,o=\"\",r=\"\",i)=>{try{let l=`${t.API_URL}/api/folders?vault=${encodeURIComponent(e)}`;o&&(l+=`&path=${encodeURIComponent(o)}`),r&&(l+=`&path_hash=${encodeURIComponent(r)}`);const d=await fetch(a(l),{method:\"GET\",headers:n()});if(!d.ok)return 508===d.status?c():s.error(\"Network response was not ok\"),void i(null);const h=await d.json();h.code>0&&h.code<=200?i(h.data||[]):(508===h.code?c():s.error(h.message),i(null))}catch(l){s.error(l instanceof Error?l.message:String(l)),i(null)}},[n,c]),S=e.useCallback(async(e,o=\"\",r=\"\",i,l,d=\"mtime\",h=\"desc\",g)=>{try{const m=Math.floor(i).toString(),w=Math.floor(l).toString();let p=`${t.API_URL}/api/folder/notes?vault=${encodeURIComponent(e)}&page=${m}&pageSize=${w}`;o&&(p+=`&path=${encodeURIComponent(o)}`),r&&(p+=`&path_hash=${encodeURIComponent(r)}`),d&&(p+=`&sortBy=${d}`),h&&(p+=`&sortOrder=${h}`);const f=await fetch(a(p),{method:\"GET\",headers:n()});if(!f.ok)return 508===f.status?c():s.error(\"Network response was not ok\"),void g(null);const u=await f.json();if(u.code>0&&u.code<=200){const e=u.data||{list:[],pager:{page:i,pageSize:l,totalRows:0}};e.list||(e.list=[]),g(e)}else 508===u.code?c():s.error(u.message),g(null)}catch(m){s.error(m instanceof Error?m.message:String(m)),g(null)}},[n,c]),E=e.useCallback(async(e,o,r,i,l=\"mtime\",d=\"desc\",h)=>{try{const g=Math.floor(r).toString(),m=Math.floor(i).toString(),w=encodeURIComponent(o.join(\",\"));let p=`${t.API_URL}/api/notes?vault=${encodeURIComponent(e)}&page=${g}&pageSize=${m}&paths=${w}`;l&&\"mtime\"!==l&&(p+=`&sortBy=${l}`),d&&\"desc\"!==d&&(p+=`&sortOrder=${d}`);const f=await fetch(a(p),{method:\"GET\",headers:n()});if(!f.ok)return 508===f.status?c():s.error(\"Network response was not ok\"),void h(null);const u=await f.json();if(u.code>0&&u.code<=200){const e=u.data||{list:[],pager:{page:1,pageSize:i,totalRows:0}};e.list||(e.list=[]),h(e)}else 508===u.code?c():s.error(u.message),h(null)}catch(g){s.error(g instanceof Error?g.message:String(g)),h(null)}},[n,c]);return e.useMemo(()=>({handleNoteList:i,handleNoteListByPaths:E,handleFolderList:$,handleFolderNotes:S,handleGetNote:l,handleSaveNote:d,handleDeleteNote:h,handlePermanentDeleteNote:g,handleClearNoteRecycle:m,handleRestoreNote:w,handleRenameNote:p,handleNoteHistoryList:f,handleNoteHistoryDetail:k,handleRestoreFromHistory:y,handleGetShareNote:u}),[i,E,$,S,l,d,h,g,m,w,p,f,k,y,u])}export{r as u};\n"
  },
  {
    "path": "frontend/assets/note-manager-DjJcxkCE.js",
    "content": "const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=[\"assets/markdown-editor-CX5kQlgI.js\",\"assets/font-loader-CIrh3KnA.js\",\"assets/font-loader-B-ynJ_1p.css\",\"assets/index-JfsWWBj_.js\",\"assets/zap-CLLhzk_y.js\",\"assets/pencil-DqQhr35g.js\",\"assets/markdown-editor-DMUawZD_.css\"])))=>i.map(i=>d[i]);\nimport{c as e,u as s,r as t,j as r,B as a,C as n,a5 as i,t as l,I as o,X as c,_ as d,b as m,a as h,e as u,a0 as x,L as p,a6 as f,a7 as g,n as j,o as v,q as b,F as N,V as w}from\"./font-loader-CIrh3KnA.js\";import{D as y,a as k,b as C,c as S,d as H,C as P,N as z,L as M,E as L,u as T,e as R,f as E}from\"./main-BIi-kGYY.js\";import{u as V}from\"./note-handle-IK8dQjtF.js\";import{A,a as $,b as D,c as F,d as O,e as I,f as B,g as U,S as _,L as W,G as q}from\"./alert-dialog-CfMssux5.js\";import{T as G,a as K,b as J,c as Z,d as Q,e as X}from\"./table-D9wbHMTA.js\";import{C as Y}from\"./checkbox-DhTHgmeh.js\";import{T as ee}from\"./tooltip-Dr-qRlmI.js\";import{B as se}from\"./badge-C63ATniC.js\";import{H as te}from\"./history-BseqF3eb.js\";import{R as re,A as ae,F as ne,a as ie,C as le}from\"./canvas-viewer-Cxwbo1vR.js\";import{C as oe,a as ce}from\"./copy-CEhXannp.js\";import{f as de}from\"./format-CdHm7RWL.js\";import{P as me}from\"./pencil-DqQhr35g.js\";import{E as he,a as ue}from\"./eye-DrvrOb4o.js\";import{S as xe,a as pe,b as fe,c as ge,d as je}from\"./select-CJF_alSt.js\";import{S as ve}from\"./share-2-BVJjAadJ.js\";import{S as be}from\"./search-DdihTHF8.js\";import{R as Ne}from\"./refresh-cw-BxIJAPy3.js\";import{P as we}from\"./plus-BBfuNxDX.js\";import{C as ye}from\"./clock-C9LPHszx.js\";import{C as ke}from\"./markdown-editor-CX5kQlgI.js\";import{A as Ce,D as Se}from\"./database-eyf5nvY6.js\";import{T as He}from\"./trash-2-ad7PiUnC.js\";import{T as Pe}from\"./text-cursor-input-Bphfsfyn.js\";import\"./index-JfsWWBj_.js\";import\"./zap-CLLhzk_y.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const ze=e(\"Apple\",[[\"path\",{d:\"M12 20.94c1.5 0 2.75 1.06 4 1.06 3 0 6-8 6-12.22A4.91 4.91 0 0 0 17 5c-2.22 0-4 1.44-5 2-1-.56-2.78-2-5-2a4.9 4.9 0 0 0-5 4.78C2 14 5 22 8 22c1.25 0 2.5-1.06 4-1.06Z\",key:\"3s7exb\"}],[\"path\",{d:\"M10 2c1 .5 2 2 2 5\",key:\"fcco2y\"}]]),Me=e(\"ArrowLeftRight\",[[\"path\",{d:\"M8 3 4 7l4 4\",key:\"9rb6wj\"}],[\"path\",{d:\"M4 7h16\",key:\"6tx8e3\"}],[\"path\",{d:\"m16 21 4-4-4-4\",key:\"siv7j2\"}],[\"path\",{d:\"M20 17H4\",key:\"h6l3hr\"}]]),Le=e(\"Chrome\",[[\"circle\",{cx:\"12\",cy:\"12\",r:\"10\",key:\"1mglay\"}],[\"circle\",{cx:\"12\",cy:\"12\",r:\"4\",key:\"4exip2\"}],[\"line\",{x1:\"21.17\",x2:\"12\",y1:\"8\",y2:\"8\",key:\"a0cw5f\"}],[\"line\",{x1:\"3.95\",x2:\"8.54\",y1:\"6.06\",y2:\"14\",key:\"1kftof\"}],[\"line\",{x1:\"10.88\",x2:\"15.46\",y1:\"21.94\",y2:\"14\",key:\"1ymyh8\"}]]),Te=e(\"Columns2\",[[\"rect\",{width:\"18\",height:\"18\",x:\"3\",y:\"3\",rx:\"2\",key:\"afitv7\"}],[\"path\",{d:\"M12 3v18\",key:\"108xh3\"}]]),Re=e(\"FolderSearch\",[[\"path\",{d:\"M10.7 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v4.1\",key:\"1bw5m7\"}],[\"path\",{d:\"m21 21-1.9-1.9\",key:\"1g2n9r\"}],[\"circle\",{cx:\"17\",cy:\"17\",r:\"3\",key:\"18b49y\"}]]),Ee=e(\"Fullscreen\",[[\"path\",{d:\"M3 7V5a2 2 0 0 1 2-2h2\",key:\"aa7l1z\"}],[\"path\",{d:\"M17 3h2a2 2 0 0 1 2 2v2\",key:\"4qcy5o\"}],[\"path\",{d:\"M21 17v2a2 2 0 0 1-2 2h-2\",key:\"6vwrx8\"}],[\"path\",{d:\"M7 21H5a2 2 0 0 1-2-2v-2\",key:\"ioqczr\"}],[\"rect\",{width:\"10\",height:\"8\",x:\"7\",y:\"8\",rx:\"1\",key:\"vys8me\"}]]),Ve=e(\"Link2Off\",[[\"path\",{d:\"M9 17H7A5 5 0 0 1 7 7\",key:\"10o201\"}],[\"path\",{d:\"M15 7h2a5 5 0 0 1 4 8\",key:\"1d3206\"}],[\"line\",{x1:\"8\",x2:\"12\",y1:\"12\",y2:\"12\",key:\"rvw6j4\"}],[\"line\",{x1:\"2\",x2:\"22\",y1:\"2\",y2:\"22\",key:\"a6p6uj\"}]]),Ae=e(\"Link2\",[[\"path\",{d:\"M9 17H7A5 5 0 0 1 7 7h2\",key:\"8i5ue5\"}],[\"path\",{d:\"M15 7h2a5 5 0 1 1 0 10h-2\",key:\"1b9ql8\"}],[\"line\",{x1:\"8\",x2:\"16\",y1:\"12\",y2:\"12\",key:\"1jonct\"}]]),$e=e(\"PanelLeftClose\",[[\"rect\",{width:\"18\",height:\"18\",x:\"3\",y:\"3\",rx:\"2\",key:\"afitv7\"}],[\"path\",{d:\"M9 3v18\",key:\"fh3hqa\"}],[\"path\",{d:\"m16 15-3-3 3-3\",key:\"14y99z\"}]]),De=e(\"RefreshCcw\",[[\"path\",{d:\"M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\",key:\"14sxne\"}],[\"path\",{d:\"M3 3v5h5\",key:\"1xhq8a\"}],[\"path\",{d:\"M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16\",key:\"1hlbsb\"}],[\"path\",{d:\"M16 16h5v5\",key:\"ccwih5\"}]]),Fe=e(\"Shrink\",[[\"path\",{d:\"m15 15 6 6m-6-6v4.8m0-4.8h4.8\",key:\"17vawe\"}],[\"path\",{d:\"M9 19.8V15m0 0H4.2M9 15l-6 6\",key:\"chjx8e\"}],[\"path\",{d:\"M15 4.2V9m0 0h4.8M15 9l6-6\",key:\"lav6yq\"}],[\"path\",{d:\"M9 4.2V9m0 0H4.2M9 9 3 3\",key:\"1pxi2q\"}]]),Oe=({className:e})=>r.jsx(\"svg\",{viewBox:\"0 0 24 24\",fill:\"currentColor\",className:e,children:r.jsx(\"path\",{d:\"M0 3.449L9.75 2.1V11.7H0V3.449zm0 17.1L9.75 21.9V12.3H0v8.249zM10.5 2V11.7H24V0L10.5 2zm0 19.9l13.5 2V12.3H10.5v9.6z\"})}),Ie=({className:e})=>r.jsx(\"svg\",{viewBox:\"0 0 24 24\",fill:\"currentColor\",className:e,children:r.jsx(\"path\",{d:\"M17.523 15.3414C17.0709 15.3414 16.7042 14.9747 16.7042 14.5226C16.7042 14.0705 17.0709 13.7038 17.523 13.7038C17.9751 13.7038 18.3418 14.0705 18.3418 14.5226C18.3418 14.9747 17.9751 15.3414 17.523 15.3414ZM6.47702 15.3414C6.02492 15.3414 5.65823 14.9747 5.65823 14.5226C5.65823 14.0705 6.02492 13.7038 6.47702 13.7038C6.92911 13.7038 7.29581 14.0705 7.29581 14.5226C7.29581 14.9747 6.92911 15.3414 6.47702 15.3414ZM17.8465 10.925L19.4975 8.06544C19.648 7.80481 19.5588 7.47196 19.2982 7.32145C19.0375 7.17094 18.7047 7.26017 18.5542 7.52081L16.8529 10.4677C15.4269 9.81534 13.8052 9.42997 12 9.42997C10.1948 9.42997 8.57307 9.81534 7.14713 10.4677L5.44577 7.52081C5.29527 7.26017 4.96245 7.17094 4.70181 7.32145C4.44116 7.47196 4.35194 7.80481 4.50244 8.06544L6.15347 10.925C3.06456 12.6015 1 15.7725 1 19.4673H23C23 15.7725 20.9354 12.6015 17.8465 10.925Z\"})});\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */function Be({isOpen:e,onClose:l,vault:o,notePath:c,pathHash:d,isRecycle:m=!1,onRestoreSuccess:h}){const{t:u}=s(),{handleNoteHistoryList:x,handleNoteHistoryDetail:p,handleRestoreFromHistory:f}=V(),[g,j]=t.useState([]),[v,b]=t.useState(1),[N,w]=t.useState(0),[L,T]=t.useState(!1),[R,E]=t.useState(null),[q,ae]=t.useState(!1),[ne,ie]=t.useState(!1),[le,ce]=t.useState(!1),[me,he]=t.useState(!1),[ue,xe]=t.useState(!1),[pe,fe]=t.useState(!1),ge=t.useRef(0),je=t.useRef(0),ve=t.useCallback(e=>{const s=++ge.current;T(!0),x(o,c,d,e,5,m,e=>{s===ge.current&&(e?(j(e.list||[]),e.pager&&w(e.pager.totalRows||0)):(j([]),w(0)),T(!1))})},[x,o,c,d,5,m]);t.useEffect(()=>{if(e)return ve(v),E(null),void ce(!1);ge.current+=1,je.current+=1,T(!1),ae(!1)},[e,v,ve]),t.useEffect(()=>()=>{ge.current+=1,je.current+=1},[]);const be=t.useCallback(e=>{const s=++je.current;ae(!0),he(!1),p(o,e,e=>{s===je.current&&(E(e),ae(!1))})},[p,o]),Ne=(e=\"\")=>{const s=String(e||\"\").toLowerCase();return s.includes(\"web\")?r.jsx(Le,{className:\"h-4 w-4 text-[#4285F4] shrink-0\"}):s.includes(\"mac\")||s.includes(\"apple\")?r.jsx(ze,{className:\"h-4 w-4 text-[#555555] shrink-0\"}):s.includes(\"win\")?r.jsx(Oe,{className:\"h-3.5 w-3.5 text-[#0078D4] shrink-0 translate-y-[1px]\"}):s.includes(\"ios\")||s.includes(\"iphone\")||s.includes(\"ipad\")?r.jsx(_,{className:\"h-4 w-4 text-[#555555] shrink-0\"}):s.includes(\"android\")?r.jsx(Ie,{className:\"h-4 w-4 text-[#3DDC84] shrink-0\"}):r.jsx(W,{className:\"h-4 w-4 text-slate-400 shrink-0\"})},we=Math.ceil((N||0)/5);return r.jsxs(y,{open:e,onOpenChange:e=>!e&&l(),children:[r.jsxs(k,{className:\"max-w-4xl w-[95vw] sm:w-auto max-h-[85vh] sm:max-h-[90vh] flex flex-col p-4 sm:p-6 rounded-lg sm:rounded-xl\",children:[r.jsxs(C,{children:[r.jsxs(S,{className:\"flex items-center gap-2 font-normal overflow-hidden text-sm sm:text-base\",children:[r.jsx(te,{className:\"h-4 w-4 shrink-0 text-muted-foreground opacity-70\"}),r.jsxs(\"span\",{className:\"hidden sm:inline shrink-0 text-muted-foreground\",children:[u(\"ui.history.title\"),\":\"]}),r.jsx(\"span\",{className:\"truncate text-foreground font-medium\",children:String(c||\"\").replace(/\\.md$/,\"\")})]}),r.jsx(H,{className:\"sr-only\",children:u(\"ui.history.description\")})]}),r.jsx(\"div\",{className:\"flex-1 overflow-auto py-2 sm:py-4 custom-scrollbar\",children:r.jsxs(\"div\",{className:\"space-y-4 sm:space-y-6\",children:[r.jsxs(\"div\",{className:\"border rounded-md overflow-hidden\",children:[r.jsx(\"div\",{className:\"hidden sm:block\",children:r.jsxs(G,{children:[r.jsx(K,{children:r.jsxs(J,{children:[r.jsx(Z,{className:\"w-[80px]\",children:u(\"ui.history.versionLabel\")}),r.jsx(Z,{children:u(\"ui.history.clientSource\")}),r.jsx(Z,{children:u(\"ui.common.updatedAt\")}),r.jsx(Z,{className:\"text-right pr-7\",children:u(\"ui.common.viewDetail\")})]})}),r.jsx(Q,{children:L?r.jsx(J,{children:r.jsx(X,{colSpan:4,className:\"text-center py-8 text-muted-foreground\",children:u(\"ui.history.loading\")})}):!Array.isArray(g)||Array.isArray(g)&&0===g.length?r.jsx(J,{children:r.jsx(X,{colSpan:4,className:\"text-center py-8 text-muted-foreground\",children:u(\"ui.history.noHistory\")})}):g.filter(e=>null!==e).map(e=>r.jsxs(J,{className:\"hover:bg-muted/50 transition-colors \"+((null==R?void 0:R.id)===e.id?\"bg-blue-50\":\"\"),children:[r.jsxs(X,{className:\"font-mono\",children:[\"v\",e.version]}),r.jsx(X,{className:\"text-muted-foreground\",children:r.jsx(ee,{content:r.jsxs(\"div\",{className:\"flex flex-col gap-1.5 p-1 min-w-[120px]\",children:[r.jsxs(\"div\",{className:\"flex justify-between items-center gap-4\",children:[r.jsx(\"span\",{className:\"text-muted-foreground/70\",children:u(\"ui.system.wsClientName\")}),r.jsx(\"span\",{className:\"font-medium text-foreground capitalize\",children:e.clientType||u(\"ui.common.na\")})]}),r.jsxs(\"div\",{className:\"flex justify-between items-center gap-4 border-t border-border/30 pt-1.5\",children:[r.jsx(\"span\",{className:\"text-muted-foreground/70\",children:u(\"ui.share.version\")}),r.jsx(\"span\",{className:\"font-mono text-foreground font-medium\",children:e.clientVersion?`v${e.clientVersion}`:u(\"ui.common.na\")})]}),r.jsxs(\"div\",{className:\"flex justify-between items-center gap-4 border-t border-border/30 pt-1.5\",children:[r.jsx(\"span\",{className:\"text-muted-foreground/70\",children:u(\"ui.common.name\")}),r.jsx(\"span\",{className:\"font-semibold text-foreground\",children:e.clientName||u(\"ui.common.na\")})]})]}),children:r.jsxs(\"div\",{className:\"flex items-center gap-2 cursor-help\",children:[r.jsx(se,{variant:\"outline\",className:\"text-xs font-medium px-2 py-0.5 rounded-md hover:bg-primary/5 transition-colors whitespace-nowrap\",children:e.clientType||e.clientName||u(\"ui.common.na\")}),Ne(e.clientName)]})})}),r.jsx(X,{className:\"text-muted-foreground\",children:e.createdAt?de(new Date(e.createdAt),\"yyyy-MM-dd HH:mm:ss\"):\"-\"}),r.jsx(X,{className:\"text-right\",children:r.jsx(a,{variant:\"ghost\",size:\"sm\",onClick:()=>be(e.id),children:u(\"ui.common.view\")})})]},e.id))})]})}),r.jsx(\"div\",{className:\"sm:hidden divide-y\",children:L?r.jsx(\"div\",{className:\"text-center py-8 text-muted-foreground\",children:u(\"ui.history.loading\")}):Array.isArray(g)&&0!==g.length?g.filter(e=>null!==e).map(e=>r.jsxs(\"div\",{className:\"p-3 \"+((null==R?void 0:R.id)===e.id?\"bg-blue-50\":\"\"),onClick:()=>be(e.id),children:[r.jsxs(\"div\",{className:\"flex items-center justify-between mb-1\",children:[r.jsxs(\"span\",{className:\"font-mono font-medium\",children:[\"v\",e.version]}),r.jsx(\"div\",{className:\"flex items-center gap-1.5 text-muted-foreground text-xs\",children:r.jsx(ee,{content:r.jsxs(\"div\",{className:\"flex flex-col gap-1.5 p-1 min-w-[120px]\",children:[r.jsxs(\"div\",{className:\"flex justify-between items-center gap-4\",children:[r.jsx(\"span\",{className:\"text-muted-foreground/70\",children:u(\"ui.system.wsClientName\")}),r.jsx(\"span\",{className:\"font-medium text-foreground capitalize\",children:e.clientType||u(\"ui.common.na\")})]}),r.jsxs(\"div\",{className:\"flex justify-between items-center gap-4 border-t border-border/30 pt-1.5\",children:[r.jsx(\"span\",{className:\"text-muted-foreground/70\",children:u(\"ui.share.version\")}),r.jsx(\"span\",{className:\"font-mono text-foreground font-medium\",children:e.clientVersion?`v${e.clientVersion}`:u(\"ui.common.na\")})]}),r.jsxs(\"div\",{className:\"flex justify-between items-center gap-4 border-t border-border/30 pt-1.5\",children:[r.jsx(\"span\",{className:\"text-muted-foreground/70\",children:u(\"ui.common.name\")}),r.jsx(\"span\",{className:\"font-semibold text-foreground\",children:e.clientName||u(\"ui.common.na\")})]})]}),children:r.jsxs(\"div\",{className:\"flex items-center gap-1.5 cursor-help\",children:[Ne(e.clientName),r.jsx(\"span\",{className:\"capitalize\",children:e.clientType||e.clientName||\"Unknown\"})]})})})]}),r.jsx(\"div\",{className:\"text-xs text-muted-foreground\",children:e.createdAt?de(new Date(e.createdAt),\"yyyy-MM-dd HH:mm:ss\"):\"-\"})]},e.id)):r.jsx(\"div\",{className:\"text-center py-8 text-muted-foreground\",children:u(\"ui.history.noHistory\")})})]}),we>1&&r.jsxs(\"div\",{className:\"flex flex-col sm:flex-row items-center justify-between gap-2 px-2\",children:[r.jsx(\"p\",{className:\"text-xs sm:text-sm text-muted-foreground\",children:u(\"ui.history.count\",{count:N})}),r.jsxs(\"div\",{className:\"flex items-center space-x-2\",children:[r.jsxs(a,{variant:\"outline\",size:\"sm\",onClick:()=>b(e=>Math.max(1,e-1)),disabled:1===v||L,className:\"h-8 px-2 sm:px-3\",children:[r.jsx(P,{className:\"h-4 w-4\"}),r.jsx(\"span\",{className:\"hidden sm:inline ml-1\",children:u(\"ui.common.previous\")})]}),r.jsxs(\"span\",{className:\"text-xs sm:text-sm font-medium\",children:[v,\" / \",we]}),r.jsxs(a,{variant:\"outline\",size:\"sm\",onClick:()=>b(e=>Math.min(we,e+1)),disabled:v===we||L,className:\"h-8 px-2 sm:px-3\",children:[r.jsx(\"span\",{className:\"hidden sm:inline mr-1\",children:u(\"ui.common.next\")}),r.jsx(n,{className:\"h-4 w-4\"})]})]})]}),R&&r.jsxs(\"div\",{className:\"mt-4 sm:mt-6 border-t pt-4 sm:pt-6\",children:[r.jsxs(\"div\",{className:\"flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-4\",children:[r.jsxs(\"h3\",{className:\"text-base sm:text-lg font-semibold flex items-baseline gap-2\",children:[r.jsx(z,{className:\"h-4 w-4 self-center\"}),r.jsx(\"span\",{children:u(\"ui.history.diffDetails\",{version:R.version})}),r.jsxs(\"div\",{className:\"flex items-baseline gap-2 ml-2 text-[10px] font-normal uppercase tracking-wider\",children:[r.jsx(\"span\",{className:\"px-1.5 py-0 rounded bg-green-100 text-muted-foreground/60 border border-green-200/50 leading-tight\",children:u(\"ui.history.diffLegendAdd\")}),r.jsx(\"span\",{className:\"px-1.5 py-0 rounded bg-red-100 text-muted-foreground/60 border border-red-200/50 leading-tight\",children:u(\"ui.history.diffLegendDel\")})]})]}),r.jsxs(\"div\",{className:\"flex items-center gap-2 flex-wrap\",children:[!m&&r.jsxs(a,{variant:\"outline\",size:\"sm\",onClick:()=>xe(!0),className:\"h-8 px-3 text-blue-600 border-blue-200 hover:bg-blue-50 hover:border-blue-300\",children:[r.jsx(re,{className:\"h-3.5 w-3.5 mr-1.5\"}),u(\"ui.history.restoreToVersion\")]}),r.jsxs(\"div\",{className:\"flex items-center space-x-2 bg-slate-100 px-2 sm:px-3 py-1.5 rounded-lg border border-slate-200 shadow-sm\",children:[r.jsx(Y,{id:\"showDiffOnly\",checked:ne,onCheckedChange:e=>{const s=!!e;ie(s),s&&ce(!1)}}),r.jsx(M,{htmlFor:\"showDiffOnly\",className:\"text-xs sm:text-sm font-medium leading-none cursor-pointer text-slate-700\",children:u(\"ui.history.showDiffOnly\")})]}),r.jsxs(\"div\",{className:\"flex items-center space-x-2 bg-slate-100 px-2 sm:px-3 py-1.5 rounded-lg border border-slate-200 shadow-sm\",children:[r.jsx(Y,{id:\"showOriginalContent\",checked:le,onCheckedChange:e=>{const s=!!e;ce(s),s&&ie(!1)}}),r.jsx(M,{htmlFor:\"showOriginalContent\",className:\"text-xs sm:text-sm font-medium leading-none cursor-pointer text-slate-700\",children:u(\"ui.history.showOriginalContent\")})]})]})]}),q?r.jsx(\"div\",{className:\"py-8 text-center text-muted-foreground\",children:u(\"loadingHistory\")}):le?r.jsxs(\"div\",{className:\"relative group\",children:[(e=>{if(!e)return null;const s=e.replace(/\\r\\n/g,\"\\n\").replace(/\\n$/,\"\").split(\"\\n\");return r.jsxs(\"div\",{className:\"diff-content overflow-auto max-h-[500px] p-6 bg-slate-50 rounded-xl border border-slate-200 font-mono text-[13px] leading-7 shadow-inner text-slate-700 select-text selection:bg-blue-100 selection:text-blue-900\",children:[r.jsxs(\"div\",{className:\"diff-line-row flex items-start group min-h-[28px]\",children:[r.jsx(\"div\",{className:\"line-number w-10 shrink-0 text-right pr-3 select-none text-transparent font-mono text-[11px] border-r border-transparent mr-3 leading-7\",children:\"-\"}),r.jsx(\"div\",{className:\"line-content flex-1 min-w-0 py-0 text-slate-300 font-mono text-[11px] select-text cursor-text min-h-[28px] leading-7 break-words\",children:c})]}),s.map((e,s)=>r.jsxs(\"div\",{className:\"diff-line-row flex items-start group min-h-[28px]\",children:[r.jsx(\"div\",{className:\"line-number w-10 shrink-0 text-right pr-3 select-none text-slate-300 font-mono text-[11px] border-r border-slate-100 mr-3 group-hover:text-slate-400 transition-colors leading-7\",children:s+1}),r.jsx(\"div\",{className:\"line-content flex-1 min-w-0 py-0 min-h-[28px] leading-7 break-words\",children:e||\" \"})]},`original-line-${s}`))]})})(R.content),r.jsx(a,{variant:\"secondary\",size:me?\"sm\":\"icon\",className:`absolute top-2 right-2 h-8 ${me?\"w-auto px-3\":\"w-8\"} transition-all shadow-sm bg-white/80 hover:bg-white backdrop-blur-sm border border-slate-200 z-10 flex items-center gap-1`,onClick:()=>{R.content&&(navigator.clipboard.writeText(R.content),he(!0),setTimeout(()=>he(!1),2e3))},title:u(\"ui.note.copyNote\"),children:me?r.jsxs(\"div\",{className:\"flex items-center gap-1 text-green-600 animate-in fade-in zoom-in duration-200\",children:[r.jsx(i,{className:\"h-3.5 w-3.5\"}),r.jsx(\"span\",{className:\"text-xs font-medium\",children:u(\"ui.common.copied\")})]}):r.jsx(oe,{className:\"h-4 w-4 text-slate-500\"})})]}):(e=>{if(!e||!Array.isArray(e))return null;let s=0;const t=[];let a=[];const n=()=>{let e=\"-\";a.every(e=>-1===e.Type)||(s++,e=s.toString()),t.push({lineNum:e,segments:[...a]}),a=[]};e.forEach(e=>{if(!e||null===e.Text||void 0===e.Text)return;const s=String(e.Text).split(\"\\n\");s.forEach((t,r)=>{a.push({Type:e.Type,Text:t}),r<s.length-1&&n()})}),a.length>0&&n();const i=ne?t.filter(e=>e.segments&&e.segments.some(e=>0!==e.Type)):t;return r.jsxs(\"div\",{className:\"diff-content overflow-auto max-h-[500px] p-6 bg-slate-50 rounded-xl border border-slate-200 font-mono text-[13px] leading-7 shadow-inner text-slate-700 select-text selection:bg-blue-100 selection:text-blue-900\",children:[r.jsxs(\"div\",{className:\"diff-line-row flex items-start group min-h-[28px]\",children:[r.jsx(\"div\",{className:\"line-number w-10 shrink-0 text-right pr-3 select-none text-transparent font-mono text-[11px] border-r border-transparent mr-3 leading-7\",children:\"-\"}),r.jsx(\"div\",{className:\"line-content flex-1 min-w-0 py-0 text-slate-300 font-mono text-[11px] select-text cursor-text min-h-[28px] leading-7 break-words\",children:c})]}),0===i.length?r.jsx(\"div\",{className:\"text-center py-4 opacity-50 italic\",children:u(\"ui.history.noHistory\")}):i.map((e,s)=>{const t=e.segments.some(e=>String(e.Text??\"\").length>0);return r.jsxs(\"div\",{className:\"diff-line-row flex items-start group min-h-[28px]\",children:[r.jsx(\"div\",{className:\"line-number w-10 shrink-0 text-right pr-3 select-none text-slate-300 font-mono text-[11px] border-r border-slate-100 mr-3 group-hover:text-slate-400 transition-colors leading-7\",children:e.lineNum}),r.jsx(\"div\",{className:\"line-content flex-1 min-w-0 py-0 min-h-[28px] leading-7 break-words\",children:t?e.segments.map((e,t)=>{const a=`segment-${s}-${t}`,n=String(e.Text??\"\");return 1===e.Type?r.jsx(\"ins\",{className:\"bg-green-100 text-green-900 no-underline px-0.5 rounded-sm border-b border-green-300 font-bold\",children:n},a):-1===e.Type?r.jsx(\"del\",{className:\"bg-red-100 text-red-900 line-through decoration-red-400 px-0.5 rounded-sm opacity-70\",children:n},a):r.jsx(\"span\",{children:n},a)}):\" \"})]},`diff-line-${s}`)})]})})(R.diffs)]})]})})]}),r.jsx(A,{open:ue,onOpenChange:xe,children:r.jsxs($,{children:[r.jsxs(D,{children:[r.jsx(F,{children:u(\"ui.history.restoreConfirmTitle\")}),r.jsx(O,{children:u(\"ui.history.restoreConfirmDesc\",{version:null==R?void 0:R.version})})]}),r.jsxs(I,{children:[r.jsx(B,{disabled:pe,children:u(\"ui.common.cancel\")}),r.jsx(U,{onClick:()=>{R&&(fe(!0),f(o,R.id,()=>{fe(!1),xe(!1),l(),null==h||h()}),setTimeout(()=>fe(!1),5e3))},disabled:pe,children:u(pe?\"ui.common.restoring\":\"ui.common.confirm\")})]})]})})]})}function Ue(e){let s=0;if(0===e.length)return\"0\";for(let t=0;t<e.length;t++){s=(s<<5)-s+e.charCodeAt(t),s|=0}return s.toString()}const _e=t.lazy(()=>d(()=>import(\"./markdown-editor-CX5kQlgI.js\").then(e=>e.m),__vite__mapDeps([0,1,2,3,4,5,6])).then(e=>({default:e.MarkdownEditor}))),We=()=>{const{t:e}=s();return r.jsx(\"div\",{className:\"flex items-center justify-center h-full text-muted-foreground\",children:r.jsxs(\"div\",{className:\"flex flex-col items-center gap-2\",children:[r.jsx(\"div\",{className:\"animate-spin rounded-full h-8 w-8 border-b-2 border-primary\"}),r.jsx(\"span\",{children:e(\"ui.note.loadingEditor\")})]})})};function qe({vault:e,note:n,onBack:d,onNavigateToFolder:m,onSaveSuccess:h,onViewHistory:u,isMaximized:x=!1,onToggleMaximize:p,isRecycle:f=!1,initialPreviewMode:g=!1,onWikiLinkClick:j}){const{t:v}=s(),{handleGetNote:b,handleSaveNote:N,handleRenameNote:w}=V(),y=t.useRef(null),k=t.useRef(null),C=t.useRef(null),S=t.useRef(\"\"),H=t.useRef(\"\"),P=t.useRef(()=>{}),z=t.useRef(0),[M,L]=t.useState(\"\"),[T,R]=t.useState(\"\"),[E,A]=t.useState(!1),[$,D]=t.useState(!1),[F,O]=t.useState(null),[I,B]=t.useState(!1),[U,_]=t.useState(\"\"),[W,q]=t.useState(null),[G,K]=t.useState(!1),[J,Z]=t.useState(g),[Q,X]=t.useState(\"single\"),[Y,se]=t.useState(!1),re=t.useRef(null);t.useEffect(()=>{S.current=T},[T]),t.useEffect(()=>{Z(g)},[g,null==n?void 0:n.id]);const ie=!n,le=t.useCallback(e=>{H.current=e,L(e)},[]),oe=t.useCallback((s,t,r=!1)=>{if(!s||f)return;const a=s.endsWith(\".md\")?s:s+\".md\",n={pathHash:Ue(a),contentHash:Ue(t)};D(!0),N(e,a,t,n,()=>{D(!1),q(new Date),F&&F.path!==a&&O(e=>e?{...e,path:a}:null),h(a,n.pathHash||\"\")},()=>{D(!1)},r)},[e,F,N,h,f]),ue=t.useRef(\"\"),xe=t.useCallback(()=>{var s;if(k.current){clearTimeout(k.current),k.current=null;const e=(null==(s=y.current)?void 0:s.getValue())??S.current;oe(H.current,e,!0)}const t=++z.current;n?(le(n.path.replace(/\\.md$/,\"\")),A(!0),b(e,n.path,n.pathHash,f,e=>{t===z.current&&(O(e),R(e.content))},()=>{t===z.current&&A(!1)})):(le(\"\"),R(\"\"),O(null),A(!1))},[n,e,b,f,le,oe]);t.useEffect(()=>{const e=n?`${n.id}-${n.pathHash}-${n.path}`:\"new-note\";e!==ue.current&&(ue.current=e,xe())},[n,xe]),t.useEffect(()=>()=>{P.current()},[]);const pe=t.useCallback(()=>{C.current&&(document.fullscreenElement?document.exitFullscreen():C.current.requestFullscreen().catch(()=>{}))},[]);t.useEffect(()=>{const e=()=>{K(!!document.fullscreenElement)};return document.addEventListener(\"fullscreenchange\",e),()=>{document.removeEventListener(\"fullscreenchange\",e)}},[]),t.useEffect(()=>{var e;if(\"split\"!==Q)return;const s=window.matchMedia(\"(min-width: 768px)\"),t=e=>{var s;if(!e.matches){const e=(null==(s=y.current)?void 0:s.getValue())??S.current;R(e),X(\"single\")}};if(!s.matches){const s=(null==(e=y.current)?void 0:e.getValue())??S.current;return R(s),void X(\"single\")}return s.addEventListener(\"change\",t),()=>s.removeEventListener(\"change\",t)},[Q]);const fe=t.useCallback(()=>{var e;if(!k.current)return;clearTimeout(k.current),k.current=null;const s=(null==(e=y.current)?void 0:e.getValue())??S.current;oe(H.current,s,!0)},[oe]);t.useEffect(()=>{P.current=fe},[fe]);const ge=t.useCallback(e=>{S.current=e,\"split\"===Q&&R(e),ie&&!H.current||f||(k.current&&(clearTimeout(k.current),k.current=null),k.current=setTimeout(()=>{var e;k.current=null;const s=(null==(e=y.current)?void 0:e.getValue())??S.current;oe(H.current,s,!0)},1e3))},[ie,f,oe,Q]),je=t.useCallback(()=>{f||(_(M),B(!0),setTimeout(()=>{var e;return null==(e=re.current)?void 0:e.focus()},0))},[M,f]),ve=t.useCallback(()=>{B(!1),_(\"\")},[]),be=t.useCallback(()=>{const s=U.replace(/[<>:\"\\\\|?*]/g,\"\").split(\"\").filter(e=>e.charCodeAt(0)>=32).join(\"\");if(!s)return void ve();const t=M,r=t.endsWith(\".md\")?t:t+\".md\",a=s.endsWith(\".md\")?s:s+\".md\";if(a!==r){if(ie)return le(s),void B(!1);D(!0),w({vault:e,oldPath:r,path:a,oldPathHash:null==F?void 0:F.pathHash},()=>{D(!1),le(s),B(!1),q(new Date),F&&O({...F,path:a,pathHash:Ue(a)}),h(a,Ue(a))})}else B(!1)},[U,M,e,F,w,le,ie,h,ve]),Ne=t.useCallback(e=>{\"Enter\"===e.key?(e.preventDefault(),be()):\"Escape\"===e.key&&ve()},[be,ve]),we=t.useCallback(e=>{const s=e.target.value.replace(/[<>:\"\\\\|?*]/g,\"\").split(\"\").filter(e=>e.charCodeAt(0)>=32).join(\"\");le(s)},[le]),ye=t.useCallback(()=>{var e;if(!M)return void l.error(v(\"ui.note.noteTitleRequired\"));const s=(null==(e=y.current)?void 0:e.getValue())??S.current;oe(M,s,!0)},[M,oe,v]),ke=t.useCallback(()=>{fe(),ie&&!H.current&&S.current.trim()&&l.warning(v(\"ui.note.unsavedContentWithoutTitle\",{defaultValue:\"Content is not saved because the title is empty.\"})),d()},[fe,ie,d,v]),{folder:Ce,filename:Se}=(e=>{const s=e.lastIndexOf(\"/\");return-1===s?{folder:\"\",filename:e}:{folder:e.substring(0,s),filename:e.substring(s+1)}})(M);return r.jsxs(\"div\",{ref:C,className:\"w-full h-full flex flex-col \"+(G?\"bg-background p-2 sm:p-4\":\"\"),children:[r.jsxs(\"div\",{className:\"flex items-center justify-between gap-1 sm:gap-4 mb-1 sm:mb-4\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-1 sm:gap-3 min-w-0 flex-1\",children:[r.jsx(a,{variant:\"ghost\",size:\"icon\",\"aria-label\":v(\"ui.common.previous\"),onClick:ke,className:\"shrink-0 rounded-lg sm:rounded-xl h-7 w-7 sm:h-10 sm:w-10\",children:r.jsx(ae,{className:\"h-4 w-4 sm:h-5 sm:w-5\"})}),ie?r.jsxs(\"div\",{className:\"flex items-center gap-1 flex-1 min-w-0\",children:[r.jsx(o,{value:M,onChange:we,onKeyDown:e=>\"Enter\"===e.key&&ye(),placeholder:null==(He=v(\"ui.note.noteTitlePlaceholder\"))?void 0:He.replace(\" (e.g., note.md)\",\"\").replace(\" (例如: note.md)\",\"\"),className:\"font-bold text-sm sm:text-lg border-none shadow-none focus-visible:ring-0 px-1 sm:px-2 flex-1 h-7 sm:h-auto\",autoFocus:!0}),r.jsx(a,{variant:\"ghost\",size:\"icon-sm\",\"aria-label\":v(\"ui.common.save\"),className:\"self-center h-6 w-6 sm:h-7 sm:w-7 shrink-0\",onClick:ye,disabled:!M||$,children:r.jsx(i,{className:\"h-3.5 w-3.5 sm:h-4 sm:w-4\"})})]}):I?r.jsxs(\"div\",{className:\"flex items-center gap-1 flex-1 min-w-0\",children:[Ce&&r.jsxs(\"div\",{className:\"hidden sm:flex items-center gap-2 shrink-0\",children:[r.jsx(ne,{className:\"h-4 w-4 text-muted-foreground\"}),r.jsx(\"span\",{className:\"text-muted-foreground truncate max-w-37.5\",children:Ce}),r.jsx(\"span\",{className:\"text-muted-foreground\",children:\"/\"})]}),r.jsx(o,{ref:re,value:U,onChange:e=>_(e.target.value),onKeyDown:Ne,onBlur:be,className:\"font-bold text-sm sm:text-lg border-none shadow-none focus-visible:ring-0 px-1 h-7 sm:h-auto py-0 flex-1\"}),r.jsx(a,{variant:\"ghost\",size:\"icon\",\"aria-label\":v(\"ui.common.save\"),className:\"h-6 w-6 sm:h-7 sm:w-7 shrink-0\",onClick:be,children:r.jsx(i,{className:\"h-3.5 w-3.5 sm:h-4 sm:w-4\"})}),r.jsx(a,{variant:\"ghost\",size:\"icon\",\"aria-label\":v(\"ui.common.cancel\"),className:\"h-6 w-6 sm:h-7 sm:w-7 shrink-0\",onClick:ve,children:r.jsx(c,{className:\"h-3.5 w-3.5 sm:h-4 sm:w-4\"})})]}):r.jsxs(\"div\",{className:\"flex items-center gap-1 min-w-0 flex-1\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-1 min-w-0 text-sm sm:text-lg\",children:[Ce&&r.jsxs(\"div\",{className:\"hidden sm:flex items-center gap-2 shrink-0 cursor-pointer hover:text-primary transition-colors\",onClick:e=>{e.stopPropagation(),m&&m(Ce)},children:[r.jsx(ne,{className:\"h-4 w-4 text-muted-foreground\"}),r.jsx(\"span\",{className:\"text-muted-foreground truncate max-w-37.5 hover:text-primary\",children:Ce}),r.jsx(\"span\",{className:\"text-muted-foreground\",children:\"/\"})]}),r.jsx(\"span\",{className:\"font-bold truncate \"+(f?\"\":\"cursor-pointer hover:bg-muted/50 rounded px-1 py-0.5 -mx-1 -my-0.5 transition-colors\"),onClick:je,children:Se})]}),!f&&r.jsxs(\"div\",{className:\"flex items-center gap-1.5 text-xs text-muted-foreground shrink-0\",children:[void 0!==(null==F?void 0:F.version)&&F.version>0&&r.jsx(ee,{content:v(\"ui.history.title\"),side:\"bottom\",delay:200,children:r.jsxs(\"span\",{className:\"flex items-center gap-0.5 px-1 py-0.5 rounded bg-muted/50 text-muted-foreground hover:bg-muted cursor-default\",children:[r.jsx(te,{className:\"h-3 w-3\"}),r.jsxs(\"span\",{children:[\"v\",F.version]})]})}),$?r.jsxs(r.Fragment,{children:[r.jsx(ce,{className:\"h-3 w-3 sm:h-3.5 sm:w-3.5 animate-pulse\"}),r.jsx(\"span\",{className:\"hidden sm:inline\",children:v(\"ui.common.saving\")})]}):W?r.jsxs(r.Fragment,{children:[r.jsx(ce,{className:\"h-3 w-3 sm:h-3.5 sm:w-3.5 text-green-500\"}),r.jsx(\"span\",{children:de(W,\"HH:mm:ss\")})]}):(null==F?void 0:F.mtime)?r.jsxs(r.Fragment,{children:[r.jsx(ce,{className:\"h-3 w-3 sm:h-3.5 sm:w-3.5 text-muted-foreground/50\"}),r.jsx(\"span\",{className:\"hidden xs:inline\",children:de(new Date(F.mtime),\"MM-dd HH:mm\")})]}):null]})]})]}),r.jsxs(\"div\",{className:\"flex items-center gap-0.5 sm:gap-2 shrink-0\",children:[n&&r.jsx(ee,{content:v(\"ui.common.refresh\"),side:\"bottom\",delay:200,children:r.jsx(a,{onClick:xe,variant:\"outline\",size:\"icon\",className:\"rounded-lg sm:rounded-xl h-7 w-7 sm:h-10 sm:w-10\",children:r.jsx(De,{className:\"h-3.5 w-3.5 sm:h-4 sm:w-4 \"+(E?\"animate-spin\":\"\")})})}),n&&\"single\"===Q&&r.jsx(ee,{content:v(J?\"ui.note.editNote\":\"ui.note.viewNote\"),side:\"bottom\",delay:200,children:r.jsx(a,{onClick:()=>{var e;if(!J){const s=(null==(e=y.current)?void 0:e.getValue())??S.current;R(s)}Z(!J)},variant:\"outline\",size:\"icon\",className:\"rounded-lg sm:rounded-xl h-7 w-7 sm:h-10 sm:w-10\",children:J?r.jsx(me,{className:\"h-3.5 w-3.5 sm:h-4 sm:w-4\"}):r.jsx(he,{className:\"h-3.5 w-3.5 sm:h-4 sm:w-4\"})})}),n&&!f&&r.jsx(ee,{content:\"split\"===Q?v(\"ui.note.singlePanel\",{defaultValue:\"Single panel\"}):v(\"ui.note.splitPanel\",{defaultValue:\"Split panel\"}),side:\"bottom\",delay:200,children:r.jsx(a,{onClick:()=>{var e,s;if(\"single\"===Q){const s=(null==(e=y.current)?void 0:e.getValue())??S.current;R(s),Z(!1),X(\"split\")}else{const e=(null==(s=y.current)?void 0:s.getValue())??S.current;R(e),X(\"single\")}},variant:\"outline\",size:\"icon\",className:\"hidden md:inline-flex rounded-lg sm:rounded-xl h-7 w-7 sm:h-10 sm:w-10\",children:\"split\"===Q?r.jsx($e,{className:\"h-3.5 w-3.5 sm:h-4 sm:w-4\"}):r.jsx(Te,{className:\"h-3.5 w-3.5 sm:h-4 sm:w-4\"})})}),\"split\"===Q&&r.jsx(ee,{content:v(\"ui.note.swapPanels\",{defaultValue:\"Swap panels\"}),side:\"bottom\",delay:200,children:r.jsx(a,{onClick:()=>se(e=>!e),variant:\"outline\",size:\"icon\",className:\"rounded-lg sm:rounded-xl h-7 w-7 sm:h-10 sm:w-10\",children:r.jsx(Me,{className:\"h-3.5 w-3.5 sm:h-4 sm:w-4\"})})}),n&&u&&r.jsx(ee,{content:v(\"ui.history.title\"),side:\"bottom\",delay:200,children:r.jsx(a,{onClick:u,variant:\"outline\",size:\"icon\",className:\"rounded-lg sm:rounded-xl h-7 w-7 sm:h-10 sm:w-10\",children:r.jsx(te,{className:\"h-3.5 w-3.5 sm:h-4 sm:w-4\"})})}),r.jsx(ee,{content:v(G?\"ui.note.exitFullscreen\":\"ui.note.fullscreen\"),side:\"bottom\",delay:200,children:r.jsx(a,{onClick:pe,variant:\"outline\",size:\"icon\",className:\"rounded-lg sm:rounded-xl h-7 w-7 sm:h-10 sm:w-10\",children:G?r.jsx(Fe,{className:\"h-3.5 w-3.5 sm:h-4 sm:w-4\"}):r.jsx(Ee,{className:\"h-3.5 w-3.5 sm:h-4 sm:w-4\"})})})]})]}),r.jsx(\"div\",{className:\"flex-1 min-h-0\",children:E?r.jsx(\"div\",{className:\"flex items-center justify-center h-full rounded-xl border border-border bg-card\",children:r.jsx(\"div\",{className:\"animate-spin rounded-full h-8 w-8 border-2 border-primary border-t-transparent\"})}):\"split\"===Q?r.jsxs(\"div\",{className:\"h-full flex gap-1\",children:[r.jsx(\"div\",{className:\"flex-1 min-w-0 overflow-visible rounded-xl border border-border bg-card\",children:r.jsx(t.Suspense,{fallback:r.jsx(We,{}),children:r.jsx(_e,{ref:Y?void 0:y,value:T,onChange:Y?void 0:ge,readOnly:Y||f,initialMode:Y?\"preview\":\"edit\",vault:e,fileLinks:null==F?void 0:F.fileLinks,onWikiLinkClick:j},`${null==n?void 0:n.id}-${Y?\"preview\":\"edit\"}`)})}),r.jsx(\"div\",{className:\"flex-1 min-w-0 overflow-visible rounded-xl border border-border bg-card\",children:r.jsx(t.Suspense,{fallback:r.jsx(We,{}),children:r.jsx(_e,{ref:Y?y:void 0,value:T,onChange:Y?ge:void 0,readOnly:!Y||f,initialMode:Y?\"edit\":\"preview\",vault:e,fileLinks:null==F?void 0:F.fileLinks,onWikiLinkClick:j},`${null==n?void 0:n.id}-${Y?\"edit\":\"preview\"}`)})})]}):r.jsx(\"div\",{className:\"h-full overflow-visible rounded-xl border border-border bg-card\",children:r.jsx(t.Suspense,{fallback:r.jsx(We,{}),children:r.jsx(_e,{ref:y,value:T,onChange:ge,readOnly:f||J,placeholder:v(\"ui.note.noteContentPlaceholder\"),ariaLabel:v(\"ui.note.editNote\"),vault:e,fileLinks:null==F?void 0:F.fileLinks,initialMode:J?\"preview\":\"edit\",onWikiLinkClick:j},`${null==n?void 0:n.id}-${J}`)})})})]});var He}function Ge(){const e=localStorage.getItem(\"token\"),s=t.useCallback((s={})=>m({token:e,extraHeaders:s}),[e]),r=t.useCallback(()=>{localStorage.removeItem(\"token\"),window.location.reload()},[]),a=t.useCallback(async(e,t)=>{try{const a=new URLSearchParams;e.page&&a.append(\"page\",e.page.toString()),e.pageSize&&a.append(\"pageSize\",e.pageSize.toString());const n=h(`${u.API_URL}/api/shares?${a.toString()}`),i=await fetch(n,{method:\"GET\",headers:s()});if(401===i.status)return void r();const o=await i.json();o.code>0&&o.data?t(o.data):l.error(o.message||\"Failed to fetch share list\")}catch(a){l.error(a instanceof Error?a.message:String(a))}},[s,r]),n=t.useCallback(async(e,t,a,n,i)=>{try{const l=h(`${u.API_URL}/api/share?path=${encodeURIComponent(t)}&pathHash=${encodeURIComponent(a)}&vault=${encodeURIComponent(e)}`),o=await fetch(l,{method:\"GET\",headers:s()});if(401===o.status)return void r();const c=await o.json();c.code>0&&c.data?n(c.data):null==i||i()}catch(o){l.error(o instanceof Error?o.message:String(o))}},[s,r]),i=t.useCallback(async(e,t,a,n)=>{try{const i=await fetch(`${u.API_URL}/api/share`,{method:\"POST\",headers:s(),body:JSON.stringify({vault:e,path:t,pathHash:a})});if(401===i.status)return void r();const o=await i.json();o.code>0&&o.data?n(o.data):l.error(o.message||\"Failed to create share\")}catch(i){l.error(i instanceof Error?i.message:String(i))}},[s,r]),o=t.useCallback(async(e,t)=>{try{const a=await fetch(`${u.API_URL}/api/share/password`,{method:\"POST\",headers:s(),body:JSON.stringify(e)});if(401===a.status)return void r();const n=await a.json();n.code>0?null==t||t():l.error(n.message||\"Failed to update share password\")}catch(a){l.error(a instanceof Error?a.message:String(a))}},[s,r]),c=t.useCallback(async(e,t)=>{try{const a=await fetch(`${u.API_URL}/api/share/short_link`,{method:\"POST\",headers:s(),body:JSON.stringify(e)});if(401===a.status)return void r();const n=await a.json();n.code>0&&n.data?t(n.data):l.error(n.message||\"Failed to create short link\")}catch(a){l.error(a instanceof Error?a.message:String(a))}},[s,r]);return{handleShareList:a,handleGetShareByPath:n,handleCreateShare:i,handleCancelShare:t.useCallback(async(e,t)=>{try{const a=await fetch(`${u.API_URL}/api/share`,{method:\"DELETE\",headers:s(),body:JSON.stringify(e)});if(401===a.status)return void r();const n=await a.json();n.code>0?null==t||t():l.error(n.message||\"Failed to cancel share\")}catch(a){l.error(a instanceof Error?a.message:String(a))}},[s,r]),handleUpdateSharePassword:o,handleCreateShortLink:c,handleGetNoteSharePaths:t.useCallback(async(e,t)=>{try{const a=h(`${u.API_URL}/api/notes/share-paths?vault=${encodeURIComponent(e)}`),n=await fetch(a,{method:\"GET\",headers:s()});if(401===n.status)return void r();const i=await n.json();i.code>0&&i.data?t(i.data):t([])}catch{t([])}},[s,r])}}function Ke({vault:e,path:n,pathHash:c,open:d,onOpenChange:m,onShareChange:h}){const{t:u}=s(),{handleGetShareByPath:g,handleCreateShare:j,handleCancelShare:v,handleUpdateSharePassword:b,handleCreateShortLink:N}=Ge(),[w,H]=t.useState(!1),[P,z]=t.useState(null),[M,T]=t.useState(\"\"),[R,E]=t.useState(!1),[V,A]=t.useState(\"\"),[$,D]=t.useState(!1),F=t.useCallback(()=>{H(!0),g(e,n,c,e=>{z(e),e.isPassword?T(\"******\"):T(\"\"),A(e.shortLink||\"\"),H(!1)},()=>{z(null),E(!1),H(!1)})},[e,n,c,g]);t.useEffect(()=>{d?F():(z(null),T(\"\"),E(!1),A(\"\"))},[d,F]);const O=t.useCallback(()=>{P&&(D(!0),N({vault:e,path:n,pathHash:c,url:I(P)},e=>{A(e),D(!1)}))},[e,n,c,P,N]),I=e=>e&&e.token?`${window.location.origin}/share/${e.id}/${e.token}`:\"\";return r.jsx(y,{open:d,onOpenChange:m,children:r.jsxs(k,{className:\"sm:max-w-md rounded-2xl overflow-hidden shadow-2xl border-none\",children:[r.jsx(C,{children:r.jsxs(S,{className:\"flex items-center gap-2\",children:[r.jsx(ve,{className:\"h-5 w-5 text-primary\"}),u(\"ui.share.title\")]})}),r.jsxs(\"div\",{className:\"py-4 space-y-4\",children:[r.jsx(\"div\",{className:\"text-sm text-muted-foreground break-all bg-muted/50 p-3 rounded-xl border border-border/50\",children:n}),w?r.jsxs(\"div\",{className:\"flex flex-col items-center justify-center py-8 text-muted-foreground animate-in fade-in duration-300\",children:[r.jsx(x,{className:\"h-8 w-8 animate-spin text-primary mb-2\"}),r.jsx(\"p\",{className:\"text-sm\",children:u(\"ui.share.checking\")})]}):P?r.jsxs(\"div\",{className:\"space-y-4 animate-in zoom-in-95 duration-200\",children:[r.jsxs(\"div\",{className:\"space-y-2\",children:[r.jsxs(\"label\",{className:\"text-xs font-semibold text-muted-foreground uppercase tracking-wider ml-1 flex items-center gap-1\",children:[r.jsx(q,{className:\"h-3 w-3\"}),u(\"ui.share.link\")]}),r.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[r.jsxs(\"div\",{className:\"relative flex-1 group\",children:[r.jsx(o,{readOnly:!0,value:I(P),className:\"pr-10 bg-muted/30 border-muted-foreground/20 hover:border-primary/50 transition-colors rounded-xl\"}),r.jsx(\"div\",{className:\"absolute right-1 top-1/2 -translate-y-1/2\",children:r.jsx(ee,{content:u(\"ui.share.copy\"),side:\"top\",children:r.jsx(a,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 text-muted-foreground hover:text-primary transition-colors\",onClick:()=>{if(!P)return;const e=I(P);navigator.clipboard.writeText(e).then(()=>{l.success(u(\"ui.share.copySuccess\"))})},children:r.jsx(oe,{className:\"h-4 w-4\"})})})})]}),r.jsx(ee,{content:u(\"ui.share.viewShare\"),side:\"top\",children:r.jsx(a,{variant:\"outline\",size:\"icon\",className:\"h-10 w-10 shrink-0 border-muted-foreground/20 hover:border-primary/50 hover:bg-primary/5 text-muted-foreground hover:text-primary transition-all rounded-xl shadow-sm\",onClick:()=>{if(!P)return;const e=I(P);window.open(e,\"_blank\")},children:r.jsx(L,{className:\"h-4 w-4\"})})})]})]}),r.jsxs(\"div\",{className:\"space-y-2\",children:[r.jsxs(\"label\",{className:\"text-xs font-semibold text-muted-foreground uppercase tracking-wider ml-1 flex items-center gap-1\",children:[r.jsx(p,{className:\"h-3 w-3\"}),u(\"ui.user.password\")]}),r.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[r.jsxs(\"div\",{className:\"relative flex-1\",children:[r.jsx(o,{type:R?\"text\":\"password\",value:M,onChange:e=>T(e.target.value),placeholder:u(\"ui.share.passwordPlaceholder\")||\"Set a password (Optional)\",className:\"pr-10 bg-muted/30 border-muted-foreground/20 hover:border-primary/50 transition-colors rounded-xl\"}),r.jsx(\"button\",{type:\"button\",className:\"absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-primary transition-colors\",onClick:()=>{const e=!R;E(e),e&&\"******\"===M?T(\"\"):!e&&\"\"===M&&(null==P?void 0:P.isPassword)&&T(\"******\")},children:R?r.jsx(ue,{className:\"h-4 w-4\"}):r.jsx(he,{className:\"h-4 w-4\"})})]}),r.jsx(ee,{content:u(\"ui.common.save\"),side:\"top\",children:r.jsx(a,{variant:\"outline\",size:\"icon\",className:\"h-10 w-10 shrink-0 border-muted-foreground/20 hover:border-primary/50 hover:bg-primary/5 text-muted-foreground hover:text-primary transition-all rounded-xl shadow-sm\",onClick:()=>{P&&(\"******\"===M&&P.isPassword?l.error(u(\"ui.common.noChange\")||\"No changes to save\"):b({vault:e,path:n,pathHash:c,password:M},()=>{l.success(u(\"ui.common.saveSuccess\")),F()}))},children:r.jsx(i,{className:\"h-4 w-4\"})})})]})]}),r.jsxs(\"div\",{className:\"space-y-2\",children:[r.jsxs(\"label\",{className:\"text-xs font-semibold text-muted-foreground uppercase tracking-wider ml-1 flex items-center gap-1\",children:[r.jsx(Ae,{className:\"h-3 w-3\"}),u(\"ui.share.shortLink\")]}),r.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[V?r.jsxs(\"div\",{className:\"relative flex-1 group\",children:[r.jsx(o,{readOnly:!0,value:V,className:\"pr-10 bg-muted/30 border-muted-foreground/20 hover:border-primary/50 transition-colors rounded-xl\"}),r.jsx(\"div\",{className:\"absolute right-1 top-1/2 -translate-y-1/2\",children:r.jsx(ee,{content:u(\"ui.share.shortLinkCopy\"),side:\"top\",children:r.jsx(a,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 text-muted-foreground hover:text-primary transition-colors\",onClick:()=>{navigator.clipboard.writeText(V).then(()=>{l.success(u(\"ui.share.copySuccess\"))})},children:r.jsx(oe,{className:\"h-4 w-4\"})})})})]}):r.jsxs(a,{variant:\"outline\",className:\"w-full justify-start rounded-xl border-dashed border-2 hover:border-primary/50 hover:bg-primary/5 text-muted-foreground hover:text-primary transition-all h-10 px-4\",onClick:O,disabled:$,children:[$?r.jsx(x,{className:\"h-4 w-4 animate-spin mr-2\"}):r.jsx(Ae,{className:\"h-4 w-4 mr-2\"}),u(\"ui.share.shortLinkCreate\")]}),V&&r.jsx(ee,{content:u(\"ui.common.refresh\"),side:\"top\",children:r.jsx(a,{variant:\"outline\",size:\"icon\",className:\"h-10 w-10 shrink-0 border-muted-foreground/20 hover:border-primary/50 hover:bg-primary/5 text-muted-foreground hover:text-primary transition-all rounded-xl shadow-sm\",onClick:()=>{A(\"\"),O()},disabled:$,children:r.jsx(x,{className:\"h-4 w-4 \"+($?\"animate-spin\":\"\")})})})]})]})]}):r.jsxs(\"div\",{className:\"flex flex-col items-center justify-center py-6 space-y-4 animate-in fade-in duration-300\",children:[r.jsx(\"div\",{className:\"h-16 w-16 rounded-full bg-primary/10 flex items-center justify-center\",children:r.jsx(ve,{className:\"h-8 w-8 text-primary opacity-60\"})}),r.jsx(\"p\",{className:\"text-sm text-muted-foreground text-center px-4\",children:u(\"ui.share.noShares\")}),r.jsx(a,{onClick:async()=>{H(!0),j(e,n,c,e=>{z(e),A(e.shortLink||\"\"),H(!1),l.success(u(\"ui.share.success\")),null==h||h()})},disabled:w,className:\"w-full sm:w-auto px-8 rounded-xl shadow-lg shadow-primary/20 hover:shadow-primary/30 active:scale-95 transition-all\",children:u(\"ui.share.create\")})]})]}),P&&!w&&r.jsxs(\"div\",{className:\"flex items-center justify-between border-t pt-4 mt-4 w-full\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-2 text-xs text-green-600 bg-green-500/5 px-3 py-1.5 rounded-full border border-green-200/50\",children:[r.jsx(f,{className:\"h-3.5 w-3.5 shrink-0\"}),r.jsx(\"span\",{children:u(\"ui.share.success\")})]}),r.jsxs(a,{variant:\"ghost\",size:\"sm\",className:\"text-destructive hover:bg-destructive/10 hover:text-destructive transition-colors rounded-xl font-medium\",onClick:async()=>{H(!0),v({vault:e,path:n,pathHash:c},()=>{z(null),H(!1),l.success(u(\"ui.share.cancelSuccess\")),null==h||h()})},disabled:w,children:[r.jsx(Ve,{className:\"h-4 w-4 mr-2\"}),u(\"ui.share.cancelShare\")]})]})]})})}function Je({vault:e,vaults:i,onVaultChange:l,onSelectNote:d,onCreateNote:m,page:h,setPage:u,pageSize:x,setPageSize:p,onViewHistory:f,isRecycle:y=!1,searchKeyword:k,setSearchKeyword:C,currentPath:S,setCurrentPath:H,currentPathHash:M,setCurrentPathHash:L,pathHashMap:E,setPathHashMap:A,shareFilter:$,setShareFilter:D,viewMode:F,setViewMode:O}){const{t:I}=s(),{handleNoteList:B,handleDeleteNote:U,handleRestoreNote:_,handleFolderList:W,handleFolderNotes:q,handlePermanentDeleteNote:G,handleClearNoteRecycle:K,handleRenameNote:J,handleNoteListByPaths:Z}=V(),{handleGetNoteSharePaths:Q}=Ge(),{openConfirmDialog:X}=g(),[se,ae]=t.useState([]),[le,oe]=t.useState(!1),[ce,ue]=t.useState(0),[Se,ze]=t.useState(k),[Me,Le]=t.useState(\"path\"),[Te,Ee]=t.useState(\"mtime\"),[Ve,Ae]=t.useState(\"desc\"),[$e,De]=t.useState(new Set),[Fe,Oe]=t.useState(null),[Ie,Be]=t.useState([]),Ue=t.useRef(0),{trashType:_e,setModule:We}=T(),[qe,Je]=t.useState(!1),[Ze,Qe]=t.useState(null),[Xe,Ye]=t.useState(new Set),es=()=>{y||Q(e,e=>{Ye(s=>e.length===s.size&&e.every(e=>s.has(e))?s:new Set(e))})};t.useEffect(()=>{es()},[e]);const ss=Xe.size,ts=\"active\"===$?Xe.size:0;t.useEffect(()=>{const e=setTimeout(()=>{ze(k)},300);return()=>clearTimeout(e)},[k]);const rs=(s=h,t=x,r=Se)=>{const a=++Ue.current;if(oe(!0),\"active\"===$&&!y)return Be([]),0===Xe.size?(ae([]),ue(0),void oe(!1)):void Z(e,[...Xe],s,t,Te,Ve,e=>{var s;a===Ue.current&&(ae((null==e?void 0:e.list)||[]),ue((null==(s=null==e?void 0:e.pager)?void 0:s.totalRows)||0),oe(!1))});\"folder\"!==F||y?B(e,s,t,r,y,Me,!1,Te,Ve,e=>{var s;if(a!==Ue.current)return;let t=(null==e?void 0:e.list)||[];ae(t),ue((null==(s=null==e?void 0:e.pager)?void 0:s.totalRows)||0),oe(!1)}):Promise.all([new Promise(s=>W(e,S,M,s)),new Promise(r=>q(e,S,M,s,t,Te,Ve,r))]).then(([e,s])=>{var t;a===Ue.current&&(Be(e||[]),ae((null==s?void 0:s.list)||[]),ue((null==(t=null==s?void 0:s.pager)?void 0:t.totalRows)||0),oe(!1))})};t.useEffect(()=>{rs(h,x,Se),De(new Set)},[e,h,x,Se,y,Me,Te,Ve,F,S,$,ts]),t.useEffect(()=>{Se&&O(\"flat\")},[Se,S,F,u]);const as=e=>{e>=1&&e<=Math.ceil(ce/x)&&u(e)},ns=Math.ceil(ce/x);return r.jsxs(\"div\",{className:\"w-full flex flex-col space-y-4\",children:[r.jsxs(\"div\",{className:\"flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 py-1\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[i&&l&&r.jsxs(xe,{value:e,onValueChange:l,children:[r.jsx(pe,{className:\"w-auto min-w-45 rounded-xl\",children:r.jsx(fe,{placeholder:I(\"ui.common.selectVault\")})}),r.jsx(ge,{className:\"rounded-xl\",children:i.map(e=>r.jsx(je,{value:e.vault,className:\"rounded-xl\",children:e.vault},e.id))})]}),!y&&r.jsxs(a,{variant:\"active\"===$?\"default\":\"outline\",size:\"sm\",className:\"rounded-xl text-xs h-8\",onClick:()=>{const e=\"active\"===$?null:\"active\";D(e),e&&u(1)},children:[r.jsx(ve,{className:\"h-3 w-3 mr-1\"}),I(\"ui.share.tabActive\"),\" (\",ss,\")\"]})]}),r.jsx(\"div\",{className:\"flex flex-col gap-2 w-full sm:w-auto\",children:r.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[r.jsxs(\"div\",{className:\"relative flex-1 sm:w-64 group\",children:[r.jsx(be,{className:\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none\"}),r.jsx(o,{type:\"text\",placeholder:I(\"ui.note.searchPlaceholder\"),className:\"pl-9 pr-14 rounded-xl\",value:k,onChange:e=>C(e.target.value)}),r.jsxs(\"div\",{className:\"absolute right-1 top-1/2 -translate-y-1/2 flex items-center gap-0.5\",children:[k&&r.jsx(\"button\",{className:\"p-1.5 text-muted-foreground hover:text-foreground transition-colors\",onClick:()=>C(\"\"),children:r.jsx(c,{className:\"h-4 w-4\"})}),r.jsxs(j,{children:[r.jsx(v,{asChild:!0,children:r.jsxs(\"button\",{className:\"flex items-center gap-1 px-1.5 py-1 text-xs font-medium text-muted-foreground hover:text-foreground hover:bg-muted/80 rounded-lg transition-colors\",children:[\"path\"===Me&&r.jsx(Re,{className:\"h-3.5 w-3.5\"}),\"content\"===Me&&r.jsx(z,{className:\"h-3.5 w-3.5\"}),r.jsx(R,{className:\"h-3 w-3\"})]})}),r.jsxs(b,{align:\"end\",className:\"rounded-xl min-w-32\",children:[r.jsx(N,{onClick:()=>Le(\"path\"),className:\"rounded-lg flex items-center justify-between \"+(\"path\"===Me?\"bg-accent\":\"\"),children:r.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[r.jsx(Re,{className:\"h-4 w-4\"}),r.jsx(\"span\",{children:I(\"ui.note.searchPath\")})]})}),r.jsx(N,{onClick:()=>Le(\"content\"),className:\"rounded-lg flex items-center justify-between \"+(\"content\"===Me?\"bg-accent\":\"\"),children:r.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[r.jsx(z,{className:\"h-4 w-4\"}),r.jsx(\"span\",{children:I(\"ui.note.searchContentMode\")})]})})]})]})]})]}),r.jsx(a,{variant:\"outline\",size:\"icon\",\"aria-label\":I(\"ui.common.refresh\"),onClick:()=>rs(),disabled:le,className:\"rounded-xl shrink-0\",children:r.jsx(Ne,{className:\"h-4 w-4 \"+(le?\"animate-spin\":\"\")})}),!y&&r.jsxs(a,{onClick:m,className:\"rounded-xl shrink-0\",children:[r.jsx(we,{className:\"h-4 w-4 sm:mr-2\"}),r.jsx(\"span\",{className:\"hidden sm:inline\",children:I(\"ui.note.newNote\")})]})]})})]}),!y&&r.jsxs(\"div\",{className:\"flex flex-wrap items-center gap-4 py-2 px-2 bg-muted/30 rounded-xl border border-border/50\",children:[!$&&r.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[r.jsxs(\"div\",{className:\"flex items-center h-8 rounded-lg border border-border overflow-hidden bg-background shadow-sm\",children:[r.jsx(\"button\",{className:\"px-4 h-full text-xs font-medium transition-colors \"+(\"folder\"===F?\"bg-primary text-primary-foreground\":\"hover:bg-muted\"),onClick:()=>{C(\"\"),ze(\"\"),O(\"folder\")},children:I(\"ui.note.viewFolder\")}),r.jsx(\"button\",{className:\"px-4 h-full text-xs font-medium transition-colors border-l border-border \"+(\"flat\"===F?\"bg-primary text-primary-foreground\":\"hover:bg-muted\"),onClick:()=>O(\"flat\"),children:I(\"ui.note.viewFlat\")})]}),r.jsxs(\"span\",{className:\"text-sm font-medium text-muted-foreground mr-2\",children:[ce,\" \",I(\"ui.note.note\")]})]}),r.jsxs(\"div\",{className:\"flex items-center h-8 rounded-xl border border-border overflow-hidden bg-background shadow-sm ml-auto\",children:[r.jsxs(\"button\",{className:\"px-3 h-full text-xs flex items-center gap-1.5 transition-colors \"+(\"mtime\"===Te?\"bg-accent text-accent-foreground\":\"hover:bg-muted\"),onClick:()=>Ee(\"mtime\"),children:[r.jsx(ye,{className:\"h-3.5 w-3.5\"}),I(\"ui.note.sortByMtime\")]}),r.jsxs(\"button\",{className:\"px-3 h-full text-xs flex items-center gap-1.5 transition-colors border-l border-border \"+(\"ctime\"===Te?\"bg-accent text-accent-foreground\":\"hover:bg-muted\"),onClick:()=>Ee(\"ctime\"),children:[r.jsx(ke,{className:\"h-3.5 w-3.5\"}),I(\"ui.note.sortByCtime\")]}),r.jsxs(\"button\",{className:\"px-3 h-full text-xs flex items-center gap-1.5 transition-colors border-l border-border \"+(\"path\"===Te?\"bg-accent text-accent-foreground\":\"hover:bg-muted\"),onClick:()=>Ee(\"path\"),children:[r.jsx(z,{className:\"h-3.5 w-3.5\"}),I(\"ui.note.sortByPath\")]}),r.jsx(ee,{content:I(\"desc\"===Ve?\"ui.note.sortDesc\":\"ui.note.sortAsc\"),side:\"top\",delay:200,children:r.jsx(\"button\",{className:\"px-2.5 h-full text-xs flex items-center transition-colors border-l border-border hover:bg-muted\",onClick:()=>Ae(\"desc\"===Ve?\"asc\":\"desc\"),children:\"desc\"===Ve?r.jsx(Ce,{className:\"h-3.5 w-3.5\"}):r.jsx(ie,{className:\"h-3.5 w-3.5\"})})})]})]}),y&&r.jsxs(\"div\",{className:\"flex flex-wrap items-center gap-2 sm:gap-4 py-2 px-2 bg-muted/30 rounded-xl border border-border/50\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[r.jsxs(\"div\",{className:\"flex items-center h-8 rounded-lg border border-border overflow-hidden bg-background shadow-sm\",children:[r.jsx(\"button\",{className:\"px-4 h-full text-xs font-medium transition-colors \"+(\"notes\"===_e?\"bg-primary text-primary-foreground\":\"hover:bg-muted\"),onClick:()=>We(\"trash\",\"notes\"),children:I(\"ui.note.note\")}),r.jsx(\"button\",{className:\"px-4 h-full text-xs font-medium transition-colors border-l border-border \"+(\"files\"===_e?\"bg-primary text-primary-foreground\":\"hover:bg-muted\"),onClick:()=>We(\"trash\",\"files\"),children:I(\"ui.file.file\")})]}),r.jsxs(\"span\",{className:\"text-sm font-medium text-muted-foreground mr-2\",children:[ce,\" \",I(\"ui.nav.menuTrash\"),I(\"ui.note.note\")]}),se.length>0&&r.jsxs(a,{variant:\"ghost\",size:\"sm\",onClick:()=>{X(I(\"ui.note.clearRecycleConfirm\"),\"confirm\",()=>{K(e,()=>{rs()})})},className:\"h-8 rounded-lg text-destructive hover:bg-destructive/10 hover:text-destructive\",children:[r.jsx(He,{className:\"h-3.5 w-3.5 mr-1.5\"}),I(\"ui.common.clear\")]})]}),se.length>0&&r.jsxs(\"div\",{className:\"flex items-center gap-3 pl-0 sm:pl-4 border-l-0 sm:border-l border-border/60\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[r.jsx(Y,{id:\"select-all\",checked:$e.size===se.length&&se.length>0,onCheckedChange:()=>{$e.size===se.length&&se.length>0?De(new Set):De(new Set(se.map(e=>e.pathHash)))},className:\"rounded-md\"}),r.jsx(\"label\",{htmlFor:\"select-all\",className:\"text-xs font-medium cursor-pointer text-muted-foreground hover:text-foreground transition-colors\",children:I(\"ui.common.selectAll\")})]}),$e.size>0&&r.jsxs(\"div\",{className:\"flex items-center gap-3 animate-in fade-in slide-in-from-left-2 duration-200\",children:[r.jsx(\"span\",{className:\"text-xs text-primary font-semibold bg-primary/10 px-2 py-0.5 rounded-full\",children:I(\"ui.file.selectedCount\",{count:$e.size})}),r.jsxs(a,{variant:\"outline\",size:\"sm\",onClick:()=>{0!==$e.size&&X(I(\"ui.file.batchRestoreConfirm\",{count:$e.size}),\"confirm\",async()=>{oe(!0);const s=se.filter(e=>$e.has(e.pathHash)),t=s.length;try{for(let r=0;r<s.length;r++)Oe({current:r+1,total:t}),await Promise.race([new Promise(t=>{_(e,s[r].path,s[r].pathHash,t)}),new Promise(e=>setTimeout(e,3e4))])}finally{Oe(null),De(new Set),rs()}})},className:\"h-8 rounded-lg text-green-600 border-green-200 hover:bg-green-50 hover:text-green-700 hover:border-green-300 shadow-sm\",children:[r.jsx(re,{className:\"h-3.5 w-3.5 mr-1.5\"}),I(\"ui.file.batchRestore\")]}),r.jsxs(a,{variant:\"outline\",size:\"sm\",onClick:()=>{0!==$e.size&&X(I(\"ui.common.batchPermanentDeleteConfirm\",{count:$e.size}),\"confirm\",async()=>{oe(!0);const s=se.filter(e=>$e.has(e.pathHash)),t=s.length;try{for(let r=0;r<s.length;r++)Oe({current:r+1,total:t}),await Promise.race([new Promise(t=>{G(e,s[r].path,s[r].pathHash,t)}),new Promise(e=>setTimeout(e,3e4))])}finally{Oe(null),De(new Set),rs()}})},className:\"h-8 rounded-lg text-destructive border-destructive/20 hover:bg-destructive/5 hover:text-destructive hover:border-destructive/40 shadow-sm\",children:[r.jsx(He,{className:\"h-3.5 w-3.5 mr-1.5\"}),I(\"ui.common.batchPermanentDelete\")]})]})]}),r.jsxs(\"div\",{className:\"flex items-center h-8 rounded-xl border border-border overflow-hidden bg-background shadow-sm ml-auto\",children:[r.jsxs(\"button\",{className:\"px-3 h-full text-xs flex items-center gap-1.5 transition-colors \"+(\"mtime\"===Te?\"bg-accent text-accent-foreground\":\"hover:bg-muted\"),onClick:()=>Ee(\"mtime\"),children:[r.jsx(ye,{className:\"h-3.5 w-3.5\"}),I(\"ui.note.sortByMtime\")]}),r.jsxs(\"button\",{className:\"px-3 h-full text-xs flex items-center gap-1.5 transition-colors border-l border-border \"+(\"ctime\"===Te?\"bg-accent text-accent-foreground\":\"hover:bg-muted\"),onClick:()=>Ee(\"ctime\"),children:[r.jsx(ke,{className:\"h-3.5 w-3.5\"}),I(\"ui.note.sortByCtime\")]}),r.jsxs(\"button\",{className:\"px-3 h-full text-xs flex items-center gap-1.5 transition-colors border-l border-border \"+(\"path\"===Te?\"bg-accent text-accent-foreground\":\"hover:bg-muted\"),onClick:()=>Ee(\"path\"),children:[r.jsx(z,{className:\"h-3.5 w-3.5\"}),I(\"ui.note.sortByPath\")]}),r.jsx(ee,{content:I(\"desc\"===Ve?\"ui.note.sortDesc\":\"ui.note.sortAsc\"),side:\"top\",delay:200,children:r.jsx(\"button\",{className:\"px-2.5 h-full text-xs flex items-center transition-colors border-l border-border hover:bg-muted\",onClick:()=>Ae(\"desc\"===Ve?\"asc\":\"desc\"),children:\"desc\"===Ve?r.jsx(Ce,{className:\"h-3.5 w-3.5\"}):r.jsx(ie,{className:\"h-3.5 w-3.5\"})})})]})]}),\"folder\"===F&&!y&&S&&!$&&r.jsxs(\"div\",{className:\"flex items-center gap-2 px-1 text-sm text-muted-foreground overflow-x-auto whitespace-nowrap scrollbar-hide\",children:[r.jsx(\"button\",{className:\"hover:text-primary transition-colors flex items-center\",onClick:()=>{H(\"\"),L(\"\"),u(1)},children:e}),S.split(\"/\").filter(Boolean).map((e,s,t)=>r.jsxs(w.Fragment,{children:[r.jsx(n,{className:\"h-4 w-4 shrink-0\"}),r.jsx(\"button\",{className:\"transition-colors \"+(s===t.length-1?\"text-foreground font-medium pointer-events-none\":\"hover:text-primary\"),onClick:()=>{const e=t.slice(0,s+1).join(\"/\");H(e),L(E[e]||\"\"),u(1)},children:e})]},`breadcrumb-${s}`))]}),le?r.jsxs(\"div\",{className:\"rounded-xl border border-border bg-card p-12 text-center text-muted-foreground\",children:[r.jsx(Ne,{className:\"h-6 w-6 animate-spin mx-auto mb-2\"}),Fe?`${Fe.current} / ${Fe.total}`:I(\"ui.common.loading\")]}):Array.isArray(se)&&0!==se.length||Array.isArray(Ie)&&0!==Ie.length&&\"flat\"!==F?r.jsx(\"div\",{className:\"-mx-2 px-2\",children:r.jsxs(\"div\",{className:\"grid grid-cols-1 gap-3 py-1\",children:[\"folder\"===F&&!y&&Ie.map(e=>r.jsx(\"article\",{className:\"rounded-xl border border-border bg-card p-4 cursor-pointer transition-all duration-200 hover:shadow-md hover:border-primary/30\",onClick:()=>{A({...E,[e.path]:e.pathHash}),H(e.path),L(e.pathHash),u(1)},children:r.jsxs(\"div\",{className:\"flex items-center justify-between gap-4\",children:[r.jsxs(\"div\",{className:\"flex items-start gap-3 min-w-0 flex-1\",children:[r.jsx(\"span\",{className:\"flex h-10 w-10 items-center justify-center rounded-xl bg-blue-500/10 text-blue-500 shrink-0\",children:r.jsx(ne,{className:\"h-5 w-5 fill-current opacity-70\"})}),r.jsxs(\"div\",{className:\"min-w-0 flex-1\",children:[r.jsx(\"h3\",{className:\"font-semibold text-card-foreground truncate\",children:e.path.split(\"/\").pop()}),r.jsxs(\"div\",{className:\"flex flex-wrap items-center gap-x-4 gap-y-1 mt-1.5 text-xs text-muted-foreground\",children:[r.jsx(ee,{content:I(\"ui.common.createdAt\"),side:\"top\",delay:300,children:r.jsxs(\"span\",{className:\"hidden sm:flex items-center gap-1\",children:[r.jsx(ke,{className:\"h-3.5 w-3.5\"}),de(new Date(e.ctime),\"yyyy-MM-dd HH:mm\")]})}),r.jsx(ee,{content:I(\"ui.common.updatedAt\"),side:\"top\",delay:300,children:r.jsxs(\"span\",{className:\"flex items-center gap-1\",children:[r.jsx(ye,{className:\"h-3.5 w-3.5\"}),de(new Date(e.mtime),\"yyyy-MM-dd HH:mm\")]})})]})]})]}),r.jsx(\"div\",{className:\"shrink-0\",children:r.jsx(n,{className:\"h-5 w-5 text-muted-foreground\"})})]})},`folder-${e.pathHash}`)),Array.isArray(se)&&se.map(s=>{var t;const n=!y&&Xe.has(s.path);return r.jsx(\"article\",{className:\"rounded-xl border border-border bg-card p-2.5 sm:p-4 cursor-pointer transition-all duration-200 hover:shadow-md hover:border-primary/30\",onClick:()=>d(s,!0),children:r.jsxs(\"div\",{className:\"flex items-center justify-between gap-2 sm:gap-4\",children:[r.jsxs(\"div\",{className:\"flex items-start gap-3 min-w-0 flex-1\",children:[y&&r.jsx(\"div\",{className:\"flex items-center self-center\",onClick:e=>((e,s)=>{e.stopPropagation();const t=new Set($e);t.has(s)?t.delete(s):t.add(s),De(t)})(e,s.pathHash),children:r.jsx(Y,{checked:$e.has(s.pathHash),className:\"rounded-md\"})}),r.jsx(\"span\",{className:\"flex h-8 w-8 sm:h-10 sm:w-10 items-center justify-center rounded-xl bg-primary/10 text-primary shrink-0\",children:r.jsx(z,{className:\"h-4 w-4 sm:h-5 sm:w-5\"})}),r.jsxs(\"div\",{className:\"min-w-0 flex-1\",children:[r.jsxs(\"h3\",{className:\"font-semibold text-card-foreground truncate flex items-center gap-1\",children:[r.jsx(\"span\",{className:\"truncate\",children:null==(t=\"folder\"!==F||y||$?s.path:s.path.split(\"/\").pop())?void 0:t.replace(/\\.md$/,\"\")}),n&&r.jsx(ve,{className:\"h-3 w-3 text-green-500 shrink-0\"})]}),r.jsxs(\"div\",{className:\"flex flex-wrap items-center gap-x-4 gap-y-1 mt-1.5 text-xs text-muted-foreground\",children:[r.jsx(ee,{content:I(\"ui.common.createdAt\"),side:\"top\",delay:300,children:r.jsxs(\"span\",{className:\"hidden sm:flex items-center gap-1\",children:[r.jsx(ke,{className:\"h-3.5 w-3.5\"}),de(new Date(s.ctime),\"yyyy-MM-dd HH:mm\")]})}),r.jsx(ee,{content:I(\"ui.common.updatedAt\"),side:\"top\",delay:300,children:r.jsxs(\"span\",{className:\"flex items-center gap-1\",children:[r.jsx(ye,{className:\"h-3.5 w-3.5\"}),r.jsx(\"span\",{className:\"sm:hidden\",children:de(new Date(s.mtime),\"MM-dd HH:mm\")}),r.jsx(\"span\",{className:\"hidden sm:inline\",children:de(new Date(s.mtime),\"yyyy-MM-dd HH:mm\")})]})}),s.version>0&&r.jsx(ee,{content:I(\"ui.history.title\"),side:\"top\",delay:300,children:r.jsxs(\"span\",{className:\"flex items-center gap-1\",children:[r.jsx(te,{className:\"h-3.5 w-3.5\"}),\"v\",s.version]})})]})]})]}),r.jsxs(\"div\",{className:\"flex items-center gap-1 shrink-0\",children:[r.jsx(ee,{content:I(\"ui.note.viewNote\"),side:\"top\",delay:200,children:r.jsx(a,{variant:\"ghost\",size:\"icon\",className:\"h-7 w-7 sm:h-8 sm:w-8 rounded-xl text-muted-foreground hover:text-primary\",onClick:e=>{e.stopPropagation(),d(s,!0)},children:r.jsx(he,{className:\"h-4 w-4\"})})}),r.jsx(ee,{content:I(\"ui.note.editNote\"),side:\"top\",delay:200,children:r.jsx(a,{variant:\"ghost\",size:\"icon\",className:\"hidden sm:inline-flex h-7 w-7 sm:h-8 sm:w-8 rounded-xl text-muted-foreground hover:text-blue-600\",onClick:e=>{e.stopPropagation(),d(s,!1)},children:r.jsx(me,{className:\"h-4 w-4\"})})}),r.jsx(ee,{content:I(\"ui.history.title\"),side:\"top\",delay:200,children:r.jsx(a,{variant:\"ghost\",size:\"icon\",className:\"hidden sm:inline-flex h-7 w-7 sm:h-8 sm:w-8 rounded-xl text-muted-foreground hover:text-purple-600\",onClick:e=>{e.stopPropagation(),f(s)},children:r.jsx(te,{className:\"h-4 w-4\"})})}),!y&&r.jsx(ee,{content:I(\"ui.common.rename\"),side:\"top\",delay:200,children:r.jsx(a,{variant:\"ghost\",size:\"icon\",className:\"h-7 w-7 sm:h-8 sm:w-8 rounded-xl text-muted-foreground hover:text-blue-500\",onClick:t=>((s,t)=>{s.stopPropagation();const a=t.path.split(\"/\").pop()||\"\",n=a.includes(\".\")?a.substring(a.lastIndexOf(\".\")):\".md\",i=a.includes(\".\")?a.substring(0,a.lastIndexOf(\".\")):a;let l=i;X(I(\"ui.note.renameNote\"),\"confirm\",()=>{if(!l||l===i)return;const s=l.endsWith(n)?l:l+n,r=t.path.includes(\"/\")?t.path.substring(0,t.path.lastIndexOf(\"/\")+1):\"\";J({vault:e,oldPath:t.path,path:r+s,oldPathHash:t.pathHash},()=>{rs()})},r.jsx(\"div\",{className:\"pt-2\",children:r.jsx(o,{autoFocus:!0,defaultValue:i,placeholder:I(\"ui.note.renameNotePlaceholder\"),onChange:e=>{l=e.target.value}})}))})(t,s),children:r.jsx(Pe,{className:\"h-4 w-4\"})})}),!y&&r.jsx(ee,{content:I(\"ui.share.title\"),side:\"top\",delay:200,children:r.jsx(a,{variant:\"ghost\",size:\"icon\",className:\"h-7 w-7 sm:h-8 sm:w-8 rounded-xl \"+(n?\"text-green-600 hover:text-green-700 bg-green-500/10\":\"text-muted-foreground hover:text-primary\"),onClick:e=>{e.stopPropagation(),Qe(s),Je(!0)},children:r.jsx(ve,{className:\"h-4 w-4\"})})}),!y&&r.jsx(ee,{content:I(\"ui.common.delete\"),side:\"top\",delay:200,children:r.jsx(a,{variant:\"ghost\",size:\"icon\",className:\"h-7 w-7 sm:h-8 sm:w-8 rounded-xl text-muted-foreground hover:text-destructive\",onClick:t=>((s,t)=>{s.stopPropagation();const r=t.path.replace(/\\.md$/,\"\");X(I(\"ui.note.deleteNoteConfirm\",{title:r}),\"confirm\",()=>{U(e,t.path,t.pathHash,()=>{rs()})})})(t,s),children:r.jsx(He,{className:\"h-4 w-4\"})})}),y&&r.jsxs(r.Fragment,{children:[r.jsx(ee,{content:I(\"ui.common.restore\"),side:\"top\",delay:200,children:r.jsx(a,{variant:\"ghost\",size:\"icon\",className:\"h-7 w-7 sm:h-8 sm:w-8 rounded-xl text-muted-foreground hover:text-green-600\",onClick:t=>((s,t)=>{s.stopPropagation();const r=t.path.replace(/\\.md$/,\"\");X(I(\"ui.note.restoreNoteConfirm\",{title:r}),\"confirm\",()=>{_(e,t.path,t.pathHash,()=>{rs()})})})(t,s),children:r.jsx(re,{className:\"h-4 w-4\"})})}),r.jsx(ee,{content:I(\"ui.common.permanentDelete\"),side:\"top\",delay:200,children:r.jsx(a,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 rounded-xl text-muted-foreground hover:text-destructive\",onClick:t=>((s,t)=>{s.stopPropagation();const r=t.path.replace(/\\.md$/,\"\");X(I(\"ui.note.permanentDeleteConfirm\",{title:r}),\"confirm\",()=>{G(e,t.path,t.pathHash,()=>{rs()})})})(t,s),children:r.jsx(He,{className:\"h-4 w-4\"})})})]})]})]})},`note-${s.pathHash}`)})]})}):r.jsx(\"div\",{className:\"rounded-xl border border-border bg-card p-12 text-center text-muted-foreground\",children:I(\"ui.note.noNotes\")}),se.length>0&&r.jsxs(\"div\",{className:\"flex flex-col sm:flex-row items-start sm:items-center sm:justify-between gap-4 pt-2 shrink-0\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-2 text-sm text-muted-foreground\",children:[r.jsxs(\"span\",{children:[I(\"ui.common.of\"),\" \",ce,\" \",I(\"ui.note.results\")]}),r.jsxs(xe,{value:x.toString(),onValueChange:e=>{const s=parseInt(e);p(s),u(1)},children:[r.jsx(pe,{className:\"h-8 w-25 rounded-xl\",children:r.jsx(fe,{placeholder:x.toString()})}),r.jsx(ge,{className:\"rounded-xl\",children:[10,20,50,100].map(e=>r.jsxs(je,{value:e.toString(),className:\"rounded-xl\",children:[e,\" \",I(\"ui.common.perPage\")]},e))})]})]}),r.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[r.jsxs(a,{variant:\"outline\",size:\"sm\",onClick:()=>as(h-1),disabled:1===h||le,className:\"rounded-xl\",children:[r.jsx(P,{className:\"h-4 w-4\"}),I(\"ui.common.previous\")]}),r.jsxs(\"span\",{className:\"text-sm font-medium px-2\",children:[h,\" / \",ns]}),r.jsxs(a,{variant:\"outline\",size:\"sm\",onClick:()=>as(h+1),disabled:h===ns||le,className:\"rounded-xl\",children:[I(\"ui.common.next\"),r.jsx(n,{className:\"h-4 w-4\"})]})]})]}),Ze&&r.jsx(Ke,{vault:e,path:Ze.path,pathHash:Ze.pathHash,open:qe,onOpenChange:Je,onShareChange:es})]})}const Ze=()=>document.querySelector(\"main\");function Qe({vault:e,onVaultChange:n,onNavigateToVaults:i,isMaximized:o=!1,onToggleMaximize:c,isRecycle:d=!1}){var m;const{t:h}=s(),[u,x]=t.useState(\"list\"),[p,f]=t.useState(void 0),[g,j]=t.useState(!1),[v,b]=t.useState([]),[N,w]=t.useState(!1),[y,k]=t.useState(null),C=t.useRef(!1),S=t.useRef(0),[H,P]=t.useState(1),[z,M]=t.useState(()=>{const e=localStorage.getItem(\"notePageSize\");return e?parseInt(e,10):10}),[L,T]=t.useState(\"\"),[R,A]=t.useState(\"\"),[$,D]=t.useState(\"\"),[F,O]=t.useState({}),[I,B]=t.useState(null),[U,_]=t.useState(\"folder\");t.useEffect(()=>{localStorage.setItem(\"notePageSize\",z.toString())},[z]);const{handleVaultList:W}=E();t.useEffect(()=>{let e=!0;return(async()=>{try{await W(s=>{e&&b(s)})}catch(s){if(!e)return;l.error(s instanceof Error?s.message:String(s)),b([])}finally{e&&(C.current=!0)}})(),()=>{e=!1}},[W]),t.useEffect(()=>{P(1),A(\"\"),D(\"\"),O({}),_(\"folder\"),B(null)},[e]),t.useLayoutEffect(()=>{var e;\"list\"===u&&S.current>0&&(null==(e=Ze())||e.scrollTo({top:S.current}))},[u]);const{handleNoteList:q}=V(),G=t.useCallback((e,s=!1)=>{var t;S.current=(null==(t=Ze())?void 0:t.scrollTop)??0,f(e),j(s),x(\"editor\")},[]),K=t.useCallback(s=>{const t=s.replace(/#.*$/,\"\").replace(/\\.md$/i,\"\").trim();t&&q(e,1,50,t,!1,\"path\",!1,\"mtime\",\"desc\",e=>{var s;if(!(null==(s=null==e?void 0:e.list)?void 0:s.length))return void l.info(h(\"ui.note.wikiLinkNotFound\",{target:t}));const r=e.list.find(e=>{const s=e.path.replace(/\\.md$/i,\"\");return s===t||s.endsWith(\"/\"+t)});r?G(r,!0):l.info(h(\"ui.note.wikiLinkNotFound\",{target:t}))})},[e,q,G,h]),J=()=>{x(\"list\"),f(void 0)},Z=e=>{k(e),w(!0)};return C.current&&0===v.length?r.jsxs(\"div\",{className:\"rounded-xl border border-border bg-card p-12 flex flex-col items-center justify-center\",children:[r.jsx(Se,{className:\"h-16 w-16 text-muted-foreground/30 mb-4\"}),r.jsx(\"h3\",{className:\"text-lg font-semibold text-foreground mb-2\",children:h(\"ui.note.noVaults\")}),r.jsx(\"p\",{className:\"text-muted-foreground mb-6 text-center\",children:h(\"ui.note.createVaultFirst\")}),r.jsx(a,{onClick:()=>{i&&i()},className:\"rounded-xl\",children:h(\"ui.note.goToVaultManagement\")})]}):r.jsxs(r.Fragment,{children:[r.jsx(\"div\",{hidden:\"editor\"===u,children:r.jsx(Je,{vault:e,vaults:v,onVaultChange:n,onSelectNote:G,onCreateNote:()=>{f(void 0),j(!1),x(\"editor\")},page:H,setPage:P,pageSize:z,setPageSize:M,searchKeyword:L,setSearchKeyword:T,onViewHistory:Z,isRecycle:d,currentPath:R,setCurrentPath:A,currentPathHash:$,setCurrentPathHash:D,pathHashMap:F,setPathHashMap:O,shareFilter:I,setShareFilter:B,viewMode:U,setViewMode:_})}),\"editor\"===u&&((null==(m=null==p?void 0:p.path)?void 0:m.endsWith(\".canvas\"))?r.jsx(le,{vault:e,note:p,onBack:J,onWikiLinkClick:K}):r.jsx(qe,{vault:e,note:p,onBack:J,onNavigateToFolder:e=>{A(e),D(F[e]||\"\"),P(1),x(\"list\"),f(void 0)},onSaveSuccess:(e,s)=>{p||f({id:Date.now(),path:e,pathHash:s,mtime:Date.now(),ctime:Date.now(),version:0})},onViewHistory:()=>p&&Z(p),isMaximized:o,onToggleMaximize:c,isRecycle:d,initialPreviewMode:g,onWikiLinkClick:K})),y&&r.jsx(Be,{isOpen:N,onClose:()=>{w(!1),k(null)},vault:e,notePath:y.path,pathHash:y.pathHash,isRecycle:d,onRestoreSuccess:()=>{p&&y&&p.pathHash===y.pathHash&&f({...p,version:(p.version||0)+1})}})]})}export{Qe as NoteManager};\n"
  },
  {
    "path": "frontend/assets/pencil-DqQhr35g.js",
    "content": "import{c as a}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const o=a(\"Pencil\",[[\"path\",{d:\"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z\",key:\"1a8usu\"}],[\"path\",{d:\"m15 5 4 4\",key:\"1mk7zo\"}]]);export{o as P};\n"
  },
  {
    "path": "frontend/assets/plus-BBfuNxDX.js",
    "content": "import{c as s}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const a=s(\"Plus\",[[\"path\",{d:\"M5 12h14\",key:\"1ays0h\"}],[\"path\",{d:\"M12 5v14\",key:\"s699le\"}]]);export{a as P};\n"
  },
  {
    "path": "frontend/assets/refresh-cw-BxIJAPy3.js",
    "content": "import{c as a}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const t=a(\"RefreshCw\",[[\"path\",{d:\"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\",key:\"v9h5vc\"}],[\"path\",{d:\"M21 3v5h-5\",key:\"1q7to0\"}],[\"path\",{d:\"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\",key:\"3uifl3\"}],[\"path\",{d:\"M8 16H3v5\",key:\"1cv678\"}]]);export{t as R};\n"
  },
  {
    "path": "frontend/assets/search-DdihTHF8.js",
    "content": "import{c}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const e=c(\"Search\",[[\"circle\",{cx:\"11\",cy:\"11\",r:\"8\",key:\"4ej97u\"}],[\"path\",{d:\"m21 21-4.3-4.3\",key:\"1qie3q\"}]]);export{e as S};\n"
  },
  {
    "path": "frontend/assets/select-CJF_alSt.js",
    "content": "import{r as e,j as t,aj as n,aD as o,af as r,ag as a,aE as l,ah as s,ai as i,aF as c,aB as d,aG as u,aH as p,an as f,aI as h,g as m,aJ as v,aK as g,aL as w,aM as x,aN as y,aO as b,aP as S,aQ as C,aR as j,f as N,a5 as R}from\"./font-loader-CIrh3KnA.js\";import{e as P,k as _}from\"./main-BIi-kGYY.js\";function I(t){const n=e.useRef({value:t,previous:t});return e.useMemo(()=>(n.current.value!==t&&(n.current.previous=n.current.value,n.current.value=t),n.current.previous),[t])}function T(e,[t,n]){return Math.min(n,Math.max(t,e))}var k=Object.freeze({position:\"absolute\",border:0,width:1,height:1,padding:0,margin:-1,overflow:\"hidden\",clip:\"rect(0, 0, 0, 0)\",whiteSpace:\"nowrap\",wordWrap:\"normal\"});e.forwardRef((e,o)=>t.jsx(n.span,{...e,ref:o,style:{...k,...e.style}})).displayName=\"VisuallyHidden\";var D=[\" \",\"Enter\",\"ArrowUp\",\"ArrowDown\"],E=[\" \",\"Enter\"],M=\"Select\",[L,O,H]=c(M),[A]=s(M,[H,o]),V=o(),[B,K]=A(M),[F,W]=A(M),z=n=>{const{__scopeSelect:o,children:s,open:c,defaultOpen:d,onOpenChange:u,value:p,defaultValue:f,onValueChange:h,dir:m,name:v,autoComplete:g,disabled:w,required:x,form:y}=n,b=V(o),[S,C]=e.useState(null),[j,N]=e.useState(null),[R,P]=e.useState(!1),_=r(m),[I,T]=a({prop:c,defaultProp:d??!1,onChange:u,caller:M}),[k,D]=a({prop:p,defaultProp:f,onChange:h,caller:M}),E=e.useRef(null),O=!S||(y||!!S.closest(\"form\")),[H,A]=e.useState(new Set),K=Array.from(H).map(e=>e.props.value).join(\";\");return t.jsx(l,{...b,children:t.jsxs(B,{required:x,scope:o,trigger:S,onTriggerChange:C,valueNode:j,onValueNodeChange:N,valueNodeHasChildren:R,onValueNodeHasChildrenChange:P,contentId:i(),value:k,onValueChange:D,open:I,onOpenChange:T,dir:_,triggerPointerDownPosRef:E,disabled:w,children:[t.jsx(L.Provider,{scope:o,children:t.jsx(F,{scope:n.__scopeSelect,onNativeOptionAdd:e.useCallback(e=>{A(t=>new Set(t).add(e))},[]),onNativeOptionRemove:e.useCallback(e=>{A(t=>{const n=new Set(t);return n.delete(e),n})},[]),children:s})}),O?t.jsxs(Te,{\"aria-hidden\":!0,required:x,tabIndex:-1,name:v,autoComplete:g,value:k,onChange:e=>D(e.target.value),disabled:w,form:y,children:[void 0===k?t.jsx(\"option\",{value:\"\"}):null,Array.from(H)]},K):null]})})};z.displayName=M;var U=\"SelectTrigger\",q=e.forwardRef((o,r)=>{const{__scopeSelect:a,disabled:l=!1,...s}=o,i=V(a),c=K(U,a),u=c.disabled||l,h=d(r,c.onTriggerChange),m=O(a),v=e.useRef(\"touch\"),[g,w,x]=De(e=>{const t=m().filter(e=>!e.disabled),n=t.find(e=>e.value===c.value),o=Ee(t,e,n);void 0!==o&&c.onValueChange(o.value)}),y=e=>{u||(c.onOpenChange(!0),x()),e&&(c.triggerPointerDownPosRef.current={x:Math.round(e.pageX),y:Math.round(e.pageY)})};return t.jsx(p,{asChild:!0,...i,children:t.jsx(n.button,{type:\"button\",role:\"combobox\",\"aria-controls\":c.contentId,\"aria-expanded\":c.open,\"aria-required\":c.required,\"aria-autocomplete\":\"none\",dir:c.dir,\"data-state\":c.open?\"open\":\"closed\",disabled:u,\"data-disabled\":u?\"\":void 0,\"data-placeholder\":ke(c.value)?\"\":void 0,...s,ref:h,onClick:f(s.onClick,e=>{e.currentTarget.focus(),\"mouse\"!==v.current&&y(e)}),onPointerDown:f(s.onPointerDown,e=>{v.current=e.pointerType;const t=e.target;t.hasPointerCapture(e.pointerId)&&t.releasePointerCapture(e.pointerId),0===e.button&&!1===e.ctrlKey&&\"mouse\"===e.pointerType&&(y(e),e.preventDefault())}),onKeyDown:f(s.onKeyDown,e=>{const t=\"\"!==g.current;e.ctrlKey||e.altKey||e.metaKey||1!==e.key.length||w(e.key),t&&\" \"===e.key||D.includes(e.key)&&(y(),e.preventDefault())})})})});q.displayName=U;var G=\"SelectValue\",X=e.forwardRef((e,o)=>{const{__scopeSelect:r,className:a,style:l,children:s,placeholder:i=\"\",...c}=e,p=K(G,r),{onValueNodeHasChildrenChange:f}=p,h=void 0!==s,m=d(o,p.onValueNodeChange);return u(()=>{f(h)},[f,h]),t.jsx(n.span,{...c,ref:m,style:{pointerEvents:\"none\"},children:ke(p.value)?t.jsx(t.Fragment,{children:i}):s})});X.displayName=G;var Y=e.forwardRef((e,o)=>{const{__scopeSelect:r,children:a,...l}=e;return t.jsx(n.span,{\"aria-hidden\":!0,...l,ref:o,children:a||\"▼\"})});Y.displayName=\"SelectIcon\";var J=e=>t.jsx(h,{asChild:!0,...e});J.displayName=\"SelectPortal\";var Q=\"SelectContent\",Z=e.forwardRef((n,o)=>{const r=K(Q,n.__scopeSelect),[a,l]=e.useState();if(u(()=>{l(new DocumentFragment)},[]),!r.open){const e=a;return e?m.createPortal(t.jsx(ee,{scope:n.__scopeSelect,children:t.jsx(L.Slot,{scope:n.__scopeSelect,children:t.jsx(\"div\",{children:n.children})})}),e):null}return t.jsx(oe,{...n,ref:o})});Z.displayName=Q;var $=10,[ee,te]=A(Q),ne=y(\"SelectContent.RemoveScroll\"),oe=e.forwardRef((n,o)=>{const{__scopeSelect:r,position:a=\"item-aligned\",onCloseAutoFocus:l,onEscapeKeyDown:s,onPointerDownOutside:i,side:c,sideOffset:u,align:p,alignOffset:h,arrowPadding:m,collisionBoundary:v,collisionPadding:y,sticky:C,hideWhenDetached:j,avoidCollisions:N,...R}=n,P=K(Q,r),[_,I]=e.useState(null),[T,k]=e.useState(null),D=d(o,e=>I(e)),[E,M]=e.useState(null),[L,H]=e.useState(null),A=O(r),[V,B]=e.useState(!1),F=e.useRef(!1);e.useEffect(()=>{if(_)return g(_)},[_]),w();const W=e.useCallback(e=>{const[t,...n]=A().map(e=>e.ref.current),[o]=n.slice(-1),r=document.activeElement;for(const a of e){if(a===r)return;if(null==a||a.scrollIntoView({block:\"nearest\"}),a===t&&T&&(T.scrollTop=0),a===o&&T&&(T.scrollTop=T.scrollHeight),null==a||a.focus(),document.activeElement!==r)return}},[A,T]),z=e.useCallback(()=>W([E,_]),[W,E,_]);e.useEffect(()=>{V&&z()},[V,z]);const{onOpenChange:U,triggerPointerDownPosRef:q}=P;e.useEffect(()=>{if(_){let e={x:0,y:0};const t=t=>{var n,o;e={x:Math.abs(Math.round(t.pageX)-((null==(n=q.current)?void 0:n.x)??0)),y:Math.abs(Math.round(t.pageY)-((null==(o=q.current)?void 0:o.y)??0))}},n=n=>{e.x<=10&&e.y<=10?n.preventDefault():_.contains(n.target)||U(!1),document.removeEventListener(\"pointermove\",t),q.current=null};return null!==q.current&&(document.addEventListener(\"pointermove\",t),document.addEventListener(\"pointerup\",n,{capture:!0,once:!0})),()=>{document.removeEventListener(\"pointermove\",t),document.removeEventListener(\"pointerup\",n,{capture:!0})}}},[_,U,q]),e.useEffect(()=>{const e=()=>U(!1);return window.addEventListener(\"blur\",e),window.addEventListener(\"resize\",e),()=>{window.removeEventListener(\"blur\",e),window.removeEventListener(\"resize\",e)}},[U]);const[G,X]=De(e=>{const t=A().filter(e=>!e.disabled),n=t.find(e=>e.ref.current===document.activeElement),o=Ee(t,e,n);o&&setTimeout(()=>o.ref.current.focus())}),Y=e.useCallback((e,t,n)=>{const o=!F.current&&!n;(void 0!==P.value&&P.value===t||o)&&(M(e),o&&(F.current=!0))},[P.value]),J=e.useCallback(()=>null==_?void 0:_.focus(),[_]),Z=e.useCallback((e,t,n)=>{const o=!F.current&&!n;(void 0!==P.value&&P.value===t||o)&&H(e)},[P.value]),$=\"popper\"===a?ae:re,te=$===ae?{side:c,sideOffset:u,align:p,alignOffset:h,arrowPadding:m,collisionBoundary:v,collisionPadding:y,sticky:C,hideWhenDetached:j,avoidCollisions:N}:{};return t.jsx(ee,{scope:r,content:_,viewport:T,onViewportChange:k,itemRefCallback:Y,selectedItem:E,onItemLeave:J,itemTextRefCallback:Z,focusSelectedItem:z,selectedItemText:L,position:a,isPositioned:V,searchRef:G,children:t.jsx(x,{as:ne,allowPinchZoom:!0,children:t.jsx(b,{asChild:!0,trapped:P.open,onMountAutoFocus:e=>{e.preventDefault()},onUnmountAutoFocus:f(l,e=>{var t;null==(t=P.trigger)||t.focus({preventScroll:!0}),e.preventDefault()}),children:t.jsx(S,{asChild:!0,disableOutsidePointerEvents:!0,onEscapeKeyDown:s,onPointerDownOutside:i,onFocusOutside:e=>e.preventDefault(),onDismiss:()=>P.onOpenChange(!1),children:t.jsx($,{role:\"listbox\",id:P.contentId,\"data-state\":P.open?\"open\":\"closed\",dir:P.dir,onContextMenu:e=>e.preventDefault(),...R,...te,onPlaced:()=>B(!0),ref:D,style:{display:\"flex\",flexDirection:\"column\",outline:\"none\",...R.style},onKeyDown:f(R.onKeyDown,e=>{const t=e.ctrlKey||e.altKey||e.metaKey;if(\"Tab\"===e.key&&e.preventDefault(),t||1!==e.key.length||X(e.key),[\"ArrowUp\",\"ArrowDown\",\"Home\",\"End\"].includes(e.key)){let t=A().filter(e=>!e.disabled).map(e=>e.ref.current);if([\"ArrowUp\",\"End\"].includes(e.key)&&(t=t.slice().reverse()),[\"ArrowUp\",\"ArrowDown\"].includes(e.key)){const n=e.target,o=t.indexOf(n);t=t.slice(o+1)}setTimeout(()=>W(t)),e.preventDefault()}})})})})})})});oe.displayName=\"SelectContentImpl\";var re=e.forwardRef((o,r)=>{const{__scopeSelect:a,onPlaced:l,...s}=o,i=K(Q,a),c=te(Q,a),[p,f]=e.useState(null),[h,m]=e.useState(null),v=d(r,e=>m(e)),g=O(a),w=e.useRef(!1),x=e.useRef(!0),{viewport:y,selectedItem:b,selectedItemText:S,focusSelectedItem:C}=c,j=e.useCallback(()=>{if(i.trigger&&i.valueNode&&p&&h&&y&&b&&S){const e=i.trigger.getBoundingClientRect(),t=h.getBoundingClientRect(),n=i.valueNode.getBoundingClientRect(),o=S.getBoundingClientRect();if(\"rtl\"!==i.dir){const r=o.left-t.left,a=n.left-r,l=e.left-a,s=e.width+l,i=Math.max(s,t.width),c=window.innerWidth-$,d=T(a,[$,Math.max($,c-i)]);p.style.minWidth=s+\"px\",p.style.left=d+\"px\"}else{const r=t.right-o.right,a=window.innerWidth-n.right-r,l=window.innerWidth-e.right-a,s=e.width+l,i=Math.max(s,t.width),c=window.innerWidth-$,d=T(a,[$,Math.max($,c-i)]);p.style.minWidth=s+\"px\",p.style.right=d+\"px\"}const r=g(),a=window.innerHeight-2*$,s=y.scrollHeight,c=window.getComputedStyle(h),d=parseInt(c.borderTopWidth,10),u=parseInt(c.paddingTop,10),f=parseInt(c.borderBottomWidth,10),m=d+u+s+parseInt(c.paddingBottom,10)+f,v=Math.min(5*b.offsetHeight,m),x=window.getComputedStyle(y),C=parseInt(x.paddingTop,10),j=parseInt(x.paddingBottom,10),N=e.top+e.height/2-$,R=a-N,P=b.offsetHeight/2,_=d+u+(b.offsetTop+P),I=m-_;if(_<=N){const e=r.length>0&&b===r[r.length-1].ref.current;p.style.bottom=\"0px\";const t=h.clientHeight-y.offsetTop-y.offsetHeight,n=_+Math.max(R,P+(e?j:0)+t+f);p.style.height=n+\"px\"}else{const e=r.length>0&&b===r[0].ref.current;p.style.top=\"0px\";const t=Math.max(N,d+y.offsetTop+(e?C:0)+P)+I;p.style.height=t+\"px\",y.scrollTop=_-N+y.offsetTop}p.style.margin=`${$}px 0`,p.style.minHeight=v+\"px\",p.style.maxHeight=a+\"px\",null==l||l(),requestAnimationFrame(()=>w.current=!0)}},[g,i.trigger,i.valueNode,p,h,y,b,S,i.dir,l]);u(()=>j(),[j]);const[N,R]=e.useState();u(()=>{h&&R(window.getComputedStyle(h).zIndex)},[h]);const P=e.useCallback(e=>{e&&!0===x.current&&(j(),null==C||C(),x.current=!1)},[j,C]);return t.jsx(le,{scope:a,contentWrapper:p,shouldExpandOnScrollRef:w,onScrollButtonChange:P,children:t.jsx(\"div\",{ref:f,style:{display:\"flex\",flexDirection:\"column\",position:\"fixed\",zIndex:N},children:t.jsx(n.div,{...s,ref:v,style:{boxSizing:\"border-box\",maxHeight:\"100%\",...s.style}})})})});re.displayName=\"SelectItemAlignedPosition\";var ae=e.forwardRef((e,n)=>{const{__scopeSelect:o,align:r=\"start\",collisionPadding:a=$,...l}=e,s=V(o);return t.jsx(C,{...s,...l,ref:n,align:r,collisionPadding:a,style:{boxSizing:\"border-box\",...l.style,\"--radix-select-content-transform-origin\":\"var(--radix-popper-transform-origin)\",\"--radix-select-content-available-width\":\"var(--radix-popper-available-width)\",\"--radix-select-content-available-height\":\"var(--radix-popper-available-height)\",\"--radix-select-trigger-width\":\"var(--radix-popper-anchor-width)\",\"--radix-select-trigger-height\":\"var(--radix-popper-anchor-height)\"}})});ae.displayName=\"SelectPopperPosition\";var[le,se]=A(Q,{}),ie=\"SelectViewport\",ce=e.forwardRef((o,r)=>{const{__scopeSelect:a,nonce:l,...s}=o,i=te(ie,a),c=se(ie,a),u=d(r,i.onViewportChange),p=e.useRef(0);return t.jsxs(t.Fragment,{children:[t.jsx(\"style\",{dangerouslySetInnerHTML:{__html:\"[data-radix-select-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-select-viewport]::-webkit-scrollbar{display:none}\"},nonce:l}),t.jsx(L.Slot,{scope:a,children:t.jsx(n.div,{\"data-radix-select-viewport\":\"\",role:\"presentation\",...s,ref:u,style:{position:\"relative\",flex:1,overflow:\"hidden auto\",...s.style},onScroll:f(s.onScroll,e=>{const t=e.currentTarget,{contentWrapper:n,shouldExpandOnScrollRef:o}=c;if((null==o?void 0:o.current)&&n){const e=Math.abs(p.current-t.scrollTop);if(e>0){const o=window.innerHeight-2*$,r=parseFloat(n.style.minHeight),a=parseFloat(n.style.height),l=Math.max(r,a);if(l<o){const r=l+e,a=Math.min(o,r),s=r-a;n.style.height=a+\"px\",\"0px\"===n.style.bottom&&(t.scrollTop=s>0?s:0,n.style.justifyContent=\"flex-end\")}}}p.current=t.scrollTop})})})]})});ce.displayName=ie;var de=\"SelectGroup\",[ue,pe]=A(de);e.forwardRef((e,o)=>{const{__scopeSelect:r,...a}=e,l=i();return t.jsx(ue,{scope:r,id:l,children:t.jsx(n.div,{role:\"group\",\"aria-labelledby\":l,...a,ref:o})})}).displayName=de;var fe=\"SelectLabel\",he=e.forwardRef((e,o)=>{const{__scopeSelect:r,...a}=e,l=pe(fe,r);return t.jsx(n.div,{id:l.id,...a,ref:o})});he.displayName=fe;var me=\"SelectItem\",[ve,ge]=A(me),we=e.forwardRef((o,r)=>{const{__scopeSelect:a,value:l,disabled:s=!1,textValue:c,...u}=o,p=K(me,a),h=te(me,a),m=p.value===l,[v,g]=e.useState(c??\"\"),[w,x]=e.useState(!1),y=d(r,e=>{var t;return null==(t=h.itemRefCallback)?void 0:t.call(h,e,l,s)}),b=i(),S=e.useRef(\"touch\"),C=()=>{s||(p.onValueChange(l),p.onOpenChange(!1))};if(\"\"===l)throw new Error(\"A <Select.Item /> must have a value prop that is not an empty string. This is because the Select value can be set to an empty string to clear the selection and show the placeholder.\");return t.jsx(ve,{scope:a,value:l,disabled:s,textId:b,isSelected:m,onItemTextChange:e.useCallback(e=>{g(t=>t||((null==e?void 0:e.textContent)??\"\").trim())},[]),children:t.jsx(L.ItemSlot,{scope:a,value:l,disabled:s,textValue:v,children:t.jsx(n.div,{role:\"option\",\"aria-labelledby\":b,\"data-highlighted\":w?\"\":void 0,\"aria-selected\":m&&w,\"data-state\":m?\"checked\":\"unchecked\",\"aria-disabled\":s||void 0,\"data-disabled\":s?\"\":void 0,tabIndex:s?void 0:-1,...u,ref:y,onFocus:f(u.onFocus,()=>x(!0)),onBlur:f(u.onBlur,()=>x(!1)),onClick:f(u.onClick,()=>{\"mouse\"!==S.current&&C()}),onPointerUp:f(u.onPointerUp,()=>{\"mouse\"===S.current&&C()}),onPointerDown:f(u.onPointerDown,e=>{S.current=e.pointerType}),onPointerMove:f(u.onPointerMove,e=>{var t;S.current=e.pointerType,s?null==(t=h.onItemLeave)||t.call(h):\"mouse\"===S.current&&e.currentTarget.focus({preventScroll:!0})}),onPointerLeave:f(u.onPointerLeave,e=>{var t;e.currentTarget===document.activeElement&&(null==(t=h.onItemLeave)||t.call(h))}),onKeyDown:f(u.onKeyDown,e=>{var t;\"\"!==(null==(t=h.searchRef)?void 0:t.current)&&\" \"===e.key||(E.includes(e.key)&&C(),\" \"===e.key&&e.preventDefault())})})})})});we.displayName=me;var xe=\"SelectItemText\",ye=e.forwardRef((o,r)=>{const{__scopeSelect:a,className:l,style:s,...i}=o,c=K(xe,a),p=te(xe,a),f=ge(xe,a),h=W(xe,a),[v,g]=e.useState(null),w=d(r,e=>g(e),f.onItemTextChange,e=>{var t;return null==(t=p.itemTextRefCallback)?void 0:t.call(p,e,f.value,f.disabled)}),x=null==v?void 0:v.textContent,y=e.useMemo(()=>t.jsx(\"option\",{value:f.value,disabled:f.disabled,children:x},f.value),[f.disabled,f.value,x]),{onNativeOptionAdd:b,onNativeOptionRemove:S}=h;return u(()=>(b(y),()=>S(y)),[b,S,y]),t.jsxs(t.Fragment,{children:[t.jsx(n.span,{id:f.textId,...i,ref:w}),f.isSelected&&c.valueNode&&!c.valueNodeHasChildren?m.createPortal(i.children,c.valueNode):null]})});ye.displayName=xe;var be=\"SelectItemIndicator\",Se=e.forwardRef((e,o)=>{const{__scopeSelect:r,...a}=e;return ge(be,r).isSelected?t.jsx(n.span,{\"aria-hidden\":!0,...a,ref:o}):null});Se.displayName=be;var Ce=\"SelectScrollUpButton\",je=e.forwardRef((n,o)=>{const r=te(Ce,n.__scopeSelect),a=se(Ce,n.__scopeSelect),[l,s]=e.useState(!1),i=d(o,a.onScrollButtonChange);return u(()=>{if(r.viewport&&r.isPositioned){let e=function(){const e=t.scrollTop>0;s(e)};const t=r.viewport;return e(),t.addEventListener(\"scroll\",e),()=>t.removeEventListener(\"scroll\",e)}},[r.viewport,r.isPositioned]),l?t.jsx(Pe,{...n,ref:i,onAutoScroll:()=>{const{viewport:e,selectedItem:t}=r;e&&t&&(e.scrollTop=e.scrollTop-t.offsetHeight)}}):null});je.displayName=Ce;var Ne=\"SelectScrollDownButton\",Re=e.forwardRef((n,o)=>{const r=te(Ne,n.__scopeSelect),a=se(Ne,n.__scopeSelect),[l,s]=e.useState(!1),i=d(o,a.onScrollButtonChange);return u(()=>{if(r.viewport&&r.isPositioned){let e=function(){const e=t.scrollHeight-t.clientHeight,n=Math.ceil(t.scrollTop)<e;s(n)};const t=r.viewport;return e(),t.addEventListener(\"scroll\",e),()=>t.removeEventListener(\"scroll\",e)}},[r.viewport,r.isPositioned]),l?t.jsx(Pe,{...n,ref:i,onAutoScroll:()=>{const{viewport:e,selectedItem:t}=r;e&&t&&(e.scrollTop=e.scrollTop+t.offsetHeight)}}):null});Re.displayName=Ne;var Pe=e.forwardRef((o,r)=>{const{__scopeSelect:a,onAutoScroll:l,...s}=o,i=te(\"SelectScrollButton\",a),c=e.useRef(null),d=O(a),p=e.useCallback(()=>{null!==c.current&&(window.clearInterval(c.current),c.current=null)},[]);return e.useEffect(()=>()=>p(),[p]),u(()=>{var e;const t=d().find(e=>e.ref.current===document.activeElement);null==(e=null==t?void 0:t.ref.current)||e.scrollIntoView({block:\"nearest\"})},[d]),t.jsx(n.div,{\"aria-hidden\":!0,...s,ref:r,style:{flexShrink:0,...s.style},onPointerDown:f(s.onPointerDown,()=>{null===c.current&&(c.current=window.setInterval(l,50))}),onPointerMove:f(s.onPointerMove,()=>{var e;null==(e=i.onItemLeave)||e.call(i),null===c.current&&(c.current=window.setInterval(l,50))}),onPointerLeave:f(s.onPointerLeave,()=>{p()})})}),_e=e.forwardRef((e,o)=>{const{__scopeSelect:r,...a}=e;return t.jsx(n.div,{\"aria-hidden\":!0,...a,ref:o})});_e.displayName=\"SelectSeparator\";var Ie=\"SelectArrow\";e.forwardRef((e,n)=>{const{__scopeSelect:o,...r}=e,a=V(o),l=K(Ie,o),s=te(Ie,o);return l.open&&\"popper\"===s.position?t.jsx(j,{...a,...r,ref:n}):null}).displayName=Ie;var Te=e.forwardRef(({__scopeSelect:o,value:r,...a},l)=>{const s=e.useRef(null),i=d(l,s),c=I(r);return e.useEffect(()=>{const e=s.current;if(!e)return;const t=window.HTMLSelectElement.prototype,n=Object.getOwnPropertyDescriptor(t,\"value\").set;if(c!==r&&n){const t=new Event(\"change\",{bubbles:!0});n.call(e,r),e.dispatchEvent(t)}},[c,r]),t.jsx(n.select,{...a,style:{...k,...a.style},ref:i,defaultValue:r})});function ke(e){return\"\"===e||void 0===e}function De(t){const n=v(t),o=e.useRef(\"\"),r=e.useRef(0),a=e.useCallback(e=>{const t=o.current+e;n(t),function e(t){o.current=t,window.clearTimeout(r.current),\"\"!==t&&(r.current=window.setTimeout(()=>e(\"\"),1e3))}(t)},[n]),l=e.useCallback(()=>{o.current=\"\",window.clearTimeout(r.current)},[]);return e.useEffect(()=>()=>window.clearTimeout(r.current),[]),[o,a,l]}function Ee(e,t,n){const o=t.length>1&&Array.from(t).every(e=>e===t[0])?t[0]:t,r=n?e.indexOf(n):-1;let a=(l=e,s=Math.max(r,0),l.map((e,t)=>l[(s+t)%l.length]));var l,s;1===o.length&&(a=a.filter(e=>e!==n));const i=a.find(e=>e.textValue.toLowerCase().startsWith(o.toLowerCase()));return i!==n?i:void 0}Te.displayName=\"SelectBubbleInput\";var Me=q,Le=Y,Oe=J,He=Z,Ae=ce,Ve=he,Be=we,Ke=ye,Fe=Se,We=je,ze=Re,Ue=_e;const qe=z,Ge=X,Xe=e.forwardRef(({className:e,children:n,...o},r)=>t.jsxs(Me,{ref:r,className:N(\"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1\",e),...o,children:[n,t.jsx(Le,{asChild:!0,children:t.jsx(P,{className:\"h-4 w-4 opacity-50\"})})]}));Xe.displayName=Me.displayName;const Ye=e.forwardRef(({className:e,...n},o)=>t.jsx(We,{ref:o,className:N(\"flex cursor-pointer items-center justify-center py-1\",e),...n,children:t.jsx(_,{className:\"h-4 w-4\"})}));Ye.displayName=We.displayName;const Je=e.forwardRef(({className:e,...n},o)=>t.jsx(ze,{ref:o,className:N(\"flex cursor-pointer items-center justify-center py-1\",e),...n,children:t.jsx(P,{className:\"h-4 w-4\"})}));Je.displayName=ze.displayName;const Qe=e.forwardRef(({className:e,children:n,position:o=\"popper\",...r},a)=>t.jsx(Oe,{children:t.jsxs(He,{ref:a,className:N(\"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\"popper\"===o&&\"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1\",e),position:o,...r,children:[t.jsx(Ye,{}),t.jsx(Ae,{className:N(\"p-1\",\"popper\"===o&&\"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]\"),children:n}),t.jsx(Je,{})]})}));Qe.displayName=He.displayName;e.forwardRef(({className:e,...n},o)=>t.jsx(Ve,{ref:o,className:N(\"py-1.5 pl-8 pr-2 text-sm font-semibold\",e),...n})).displayName=Ve.displayName;const Ze=e.forwardRef(({className:e,children:n,...o},r)=>t.jsxs(Be,{ref:r,className:N(\"relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",e),...o,children:[t.jsx(\"span\",{className:\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\",children:t.jsx(Fe,{children:t.jsx(R,{className:\"h-4 w-4\"})})}),t.jsx(Ke,{children:n})]}));Ze.displayName=Be.displayName;e.forwardRef(({className:e,...n},o)=>t.jsx(Ue,{ref:o,className:N(\"-mx-1 my-1 h-px bg-muted\",e),...n})).displayName=Ue.displayName;export{qe as S,Xe as a,Ge as b,Qe as c,Ze as d,I as u};\n"
  },
  {
    "path": "frontend/assets/server-DzJVVqse.js",
    "content": "import{c as e}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const y=e(\"Server\",[[\"rect\",{width:\"20\",height:\"8\",x:\"2\",y:\"2\",rx:\"2\",ry:\"2\",key:\"ngkwjq\"}],[\"rect\",{width:\"20\",height:\"8\",x:\"2\",y:\"14\",rx:\"2\",ry:\"2\",key:\"iecqi9\"}],[\"line\",{x1:\"6\",x2:\"6.01\",y1:\"6\",y2:\"6\",key:\"16zg32\"}],[\"line\",{x1:\"6\",x2:\"6.01\",y1:\"18\",y2:\"18\",key:\"nzw8ys\"}]]);export{y as S};\n"
  },
  {
    "path": "frontend/assets/server-DzJVVqse.js.br",
    "content": "\u001b\u0001\u0002 ^yt6QS\u0005\u00029Y9\u0012K\u0005(i>XwQS\u001ce}}\u0016nN(\u0001ŊmPB\t4\u0015ﵶ\u0019\u0013}GI,K\u0001\u000bmwks+{q>-i2\bpQZeM@t\u0004͙\u000f1j\u001c\u0011TWP2F~\u0001X\t7g@Ư\u0011!\u00169sFU6*P%$+Gƪ\u000b{Գ\u000b\u0014ץq\u0014zhf R䨘JS3\u0016E|M\u001eÞ/|("
  },
  {
    "path": "frontend/assets/setting-manager-DaP9o-yD.js",
    "content": "import{c as e,r as s,b as t,a,e as r,t as n,u as o,a7 as i,j as l,B as c,C as d,I as m,X as h}from\"./font-loader-CIrh3KnA.js\";import{F as u,C as x,f as p}from\"./main-BIi-kGYY.js\";import{R as g,E as f,m as j}from\"./index-JfsWWBj_.js\";import{T as w}from\"./tooltip-Dr-qRlmI.js\";import{T as N,a as v,b,c as y,d as k,e as S}from\"./table-D9wbHMTA.js\";import{F as C}from\"./file-type-DbD_pFnN.js\";import{F as B,f as E}from\"./format-CdHm7RWL.js\";import{C as M}from\"./clock-C9LPHszx.js\";import{T as P}from\"./text-cursor-input-Bphfsfyn.js\";import{P as R}from\"./pencil-DqQhr35g.js\";import{T as A}from\"./trash-2-ad7PiUnC.js\";import{I as L,F as I}from\"./image-BFJJNQpe.js\";import{S as $,a as F,b as z,c as H,d as T}from\"./select-CJF_alSt.js\";import{S as U}from\"./search-DdihTHF8.js\";import{R as G}from\"./refresh-cw-BxIJAPy3.js\";import{P as O}from\"./plus-BBfuNxDX.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const V=e(\"FileBox\",[[\"path\",{d:\"M14.5 22H18a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4\",key:\"16lz6z\"}],[\"path\",{d:\"M14 2v4a2 2 0 0 0 2 2h4\",key:\"tnqrlb\"}],[\"path\",{d:\"M3 13.1a2 2 0 0 0-1 1.76v3.24a2 2 0 0 0 .97 1.78L6 21.7a2 2 0 0 0 2.03.01L11 19.9a2 2 0 0 0 1-1.76V14.9a2 2 0 0 0-.97-1.78L8 11.3a2 2 0 0 0-2.03-.01Z\",key:\"99pj1s\"}],[\"path\",{d:\"M7 17v5\",key:\"1yj1jh\"}],[\"path\",{d:\"M11.7 14.2 7 17l-4.7-2.8\",key:\"1yk8tc\"}]]);function D({vault:e,searchKeyword:h,currentPage:p,onPageChange:$,refreshSignal:F,onRegisterAdd:z}){const{t:H}=o(),{handleSettingList:T,handleSaveSetting:U,handleDeleteSetting:G,handleRenameSetting:O}=function(){const e=localStorage.getItem(\"token\"),o=s.useCallback(()=>({...t({token:e})}),[e]),i=s.useCallback(async(e,s,t,i,l=1,c=24)=>{try{const d=a(`${r.API_URL}/api/settings?vault=${encodeURIComponent(e)}&page=${l}&pageSize=${c}${i?`&keyword=${encodeURIComponent(i)}`:\"\"}`),m=await fetch(d,{method:\"GET\",headers:o()});if(!m.ok)throw new Error(\"Network response was not ok\");const h=await m.json();h.code>0&&h.code<=200?s(h.data||{list:[],pager:{totalRows:0,page:l,pageSize:c}}):(n.error(h.message),t&&t())}catch(d){n.error(d instanceof Error?d.message:String(d)),t&&t()}},[o]),l=s.useCallback(async(e,s,t,i)=>{try{const l=a(`${r.API_URL}/api/setting?vault=${encodeURIComponent(e)}&path=${encodeURIComponent(s)}${t?`&pathHash=${t}`:\"\"}`),c=await fetch(l,{method:\"GET\",headers:o()});if(!c.ok)throw new Error(\"Network response was not ok\");const d=await c.json();return d.code>0&&d.code<=200&&d.data?(null==i||i(d.data),d.data):(n.error(d.message),null)}catch(l){return n.error(l instanceof Error?l.message:String(l)),null}},[o]),c=s.useCallback(async(e,s,t)=>{try{const i={...s,vault:e},l=await fetch(a(`${r.API_URL}/api/setting`),{method:\"POST\",body:JSON.stringify(i),headers:o()});if(!l.ok)throw new Error(\"Network response was not ok\");const c=await l.json();c.code>0&&c.code<=200?(n.success(c.message),t()):n.error(c.message)}catch(i){n.error(i instanceof Error?i.message:String(i))}},[o]),d=s.useCallback(async(e,s,t,i)=>{try{const l=await fetch(a(`${r.API_URL}/api/setting`),{method:\"DELETE\",body:JSON.stringify({vault:e,path:s,pathHash:t}),headers:o()});if(!l.ok)throw new Error(\"Network response was not ok\");const c=await l.json();c.code>0&&c.code<=200?(n.success(c.message),null==i||i()):n.error(c.message)}catch(l){n.error(l instanceof Error?l.message:String(l))}},[o]),m=s.useCallback(async(e,s,t,i,l)=>{try{const c=await fetch(a(`${r.API_URL}/api/setting/rename`),{method:\"POST\",body:JSON.stringify({vault:e,oldPath:s,newPath:t,oldPathHash:i}),headers:o()});if(!c.ok)throw new Error(\"Network response was not ok\");const d=await c.json();d.code>0&&d.code<=200?(n.success(d.message),null==l||l()):n.error(d.message)}catch(c){n.error(c instanceof Error?c.message:String(c))}},[o]);return s.useMemo(()=>({handleSettingList:i,handleGetSetting:l,handleSaveSetting:c,handleDeleteSetting:d,handleRenameSetting:m}),[i,l,c,d,m])}(),{openConfirmDialog:D}=i(),[_,K]=s.useState([]),[J,W]=s.useState(!1),[q,Q]=s.useState(0),Z=s.useCallback(()=>{e&&(W(!0),T(e,e=>{var s;K(e.list||[]),Q((null==(s=e.pager)?void 0:s.totalRows)||0),W(!1)},()=>{W(!1)},h,p,20))},[T,e,h,p]);s.useEffect(()=>{Z()},[Z,F]);const X=_,Y=e=>[j()],ee=s.useCallback(()=>{let t=\"\",a=\"\";const r=({initialPath:e})=>{const[r,n]=s.useState(e),o=s.useMemo(()=>Y(),[r]);return l.jsxs(\"div\",{className:\"space-y-4 pt-2\",children:[l.jsxs(\"div\",{className:\"space-y-2\",children:[l.jsx(\"label\",{className:\"text-sm font-medium\",children:H(\"ui.settingsBrowser.key\")}),l.jsx(m,{autoFocus:!0,placeholder:\"e.g. system/config.json\",onChange:e=>{t=e.target.value,n(e.target.value)}})]}),l.jsxs(\"div\",{className:\"space-y-2\",children:[l.jsx(\"label\",{className:\"text-sm font-medium\",children:H(\"ui.settingsBrowser.content\")}),l.jsx(\"div\",{className:\"border border-border rounded-xl overflow-hidden min-h-[300px] focus-within:ring-2 focus-within:ring-primary/20\",children:l.jsx(g,{height:\"300px\",theme:\"dark\",extensions:[...o,f.lineWrapping],onChange:e=>{a=e},basicSetup:{lineNumbers:!0,foldGutter:!0,dropCursor:!0,allowMultipleSelections:!0,indentOnInput:!0,bracketMatching:!0,closeBrackets:!0,highlightActiveLine:!0}})})]})]})};D(H(\"ui.settingsBrowser.add\"),\"confirm\",()=>{t&&U(e,{path:t,content:a},Z)},l.jsx(r,{initialPath:\"\"}),\"max-w-3xl\")},[e,Z,D,H]);s.useEffect(()=>{z(ee)},[ee,z]);const se=Math.ceil(q/20),te=e=>{if(0===e)return\"0 B\";const s=Math.floor(Math.log(e)/Math.log(1024));return parseFloat((e/Math.pow(1024,s)).toFixed(2))+\" \"+[\"B\",\"KB\",\"MB\",\"GB\"][s]},ae=e=>{var s;switch(null==(s=e.split(\".\").pop())?void 0:s.toLowerCase()){case\"json\":return l.jsx(u,{className:\"h-4 w-4 text-orange-500/70\"});case\"js\":case\"ts\":case\"jsx\":case\"tsx\":return l.jsx(I,{className:\"h-4 w-4 text-blue-500/70\"});case\"css\":case\"scss\":case\"less\":return l.jsx(C,{className:\"h-4 w-4 text-pink-500/70\"});case\"md\":case\"txt\":return l.jsx(B,{className:\"h-4 w-4 text-emerald-500/70\"});case\"yml\":case\"yaml\":return l.jsx(V,{className:\"h-4 w-4 text-purple-500/70\"});case\"png\":case\"jpg\":case\"jpeg\":case\"gif\":case\"svg\":case\"webp\":return l.jsx(L,{className:\"h-4 w-4 text-indigo-500/70\"});default:return l.jsx(B,{className:\"h-4 w-4 text-primary/70\"})}};return l.jsx(\"div\",{className:\"w-full flex flex-col space-y-4\",children:J&&0===_.length?l.jsxs(\"div\",{className:\"rounded-xl border border-border bg-card p-24 text-center\",children:[l.jsx(\"div\",{className:\"animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto\"}),l.jsx(\"p\",{className:\"mt-4 text-muted-foreground\",children:H(\"ui.common.loading\")})]}):0===X.length?l.jsxs(\"div\",{className:\"rounded-xl border border-border bg-card p-24 text-center\",children:[l.jsx(\"div\",{className:\"p-4 rounded-full bg-muted w-16 h-16 flex items-center justify-center mx-auto mb-4\",children:l.jsx(u,{className:\"h-8 w-8 text-muted-foreground\"})}),l.jsx(\"p\",{className:\"text-lg font-medium text-foreground\",children:H(\"ui.settingsBrowser.noSettings\")})]}):l.jsxs(l.Fragment,{children:[l.jsx(\"div\",{className:\"rounded-xl border border-border/50 bg-card/50 backdrop-blur-sm overflow-hidden shadow-sm overflow-x-auto\",children:l.jsxs(N,{children:[l.jsx(v,{className:\"bg-muted/50\",children:l.jsxs(b,{children:[l.jsx(y,{className:\"min-w-[300px] py-4\",children:l.jsxs(\"div\",{className:\"flex items-center gap-2 px-2\",children:[l.jsx(C,{className:\"h-3.5 w-3.5 text-muted-foreground\"}),\" \",H(\"ui.settingsBrowser.key\")]})}),l.jsx(y,{className:\"w-[120px] py-4\",children:l.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[l.jsx(B,{className:\"h-3.5 w-3.5 text-muted-foreground\"}),\" \",H(\"ui.settingsBrowser.value\")]})}),l.jsx(y,{className:\"w-[180px] py-4\",children:l.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[l.jsx(M,{className:\"h-3.5 w-3.5 text-muted-foreground\"}),\" \",H(\"ui.common.updatedAt\")]})}),l.jsx(y,{className:\"w-[150px] py-4 text-right px-6\",children:H(\"ui.common.actions\")})]})}),l.jsx(k,{children:X.map(s=>{var t;const a=s.path.split(\"/\"),r=a.pop()||\"\",n=a.join(\"/\");return l.jsxs(b,{className:\"group hover:bg-muted/30 transition-colors border-border/40\",children:[l.jsx(S,{className:\"py-3 px-4\",children:l.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[l.jsx(\"div\",{className:\"flex-shrink-0 w-8 h-8 rounded-lg bg-primary/5 flex items-center justify-center group-hover:bg-primary/10 transition-colors\",children:ae(r)}),l.jsxs(\"div\",{className:\"flex flex-col min-w-0\",children:[l.jsxs(\"div\",{className:\"flex items-center gap-1 text-sm\",children:[n&&l.jsxs(\"span\",{className:\"text-muted-foreground/60\",children:[n,\"/\"]}),l.jsx(\"span\",{className:\"font-bold text-foreground\",children:r})]}),l.jsx(\"span\",{className:\"text-[10px] text-muted-foreground/40 font-mono truncate max-w-[200px]\",children:s.pathHash})]})]})}),l.jsx(S,{className:\"py-3\",children:l.jsx(\"div\",{className:\"px-2 py-0.5 rounded bg-muted/40 text-[10px] font-mono text-muted-foreground inline-block\",children:te((null==(t=s.content)?void 0:t.length)||0)})}),l.jsx(S,{className:\"py-3 text-xs text-muted-foreground\",children:(s.updatedAt||s.createdAt)&&E(new Date(s.updatedAt||s.createdAt||\"\"),\"yyyy-MM-dd HH:mm\")}),l.jsx(S,{className:\"py-3 text-right px-4\",children:l.jsxs(\"div\",{className:\"flex items-center justify-end gap-1 opacity-0 group-hover:opacity-100 transition-opacity\",children:[l.jsx(w,{content:H(\"ui.settingsBrowser.rename\"),side:\"top\",children:l.jsx(c,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 rounded-lg hover:bg-primary/10 hover:text-primary transition-all\",onClick:()=>(s=>{let t=s.path;D(H(\"ui.settingsBrowser.rename\"),\"confirm\",()=>{t&&t!==s.path&&O(e,s.path,t,s.pathHash,Z)},l.jsxs(\"div\",{className:\"space-y-2 pt-2\",children:[l.jsx(\"label\",{className:\"text-sm font-medium\",children:H(\"ui.settingsBrowser.newKey\")}),l.jsx(m,{autoFocus:!0,defaultValue:s.path,onChange:e=>{t=e.target.value}})]}))})(s),children:l.jsx(P,{className:\"h-4 w-4\"})})}),l.jsx(w,{content:H(\"ui.common.edit\"),side:\"top\",children:l.jsx(c,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 rounded-lg hover:bg-primary/10 hover:text-primary transition-all\",onClick:()=>(s=>{let t=s.content;const a=Y(s.path);D(H(\"ui.settingsBrowser.edit\")+\": \"+s.path,\"confirm\",()=>{U(e,{...s,content:t},Z)},l.jsxs(\"div\",{className:\"space-y-2 pt-2\",children:[l.jsx(\"label\",{className:\"text-sm font-medium\",children:H(\"ui.settingsBrowser.content\")}),l.jsx(\"div\",{className:\"border border-border rounded-xl overflow-hidden min-h-[450px] focus-within:ring-2 focus-within:ring-primary/20\",children:l.jsx(g,{autoFocus:!0,value:s.content,height:\"450px\",theme:\"dark\",extensions:[...a,f.lineWrapping],onChange:e=>{t=e},basicSetup:{lineNumbers:!0,foldGutter:!0,dropCursor:!0,allowMultipleSelections:!0,indentOnInput:!0,bracketMatching:!0,closeBrackets:!0,highlightActiveLine:!0}})})]}),\"max-w-4xl\")})(s),children:l.jsx(R,{className:\"h-4 w-4\"})})}),l.jsx(w,{content:H(\"ui.common.delete\"),side:\"top\",children:l.jsx(c,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 rounded-lg text-destructive/70 hover:text-destructive hover:bg-destructive/10 transition-all\",onClick:()=>(s=>{D(H(\"ui.settingsBrowser.confirmDelete\",{key:s.path}),\"confirm\",()=>{G(e,s.path,s.pathHash,Z)})})(s),children:l.jsx(A,{className:\"h-4 w-4\"})})})]})})]},s.pathHash||s.path)})})]})}),se>1&&l.jsxs(\"div\",{className:\"flex items-center justify-center gap-2 mt-6\",children:[l.jsx(c,{variant:\"outline\",size:\"icon\",className:\"h-9 w-9 rounded-xl\",disabled:1===p||J,onClick:()=>$(p-1),children:l.jsx(x,{className:\"h-4 w-4\"})}),l.jsxs(\"div\",{className:\"flex items-center gap-1 px-4 py-1.5 bg-muted/50 rounded-xl border border-border/50 shadow-inner\",children:[l.jsx(\"span\",{className:\"text-sm font-semibold\",children:p}),l.jsx(\"span\",{className:\"text-sm text-muted-foreground\",children:\"/\"}),l.jsx(\"span\",{className:\"text-sm text-muted-foreground\",children:se})]}),l.jsx(c,{variant:\"outline\",size:\"icon\",className:\"h-9 w-9 rounded-xl\",disabled:p===se||J,onClick:()=>$(p+1),children:l.jsx(d,{className:\"h-4 w-4\"})})]})]})})}function _({vault:e,onVaultChange:t}){const{t:a}=o(),{handleVaultList:r}=p(),[n,i]=s.useState([]),[d,x]=s.useState(\"\"),[g,f]=s.useState(1),[j,w]=s.useState(0),[N,v]=s.useState(null);s.useEffect(()=>{r(e=>{i(e)})},[r]);return l.jsxs(\"div\",{className:\"w-full space-y-6 pt-2 animate-in fade-in slide-in-from-bottom-4 duration-500\",children:[l.jsxs(\"div\",{className:\"flex flex-col lg:flex-row items-start lg:items-center justify-between gap-4\",children:[l.jsxs(\"div\",{className:\"flex items-center gap-4\",children:[l.jsx(\"div\",{className:\"flex h-12 w-12 items-center justify-center rounded-2xl bg-primary/10 text-primary shadow-inner\",children:l.jsx(u,{className:\"h-6 w-6\"})}),l.jsxs(\"div\",{children:[l.jsx(\"h2\",{className:\"text-2xl font-bold tracking-tight\",children:a(\"ui.settingsBrowser.title\")}),l.jsx(\"p\",{className:\"text-sm text-muted-foreground\",children:a(\"ui.settingsBrowser.description\")})]})]}),l.jsxs(\"div\",{className:\"flex flex-wrap items-center gap-2 w-full lg:w-auto\",children:[l.jsxs(\"div\",{className:\"relative flex-1 lg:w-64 group min-w-[200px]\",children:[l.jsx(U,{className:\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none group-focus-within:text-primary transition-colors\"}),l.jsx(m,{type:\"text\",placeholder:a(\"ui.common.search\"),className:\"h-10 pl-9 pr-10 rounded-xl bg-card border-border hover:bg-muted/50 focus:border-primary/50 focus:ring-primary/20 transition-all font-medium shadow-sm\",value:d,onChange:e=>{x(e.target.value),f(1)}}),d&&l.jsx(\"button\",{className:\"absolute right-3 top-1/2 -translate-y-1/2 p-1 text-muted-foreground hover:text-foreground transition-colors\",onClick:()=>{x(\"\"),f(1)},children:l.jsx(h,{className:\"h-3.5 w-3.5\"})})]}),l.jsx(\"div\",{className:\"flex flex-col gap-1.5 min-w-[140px]\",children:l.jsxs($,{value:e,onValueChange:e=>{t(e),f(1)},children:[l.jsx(F,{className:\"rounded-xl h-10 bg-card border border-border hover:bg-muted/50 transition-all shadow-sm font-medium\",children:l.jsx(z,{placeholder:a(\"ui.common.selectVault\")})}),l.jsx(H,{className:\"rounded-xl shadow-xl\",children:n.map(e=>l.jsx(T,{value:e.vault,className:\"rounded-lg\",children:e.vault},e.id))})]})}),l.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[l.jsx(c,{variant:\"outline\",size:\"icon\",className:\"h-10 w-10 rounded-xl bg-card border-border hover:bg-muted/50 transition-all shadow-sm\",onClick:()=>{w(e=>e+1)},title:a(\"ui.common.refresh\"),children:l.jsx(G,{className:\"h-4 w-4\"})}),l.jsxs(c,{onClick:()=>null==N?void 0:N(),className:\"h-10 rounded-xl shadow-lg shadow-primary/20 hover:shadow-primary/30 transition-all font-bold px-4\",children:[l.jsx(O,{className:\"h-4 w-4 mr-1.5\"}),l.jsx(\"span\",{children:a(\"ui.common.add\")})]})]})]})]}),l.jsx(D,{vault:e,searchKeyword:d,currentPage:g,onPageChange:f,refreshSignal:j,onRegisterAdd:e=>v(()=>e)})]})}export{_ as SettingManager};\n"
  },
  {
    "path": "frontend/assets/share-2-BVJjAadJ.js",
    "content": "import{c}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const e=c(\"Share2\",[[\"circle\",{cx:\"18\",cy:\"5\",r:\"3\",key:\"gq8acd\"}],[\"circle\",{cx:\"6\",cy:\"12\",r:\"3\",key:\"w7nqdw\"}],[\"circle\",{cx:\"18\",cy:\"19\",r:\"3\",key:\"1xt0gg\"}],[\"line\",{x1:\"8.59\",x2:\"15.42\",y1:\"13.51\",y2:\"17.49\",key:\"47mynk\"}],[\"line\",{x1:\"15.41\",x2:\"8.59\",y1:\"6.51\",y2:\"10.49\",key:\"1n3mei\"}]]);export{e as S};\n"
  },
  {
    "path": "frontend/assets/share-CN7oeKGv.js",
    "content": "const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=[\"assets/font-loader-CIrh3KnA.js\",\"assets/font-loader-B-ynJ_1p.css\"])))=>i.map(i=>d[i]);\nimport{c as e,u as s,G as a,$ as t,r,_ as l,j as n,a0 as i,B as c,N as o,K as d,L as m,I as h,f as x,J as u,S as p,M as j,H as g,n as f,o as w,q as N,F as v,s as y,w as k,x as b,a1 as S,z,A as C,a2 as L,E as I,t as E,a3 as M,a4 as T,e as _,a as P,b as R,Q as A,U as F,V as H,W as B,Y as D,Z as U}from\"./font-loader-CIrh3KnA.js\";import{u as q}from\"./note-handle-IK8dQjtF.js\";import{T as K}from\"./tooltip-Dr-qRlmI.js\";import{C as V,M as W}from\"./markdown-editor-CX5kQlgI.js\";import{S as O}from\"./share-2-BVJjAadJ.js\";import{R as $}from\"./refresh-cw-BxIJAPy3.js\";import{F as G,f as J}from\"./format-CdHm7RWL.js\";import\"./index-JfsWWBj_.js\";import\"./zap-CLLhzk_y.js\";import\"./pencil-DqQhr35g.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const Q=e(\"EllipsisVertical\",[[\"circle\",{cx:\"12\",cy:\"12\",r:\"1\",key:\"41hilf\"}],[\"circle\",{cx:\"12\",cy:\"5\",r:\"1\",key:\"gxeob9\"}],[\"circle\",{cx:\"12\",cy:\"19\",r:\"1\",key:\"lyex9k\"}]]),Y=e(\"MoveHorizontal\",[[\"path\",{d:\"m18 8 4 4-4 4\",key:\"1ak13k\"}],[\"path\",{d:\"M2 12h20\",key:\"9i4pu4\"}],[\"path\",{d:\"m6 8-4 4 4 4\",key:\"15zrgr\"}]]);\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */function Z(){var e;const{t:F}=s(),{theme:H,resolvedTheme:B,setTheme:D}=a(),{handleGetShareNote:U}=q(),{colorScheme:Z}=t(),[X,ee]=r.useState(null),[se,ae]=r.useState(null),[te,re]=r.useState(!0),[le,ne]=r.useState(null),[ie,ce]=r.useState(\"\"),[oe,de]=r.useState(\"\"),[me,he]=r.useState(!1),[xe,ue]=r.useState(null),[pe,je]=r.useState(null),[ge,fe]=r.useState(()=>\"true\"===localStorage.getItem(\"share-is-full-width\"));r.useEffect(()=>{localStorage.setItem(\"share-is-full-width\",String(ge))},[ge]),r.useEffect(()=>{const e=localStorage.getItem(\"share-lang\");e&&l(()=>import(\"./font-loader-CIrh3KnA.js\").then(e=>e.aS),__vite__mapDeps([0,1])).then(s=>s.changeLang(e,\"share-lang\"));let s=!0;return(async()=>{try{const e=_.API_URL.endsWith(\"/\")?_.API_URL.slice(0,-1):_.API_URL,a=await fetch(P(`${e}/api/webgui/config`),{headers:R({token:null,includeContentType:!1,includeDomain:!1})});if(a.ok&&s){const e=await a.json();e.code>0&&e.data&&A(e.data.fontSet||e.data.FontSet||\"\")}}catch(e){}})(),()=>{s=!1}},[]);const we=r.useCallback(()=>{const e=window.location.pathname,s=window.location.search.substring(1);let a=null,t=null;const r=e.split(\"/\").filter(Boolean),l=r.findIndex(e=>e.toLowerCase().includes(\"share.html\")||\"share\"===e.toLowerCase());if(-1!==l&&r.length>=l+3)a=r[l+1],t=r[l+2];else if(s.includes(\"/\")){const e=s.split(\"/\");a=e[0],t=e[1]}else{const e=new URLSearchParams(window.location.search);a=e.get(\"id\"),t=e.get(\"Share-Token\")||e.get(\"token\")}if(!a||!t)return ne(F(\"ui.share.invalidLink\")),void re(!1);if(ue(a),je(t),!ie){const e=localStorage.getItem(\"share-common-password\"),s=localStorage.getItem(\"share-common-password-id\");if(e&&s===a)return void ce(e)}re(!0),U(a,t,ie,e=>{ee(e),he(!1),ne(null),re(!1),ie&&(localStorage.setItem(\"share-common-password\",ie),localStorage.setItem(\"share-common-password-id\",a))},(e,s)=>{ae(e),483===e?(he(!0),ne(null)):484===e?(he(!1),ne(s),localStorage.removeItem(\"share-common-password\"),localStorage.removeItem(\"share-common-password-id\")):ne(s),re(!1)})},[U,F,ie]),Ne=e=>{e.preventDefault(),ce(oe)};return r.useEffect(()=>{we()},[we]),r.useEffect(()=>{var e;if(X){const s=(null==(e=X.path.split(\"/\").pop())?void 0:e.replace(\".md\",\"\"))||\"Note\";document.title=`${s} - Fast Note Sync`}},[X]),r.useEffect(()=>{const e=window.matchMedia(\"(prefers-color-scheme: dark)\"),s=e=>{const s=e?\"/static/images/icon.svg\":\"/static/images/icon-black.svg\";[\"link[rel='icon']\",\"link[rel='shortcut icon']\",\"link[rel='apple-touch-icon']\",\"link[rel='apple-touch-icon-precomposed']\"].forEach(e=>{document.querySelectorAll(e).forEach(e=>{var a;const t=e.cloneNode(!0);t.href=s,null==(a=e.parentNode)||a.replaceChild(t,e)})})};s(e.matches);const a=e=>s(e.matches);try{e.addEventListener(\"change\",a)}catch(t){e.addListener(a)}return()=>{try{e.removeEventListener(\"change\",a)}catch(t){e.removeListener(a)}}},[]),te?n.jsx(\"div\",{className:\"flex h-screen w-screen items-center justify-center bg-background\",children:n.jsxs(\"div\",{className:\"flex flex-col items-center gap-4\",children:[n.jsx(i,{className:\"h-10 w-10 animate-spin text-primary\"}),n.jsx(\"p\",{className:\"text-muted-foreground animate-pulse\",children:F(\"ui.common.loading\")})]})}):le||!me&&!X?n.jsx(\"div\",{className:\"flex h-screen w-screen items-center justify-center bg-background p-6\",children:n.jsxs(\"div\",{className:\"max-w-md text-center\",children:[n.jsx(\"div\",{className:\"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-destructive/10\",children:n.jsx(O,{className:\"h-8 w-8 text-destructive\"})}),n.jsx(\"h1\",{className:\"mb-2 text-2xl font-bold\",children:F(\"ui.share.errorTitle\")}),n.jsx(\"p\",{className:\"text-muted-foreground\",children:le||F(\"ui.share.noteNotFound\")}),n.jsxs(c,{className:\"mt-6 rounded-xl\",variant:\"outline\",onClick:()=>{483===se||484===se?(he(!0),ne(null),ae(null)):window.location.reload()},children:[n.jsx($,{className:\"mr-2 h-4 w-4\"}),F(\"ui.common.retry\")]})]})}):me&&!X?n.jsxs(\"div\",{className:\"flex h-screen w-screen items-center justify-center bg-background p-6\",children:[\"default\"===Z&&n.jsx(o,{}),n.jsx(\"div\",{className:\"z-10 w-full max-w-sm\",children:n.jsxs(\"div\",{className:\"relative rounded-3xl border bg-card p-8 shadow-xl backdrop-blur-sm\",children:[n.jsx(\"div\",{className:\"absolute top-4 right-4\",children:n.jsx(d,{storageKey:\"share-lang\"})}),n.jsxs(\"div\",{className:\"mb-6 text-center\",children:[n.jsx(\"div\",{className:\"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-2xl bg-primary/10 text-primary\",children:n.jsx(m,{className:\"h-8 w-8\"})}),n.jsx(\"h1\",{className:\"text-2xl font-bold\",children:F(\"ui.share.passwordRequired\",\"Password Required\")}),n.jsx(\"p\",{className:\"mt-2 text-sm text-muted-foreground\",children:F(\"ui.share.passwordHint\",\"This note is password protected. Please enter the password to view it.\")})]}),n.jsxs(\"form\",{onSubmit:Ne,className:\"space-y-4\",children:[n.jsx(\"div\",{className:\"space-y-2\",children:n.jsx(h,{type:\"password\",placeholder:F(\"ui.share.passwordPlaceholder\",\"Enter password...\"),value:oe,onChange:e=>de(e.target.value),className:\"h-12 rounded-xl text-center text-lg tracking-widest focus-visible:ring-primary\",autoFocus:!0})}),n.jsx(c,{type:\"submit\",className:\"h-12 w-full rounded-xl text-lg font-semibold shadow-lg shadow-primary/20\",children:F(\"ui.common.confirm\")})]})]})})]}):X?n.jsxs(\"div\",{className:\"min-h-screen bg-background relative overflow-x-hidden\",children:[\"default\"===Z&&n.jsx(o,{}),n.jsxs(\"div\",{className:\"relative z-10 flex flex-col min-h-screen\",children:[n.jsx(\"header\",{className:\"border-b bg-card/50 backdrop-blur-md sticky top-0 z-20 px-4 py-3 sm:px-6\",children:n.jsxs(\"div\",{className:x(\"mx-auto flex items-center justify-between gap-4 transition-all duration-300\",ge?\"max-w-none\":\"max-w-5xl\"),children:[n.jsxs(\"div\",{className:\"flex items-center gap-3 min-w-0\",children:[n.jsx(\"div\",{className:\"flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary\",children:n.jsx(G,{className:\"h-5 w-5\"})}),n.jsxs(\"div\",{className:\"min-w-0\",children:[n.jsx(\"h1\",{className:\"truncate text-lg font-bold sm:text-xl\",children:null==(e=X.path.split(\"/\").pop())?void 0:e.replace(\".md\",\"\")}),n.jsxs(\"div\",{className:\"flex items-center gap-3 text-xs text-muted-foreground\",children:[n.jsxs(\"span\",{className:\"flex items-center gap-1\",children:[n.jsx(V,{className:\"h-3 w-3\"}),J(new Date(X.mtime),\"yyyy-MM-dd HH:mm\")]}),n.jsxs(\"span\",{className:\"hidden sm:inline-flex items-center gap-1\",children:[F(\"ui.share.version\"),\" v\",X.version]})]})]})]}),n.jsxs(\"div\",{className:\"flex items-center gap-1 sm:gap-2\",children:[n.jsxs(\"div\",{className:\"hidden sm:flex items-center gap-1 sm:gap-2\",children:[n.jsx(K,{content:F(ge?\"ui.common.narrowMode\":\"ui.common.wideMode\"),children:n.jsx(c,{variant:\"ghost\",size:\"icon\",className:x(\"size-9 rounded-xl transition-colors\",ge&&\"bg-primary/10 text-primary\"),onClick:()=>fe(!ge),children:n.jsx(Y,{className:\"size-5\"})})}),n.jsx(K,{content:F(\"ui.common.refresh\"),children:n.jsx(c,{variant:\"ghost\",size:\"icon\",className:\"size-9 rounded-xl\",onClick:we,disabled:te,children:n.jsx($,{className:x(\"size-5\",te&&\"animate-spin\")})})}),n.jsx(K,{content:F(\"ui.settings.colorScheme\"),children:n.jsx(\"div\",{children:n.jsx(u,{className:\"rounded-xl\",isShare:!0})})}),n.jsx(K,{content:F(\"ui.common.switchLanguage\"),children:n.jsx(\"div\",{children:n.jsx(d,{className:\"size-9 rounded-xl\",storageKey:\"share-lang\"})})})]}),n.jsx(K,{content:F(\"auto\"===H?\"ui.settings.themeAuto\":\"dark\"===B?\"ui.settings.themeDark\":\"ui.settings.themeLight\"),children:n.jsx(c,{variant:\"ghost\",size:\"icon\",className:\"size-9 rounded-xl\",onClick:()=>{D(\"light\"===H?\"dark\":\"dark\"===H?\"auto\":\"light\")},children:\"auto\"===H?n.jsx(p,{className:\"size-5 text-primary\"}):\"dark\"===B?n.jsx(j,{className:\"size-5\"}):n.jsx(g,{className:\"size-5\"})})}),n.jsx(\"div\",{className:\"flex sm:hidden\",children:n.jsxs(f,{children:[n.jsx(w,{asChild:!0,children:n.jsx(c,{variant:\"ghost\",size:\"icon\",className:\"size-9 rounded-xl\",children:n.jsx(Q,{className:\"size-5\"})})}),n.jsxs(N,{align:\"end\",className:\"w-48 rounded-xl\",children:[n.jsxs(v,{onClick:we,className:\"rounded-lg cursor-pointer\",children:[n.jsx($,{className:x(\"mr-2 h-4 w-4\",te&&\"animate-spin\")}),F(\"ui.common.refresh\")]}),n.jsx(y,{}),n.jsxs(k,{children:[n.jsxs(b,{className:\"rounded-lg cursor-pointer\",children:[n.jsx(S,{className:\"mr-2 h-4 w-4\"}),F(\"ui.settings.colorScheme\")]}),n.jsx(z,{className:\"rounded-xl w-48\",children:n.jsx(C,{value:Z,onValueChange:e=>{t.getState().setColorScheme(e);const s=L.find(s=>s.value===e);s&&E.success(F(\"ui.settings.colorSchemeSwitched\",{scheme:F(s.label)}))},children:L.map(e=>n.jsxs(I,{value:e.value,className:\"rounded-lg cursor-pointer\",children:[n.jsx(\"span\",{className:\"mr-2 flex h-2 w-2 rounded-full\",style:{backgroundColor:e.color}}),F(e.label)]},e.value))})})]}),n.jsxs(k,{children:[n.jsxs(b,{className:\"rounded-lg cursor-pointer\",children:[n.jsx(M,{className:\"mr-2 h-4 w-4\"}),F(\"ui.common.switchLanguage\")]}),n.jsxs(z,{className:\"rounded-xl\",children:[n.jsx(v,{onClick:()=>T(\"en\",\"share-lang\"),children:\"🇺🇸 English\"}),n.jsx(v,{onClick:()=>T(\"zh-CN\",\"share-lang\"),children:\"🇨🇳 简体中文\"}),n.jsx(v,{onClick:()=>T(\"zh-TW\",\"share-lang\"),children:\"🇭🇰 繁體中文\"}),n.jsx(v,{onClick:()=>T(\"ja\",\"share-lang\"),children:\"🇯🇵 日本語\"}),n.jsx(v,{onClick:()=>T(\"ko\",\"share-lang\"),children:\"🇰🇷 한국어\"})]})]})]})]})})]})]})}),n.jsx(\"main\",{className:\"flex-1 overflow-visible p-4 sm:p-6 lg:p-8\",children:n.jsx(\"div\",{className:x(\"mx-auto rounded-2xl border bg-card shadow-sm transition-all duration-300\",ge?\"max-w-none\":\"max-w-5xl\"),children:n.jsx(W,{value:X.content,readOnly:!0,initialMode:\"preview\",vault:\"\",fileLinks:X.fileLinks,fullWidth:ge,autoHeight:!0,shareId:xe||\"\",shareToken:pe||\"\",password:ie})})}),n.jsx(\"footer\",{className:\"border-t bg-muted/30 py-6 px-4 text-center\",children:n.jsx(\"div\",{className:\"mx-auto max-w-5xl\",children:n.jsxs(\"p\",{className:\"text-sm text-muted-foreground flex items-center justify-center\",children:[n.jsx(\"span\",{children:F(\"ui.share.poweredByPrefix\")}),n.jsx(\"a\",{href:\"https://github.com/haierkeys/fast-note-sync-service\",target:\"_blank\",rel:\"noreferrer\",className:\"font-semibold text-primary hover:underline mx-1\",children:\"Fast Note Sync\"}),n.jsx(\"span\",{children:F(\"ui.share.poweredBySuffix\")})]})})})]})]}):null}(()=>{const e=localStorage.getItem(\"share-settings\");if(e)try{const{state:s}=JSON.parse(e);(null==s?void 0:s.colorScheme)&&document.documentElement.setAttribute(\"data-color-scheme\",s.colorScheme)}catch{}})(),F.createRoot(document.getElementById(\"root\")).render(n.jsx(H.StrictMode,{children:n.jsx(B,{defaultTheme:\"auto\",storageKey:\"share-theme\",children:n.jsxs(D,{children:[n.jsx(Z,{}),n.jsx(U,{})]})})}));\n"
  },
  {
    "path": "frontend/assets/shield-check-CH_gKEpx.js",
    "content": "import{c as a}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const o=a(\"Play\",[[\"polygon\",{points:\"6 3 20 12 6 21 6 3\",key:\"1oa8hb\"}]]),e=a(\"ShieldCheck\",[[\"path\",{d:\"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z\",key:\"oel41y\"}],[\"path\",{d:\"m9 12 2 2 4-4\",key:\"dzmm74\"}]]);\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */export{o as P,e as S};\n"
  },
  {
    "path": "frontend/assets/sync-backup-Bp7n2yHp.js",
    "content": "import{u as e,a7 as s,r as t,a,e as r,b as o,t as i,j as n,ae as l,a0 as c,f as d,B as u,C as m,I as x,ad as p,a6 as h,aA as g,a5 as b}from\"./font-loader-CIrh3KnA.js\";import{T as f,a as j,b as y,c as N,d as v,e as k}from\"./table-D9wbHMTA.js\";import{D as w,a as S,b as E,c as C,E as P,C as I,L as U,f as T,i as D}from\"./main-BIi-kGYY.js\";import{C as B,a as F}from\"./copy-CEhXannp.js\";import{S as R,a as L,b as V,c as z,d as $}from\"./select-CJF_alSt.js\";import{o as _,a as A,s as K,e as M,b as O,n as q,u as J,t as G}from\"./zod-B54Zg8Xp.js\";import{C as H}from\"./checkbox-DhTHgmeh.js\";import{a as W,E as Q}from\"./eye-DrvrOb4o.js\";import{S as X,P as Y}from\"./shield-check-CH_gKEpx.js\";import{T as Z}from\"./tooltip-Dr-qRlmI.js\";import{R as ee}from\"./refresh-cw-BxIJAPy3.js\";import{P as se}from\"./plus-BBfuNxDX.js\";import{H as te}from\"./history-BseqF3eb.js\";import{P as ae}from\"./pencil-DqQhr35g.js\";import{T as re}from\"./trash-2-ad7PiUnC.js\";import{C as oe}from\"./circle-alert-EFzISefA.js\";import{C as ie}from\"./clock-C9LPHszx.js\";import{H as ne}from\"./hard-drive-Dw58lXyp.js\";import{S as le}from\"./server-DzJVVqse.js\";import{S as ce}from\"./share-2-BVJjAadJ.js\";function de(){const{t:n}=e(),{openConfirmDialog:l}=s(),c=localStorage.getItem(\"token\");return{handleStorageList:t.useCallback(async e=>{try{const s=await fetch(a(r.API_URL+\"/api/storage?limit=100\"),{method:\"GET\",headers:o({token:c})});if(!s.ok)throw new Error(\"Network response was not ok\");const t=await s.json();t.code<100&&t.code>0?e(t.data||[]):l(t.message+\": \"+t.details,\"error\")}catch(s){l(n(\"api.storage.list.error\")+\": \"+s,\"error\")}},[c,l,n]),handleStorageDelete:async e=>{try{const s=await fetch(a(r.API_URL+\"/api/storage\"),{method:\"DELETE\",body:JSON.stringify({id:e}),headers:o({token:c})});if(!s.ok)throw new Error(\"Network response was not ok\");const t=await s.json();t.code<100&&t.code>0?l(t.message||n(\"api.storage.delete.success\"),\"success\"):l(t.message+\": \"+t.details,\"error\")}catch(s){l(n(\"api.storage.delete.error\")+\": \"+s,\"error\")}},handleStorageUpdate:async(e,s)=>{try{const t={...e,isEnabled:e.isEnabled?1:0},i=await fetch(a(r.API_URL+\"/api/storage\"),{method:\"POST\",body:JSON.stringify(t),headers:o({token:c})});if(!i.ok)throw new Error(\"Network response was not ok\");const n=await i.json();n.code<100&&n.code>0?(l(n.message,\"success\"),e.id=n.data,s(e)):l(n.message+\": \"+n.details,\"error\")}catch(t){l(n(\"api.storage.save.error\")+\": \"+t,\"error\")}},handleStorageTypes:async e=>{try{const s=await fetch(a(r.API_URL+\"/api/storage/enabled_types\"),{method:\"GET\",headers:o({token:c})});if(!s.ok)throw new Error(\"Network response was not ok\");const t=await s.json();t.code<100&&t.code>0?e(t.data||[]):l(t.message+\": \"+t.details,\"error\")}catch(s){l(n(\"api.storage.types.error\")+\": \"+s,\"error\")}},handleStorageValidate:t.useCallback(async(e,s)=>{try{const t={...e,isEnabled:e.isEnabled?1:0,accessUrlPrefix:e.accessUrlPrefix||\"http://placeholder\"},l=await fetch(a(r.API_URL+\"/api/storage/validate\"),{method:\"POST\",body:JSON.stringify(t),headers:o({token:c})});if(!l.ok)throw new Error(n(\"api.storage.validate.error\"));const d=await l.json();if(d.code>0&&d.code<=200)i.success(n(\"api.storage.validate.success\")),s();else{const e=d.details||d.message||\"\";i.error(e?`${n(\"api.storage.validate.error\")}: ${e}`:n(\"api.storage.validate.error\"))}}catch(t){i.error(t instanceof Error?t.message:n(\"api.storage.validate.error\"))}},[c,n])}}function ue(){const{t:n}=e(),{openConfirmDialog:l}=s(),c=localStorage.getItem(\"token\"),d=t.useCallback(async e=>{try{const s=await fetch(a(r.API_URL+\"/api/backup/configs\"),{method:\"GET\",headers:o({token:c})});if(!s.ok)throw new Error(\"Network response was not ok\");const t=await s.json();t.code<100&&t.code>0?e(t.data||[]):l(t.message+\": \"+t.details,\"error\")}catch(s){l(n(\"api.backup.configList.error\")+\": \"+s,\"error\")}},[c,l,n]),u=t.useCallback(async e=>{try{const s=await fetch(a(r.API_URL+`/api/backup/config?id=${e}`),{method:\"DELETE\",headers:o({token:c,includeContentType:!1})});if(!s.ok)throw new Error(\"Network response was not ok\");const t=await s.json();t.code<100&&t.code>0?i.success(t.message||n(\"api.backup.delete.success\")):l(t.message+\": \"+t.details,\"error\")}catch(s){l(n(\"api.backup.delete.error\")+\": \"+s,\"error\")}},[c,l,n]),m=t.useCallback(async(e,s)=>{try{const t=await fetch(a(r.API_URL+\"/api/backup/config\"),{method:\"POST\",body:JSON.stringify(e),headers:o({token:c})});if(!t.ok)throw new Error(\"Network response was not ok\");const d=await t.json();d.code<100&&d.code>0?(i.success(d.message||n(\"api.backup.save.success\")),s(d.data)):l(d.message+\": \"+d.details,\"error\")}catch(t){l(n(\"api.backup.save.error\")+\": \"+t,\"error\")}},[c,l,n]),x=t.useCallback(async e=>{try{const s=await fetch(a(r.API_URL+\"/api/backup/execute\"),{method:\"POST\",body:JSON.stringify({id:e}),headers:o({token:c})});if(!s.ok)throw new Error(\"Network response was not ok\");const t=await s.json();t.code<100&&t.code>0?i.success(t.message||n(\"api.backup.execute.success\")):l(t.message+\": \"+t.details,\"error\")}catch(s){l(n(\"api.backup.execute.error\")+\": \"+s,\"error\")}},[c,l,n]),p=t.useCallback(async(e,s,t,i)=>{var d;try{const n=r.API_URL+`/api/backup/historys?page=${e}&pageSize=${s}&configId=${t}`,u=await fetch(a(n),{method:\"GET\",headers:o({token:c})});if(!u.ok)throw new Error(\"Network response was not ok\");const m=await u.json();if(m.code<100&&m.code>0){if(i)if(m.data&&Array.isArray(m.data))i({list:m.data,total:m.total??-1});else if(m.data&&m.data.list){const e=(null==(d=m.data.pager)?void 0:d.totalRows)??m.data.total??0;i({list:m.data.list,total:e})}else i({list:[],total:0})}else l(m.message+\": \"+m.details,\"error\")}catch(u){l(n(\"api.backup.history.error\")+\": \"+u,\"error\")}},[c,l,n]);return t.useMemo(()=>({handleBackupConfigList:d,handleBackupConfigDelete:u,handleBackupConfigUpdate:m,handleBackupExecute:x,handleBackupHistory:p}),[d,u,m,x,p])}const me=[{pattern:/webdav;.*(401|Unauthorized)/i,key:\"error.storage.webdav.unauthorized\"},{pattern:/webdav;.*(403|Forbidden)/i,key:\"error.storage.webdav.forbidden\"},{pattern:/webdav;.*(404|Not Found)/i,key:\"error.storage.webdav.notFound\"},{pattern:/webdav;.*(405|Method Not Allowed)/i,key:\"error.storage.webdav.methodNotAllowed\"},{pattern:/webdav;.*no such host/i,key:\"error.storage.webdav.unreachable\"},{pattern:/webdav;.*connection refused/i,key:\"error.storage.webdav.connectionRefused\"},{pattern:/webdav;.*(timeout|deadline exceeded)/i,key:\"error.storage.webdav.timeout\"},{pattern:/webdav;/i,key:\"error.storage.webdav.generic\"},{pattern:/(aws_s3|minio|cloudflare_r2):.*NoSuchBucket/i,key:\"error.storage.s3.noSuchBucket\"},{pattern:/(aws_s3|minio|cloudflare_r2):.*AccessDenied/i,key:\"error.storage.s3.accessDenied\"},{pattern:/(aws_s3|minio|cloudflare_r2):.*no such host/i,key:\"error.storage.s3.unreachable\"},{pattern:/(aws_s3|minio|cloudflare_r2):.*(timeout|deadline exceeded)/i,key:\"error.storage.s3.timeout\"},{pattern:/oss:.*NoSuchBucket/i,key:\"error.storage.oss.noSuchBucket\"},{pattern:/oss:.*AccessDenied/i,key:\"error.storage.oss.accessDenied\"},{pattern:/oss:.*no such host/i,key:\"error.storage.oss.unreachable\"},{pattern:/no permission to upload/i,key:\"error.storage.local.noPermission\"},{pattern:/failed to create the save-fileurl directory/i,key:\"error.storage.local.createDirFailed\"},{pattern:/permission denied/i,key:\"error.storage.local.permissionDenied\"},{pattern:/Partial failure:/i,key:\"error.backup.partialFailure\"},{pattern:/Upload failed:/i,key:\"error.backup.uploadFailed\"},{pattern:/Failed to open backup file:/i,key:\"error.backup.openFileFailed\"},{pattern:/Note Vault does not exist/i,key:\"error.backup.vaultNotExist\"},{pattern:/no such host/i,key:\"error.network.unreachable\"},{pattern:/connection refused/i,key:\"error.network.connectionRefused\"},{pattern:/(timeout|deadline exceeded)/i,key:\"error.network.timeout\"}];function xe(e){if(!e)return null;for(const s of me)if(s.pattern.test(e))return s.key;return null}function pe({configId:s,configType:a,open:r,onOpenChange:o}){const{t:x}=e(),{handleBackupHistory:p}=ue(),{handleStorageList:h}=de(),[g,b]=t.useState([]),[U,T]=t.useState([]),[D,F]=t.useState(0),[R,L]=t.useState(1),[V,z]=t.useState(!1),$=t.useRef(0),_=t.useRef(0),A=t.useCallback(async e=>{const t=++$.current;z(!0),await p(e,5,s,e=>{t===$.current&&(b(e.list),F(e.total))}),t===$.current&&z(!1)},[s,p]);t.useEffect(()=>{if(r){const e=++_.current;return h(s=>{e===_.current&&T(s)}),L(1),void A(1)}$.current+=1,_.current+=1,z(!1)},[r,A,h]),t.useEffect(()=>()=>{$.current+=1,_.current+=1},[]);const K=e=>{const s=U.find(s=>Number(s.id)===e);return s?s.type:\"-\"},M=e=>{if(!e)return\"0 B\";const s=Math.floor(Math.log(e)/Math.log(1024));return parseFloat((e/Math.pow(1024,s)).toFixed(2))+\" \"+[\"B\",\"KB\",\"MB\",\"GB\",\"TB\"][s]},O=Math.ceil(D/5);return n.jsx(w,{open:r,onOpenChange:o,children:n.jsxs(S,{className:\"max-w-5xl max-h-[85vh] flex flex-col p-6\",children:[n.jsx(E,{className:\"mb-4\",children:n.jsxs(C,{className:\"flex items-center gap-2\",children:[n.jsx(l,{className:\"h-5 w-5 text-primary\"}),x(\"ui.backup.history.title\")]})}),n.jsx(\"div\",{className:\"flex-1 overflow-auto border rounded-xl bg-card/50\",children:n.jsxs(f,{children:[n.jsx(j,{className:\"bg-muted/50 sticky top-0 z-10\",children:n.jsxs(y,{children:[n.jsx(N,{className:\"w-[180px]\",children:x(\"ui.backup.history.startTime\")}),n.jsx(N,{children:x(\"ui.backup.history.storage\")}),n.jsx(N,{children:x(\"ui.backup.history.status\")}),n.jsx(N,{children:x(\"sync\"===a?\"ui.backup.history.syncStats\":\"ui.backup.history.backupStats\")}),n.jsx(N,{children:x(\"ui.backup.history.backupFile\")}),n.jsx(N,{className:\"max-w-[200px]\",children:x(\"ui.backup.history.message\")})]})}),n.jsx(v,{children:V?n.jsx(y,{children:n.jsx(k,{colSpan:6,className:\"h-48\",children:n.jsxs(\"div\",{className:\"flex flex-col items-center justify-center text-muted-foreground gap-2\",children:[n.jsx(c,{className:\"h-8 w-8 animate-spin opacity-50\"}),n.jsx(\"span\",{className:\"text-xs\",children:x(\"ui.common.loading\")})]})})}):g.length>0?g.map(e=>n.jsxs(y,{className:\"text-xs hover:bg-muted/30 transition-colors\",children:[n.jsx(k,{className:\"font-mono text-muted-foreground\",children:e.startTime}),n.jsx(k,{children:\"-\"!==K(e.storageId)?`#${e.storageId} ${x(`ui.storage.storageType.${K(e.storageId)}`)}`:\"-\"}),n.jsx(k,{children:n.jsx(\"span\",{className:d(\"px-2 py-0.5 rounded-full font-medium inline-block\",2===e.status?\"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400\":3===e.status?\"bg-destructive/10 text-destructive dark:bg-destructive/20\":5===e.status?\"bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400\":\"bg-muted text-muted-foreground\"),children:x(`ui.backup.status.${e.status}`)})}),n.jsx(k,{children:n.jsxs(\"div\",{className:\"flex flex-col gap-0.5\",children:[n.jsx(\"span\",{children:M(e.fileSize)}),n.jsxs(\"span\",{className:\"text-muted-foreground opacity-70 text-[10px]\",children:[e.fileCount||0,\" \",x(\"ui.backup.fileCountUnit\")]})]})}),n.jsx(k,{className:\"max-w-[150px] truncate\",children:\"sync\"!==e.type&&e.filePath?(()=>{const s=(e=>{if(!e)return null;try{const s=new URL(e);return\"http:\"===s.protocol||\"https:\"===s.protocol?e:null}catch{return null}})(((e,s)=>{const t=U.find(s=>Number(s.id)===e);if(!t||!t.accessUrlPrefix)return null;let a=t.accessUrlPrefix.endsWith(\"/\")?t.accessUrlPrefix.slice(0,-1):t.accessUrlPrefix;if(t.customPath){let e=t.customPath.startsWith(\"/\")?t.customPath.substring(1):t.customPath;e=e.endsWith(\"/\")?e.slice(0,-1):e,e&&(a+=\"/\"+e)}return a+=\"/\"+(s.startsWith(\"/\")?s.substring(1):s),a})(e.storageId,e.filePath)),t=e.filePath.split(\"/\").pop()||e.filePath;return s?n.jsxs(\"a\",{href:s,target:\"_blank\",rel:\"noopener noreferrer\",className:\"text-primary hover:text-blue-500 hover:underline flex items-center gap-1 transition-colors\",title:e.filePath,children:[n.jsx(\"span\",{className:\"truncate\",children:t}),n.jsx(P,{className:\"h-3 w-3 shrink-0 opacity-50\"})]}):n.jsx(\"span\",{title:e.filePath,className:\"truncate inline-block max-w-full\",children:t})})():n.jsx(\"span\",{className:\"text-muted-foreground\",children:\"-\"})}),n.jsx(k,{className:\"max-w-[250px]\",children:(()=>{if(!e.message)return n.jsx(\"span\",{className:\"opacity-70\",children:\"-\"});const s=3===e.status?xe(e.message):null;return s?n.jsxs(\"div\",{className:\"flex items-center gap-1\",children:[n.jsx(\"span\",{className:\"truncate text-destructive/90\",title:e.message,children:x(s)}),n.jsx(u,{variant:\"ghost\",size:\"icon\",className:\"h-5 w-5 shrink-0 text-muted-foreground hover:text-primary\",onClick:()=>{navigator.clipboard.writeText(e.message).then(()=>i.success(x(\"ui.common.copied\")),()=>i.error(x(\"ui.common.error\")))},title:x(\"ui.backup.history.copyError\"),children:n.jsx(B,{className:\"h-3 w-3\"})})]}):3===e.status?n.jsxs(\"div\",{className:\"flex items-center gap-1\",children:[n.jsx(\"span\",{className:\"truncate opacity-70\",title:e.message,children:e.message}),n.jsx(u,{variant:\"ghost\",size:\"icon\",className:\"h-5 w-5 shrink-0 text-muted-foreground hover:text-primary\",onClick:()=>{navigator.clipboard.writeText(e.message).then(()=>i.success(x(\"ui.common.copied\")),()=>i.error(x(\"ui.common.error\")))},title:x(\"ui.backup.history.copyError\"),children:n.jsx(B,{className:\"h-3 w-3\"})})]}):n.jsx(\"span\",{className:\"truncate opacity-70\",title:e.message,children:e.message})})()})]},e.id)):n.jsx(y,{children:n.jsx(k,{colSpan:6,className:\"h-48 text-center text-muted-foreground\",children:x(\"ui.backup.history.noData\")})})})]})}),(O>1||-1===D&&(5===g.length||R>1))&&n.jsxs(\"div\",{className:\"flex items-center justify-between mt-4 px-1\",children:[n.jsx(\"div\",{className:\"text-xs text-muted-foreground\",children:-1!==D&&`${x(\"ui.common.total\")} ${D} ${x(\"ui.common.items\")}`}),n.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[n.jsx(u,{variant:\"outline\",size:\"sm\",className:\"h-8 w-8 p-0\",onClick:()=>{const e=R-1;L(e),A(e)},disabled:R<=1||V,children:n.jsx(I,{className:\"h-4 w-4\"})}),n.jsx(\"span\",{className:\"text-xs font-medium px-2\",children:-1!==D?`${R} / ${O}`:`${x(\"ui.common.page\")} ${R}`}),n.jsx(u,{variant:\"outline\",size:\"sm\",className:\"h-8 w-8 p-0\",onClick:()=>{const e=R+1;L(e),A(e)},disabled:(-1!==D?R>=O:g.length<5)||V,children:n.jsx(m,{className:\"h-4 w-4\"})})]})]})]})})}const he=[\"oss\",\"s3\",\"r2\",\"minio\",\"localfs\",\"webdav\"];function ge({config:s,types:a,onSubmit:r,onCancel:o}){const{t:i}=e(),[l,c]=t.useState(null==s?void 0:s.type),{handleStorageUpdate:d,handleStorageValidate:m}=de(),[p,h]=t.useState(!1),[g,b]=t.useState(!1),[f,j]=t.useState(!1),y=t.useMemo(()=>{return _({type:M(he,{required_error:(e=i)(\"ui.validation.storage.typeRequired\")}),endpoint:K().optional(),region:K().optional(),accountId:K().optional(),bucketName:K().optional(),accessKeyId:K().optional(),accessKeySecret:K().optional(),user:K().optional(),password:K().optional(),customPath:K().optional(),accessUrlPrefix:K().min(1,e(\"ui.validation.storage.accessUrlPrefixRequired\")),isEnabled:A([O(),q()]).transform(e=>Boolean(e)).default(!1).optional()});var e},[i]),{register:N,handleSubmit:v,formState:{errors:k,isSubmitting:w},setValue:S,getValues:E}=J({resolver:G(y),defaultValues:s||{isEnabled:!0}});t.useEffect(()=>{const e=e=>{\"Escape\"===e.key&&o&&(e.preventDefault(),o())};return document.addEventListener(\"keydown\",e),()=>document.removeEventListener(\"keydown\",e)},[o]);return n.jsxs(\"form\",{onSubmit:v(e=>{s&&(e.id=s.id),d(e,()=>{r()})}),className:\"space-y-4\",children:[n.jsxs(\"div\",{className:\"grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-3\",children:[n.jsxs(\"div\",{className:\"space-y-1.5\",children:[n.jsx(U,{htmlFor:\"type\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:i(\"ui.storage.type\")}),n.jsxs(R,{name:\"type\",onValueChange:e=>{S(\"type\",e),c(e)},defaultValue:null==s?void 0:s.type,children:[n.jsx(L,{id:\"type\",className:\"bg-background border-input focus:ring-primary/20\",children:n.jsx(V,{placeholder:i(\"ui.storage.selectType\")})}),n.jsx(z,{children:a.map((e,s)=>n.jsx($,{value:e,children:i(`ui.storage.storageType.${e}`)},s))})]}),k.type&&n.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:k.type.message})]}),\"oss\"===l&&n.jsxs(\"div\",{className:\"space-y-1.5\",children:[n.jsx(U,{htmlFor:\"endpoint\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:i(\"ui.storage.endpoint\")}),n.jsx(x,{id:\"endpoint\",autoComplete:\"off\",placeholder:i(\"ui.storage.placeholder.endpoint.oss\"),className:\"bg-background border-input\",...N(\"endpoint\")}),n.jsx(\"p\",{className:\"text-[11px] text-muted-foreground ml-1\",children:i(\"ui.storage.help.endpoint.oss\")}),k.endpoint&&n.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:k.endpoint.message})]}),(\"s3\"===l||\"minio\"===l)&&n.jsxs(\"div\",{className:\"space-y-1.5\",children:[n.jsx(U,{htmlFor:\"region\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:i(\"ui.storage.region\")}),n.jsx(x,{id:\"region\",autoComplete:\"off\",placeholder:i(\"ui.storage.placeholder.region\"),className:\"bg-background border-input\",...N(\"region\")}),n.jsx(\"p\",{className:\"text-[11px] text-muted-foreground ml-1\",children:i(\"ui.storage.help.region\")}),k.region&&n.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:k.region.message})]}),\"r2\"===l&&n.jsxs(\"div\",{className:\"space-y-1.5\",children:[n.jsx(U,{htmlFor:\"accountId\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:i(\"ui.storage.accountId\")}),n.jsx(x,{id:\"accountId\",autoComplete:\"off\",placeholder:i(\"ui.storage.placeholder.accountId\"),className:\"bg-background border-input\",...N(\"accountId\")}),n.jsx(\"p\",{className:\"text-[11px] text-muted-foreground ml-1\",children:i(\"ui.storage.help.accountId\")}),k.accountId&&n.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:k.accountId.message})]}),\"webdav\"===l&&n.jsxs(\"div\",{className:\"space-y-1.5\",children:[n.jsx(U,{htmlFor:\"endpoint\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:i(\"ui.storage.webdavUrl\")}),n.jsx(x,{id:\"endpoint\",autoComplete:\"off\",placeholder:i(\"ui.storage.placeholder.webdavUrl\"),className:\"bg-background border-input\",...N(\"endpoint\")}),n.jsx(\"p\",{className:\"text-[11px] text-muted-foreground ml-1\",children:i(\"ui.storage.help.webdavUrl\")}),k.endpoint&&n.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:k.endpoint.message})]}),\"webdav\"===l&&n.jsxs(\"div\",{className:\"space-y-1.5\",children:[n.jsx(U,{htmlFor:\"user\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:i(\"ui.storage.webdavUser\")}),n.jsx(x,{id:\"user\",autoComplete:\"off\",placeholder:i(\"ui.storage.placeholder.webdavUser\"),className:\"bg-background border-input\",...N(\"user\")}),n.jsx(\"p\",{className:\"text-[11px] text-muted-foreground ml-1\",children:i(\"ui.storage.help.webdavUser\")}),k.user&&n.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:k.user.message})]}),\"webdav\"===l&&n.jsxs(\"div\",{className:\"space-y-1.5\",children:[n.jsx(U,{htmlFor:\"password\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:i(\"ui.storage.webdavPassword\")}),n.jsxs(\"div\",{className:\"relative\",children:[n.jsx(x,{id:\"password\",type:g?\"text\":\"password\",placeholder:i(\"ui.storage.placeholder.webdavPassword\"),className:\"bg-background border-input no-password-prompt pr-10\",...N(\"password\")}),n.jsx(u,{type:\"button\",variant:\"ghost\",size:\"icon\",className:\"absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent text-muted-foreground hover:text-foreground\",onClick:()=>b(!g),children:g?n.jsx(W,{className:\"h-4 w-4\"}):n.jsx(Q,{className:\"h-4 w-4\"})})]}),n.jsx(\"p\",{className:\"text-[11px] text-muted-foreground ml-1\",children:i(\"ui.storage.help.webdavPassword\")}),k.password&&n.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:k.password.message})]}),\"minio\"===l&&n.jsxs(\"div\",{className:\"space-y-1.5\",children:[n.jsx(U,{htmlFor:\"endpoint\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:i(\"ui.storage.endpoint\")}),n.jsx(x,{id:\"endpoint\",autoComplete:\"off\",placeholder:i(\"ui.storage.placeholder.endpoint.minio\"),className:\"bg-background border-input\",...N(\"endpoint\")}),n.jsx(\"p\",{className:\"text-[11px] text-muted-foreground ml-1\",children:i(\"ui.storage.help.endpoint.minio\")}),k.endpoint&&n.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:k.endpoint.message})]}),\"localfs\"!==l&&\"webdav\"!==l&&void 0!==l&&n.jsxs(\"div\",{className:\"space-y-1.5\",children:[n.jsx(U,{htmlFor:\"bucketName\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:i(\"ui.storage.bucketName\")}),n.jsx(x,{id:\"bucketName\",autoComplete:\"off\",placeholder:i(\"ui.storage.placeholder.bucketName\"),className:\"bg-background border-input\",...N(\"bucketName\")}),k.bucketName&&n.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:k.bucketName.message})]}),\"localfs\"!==l&&\"webdav\"!==l&&n.jsxs(\"div\",{className:\"space-y-1.5\",children:[n.jsx(U,{htmlFor:\"accessKeyId\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:i(\"ui.storage.accessKeyId\")}),n.jsx(x,{id:\"accessKeyId\",autoComplete:\"off\",placeholder:i(\"ui.storage.placeholder.accessKeyId\"),className:\"bg-background border-input\",...N(\"accessKeyId\")}),k.accessKeyId&&n.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:k.accessKeyId.message})]}),\"localfs\"!==l&&\"webdav\"!==l&&n.jsxs(\"div\",{className:\"space-y-1.5\",children:[n.jsx(U,{htmlFor:\"accessKeySecret\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:i(\"ui.storage.accessKeySecret\")}),n.jsxs(\"div\",{className:\"relative\",children:[n.jsx(x,{id:\"accessKeySecret\",type:f?\"text\":\"password\",placeholder:i(\"ui.storage.placeholder.accessKeySecret\"),className:\"bg-background border-input no-password-prompt pr-10\",...N(\"accessKeySecret\")}),n.jsx(u,{type:\"button\",variant:\"ghost\",size:\"icon\",className:\"absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent text-muted-foreground hover:text-foreground\",onClick:()=>j(!f),children:f?n.jsx(W,{className:\"h-4 w-4\"}):n.jsx(Q,{className:\"h-4 w-4\"})})]}),k.accessKeySecret&&n.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:k.accessKeySecret.message})]}),n.jsxs(\"div\",{className:\"space-y-1.5\",children:[n.jsx(U,{htmlFor:\"customPath\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:i(\"ui.storage.customPath\")}),n.jsx(x,{id:\"customPath\",autoComplete:\"off\",placeholder:i(\"ui.storage.placeholder.customPath\"),className:\"bg-background border-input\",...N(\"customPath\")}),n.jsx(\"p\",{className:\"text-[11px] text-muted-foreground ml-1\",children:i(\"ui.storage.help.customPath\")}),k.customPath&&n.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:k.customPath.message})]}),n.jsxs(\"div\",{className:\"space-y-1.5\",children:[n.jsx(U,{htmlFor:\"accessUrlPrefix\",className:\"text-xs font-semibold text-muted-foreground ml-1\",children:i(\"ui.storage.accessUrlPrefix\")}),n.jsx(x,{id:\"accessUrlPrefix\",autoComplete:\"off\",placeholder:i(\"ui.storage.placeholder.accessUrlPrefix\"),className:\"bg-background border-input\",...N(\"accessUrlPrefix\")}),n.jsx(\"p\",{className:\"text-[11px] text-muted-foreground ml-1\",children:i(\"ui.storage.help.accessUrlPrefix\")}),k.accessUrlPrefix&&n.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:k.accessUrlPrefix.message})]})]}),n.jsxs(\"div\",{className:\"flex items-center justify-between pt-3 border-t border-border\",children:[n.jsxs(\"div\",{className:\"flex items-center space-x-2\",children:[n.jsx(H,{id:\"isEnabled\",name:\"isEnabled\",defaultChecked:!s||!!s.isEnabled,onCheckedChange:e=>S(\"isEnabled\",Boolean(e)),className:\"border-input data-[state=checked]:bg-primary data-[state=checked]:border-primary\"}),n.jsx(U,{htmlFor:\"isEnabled\",className:\"text-sm font-medium text-foreground\",children:i(\"ui.common.isEnabled\")})]}),n.jsxs(\"div\",{className:\"flex items-center gap-3 flex-wrap justify-end\",children:[o&&n.jsx(u,{type:\"button\",variant:\"ghost\",onClick:o,disabled:w||p,children:i(\"ui.common.cancel\")}),n.jsxs(u,{type:\"button\",variant:\"outline\",size:\"sm\",className:\"px-6 rounded-lg\",disabled:w||p||!l,onClick:async()=>{const e=E();if(e.type){h(!0);try{await m(e,()=>{})}finally{h(!1)}}},children:[n.jsx(X,{className:\"h-4 w-4 mr-1.5\"}),i(p?\"ui.storage.validate.loading\":\"ui.storage.validate.title\")]}),n.jsx(u,{type:\"submit\",size:\"sm\",className:\"px-8 rounded-lg shadow-sm\",disabled:w||p,children:i(s?\"ui.common.save\":\"ui.common.add\")})]})]})]})}function be({config:s,storages:a,onSubmit:r,onCancel:o}){const{t:i}=e(),{handleBackupConfigUpdate:l}=ue(),{handleVaultList:c}=T(),[m,h]=t.useState([]),g=t.useMemo(()=>a.filter(e=>e.isEnabled),[a]),b=t.useMemo(()=>{if(!(null==s?void 0:s.storageIds))return[];try{return JSON.parse(s.storageIds).filter(e=>g.some(s=>Number(s.id)===e))}catch{return[]}},[null==s?void 0:s.storageIds,g]),[f,j]=t.useState(b);t.useEffect(()=>{c(h)},[c]);const y=(N=i,_({vault:K().min(1,N(\"ui.backup.validation.vaultRequired\")),type:M([\"full\",\"incremental\",\"sync\"],{required_error:N(\"ui.backup.validation.typeRequired\")}),cronStrategy:M([\"daily\",\"weekly\",\"monthly\",\"custom\"],{required_error:N(\"ui.backup.validation.strategyRequired\")}),cronExpression:K().optional(),storageIds:K().refine(e=>{try{const s=JSON.parse(e);return Array.isArray(s)&&s.length>0}catch{return!1}},N(\"ui.backup.validation.storageRequired\")),isEnabled:O().default(!0),includeVaultName:O().default(!1),retentionDays:q().int().min(-1,N(\"ui.backup.validation.retentionDaysMin\")).optional()}).refine(e=>!(\"custom\"===e.cronStrategy&&!e.cronExpression),{message:N(\"ui.backup.validation.cronExpressionRequired\"),path:[\"cronExpression\"]}));var N;const v=t.useMemo(()=>({vault:(null==s?void 0:s.vault)||\"\",type:null==s?void 0:s.type,cronStrategy:(null==s?void 0:s.cronStrategy)||\"daily\",cronExpression:(null==s?void 0:s.cronExpression)||\"0 0 * * *\",storageIds:JSON.stringify(b),isEnabled:(null==s?void 0:s.isEnabled)??!0,includeVaultName:(null==s?void 0:s.includeVaultName)??!1,retentionDays:(null==s?void 0:s.retentionDays)??30}),[null==s?void 0:s.vault,null==s?void 0:s.type,null==s?void 0:s.cronStrategy,null==s?void 0:s.cronExpression,null==s?void 0:s.isEnabled,null==s?void 0:s.includeVaultName,null==s?void 0:s.retentionDays,b]),{register:k,handleSubmit:w,formState:{errors:S},setValue:E,watch:C,reset:P}=J({resolver:G(y),defaultValues:v}),I=C(\"vault\"),D=C(\"cronStrategy\");t.useEffect(()=>{j(b),P(v)},[v,b,P]);const B=t.useCallback(()=>{j(b),P(v),null==o||o()},[b,v,P,o]);t.useEffect(()=>{const e=e=>{\"Escape\"===e.key&&o&&(e.preventDefault(),B())};return document.addEventListener(\"keydown\",e),()=>document.removeEventListener(\"keydown\",e)},[B,o]);return n.jsxs(\"form\",{onSubmit:w(e=>{l({...e,id:null==s?void 0:s.id},()=>{r()})}),className:\"space-y-4\",children:[n.jsxs(\"div\",{className:\"grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-3\",children:[n.jsxs(\"div\",{className:\"space-y-1.5\",children:[n.jsx(U,{className:\"text-xs font-semibold text-muted-foreground ml-1\",children:i(\"ui.backup.vault\")}),n.jsxs(R,{onValueChange:e=>E(\"vault\",e),value:I||void 0,children:[n.jsx(L,{className:\"bg-background border-input\",children:n.jsx(V,{placeholder:i(\"ui.backup.selectVault\")})}),n.jsx(z,{children:m.map(e=>n.jsx($,{value:e.vault,children:e.vault},e.id))})]}),S.vault&&n.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:S.vault.message})]}),n.jsxs(\"div\",{className:\"space-y-1.5\",children:[n.jsx(U,{className:\"text-xs font-semibold text-muted-foreground ml-1\",children:i(\"ui.backup.type\")}),n.jsxs(R,{onValueChange:e=>E(\"type\",e),value:C(\"type\"),children:[n.jsx(L,{className:\"bg-background border-input\",children:n.jsx(V,{placeholder:i(\"ui.backup.selectType\")})}),n.jsxs(z,{children:[n.jsx($,{value:\"full\",children:i(\"ui.backup.backupType.full\")}),n.jsx($,{value:\"incremental\",children:i(\"ui.backup.backupType.incremental\")}),n.jsx($,{value:\"sync\",children:i(\"ui.backup.backupType.sync\")})]})]}),S.type&&n.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:S.type.message})]}),\"sync\"===C(\"type\")&&n.jsx(\"div\",{className:\"space-y-1.5 md:col-span-2\",children:n.jsxs(\"div\",{className:\"flex items-center space-x-2\",children:[n.jsx(H,{id:\"includeVaultName\",checked:C(\"includeVaultName\"),onCheckedChange:e=>E(\"includeVaultName\",Boolean(e))}),n.jsx(U,{htmlFor:\"includeVaultName\",className:\"text-sm font-medium text-foreground\",children:i(\"ui.backup.includeVaultName.label\")}),n.jsx(Z,{content:n.jsx(\"div\",{className:\"whitespace-pre-line text-left\",children:i(\"ui.backup.includeVaultName.tooltip\")}),side:\"right\",children:n.jsx(p,{className:\"h-4 w-4 text-muted-foreground cursor-help\"})})]})}),(\"full\"===C(\"type\")||\"incremental\"===C(\"type\"))&&n.jsxs(n.Fragment,{children:[n.jsxs(\"div\",{className:\"space-y-1.5\",children:[n.jsx(U,{className:\"text-xs font-semibold text-muted-foreground ml-1\",children:i(\"ui.backup.cronStrategy\")}),n.jsxs(R,{onValueChange:e=>E(\"cronStrategy\",e),value:D||\"daily\",children:[n.jsx(L,{className:\"bg-background border-input\",children:n.jsx(V,{})}),n.jsxs(z,{children:[n.jsx($,{value:\"daily\",children:i(\"ui.backup.strategy.daily\")}),n.jsx($,{value:\"weekly\",children:i(\"ui.backup.strategy.weekly\")}),n.jsx($,{value:\"monthly\",children:i(\"ui.backup.strategy.monthly\")}),n.jsx($,{value:\"custom\",children:i(\"ui.backup.strategy.custom\")})]})]}),S.cronStrategy&&n.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:S.cronStrategy.message})]}),n.jsxs(\"div\",{className:d(\"space-y-1.5\",\"custom\"!==D&&\"opacity-50 pointer-events-none\"),children:[n.jsx(U,{className:\"text-xs font-semibold text-muted-foreground ml-1\",children:i(\"ui.backup.cronExpression\")}),n.jsx(x,{...k(\"cronExpression\"),placeholder:\"0 0 * * *\",className:\"bg-background border-input\",disabled:\"custom\"!==D}),S.cronExpression&&n.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:S.cronExpression.message})]})]}),(\"full\"===C(\"type\")||\"incremental\"===C(\"type\")||\"sync\"===C(\"type\"))&&n.jsxs(\"div\",{className:\"space-y-1.5\",children:[n.jsx(U,{className:\"text-xs font-semibold text-muted-foreground ml-1\",children:\"sync\"===C(\"type\")?i(\"ui.backup.retentionDays.sync\"):\"full\"===C(\"type\")||\"incremental\"===C(\"type\")?i(\"ui.backup.retentionDays.backup\"):i(\"ui.backup.retentionDays\")}),n.jsx(x,{type:\"number\",...k(\"retentionDays\",{valueAsNumber:!0}),placeholder:\"30\",className:\"bg-background border-input\"}),S.retentionDays&&n.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:S.retentionDays.message})]}),n.jsxs(\"div\",{className:\"space-y-1.5 md:col-span-2\",children:[n.jsx(U,{className:\"text-xs font-semibold text-muted-foreground ml-1\",children:i(\"ui.backup.storages\")}),n.jsx(\"div\",{className:\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-2 border border-input rounded-md p-3 bg-background/50\",children:0===g.length?n.jsxs(\"div\",{className:\"col-span-full flex flex-col items-center justify-center p-4 text-center text-muted-foreground bg-muted/30 rounded-md border border-dashed\",children:[n.jsx(\"span\",{className:\"text-sm mb-2\",children:i(\"ui.backup.noAvailableStorage\")}),n.jsx(\"p\",{className:\"text-xs opacity-70 mb-2\",children:i(\"ui.backup.addStorageTip\")})]}):g.map(e=>n.jsxs(\"div\",{className:\"flex items-center space-x-2\",children:[n.jsx(H,{id:`storage-${e.id}`,checked:f.includes(Number(e.id)),onCheckedChange:()=>(e=>{const s=f.includes(e)?f.filter(s=>s!==e):[...f,e];j(s),E(\"storageIds\",JSON.stringify(s))})(Number(e.id))}),n.jsxs(U,{htmlFor:`storage-${e.id}`,className:\"text-sm font-normal cursor-pointer truncate\",children:[i(`ui.storage.storageType.${e.type}`),\" (\",e.id,\")\"]})]},e.id))}),S.storageIds&&n.jsx(\"p\",{className:\"text-[11px] text-destructive mt-1 ml-1\",children:S.storageIds.message})]})]}),n.jsxs(\"div\",{className:\"flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 pt-3 border-t border-border\",children:[n.jsxs(\"div\",{className:\"flex items-center space-x-2\",children:[n.jsx(H,{id:\"isEnabledBackup\",checked:C(\"isEnabled\"),onCheckedChange:e=>E(\"isEnabled\",Boolean(e))}),n.jsx(U,{htmlFor:\"isEnabledBackup\",className:\"text-sm font-medium text-foreground\",children:i(\"ui.common.isEnabled\")})]}),n.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[o&&n.jsx(u,{type:\"button\",variant:\"ghost\",onClick:B,children:i(\"ui.common.cancel\")}),n.jsx(u,{type:\"submit\",size:\"sm\",className:\"px-8 rounded-lg shadow-sm\",children:i(s?\"ui.common.save\":\"ui.common.add\")})]})]})]})}function fe(){const{t:a}=e(),{openConfirmDialog:r}=s(),[o,i]=t.useState([]),[l,m]=t.useState(null),[x,p]=t.useState(!1),[f,j]=t.useState(he),[y,N]=t.useState([]),[v,k]=t.useState(null),[w,S]=t.useState(!1),[E,C]=t.useState(null),[P,I]=t.useState(void 0),[U,T]=t.useState(!1),{handleStorageList:B,handleStorageDelete:R,handleStorageTypes:L}=de(),{handleBackupConfigList:V,handleBackupConfigDelete:z,handleBackupExecute:$}=ue();t.useEffect(()=>{_()},[]);const _=async()=>{L(j),B(i),V(N)};return n.jsxs(\"div\",{className:\"grid grid-cols-1 md:grid-cols-2 gap-4 pb-24 md:pb-4 items-start\",children:[n.jsx(\"div\",{className:\"rounded-xl border border-border bg-card p-6 custom-shadow\",children:n.jsxs(\"div\",{className:\"space-y-4\",children:[n.jsxs(\"div\",{className:\"flex items-center justify-between\",children:[n.jsx(\"h2\",{className:\"text-2xl font-bold\",children:a(\"ui.backup.management\")}),n.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[n.jsx(u,{variant:\"ghost\",size:\"icon\",onClick:_,className:\"rounded-xl shrink-0 h-10 w-10 text-muted-foreground hover:text-primary\",title:a(\"ui.common.refresh\"),children:n.jsx(ee,{className:\"h-4 w-4\"})}),n.jsxs(u,{onClick:()=>S(!0),className:\"rounded-xl shrink-0\",children:[n.jsx(se,{className:\"h-4 w-4 mr-2\"}),a(\"ui.common.add\")]})]})]}),w&&n.jsxs(\"div\",{className:\"p-4 md:p-5 border border-primary/10 rounded-xl bg-primary/5 custom-shadow-sm mb-4 animate-in fade-in slide-in-from-top-4 duration-300\",children:[n.jsxs(\"div\",{className:\"flex items-center mb-4\",children:[n.jsx(\"div\",{className:\"h-6 w-1 bg-primary rounded-full mr-3\"}),n.jsx(\"h3\",{className:\"text-base font-bold text-foreground\",children:a(\"ui.backup.add\")})]}),n.jsx(be,{storages:o,onSubmit:async()=>{await _(),S(!1)},onCancel:()=>S(!1)})]}),null!==v&&n.jsxs(\"div\",{className:\"p-4 md:p-5 border border-primary/10 rounded-xl bg-primary/5 custom-shadow-sm mb-4 animate-in fade-in slide-in-from-top-4 duration-300\",children:[n.jsxs(\"div\",{className:\"flex items-center mb-4\",children:[n.jsx(\"div\",{className:\"h-6 w-1 bg-primary rounded-full mr-3\"}),n.jsx(\"h3\",{className:\"text-base font-bold text-foreground\",children:a(\"ui.backup.edit\")})]}),n.jsx(be,{storages:o,config:y.find(e=>e.id===v),onSubmit:async()=>{await _(),k(null)},onCancel:()=>k(null)})]}),n.jsx(\"div\",{className:\"flex flex-col gap-2\",children:y&&y.length>0?y.map(e=>n.jsxs(\"div\",{className:d(\"group relative flex flex-col p-3 transition-all duration-200 hover:shadow-sm border rounded-lg bg-background hover:bg-accent/50\",e.isEnabled?\"border-l-4 border-l-blue-600 dark:border-l-blue-500 shadow-sm\":\"border-l-4 border-l-muted border-y-border border-r-border\"),children:[n.jsxs(\"div\",{className:\"flex items-center justify-between mb-2\",children:[n.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[n.jsx(X,{className:d(\"h-4 w-4\",e.isEnabled?\"text-blue-500\":\"text-muted-foreground\")}),n.jsxs(\"span\",{className:\"text-sm tabular-nums text-muted-foreground font-mono font-bold\",children:[\"#\",e.id]}),n.jsx(\"span\",{className:\"font-bold text-sm\",children:e.vault}),n.jsx(\"span\",{className:\"text-[10px] px-1.5 py-0.5 bg-accent rounded text-accent-foreground uppercase\",children:a(`ui.backup.backupType.${e.type}`)})]}),n.jsxs(\"div\",{className:\"flex items-center gap-1\",children:[n.jsx(u,{variant:\"ghost\",size:\"icon\",className:\"h-7 w-7 text-muted-foreground hover:text-primary rounded-md\",onClick:()=>{C(e.id),I(e.type),T(!0)},title:a(\"ui.backup.history.title\"),children:n.jsx(te,{className:\"h-3 w-3\"})}),n.jsx(u,{variant:\"ghost\",size:\"icon\",className:\"h-7 w-7 text-muted-foreground hover:text-blue-600 rounded-md\",onClick:()=>(async e=>{await $(e),setTimeout(()=>V(N),1e3)})(e.id),title:a(\"ui.backup.executeNow\"),children:n.jsx(Y,{className:\"h-3 w-3\"})}),n.jsx(u,{variant:\"ghost\",size:\"icon\",className:\"h-7 w-7 text-muted-foreground hover:text-primary rounded-md\",onClick:()=>{return s=e.id,k(null),void setTimeout(()=>k(s),0);var s},children:n.jsx(ae,{className:\"h-3 w-3\"})}),n.jsx(u,{variant:\"ghost\",size:\"icon\",className:\"h-7 w-7 text-muted-foreground hover:text-destructive rounded-md\",onClick:()=>(async e=>{r(a(\"ui.backup.confirmDelete\"),\"confirm\",async()=>{await z(e),await _()})})(e.id),children:n.jsx(re,{className:\"h-3 w-3\"})})]})]}),n.jsxs(\"div\",{className:\"flex flex-col gap-y-1 text-[11px] text-muted-foreground\",children:[n.jsxs(\"div\",{className:\"mt-1 pt-1 border-t border-border/50 opacity-70 flex items-center justify-between\",children:[n.jsx(\"div\",{className:\"flex items-center gap-1.5\",children:e.lastRunTime?n.jsxs(\"span\",{children:[a(\"ui.backup.lastRunTime\"),\": \",e.lastRunTime]}):n.jsx(\"span\",{children:a(\"ui.backup.noBackup\")})}),n.jsxs(\"div\",{className:\"flex items-center gap-1.5 min-w-[3.5rem] justify-end\",children:[1===e.lastStatus?n.jsx(c,{className:\"h-3 w-3 animate-spin text-blue-500\"}):2===e.lastStatus?n.jsx(h,{className:\"h-3 w-3 text-green-500\"}):3===e.lastStatus?n.jsx(oe,{className:\"h-3 w-3 text-destructive\"}):4===e.lastStatus?n.jsx(g,{className:\"h-3 w-3 text-muted-foreground\"}):5===e.lastStatus?n.jsx(h,{className:\"h-3 w-3 text-blue-400\"}):n.jsx(ie,{className:\"h-3 w-3 opacity-50\"}),n.jsx(\"span\",{className:d(\"font-medium\",2===e.lastStatus?\"text-green-500\":3===e.lastStatus?\"text-destructive\":5===e.lastStatus?\"text-blue-400 dark:text-blue-300\":\"\"),children:void 0!==e.lastStatus?a(`ui.backup.status.${e.lastStatus}`):a(\"ui.common.unknown\")})]})]}),3===e.lastStatus&&e.lastMessage&&(()=>{const s=xe(e.lastMessage);return n.jsx(\"div\",{className:\"text-[10px] text-destructive/80 truncate\",title:e.lastMessage,children:s?a(s):e.lastMessage})})()]})]},e.id)):n.jsxs(\"div\",{className:\"py-12 flex flex-col items-center justify-center text-muted-foreground border-2 border-dashed border-border/50 rounded-xl bg-muted/20 mt-4\",children:[n.jsx(D,{className:\"h-12 w-12 mb-3 opacity-20\"}),n.jsx(\"p\",{className:\"text-sm opacity-70 font-medium\",children:a(\"ui.backup.noBackup\")})]})})]})}),n.jsx(\"div\",{className:\"rounded-xl border border-border bg-card p-6 custom-shadow\",children:n.jsxs(\"div\",{className:\"space-y-4\",children:[n.jsxs(\"div\",{className:\"flex items-center justify-between\",children:[n.jsx(\"h2\",{className:\"text-2xl font-bold\",children:a(\"ui.storage.management\")}),n.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[n.jsx(u,{variant:\"ghost\",size:\"icon\",onClick:_,className:\"rounded-xl shrink-0 h-10 w-10 text-muted-foreground hover:text-primary\",title:a(\"ui.common.refresh\"),children:n.jsx(ee,{className:\"h-4 w-4\"})}),n.jsxs(u,{onClick:()=>p(!0),className:\"rounded-xl shrink-0\",children:[n.jsx(se,{className:\"h-4 w-4 mr-2\"}),a(\"ui.common.add\")]})]})]}),x&&n.jsxs(\"div\",{className:\"p-4 md:p-5 border border-primary/10 rounded-xl bg-primary/5 custom-shadow-sm mb-4 animate-in fade-in slide-in-from-top-4 duration-300\",children:[n.jsxs(\"div\",{className:\"flex items-center mb-4\",children:[n.jsx(\"div\",{className:\"h-6 w-1 bg-primary rounded-full mr-3\"}),n.jsx(\"h3\",{className:\"text-base font-bold text-foreground\",children:a(\"ui.storage.add\")})]}),n.jsx(ge,{types:f,onSubmit:async()=>{await _(),p(!1)},onCancel:()=>p(!1)})]}),l&&n.jsxs(\"div\",{className:\"p-4 md:p-5 border border-primary/10 rounded-xl bg-primary/5 custom-shadow-sm mb-4 animate-in fade-in slide-in-from-top-4 duration-300\",children:[n.jsxs(\"div\",{className:\"flex items-center mb-4\",children:[n.jsx(\"div\",{className:\"h-6 w-1 bg-primary rounded-full mr-3\"}),n.jsx(\"h3\",{className:\"text-base font-bold text-foreground\",children:a(\"ui.storage.edit\")})]}),n.jsx(ge,{types:f,config:o.find(e=>e.id===l),onSubmit:async()=>{await _(),m(null)},onCancel:()=>m(null)})]}),n.jsx(\"div\",{className:\"flex flex-col gap-2\",children:o&&o.length>0?o.map(e=>{const s=\"localfs\"===e.type?ne:\"webdav\"===e.type?le:F;return n.jsxs(\"div\",{className:d(\"group relative flex items-center p-2 px-3 transition-all duration-200 hover:shadow-sm border rounded-lg bg-background hover:bg-accent/50\",e.isEnabled?\"border-l-4 border-l-green-600 dark:border-l-green-500 shadow-sm\":\"border-l-4 border-l-muted border-y-border border-r-border\"),children:[n.jsx(\"div\",{className:d(\"flex items-center justify-center w-8 h-8 rounded-md mr-3 shrink-0 transition-colors\",e.isEnabled?\"bg-green-100 text-green-600 dark:bg-green-500/20 dark:text-green-400\":\"bg-muted text-muted-foreground\"),children:n.jsx(s,{className:\"h-4.5 w-4.5\"})}),n.jsxs(\"div\",{className:\"flex items-center flex-1 min-w-0\",children:[n.jsxs(\"div\",{className:\"flex flex-col sm:flex-row sm:items-center sm:gap-3\",children:[n.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[n.jsxs(\"span\",{className:\"text-lg tabular-nums text-muted-foreground font-mono font-bold\",children:[\"#\",e.id]}),n.jsx(\"span\",{className:\"text-sm font-semibold truncate min-w-20 text-foreground\",children:a(`ui.storage.storageType.${e.type}`)})]}),n.jsxs(\"span\",{className:d(\"inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-medium transition-colors\",e.isEnabled?\"bg-green-100 text-green-700 dark:bg-green-500/20 dark:text-green-400\":\"bg-muted text-muted-foreground\"),children:[e.isEnabled?n.jsx(b,{className:\"h-2.5 w-2.5\"}):null,e.isEnabled?a(\"ui.common.isEnabled\"):a(\"ui.common.isDisabled\")]})]}),e.accessUrlPrefix&&n.jsxs(\"div\",{className:\"hidden lg:flex items-center ml-6 text-xs text-muted-foreground truncate max-w-75 shrink overflow-hidden\",children:[n.jsx(ce,{className:\"h-3 w-3 mr-1 opacity-50\"}),n.jsx(\"span\",{className:\"truncate opacity-70 hover:opacity-100 transition-opacity\",title:e.accessUrlPrefix,children:e.accessUrlPrefix})]})]}),n.jsxs(\"div\",{className:\"flex items-center pl-2 ml-2 border-l border-border space-x-0.5\",children:[n.jsx(u,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 text-muted-foreground hover:text-primary hover:bg-primary/10 rounded-md transition-all\",onClick:()=>{return s=e.id,m(null),void setTimeout(()=>m(s),0);var s},children:n.jsx(ae,{className:\"h-3.5 w-3.5\"})}),n.jsx(u,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 text-muted-foreground hover:text-destructive hover:bg-destructive/10 rounded-md transition-all\",onClick:()=>(async e=>{r(a(\"ui.storage.confirmDelete\"),\"confirm\",async()=>{await R(e),await _()})})(e.id),children:n.jsx(re,{className:\"h-3.5 w-3.5\"})})]})]},e.id)}):n.jsxs(\"div\",{className:\"py-12 flex flex-col items-center justify-center text-muted-foreground border-2 border-dashed border-border/50 rounded-xl bg-muted/20 mt-4\",children:[n.jsx(D,{className:\"h-12 w-12 mb-3 opacity-20\"}),n.jsx(\"p\",{className:\"text-sm opacity-70 font-medium\",children:a(\"ui.storage.noStorage\")})]})})]})}),E&&n.jsx(pe,{configId:E,configType:P,open:U,onOpenChange:T})]})}export{fe as SyncBackup};\n"
  },
  {
    "path": "frontend/assets/sync-log-manager-Zjq-lA99.js",
    "content": "import{c as e,r as s,b as a,a as l,e as r,t,u as n,j as i,ae as c,f as d,B as o,C as m,a6 as x,aA as u}from\"./font-loader-CIrh3KnA.js\";import{C as h,f as p,j as g}from\"./main-BIi-kGYY.js\";import{T as j,a as f,b as N,c as w,d as v,e as b}from\"./table-D9wbHMTA.js\";import{B as y}from\"./badge-C63ATniC.js\";import{T as L}from\"./tooltip-Dr-qRlmI.js\";import{C as k}from\"./clock-C9LPHszx.js\";import{H as C}from\"./hard-drive-Dw58lXyp.js\";import{F as S}from\"./file-type-DbD_pFnN.js\";import{A as F,M as z}from\"./monitor-BGNS5Y9j.js\";import{F as M,f as V}from\"./format-CdHm7RWL.js\";import{S as E,a as P,b as A,c as B,d as T}from\"./select-CJF_alSt.js\";import{R as $}from\"./refresh-cw-BxIJAPy3.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const D=e(\"FilterX\",[[\"path\",{d:\"M13.013 3H2l8 9.46V19l4 2v-8.54l.9-1.055\",key:\"1fi1da\"}],[\"path\",{d:\"m22 3-5 5\",key:\"12jva0\"}],[\"path\",{d:\"m17 3 5 5\",key:\"k36vhe\"}]]);function H({logs:e,vaults:s,loading:a,currentPage:l,totalPages:r,onPageChange:t}){const{t:p}=n(),g=e=>{if(0===e)return\"0 B\";const s=Math.floor(Math.log(e)/Math.log(1024));return parseFloat((e/Math.pow(1024,s)).toFixed(2))+\" \"+[\"B\",\"KB\",\"MB\",\"GB\"][s]};return a&&0===e.length?i.jsxs(\"div\",{className:\"rounded-xl border border-border bg-card p-24 text-center\",children:[i.jsx(\"div\",{className:\"animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto\"}),i.jsx(\"p\",{className:\"mt-4 text-muted-foreground\",children:p(\"ui.common.loading\")})]}):a||0!==e.length?i.jsxs(\"div\",{className:\"space-y-4\",children:[i.jsx(\"div\",{className:\"rounded-xl border border-border bg-card/50 backdrop-blur-sm overflow-hidden shadow-sm overflow-x-auto\",children:i.jsxs(j,{children:[i.jsx(f,{className:\"bg-muted/50\",children:i.jsxs(N,{children:[i.jsx(w,{className:\"w-[180px] whitespace-nowrap shrink-0\",children:i.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[i.jsx(k,{className:\"h-3.5 w-3.5 text-muted-foreground\"}),\" \",p(\"ui.syncLog.time\")]})}),i.jsx(w,{className:\"w-[120px] whitespace-nowrap shrink-0\",children:i.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[i.jsx(C,{className:\"h-3.5 w-3.5 text-muted-foreground\"}),\" \",p(\"ui.syncLog.vault\")]})}),i.jsx(w,{className:\"w-[100px] whitespace-nowrap shrink-0\",children:i.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[i.jsx(S,{className:\"h-3.5 w-3.5 text-muted-foreground\"}),\" \",p(\"ui.syncLog.type\")]})}),i.jsx(w,{className:\"w-[110px] whitespace-nowrap shrink-0\",children:i.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[i.jsx(F,{className:\"h-3.5 w-3.5 text-muted-foreground\"}),\" \",p(\"ui.syncLog.action\")]})}),i.jsx(w,{className:\"min-w-[250px] whitespace-nowrap shrink-0\",children:i.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[i.jsx(M,{className:\"h-3.5 w-3.5 text-muted-foreground\"}),\" \",p(\"ui.syncLog.path\")]})}),i.jsx(w,{className:\"w-[100px] whitespace-nowrap shrink-0 text-right\",children:p(\"ui.syncLog.size\")}),i.jsx(w,{className:\"w-[100px] whitespace-nowrap shrink-0\",children:i.jsxs(\"div\",{className:\"flex items-center gap-2 justify-center\",children:[i.jsx(z,{className:\"h-3.5 w-3.5 text-muted-foreground\"}),\" \",p(\"ui.syncLog.client\")]})}),i.jsx(w,{className:\"w-[100px] whitespace-nowrap shrink-0\",children:p(\"ui.syncLog.status\")}),i.jsx(w,{className:\"min-w-[120px] whitespace-nowrap shrink-0\",children:p(\"ui.syncLog.changedFields\")})]})}),i.jsx(v,{children:e.map(e=>{var a,l,r;return i.jsxs(N,{className:\"hover:bg-muted/30 transition-colors group\",children:[i.jsx(b,{className:\"text-xs font-mono text-muted-foreground whitespace-nowrap shrink-0\",children:V(new Date(e.createdAt),\"yyyy-MM-dd HH:mm:ss\")}),i.jsx(b,{className:\"whitespace-nowrap shrink-0\",children:i.jsx(\"span\",{className:\"text-sm font-medium\",children:(null==(a=s.find(s=>String(s.id)===String(e.vaultId)))?void 0:a.vault)||`Vault-${e.vaultId}`})}),i.jsx(b,{className:\"whitespace-nowrap shrink-0\",children:(()=>{const s=e.type.toLowerCase(),a={note:\"#08b94e\",file:\"#7C4DFF\",setting:\"#FF8A33\",folder:\"#1E88E5\"}[s]||\"#666\";return i.jsx(y,{variant:\"outline\",className:\"text-white border-none font-normal px-2 py-0.5\",style:{backgroundColor:a},children:p(`ui.syncLog.type.${s}`,{defaultValue:e.type})})})()}),i.jsx(b,{className:\"whitespace-nowrap shrink-0\",children:i.jsx(\"span\",{className:d(\"text-xs font-medium px-2 py-0.5 rounded-full capitalize\",\"create\"===e.action?\"bg-blue-500/10 text-blue-500\":\"modify\"===e.action?\"bg-amber-500/10 text-amber-500\":\"soft_delete\"===e.action?\"bg-orange-500/10 text-orange-500\":\"delete\"===e.action?\"bg-rose-500/10 text-rose-500\":\"rename\"===e.action?\"bg-purple-500/10 text-purple-500\":\"restore\"===e.action?\"bg-emerald-500/10 text-emerald-500\":\"bg-muted text-muted-foreground\"),children:p(`ui.syncLog.action.${e.action.toLowerCase()}`,{defaultValue:e.action})})}),i.jsx(b,{className:\"whitespace-nowrap shrink-0\",children:i.jsxs(\"div\",{className:\"flex flex-col gap-0.5 max-w-[300px]\",children:[i.jsx(\"span\",{className:\"text-xs font-medium truncate\",title:e.path,children:e.path.split(\"/\").pop()}),i.jsx(\"span\",{className:\"text-[10px] text-muted-foreground truncate opacity-70\",title:e.path,children:e.path})]})}),i.jsx(b,{className:\"text-right text-xs font-mono text-muted-foreground whitespace-nowrap shrink-0\",children:g(e.size)}),i.jsx(b,{className:\"text-center whitespace-nowrap shrink-0\",children:i.jsx(L,{content:i.jsxs(\"div\",{className:\"flex flex-col gap-1.5 p-1 min-w-[120px]\",children:[i.jsxs(\"div\",{className:\"flex justify-between items-center gap-4\",children:[i.jsx(\"span\",{className:\"text-muted-foreground/70\",children:p(\"ui.system.wsClientName\")}),i.jsx(\"span\",{className:\"font-medium text-foreground capitalize\",children:e.clientType||p(\"ui.common.na\")})]}),i.jsxs(\"div\",{className:\"flex justify-between items-center gap-4 border-t border-border/30 pt-1.5\",children:[i.jsx(\"span\",{className:\"text-muted-foreground/70\",children:p(\"ui.share.version\")}),i.jsx(\"span\",{className:\"font-mono text-foreground font-medium\",children:e.clientVersion?`v${e.clientVersion}`:p(\"ui.common.na\")})]}),i.jsxs(\"div\",{className:\"flex justify-between items-center gap-4 border-t border-border/30 pt-1.5\",children:[i.jsx(\"span\",{className:\"text-muted-foreground/70\",children:p(\"ui.common.name\")}),i.jsx(\"span\",{className:\"font-semibold text-foreground\",children:e.clientName||p(\"ui.common.na\")})]})]}),children:i.jsx(\"div\",{className:\"flex items-center justify-center\",children:i.jsx(y,{variant:\"outline\",className:\"text-[10px] font-medium px-2 py-0.5 rounded-md hover:bg-primary/5 cursor-help transition-colors\",children:e.clientType||e.clientName||p(\"ui.common.na\")})})})}),i.jsx(b,{className:\"whitespace-nowrap shrink-0\",children:(r=e.status,1===r?i.jsxs(y,{variant:\"outline\",className:\"bg-emerald-500/10 text-emerald-500 border-emerald-500/20 gap-1 px-1.5 py-0.5\",children:[i.jsx(x,{className:\"h-3 w-3\"}),p(\"ui.syncLog.statusSuccess\")]}):i.jsxs(y,{variant:\"outline\",className:\"bg-rose-500/10 text-rose-500 border-rose-500/20 gap-1 px-1.5 py-0.5\",children:[i.jsx(u,{className:\"h-3 w-3\"}),p(\"ui.syncLog.statusFailed\")]}))}),i.jsxs(b,{className:\"whitespace-nowrap shrink-0\",children:[(l=e.changedFields,l?i.jsx(\"div\",{className:\"flex flex-wrap gap-1\",children:l.split(\",\").map((e,s)=>i.jsx(\"span\",{className:\"px-1.5 py-0.5 bg-primary/5 text-primary/70 text-[10px] rounded border border-primary/10 font-mono\",children:e.trim()},s))}):null),e.message&&i.jsx(\"p\",{className:\"mt-1 text-[10px] text-rose-500 italic truncate max-w-[200px]\",title:e.message,children:e.message})]})]},e.id)})})]})}),r>1&&i.jsxs(\"div\",{className:\"flex items-center justify-center gap-2 mt-6\",children:[i.jsx(o,{variant:\"outline\",size:\"icon\",className:\"h-9 w-9 rounded-xl\",disabled:1===l||a,onClick:()=>t(l-1),children:i.jsx(h,{className:\"h-4 w-4\"})}),i.jsxs(\"div\",{className:\"flex items-center gap-1 px-4 py-1.5 bg-muted/50 rounded-xl border border-border/50 shadow-inner\",children:[i.jsx(\"span\",{className:\"text-sm font-semibold\",children:l}),i.jsx(\"span\",{className:\"text-sm text-muted-foreground\",children:\"/\"}),i.jsx(\"span\",{className:\"text-sm text-muted-foreground\",children:r})]}),i.jsx(o,{variant:\"outline\",size:\"icon\",className:\"h-9 w-9 rounded-xl\",disabled:l===r||a,onClick:()=>t(l+1),children:i.jsx(m,{className:\"h-4 w-4\"})})]})]}):i.jsxs(\"div\",{className:\"rounded-xl border border-border bg-card p-24 text-center\",children:[i.jsx(\"div\",{className:\"p-4 rounded-full bg-muted w-16 h-16 flex items-center justify-center mx-auto mb-4\",children:i.jsx(c,{className:\"h-8 w-8 text-muted-foreground\"})}),i.jsx(\"p\",{className:\"text-lg font-medium text-foreground\",children:p(\"ui.syncLog.noLogs\")}),i.jsx(\"p\",{className:\"text-sm text-muted-foreground mt-1\",children:p(\"ui.syncLog.noLogsDescription\")})]})}function I({vault:e,onVaultChange:c}){const{t:m}=n(),{handleSyncLogList:x}=function(){const e=localStorage.getItem(\"token\"),n=s.useCallback(()=>({...a({token:e})}),[e]),i=s.useCallback(async(e,s,a)=>{try{const{vault:i,type:c,action:d,page:o=1,pageSize:m=20}=e,x=new URLSearchParams({page:o.toString(),pageSize:m.toString()});i&&x.append(\"vault\",i),c&&x.append(\"type\",c),d&&x.append(\"action\",d);const u=l(`${r.API_URL}/api/sync-logs?${x.toString()}`),h=await fetch(u,{method:\"GET\",headers:n()});if(!h.ok)throw new Error(\"Network response was not ok\");const p=await h.json();p.code>0&&p.code<=200?s(p.data):(t.error(p.message),a&&a())}catch(i){t.error(i instanceof Error?i.message:String(i)),a&&a()}},[n]);return s.useMemo(()=>({handleSyncLogList:i}),[i])}(),{handleVaultList:u}=p(),[h,j]=s.useState([]),[f,N]=s.useState(!1),[w,v]=s.useState([]),[b,y]=s.useState(e||\"all\"),[L,k]=s.useState(\"all\"),[C,S]=s.useState(\"all\"),[F,z]=s.useState(1),[M,V]=s.useState(1),I=s.useCallback(()=>{N(!0),x({vault:\"all\"===b?void 0:b,type:\"all\"===L?void 0:L,action:\"all\"===C?void 0:C,page:F,pageSize:20},e=>{var s;j(e.list||[]);const a=(null==(s=e.pager)?void 0:s.totalRows)||0;V(Math.max(1,Math.ceil(a/20))),N(!1)},()=>{N(!1)})},[x,b,L,C,F]);s.useEffect(()=>{u(e=>v(e))},[u]),s.useEffect(()=>{I()},[I]),s.useEffect(()=>{e&&e!==b?(y(e),z(1)):e||\"all\"===b||(y(\"all\"),z(1))},[e]);return i.jsxs(\"div\",{className:\"w-full space-y-6 pt-2\",children:[i.jsxs(\"div\",{className:\"flex flex-col lg:flex-row items-start lg:items-center justify-between gap-4\",children:[i.jsxs(\"div\",{className:\"flex items-center gap-4\",children:[i.jsx(\"div\",{className:\"flex h-12 w-12 items-center justify-center rounded-2xl bg-primary/10 text-primary shadow-inner\",children:i.jsx(g,{className:\"h-5 w-5\"})}),i.jsxs(\"div\",{children:[i.jsx(\"h2\",{className:\"text-2xl font-bold tracking-tight\",children:m(\"ui.syncLog.title\")}),i.jsx(\"p\",{className:\"text-sm text-muted-foreground\",children:m(\"ui.syncLog.description\")})]})]}),i.jsxs(\"div\",{className:\"flex flex-wrap items-center gap-3 w-full lg:w-auto\",children:[i.jsx(\"div\",{className:\"flex flex-col gap-1.5 min-w-[140px]\",children:i.jsxs(E,{value:b,onValueChange:e=>{y(e),z(1),c&&c(\"all\"===e?null:e)},children:[i.jsx(P,{className:\"rounded-xl h-10 bg-card hover:bg-muted/50 transition-colors\",children:i.jsx(A,{placeholder:m(\"ui.syncLog.vault\")})}),i.jsxs(B,{className:\"rounded-xl shadow-xl\",children:[i.jsx(T,{value:\"all\",className:\"rounded-lg\",children:m(\"ui.syncLog.allVaults\")}),w.map(e=>i.jsx(T,{value:e.vault,className:\"rounded-lg\",children:e.vault},e.id))]})]})}),i.jsx(\"div\",{className:\"flex flex-col gap-1.5 min-w-[110px]\",children:i.jsxs(E,{value:L,onValueChange:e=>{k(e),z(1)},children:[i.jsx(P,{className:\"rounded-xl h-10 bg-card hover:bg-muted/50 transition-colors\",children:i.jsx(A,{placeholder:m(\"ui.syncLog.type\")})}),i.jsxs(B,{className:\"rounded-xl shadow-xl\",children:[i.jsx(T,{value:\"all\",className:\"rounded-lg\",children:m(\"ui.syncLog.allTypes\")}),i.jsx(T,{value:\"note\",className:\"rounded-lg\",children:i.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[i.jsx(\"div\",{className:\"w-2 h-2 rounded-full bg-[#08b94e]\"}),i.jsx(\"span\",{className:\"uppercase\",children:m(\"ui.syncLog.type.note\")})]})}),i.jsx(T,{value:\"file\",className:\"rounded-lg\",children:i.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[i.jsx(\"div\",{className:\"w-2 h-2 rounded-full bg-[#7C4DFF]\"}),i.jsx(\"span\",{className:\"uppercase\",children:m(\"ui.syncLog.type.file\")})]})}),i.jsx(T,{value:\"folder\",className:\"rounded-lg\",children:i.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[i.jsx(\"div\",{className:\"w-2 h-2 rounded-full bg-[#1E88E5]\"}),i.jsx(\"span\",{className:\"uppercase\",children:m(\"ui.syncLog.type.folder\")})]})}),i.jsx(T,{value:\"setting\",className:\"rounded-lg\",children:i.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[i.jsx(\"div\",{className:\"w-2 h-2 rounded-full bg-[#FF8A33]\"}),i.jsx(\"span\",{className:\"uppercase\",children:m(\"ui.syncLog.type.setting\")})]})})]})]})}),i.jsx(\"div\",{className:\"flex flex-col gap-1.5 min-w-[110px]\",children:i.jsxs(E,{value:C,onValueChange:e=>{S(e),z(1)},children:[i.jsx(P,{className:\"rounded-xl h-10 bg-card hover:bg-muted/50 transition-colors\",children:i.jsx(A,{placeholder:m(\"ui.syncLog.action\")})}),i.jsxs(B,{className:\"rounded-xl shadow-xl\",children:[i.jsx(T,{value:\"all\",className:\"rounded-lg\",children:m(\"ui.syncLog.allActions\")}),i.jsx(T,{value:\"create\",className:\"rounded-lg\",children:i.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[i.jsx(\"div\",{className:\"w-2 h-2 rounded-full bg-blue-500\"}),i.jsx(\"span\",{className:\"uppercase\",children:m(\"ui.syncLog.action.create\")})]})}),i.jsx(T,{value:\"modify\",className:\"rounded-lg\",children:i.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[i.jsx(\"div\",{className:\"w-2 h-2 rounded-full bg-amber-500\"}),i.jsx(\"span\",{className:\"uppercase\",children:m(\"ui.syncLog.action.modify\")})]})}),i.jsx(T,{value:\"soft_delete\",className:\"rounded-lg\",children:i.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[i.jsx(\"div\",{className:\"w-2 h-2 rounded-full bg-orange-500\"}),i.jsx(\"span\",{className:\"uppercase\",children:m(\"ui.syncLog.action.soft_delete\")})]})}),i.jsx(T,{value:\"delete\",className:\"rounded-lg\",children:i.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[i.jsx(\"div\",{className:\"w-2 h-2 rounded-full bg-rose-500\"}),i.jsx(\"span\",{className:\"uppercase\",children:m(\"ui.syncLog.action.delete\")})]})}),i.jsx(T,{value:\"rename\",className:\"rounded-lg\",children:i.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[i.jsx(\"div\",{className:\"w-2 h-2 rounded-full bg-purple-500\"}),i.jsx(\"span\",{className:\"uppercase\",children:m(\"ui.syncLog.action.rename\")})]})}),i.jsx(T,{value:\"restore\",className:\"rounded-lg\",children:i.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[i.jsx(\"div\",{className:\"w-2 h-2 rounded-full bg-emerald-500\"}),i.jsx(\"span\",{className:\"uppercase\",children:m(\"ui.syncLog.action.restore\")})]})})]})]})}),i.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[i.jsx(o,{variant:\"outline\",size:\"icon\",className:d(\"h-10 w-10 rounded-xl bg-card border-border hover:bg-muted/50 transition-all\",(\"all\"!==b||\"all\"!==L||\"all\"!==C)&&\"border-primary/50 text-primary bg-primary/5\"),onClick:()=>{y(\"all\"),k(\"all\"),S(\"all\"),z(1)},title:m(\"ui.syncLog.resetFilters\"),children:i.jsx(D,{className:\"h-4 w-4\"})}),i.jsx(o,{variant:\"outline\",size:\"icon\",className:\"h-10 w-10 rounded-xl bg-card border-border hover:bg-muted/50 transition-all\",onClick:I,disabled:f,title:m(\"ui.common.refresh\"),children:i.jsx($,{className:d(\"h-4 w-4\",f&&\"animate-spin\")})})]})]})]}),i.jsx(H,{logs:h,vaults:w,loading:f,currentPage:F,totalPages:M,onPageChange:z})]})}export{I as SyncLogManager};\n"
  },
  {
    "path": "frontend/assets/system-settings-DSUsRYMo.js",
    "content": "import{c as e,r as s,af as t,ag as a,j as r,ah as n,ai as i,aj as l,ak as o,al as d,am as c,an as m,ao as u,f as x,a as h,e as p,b as g,u as f,ae as j,B as y,ap as v,a0 as N,t as b,n as w,o as k,q as C,aq as S,s as M,F as T,C as I,ad as L,I as _,L as D}from\"./font-loader-CIrh3KnA.js\";import{S as E,L as R,A as F,a as A,b as $,c as U,d as P,e as z,f as V,g as q,G as H}from\"./alert-dialog-CfMssux5.js\";import{S as K,a as G,b as O,c as B,d as W}from\"./select-CJF_alSt.js\";import{C as J}from\"./checkbox-DhTHgmeh.js\";import{u as Z,E as X,C as Q,L as Y}from\"./main-BIi-kGYY.js\";import{G as ee}from\"./git-branch-B1vNHBXG.js\";import{R as se}from\"./refresh-cw-BxIJAPy3.js\";import{C as te}from\"./circle-alert-EFzISefA.js\";import{B as ae}from\"./badge-C63ATniC.js\";import{M as re,A as ne}from\"./monitor-BGNS5Y9j.js\";import{T as ie}from\"./tooltip-Dr-qRlmI.js\";import{A as le,D as oe}from\"./database-eyf5nvY6.js\";import{S as de}from\"./server-DzJVVqse.js\";import{G as ce}from\"./github-Bzk-4SPC.js\";import{S as me}from\"./share-2-BVJjAadJ.js\";import{H as ue}from\"./hard-drive-Dw58lXyp.js\";import{T as xe}from\"./trash-2-ad7PiUnC.js\";import{C as he}from\"./clock-C9LPHszx.js\";import{a as pe,E as ge}from\"./eye-DrvrOb4o.js\";import{Z as fe}from\"./zap-CLLhzk_y.js\";import{D as je}from\"./download-CKtDCbjj.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const ye=e(\"BookOpen\",[[\"path\",{d:\"M12 7v14\",key:\"1akyts\"}],[\"path\",{d:\"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z\",key:\"ruj8y\"}]]),ve=e(\"CircleArrowUp\",[[\"circle\",{cx:\"12\",cy:\"12\",r:\"10\",key:\"1mglay\"}],[\"path\",{d:\"m16 12-4-4-4 4\",key:\"177agl\"}],[\"path\",{d:\"M12 16V8\",key:\"1sbj14\"}]]),Ne=e(\"Coffee\",[[\"path\",{d:\"M10 2v2\",key:\"7u0qdc\"}],[\"path\",{d:\"M14 2v2\",key:\"6buw04\"}],[\"path\",{d:\"M16 8a1 1 0 0 1 1 1v8a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1h14a4 4 0 1 1 0 8h-1\",key:\"pwadti\"}],[\"path\",{d:\"M6 2v2\",key:\"colzsn\"}]]),be=e(\"Cpu\",[[\"rect\",{width:\"16\",height:\"16\",x:\"4\",y:\"4\",rx:\"2\",key:\"14l7u7\"}],[\"rect\",{width:\"6\",height:\"6\",x:\"9\",y:\"9\",rx:\"1\",key:\"5aljv4\"}],[\"path\",{d:\"M15 2v2\",key:\"13l42r\"}],[\"path\",{d:\"M15 20v2\",key:\"15mkzm\"}],[\"path\",{d:\"M2 15h2\",key:\"1gxd5l\"}],[\"path\",{d:\"M2 9h2\",key:\"1bbxkp\"}],[\"path\",{d:\"M20 15h2\",key:\"19e6y8\"}],[\"path\",{d:\"M20 9h2\",key:\"19tzq7\"}],[\"path\",{d:\"M9 2v2\",key:\"165o2o\"}],[\"path\",{d:\"M9 20v2\",key:\"i2bqo8\"}]]),we=e(\"Heart\",[[\"path\",{d:\"M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z\",key:\"c3ymky\"}]]),ke=e(\"MessageCircle\",[[\"path\",{d:\"M7.9 20A9 9 0 1 0 4 16.1L2 22Z\",key:\"vv11sd\"}]]),Ce=e(\"QrCode\",[[\"rect\",{width:\"5\",height:\"5\",x:\"3\",y:\"3\",rx:\"1\",key:\"1tu5fj\"}],[\"rect\",{width:\"5\",height:\"5\",x:\"16\",y:\"3\",rx:\"1\",key:\"1v8r4q\"}],[\"rect\",{width:\"5\",height:\"5\",x:\"3\",y:\"16\",rx:\"1\",key:\"1x03jg\"}],[\"path\",{d:\"M21 16h-3a2 2 0 0 0-2 2v3\",key:\"177gqh\"}],[\"path\",{d:\"M21 21v.01\",key:\"ents32\"}],[\"path\",{d:\"M12 7v3a2 2 0 0 1-2 2H7\",key:\"8crl2c\"}],[\"path\",{d:\"M3 12h.01\",key:\"nlz23k\"}],[\"path\",{d:\"M12 3h.01\",key:\"n36tog\"}],[\"path\",{d:\"M12 16v.01\",key:\"133mhm\"}],[\"path\",{d:\"M16 12h1\",key:\"1slzba\"}],[\"path\",{d:\"M21 12v.01\",key:\"1lwtk9\"}],[\"path\",{d:\"M12 21v-1\",key:\"1880an\"}]]),Se=e(\"Router\",[[\"rect\",{width:\"20\",height:\"8\",x:\"2\",y:\"14\",rx:\"2\",key:\"w68u3i\"}],[\"path\",{d:\"M6.01 18H6\",key:\"19vcac\"}],[\"path\",{d:\"M10.01 18H10\",key:\"uamcmx\"}],[\"path\",{d:\"M15 10v4\",key:\"qjz1xs\"}],[\"path\",{d:\"M17.84 7.17a4 4 0 0 0-5.66 0\",key:\"1rif40\"}],[\"path\",{d:\"M20.66 4.34a8 8 0 0 0-11.31 0\",key:\"6a5xfq\"}]]),Me=e(\"Save\",[[\"path\",{d:\"M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z\",key:\"1c8476\"}],[\"path\",{d:\"M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7\",key:\"1ydtos\"}],[\"path\",{d:\"M7 3v4a1 1 0 0 0 1 1h7\",key:\"t51u73\"}]]),Te=e(\"Send\",[[\"path\",{d:\"M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z\",key:\"1ffxy3\"}],[\"path\",{d:\"m21.854 2.147-10.94 10.939\",key:\"12cjpa\"}]]),Ie=e(\"Shield\",[[\"path\",{d:\"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z\",key:\"oel41y\"}]]),Le=e(\"SlidersHorizontal\",[[\"line\",{x1:\"21\",x2:\"14\",y1:\"4\",y2:\"4\",key:\"obuewd\"}],[\"line\",{x1:\"10\",x2:\"3\",y1:\"4\",y2:\"4\",key:\"1q6298\"}],[\"line\",{x1:\"21\",x2:\"12\",y1:\"12\",y2:\"12\",key:\"1iu8h1\"}],[\"line\",{x1:\"8\",x2:\"3\",y1:\"12\",y2:\"12\",key:\"ntss68\"}],[\"line\",{x1:\"21\",x2:\"16\",y1:\"20\",y2:\"20\",key:\"14d8ph\"}],[\"line\",{x1:\"12\",x2:\"3\",y1:\"20\",y2:\"20\",key:\"m0wm8r\"}],[\"line\",{x1:\"14\",x2:\"14\",y1:\"2\",y2:\"6\",key:\"14e1ph\"}],[\"line\",{x1:\"8\",x2:\"8\",y1:\"10\",y2:\"14\",key:\"1i6ji0\"}],[\"line\",{x1:\"16\",x2:\"16\",y1:\"18\",y2:\"22\",key:\"1lctlv\"}]]),_e=e(\"Smile\",[[\"circle\",{cx:\"12\",cy:\"12\",r:\"10\",key:\"1mglay\"}],[\"path\",{d:\"M8 14s1.5 2 4 2 4-2 4-2\",key:\"1y1vjs\"}],[\"line\",{x1:\"9\",x2:\"9.01\",y1:\"9\",y2:\"9\",key:\"yxxnd0\"}],[\"line\",{x1:\"15\",x2:\"15.01\",y1:\"9\",y2:\"9\",key:\"1p4y9e\"}]]),De=e(\"Tag\",[[\"path\",{d:\"M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z\",key:\"vktsd0\"}],[\"circle\",{cx:\"7.5\",cy:\"7.5\",r:\".5\",fill:\"currentColor\",key:\"kqv944\"}]]),Ee=e(\"Type\",[[\"polyline\",{points:\"4 7 4 4 20 4 20 7\",key:\"1nosan\"}],[\"line\",{x1:\"9\",x2:\"15\",y1:\"20\",y2:\"20\",key:\"swin9y\"}],[\"line\",{x1:\"12\",x2:\"12\",y1:\"4\",y2:\"20\",key:\"1tx1rr\"}]]),Re=e(\"UserPlus\",[[\"path\",{d:\"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2\",key:\"1yyitq\"}],[\"circle\",{cx:\"9\",cy:\"7\",r:\"4\",key:\"nufk8\"}],[\"line\",{x1:\"19\",x2:\"19\",y1:\"8\",y2:\"14\",key:\"1bvyxn\"}],[\"line\",{x1:\"22\",x2:\"16\",y1:\"11\",y2:\"11\",key:\"1shjgl\"}]]);\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */var Fe=\"Tabs\",[Ae]=n(Fe,[o]),$e=o(),[Ue,Pe]=Ae(Fe),ze=s.forwardRef((e,s)=>{const{__scopeTabs:n,value:o,onValueChange:d,defaultValue:c,orientation:m=\"horizontal\",dir:u,activationMode:x=\"automatic\",...h}=e,p=t(u),[g,f]=a({prop:o,onChange:d,defaultProp:c??\"\",caller:Fe});return r.jsx(Ue,{scope:n,baseId:i(),value:g,onValueChange:f,orientation:m,dir:p,activationMode:x,children:r.jsx(l.div,{dir:p,\"data-orientation\":m,...h,ref:s})})});ze.displayName=Fe;var Ve=\"TabsList\",qe=s.forwardRef((e,s)=>{const{__scopeTabs:t,loop:a=!0,...n}=e,i=Pe(Ve,t),o=$e(t);return r.jsx(d,{asChild:!0,...o,orientation:i.orientation,dir:i.dir,loop:a,children:r.jsx(l.div,{role:\"tablist\",\"aria-orientation\":i.orientation,...n,ref:s})})});qe.displayName=Ve;var He=\"TabsTrigger\",Ke=s.forwardRef((e,s)=>{const{__scopeTabs:t,value:a,disabled:n=!1,...i}=e,o=Pe(He,t),d=$e(t),u=Be(o.baseId,a),x=We(o.baseId,a),h=a===o.value;return r.jsx(c,{asChild:!0,...d,focusable:!n,active:h,children:r.jsx(l.button,{type:\"button\",role:\"tab\",\"aria-selected\":h,\"aria-controls\":x,\"data-state\":h?\"active\":\"inactive\",\"data-disabled\":n?\"\":void 0,disabled:n,id:u,...i,ref:s,onMouseDown:m(e.onMouseDown,e=>{n||0!==e.button||!1!==e.ctrlKey?e.preventDefault():o.onValueChange(a)}),onKeyDown:m(e.onKeyDown,e=>{[\" \",\"Enter\"].includes(e.key)&&o.onValueChange(a)}),onFocus:m(e.onFocus,()=>{const e=\"manual\"!==o.activationMode;h||n||!e||o.onValueChange(a)})})})});Ke.displayName=He;var Ge=\"TabsContent\",Oe=s.forwardRef((e,t)=>{const{__scopeTabs:a,value:n,forceMount:i,children:o,...d}=e,c=Pe(Ge,a),m=Be(c.baseId,n),x=We(c.baseId,n),h=n===c.value,p=s.useRef(h);return s.useEffect(()=>{const e=requestAnimationFrame(()=>p.current=!1);return()=>cancelAnimationFrame(e)},[]),r.jsx(u,{present:i||h,children:({present:s})=>r.jsx(l.div,{\"data-state\":h?\"active\":\"inactive\",\"data-orientation\":c.orientation,role:\"tabpanel\",\"aria-labelledby\":m,hidden:!s,id:x,tabIndex:0,...d,ref:t,style:{...e.style,animationDuration:p.current?\"0s\":void 0},children:s&&o})})});function Be(e,s){return`${e}-trigger-${s}`}function We(e,s){return`${e}-content-${s}`}Oe.displayName=Ge;var Je=qe,Ze=Ke,Xe=Oe;const Qe=ze,Ye=s.forwardRef(({className:e,...s},t)=>r.jsx(Je,{ref:t,className:x(\"inline-flex h-10 items-center justify-center text-muted-foreground\",e),...s}));Ye.displayName=Je.displayName;const es=s.forwardRef(({className:e,...s},t)=>r.jsx(Ze,{ref:t,className:x(\"inline-flex items-center justify-center whitespace-nowrap text-sm font-medium transition-all outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm\",e),...s}));es.displayName=Ze.displayName;const ss=s.forwardRef(({className:e,...s},t)=>r.jsx(Xe,{ref:t,className:x(\"mt-2 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]\",e),...s}));ss.displayName=Xe.displayName;const ts=e=>{if(!e)return null;try{const s=new URL(e);return\"http:\"===s.protocol||\"https:\"===s.protocol?e:null}catch{return null}};function as({showUpgrade:e=!0,children:t}){const{t:a}=f(),{versionInfo:n,isLoading:i}=function(){const{t:e}=f(),{setVersionInfo:t}=Z(),[a,r]=s.useState(null),[n,i]=s.useState(!1),[l,o]=s.useState(null);return s.useEffect(()=>{let s=!0;return(async()=>{i(!0),o(null);try{const a=await fetch(h(p.API_URL+\"/api/version\"),{method:\"GET\",headers:g({token:null})});if(!a.ok)throw new Error(\"Network response was not ok\");const n=await a.json();if(!s)return;n.code<100&&n.code>0&&n.data?(r(n.data),t(n.data)):o(n.message||e(\"getVersionError\"))}catch(a){if(!s)return;o(e(\"getVersionError\"))}finally{s&&i(!1)}})(),()=>{s=!1}},[t,e]),{versionInfo:a,isLoading:n,error:l}}(),{checkUpdate:l,isChecking:o,updateResult:d}=function(){const[e,t]=s.useState(!1),[a,r]=s.useState(null),[n,i]=s.useState(null);return{checkUpdate:s.useCallback(async e=>{if(!e)return r(\"Current version is unknown\"),null;t(!0),r(null);try{const e=await fetch(h(p.API_URL+\"/api/version\"),{headers:g({token:null})});if(!e.ok)throw new Error(`GitHub API error: ${e.status}`);const s=await e.json();if(s.code>=100||s.code<=0||!s.data)throw new Error(s.message||\"Failed to get version info\");const t=s.data,a={hasUpdate:t.versionIsNew||!1,latestVersion:(t.versionIsNew?t.versionNewName:t.version)||t.gitTag,releaseUrl:t.versionNewLink,releaseNotes:t.versionNewChangelog,releaseNotesContent:t.versionNewChangelogContent,publishedAt:t.buildTime};return i(a),a}catch(s){const e=s instanceof Error?s.message:\"Failed to check for updates\";return r(e),null}finally{t(!1)}},[]),isChecking:e,error:a,updateResult:n}}(),[c,m]=s.useState(!1),u=s.useRef(null),x=s.useRef(!1);s.useEffect(()=>()=>{x.current=!0,null!==u.current&&window.clearTimeout(u.current)},[]);const w=ts(null==n?void 0:n.versionNewLink),k=ts((null==d?void 0:d.releaseUrl)||(null==n?void 0:n.versionNewLink));return r.jsxs(\"div\",{className:\"rounded-xl border border-border bg-card p-6 space-y-4 custom-shadow\",children:[r.jsxs(\"h2\",{className:\"text-lg font-bold text-card-foreground flex items-center gap-2\",children:[r.jsx(j,{className:\"h-5 w-5\"}),a(\"ui.system.versionInfo\")]}),r.jsxs(\"div\",{className:\"flex flex-col space-y-3\",children:[r.jsxs(\"div\",{className:\"flex items-center justify-between gap-4 text-sm\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-2 text-muted-foreground font-medium\",children:[r.jsx(ee,{className:\"h-4 w-4\"}),r.jsx(\"span\",{children:a(\"ui.system.repo\")})]}),r.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[r.jsx(\"a\",{href:\"https://github.com/haierkeys/fast-note-sync-service\",target:\"_blank\",rel:\"noopener noreferrer\",className:\"text-primary hover:underline truncate max-w-37.5 sm:max-w-none\",children:a(\"ui.system.githubRepo\")}),r.jsx(\"span\",{className:\"text-muted-foreground\",children:\"/\"}),r.jsx(\"a\",{href:\"https://cnb.cool/haierkeys/fast-note-sync-service\",target:\"_blank\",rel:\"noopener noreferrer\",className:\"text-primary hover:underline truncate max-w-37.5 sm:max-w-none\",children:a(\"ui.system.cnbMirror\")})]})]}),r.jsxs(\"div\",{className:\"flex items-center justify-between gap-4 text-sm\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-2 text-muted-foreground font-medium\",children:[r.jsx(De,{className:\"h-4 w-4\"}),r.jsx(\"span\",{children:a(\"ui.system.currentVersion\")})]}),r.jsx(\"div\",{className:\"flex items-center gap-2\",children:i?r.jsx(\"code\",{className:\"font-mono text-muted-foreground\",children:a(\"ui.common.loading\")}):r.jsx(r.Fragment,{children:w?r.jsx(\"a\",{href:w,target:\"_blank\",rel:\"noopener noreferrer\",className:\"font-mono text-primary hover:underline\",children:null==n?void 0:n.version}):r.jsx(\"code\",{className:\"font-mono text-muted-foreground\",children:(null==n?void 0:n.version)||a(\"ui.common.unknown\")})})})]})]}),r.jsx(\"div\",{className:\"border-t border-border/50\"}),r.jsxs(\"div\",{className:\"space-y-3 text-sm\",children:[r.jsxs(\"div\",{className:\"flex items-center justify-between gap-4\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-2 text-muted-foreground font-medium\",children:[r.jsx(se,{className:\"h-4 w-4 \"+(o?\"animate-spin\":\"\")}),r.jsx(\"span\",{children:a(\"ui.system.checkUpdate\")})]}),r.jsx(y,{variant:\"outline\",size:\"sm\",onClick:async()=>{if(null==n?void 0:n.version){const e=await l(n.version);e&&b.success(e.hasUpdate?a(\"ui.system.newVersionAvailable\"):a(\"ui.system.alreadyLatest\"))}},disabled:o||i||!(null==n?void 0:n.version),className:\"rounded-xl px-3\",children:a(o?\"ui.system.checking\":\"ui.system.checkNow\")})]}),d||(null==n?void 0:n.versionIsNew)?r.jsx(\"div\",{className:\"rounded-xl p-4 \"+((null==d?void 0:d.hasUpdate)||(null==n?void 0:n.versionIsNew)?\"bg-primary/10 border border-primary/20\":\"bg-muted/50\"),children:r.jsxs(\"div\",{className:\"flex-1 space-y-2\",children:[r.jsxs(\"div\",{className:\"flex items-start gap-3\",children:[(null==d?void 0:d.hasUpdate)||(null==n?void 0:n.versionIsNew)?r.jsx(te,{className:\"h-5 w-5 text-primary shrink-0 mt-0.5\"}):r.jsx(v,{className:\"h-5 w-5 text-green-500 shrink-0 mt-0.5\"}),r.jsxs(\"div\",{className:\"flex-1 flex items-center justify-between\",children:[r.jsx(\"span\",{className:\"text-sm font-medium\",children:(null==d?void 0:d.hasUpdate)||(null==n?void 0:n.versionIsNew)?a(\"ui.system.newVersionAvailable\"):a(\"ui.system.alreadyLatest\")}),r.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[((null==d?void 0:d.latestVersion)||(null==n?void 0:n.versionNewName)||(null==n?void 0:n.version))&&r.jsx(\"code\",{className:\"text-xs font-mono bg-background px-2 py-0.5 rounded\",children:(null==d?void 0:d.latestVersion)||(null==n?void 0:n.versionNewName)||(null==n?void 0:n.version)}),((null==d?void 0:d.hasUpdate)||(null==n?void 0:n.versionIsNew))&&k&&r.jsxs(\"a\",{href:k,target:\"_blank\",rel:\"noopener noreferrer\",className:\"inline-flex items-center gap-1 text-xs text-primary hover:underline\",children:[a(\"ui.system.viewRelease\"),\" \",r.jsx(X,{className:\"h-3 w-3\"})]}),e&&((null==d?void 0:d.hasUpdate)||(null==n?void 0:n.versionIsNew))&&r.jsxs(y,{variant:\"ghost\",size:\"sm\",disabled:c,onClick:async()=>{const e=localStorage.getItem(\"token\");if(e){x.current=!1,null!==u.current&&(window.clearTimeout(u.current),u.current=null),m(!0);try{const s=await fetch(h(p.API_URL+\"/api/admin/upgrade?version=latest\"),{headers:g({token:e,includeContentType:!1,includeDomain:!1})}),t=await s.json();if(0===t.code||t.code<100&&t.code>0){b.success(a(\"ui.system.upgradeSuccess\"));const e=Date.now();let s=!1;const t=async()=>{if(!x.current){if(Date.now()-e>=3e5)return b.error(a(\"ui.system.upgradeRefreshTimeout\")),void m(!1);try{const e=new AbortController,t=setTimeout(()=>e.abort(),5e3),a=await fetch(h(p.API_URL+\"/api/health\"),{signal:e.signal});if(clearTimeout(t),a.ok){if(s)return void window.location.reload()}else s=!0}catch{s=!0}u.current=window.setTimeout(()=>{t()},2e3)}};t()}else b.error(t.message||a(\"ui.system.upgradeFailed\")),m(!1)}catch{b.error(a(\"ui.system.upgradeFailed\")),m(!1)}}},className:\"h-6 px-2 text-xs text-primary hover:bg-primary/10 rounded-md gap-1 ml-1 relative group transition-all duration-300\",children:[c?r.jsx(N,{className:\"h-3 w-3 animate-spin\"}):r.jsx(ve,{className:\"h-3 w-3 group-hover:scale-110 transition-transform duration-300\"}),r.jsxs(\"span\",{className:\"relative\",children:[a(c?\"ui.system.upgrading\":\"ui.system.upgradeNow\"),!c&&r.jsxs(\"span\",{className:\"absolute -top-1 -right-1.5 flex h-1.5 w-1.5\",children:[r.jsx(\"span\",{className:\"animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75\"}),r.jsx(\"span\",{className:\"relative inline-flex rounded-full h-1.5 w-1.5 bg-red-500 border border-background\"})]})]})]})]})]})]}),r.jsx(\"div\",{className:\"space-y-2\",children:((null==d?void 0:d.releaseNotesContent)||(null==n?void 0:n.versionNewChangelogContent))&&r.jsx(\"div\",{className:\"text-xs text-muted-foreground bg-background/50 p-3 rounded-lg border border-border/50 max-h-40 overflow-y-auto whitespace-pre-wrap leading-relaxed\",children:(null==d?void 0:d.releaseNotesContent)||(null==n?void 0:n.versionNewChangelogContent)})})]})}):null]}),t&&r.jsxs(r.Fragment,{children:[r.jsx(\"div\",{className:\"border-t border-border\"}),t]})]})}function rs(){const{t:e}=f(),{clients:t,isLoading:a,refresh:n}=function(){const[e,t]=s.useState([]),[a,r]=s.useState(!1),[n,i]=s.useState(null),l=localStorage.getItem(\"token\"),o=s.useCallback(async e=>{if(l){r(!0),i(null);try{const s=await fetch(h(p.API_URL+\"/api/admin/ws_clients\"),{method:\"GET\",headers:g({token:l,includeDomain:!1})});if(!s.ok)throw new Error(\"Network response was not ok\");const a=await s.json();if(e&&!e.current)return;1===a.code||!0===a.status?t(a.data||[]):i(a.message||\"Failed to get WS clients\")}catch(s){if(e&&!e.current)return;if(\"AbortError\"===s.name)return;i(\"Failed to get WS clients\")}finally{e&&!e.current||r(!1)}}},[l]);return s.useEffect(()=>{const e={current:!0};return o(e),()=>{e.current=!1}},[o]),{clients:e,isLoading:a,error:n,refresh:o}}();return r.jsxs(\"div\",{className:\"rounded-xl border border-border bg-card p-6 space-y-4 custom-shadow\",children:[r.jsxs(\"div\",{className:\"flex items-center justify-between\",children:[r.jsxs(\"h2\",{className:\"text-lg font-bold text-card-foreground flex items-center gap-2\",children:[r.jsx(re,{className:\"h-5 w-5\"}),e(\"ui.system.websocketClients\")]}),r.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[t.length>0&&r.jsxs(ae,{variant:\"outline\",className:\"text-[10px] font-normal opacity-70\",children:[t.length,\" \",e(\"ui.common.count\")]}),r.jsx(y,{variant:\"ghost\",size:\"icon\",onClick:()=>n(),disabled:a,className:\"h-8 w-8 rounded-full hover:bg-muted\",children:r.jsx(se,{className:\"h-4 w-4 \"+(a?\"animate-spin\":\"\")})})]})]}),r.jsx(\"div\",{className:\"space-y-3\",children:0===t.length?r.jsx(\"div\",{className:\"text-center py-6 text-xs text-muted-foreground italic border border-dashed border-border/50 rounded-lg\",children:a?r.jsxs(\"div\",{className:\"flex items-center justify-center gap-2\",children:[r.jsx(N,{className:\"h-4 w-4 animate-spin\"}),r.jsx(\"span\",{children:e(\"ui.common.loading\")})]}):e(\"ui.system.wsNoClients\")}):r.jsx(\"div\",{className:\"grid grid-cols-1 gap-3\",children:t.map(s=>{var t;return r.jsxs(\"div\",{className:\"p-3 bg-secondary/20 rounded-lg border border-border/50 space-y-2 relative overflow-hidden group\",children:[r.jsxs(\"div\",{className:\"flex items-center justify-between\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-2\",children:[r.jsx(\"div\",{className:\"p-1.5 bg-background rounded-md border border-border/50\",children:(null==(t=s.platformInfo)?void 0:t.isMobile)?r.jsx(E,{className:\"h-3.5 w-3.5 text-primary\"}):r.jsx(R,{className:\"h-3.5 w-3.5 text-primary\"})}),r.jsxs(\"div\",{children:[r.jsxs(\"div\",{className:\"text-xs font-bold leading-none flex items-center gap-1.5\",children:[s.clientName||s.nickname||e(\"ui.common.unknown\"),r.jsxs(\"span\",{className:\"text-[10px] font-normal text-muted-foreground opacity-70\",children:[\"v\",s.clientVersion]})]}),r.jsx(\"div\",{className:\"text-[10px] text-muted-foreground mt-1 font-mono opacity-80\",children:s.remoteAddr})]})]}),r.jsx(ae,{variant:\"secondary\",className:\"text-[10px] font-medium h-5\",children:s.clientType})]}),r.jsxs(\"div\",{className:\"flex items-center justify-between pt-1 border-t border-border/10\",children:[r.jsxs(\"div\",{className:\"text-[10px] text-muted-foreground\",children:[e(\"ui.system.wsStartTime\"),\": \",new Date(s.startTime).toLocaleString()]}),r.jsx(\"div\",{className:\"text-[10px] text-muted-foreground font-mono opacity-40 group-hover:opacity-100 transition-opacity\",children:e(\"ui.auth.userUid\",{uid:s.uid})})]})]},s.traceId)})})})]})}function ns(){const{t:e}=f(),{supportList:t,pager:a,isLoading:n,error:i,refresh:l}=function(){const[e,t]=s.useState([]),[a,r]=s.useState({page:1,pageSize:20,totalRows:0}),[n,i]=s.useState(!1),[l,o]=s.useState(null),d=s.useRef(0),c=s.useCallback(async(e=1,s=20,a=\"amount\",n=\"desc\")=>{const l=++d.current;i(!0),o(null);try{const i=Math.floor(e).toString(),c=Math.floor(s).toString();let m=`${p.API_URL}/api/support?page=${i}&pageSize=${c}`;a&&(m+=`&sortBy=${a}`),n&&(m+=`&sortOrder=${n}`);const u=await fetch(h(m),{method:\"GET\",headers:g({token:null,includeDomain:!1})});if(!u.ok)throw new Error(\"Network response was not ok\");const x=await u.json();if(d.current!==l)return;0===x.code||x.code<100&&x.code>0?x.data&&Array.isArray(x.data.list)?(t(x.data.list||[]),r(x.data.pager||{page:e,pageSize:s,totalRows:0})):Array.isArray(x.data)?(t(x.data||[]),r({page:1,pageSize:x.data.length,totalRows:x.data.length})):(t([]),r({page:e,pageSize:s,totalRows:0})):o(x.message||\"Failed to get support records\")}catch(c){if(d.current!==l)return;o(\"Failed to get support records\")}finally{d.current===l&&i(!1)}},[]);return s.useEffect(()=>()=>{d.current++},[]),{supportList:e,pager:a,isLoading:n,error:l,refresh:c}}(),[o,d]=s.useState(1),[c]=s.useState(20),[m,u]=s.useState(\"amount\"),[x,j]=s.useState(\"desc\");s.useEffect(()=>{l(o,c,m,x)},[l,o,c,m,x]);const v=(e,s)=>{u(e),j(s),d(1)};return n&&0===t.length?r.jsxs(\"div\",{className:\"flex flex-col items-center justify-center p-12 h-full\",children:[r.jsx(N,{className:\"h-8 w-8 animate-spin text-muted-foreground mb-4\"}),r.jsx(\"span\",{className:\"text-sm text-muted-foreground\",children:e(\"ui.common.loading\")})]}):i&&0===t.length?r.jsxs(\"div\",{className:\"flex flex-col items-center justify-center p-12 h-full space-y-4\",children:[r.jsx(te,{className:\"h-8 w-8 text-destructive opacity-50\"}),r.jsx(\"div\",{className:\"text-sm text-destructive font-medium\",children:i}),r.jsxs(y,{variant:\"outline\",size:\"sm\",onClick:()=>l(o,c,m,x),className:\"rounded-xl border-primary/20 hover:border-primary/50\",children:[r.jsx(se,{className:\"h-3 w-3 mr-2\"}),e(\"ui.common.retry\",{defaultValue:\"重试\"})]})]}):r.jsxs(\"div\",{className:\"flex flex-col h-full space-y-1.5 focus-visible:outline-none\",children:[r.jsxs(\"div\",{className:\"flex items-center justify-between pb-0.5\",children:[r.jsxs(\"h2\",{className:\"text-lg font-bold text-card-foreground flex items-center gap-2\",children:[r.jsx(we,{className:\"h-5 w-5 text-red-500 fill-red-500/10\"}),e(\"ui.support.title\")]}),r.jsx(y,{variant:\"ghost\",size:\"icon\",onClick:()=>l(o,c,m,x),disabled:n,className:\"h-6 w-6 rounded-full hover:bg-muted\",children:r.jsx(se,{className:\"h-3 w-3 \"+(n?\"animate-spin\":\"\")})})]}),r.jsx(\"p\",{className:\"text-xs leading-relaxed text-muted-foreground bg-muted/30 p-2.5 rounded-lg border border-border/40\",children:e(\"ui.support.supportRequest\")}),r.jsxs(\"div\",{className:\"grid grid-cols-2 gap-2 pb-2\",children:[r.jsxs(\"a\",{href:\"https://ko-fi.com/haierkeys\",target:\"_blank\",rel:\"noopener noreferrer\",className:\"group relative flex flex-col items-center justify-center p-3 rounded-xl bg-orange-500/5 border border-orange-500/10 hover:bg-orange-500/10 hover:border-orange-500/30 transition-all cursor-pointer overflow-hidden\",children:[r.jsx(\"div\",{className:\"absolute top-1 right-1 opacity-0 group-hover:opacity-40 transition-opacity\",children:r.jsx(X,{className:\"h-2.5 w-2.5 text-orange-600\"})}),r.jsx(\"img\",{src:\"/static/images/kofi.png\",alt:\"Ko-fi\",className:\"h-16 w-auto object-contain mb-2 transition-transform group-hover:scale-105\"}),r.jsxs(\"div\",{className:\"flex items-center gap-1.5 text-[11px] font-bold text-orange-600/80\",children:[r.jsx(Ne,{className:\"h-3.5 w-3.5\"}),e(\"ui.support.buyMeACoffee\")]})]}),r.jsx(\"div\",{className:\"group relative flex flex-col items-center justify-center p-3 rounded-xl bg-green-500/5 border border-green-500/10 hover:bg-green-500/10 hover:border-green-500/30 transition-all overflow-hidden\",children:r.jsx(ie,{content:r.jsx(\"img\",{src:\"/static/images/wxds.png\",alt:\"WeChat Pay\",className:\"w-56 h-auto rounded\"}),side:\"top\",className:\"p-1.5 bg-white border-0 shadow-2xl\",children:r.jsxs(\"div\",{className:\"flex flex-col items-center justify-center w-full h-full cursor-help\",children:[r.jsx(\"img\",{src:\"/static/images/wxds.png\",alt:\"WeChat Pay\",className:\"h-16 w-auto object-contain mb-2 rounded-sm opacity-90 group-hover:opacity-100 transition-all group-hover:scale-105\"}),r.jsxs(\"div\",{className:\"flex items-center gap-1.5 text-[11px] font-bold text-green-600/80\",children:[r.jsx(Ce,{className:\"h-3.5 w-3.5\"}),e(\"ui.support.wechatReward\")]})]})})})]}),r.jsxs(\"div\",{className:\"flex items-center justify-between pl-1 pt-1 pb-1\",children:[r.jsxs(\"h3\",{className:\"text-xs font-bold text-muted-foreground/80 flex items-center gap-1.5\",children:[r.jsx(ke,{className:\"h-3.5 w-3.5\"}),e(\"ui.support.listTitle\")]}),r.jsxs(w,{children:[r.jsx(k,{asChild:!0,children:r.jsxs(y,{variant:\"ghost\",size:\"sm\",className:\"h-6 px-2 text-[11px] gap-1 hover:bg-muted text-muted-foreground\",children:[r.jsx(le,{className:\"h-3.5 w-3.5\"}),e(\"amount\"===m?\"ui.support.sortDefault\":\"ui.support.sortTime\")]})}),r.jsxs(C,{align:\"end\",className:\"text-[11px] min-w-30\",children:[r.jsx(S,{className:\"text-[10px] opacity-50 px-2 py-1\",children:e(\"ui.support.sort\")}),r.jsx(M,{}),r.jsx(T,{className:\"px-2 py-1.5 cursor-pointer \"+(\"amount\"===m?\"bg-primary/10 text-primary font-bold\":\"\"),onClick:()=>v(\"amount\",\"desc\"),children:e(\"ui.support.sortDefault\")}),r.jsx(T,{className:\"px-2 py-1.5 cursor-pointer \"+(\"time\"===m?\"bg-primary/10 text-primary font-bold\":\"\"),onClick:()=>v(\"time\",\"desc\"),children:e(\"ui.support.sortTime\")})]})]})]}),r.jsx(\"div\",{className:\"flex-1 overflow-y-auto border border-border/40 rounded-lg bg-card/10 custom-scrollbar relative min-h-30\",children:0===t.length?r.jsxs(\"div\",{className:\"flex flex-col items-center justify-center h-full text-muted-foreground space-y-2 opacity-60 py-8\",children:[r.jsx(ke,{className:\"h-6 w-6 stroke-[1.5]\"}),r.jsx(\"span\",{className:\"text-xs italic\",children:e(\"ui.support.noData\")})]}):r.jsx(\"div\",{className:\"divide-y divide-border/20\",children:t.map((e,s)=>{const t=`${e.name||\"Anonymous\"}: ${e.message||e.item}`;return r.jsx(ie,{side:\"left\",delay:300,content:t,className:\"max-w-75 whitespace-normal\",children:r.jsxs(\"div\",{className:\"grid grid-cols-[80px_1fr_80px] gap-2 px-3 py-2 items-center hover:bg-primary/5 transition-colors cursor-default text-[11px]\",children:[r.jsx(\"div\",{className:\"text-muted-foreground font-mono tabular-nums whitespace-nowrap overflow-hidden text-ellipsis opacity-70\",children:(e.time||\"\").split(\" \")[0]||\"N/A\"}),r.jsxs(\"div\",{className:\"flex items-center gap-1.5 min-w-0 overflow-hidden\",children:[r.jsx(_e,{className:\"h-3 w-3 text-primary/60 shrink-0\"}),r.jsx(\"span\",{className:\"font-medium shrink-0 max-w-20 truncate\",children:e.name||\"Anonymous\"}),e.message&&r.jsx(\"span\",{className:\"text-muted-foreground truncate text-[10px] opacity-70 border-l border-border/50 pl-1.5\",children:e.message})]}),r.jsxs(\"div\",{className:\"text-right font-bold text-primary tabular-nums\",children:[e.amount,\" \",r.jsx(\"span\",{className:\"text-[9px] opacity-70 font-normal ml-0.5\",children:e.unit})]})]})},s)})})}),a&&a.totalRows>0&&r.jsxs(\"div\",{className:\"flex items-center justify-between pt-1 pb-1 px-1\",children:[r.jsx(\"div\",{className:\"text-[10px] text-muted-foreground opacity-70\",children:e(\"ui.common.total\",{defaultValue:`共 ${a.totalRows} 条`})}),r.jsxs(\"div\",{className:\"flex items-center gap-1.5\",children:[r.jsx(y,{variant:\"ghost\",size:\"icon\",className:\"h-6 w-6 rounded-full hover:bg-muted\",onClick:()=>d(e=>Math.max(1,e-1)),disabled:o<=1||n,children:r.jsx(Q,{className:\"h-3.5 w-3.5\"})}),r.jsxs(\"span\",{className:\"text-[10px] text-muted-foreground font-mono tabular-nums min-w-7.5 text-center\",children:[o,\" / \",Math.max(1,Math.ceil(a.totalRows/c))]}),r.jsx(y,{variant:\"ghost\",size:\"icon\",className:\"h-6 w-6 rounded-full hover:bg-muted\",onClick:()=>d(e=>e+1),disabled:o>=Math.ceil(a.totalRows/c)||n,children:r.jsx(I,{className:\"h-3.5 w-3.5\"})})]})]}),r.jsx(\"div\",{className:\"pt-0.5 text-[10px] text-center text-muted-foreground italic opacity-40\",children:e(\"ui.support.thanks\")})]})}function is(e){if(0===e)return\"0 B\";const s=Math.floor(Math.log(e)/Math.log(1024));return Math.round(e/Math.pow(1024,s)*100)/100+\" \"+[\"B\",\"KB\",\"MB\",\"GB\",\"TB\"][s]}function ls({refreshKey:e,children:t}){const{t:a}=f(),{systemInfo:n,isLoading:i,refresh:l}=function(){const[e,t]=s.useState(null),[a,r]=s.useState(!1),[n,i]=s.useState(null),l=localStorage.getItem(\"token\"),o=s.useCallback(async e=>{if(l){r(!0),i(null);try{const s=await fetch(h(p.API_URL+\"/api/admin/systeminfo\"),{method:\"GET\",headers:g({token:l,includeDomain:!1})});if(!s.ok)throw new Error(\"Network response was not ok\");const a=await s.json();if(e&&!e.current)return;0===a.code||a.code<100&&a.code>0?t(a.data):i(a.message||\"Failed to get system info\")}catch(s){if(e&&!e.current)return;if(s instanceof Error&&\"AbortError\"===s.name)return;i(\"Failed to get system info\")}finally{e&&!e.current||r(!1)}}},[l]);return s.useEffect(()=>{const e={current:!0};return o(e),()=>{e.current=!1}},[o]),{systemInfo:e,isLoading:a,error:n,refresh:o}}();s.useEffect(()=>{e&&l()},[e,l]);return i&&!n?r.jsxs(\"div\",{className:\"flex items-center justify-center p-12 bg-card rounded-xl border border-border\",children:[r.jsx(N,{className:\"h-6 w-6 animate-spin text-muted-foreground mr-2\"}),r.jsx(\"span\",{className:\"text-sm text-muted-foreground\",children:a(\"ui.common.loading\")})]}):n?r.jsxs(\"div\",{className:\"rounded-xl border border-border bg-card p-6 space-y-5 custom-shadow\",children:[r.jsxs(\"div\",{className:\"flex items-center justify-between\",children:[r.jsxs(\"h2\",{className:\"text-lg font-bold text-card-foreground flex items-center gap-2\",children:[r.jsx(de,{className:\"h-5 w-5\"}),a(\"ui.system.serviceInfo\")]}),r.jsx(y,{variant:\"ghost\",size:\"icon\",onClick:()=>{l()},disabled:i,className:\"h-8 w-8 rounded-full hover:bg-muted\",children:r.jsx(se,{className:\"h-4 w-4 \"+(i?\"animate-spin\":\"\")})})]}),r.jsxs(\"div\",{className:\"space-y-5\",children:[r.jsxs(\"div\",{className:\"space-y-4\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-2 text-sm font-bold text-primary\",children:[r.jsx(ne,{className:\"h-4 w-4\"}),a(\"ui.system.runtimeInfo\")]}),r.jsxs(\"div\",{className:\"grid grid-cols-1 sm:grid-cols-2 gap-y-2 text-xs\",children:[r.jsxs(\"div\",{className:\"text-muted-foreground\",children:[a(\"ui.system.goVersion\"),\" / \",a(\"ui.system.goroutines\"),\" / \",a(\"ui.system.numGc\")]}),r.jsxs(\"div\",{className:\"sm:text-right font-medium text-[11px] sm:text-xs\",children:[n.host.os,\"/\",n.host.arch,r.jsx(\"span\",{className:\"text-muted-foreground mx-1.5 opacity-50\",children:\"|\"}),r.jsx(\"span\",{className:\"font-mono\",children:n.runtimeStatus.numGoroutine}),r.jsx(\"span\",{className:\"text-muted-foreground mx-1.5 opacity-50\",children:\"|\"}),r.jsx(\"span\",{className:\"font-mono\",children:n.runtimeStatus.numGc})]}),r.jsxs(\"div\",{className:\"text-muted-foreground\",children:[a(\"ui.system.startTime\"),\" / \",a(\"ui.system.serviceUptime\")]}),r.jsxs(\"div\",{className:\"sm:text-right text-[11px] font-medium\",children:[r.jsx(\"span\",{children:(()=>{var e;const s=new Date(n.startTime);return`${s.toLocaleString()} (${(null==(e=new Intl.DateTimeFormat(void 0,{timeZoneName:\"shortOffset\"}).formatToParts(s).find(e=>\"timeZoneName\"===e.type))?void 0:e.value)||\"\"})`})()}),r.jsx(\"span\",{className:\"text-muted-foreground mx-1.5 opacity-50\",children:\"|\"}),r.jsxs(\"span\",{className:\"text-muted-foreground\",children:[Math.floor(n.uptime/3600),\"h\",Math.floor(n.uptime%3600/60),\"m\",Math.floor(n.uptime%60),\"s\"]})]}),r.jsxs(\"div\",{className:\"col-span-2 space-y-2 mt-1\",children:[r.jsxs(\"div\",{className:\"flex justify-between text-xs\",children:[r.jsx(\"span\",{className:\"text-muted-foreground\",children:a(\"ui.system.heapMemory\")}),r.jsxs(\"span\",{className:\"font-medium\",children:[is(n.runtimeStatus.memAlloc),\" / \",is(n.runtimeStatus.memSys)]})]}),r.jsx(\"div\",{className:\"h-1.5 w-full bg-secondary/50 rounded-full overflow-hidden\",children:r.jsx(\"div\",{className:\"h-full bg-primary transition-all duration-700 ease-out\",style:{width:`${Math.min(100,n.runtimeStatus.memAlloc/n.runtimeStatus.memSys*100)}%`}})})]})]})]}),r.jsxs(\"div\",{className:\"space-y-4\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-2 text-sm font-bold text-primary\",children:[r.jsx(be,{className:\"h-4 w-4\"}),a(\"ui.system.hostInfo\")]}),r.jsxs(\"div\",{className:\"grid grid-cols-1 sm:grid-cols-2 gap-y-2 text-xs\",children:[r.jsx(\"div\",{className:\"text-muted-foreground\",children:a(\"ui.system.systemTime\")}),r.jsx(\"div\",{className:\"sm:text-right font-medium\",children:(()=>{var e;const s=new Date(n.host.currentTime);return`${s.toLocaleString()} (${(null==(e=new Intl.DateTimeFormat(void 0,{timeZoneName:\"shortOffset\"}).formatToParts(s).find(e=>\"timeZoneName\"===e.type))?void 0:e.value)||\"\"})`})()}),r.jsxs(\"div\",{className:\"text-muted-foreground\",children:[a(\"ui.system.os\"),\" / \",a(\"ui.system.kernelVersion\"),\" / \",a(\"ui.system.modelName\")]}),r.jsxs(\"div\",{className:\"sm:text-right font-medium text-[10px] sm:text-xs break-all\",title:`${n.host.osPretty} / ${n.host.kernelVersion} / ${n.cpu.modelName}`,children:[n.host.osPretty,r.jsx(\"span\",{className:\"text-muted-foreground mx-1.5 opacity-50\",children:\"|\"}),r.jsx(\"span\",{className:\"font-mono\",children:n.host.kernelVersion}),r.jsx(\"span\",{className:\"text-muted-foreground mx-1.5 opacity-50\",children:\"|\"}),r.jsx(\"span\",{children:n.cpu.modelName})]}),r.jsxs(\"div\",{className:\"text-muted-foreground\",children:[a(\"ui.system.physicalCores\"),\" / \",a(\"ui.system.cpuLoad\")]}),r.jsxs(\"div\",{className:\"sm:text-right font-medium\",children:[n.cpu.logicalCores,\"/\",n.cpu.physicalCores,r.jsx(\"span\",{className:\"text-muted-foreground mx-1.5 opacity-50\",children:\"|\"}),n.cpu.loadAvg.load1.toFixed(2),\" \",n.cpu.loadAvg.load5.toFixed(2),\" \",n.cpu.loadAvg.load15.toFixed(2)]}),r.jsxs(\"div\",{className:\"col-span-2 space-y-2 mt-1\",children:[r.jsxs(\"div\",{className:\"flex flex-wrap justify-between text-xs\",children:[r.jsxs(\"span\",{className:\"text-muted-foreground\",children:[a(\"ui.system.memoryUsage\"),\" / \",a(\"ui.system.usedMemory\"),\" / \",a(\"ui.system.totalMemory\")]}),r.jsxs(\"span\",{className:\"font-medium\",children:[r.jsxs(\"span\",{className:\"font-semibold\",children:[n.memory.usedPercent.toFixed(1),\"%\"]}),r.jsx(\"span\",{className:\"text-muted-foreground mx-1.5 opacity-50\",children:\"|\"}),is(n.memory.used),\" / \",is(n.memory.total)]})]}),r.jsx(\"div\",{className:\"h-1.5 w-full bg-secondary/50 rounded-full overflow-hidden\",children:r.jsx(\"div\",{className:\"h-full transition-all duration-700 ease-out fill-mode-forwards \"+(n.memory.usedPercent>85?\"bg-destructive\":n.memory.usedPercent>65?\"bg-orange-500\":\"bg-primary\"),style:{width:`${n.memory.usedPercent}%`}})})]})]})]})]}),t&&r.jsxs(r.Fragment,{children:[r.jsx(\"div\",{className:\"border-t border-border/50\"}),t]})]}):r.jsxs(\"div\",{className:\"flex flex-col items-center justify-center p-12 bg-card rounded-xl border border-border space-y-2\",children:[r.jsx(te,{className:\"h-8 w-8 text-destructive opacity-50\"}),r.jsx(\"div\",{className:\"text-sm text-destructive\",children:a(\"ui.common.error\")})]})}function os({onBack:e,isDashboard:t=!1,isAdmin:a=!1}){const{t:n}=f(),[i,l]=s.useState(null),[o,d]=s.useState(null),[c,m]=s.useState(null),[u,x]=s.useState(!0),[j,v]=s.useState(!1),[w,k]=s.useState(!1),[C,S]=s.useState(!1),[M,T]=s.useState(!1),[E,R]=s.useState(!1),[Z,X]=s.useState(!1),[te,ae]=s.useState(!1),[re,ne]=s.useState(0),[ie,le]=s.useState(!1),[de,ve]=s.useState(!1),[Ne,we]=s.useState(!1),[ke,Ce]=s.useState(\"\"),[_e,De]=s.useState(null),[Fe,Ae]=s.useState(!1),[$e,Ue]=s.useState(!1),[Pe,ze]=s.useState(!1),[Ve,qe]=s.useState(!1),[He,Ke]=s.useState(\"font\"),Ge=localStorage.getItem(\"token\"),Oe=s.useCallback(e=>{l(s=>s?{...s,...e}:null)},[]),Be=s.useCallback(e=>{d(s=>s?{...s,...e}:null)},[]),We=s.useCallback(e=>{m(s=>s?{...s,...e}:null)},[]),Je=s.useCallback(e=>{De(s=>s?{...s,...e}:null),ze(!1)},[]),Ze=async()=>{if(i)if(i.historyKeepVersions<100)b.error(n(\"ui.settings.historyKeepVersionsMinError\"));else{if(i.historySaveDelay){const e=(e=>{if(!e)return null;const s=e.match(/^(\\d+)(s|m|h|d)$/);if(!s)return null;const t=parseInt(s[1]);switch(s[2]){case\"s\":return t;case\"m\":return 60*t;case\"h\":return 3600*t;case\"d\":return 86400*t;default:return null}})(i.historySaveDelay);if(null===e)return void b.error(n(\"ui.settings.historySaveDelayFormatError\"));if(e<10)return void b.error(n(\"ui.settings.historySaveDelayMinError\"))}v(!0);try{const e=await fetch(h(p.API_URL+\"/api/admin/config\"),{method:\"POST\",headers:g({token:Ge}),body:JSON.stringify(i)}),s=await e.json();s.code>0&&s.code<200&&!1!==s.status?b.success(n(\"ui.settings.saveSuccess\")):b.error(`${s.message||n(\"ui.settings.saveFailed\")}${s.details?`\\n${s.details}`:\"\"}`)}catch{b.error(n(\"ui.settings.saveFailed\"))}finally{v(!1)}}};s.useEffect(()=>{let s=!0;return(async()=>{if(t)s&&x(!1);else{s&&x(!0);try{const[t,a,r,o]=await Promise.all([fetch(h(p.API_URL+\"/api/admin/config\"),{headers:g({token:Ge,includeDomain:!1,includeContentType:!1})}),fetch(h(p.API_URL+\"/api/admin/config/ngrok\"),{headers:g({token:Ge,includeDomain:!1,includeContentType:!1})}),fetch(h(p.API_URL+\"/api/admin/config/cloudflare\"),{headers:g({token:Ge,includeDomain:!1,includeContentType:!1})}),fetch(h(p.API_URL+\"/api/admin/config/user_database\"),{headers:g({token:Ge,includeDomain:!1,includeContentType:!1})})]);if(!s)return;const[c,u,x,f]=await Promise.all([t.json(),a.json(),r.json(),o.json()]);if(!s)return;c.code>0&&c.code<200&&!1!==c.status?l(c.data):(b.error(c.message||n(\"ui.common.error\")),i||null==e||e()),u.code>0&&u.code<200&&!1!==u.status&&d(u.data),x.code>0&&x.code<200&&!1!==x.status&&m(x.data),f.code>0&&f.code<200&&!1!==f.status&&(De(f.data),ze(!0))}catch(a){if(!s)return;b.error(n(\"ui.common.error\")),i||null==e||e()}finally{s&&x(!1)}}})(),()=>{s=!1}},[e,n,Ge,t]);const Xe=s.useRef(null),[ts,is]=s.useState(!1),[os,ds]=s.useState(!1),cs=s.useCallback(()=>{if(Xe.current){const{scrollLeft:e,scrollWidth:s,clientWidth:t}=Xe.current;is(e>0),ds(e+t<s-1)}},[]);s.useEffect(()=>{cs();const e=Xe.current;if(e){const s=new ResizeObserver(()=>{cs()});return s.observe(e),window.addEventListener(\"resize\",cs),()=>{s.disconnect(),window.removeEventListener(\"resize\",cs)}}},[cs,i]),s.useEffect(()=>{if(!Xe.current)return;const e=Xe.current.querySelector('[data-state=\"active\"]');e&&e.scrollIntoView({behavior:\"smooth\",block:\"nearest\",inline:\"nearest\"})},[He]);const ms=s.useRef(!1),us=s.useRef(0),xs=s.useRef(0),hs=s.useCallback(e=>{Xe.current&&(ms.current=!0,us.current=e.pageX-Xe.current.offsetLeft,xs.current=Xe.current.scrollLeft)},[]),ps=s.useCallback(()=>{ms.current=!1},[]),gs=s.useCallback(()=>{ms.current=!1},[]),fs=s.useCallback(e=>{if(!ms.current||!Xe.current)return;e.preventDefault();const s=1.5*(e.pageX-Xe.current.offsetLeft-us.current);Xe.current.scrollLeft=xs.current-s},[]);return u?r.jsx(\"div\",{className:\"p-8 text-center\",children:n(\"ui.common.loading\")}):i||t?r.jsxs(\"div\",{className:\"grid grid-cols-1 md:grid-cols-2 gap-4 pb-24 md:pb-4\",children:[r.jsxs(\"div\",{className:\"flex flex-col gap-4\",children:[r.jsx(as,{showUpgrade:!t}),!t&&r.jsx(ls,{refreshKey:re,children:r.jsx(\"div\",{className:\"space-y-3\",children:r.jsxs(\"div\",{className:\"grid grid-cols-1 sm:grid-cols-2 gap-3\",children:[r.jsxs(F,{open:Z,onOpenChange:X,children:[r.jsxs(y,{variant:\"outline\",onClick:()=>X(!0),disabled:M,className:\"rounded-xl border-destructive/20 hover:border-destructive/50 hover:bg-destructive/10 text-destructive\",children:[M?r.jsx(N,{className:\"h-4 w-4 mr-2 animate-spin\"}):r.jsx(se,{className:\"h-4 w-4 mr-2\"}),n(\"ui.system.restartService\")]}),r.jsxs(A,{className:\"rounded-2xl\",children:[r.jsxs($,{children:[r.jsx(U,{children:n(\"ui.system.restartService\")}),r.jsx(P,{children:n(\"ui.system.restartServiceConfirm\")})]}),r.jsxs(z,{children:[r.jsx(V,{className:\"rounded-xl\",children:n(\"ui.common.cancel\")}),r.jsx(q,{onClick:async()=>{T(!0),X(!1);try{const e=await fetch(h(p.API_URL+\"/api/admin/restart\"),{headers:g({token:Ge,includeDomain:!1,includeContentType:!1})}),s=await e.json();s.code>0&&s.code<200&&!1!==s.status?(b.success(n(\"api.system.restart.success\")),ne(e=>e+1)):b.error(`${s.message||n(\"api.system.restart.error\")}${s.details?`\\n${s.details}`:\"\"}`)}catch{b.error(n(\"api.system.restart.error\"))}finally{T(!1)}},className:\"rounded-xl bg-destructive hover:bg-destructive/90\",children:n(\"ui.common.confirm\")})]})]})]}),r.jsxs(F,{open:te,onOpenChange:ae,children:[r.jsxs(y,{variant:\"outline\",onClick:()=>ae(!0),disabled:E,className:\"rounded-xl\",children:[E?r.jsx(N,{className:\"h-4 w-4 mr-2 animate-spin\"}):r.jsx(be,{className:\"h-4 w-4 mr-2\"}),n(\"ui.system.manualGC\")]}),r.jsxs(A,{className:\"rounded-2xl\",children:[r.jsxs($,{children:[r.jsx(U,{children:n(\"ui.system.manualGC\")}),r.jsx(P,{children:n(\"ui.system.manualGCConfirm\")})]}),r.jsxs(z,{children:[r.jsx(V,{className:\"rounded-xl\",children:n(\"ui.common.cancel\")}),r.jsx(q,{onClick:async()=>{R(!0),ae(!1);try{const e=await fetch(h(p.API_URL+\"/api/admin/gc\"),{headers:g({token:Ge,includeDomain:!1,includeContentType:!1})}),s=await e.json();s.code>0&&s.code<200&&!1!==s.status?(b.success(n(\"ui.system.manualGCSuccess\")),ne(e=>e+1)):b.error(`${s.message||n(\"api.system.gc.error\")}${s.details?`\\n${s.details}`:\"\"}`)}catch{b.error(n(\"api.system.gc.error\"))}finally{R(!1)}},className:\"rounded-xl\",children:n(\"ui.common.confirm\")})]})]})]})]})})}),t&&a&&r.jsx(rs,{}),t&&r.jsxs(\"div\",{className:\"rounded-xl border border-border bg-card p-6 space-y-4 custom-shadow\",children:[r.jsxs(\"h2\",{className:\"text-lg font-bold text-card-foreground flex items-center gap-2\",children:[r.jsx(L,{className:\"h-5 w-5\"}),n(\"ui.common.helpAndSupport\")]}),r.jsxs(\"div\",{className:\"grid grid-cols-1 sm:grid-cols-2 gap-3\",children:[r.jsxs(\"a\",{href:\"https://github.com/haierkeys/fast-note-sync-service/issues/new\",target:\"_blank\",rel:\"noopener noreferrer\",className:\"flex items-center gap-3 p-3 rounded-xl bg-muted/30 hover:bg-primary/5 border border-border/50 hover:border-primary/20 transition-all group\",children:[r.jsx(\"div\",{className:\"p-2 rounded-lg bg-background border border-border group-hover:border-primary/20 transition-colors\",children:r.jsx(ce,{className:\"h-5 w-5 text-muted-foreground group-hover:text-primary transition-colors\"})}),r.jsxs(\"div\",{className:\"min-w-0\",children:[r.jsx(\"p\",{className:\"text-sm font-medium truncate\",children:n(\"ui.common.githubIssue\")}),r.jsx(\"p\",{className:\"text-xs text-muted-foreground truncate\",children:n(\"ui.common.githubIssueDesc\")})]})]}),r.jsxs(\"a\",{href:\"https://t.me/obsidian_users\",target:\"_blank\",rel:\"noopener noreferrer\",className:\"flex items-center gap-3 p-3 rounded-xl bg-muted/30 hover:bg-primary/5 border border-border/50 hover:border-primary/20 transition-all group\",children:[r.jsx(\"div\",{className:\"p-2 rounded-lg bg-background border border-border group-hover:border-primary/20 transition-colors\",children:r.jsx(Te,{className:\"h-5 w-5 text-muted-foreground group-hover:text-primary transition-colors\"})}),r.jsxs(\"div\",{className:\"min-w-0\",children:[r.jsx(\"p\",{className:\"text-sm font-medium truncate\",children:n(\"ui.common.telegramGroup\")}),r.jsx(\"p\",{className:\"text-xs text-muted-foreground truncate\",children:n(\"ui.common.telegramGroupDesc\")})]})]})]})]})]}),r.jsx(\"div\",{className:\"flex flex-col gap-4\",children:t?r.jsx(\"div\",{className:\"rounded-xl border border-border bg-card p-4 custom-shadow\",children:r.jsx(ns,{})}):i?r.jsx(\"div\",{className:\"rounded-xl border border-border bg-card custom-shadow overflow-hidden flex flex-col\",children:r.jsxs(Qe,{value:He,onValueChange:Ke,className:\"w-full\",children:[r.jsxs(\"div\",{className:\"bg-muted/60 h-12 relative overflow-hidden group/tabs\",children:[ts&&r.jsx(\"div\",{className:\"absolute left-0 top-0 bottom-0 w-12 bg-gradient-to-r from-muted/60 to-transparent z-20 pointer-events-none flex items-center justify-start\",children:r.jsx(Q,{className:\"h-4 w-4 text-muted-foreground ml-1\"})}),r.jsx(\"div\",{className:\"absolute bottom-0 left-0 right-0 h-[1px] bg-border/60 z-0\"}),r.jsxs(Ye,{ref:Xe,onScroll:cs,onMouseDown:hs,onMouseLeave:ps,onMouseUp:gs,onMouseMove:fs,className:\"w-full h-12 flex flex-nowrap justify-start items-stretch overflow-x-auto bg-transparent p-0 gap-0 no-scrollbar relative z-10 cursor-grab active:cursor-grabbing select-none transition-shadow\",children:[r.jsxs(es,{value:\"font\",className:\"flex-none px-6 h-12 rounded-none border-y-transparent border-x-transparent border-r border-r-border/30 bg-transparent data-[state=active]:bg-card transition-all font-medium text-muted-foreground data-[state=active]:text-primary\",children:[r.jsx(Le,{className:\"h-4 w-4 mr-2\"}),n(\"ui.settings.fontConfig\")]}),r.jsxs(es,{value:\"notes\",className:\"flex-none px-6 h-12 rounded-none border-y-transparent border-x-transparent border-r border-r-border/30 bg-transparent data-[state=active]:bg-card transition-all font-medium text-muted-foreground data-[state=active]:text-primary\",children:[r.jsx(ye,{className:\"h-4 w-4 mr-2\"}),n(\"ui.settings.noteRelatedConfig\")]}),r.jsxs(es,{value:\"security\",className:\"flex-none px-6 h-12 rounded-none border-y-transparent border-x-transparent border-r border-r-border/30 bg-transparent data-[state=active]:bg-card transition-all font-medium text-muted-foreground data-[state=active]:text-primary\",children:[r.jsx(Ie,{className:\"h-4 w-4 mr-2\"}),n(\"ui.settings.securityConfig\")]}),r.jsxs(es,{value:\"share\",className:\"flex-none px-6 h-12 rounded-none border-y-transparent border-x-transparent border-r border-r-border/30 bg-transparent data-[state=active]:bg-card transition-all font-medium text-muted-foreground data-[state=active]:text-primary\",children:[r.jsx(me,{className:\"h-4 w-4 mr-2\"}),n(\"ui.settings.systemConfig\")]}),r.jsxs(es,{value:\"database\",className:\"flex-none px-6 h-12 rounded-none border-y-transparent border-x-transparent border-r border-r-border/30 bg-transparent data-[state=active]:bg-card transition-all font-medium text-muted-foreground data-[state=active]:text-primary\",children:[r.jsx(oe,{className:\"h-4 w-4 mr-2\"}),n(\"ui.settings.userDatabaseConfig\")]}),r.jsxs(es,{value:\"tunnel\",className:\"flex-none px-6 h-12 rounded-none border-y-transparent border-r-transparent border-l-transparent bg-transparent data-[state=active]:bg-card transition-all font-medium text-muted-foreground data-[state=active]:text-primary\",children:[r.jsx(Se,{className:\"h-4 w-4 mr-2\"}),n(\"ui.settings.tunnelGatewayConfig\")]})]}),os&&r.jsx(\"div\",{className:\"absolute right-0 top-0 bottom-0 w-12 bg-gradient-to-l from-muted/60 to-transparent z-20 pointer-events-none flex items-center justify-end\",children:r.jsx(I,{className:\"h-4 w-4 text-muted-foreground mr-1\"})})]}),r.jsxs(\"div\",{className:\"flex-1\",children:[r.jsxs(ss,{value:\"font\",className:\"p-6 space-y-5 mt-0 outline-none\",children:[r.jsxs(\"h2\",{className:\"text-lg font-bold text-card-foreground flex items-center gap-2\",children:[r.jsx(Le,{className:\"h-5 w-5\"}),n(\"ui.settings.fontConfig\")]}),r.jsxs(\"div\",{className:\"space-y-3\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[r.jsx(H,{className:\"h-5 w-5 text-muted-foreground\"}),r.jsx(\"span\",{className:\"text-sm font-medium\",children:n(\"ui.settings.pullSource\")})]}),r.jsxs(K,{value:i.pullSource||\"auto\",onValueChange:e=>Oe({pullSource:e}),children:[r.jsx(G,{className:\"rounded-xl\",children:r.jsx(O,{placeholder:n(\"ui.settings.pullSource\")})}),r.jsxs(B,{className:\"rounded-xl\",children:[r.jsx(W,{value:\"auto\",children:n(\"ui.settings.pullSource.auto\")}),r.jsx(W,{value:\"github\",children:n(\"ui.settings.pullSource.github\")}),r.jsx(W,{value:\"cnb\",children:n(\"ui.settings.pullSource.cnb\")})]})]}),r.jsx(\"p\",{className:\"text-xs text-muted-foreground\",children:n(\"ui.settings.pullSourceDesc\")})]}),r.jsx(\"div\",{className:\"border-t border-border\"}),r.jsxs(\"div\",{className:\"space-y-3\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[r.jsx(Ee,{className:\"h-5 w-5 text-muted-foreground\"}),r.jsx(\"span\",{className:\"text-sm font-medium\",children:n(\"ui.settings.fontSet\")})]}),r.jsx(_,{value:i.fontSet,onChange:e=>Oe({fontSet:e.target.value}),placeholder:\"e.g. /static/fonts/font.css\",className:\"rounded-xl\"}),r.jsx(\"p\",{className:\"text-xs text-muted-foreground whitespace-pre-line\",dangerouslySetInnerHTML:{__html:n(\"ui.settings.fontSetDesc\")}})]}),r.jsx(\"div\",{className:\"pt-2\",children:r.jsx(y,{onClick:Ze,disabled:j,className:\"rounded-xl\",children:j?r.jsxs(r.Fragment,{children:[r.jsx(N,{className:\"h-4 w-4 mr-2 animate-spin\"}),n(\"ui.auth.submitting\")]}):r.jsxs(r.Fragment,{children:[r.jsx(Me,{className:\"h-4 w-4 mr-2\"}),n(\"ui.settings.saveSettings\")]})})})]}),r.jsxs(ss,{value:\"notes\",className:\"p-6 space-y-5 mt-0 outline-none\",children:[r.jsxs(\"h2\",{className:\"text-lg font-bold text-card-foreground flex items-center gap-2\",children:[r.jsx(ye,{className:\"h-5 w-5\"}),n(\"ui.settings.noteRelatedConfig\")]}),r.jsxs(\"div\",{className:\"space-y-3\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[r.jsx(ue,{className:\"h-5 w-5 text-muted-foreground\"}),r.jsx(\"span\",{className:\"text-sm font-medium\",children:n(\"ui.settings.fileChunkSize\")})]}),r.jsx(_,{value:i.fileChunkSize,onChange:e=>Oe({fileChunkSize:e.target.value}),placeholder:\"e.g. 1MB, 512KB\",className:\"rounded-xl\"}),r.jsx(\"p\",{className:\"text-xs text-muted-foreground whitespace-pre-line\",dangerouslySetInnerHTML:{__html:n(\"ui.settings.fileChunkSizeDesc\")}})]}),r.jsx(\"div\",{className:\"border-t border-border\"}),r.jsxs(\"div\",{className:\"space-y-3\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[r.jsx(xe,{className:\"h-5 w-5 text-muted-foreground\"}),r.jsx(\"span\",{className:\"text-sm font-medium\",children:n(\"ui.settings.softDeleteRetentionTime\")})]}),r.jsx(_,{value:i.softDeleteRetentionTime,onChange:e=>Oe({softDeleteRetentionTime:e.target.value}),placeholder:\"e.g. 30d, 24h\",className:\"rounded-xl\"}),r.jsx(\"p\",{className:\"text-xs text-muted-foreground whitespace-pre-line\",dangerouslySetInnerHTML:{__html:n(\"ui.settings.softDeleteRetentionTimeDesc\")}})]}),r.jsx(\"div\",{className:\"border-t border-border\"}),r.jsxs(\"div\",{className:\"space-y-3\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[r.jsx(he,{className:\"h-5 w-5 text-muted-foreground\"}),r.jsx(\"span\",{className:\"text-sm font-medium\",children:n(\"ui.settings.uploadSessionTimeout\")})]}),r.jsx(_,{value:i.uploadSessionTimeout,onChange:e=>Oe({uploadSessionTimeout:e.target.value}),placeholder:\"e.g. 1h, 30m\",className:\"rounded-xl\"}),r.jsx(\"p\",{className:\"text-xs text-muted-foreground whitespace-pre-line\",dangerouslySetInnerHTML:{__html:n(\"ui.settings.uploadSessionTimeoutDesc\")}})]}),r.jsx(\"div\",{className:\"border-t border-border\"}),r.jsxs(\"div\",{className:\"space-y-3\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[r.jsx(ee,{className:\"h-5 w-5 text-muted-foreground\"}),r.jsx(\"span\",{className:\"text-sm font-medium\",children:n(\"ui.settings.historyKeepVersions\")})]}),r.jsx(_,{type:\"number\",min:\"100\",value:i.historyKeepVersions,onChange:e=>Oe({historyKeepVersions:parseInt(e.target.value)||100}),placeholder:\"e.g. 100\",className:\"rounded-xl\"}),r.jsx(\"p\",{className:\"text-xs text-muted-foreground whitespace-pre-line\",dangerouslySetInnerHTML:{__html:n(\"ui.settings.historyKeepVersionsDesc\")}})]}),r.jsx(\"div\",{className:\"border-t border-border\"}),r.jsxs(\"div\",{className:\"space-y-3\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[r.jsx(he,{className:\"h-5 w-5 text-muted-foreground\"}),r.jsx(\"span\",{className:\"text-sm font-medium\",children:n(\"ui.settings.historySaveDelay\")})]}),r.jsx(_,{value:i.historySaveDelay,onChange:e=>Oe({historySaveDelay:e.target.value}),placeholder:\"e.g. 10s, 1m\",className:\"rounded-xl\"}),r.jsx(\"p\",{className:\"text-xs text-muted-foreground whitespace-pre-line\",dangerouslySetInnerHTML:{__html:n(\"ui.settings.historySaveDelayDesc\")}})]}),r.jsx(\"div\",{className:\"pt-2\",children:r.jsx(y,{onClick:Ze,disabled:j,className:\"rounded-xl\",children:j?r.jsxs(r.Fragment,{children:[r.jsx(N,{className:\"h-4 w-4 mr-2 animate-spin\"}),n(\"ui.auth.submitting\")]}):r.jsxs(r.Fragment,{children:[r.jsx(Me,{className:\"h-4 w-4 mr-2\"}),n(\"ui.settings.saveSettings\")]})})})]}),r.jsxs(ss,{value:\"security\",className:\"p-6 space-y-5 mt-0 outline-none\",children:[r.jsxs(\"h2\",{className:\"text-lg font-bold text-card-foreground flex items-center gap-2\",children:[r.jsx(D,{className:\"h-5 w-5\"}),n(\"ui.settings.securityConfig\")]}),r.jsxs(\"div\",{className:\"space-y-3\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[r.jsx(D,{className:\"h-5 w-5 text-muted-foreground\"}),r.jsx(\"span\",{className:\"text-sm font-medium\",children:n(\"ui.settings.authTokenKey\")})]}),r.jsx(_,{value:i.authTokenKey,onChange:e=>Oe({authTokenKey:e.target.value}),placeholder:\"e.g. token\",className:\"rounded-xl\"}),r.jsx(\"p\",{className:\"text-xs text-muted-foreground whitespace-pre-line\",dangerouslySetInnerHTML:{__html:n(\"ui.settings.authTokenKeyDesc\")}})]}),r.jsx(\"div\",{className:\"border-t border-border\"}),r.jsxs(\"div\",{className:\"space-y-3\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[r.jsx(he,{className:\"h-5 w-5 text-muted-foreground\"}),r.jsx(\"span\",{className:\"text-sm font-medium\",children:n(\"ui.settings.tokenExpiry\")})]}),r.jsx(_,{value:i.tokenExpiry,onChange:e=>Oe({tokenExpiry:e.target.value}),placeholder:\"e.g. 365d, 24h, 30m\",className:\"rounded-xl\"}),r.jsx(\"p\",{className:\"text-xs text-muted-foreground whitespace-pre-line\",dangerouslySetInnerHTML:{__html:n(\"ui.settings.tokenExpiryDesc\")}})]}),r.jsx(\"div\",{className:\"border-t border-border\"}),r.jsxs(\"div\",{className:\"space-y-3\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[r.jsx(Re,{className:\"h-5 w-5 text-muted-foreground\"}),r.jsx(\"span\",{className:\"text-sm font-medium\",children:n(\"ui.settings.registerIsEnable\")})]}),r.jsxs(\"div\",{className:\"flex items-center space-x-2\",children:[r.jsx(J,{id:\"registerIsEnable\",checked:i.registerIsEnable,onCheckedChange:e=>Oe({registerIsEnable:!!e})}),r.jsx(Y,{htmlFor:\"registerIsEnable\",className:\"text-sm\",children:i.registerIsEnable?n(\"ui.common.isEnabled\"):n(\"ui.common.close\")})]}),r.jsx(\"p\",{className:\"text-xs text-muted-foreground whitespace-pre-line\",dangerouslySetInnerHTML:{__html:n(\"ui.settings.registerIsEnableDesc\")}})]}),r.jsx(\"div\",{className:\"border-t border-border\"}),r.jsxs(\"div\",{className:\"space-y-3\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[r.jsx(Ie,{className:\"h-5 w-5 text-muted-foreground\"}),r.jsx(\"span\",{className:\"text-sm font-medium\",children:n(\"ui.settings.adminUid\")})]}),r.jsx(_,{type:\"number\",value:i.adminUid,onChange:e=>Oe({adminUid:parseInt(e.target.value)||0}),placeholder:\"e.g. 1\",className:\"rounded-xl\"}),r.jsx(\"p\",{className:\"text-xs text-muted-foreground whitespace-pre-line\",dangerouslySetInnerHTML:{__html:n(\"ui.settings.adminUidDesc\")}})]}),r.jsx(\"div\",{className:\"pt-2\",children:r.jsx(y,{onClick:Ze,disabled:j,className:\"rounded-xl\",children:j?r.jsxs(r.Fragment,{children:[r.jsx(N,{className:\"h-4 w-4 mr-2 animate-spin\"}),n(\"ui.auth.submitting\")]}):r.jsxs(r.Fragment,{children:[r.jsx(Me,{className:\"h-4 w-4 mr-2\"}),n(\"ui.settings.saveSettings\")]})})})]}),r.jsxs(ss,{value:\"share\",className:\"p-6 space-y-5 mt-0 outline-none\",children:[r.jsxs(\"h2\",{className:\"text-lg font-bold text-card-foreground flex items-center gap-2\",children:[r.jsx(me,{className:\"h-5 w-5\"}),n(\"ui.settings.systemConfig\")]}),r.jsxs(\"div\",{className:\"space-y-3\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[r.jsx(me,{className:\"h-5 w-5 text-muted-foreground\"}),r.jsx(\"span\",{className:\"text-sm font-medium\",children:n(\"ui.settings.shareTokenKey\")})]}),r.jsx(_,{value:i.shareTokenKey,onChange:e=>Oe({shareTokenKey:e.target.value}),placeholder:\"e.g. fns\",className:\"rounded-xl\"}),r.jsx(\"p\",{className:\"text-xs text-muted-foreground whitespace-pre-line\",dangerouslySetInnerHTML:{__html:n(\"ui.settings.shareTokenKeyDesc\")}})]}),r.jsx(\"div\",{className:\"border-t border-border\"}),r.jsxs(\"div\",{className:\"space-y-3\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[r.jsx(he,{className:\"h-5 w-5 text-muted-foreground\"}),r.jsx(\"span\",{className:\"text-sm font-medium\",children:n(\"ui.settings.shareTokenExpiry\")})]}),r.jsx(_,{value:i.shareTokenExpiry,onChange:e=>Oe({shareTokenExpiry:e.target.value}),placeholder:\"e.g. 30d, 24h, 30m\",className:\"rounded-xl\"}),r.jsx(\"p\",{className:\"text-xs text-muted-foreground whitespace-pre-line\",dangerouslySetInnerHTML:{__html:n(\"ui.settings.shareTokenExpiryDesc\")}})]}),r.jsx(\"div\",{className:\"pt-2\",children:r.jsx(y,{onClick:Ze,disabled:j,className:\"rounded-xl\",children:j?r.jsxs(r.Fragment,{children:[r.jsx(N,{className:\"h-4 w-4 mr-2 animate-spin\"}),n(\"ui.auth.submitting\")]}):r.jsxs(r.Fragment,{children:[r.jsx(Me,{className:\"h-4 w-4 mr-2\"}),n(\"ui.settings.saveSettings\")]})})})]}),r.jsx(ss,{value:\"database\",className:\"p-6 space-y-5 mt-0 outline-none\",children:_e&&r.jsxs(\"div\",{className:\"space-y-5\",children:[r.jsxs(\"div\",{className:\"flex flex-col gap-1\",children:[r.jsxs(\"h2\",{className:\"text-lg font-bold text-card-foreground flex items-center gap-2\",children:[r.jsx(oe,{className:\"h-5 w-5\"}),n(\"ui.settings.userDatabaseConfig\")]}),r.jsx(\"div\",{className:\"text-sm text-muted-foreground leading-relaxed\",dangerouslySetInnerHTML:{__html:n(\"ui.settings.userDatabaseDesc\")}})]}),r.jsxs(\"div\",{className:\"space-y-4\",children:[r.jsxs(\"div\",{className:\"space-y-3\",children:[r.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[r.jsx(ue,{className:\"h-5 w-5 text-muted-foreground\"}),r.jsx(\"span\",{className:\"text-sm font-medium\",children:n(\"ui.settings.databaseType\")})]}),r.jsxs(K,{value:_e.type||\"sqlite\",onValueChange:e=>Je({type:\"sqlite\"===e?\"\":e}),children:[r.jsx(G,{className:\"rounded-xl\",children:r.jsx(O,{placeholder:n(\"ui.settings.databaseType\")})}),r.jsxs(B,{className:\"rounded-xl\",children:[r.jsx(W,{value:\"sqlite\",children:n(\"ui.settings.databaseType.sqlite\")}),r.jsx(W,{value:\"mysql\",children:n(\"ui.settings.databaseType.mysql\")}),r.jsx(W,{value:\"postgres\",children:n(\"ui.settings.databaseType.postgres\")})]})]}),\"mysql\"===_e.type&&r.jsx(\"p\",{className:\"text-xs text-amber-500 font-medium\",children:n(\"ui.settings.mysqlPermissionWarning\")})]}),(\"mysql\"===_e.type||\"postgres\"===_e.type)&&r.jsxs(\"div\",{className:\"grid grid-cols-1 sm:grid-cols-2 gap-4 animate-in fade-in slide-in-from-top-2 duration-300 border-t border-border pt-4\",children:[r.jsxs(\"div\",{className:\"space-y-2\",children:[r.jsx(Y,{className:\"text-xs text-muted-foreground ml-1\",children:n(\"ui.settings.databaseHost\")}),r.jsx(_,{value:_e.host,onChange:e=>Je({host:e.target.value}),placeholder:\"127.0.0.1\",className:\"rounded-xl\"})]}),r.jsxs(\"div\",{className:\"space-y-2\",children:[r.jsx(Y,{className:\"text-xs text-muted-foreground ml-1\",children:n(\"ui.settings.databasePort\")}),r.jsx(_,{type:\"number\",value:_e.port,onChange:e=>Je({port:parseInt(e.target.value)||(\"mysql\"===_e.type?3306:5432)}),placeholder:\"mysql\"===_e.type?\"3306\":\"5432\",className:\"rounded-xl\"})]}),r.jsxs(\"div\",{className:\"space-y-2\",children:[r.jsx(Y,{className:\"text-xs text-muted-foreground ml-1\",children:n(\"ui.settings.databaseUser\")}),r.jsx(_,{value:_e.userName,onChange:e=>Je({userName:e.target.value}),placeholder:\"root\",className:\"rounded-xl\"})]}),r.jsxs(\"div\",{className:\"space-y-2\",children:[r.jsx(Y,{className:\"text-xs text-muted-foreground ml-1\",children:n(\"ui.settings.databasePassword\")}),r.jsxs(\"div\",{className:\"relative\",children:[r.jsx(_,{type:Ve?\"text\":\"password\",value:_e.password,onChange:e=>Je({password:e.target.value}),placeholder:\"••••••••\",className:\"rounded-xl pr-10\"}),r.jsx(y,{type:\"button\",variant:\"ghost\",size:\"icon\",className:\"absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent text-muted-foreground hover:text-foreground\",onClick:()=>qe(!Ve),children:Ve?r.jsx(pe,{className:\"h-4 w-4\"}):r.jsx(ge,{className:\"h-4 w-4\"})})]})]}),r.jsxs(\"div\",{className:\"space-y-2\",children:[r.jsx(Y,{className:\"text-xs text-muted-foreground ml-1\",children:n(\"ui.settings.databaseName\")}),r.jsx(_,{value:_e.name,onChange:e=>Je({name:e.target.value}),placeholder:\"fast_note\",className:\"rounded-xl\"})]}),\"postgres\"===_e.type&&r.jsxs(r.Fragment,{children:[r.jsxs(\"div\",{className:\"space-y-2\",children:[r.jsx(Y,{className:\"text-xs text-muted-foreground ml-1\",children:n(\"ui.settings.databaseSchema\")}),r.jsx(_,{value:_e.schema,onChange:e=>Je({schema:e.target.value}),placeholder:\"public\",className:\"rounded-xl\"})]}),r.jsxs(\"div\",{className:\"space-y-2\",children:[r.jsx(Y,{className:\"text-xs text-muted-foreground ml-1\",children:n(\"ui.settings.databaseSslMode\")}),r.jsxs(K,{value:_e.sslMode||\"disable\",onValueChange:e=>Je({sslMode:e}),children:[r.jsx(G,{className:\"rounded-xl\",children:r.jsx(O,{placeholder:\"SSL Mode\"})}),r.jsxs(B,{className:\"rounded-xl\",children:[r.jsx(W,{value:\"disable\",children:\"disable\"}),r.jsx(W,{value:\"require\",children:\"require\"}),r.jsx(W,{value:\"verify-ca\",children:\"verify-ca\"}),r.jsx(W,{value:\"verify-full\",children:\"verify-full\"})]})]})]})]}),r.jsxs(\"div\",{className:\"space-y-2\",children:[r.jsx(Y,{className:\"text-xs text-muted-foreground ml-1\",children:n(\"ui.settings.databaseMaxIdleConns\")}),r.jsx(_,{type:\"number\",value:_e.maxIdleConns,onChange:e=>Je({maxIdleConns:parseInt(e.target.value)||0}),placeholder:\"10\",className:\"rounded-xl\"})]}),r.jsxs(\"div\",{className:\"space-y-2\",children:[r.jsx(Y,{className:\"text-xs text-muted-foreground ml-1\",children:n(\"ui.settings.databaseMaxOpenConns\")}),r.jsx(_,{type:\"number\",value:_e.maxOpenConns,onChange:e=>Je({maxOpenConns:parseInt(e.target.value)||0}),placeholder:\"100\",className:\"rounded-xl\"})]}),r.jsxs(\"div\",{className:\"space-y-2\",children:[r.jsx(Y,{className:\"text-xs text-muted-foreground ml-1\",children:n(\"ui.settings.databaseConnMaxLifetime\")}),r.jsx(_,{value:_e.connMaxLifetime,onChange:e=>Je({connMaxLifetime:e.target.value}),placeholder:\"30m\",className:\"rounded-xl\"})]}),r.jsxs(\"div\",{className:\"space-y-2\",children:[r.jsx(Y,{className:\"text-xs text-muted-foreground ml-1\",children:n(\"ui.settings.databaseConnMaxIdleTime\")}),r.jsx(_,{value:_e.connMaxIdleTime,onChange:e=>Je({connMaxIdleTime:e.target.value}),placeholder:\"10m\",className:\"rounded-xl\"})]}),r.jsxs(\"div\",{className:\"space-y-2\",children:[r.jsx(Y,{className:\"text-xs text-muted-foreground ml-1\",children:n(\"ui.settings.databaseMaxWriteConcurrency\")}),r.jsx(_,{type:\"number\",value:_e.maxWriteConcurrency,onChange:e=>Je({maxWriteConcurrency:parseInt(e.target.value)||0}),placeholder:\"100\",className:\"rounded-xl\"})]})]}),r.jsxs(\"div\",{className:\"flex flex-wrap items-center gap-3 pt-2\",children:[r.jsx(y,{onClick:async()=>{if(_e){Ue(!0);try{const e=await fetch(h(p.API_URL+\"/api/admin/config/user_database/test\"),{method:\"POST\",headers:g({token:Ge}),body:JSON.stringify(_e)}),s=await e.json();s.code>0&&s.code<200&&!1!==s.status?(b.success(n(\"ui.settings.testSuccess\")),ze(!0)):(b.error(`${s.message||n(\"ui.settings.testFailed\")}${s.details?`\\n${s.details}`:\"\"}`),ze(!1))}catch{b.error(n(\"ui.settings.testFailed\")),ze(!1)}finally{Ue(!1)}}},disabled:$e,variant:\"outline\",className:\"rounded-xl\",children:$e?r.jsxs(r.Fragment,{children:[r.jsx(N,{className:\"h-4 w-4 mr-2 animate-spin\"}),n(\"ui.auth.submitting\")]}):r.jsxs(r.Fragment,{children:[r.jsx(fe,{className:\"h-4 w-4 mr-2\"}),n(\"ui.settings.testConnection\")]})}),r.jsx(y,{onClick:async()=>{if(_e)if(Pe){Ae(!0);try{const e=await fetch(h(p.API_URL+\"/api/admin/config/user_database\"),{method:\"POST\",headers:g({token:Ge}),body:JSON.stringify(_e)}),s=await e.json();s.code>0&&s.code<200&&!1!==s.status?b.success(n(\"ui.settings.saveSuccess\")):b.error(`${s.message||n(\"ui.settings.saveFailed\")}${s.details?`\\n${s.details}`:\"\"}`)}catch{b.error(n(\"ui.settings.saveFailed\"))}finally{Ae(!1)}}else b.error(n(\"ui.settings.testRequiredBeforeSave\"))},disabled:Fe||!Pe,className:\"rounded-xl\",children:Fe?r.jsxs(r.Fragment,{children:[r.jsx(N,{className:\"h-4 w-4 mr-2 animate-spin\"}),n(\"ui.auth.submitting\")]}):r.jsxs(r.Fragment,{children:[r.jsx(Me,{className:\"h-4 w-4 mr-2\"}),n(\"ui.settings.saveSettings\")]})}),!Pe&&!$e&&r.jsx(\"span\",{className:\"text-xs text-muted-foreground animate-pulse\",children:n(\"ui.settings.testRequiredBeforeSave\")})]})]})]})}),r.jsxs(ss,{value:\"tunnel\",className:\"p-6 space-y-5 mt-0 outline-none\",children:[r.jsxs(\"div\",{className:\"flex flex-col gap-1\",children:[r.jsxs(\"h2\",{className:\"text-lg font-bold text-card-foreground flex items-center gap-2\",children:[r.jsx(Se,{className:\"h-5 w-5\"}),n(\"ui.settings.tunnelGatewayConfig\")]}),r.jsx(\"p\",{className:\"text-sm text-muted-foreground whitespace-pre-line\",dangerouslySetInnerHTML:{__html:n(\"ui.settings.tunnelGatewayDesc\")}})]}),o&&r.jsxs(\"div\",{className:\"space-y-4\",children:[r.jsxs(\"div\",{className:\"space-y-1\",children:[r.jsx(\"h3\",{className:\"text-md font-semibold text-primary\",children:\"Ngrok\"}),r.jsx(\"p\",{className:\"text-xs text-muted-foreground whitespace-pre-line\",dangerouslySetInnerHTML:{__html:n(\"ui.settings.ngrokDesc\")}})]}),r.jsxs(\"div\",{className:\"space-y-3 pl-2\",children:[r.jsx(\"div\",{className:\"flex items-center gap-3\",children:r.jsxs(\"div\",{className:\"flex items-center space-x-2\",children:[r.jsx(J,{id:\"ngrokEnabled\",checked:o.enabled,onCheckedChange:e=>Be({enabled:!!e})}),r.jsx(Y,{htmlFor:\"ngrokEnabled\",className:\"text-sm cursor-pointer\",children:n(\"ui.common.isEnabled\")})]})}),r.jsxs(\"div\",{className:\"space-y-1\",children:[r.jsx(Y,{className:\"text-sm font-medium\",children:\"Token\"}),r.jsx(_,{value:o.authToken,onChange:e=>Be({authToken:e.target.value}),placeholder:\"e.g. 2Rk9...\",className:\"rounded-xl\"})]}),r.jsxs(\"div\",{className:\"space-y-1\",children:[r.jsx(Y,{className:\"text-sm font-medium\",children:n(\"ui.settings.customDomain\")}),r.jsx(_,{value:o.domain,onChange:e=>Be({domain:e.target.value}),placeholder:\"e.g. static.yourdomain.com\",className:\"rounded-xl\"}),r.jsx(\"p\",{className:\"text-xs text-muted-foreground pt-1\",children:n(\"ui.settings.customDomainDesc\")})]}),r.jsx(\"div\",{className:\"pt-2\",children:r.jsx(y,{onClick:async()=>{if(o){k(!0);try{const e=await fetch(h(p.API_URL+\"/api/admin/config/ngrok\"),{method:\"POST\",headers:g({token:Ge}),body:JSON.stringify(o)}),s=await e.json();s.code>0&&s.code<200&&!1!==s.status?b.success(n(\"ui.settings.saveSuccess\")):b.error(`${s.message||n(\"ui.settings.saveFailed\")}${s.details?`\\n${s.details}`:\"\"}`)}catch{b.error(n(\"ui.settings.saveFailed\"))}finally{k(!1)}}},disabled:w,className:\"rounded-xl\",children:w?r.jsxs(r.Fragment,{children:[r.jsx(N,{className:\"h-4 w-4 mr-2 animate-spin\"}),n(\"ui.auth.submitting\")]}):r.jsxs(r.Fragment,{children:[r.jsx(Me,{className:\"h-4 w-4 mr-2\"}),n(\"ui.settings.saveNgrok\")]})})})]})]}),o&&c&&r.jsx(\"div\",{className:\"border-t border-border\"}),c&&r.jsxs(\"div\",{className:\"space-y-4\",children:[r.jsxs(\"div\",{className:\"space-y-1\",children:[r.jsx(\"h3\",{className:\"text-md font-semibold text-primary\",children:\"Cloudflare Tunnel\"}),r.jsx(\"p\",{className:\"text-xs text-muted-foreground whitespace-pre-line\",dangerouslySetInnerHTML:{__html:n(\"ui.settings.cloudflareDesc\")}})]}),r.jsxs(\"div\",{className:\"space-y-3 pl-2\",children:[r.jsx(\"div\",{className:\"flex items-center gap-3\",children:r.jsxs(\"div\",{className:\"flex items-center space-x-2\",children:[r.jsx(J,{id:\"cfEnabled\",checked:c.enabled,onCheckedChange:e=>We({enabled:!!e})}),r.jsx(Y,{htmlFor:\"cfEnabled\",className:\"text-sm cursor-pointer\",children:n(\"ui.common.isEnabled\")})]})}),r.jsxs(\"div\",{className:\"space-y-1\",children:[r.jsx(Y,{className:\"text-sm font-medium\",children:\"Token\"}),r.jsx(_,{value:c.token,onChange:e=>We({token:e.target.value}),placeholder:\"e.g. eyJh...\",className:\"rounded-xl\"})]}),r.jsxs(\"div\",{className:\"flex flex-col gap-1 pt-1\",children:[r.jsxs(\"div\",{className:\"flex items-center space-x-2\",children:[r.jsx(J,{id:\"cfLogEnabled\",checked:c.logEnabled,onCheckedChange:e=>We({logEnabled:!!e})}),r.jsx(Y,{htmlFor:\"cfLogEnabled\",className:\"text-sm cursor-pointer\",children:n(\"ui.settings.enableLog\")})]}),r.jsx(\"p\",{className:\"text-xs text-muted-foreground pl-6\",children:n(\"ui.settings.cloudflareLogDesc\")})]}),r.jsxs(\"div\",{className:\"flex items-center gap-3 pt-2\",children:[r.jsx(y,{onClick:async()=>{if(c)if(de){S(!0);try{const e=await fetch(h(p.API_URL+\"/api/admin/config/cloudflare\"),{method:\"POST\",headers:g({token:Ge}),body:JSON.stringify(c)}),s=await e.json();s.code>0&&s.code<200&&!1!==s.status?b.success(n(\"ui.settings.saveSuccess\")):b.error(`${s.message||n(\"ui.settings.saveFailed\")}${s.details?`\\n${s.details}`:\"\"}`)}catch{b.error(n(\"ui.settings.saveFailed\"))}finally{S(!1)}}else b.error(n(\"ui.settings.cloudflaredTestRequired\"))},disabled:C,className:\"rounded-xl\",children:C?r.jsxs(r.Fragment,{children:[r.jsx(N,{className:\"h-4 w-4 mr-2 animate-spin\"}),n(\"ui.auth.submitting\")]}):r.jsxs(r.Fragment,{children:[r.jsx(Me,{className:\"h-4 w-4 mr-2\"}),n(\"ui.settings.saveCloudflare\")]})}),r.jsx(y,{onClick:async()=>{le(!0);try{const e=await fetch(h(p.API_URL+\"/api/admin/cloudflared_tunnel_download\"),{headers:g({token:Ge,includeDomain:!1,includeContentType:!1})}),s=await e.json();s.code>0&&s.code<200&&!1!==s.status?(b.success(n(\"ui.settings.downloadSuccess\")),ve(!0)):(Ce(`${s.message?s.message:n(\"ui.settings.downloadFailed\")}${s.details?`\\n\\n${s.details}`:\"\"}`),we(!0))}catch(e){const s=e instanceof Error?e.message:String(e);Ce(`${n(\"ui.settings.downloadFailed\")}${s?`\\n\\n${s}`:\"\"}`),we(!0)}finally{le(!1)}},disabled:ie,className:\"rounded-xl\",variant:\"outline\",children:ie?r.jsxs(r.Fragment,{children:[r.jsx(N,{className:\"h-4 w-4 mr-2 animate-spin\"}),n(\"ui.common.downloading\")]}):r.jsxs(r.Fragment,{children:[r.jsx(je,{className:\"h-4 w-4 mr-2\"}),n(\"ui.settings.downloadCloudflared\")]})}),r.jsx(F,{open:Ne,onOpenChange:we,children:r.jsxs(A,{className:\"rounded-2xl max-w-2xl w-[90vw]\",children:[r.jsxs($,{children:[r.jsxs(U,{className:\"text-destructive flex items-center gap-2\",children:[r.jsx(Ie,{className:\"h-5 w-5\"}),n(\"ui.settings.downloadFailed\")]}),r.jsx(P,{className:\"whitespace-pre-wrap break-all mt-2 font-mono text-xs bg-muted/50 p-4 rounded-xl border border-border/50 max-h-60 overflow-y-auto\",children:ke})]}),r.jsx(z,{children:r.jsx(V,{className:\"rounded-xl w-full sm:w-auto\",children:n(\"ui.common.confirm\")})})]})})]})]})]})]})]})]})}):null})]}):r.jsx(\"div\",{className:\"p-8 text-center text-destructive\",children:n(\"ui.common.error\")})}export{os as SystemSettings,os as default};\n"
  },
  {
    "path": "frontend/assets/table-D9wbHMTA.js",
    "content": "import{r as a,j as e,f as s}from\"./font-loader-CIrh3KnA.js\";const r=a.forwardRef(({className:a,...r},l)=>e.jsx(\"div\",{className:\"relative w-full\",children:e.jsx(\"table\",{ref:l,className:s(\"w-full caption-bottom text-sm\",a),...r})}));r.displayName=\"Table\";const l=a.forwardRef(({className:a,...r},l)=>e.jsx(\"thead\",{ref:l,className:s(\"[&_tr]:border-b\",a),...r}));l.displayName=\"TableHeader\";const t=a.forwardRef(({className:a,...r},l)=>e.jsx(\"tbody\",{ref:l,className:s(\"[&_tr:last-child]:border-0\",a),...r}));t.displayName=\"TableBody\";a.forwardRef(({className:a,...r},l)=>e.jsx(\"tfoot\",{ref:l,className:s(\"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0\",a),...r})).displayName=\"TableFooter\";const o=a.forwardRef(({className:a,...r},l)=>e.jsx(\"tr\",{ref:l,className:s(\"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted\",a),...r}));o.displayName=\"TableRow\";const d=a.forwardRef(({className:a,...r},l)=>e.jsx(\"th\",{ref:l,className:s(\"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0\",a),...r}));d.displayName=\"TableHead\";const m=a.forwardRef(({className:a,...r},l)=>e.jsx(\"td\",{ref:l,className:s(\"p-4 align-middle [&:has([role=checkbox])]:pr-0\",a),...r}));m.displayName=\"TableCell\";a.forwardRef(({className:a,...r},l)=>e.jsx(\"caption\",{ref:l,className:s(\"mt-4 text-sm text-muted-foreground\",a),...r})).displayName=\"TableCaption\";export{r as T,l as a,o as b,d as c,t as d,m as e};\n"
  },
  {
    "path": "frontend/assets/text-cursor-input-Bphfsfyn.js",
    "content": "import{c as a}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const h=a(\"TextCursorInput\",[[\"path\",{d:\"M5 4h1a3 3 0 0 1 3 3 3 3 0 0 1 3-3h1\",key:\"18xjzo\"}],[\"path\",{d:\"M13 20h-1a3 3 0 0 1-3-3 3 3 0 0 1-3 3H5\",key:\"fj48gi\"}],[\"path\",{d:\"M5 16H4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2h1\",key:\"1n9rhb\"}],[\"path\",{d:\"M13 8h7a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-7\",key:\"13ksps\"}],[\"path\",{d:\"M9 7v10\",key:\"1vc8ob\"}]]);export{h as T};\n"
  },
  {
    "path": "frontend/assets/tooltip-Dr-qRlmI.js",
    "content": "import{r as e,j as t,f as r,g as o}from\"./font-loader-CIrh3KnA.js\";function n({children:n,content:s,delay:a=300,side:c=\"top\",align:i=\"center\",disabled:l=!1,className:u}){const[d,f]=e.useState(!1),p=e.useRef(null),m=e.useRef(null),h=e.useRef(null),[b,g]=e.useState({top:0,left:0}),[w,x]=e.useState(c);e.useEffect(()=>{var e;if(d&&m.current){const t=m.current.getBoundingClientRect(),r=8;let o=c;if(\"top\"===c){const n=(null==(e=h.current)?void 0:e.offsetHeight)||200;t.top-n-r<0&&(o=\"bottom\")}x(o);let n=0,s=0;switch(o){case\"top\":n=t.top-r;break;case\"bottom\":n=t.bottom+r;break;case\"left\":n=t.top+t.height/2,s=t.left-r;break;case\"right\":n=t.top+t.height/2,s=t.right+r}if(\"top\"===o||\"bottom\"===o)switch(i){case\"start\":s=t.left;break;case\"center\":s=t.left+t.width/2;break;case\"end\":s=t.right}else switch(i){case\"start\":n=t.top;break;case\"center\":n=t.top+t.height/2;break;case\"end\":n=t.bottom}g({top:n,left:s})}},[d,c,i]);const k=e=>{\"Escape\"===e.key&&f(!1)};e.useEffect(()=>()=>{p.current&&window.clearTimeout(p.current)},[]);const v=d&&!l?t.jsx(\"div\",{ref:h,className:r(\"fixed z-[9999] px-2 py-1 text-xs font-medium whitespace-nowrap\",\"bg-popover text-popover-foreground\",\"rounded-md shadow-md border border-border\",\"animate-in fade-in-0 zoom-in-95 duration-200\",\"pointer-events-none\",u),style:{top:`${b.top}px`,left:`${b.left}px`,transform:(()=>{switch(w){case\"top\":return\"translate(-50%, -100%)\";case\"bottom\":return\"translate(-50%, 0%)\";case\"left\":return\"translate(-100%, -50%)\";case\"right\":return\"translate(0%, -50%)\"}})(),transformOrigin:(()=>{switch(w){case\"top\":return\"bottom center\";case\"bottom\":return\"top center\";case\"left\":return\"right center\";case\"right\":return\"left center\"}})()},role:\"tooltip\",onKeyDown:k,children:s}):null;return t.jsxs(\"div\",{ref:m,onMouseEnter:()=>{l||(p.current=window.setTimeout(()=>{f(!0)},a))},onMouseLeave:()=>{p.current&&(window.clearTimeout(p.current),p.current=null),f(!1)},onKeyDown:k,role:\"button\",tabIndex:0,children:[n,\"undefined\"!=typeof document&&o.createPortal(v,document.body)]})}export{n as T};\n"
  },
  {
    "path": "frontend/assets/trash-2-ad7PiUnC.js",
    "content": "import{c as e}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const t=e(\"Trash2\",[[\"path\",{d:\"M3 6h18\",key:\"d0wm0j\"}],[\"path\",{d:\"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6\",key:\"4alrt4\"}],[\"path\",{d:\"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2\",key:\"v07s0e\"}],[\"line\",{x1:\"10\",x2:\"10\",y1:\"11\",y2:\"17\",key:\"1uufr5\"}],[\"line\",{x1:\"14\",x2:\"14\",y1:\"11\",y2:\"17\",key:\"xtxkd\"}]]);export{t as T};\n"
  },
  {
    "path": "frontend/assets/vault-list-BzYzvdPK.js",
    "content": "import{c as e,r as t,V as n,g as r,u as o,a7 as i,t as a,e as s,j as l,I as c,X as d,B as u,a5 as h}from\"./font-loader-CIrh3KnA.js\";import{f,g,h as v,D as p,a as m,b,c as x,E as y}from\"./main-BIi-kGYY.js\";import{T as w}from\"./tooltip-Dr-qRlmI.js\";import{S as C}from\"./search-DdihTHF8.js\";import{R as N}from\"./refresh-cw-BxIJAPy3.js\";import{P as D}from\"./plus-BBfuNxDX.js\";import{C as E}from\"./clock-C9LPHszx.js\";import{P as j}from\"./pencil-DqQhr35g.js\";import{T as R}from\"./trash-2-ad7PiUnC.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const k=e(\"Cable\",[[\"path\",{d:\"M17 21v-2a1 1 0 0 1-1-1v-1a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v1a1 1 0 0 1-1 1\",key:\"10bnsj\"}],[\"path\",{d:\"M19 15V6.5a1 1 0 0 0-7 0v11a1 1 0 0 1-7 0V9\",key:\"1eqmu1\"}],[\"path\",{d:\"M21 21v-2h-4\",key:\"14zm7j\"}],[\"path\",{d:\"M3 5h4V3\",key:\"z442eg\"}],[\"path\",{d:\"M7 5a1 1 0 0 1 1 1v1a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a1 1 0 0 1 1-1V3\",key:\"ebdjd7\"}]]),S=e(\"GripVertical\",[[\"circle\",{cx:\"9\",cy:\"12\",r:\"1\",key:\"1vctgf\"}],[\"circle\",{cx:\"9\",cy:\"5\",r:\"1\",key:\"hp0tcf\"}],[\"circle\",{cx:\"9\",cy:\"19\",r:\"1\",key:\"fkjjf6\"}],[\"circle\",{cx:\"15\",cy:\"12\",r:\"1\",key:\"1tmaij\"}],[\"circle\",{cx:\"15\",cy:\"5\",r:\"1\",key:\"19l28e\"}],[\"circle\",{cx:\"15\",cy:\"19\",r:\"1\",key:\"f4zoj3\"}]]);\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const M=\"undefined\"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement;function I(e){const t=Object.prototype.toString.call(e);return\"[object Window]\"===t||\"[object global]\"===t}function T(e){return\"nodeType\"in e}function A(e){var t,n;return e?I(e)?e:T(e)&&null!=(t=null==(n=e.ownerDocument)?void 0:n.defaultView)?t:window:window}function O(e){const{Document:t}=A(e);return e instanceof t}function L(e){return!I(e)&&e instanceof A(e).HTMLElement}function B(e){return e instanceof A(e).SVGElement}function z(e){return e?I(e)?e.document:T(e)?O(e)?e:L(e)||B(e)?e.ownerDocument:document:document:document}const P=M?t.useLayoutEffect:t.useEffect;function F(e){const n=t.useRef(e);return P(()=>{n.current=e}),t.useCallback(function(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return null==n.current?void 0:n.current(...t)},[])}function U(e,n){void 0===n&&(n=[e]);const r=t.useRef(e);return P(()=>{r.current!==e&&(r.current=e)},n),r}function V(e,n){const r=t.useRef();return t.useMemo(()=>{const t=e(r.current);return r.current=t,t},[...n])}function K(e){const n=F(e),r=t.useRef(null),o=t.useCallback(e=>{e!==r.current&&(null==n||n(e,r.current)),r.current=e},[]);return[r,o]}function X(e){const n=t.useRef();return t.useEffect(()=>{n.current=e},[e]),n.current}let Y={};function W(e,n){return t.useMemo(()=>{if(n)return n;const t=null==Y[e]?0:Y[e]+1;return Y[e]=t,e+\"-\"+t},[e,n])}function H(e){return function(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;o<n;o++)r[o-1]=arguments[o];return r.reduce((t,n)=>{const r=Object.entries(n);for(const[o,i]of r){const n=t[o];null!=n&&(t[o]=n+e*i)}return t},{...t})}}const q=H(1),J=H(-1);function $(e){if(!e)return!1;const{KeyboardEvent:t}=A(e.target);return t&&e instanceof t}function _(e){if(function(e){if(!e)return!1;const{TouchEvent:t}=A(e.target);return t&&e instanceof t}(e)){if(e.touches&&e.touches.length){const{clientX:t,clientY:n}=e.touches[0];return{x:t,y:n}}if(e.changedTouches&&e.changedTouches.length){const{clientX:t,clientY:n}=e.changedTouches[0];return{x:t,y:n}}}return function(e){return\"clientX\"in e&&\"clientY\"in e}(e)?{x:e.clientX,y:e.clientY}:null}const G=Object.freeze({Translate:{toString(e){if(!e)return;const{x:t,y:n}=e;return\"translate3d(\"+(t?Math.round(t):0)+\"px, \"+(n?Math.round(n):0)+\"px, 0)\"}},Scale:{toString(e){if(!e)return;const{scaleX:t,scaleY:n}=e;return\"scaleX(\"+t+\") scaleY(\"+n+\")\"}},Transform:{toString(e){if(e)return[G.Translate.toString(e),G.Scale.toString(e)].join(\" \")}},Transition:{toString(e){let{property:t,duration:n,easing:r}=e;return t+\" \"+n+\"ms \"+r}}}),Q=\"a,frame,iframe,input:not([type=hidden]):not(:disabled),select:not(:disabled),textarea:not(:disabled),button:not(:disabled),*[tabindex]\";function Z(e){return e.matches(Q)?e:e.querySelector(Q)}const ee={display:\"none\"};function te(e){let{id:t,value:r}=e;return n.createElement(\"div\",{id:t,style:ee},r)}function ne(e){let{id:t,announcement:r,ariaLiveType:o=\"assertive\"}=e;return n.createElement(\"div\",{id:t,style:{position:\"fixed\",top:0,left:0,width:1,height:1,margin:-1,border:0,padding:0,overflow:\"hidden\",clip:\"rect(0 0 0 0)\",clipPath:\"inset(100%)\",whiteSpace:\"nowrap\"},role:\"status\",\"aria-live\":o,\"aria-atomic\":!0},r)}const re=t.createContext(null);const oe={draggable:\"\\n    To pick up a draggable item, press the space bar.\\n    While dragging, use the arrow keys to move the item.\\n    Press space again to drop the item in its new position, or press escape to cancel.\\n  \"},ie={onDragStart(e){let{active:t}=e;return\"Picked up draggable item \"+t.id+\".\"},onDragOver(e){let{active:t,over:n}=e;return n?\"Draggable item \"+t.id+\" was moved over droppable area \"+n.id+\".\":\"Draggable item \"+t.id+\" is no longer over a droppable area.\"},onDragEnd(e){let{active:t,over:n}=e;return n?\"Draggable item \"+t.id+\" was dropped over droppable area \"+n.id:\"Draggable item \"+t.id+\" was dropped.\"},onDragCancel(e){let{active:t}=e;return\"Dragging was cancelled. Draggable item \"+t.id+\" was dropped.\"}};function ae(e){let{announcements:o=ie,container:i,hiddenTextDescribedById:a,screenReaderInstructions:s=oe}=e;const{announce:l,announcement:c}=function(){const[e,n]=t.useState(\"\");return{announce:t.useCallback(e=>{null!=e&&n(e)},[]),announcement:e}}(),d=W(\"DndLiveRegion\"),[u,h]=t.useState(!1);if(t.useEffect(()=>{h(!0)},[]),function(e){const n=t.useContext(re);t.useEffect(()=>{if(!n)throw new Error(\"useDndMonitor must be used within a children of <DndContext>\");return n(e)},[e,n])}(t.useMemo(()=>({onDragStart(e){let{active:t}=e;l(o.onDragStart({active:t}))},onDragMove(e){let{active:t,over:n}=e;o.onDragMove&&l(o.onDragMove({active:t,over:n}))},onDragOver(e){let{active:t,over:n}=e;l(o.onDragOver({active:t,over:n}))},onDragEnd(e){let{active:t,over:n}=e;l(o.onDragEnd({active:t,over:n}))},onDragCancel(e){let{active:t,over:n}=e;l(o.onDragCancel({active:t,over:n}))}}),[l,o])),!u)return null;const f=n.createElement(n.Fragment,null,n.createElement(te,{id:a,value:s.draggable}),n.createElement(ne,{id:d,announcement:c}));return i?r.createPortal(f,i):f}var se,le;function ce(){}function de(e,n){return t.useMemo(()=>({sensor:e,options:null!=n?n:{}}),[e,n])}(le=se||(se={})).DragStart=\"dragStart\",le.DragMove=\"dragMove\",le.DragEnd=\"dragEnd\",le.DragCancel=\"dragCancel\",le.DragOver=\"dragOver\",le.RegisterDroppable=\"registerDroppable\",le.SetDroppableDisabled=\"setDroppableDisabled\",le.UnregisterDroppable=\"unregisterDroppable\";const ue=Object.freeze({x:0,y:0});function he(e,t){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))}function fe(e,t){let{data:{value:n}}=e,{data:{value:r}}=t;return n-r}function ge(e,t){let{data:{value:n}}=e,{data:{value:r}}=t;return r-n}function ve(e){let{left:t,top:n,height:r,width:o}=e;return[{x:t,y:n},{x:t+o,y:n},{x:t,y:n+r},{x:t+o,y:n+r}]}function pe(e,t){if(!e||0===e.length)return null;const[n]=e;return n[t]}function me(e,t,n){return void 0===t&&(t=e.left),void 0===n&&(n=e.top),{x:t+.5*e.width,y:n+.5*e.height}}const be=e=>{let{collisionRect:t,droppableRects:n,droppableContainers:r}=e;const o=me(t,t.left,t.top),i=[];for(const a of r){const{id:e}=a,t=n.get(e);if(t){const n=he(me(t),o);i.push({id:e,data:{droppableContainer:a,value:n}})}}return i.sort(fe)};function xe(e,t){const n=Math.max(t.top,e.top),r=Math.max(t.left,e.left),o=Math.min(t.left+t.width,e.left+e.width),i=Math.min(t.top+t.height,e.top+e.height),a=o-r,s=i-n;if(r<o&&n<i){const n=t.width*t.height,r=e.width*e.height,o=a*s;return Number((o/(n+r-o)).toFixed(4))}return 0}const ye=e=>{let{collisionRect:t,droppableRects:n,droppableContainers:r}=e;const o=[];for(const i of r){const{id:e}=i,r=n.get(e);if(r){const n=xe(r,t);n>0&&o.push({id:e,data:{droppableContainer:i,value:n}})}}return o.sort(ge)};function we(e,t){return e&&t?{x:e.left-t.left,y:e.top-t.top}:ue}function Ce(e){return function(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;o<n;o++)r[o-1]=arguments[o];return r.reduce((t,n)=>({...t,top:t.top+e*n.y,bottom:t.bottom+e*n.y,left:t.left+e*n.x,right:t.right+e*n.x}),{...t})}}const Ne=Ce(1);const De={ignoreTransform:!1};function Ee(e,t){void 0===t&&(t=De);let n=e.getBoundingClientRect();if(t.ignoreTransform){const{transform:t,transformOrigin:r}=A(e).getComputedStyle(e);t&&(n=function(e,t,n){const r=function(e){if(e.startsWith(\"matrix3d(\")){const t=e.slice(9,-1).split(/, /);return{x:+t[12],y:+t[13],scaleX:+t[0],scaleY:+t[5]}}if(e.startsWith(\"matrix(\")){const t=e.slice(7,-1).split(/, /);return{x:+t[4],y:+t[5],scaleX:+t[0],scaleY:+t[3]}}return null}(t);if(!r)return e;const{scaleX:o,scaleY:i,x:a,y:s}=r,l=e.left-a-(1-o)*parseFloat(n),c=e.top-s-(1-i)*parseFloat(n.slice(n.indexOf(\" \")+1)),d=o?e.width/o:e.width,u=i?e.height/i:e.height;return{width:d,height:u,top:c,right:l+d,bottom:c+u,left:l}}(n,t,r))}const{top:r,left:o,width:i,height:a,bottom:s,right:l}=n;return{top:r,left:o,width:i,height:a,bottom:s,right:l}}function je(e){return Ee(e,{ignoreTransform:!0})}function Re(e,t){const n=[];return e?function r(o){if(null!=t&&n.length>=t)return n;if(!o)return n;if(O(o)&&null!=o.scrollingElement&&!n.includes(o.scrollingElement))return n.push(o.scrollingElement),n;if(!L(o)||B(o))return n;if(n.includes(o))return n;const i=A(e).getComputedStyle(o);return o!==e&&function(e,t){void 0===t&&(t=A(e).getComputedStyle(e));const n=/(auto|scroll|overlay)/;return[\"overflow\",\"overflowX\",\"overflowY\"].some(e=>{const r=t[e];return\"string\"==typeof r&&n.test(r)})}(o,i)&&n.push(o),function(e,t){return void 0===t&&(t=A(e).getComputedStyle(e)),\"fixed\"===t.position}(o,i)?n:r(o.parentNode)}(e):n}function ke(e){const[t]=Re(e,1);return null!=t?t:null}function Se(e){return M&&e?I(e)?e:T(e)?O(e)||e===z(e).scrollingElement?window:L(e)?e:null:null:null}function Me(e){return I(e)?e.scrollX:e.scrollLeft}function Ie(e){return I(e)?e.scrollY:e.scrollTop}function Te(e){return{x:Me(e),y:Ie(e)}}var Ae,Oe;function Le(e){return!(!M||!e)&&e===document.scrollingElement}function Be(e){const t={x:0,y:0},n=Le(e)?{height:window.innerHeight,width:window.innerWidth}:{height:e.clientHeight,width:e.clientWidth},r={x:e.scrollWidth-n.width,y:e.scrollHeight-n.height};return{isTop:e.scrollTop<=t.y,isLeft:e.scrollLeft<=t.x,isBottom:e.scrollTop>=r.y,isRight:e.scrollLeft>=r.x,maxScroll:r,minScroll:t}}(Oe=Ae||(Ae={}))[Oe.Forward=1]=\"Forward\",Oe[Oe.Backward=-1]=\"Backward\";const ze={x:.2,y:.2};function Pe(e,t,n,r,o){let{top:i,left:a,right:s,bottom:l}=n;void 0===r&&(r=10),void 0===o&&(o=ze);const{isTop:c,isBottom:d,isLeft:u,isRight:h}=Be(e),f={x:0,y:0},g={x:0,y:0},v=t.height*o.y,p=t.width*o.x;return!c&&i<=t.top+v?(f.y=Ae.Backward,g.y=r*Math.abs((t.top+v-i)/v)):!d&&l>=t.bottom-v&&(f.y=Ae.Forward,g.y=r*Math.abs((t.bottom-v-l)/v)),!h&&s>=t.right-p?(f.x=Ae.Forward,g.x=r*Math.abs((t.right-p-s)/p)):!u&&a<=t.left+p&&(f.x=Ae.Backward,g.x=r*Math.abs((t.left+p-a)/p)),{direction:f,speed:g}}function Fe(e){if(e===document.scrollingElement){const{innerWidth:e,innerHeight:t}=window;return{top:0,left:0,right:e,bottom:t,width:e,height:t}}const{top:t,left:n,right:r,bottom:o}=e.getBoundingClientRect();return{top:t,left:n,right:r,bottom:o,width:e.clientWidth,height:e.clientHeight}}function Ue(e){return e.reduce((e,t)=>q(e,Te(t)),ue)}const Ve=[[\"x\",[\"left\",\"right\"],function(e){return e.reduce((e,t)=>e+Me(t),0)}],[\"y\",[\"top\",\"bottom\"],function(e){return e.reduce((e,t)=>e+Ie(t),0)}]];class Ke{constructor(e,t){this.rect=void 0,this.width=void 0,this.height=void 0,this.top=void 0,this.bottom=void 0,this.right=void 0,this.left=void 0;const n=Re(t),r=Ue(n);this.rect={...e},this.width=e.width,this.height=e.height;for(const[o,i,a]of Ve)for(const e of i)Object.defineProperty(this,e,{get:()=>{const t=a(n),i=r[o]-t;return this.rect[e]+i},enumerable:!0});Object.defineProperty(this,\"rect\",{enumerable:!1})}}class Xe{constructor(e){this.target=void 0,this.listeners=[],this.removeAll=()=>{this.listeners.forEach(e=>{var t;return null==(t=this.target)?void 0:t.removeEventListener(...e)})},this.target=e}add(e,t,n){var r;null==(r=this.target)||r.addEventListener(e,t,n),this.listeners.push([e,t,n])}}function Ye(e,t){const n=Math.abs(e.x),r=Math.abs(e.y);return\"number\"==typeof t?Math.sqrt(n**2+r**2)>t:\"x\"in t&&\"y\"in t?n>t.x&&r>t.y:\"x\"in t?n>t.x:\"y\"in t&&r>t.y}var We,He,qe,Je;function $e(e){e.preventDefault()}function _e(e){e.stopPropagation()}(He=We||(We={})).Click=\"click\",He.DragStart=\"dragstart\",He.Keydown=\"keydown\",He.ContextMenu=\"contextmenu\",He.Resize=\"resize\",He.SelectionChange=\"selectionchange\",He.VisibilityChange=\"visibilitychange\",(Je=qe||(qe={})).Space=\"Space\",Je.Down=\"ArrowDown\",Je.Right=\"ArrowRight\",Je.Left=\"ArrowLeft\",Je.Up=\"ArrowUp\",Je.Esc=\"Escape\",Je.Enter=\"Enter\",Je.Tab=\"Tab\";const Ge={start:[qe.Space,qe.Enter],cancel:[qe.Esc],end:[qe.Space,qe.Enter,qe.Tab]},Qe=(e,t)=>{let{currentCoordinates:n}=t;switch(e.code){case qe.Right:return{...n,x:n.x+25};case qe.Left:return{...n,x:n.x-25};case qe.Down:return{...n,y:n.y+25};case qe.Up:return{...n,y:n.y-25}}};class Ze{constructor(e){this.props=void 0,this.autoScrollEnabled=!1,this.referenceCoordinates=void 0,this.listeners=void 0,this.windowListeners=void 0,this.props=e;const{event:{target:t}}=e;this.props=e,this.listeners=new Xe(z(t)),this.windowListeners=new Xe(A(t)),this.handleKeyDown=this.handleKeyDown.bind(this),this.handleCancel=this.handleCancel.bind(this),this.attach()}attach(){this.handleStart(),this.windowListeners.add(We.Resize,this.handleCancel),this.windowListeners.add(We.VisibilityChange,this.handleCancel),setTimeout(()=>this.listeners.add(We.Keydown,this.handleKeyDown))}handleStart(){const{activeNode:e,onStart:t}=this.props,n=e.node.current;n&&function(e,t){if(void 0===t&&(t=Ee),!e)return;const{top:n,left:r,bottom:o,right:i}=t(e);ke(e)&&(o<=0||i<=0||n>=window.innerHeight||r>=window.innerWidth)&&e.scrollIntoView({block:\"center\",inline:\"center\"})}(n),t(ue)}handleKeyDown(e){if($(e)){const{active:t,context:n,options:r}=this.props,{keyboardCodes:o=Ge,coordinateGetter:i=Qe,scrollBehavior:a=\"smooth\"}=r,{code:s}=e;if(o.end.includes(s))return void this.handleEnd(e);if(o.cancel.includes(s))return void this.handleCancel(e);const{collisionRect:l}=n.current,c=l?{x:l.left,y:l.top}:ue;this.referenceCoordinates||(this.referenceCoordinates=c);const d=i(e,{active:t,context:n.current,currentCoordinates:c});if(d){const t=J(d,c),r={x:0,y:0},{scrollableAncestors:o}=n.current;for(const n of o){const o=e.code,{isTop:i,isRight:s,isLeft:l,isBottom:c,maxScroll:u,minScroll:h}=Be(n),f=Fe(n),g={x:Math.min(o===qe.Right?f.right-f.width/2:f.right,Math.max(o===qe.Right?f.left:f.left+f.width/2,d.x)),y:Math.min(o===qe.Down?f.bottom-f.height/2:f.bottom,Math.max(o===qe.Down?f.top:f.top+f.height/2,d.y))},v=o===qe.Right&&!s||o===qe.Left&&!l,p=o===qe.Down&&!c||o===qe.Up&&!i;if(v&&g.x!==d.x){const e=n.scrollLeft+t.x,i=o===qe.Right&&e<=u.x||o===qe.Left&&e>=h.x;if(i&&!t.y)return void n.scrollTo({left:e,behavior:a});r.x=i?n.scrollLeft-e:o===qe.Right?n.scrollLeft-u.x:n.scrollLeft-h.x,r.x&&n.scrollBy({left:-r.x,behavior:a});break}if(p&&g.y!==d.y){const e=n.scrollTop+t.y,i=o===qe.Down&&e<=u.y||o===qe.Up&&e>=h.y;if(i&&!t.x)return void n.scrollTo({top:e,behavior:a});r.y=i?n.scrollTop-e:o===qe.Down?n.scrollTop-u.y:n.scrollTop-h.y,r.y&&n.scrollBy({top:-r.y,behavior:a});break}}this.handleMove(e,q(J(d,this.referenceCoordinates),r))}}}handleMove(e,t){const{onMove:n}=this.props;e.preventDefault(),n(t)}handleEnd(e){const{onEnd:t}=this.props;e.preventDefault(),this.detach(),t()}handleCancel(e){const{onCancel:t}=this.props;e.preventDefault(),this.detach(),t()}detach(){this.listeners.removeAll(),this.windowListeners.removeAll()}}function et(e){return Boolean(e&&\"distance\"in e)}function tt(e){return Boolean(e&&\"delay\"in e)}Ze.activators=[{eventName:\"onKeyDown\",handler:(e,t,n)=>{let{keyboardCodes:r=Ge,onActivation:o}=t,{active:i}=n;const{code:a}=e.nativeEvent;if(r.start.includes(a)){const t=i.activatorNode.current;return(!t||e.target===t)&&(e.preventDefault(),null==o||o({event:e.nativeEvent}),!0)}return!1}}];class nt{constructor(e,t,n){var r;void 0===n&&(n=function(e){const{EventTarget:t}=A(e);return e instanceof t?e:z(e)}(e.event.target)),this.props=void 0,this.events=void 0,this.autoScrollEnabled=!0,this.document=void 0,this.activated=!1,this.initialCoordinates=void 0,this.timeoutId=null,this.listeners=void 0,this.documentListeners=void 0,this.windowListeners=void 0,this.props=e,this.events=t;const{event:o}=e,{target:i}=o;this.props=e,this.events=t,this.document=z(i),this.documentListeners=new Xe(this.document),this.listeners=new Xe(n),this.windowListeners=new Xe(A(i)),this.initialCoordinates=null!=(r=_(o))?r:ue,this.handleStart=this.handleStart.bind(this),this.handleMove=this.handleMove.bind(this),this.handleEnd=this.handleEnd.bind(this),this.handleCancel=this.handleCancel.bind(this),this.handleKeydown=this.handleKeydown.bind(this),this.removeTextSelection=this.removeTextSelection.bind(this),this.attach()}attach(){const{events:e,props:{options:{activationConstraint:t,bypassActivationConstraint:n}}}=this;if(this.listeners.add(e.move.name,this.handleMove,{passive:!1}),this.listeners.add(e.end.name,this.handleEnd),e.cancel&&this.listeners.add(e.cancel.name,this.handleCancel),this.windowListeners.add(We.Resize,this.handleCancel),this.windowListeners.add(We.DragStart,$e),this.windowListeners.add(We.VisibilityChange,this.handleCancel),this.windowListeners.add(We.ContextMenu,$e),this.documentListeners.add(We.Keydown,this.handleKeydown),t){if(null!=n&&n({event:this.props.event,activeNode:this.props.activeNode,options:this.props.options}))return this.handleStart();if(tt(t))return this.timeoutId=setTimeout(this.handleStart,t.delay),void this.handlePending(t);if(et(t))return void this.handlePending(t)}this.handleStart()}detach(){this.listeners.removeAll(),this.windowListeners.removeAll(),setTimeout(this.documentListeners.removeAll,50),null!==this.timeoutId&&(clearTimeout(this.timeoutId),this.timeoutId=null)}handlePending(e,t){const{active:n,onPending:r}=this.props;r(n,e,this.initialCoordinates,t)}handleStart(){const{initialCoordinates:e}=this,{onStart:t}=this.props;e&&(this.activated=!0,this.documentListeners.add(We.Click,_e,{capture:!0}),this.removeTextSelection(),this.documentListeners.add(We.SelectionChange,this.removeTextSelection),t(e))}handleMove(e){var t;const{activated:n,initialCoordinates:r,props:o}=this,{onMove:i,options:{activationConstraint:a}}=o;if(!r)return;const s=null!=(t=_(e))?t:ue,l=J(r,s);if(!n&&a){if(et(a)){if(null!=a.tolerance&&Ye(l,a.tolerance))return this.handleCancel();if(Ye(l,a.distance))return this.handleStart()}return tt(a)&&Ye(l,a.tolerance)?this.handleCancel():void this.handlePending(a,l)}e.cancelable&&e.preventDefault(),i(s)}handleEnd(){const{onAbort:e,onEnd:t}=this.props;this.detach(),this.activated||e(this.props.active),t()}handleCancel(){const{onAbort:e,onCancel:t}=this.props;this.detach(),this.activated||e(this.props.active),t()}handleKeydown(e){e.code===qe.Esc&&this.handleCancel()}removeTextSelection(){var e;null==(e=this.document.getSelection())||e.removeAllRanges()}}const rt={cancel:{name:\"pointercancel\"},move:{name:\"pointermove\"},end:{name:\"pointerup\"}};class ot extends nt{constructor(e){const{event:t}=e,n=z(t.target);super(e,rt,n)}}ot.activators=[{eventName:\"onPointerDown\",handler:(e,t)=>{let{nativeEvent:n}=e,{onActivation:r}=t;return!(!n.isPrimary||0!==n.button)&&(null==r||r({event:n}),!0)}}];const it={move:{name:\"mousemove\"},end:{name:\"mouseup\"}};var at,st;(st=at||(at={}))[st.RightClick=2]=\"RightClick\";(class extends nt{constructor(e){super(e,it,z(e.event.target))}}).activators=[{eventName:\"onMouseDown\",handler:(e,t)=>{let{nativeEvent:n}=e,{onActivation:r}=t;return n.button!==at.RightClick&&(null==r||r({event:n}),!0)}}];const lt={cancel:{name:\"touchcancel\"},move:{name:\"touchmove\"},end:{name:\"touchend\"}};class ct extends nt{constructor(e){super(e,lt)}static setup(){return window.addEventListener(lt.move.name,e,{capture:!1,passive:!1}),function(){window.removeEventListener(lt.move.name,e)};function e(){}}}var dt,ut,ht,ft;function gt(e){let{acceleration:n,activator:r=dt.Pointer,canScroll:o,draggingRect:i,enabled:a,interval:s=5,order:l=ht.TreeOrder,pointerCoordinates:c,scrollableAncestors:d,scrollableAncestorRects:u,delta:h,threshold:f}=e;const g=function(e){let{delta:t,disabled:n}=e;const r=X(t);return V(e=>{if(n||!r||!e)return vt;const o={x:Math.sign(t.x-r.x),y:Math.sign(t.y-r.y)};return{x:{[Ae.Backward]:e.x[Ae.Backward]||-1===o.x,[Ae.Forward]:e.x[Ae.Forward]||1===o.x},y:{[Ae.Backward]:e.y[Ae.Backward]||-1===o.y,[Ae.Forward]:e.y[Ae.Forward]||1===o.y}}},[n,t,r])}({delta:h,disabled:!a}),[v,p]=function(){const e=t.useRef(null);return[t.useCallback((t,n)=>{e.current=setInterval(t,n)},[]),t.useCallback(()=>{null!==e.current&&(clearInterval(e.current),e.current=null)},[])]}(),m=t.useRef({x:0,y:0}),b=t.useRef({x:0,y:0}),x=t.useMemo(()=>{switch(r){case dt.Pointer:return c?{top:c.y,bottom:c.y,left:c.x,right:c.x}:null;case dt.DraggableRect:return i}},[r,i,c]),y=t.useRef(null),w=t.useCallback(()=>{const e=y.current;if(!e)return;const t=m.current.x*b.current.x,n=m.current.y*b.current.y;e.scrollBy(t,n)},[]),C=t.useMemo(()=>l===ht.TreeOrder?[...d].reverse():d,[l,d]);t.useEffect(()=>{if(a&&d.length&&x){for(const e of C){if(!1===(null==o?void 0:o(e)))continue;const t=d.indexOf(e),r=u[t];if(!r)continue;const{direction:i,speed:a}=Pe(e,r,x,n,f);for(const e of[\"x\",\"y\"])g[e][i[e]]||(a[e]=0,i[e]=0);if(a.x>0||a.y>0)return p(),y.current=e,v(w,s),m.current=a,void(b.current=i)}m.current={x:0,y:0},b.current={x:0,y:0},p()}else p()},[n,w,o,p,a,s,JSON.stringify(x),JSON.stringify(g),v,d,C,u,JSON.stringify(f)])}ct.activators=[{eventName:\"onTouchStart\",handler:(e,t)=>{let{nativeEvent:n}=e,{onActivation:r}=t;const{touches:o}=n;return!(o.length>1)&&(null==r||r({event:n}),!0)}}],(ut=dt||(dt={}))[ut.Pointer=0]=\"Pointer\",ut[ut.DraggableRect=1]=\"DraggableRect\",(ft=ht||(ht={}))[ft.TreeOrder=0]=\"TreeOrder\",ft[ft.ReversedTreeOrder=1]=\"ReversedTreeOrder\";const vt={x:{[Ae.Backward]:!1,[Ae.Forward]:!1},y:{[Ae.Backward]:!1,[Ae.Forward]:!1}};var pt,mt,bt;(mt=pt||(pt={}))[mt.Always=0]=\"Always\",mt[mt.BeforeDragging=1]=\"BeforeDragging\",mt[mt.WhileDragging=2]=\"WhileDragging\",(bt||(bt={})).Optimized=\"optimized\";const xt=new Map;function yt(e,t){return V(n=>e?n||(\"function\"==typeof t?t(e):e):null,[t,e])}function wt(e){let{callback:n,disabled:r}=e;const o=F(n),i=t.useMemo(()=>{if(r||\"undefined\"==typeof window||void 0===window.ResizeObserver)return;const{ResizeObserver:e}=window;return new e(o)},[r]);return t.useEffect(()=>()=>null==i?void 0:i.disconnect(),[i]),i}function Ct(e){return new Ke(Ee(e),e)}function Nt(e,n,r){void 0===n&&(n=Ct);const[o,i]=t.useState(null);function a(){i(t=>{if(!e)return null;var o;if(!1===e.isConnected)return null!=(o=null!=t?t:r)?o:null;const i=n(e);return JSON.stringify(t)===JSON.stringify(i)?t:i})}const s=function(e){let{callback:n,disabled:r}=e;const o=F(n),i=t.useMemo(()=>{if(r||\"undefined\"==typeof window||void 0===window.MutationObserver)return;const{MutationObserver:e}=window;return new e(o)},[o,r]);return t.useEffect(()=>()=>null==i?void 0:i.disconnect(),[i]),i}({callback(t){if(e)for(const n of t){const{type:t,target:r}=n;if(\"childList\"===t&&r instanceof HTMLElement&&r.contains(e)){a();break}}}}),l=wt({callback:a});return P(()=>{a(),e?(null==l||l.observe(e),null==s||s.observe(document.body,{childList:!0,subtree:!0})):(null==l||l.disconnect(),null==s||s.disconnect())},[e]),o}const Dt=[];function Et(e,n){void 0===n&&(n=[]);const r=t.useRef(null);return t.useEffect(()=>{r.current=null},n),t.useEffect(()=>{const t=e!==ue;t&&!r.current&&(r.current=e),!t&&r.current&&(r.current=null)},[e]),r.current?J(e,r.current):ue}function jt(e){return t.useMemo(()=>e?function(e){const t=e.innerWidth,n=e.innerHeight;return{top:0,left:0,right:t,bottom:n,width:t,height:n}}(e):null,[e])}const Rt=[];function kt(e){let{measure:n}=e;const[r,o]=t.useState(null),i=wt({callback:t.useCallback(e=>{for(const{target:t}of e)if(L(t)){o(e=>{const r=n(t);return e?{...e,width:r.width,height:r.height}:r});break}},[n])}),a=t.useCallback(e=>{const t=function(e){if(!e)return null;if(e.children.length>1)return e;const t=e.children[0];return L(t)?t:e}(e);null==i||i.disconnect(),t&&(null==i||i.observe(t)),o(t?n(t):null)},[n,i]),[s,l]=K(a);return t.useMemo(()=>({nodeRef:s,rect:r,setRef:l}),[r,s,l])}const St=[{sensor:ot,options:{}},{sensor:Ze,options:{}}],Mt={current:{}},It={draggable:{measure:je},droppable:{measure:je,strategy:pt.WhileDragging,frequency:bt.Optimized},dragOverlay:{measure:Ee}};class Tt extends Map{get(e){var t;return null!=e&&null!=(t=super.get(e))?t:void 0}toArray(){return Array.from(this.values())}getEnabled(){return this.toArray().filter(e=>{let{disabled:t}=e;return!t})}getNodeFor(e){var t,n;return null!=(t=null==(n=this.get(e))?void 0:n.node.current)?t:void 0}}const At={activatorEvent:null,active:null,activeNode:null,activeNodeRect:null,collisions:null,containerNodeRect:null,draggableNodes:new Map,droppableRects:new Map,droppableContainers:new Tt,over:null,dragOverlay:{nodeRef:{current:null},rect:null,setRef:ce},scrollableAncestors:[],scrollableAncestorRects:[],measuringConfiguration:It,measureDroppableContainers:ce,windowRect:null,measuringScheduled:!1},Ot={activatorEvent:null,activators:[],active:null,activeNodeRect:null,ariaDescribedById:{draggable:\"\"},dispatch:ce,draggableNodes:new Map,over:null,measureDroppableContainers:ce},Lt=t.createContext(Ot),Bt=t.createContext(At);function zt(){return{draggable:{active:null,initialCoordinates:{x:0,y:0},nodes:new Map,translate:{x:0,y:0}},droppable:{containers:new Tt}}}function Pt(e,t){switch(t.type){case se.DragStart:return{...e,draggable:{...e.draggable,initialCoordinates:t.initialCoordinates,active:t.active}};case se.DragMove:return null==e.draggable.active?e:{...e,draggable:{...e.draggable,translate:{x:t.coordinates.x-e.draggable.initialCoordinates.x,y:t.coordinates.y-e.draggable.initialCoordinates.y}}};case se.DragEnd:case se.DragCancel:return{...e,draggable:{...e.draggable,active:null,initialCoordinates:{x:0,y:0},translate:{x:0,y:0}}};case se.RegisterDroppable:{const{element:n}=t,{id:r}=n,o=new Tt(e.droppable.containers);return o.set(r,n),{...e,droppable:{...e.droppable,containers:o}}}case se.SetDroppableDisabled:{const{id:n,key:r,disabled:o}=t,i=e.droppable.containers.get(n);if(!i||r!==i.key)return e;const a=new Tt(e.droppable.containers);return a.set(n,{...i,disabled:o}),{...e,droppable:{...e.droppable,containers:a}}}case se.UnregisterDroppable:{const{id:n,key:r}=t,o=e.droppable.containers.get(n);if(!o||r!==o.key)return e;const i=new Tt(e.droppable.containers);return i.delete(n),{...e,droppable:{...e.droppable,containers:i}}}default:return e}}function Ft(e){let{disabled:n}=e;const{active:r,activatorEvent:o,draggableNodes:i}=t.useContext(Lt),a=X(o),s=X(null==r?void 0:r.id);return t.useEffect(()=>{if(!n&&!o&&a&&null!=s){if(!$(a))return;if(document.activeElement===a.target)return;const e=i.get(s);if(!e)return;const{activatorNode:t,node:n}=e;if(!t.current&&!n.current)return;requestAnimationFrame(()=>{for(const e of[t.current,n.current]){if(!e)continue;const t=Z(e);if(t){t.focus();break}}})}},[o,n,i,s,a]),null}const Ut=t.createContext({...ue,scaleX:1,scaleY:1});var Vt,Kt;(Kt=Vt||(Vt={}))[Kt.Uninitialized=0]=\"Uninitialized\",Kt[Kt.Initializing=1]=\"Initializing\",Kt[Kt.Initialized=2]=\"Initialized\";const Xt=t.memo(function(e){var o,i,a,s;let{id:l,accessibility:c,autoScroll:d=!0,children:u,sensors:h=St,collisionDetection:f=ye,measuring:g,modifiers:v,...p}=e;const m=t.useReducer(Pt,void 0,zt),[b,x]=m,[y,w]=function(){const[e]=t.useState(()=>new Set),n=t.useCallback(t=>(e.add(t),()=>e.delete(t)),[e]);return[t.useCallback(t=>{let{type:n,event:r}=t;e.forEach(e=>{var t;return null==(t=e[n])?void 0:t.call(e,r)})},[e]),n]}(),[C,N]=t.useState(Vt.Uninitialized),D=C===Vt.Initialized,{draggable:{active:E,nodes:j,translate:R},droppable:{containers:k}}=b,S=null!=E?j.get(E):null,I=t.useRef({initial:null,translated:null}),T=t.useMemo(()=>{var e;return null!=E?{id:E,data:null!=(e=null==S?void 0:S.data)?e:Mt,rect:I}:null},[E,S]),O=t.useRef(null),[L,B]=t.useState(null),[z,F]=t.useState(null),K=U(p,Object.values(p)),X=W(\"DndDescribedBy\",l),Y=t.useMemo(()=>k.getEnabled(),[k]),H=(J=g,t.useMemo(()=>({draggable:{...It.draggable,...null==J?void 0:J.draggable},droppable:{...It.droppable,...null==J?void 0:J.droppable},dragOverlay:{...It.dragOverlay,...null==J?void 0:J.dragOverlay}}),[null==J?void 0:J.draggable,null==J?void 0:J.droppable,null==J?void 0:J.dragOverlay]));var J;const{droppableRects:$,measureDroppableContainers:G,measuringScheduled:Q}=function(e,n){let{dragging:r,dependencies:o,config:i}=n;const[a,s]=t.useState(null),{frequency:l,measure:c,strategy:d}=i,u=t.useRef(e),h=function(){switch(d){case pt.Always:return!1;case pt.BeforeDragging:return r;default:return!r}}(),f=U(h),g=t.useCallback(function(e){void 0===e&&(e=[]),f.current||s(t=>null===t?e:t.concat(e.filter(e=>!t.includes(e))))},[f]),v=t.useRef(null),p=V(t=>{if(h&&!r)return xt;if(!t||t===xt||u.current!==e||null!=a){const t=new Map;for(let n of e){if(!n)continue;if(a&&a.length>0&&!a.includes(n.id)&&n.rect.current){t.set(n.id,n.rect.current);continue}const e=n.node.current,r=e?new Ke(c(e),e):null;n.rect.current=r,r&&t.set(n.id,r)}return t}return t},[e,a,r,h,c]);return t.useEffect(()=>{u.current=e},[e]),t.useEffect(()=>{h||g()},[r,h]),t.useEffect(()=>{a&&a.length>0&&s(null)},[JSON.stringify(a)]),t.useEffect(()=>{h||\"number\"!=typeof l||null!==v.current||(v.current=setTimeout(()=>{g(),v.current=null},l))},[l,h,g,...o]),{droppableRects:p,measureDroppableContainers:g,measuringScheduled:null!=a}}(Y,{dragging:D,dependencies:[R.x,R.y],config:H.droppable}),Z=function(e,t){const n=null!=t?e.get(t):void 0,r=n?n.node.current:null;return V(e=>{var n;return null==t?null:null!=(n=null!=r?r:e)?n:null},[r,t])}(j,E),ee=t.useMemo(()=>z?_(z):null,[z]),te=function(){const e=!1===(null==L?void 0:L.autoScrollEnabled),t=\"object\"==typeof d?!1===d.enabled:!1===d,n=D&&!e&&!t;if(\"object\"==typeof d)return{...d,enabled:n};return{enabled:n}}(),ne=function(e,t){return yt(e,t)}(Z,H.draggable.measure);!function(e){let{activeNode:n,measure:r,initialRect:o,config:i=!0}=e;const a=t.useRef(!1),{x:s,y:l}=\"boolean\"==typeof i?{x:i,y:i}:i;P(()=>{if(!s&&!l||!n)return void(a.current=!1);if(a.current||!o)return;const e=null==n?void 0:n.node.current;if(!e||!1===e.isConnected)return;const t=we(r(e),o);if(s||(t.x=0),l||(t.y=0),a.current=!0,Math.abs(t.x)>0||Math.abs(t.y)>0){const n=ke(e);n&&n.scrollBy({top:t.y,left:t.x})}},[n,s,l,o,r])}({activeNode:null!=E?j.get(E):null,config:te.layoutShiftCompensation,initialRect:ne,measure:H.draggable.measure});const oe=Nt(Z,H.draggable.measure,ne),ie=Nt(Z?Z.parentElement:null),le=t.useRef({activatorEvent:null,active:null,activeNode:Z,collisionRect:null,collisions:null,droppableRects:$,draggableNodes:j,draggingNode:null,draggingNodeRect:null,droppableContainers:k,over:null,scrollableAncestors:[],scrollAdjustedTranslate:null}),ce=k.getNodeFor(null==(o=le.current.over)?void 0:o.id),de=kt({measure:H.dragOverlay.measure}),he=null!=(i=de.nodeRef.current)?i:Z,fe=D?null!=(a=de.rect)?a:oe:null,ge=Boolean(de.nodeRef.current&&de.rect),ve=we(me=ge?null:oe,yt(me));var me;const be=jt(he?A(he):null),xe=function(e){const n=t.useRef(e),r=V(t=>e?t&&t!==Dt&&e&&n.current&&e.parentNode===n.current.parentNode?t:Re(e):Dt,[e]);return t.useEffect(()=>{n.current=e},[e]),r}(D?null!=ce?ce:Z:null),Ce=function(e,n){void 0===n&&(n=Ee);const[r]=e,o=jt(r?A(r):null),[i,a]=t.useState(Rt);function s(){a(()=>e.length?e.map(e=>Le(e)?o:new Ke(n(e),e)):Rt)}const l=wt({callback:s});return P(()=>{null==l||l.disconnect(),s(),e.forEach(e=>null==l?void 0:l.observe(e))},[e]),i}(xe),De=function(e,t){let{transform:n,...r}=t;return null!=e&&e.length?e.reduce((e,t)=>t({transform:e,...r}),n):n}(v,{transform:{x:R.x-ve.x,y:R.y-ve.y,scaleX:1,scaleY:1},activatorEvent:z,active:T,activeNodeRect:oe,containerNodeRect:ie,draggingNodeRect:fe,over:le.current.over,overlayNodeRect:de.rect,scrollableAncestors:xe,scrollableAncestorRects:Ce,windowRect:be}),je=ee?q(ee,R):null,Me=function(e){const[n,r]=t.useState(null),o=t.useRef(e),i=t.useCallback(e=>{const t=Se(e.target);t&&r(e=>e?(e.set(t,Te(t)),new Map(e)):null)},[]);return t.useEffect(()=>{const t=o.current;if(e!==t){n(t);const a=e.map(e=>{const t=Se(e);return t?(t.addEventListener(\"scroll\",i,{passive:!0}),[t,Te(t)]):null}).filter(e=>null!=e);r(a.length?new Map(a):null),o.current=e}return()=>{n(e),n(t)};function n(e){e.forEach(e=>{const t=Se(e);null==t||t.removeEventListener(\"scroll\",i)})}},[i,e]),t.useMemo(()=>e.length?n?Array.from(n.values()).reduce((e,t)=>q(e,t),ue):Ue(e):ue,[e,n])}(xe),Ie=Et(Me),Ae=Et(Me,[oe]),Oe=q(De,Ie),Be=fe?Ne(fe,De):null,ze=T&&Be?f({active:T,collisionRect:Be,droppableRects:$,droppableContainers:Y,pointerCoordinates:je}):null,Pe=pe(ze,\"id\"),[Fe,Ve]=t.useState(null),Xe=function(e,t,n){return{...e,scaleX:t&&n?t.width/n.width:1,scaleY:t&&n?t.height/n.height:1}}(ge?De:q(De,Ae),null!=(s=null==Fe?void 0:Fe.rect)?s:null,oe),Ye=t.useRef(null),We=t.useCallback((e,t)=>{let{sensor:n,options:o}=t;if(null==O.current)return;const i=j.get(O.current);if(!i)return;const a=e.nativeEvent,s=new n({active:O.current,activeNode:i,event:a,options:o,context:le,onAbort(e){if(!j.get(e))return;const{onDragAbort:t}=K.current,n={id:e};null==t||t(n),y({type:\"onDragAbort\",event:n})},onPending(e,t,n,r){if(!j.get(e))return;const{onDragPending:o}=K.current,i={id:e,constraint:t,initialCoordinates:n,offset:r};null==o||o(i),y({type:\"onDragPending\",event:i})},onStart(e){const t=O.current;if(null==t)return;const n=j.get(t);if(!n)return;const{onDragStart:o}=K.current,i={activatorEvent:a,active:{id:t,data:n.data,rect:I}};r.unstable_batchedUpdates(()=>{null==o||o(i),N(Vt.Initializing),x({type:se.DragStart,initialCoordinates:e,active:t}),y({type:\"onDragStart\",event:i}),B(Ye.current),F(a)})},onMove(e){x({type:se.DragMove,coordinates:e})},onEnd:l(se.DragEnd),onCancel:l(se.DragCancel)});function l(e){return async function(){const{active:t,collisions:n,over:o,scrollAdjustedTranslate:i}=le.current;let s=null;if(t&&i){const{cancelDrop:r}=K.current;if(s={activatorEvent:a,active:t,collisions:n,delta:i,over:o},e===se.DragEnd&&\"function\"==typeof r){await Promise.resolve(r(s))&&(e=se.DragCancel)}}O.current=null,r.unstable_batchedUpdates(()=>{x({type:e}),N(Vt.Uninitialized),Ve(null),B(null),F(null),Ye.current=null;const t=e===se.DragEnd?\"onDragEnd\":\"onDragCancel\";if(s){const e=K.current[t];null==e||e(s),y({type:t,event:s})}})}}Ye.current=s},[j]),He=function(e,n){return t.useMemo(()=>e.reduce((e,t)=>{const{sensor:r}=t;return[...e,...r.activators.map(e=>({eventName:e.eventName,handler:n(e.handler,t)}))]},[]),[e,n])}(h,t.useCallback((e,t)=>(n,r)=>{const o=n.nativeEvent,i=j.get(r);if(null!==O.current||!i||o.dndKit||o.defaultPrevented)return;const a={active:i};!0===e(n,t.options,a)&&(o.dndKit={capturedBy:t.sensor},O.current=r,We(n,t))},[j,We]));!function(e){t.useEffect(()=>{if(!M)return;const t=e.map(e=>{let{sensor:t}=e;return null==t.setup?void 0:t.setup()});return()=>{for(const e of t)null==e||e()}},e.map(e=>{let{sensor:t}=e;return t}))}(h),P(()=>{oe&&C===Vt.Initializing&&N(Vt.Initialized)},[oe,C]),t.useEffect(()=>{const{onDragMove:e}=K.current,{active:t,activatorEvent:n,collisions:o,over:i}=le.current;if(!t||!n)return;const a={active:t,activatorEvent:n,collisions:o,delta:{x:Oe.x,y:Oe.y},over:i};r.unstable_batchedUpdates(()=>{null==e||e(a),y({type:\"onDragMove\",event:a})})},[Oe.x,Oe.y]),t.useEffect(()=>{const{active:e,activatorEvent:t,collisions:n,droppableContainers:o,scrollAdjustedTranslate:i}=le.current;if(!e||null==O.current||!t||!i)return;const{onDragOver:a}=K.current,s=o.get(Pe),l=s&&s.rect.current?{id:s.id,rect:s.rect.current,data:s.data,disabled:s.disabled}:null,c={active:e,activatorEvent:t,collisions:n,delta:{x:i.x,y:i.y},over:l};r.unstable_batchedUpdates(()=>{Ve(l),null==a||a(c),y({type:\"onDragOver\",event:c})})},[Pe]),P(()=>{le.current={activatorEvent:z,active:T,activeNode:Z,collisionRect:Be,collisions:ze,droppableRects:$,draggableNodes:j,draggingNode:he,draggingNodeRect:fe,droppableContainers:k,over:Fe,scrollableAncestors:xe,scrollAdjustedTranslate:Oe},I.current={initial:fe,translated:Be}},[T,Z,ze,Be,j,he,fe,$,k,Fe,xe,Oe]),gt({...te,delta:R,draggingRect:Be,pointerCoordinates:je,scrollableAncestors:xe,scrollableAncestorRects:Ce});const qe=t.useMemo(()=>({active:T,activeNode:Z,activeNodeRect:oe,activatorEvent:z,collisions:ze,containerNodeRect:ie,dragOverlay:de,draggableNodes:j,droppableContainers:k,droppableRects:$,over:Fe,measureDroppableContainers:G,scrollableAncestors:xe,scrollableAncestorRects:Ce,measuringConfiguration:H,measuringScheduled:Q,windowRect:be}),[T,Z,oe,z,ze,ie,de,j,k,$,Fe,G,xe,Ce,H,Q,be]),Je=t.useMemo(()=>({activatorEvent:z,activators:He,active:T,activeNodeRect:oe,ariaDescribedById:{draggable:X},dispatch:x,draggableNodes:j,over:Fe,measureDroppableContainers:G}),[z,He,T,oe,x,X,j,Fe,G]);return n.createElement(re.Provider,{value:w},n.createElement(Lt.Provider,{value:Je},n.createElement(Bt.Provider,{value:qe},n.createElement(Ut.Provider,{value:Xe},u)),n.createElement(Ft,{disabled:!1===(null==c?void 0:c.restoreFocus)})),n.createElement(ae,{...c,hiddenTextDescribedById:X}))}),Yt=t.createContext(null),Wt=\"button\";function Ht(e){let{id:n,data:r,disabled:o=!1,attributes:i}=e;const a=W(\"Draggable\"),{activators:s,activatorEvent:l,active:c,activeNodeRect:d,ariaDescribedById:u,draggableNodes:h,over:f}=t.useContext(Lt),{role:g=Wt,roleDescription:v=\"draggable\",tabIndex:p=0}=null!=i?i:{},m=(null==c?void 0:c.id)===n,b=t.useContext(m?Ut:Yt),[x,y]=K(),[w,C]=K(),N=function(e,n){return t.useMemo(()=>e.reduce((e,t)=>{let{eventName:r,handler:o}=t;return e[r]=e=>{o(e,n)},e},{}),[e,n])}(s,n),D=U(r);P(()=>(h.set(n,{id:n,key:a,node:x,activatorNode:w,data:D}),()=>{const e=h.get(n);e&&e.key===a&&h.delete(n)}),[h,n]);return{active:c,activatorEvent:l,activeNodeRect:d,attributes:t.useMemo(()=>({role:g,tabIndex:p,\"aria-disabled\":o,\"aria-pressed\":!(!m||g!==Wt)||void 0,\"aria-roledescription\":v,\"aria-describedby\":u.draggable}),[o,g,p,m,v,u.draggable]),isDragging:m,listeners:o?void 0:N,node:x,over:f,setNodeRef:y,setActivatorNodeRef:C,transform:b}}const qt={timeout:25};function Jt(e,t,n){const r=e.slice();return r.splice(n<0?r.length+n:n,0,r.splice(t,1)[0]),r}function $t(e,t){return e.reduce((e,n,r)=>{const o=t.get(n);return o&&(e[r]=o),e},Array(e.length))}function _t(e){return null!==e&&e>=0}const Gt=e=>{let{rects:t,activeIndex:n,overIndex:r,index:o}=e;const i=Jt(t,r,n),a=t[o],s=i[o];return s&&a?{x:s.left-a.left,y:s.top-a.top,scaleX:s.width/a.width,scaleY:s.height/a.height}:null},Qt=\"Sortable\",Zt=n.createContext({activeIndex:-1,containerId:Qt,disableTransforms:!1,items:[],overIndex:-1,useDragOverlay:!1,sortedRects:[],strategy:Gt,disabled:{draggable:!1,droppable:!1}});function en(e){let{children:r,id:o,items:i,strategy:a=Gt,disabled:s=!1}=e;const{active:l,dragOverlay:c,droppableRects:d,over:u,measureDroppableContainers:h}=t.useContext(Bt),f=W(Qt,o),g=Boolean(null!==c.rect),v=t.useMemo(()=>i.map(e=>\"object\"==typeof e&&\"id\"in e?e.id:e),[i]),p=null!=l,m=l?v.indexOf(l.id):-1,b=u?v.indexOf(u.id):-1,x=t.useRef(v),y=!function(e,t){if(e===t)return!0;if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0}(v,x.current),w=-1!==b&&-1===m||y,C=function(e){return\"boolean\"==typeof e?{draggable:e,droppable:e}:e}(s);P(()=>{y&&p&&h(v)},[y,v,p,h]),t.useEffect(()=>{x.current=v},[v]);const N=t.useMemo(()=>({activeIndex:m,containerId:f,disabled:C,disableTransforms:w,items:v,overIndex:b,useDragOverlay:g,sortedRects:$t(v,d),strategy:a}),[m,f,C.draggable,C.droppable,w,v,b,d,g,a]);return n.createElement(Zt.Provider,{value:N},r)}const tn=e=>{let{id:t,items:n,activeIndex:r,overIndex:o}=e;return Jt(n,r,o).indexOf(t)},nn=e=>{let{containerId:t,isSorting:n,wasDragging:r,index:o,items:i,newIndex:a,previousItems:s,previousContainerId:l,transition:c}=e;return!(!c||!r)&&((s===i||o!==a)&&(!!n||a!==o&&t===l))},rn={duration:200,easing:\"ease\"},on=\"transform\",an=G.Transition.toString({property:on,duration:0,easing:\"linear\"}),sn={roleDescription:\"sortable\"};function ln(e){let{animateLayoutChanges:n=nn,attributes:r,disabled:o,data:i,getNewIndex:a=tn,id:s,strategy:l,resizeObserverConfig:c,transition:d=rn}=e;const{items:u,containerId:h,activeIndex:f,disabled:g,disableTransforms:v,sortedRects:p,overIndex:m,useDragOverlay:b,strategy:x}=t.useContext(Zt),y=function(e,t){var n,r;if(\"boolean\"==typeof e)return{draggable:e,droppable:!1};return{draggable:null!=(n=null==e?void 0:e.draggable)?n:t.draggable,droppable:null!=(r=null==e?void 0:e.droppable)?r:t.droppable}}(o,g),w=u.indexOf(s),C=t.useMemo(()=>({sortable:{containerId:h,index:w,items:u},...i}),[h,i,w,u]),N=t.useMemo(()=>u.slice(u.indexOf(s)),[u,s]),{rect:D,node:E,isOver:j,setNodeRef:R}=function(e){let{data:n,disabled:r=!1,id:o,resizeObserverConfig:i}=e;const a=W(\"Droppable\"),{active:s,dispatch:l,over:c,measureDroppableContainers:d}=t.useContext(Lt),u=t.useRef({disabled:r}),h=t.useRef(!1),f=t.useRef(null),g=t.useRef(null),{disabled:v,updateMeasurementsFor:p,timeout:m}={...qt,...i},b=U(null!=p?p:o),x=wt({callback:t.useCallback(()=>{h.current?(null!=g.current&&clearTimeout(g.current),g.current=setTimeout(()=>{d(Array.isArray(b.current)?b.current:[b.current]),g.current=null},m)):h.current=!0},[m]),disabled:v||!s}),y=t.useCallback((e,t)=>{x&&(t&&(x.unobserve(t),h.current=!1),e&&x.observe(e))},[x]),[w,C]=K(y),N=U(n);return t.useEffect(()=>{x&&w.current&&(x.disconnect(),h.current=!1,x.observe(w.current))},[w,x]),t.useEffect(()=>(l({type:se.RegisterDroppable,element:{id:o,key:a,disabled:r,node:w,rect:f,data:N}}),()=>l({type:se.UnregisterDroppable,key:a,id:o})),[o]),t.useEffect(()=>{r!==u.current.disabled&&(l({type:se.SetDroppableDisabled,id:o,key:a,disabled:r}),u.current.disabled=r)},[o,a,r,l]),{active:s,rect:f,isOver:(null==c?void 0:c.id)===o,node:w,over:c,setNodeRef:C}}({id:s,data:C,disabled:y.droppable,resizeObserverConfig:{updateMeasurementsFor:N,...c}}),{active:k,activatorEvent:S,activeNodeRect:M,attributes:I,setNodeRef:T,listeners:A,isDragging:O,over:L,setActivatorNodeRef:B,transform:z}=Ht({id:s,data:C,attributes:{...sn,...r},disabled:y.draggable}),F=function(){for(var e=arguments.length,n=new Array(e),r=0;r<e;r++)n[r]=arguments[r];return t.useMemo(()=>e=>{n.forEach(t=>t(e))},n)}(R,T),V=Boolean(k),X=V&&!v&&_t(f)&&_t(m),Y=!b&&O,H=Y&&X?z:null,q=X?null!=H?H:(null!=l?l:x)({rects:p,activeNodeRect:M,activeIndex:f,overIndex:m,index:w}):null,J=_t(f)&&_t(m)?a({id:s,items:u,activeIndex:f,overIndex:m}):w,_=null==k?void 0:k.id,Q=t.useRef({activeId:_,items:u,newIndex:J,containerId:h}),Z=u!==Q.current.items,ee=n({active:k,containerId:h,isDragging:O,isSorting:V,id:s,index:w,items:u,newIndex:Q.current.newIndex,previousItems:Q.current.items,previousContainerId:Q.current.containerId,transition:d,wasDragging:null!=Q.current.activeId}),te=function(e){let{disabled:n,index:r,node:o,rect:i}=e;const[a,s]=t.useState(null),l=t.useRef(r);return P(()=>{if(!n&&r!==l.current&&o.current){const e=i.current;if(e){const t=Ee(o.current,{ignoreTransform:!0}),n={x:e.left-t.left,y:e.top-t.top,scaleX:e.width/t.width,scaleY:e.height/t.height};(n.x||n.y)&&s(n)}}r!==l.current&&(l.current=r)},[n,r,o,i]),t.useEffect(()=>{a&&s(null)},[a]),a}({disabled:!ee,index:w,node:E,rect:D});return t.useEffect(()=>{V&&Q.current.newIndex!==J&&(Q.current.newIndex=J),h!==Q.current.containerId&&(Q.current.containerId=h),u!==Q.current.items&&(Q.current.items=u)},[V,J,h,u]),t.useEffect(()=>{if(_===Q.current.activeId)return;if(null!=_&&null==Q.current.activeId)return void(Q.current.activeId=_);const e=setTimeout(()=>{Q.current.activeId=_},50);return()=>clearTimeout(e)},[_]),{active:k,activeIndex:f,attributes:I,data:C,rect:D,index:w,newIndex:J,items:u,isOver:j,isSorting:V,isDragging:O,listeners:A,node:E,overIndex:m,over:L,setNodeRef:F,setActivatorNodeRef:B,setDroppableNodeRef:R,setDraggableNodeRef:T,transform:null!=te?te:q,transition:function(){if(te||Z&&Q.current.newIndex===w)return an;if(Y&&!$(S)||!d)return;if(V||ee)return G.Transition.toString({...d,property:on});return}()}}function cn(e){if(!e)return!1;const t=e.data.current;return!!(t&&\"sortable\"in t&&\"object\"==typeof t.sortable&&\"containerId\"in t.sortable&&\"items\"in t.sortable&&\"index\"in t.sortable)}const dn=[qe.Down,qe.Right,qe.Up,qe.Left],un=(e,t)=>{let{context:{active:n,collisionRect:r,droppableRects:o,droppableContainers:i,over:a,scrollableAncestors:s}}=t;if(dn.includes(e.code)){if(e.preventDefault(),!n||!r)return;const t=[];i.getEnabled().forEach(n=>{if(!n||null!=n&&n.disabled)return;const i=o.get(n.id);if(i)switch(e.code){case qe.Down:r.top<i.top&&t.push(n);break;case qe.Up:r.top>i.top&&t.push(n);break;case qe.Left:r.left>i.left&&t.push(n);break;case qe.Right:r.left<i.left&&t.push(n)}});const l=(e=>{let{collisionRect:t,droppableRects:n,droppableContainers:r}=e;const o=ve(t),i=[];for(const a of r){const{id:e}=a,t=n.get(e);if(t){const n=ve(t),r=o.reduce((e,t,r)=>e+he(n[r],t),0),s=Number((r/4).toFixed(4));i.push({id:e,data:{droppableContainer:a,value:s}})}}return i.sort(fe)})({collisionRect:r,droppableRects:o,droppableContainers:t});let c=pe(l,\"id\");if(c===(null==a?void 0:a.id)&&l.length>1&&(c=l[1].id),null!=c){const e=i.get(n.id),t=i.get(c),a=t?o.get(t.id):null,l=null==t?void 0:t.node.current;if(l&&a&&e&&t){const n=Re(l).some((e,t)=>s[t]!==e),o=hn(e,t),i=function(e,t){if(!cn(e)||!cn(t))return!1;if(!hn(e,t))return!1;return e.data.current.sortable.index<t.data.current.sortable.index}(e,t),c=n||!o?{x:0,y:0}:{x:i?r.width-a.width:0,y:i?r.height-a.height:0},d={x:a.left,y:a.top};return c.x&&c.y?d:J(d,c)}}}};function hn(e,t){return!(!cn(e)||!cn(t))&&e.data.current.sortable.containerId===t.data.current.sortable.containerId}function fn({vault:e,editingId:t,editingName:n,setEditingName:r,startEdit:o,saveEdit:i,cancelEdit:a,handleDelete:s,onViewConfig:f,onNavigateToNotes:v,onNavigateToAttachments:p,formatBytes:m,t:b}){const{attributes:x,listeners:y,setNodeRef:C,transform:D,transition:M,isDragging:I}=ln({id:e.id}),T={transform:G.Transform.toString(D),transition:M,opacity:I?.5:1};return l.jsxs(\"article\",{ref:C,style:T,className:\"relative flex flex-col gap-4 rounded-xl border border-border bg-card p-5 transition-all duration-200 hover:shadow-md hover:border-primary/30 cursor-pointer\",onClick:()=>t!==e.id&&v&&v(e.vault),children:[l.jsxs(\"header\",{className:\"flex items-center gap-3\",children:[l.jsx(\"span\",{className:\"flex h-10 w-10 items-center justify-center rounded-xl bg-primary/5 text-primary shrink-0\",children:l.jsx(g,{className:\"h-5 w-5\"})}),t===e.id?l.jsx(\"div\",{className:\"flex items-center gap-2 flex-1\",onClick:e=>e.stopPropagation(),children:l.jsx(c,{value:n,onChange:e=>r(e.target.value),className:\"flex-1 rounded-xl\",autoFocus:!0,onKeyDown:t=>{\"Enter\"===t.key&&i(e),\"Escape\"===t.key&&a()}})}):l.jsxs(l.Fragment,{children:[l.jsx(\"h3\",{className:\"text-lg font-bold truncate flex-1\",children:e.vault}),l.jsx(\"button\",{...x,...y,className:\"p-2 rounded-xl text-muted-foreground/50 hover:text-muted-foreground hover:bg-muted/50 cursor-grab active:cursor-grabbing touch-none shrink-0\",onClick:e=>e.stopPropagation(),children:l.jsx(S,{className:\"h-5 w-5 md:h-4 md:w-4\"})})]})]}),l.jsxs(\"dl\",{className:\"grid grid-cols-2 gap-3\",children:[l.jsxs(\"div\",{className:\"flex flex-col rounded-lg border border-border/70 bg-background/80 p-3 hover:bg-primary/5 hover:border-primary/30 transition-colors group/stat\",onClick:n=>{t!==e.id&&v&&(n.stopPropagation(),v(e.vault))},children:[l.jsx(\"dt\",{className:\"text-xs text-muted-foreground group-hover/stat:text-primary transition-colors\",children:b(\"ui.vault.note\")}),l.jsxs(\"dd\",{className:\"text-xl font-semibold flex items-center justify-between gap-1\",children:[l.jsx(\"span\",{children:e.noteCount}),void 0!==e.noteSize&&l.jsx(\"span\",{className:\"text-[10px] font-normal text-muted-foreground/50\",children:m(e.noteSize)})]})]}),l.jsxs(\"div\",{className:\"flex flex-col rounded-lg border border-border/70 bg-background/80 p-3 hover:bg-primary/5 hover:border-primary/30 transition-colors group/stat\",onClick:n=>{t!==e.id&&p&&(n.stopPropagation(),p(e.vault))},children:[l.jsx(\"dt\",{className:\"text-xs text-muted-foreground group-hover/stat:text-primary transition-colors\",children:b(\"ui.vault.attachmentCount\")}),l.jsxs(\"dd\",{className:\"text-xl font-semibold flex items-center justify-between gap-1\",children:[l.jsx(\"span\",{children:e.fileCount||\"0\"}),void 0!==e.fileSize&&l.jsx(\"span\",{className:\"text-[10px] font-normal text-muted-foreground/50\",children:m(e.fileSize)})]})]})]}),l.jsxs(\"div\",{className:\"flex items-center justify-between text-xs text-muted-foreground\",children:[l.jsx(\"span\",{children:b(\"ui.vault.totalSize\",{size:m(e.size)})}),l.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[e.createdAt&&l.jsx(w,{content:b(\"ui.common.createdAt\"),side:\"top\",delay:300,children:l.jsxs(\"span\",{className:\"flex items-center gap-1\",children:[l.jsx(E,{className:\"h-3 w-3\"}),e.createdAt]})}),e.updatedAt&&l.jsx(w,{content:b(\"ui.common.updatedAt\"),side:\"top\",delay:300,children:l.jsxs(\"span\",{className:\"flex items-center gap-1\",children:[l.jsx(N,{className:\"h-3 w-3\"}),e.updatedAt]})})]})]}),l.jsx(\"div\",{className:\"flex items-center justify-between gap-1 pt-2 border-t border-border\",children:t===e.id?l.jsxs(\"div\",{className:\"flex items-center justify-end gap-1 w-full\",children:[l.jsx(w,{content:b(\"ui.common.save\"),side:\"top\",delay:200,children:l.jsx(u,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 rounded-xl text-green-600 hover:text-green-700 hover:bg-green-50\",onClick:t=>{t.stopPropagation(),i(e)},children:l.jsx(h,{className:\"h-4 w-4\"})})}),l.jsx(w,{content:b(\"ui.common.cancel\"),side:\"top\",delay:200,children:l.jsx(u,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 rounded-xl text-muted-foreground hover:text-foreground\",onClick:e=>{e.stopPropagation(),a()},children:l.jsx(d,{className:\"h-4 w-4\"})})})]}):l.jsxs(l.Fragment,{children:[l.jsx(w,{content:b(\"ui.vault.authTokenConfig\"),side:\"top\",delay:200,children:l.jsxs(u,{className:\"h-8 px-3 rounded-xl bg-sky-700 hover:bg-sky-900 text-white transition-colors border-none shadow-sm flex items-center gap-1.5\",onClick:t=>f(e.vault,t),children:[l.jsx(k,{className:\"h-4 w-4\"}),l.jsx(\"span\",{className:\"text-xs font-medium\",children:b(\"ui.vault.authTokenConfig\")})]})}),l.jsxs(\"div\",{className:\"flex items-center gap-1\",children:[l.jsx(w,{content:b(\"ui.vault.edit\"),side:\"top\",delay:200,children:l.jsx(u,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 rounded-xl text-muted-foreground hover:text-green-600\",onClick:t=>o(e,t),children:l.jsx(j,{className:\"h-4 w-4\"})})}),l.jsx(w,{content:b(\"ui.vault.delete\"),side:\"top\",delay:200,children:l.jsx(u,{variant:\"ghost\",size:\"icon\",className:\"h-8 w-8 rounded-xl text-muted-foreground hover:text-destructive\",onClick:t=>{t.stopPropagation(),s(e.id)},children:l.jsx(R,{className:\"h-4 w-4\"})})})]})]})})]})}const gn=\"vault-sort-order\";function vn({onNavigateToNotes:e,onNavigateToAttachments:n}){const{t:r}=o(),[E,j]=t.useState([]),[R,k]=t.useState(null),[S,M]=t.useState(\"\"),[I,T]=t.useState(!1),[A,O]=t.useState(\"\"),[L,B]=t.useState(\"\"),[z,P]=t.useState(!1),[F,U]=t.useState(!1),[V,K]=t.useState(\"\"),[X,Y]=t.useState(!1),{handleVaultList:W,handleVaultDelete:H,handleVaultUpdate:q}=f(),{openConfirmDialog:J}=i(),$=function(){for(var e=arguments.length,n=new Array(e),r=0;r<e;r++)n[r]=arguments[r];return t.useMemo(()=>[...n].filter(e=>null!=e),[...n])}(de(ot,{activationConstraint:{distance:8}}),de(ct,{activationConstraint:{delay:200,tolerance:5}}),de(Ze,{coordinateGetter:un})),_=t.useCallback(async(e=!1)=>{e&&Y(!0);try{await W(e=>{const t=localStorage.getItem(gn);if(t)try{const n=JSON.parse(t),r=[...e].sort((e,t)=>{const r=n.indexOf(e.id),o=n.indexOf(t.id);return-1===r?1:-1===o?-1:r-o});j(r)}catch{j(e)}else j(e)}),e&&a.success(r(\"ui.common.refreshSuccess\"))}catch(t){a.error(t instanceof Error?t.message:String(t))}finally{e&&Y(!1)}},[W,r]);t.useEffect(()=>{_()},[_]);const G=t.useMemo(()=>{if(!L.trim())return E;const e=L.toLowerCase();return E.filter(t=>t.vault.toLowerCase().includes(e))},[E,L]),Q=async e=>{J(r(\"ui.vault.confirmDelete\"),\"confirm\",async()=>{await H(e),j(E.filter(t=>t.id!==e))})},Z=(e,t)=>{t.stopPropagation(),k(e.id),M(e.vault)},ee=e=>{S.trim()?q({...e,vault:S.trim()},()=>{k(null),_()}):a.error(r(\"ui.vault.nameRequired\"))},te=()=>{k(null),M(\"\")},ne=()=>{A.trim()?q({vault:A.trim()},()=>{T(!1),O(\"\"),_()}):a.error(r(\"ui.vault.nameRequired\"))},re=e=>{if(null==e||\"\"===e)return\"0 B\";const t=\"string\"==typeof e?parseInt(e):e;if(isNaN(t)||0===t)return\"0 B\";if(t<1024)return`${t} B`;if(t<1048576)return`${(t/1024).toFixed(2)} KB`;return`${(t/1048576).toFixed(2)} MB`},oe=(e,t)=>{t.stopPropagation(),K(e),U(!1),P(!0)},ie=t.useCallback(e=>JSON.stringify({api:s.API_URL,apiToken:localStorage.getItem(\"token\")||\"\",...e?{vault:e}:{}},null,2),[]),ae=t.useCallback(e=>{const t=s.API_URL,n=localStorage.getItem(\"token\")||\"\",r=e||V;return`obsidian://fast-note-sync/sso?pushApi=${encodeURIComponent(t)}&pushApiToken=${encodeURIComponent(n)}&pushVault=${encodeURIComponent(r)}`},[V]);return l.jsxs(\"div\",{className:\"w-full space-y-4\",children:[l.jsxs(\"div\",{className:\"flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 py-1\",children:[l.jsx(\"div\",{className:\"flex items-center gap-3\",children:l.jsxs(\"span\",{className:\"text-sm text-muted-foreground\",children:[r(\"ui.vault.count\",{count:G.length}),L&&E.length!==G.length&&l.jsxs(\"span\",{className:\"ml-1\",children:[\"/ \",r(\"ui.vault.count\",{count:E.length})]})]})}),l.jsxs(\"div\",{className:\"flex items-center gap-2 w-full sm:w-auto\",children:[l.jsxs(\"div\",{className:\"relative flex-1 sm:w-64\",children:[l.jsx(C,{className:\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\"}),l.jsx(c,{type:\"text\",placeholder:r(\"ui.vault.searchPlaceholder\"),className:\"pl-9 pr-8 rounded-xl\",value:L,onChange:e=>B(e.target.value)}),L&&l.jsx(\"button\",{className:\"absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground\",onClick:()=>B(\"\"),children:l.jsx(d,{className:\"h-4 w-4\"})})]}),l.jsx(w,{content:r(\"ui.common.refresh\"),side:\"bottom\",delay:200,children:l.jsx(u,{variant:\"outline\",size:\"icon\",onClick:()=>_(!0),className:\"h-9 w-9 rounded-xl shrink-0\",disabled:X,children:l.jsx(N,{className:\"h-4 w-4 \"+(X?\"animate-spin\":\"\")})})}),l.jsxs(u,{onClick:()=>T(!0),className:\"rounded-xl shrink-0\",disabled:I,children:[l.jsx(D,{className:\"h-4 w-4 sm:mr-2\"}),l.jsx(\"span\",{className:\"hidden sm:inline\",children:r(\"ui.vault.add\")})]})]})]}),I&&l.jsx(\"div\",{className:\"rounded-xl border border-primary bg-card p-5\",children:l.jsxs(\"div\",{className:\"flex items-center gap-3\",children:[l.jsx(\"span\",{className:\"flex h-10 w-10 items-center justify-center rounded-xl bg-primary/5 text-primary shrink-0\",children:l.jsx(g,{className:\"h-5 w-5\"})}),l.jsx(c,{value:A,onChange:e=>O(e.target.value),placeholder:r(\"ui.vault.name\"),className:\"flex-1 rounded-xl\",autoFocus:!0,onKeyDown:e=>{\"Enter\"===e.key&&ne(),\"Escape\"===e.key&&(T(!1),O(\"\"))}}),l.jsx(u,{variant:\"ghost\",size:\"icon\",\"aria-label\":r(\"ui.common.save\"),className:\"h-8 w-8 rounded-xl text-green-600 hover:text-green-700 hover:bg-green-50\",onClick:ne,children:l.jsx(h,{className:\"h-4 w-4\"})}),l.jsx(u,{variant:\"ghost\",size:\"icon\",\"aria-label\":r(\"ui.common.cancel\"),className:\"h-8 w-8 rounded-xl text-muted-foreground hover:text-foreground\",onClick:()=>{T(!1),O(\"\")},children:l.jsx(d,{className:\"h-4 w-4\"})})]})}),0!==G.length||I?l.jsx(Xt,{sensors:$,collisionDetection:be,onDragEnd:e=>{const{active:t,over:n}=e;n&&t.id!==n.id&&j(e=>{const r=e.findIndex(e=>e.id===t.id),o=e.findIndex(e=>e.id===n.id),i=Jt(e,r,o),a=i.map(e=>e.id);return localStorage.setItem(gn,JSON.stringify(a)),i})},children:l.jsx(en,{items:G.map(e=>e.id),strategy:Gt,children:l.jsx(\"div\",{className:\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 p-1\",children:G.map(t=>l.jsx(fn,{vault:t,editingId:R,editingName:S,setEditingName:M,startEdit:Z,saveEdit:ee,cancelEdit:te,handleDelete:Q,onViewConfig:oe,onNavigateToNotes:e,onNavigateToAttachments:n,formatBytes:re,t:r},t.id))})})}):l.jsxs(\"div\",{className:\"rounded-xl border border-border bg-card p-12 text-center space-y-4\",children:[l.jsx(\"p\",{className:\"text-muted-foreground\",children:r(L?\"ui.common.noSearchResults\":\"ui.vault.noVaults\")}),!L&&l.jsx(\"div\",{className:\"flex flex-col items-center gap-4\",children:l.jsxs(u,{variant:\"outline\",className:\"w-full sm:w-auto rounded-xl bg-sky-700 hover:bg-sky-900 text-white hover:text-white transition-colors border-none shadow-sm\",onClick:e=>oe(\"\",e),children:[l.jsx(v,{className:\"h-4 w-4 mr-2\"}),r(\"ui.vault.oneClickImport\")]})})]}),l.jsx(p,{open:z,onOpenChange:P,children:l.jsxs(m,{className:\"w-[calc(100vw-2rem)] max-w-2xl mx-auto rounded-lg sm:rounded-xl\",children:[l.jsx(b,{children:l.jsxs(x,{className:\"text-base sm:text-lg truncate pr-8\",children:[r(F?\"ui.vault.copyConfigError\":\"ui.vault.authTokenConfig\"),V&&` - ${V}`]})}),l.jsxs(\"div\",{className:\"space-y-4\",children:[l.jsx(\"pre\",{className:\"p-3 sm:p-4 rounded-xl bg-muted text-xs sm:text-sm overflow-x-auto max-h-48 sm:max-h-64 font-mono whitespace-pre-wrap break-all\",children:ie(V)}),l.jsxs(\"div\",{className:\"flex flex-col-reverse sm:flex-row justify-end gap-2 text-nowrap\",children:[l.jsx(u,{variant:\"outline\",onClick:()=>P(!1),className:\"w-full sm:w-auto rounded-xl\",children:r(\"ui.common.close\")}),l.jsxs(u,{className:\"w-full sm:w-auto rounded-xl bg-sky-700 hover:bg-sky-900 text-white transition-colors border-none shadow-sm\",onClick:()=>{window.location.href=ae()},children:[l.jsx(y,{className:\"h-4 w-4 mr-2\"}),r(\"ui.vault.oneClickImport\")]}),l.jsxs(u,{onClick:()=>{const e=ie(V);navigator.clipboard?navigator.clipboard.writeText(e).then(()=>{a.success(r(\"ui.vault.copyConfigSuccess\"))}).catch(e=>{a.error(r(\"ui.common.error\")+e)}):a.error(r(\"ui.vault.copyConfigError\"))},className:\"w-full sm:w-auto rounded-xl\",children:[l.jsx(v,{className:\"h-4 w-4 mr-2\"}),r(\"ui.vault.copyConfig\")]})]})]})]})})]})}export{vn as VaultList};\n"
  },
  {
    "path": "frontend/assets/zap-CLLhzk_y.js",
    "content": "import{c as a}from\"./font-loader-CIrh3KnA.js\";\n/**\n * @license lucide-react v0.468.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */const o=a(\"Zap\",[[\"path\",{d:\"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z\",key:\"1xq2db\"}]]);export{o as Z};\n"
  },
  {
    "path": "frontend/assets/zh-CN-BZhLE8JW.js",
    "content": "const e={\"ui.common.title\":\"Fast Note Sync\",\"ui.common.subtitle\":\"高性能、低延迟的笔记同步, 管理, REST 服务\",\"ui.common.footerTitle\":\"基于 Golang + Websocket + Sqlite + React 构建\\n需要 <a href='https://github.com/haierkeys/obsidian-fast-note-sync' target='_blank'>Obsidian Fast Note Sync Plugin</a> 配合使用\",\"ui.common.loading\":\"加载中...\",\"ui.common.downloading\":\"下载中...\",\"ui.common.actions\":\"操作\",\"ui.common.save\":\"保存\",\"ui.common.add\":\"新增\",\"ui.common.edit\":\"编辑\",\"ui.common.view\":\"查看\",\"ui.common.viewDetail\":\"查看详情\",\"ui.common.delete\":\"删除\",\"ui.common.restore\":\"还原\",\"ui.common.permanentDelete\":\"永久删除\",\"ui.common.clear\":\"清空\",\"ui.common.batchPermanentDelete\":\"批量永久删除\",\"ui.common.batchPermanentDeleteConfirm\":\"确定要永久删除选中的 {{count}} 个项目吗？此操作不可恢复！\",\"ui.common.search\":\"搜索\",\"ui.common.refresh\":\"刷新\",\"ui.common.retry\":\"重试\",\"ui.common.refreshSuccess\":\"刷新成功\",\"ui.common.close\":\"关闭\",\"ui.common.cancel\":\"取消\",\"ui.common.confirm\":\"确定\",\"ui.common.success\":\"成功\",\"ui.common.error\":\"错误\",\"ui.common.warning\":\"警告\",\"ui.common.info\":\"信息\",\"ui.common.total\":\"总计\",\"ui.common.isEnabled\":\"启用\",\"ui.common.isDisabled\":\"已禁用\",\"ui.common.switchLanguage\":\"切换语言\",\"ui.common.comingSoon\":\"敬请期待...\",\"ui.common.comingSoonDescription\":\"此功能正在开发中，敬请期待...\",\"ui.common.helpAndSupport\":\"帮助与建议\",\"ui.common.githubIssue\":\"反馈\",\"ui.common.githubIssueDesc\":\"提交 Bug 或 功能建议\",\"ui.common.telegramGroup\":\"社群交流\",\"ui.common.telegramGroupDesc\":\"Telegram 社群讨论与获取帮助\",\"ui.common.planned\":\"待开发\",\"ui.common.unknown\":\"未知\",\"ui.common.copied\":\"已复制\",\"ui.common.restoring\":\"还原中...\",\"ui.common.rename\":\"重命名\",\"ui.common.toggleTheme\":\"切换主题\",\"ui.common.previous\":\"上一页\",\"ui.common.next\":\"下一页\",\"ui.common.page\":\"第\",\"ui.common.of\":\"共\",\"ui.common.to\":\"到\",\"ui.common.perPage\":\"每页\",\"ui.common.copy\":\"复制\",\"ui.common.createdAt\":\"创建时间\",\"ui.common.updatedAt\":\"更新时间\",\"ui.common.noSearchResults\":\"没有找到匹配的结果\",\"ui.common.selectAll\":\"全选\",\"ui.common.items\":\"条\",\"ui.common.count\":\"个\",\"ui.common.saveSuccess\":\"保存成功\",\"ui.common.selectVault\":\"选择笔记库\",\"ui.common.sourceCode\":\"源代码\",\"ui.common.wideMode\":\"宽屏模式\",\"ui.common.narrowMode\":\"正常宽度\",\"ui.common.fold\":\"收起\",\"ui.common.noChange\":\"内容未发生变化\",\"ui.common.na\":\"N/A\",\"ui.common.name\":\"名称\",\"ui.auth.login\":\"登录\",\"ui.auth.logout\":\"退出登录\",\"ui.auth.register\":\"注册\",\"ui.auth.registerButton\":\"注册\",\"ui.auth.registerClosed\":\"用户注册已关闭\",\"ui.auth.credentials\":\"用户名 ( 或 邮箱 )\",\"ui.auth.username\":\"用户名\",\"ui.auth.password\":\"密码\",\"ui.auth.remember\":\"记住我\",\"ui.auth.confirmPassword\":\"确认密码\",\"ui.auth.email\":\"邮箱\",\"ui.auth.credentialsPlaceholder\":\"请输入用户名 ( 或 邮箱 )\",\"ui.auth.usernamePlaceholder\":\"请输入用户名\",\"ui.auth.passwordPlaceholder\":\"请输入密码\",\"ui.auth.emailPlaceholder\":\"请输入邮箱\",\"ui.auth.confirmPasswordPlaceholder\":\"请确认密码\",\"ui.auth.credentialsRequired\":\"用户名（或邮箱）不能为空\",\"ui.auth.passwordMinLength\":\"密码至少需要6个字符\",\"ui.auth.usernameMinLength\":\"用户名至少3个字符\",\"ui.auth.emailInvalid\":\"请输入有效的邮箱地址\",\"ui.auth.passwordMismatch\":\"两次输入的密码不一致\",\"ui.auth.changePassword\":\"修改密码\",\"ui.auth.currentPassword\":\"当前密码\",\"ui.auth.newPassword\":\"新密码\",\"ui.auth.confirmNewPassword\":\"确认新密码\",\"ui.auth.passwordChangedSuccess\":\"密码修改成功\",\"ui.auth.passwordChangeFailed\":\"密码修改失败\",\"ui.auth.sessionExpired\":\"用户登录状态失效，请重新登录\",\"ui.auth.submitting\":\"提交中...\",\"ui.auth.unknownUser\":\"未知用户\",\"ui.auth.userUid\":\"用户 UID: {{uid}}\",\"ui.auth.loginRequestFailed\":\"登录请求失败，请检查网络状态\",\"ui.auth.registerRequestFailed\":\"注册失败，请重试\",\"ui.user.password\":\"访问密码\",\"ui.nav.navigation\":\"功能导航\",\"ui.nav.menuDashboard\":\"看板\",\"ui.nav.menuVaults\":\"笔记库\",\"ui.nav.menuNotes\":\"笔记管理\",\"ui.nav.menuTrash\":\"回收站\",\"ui.nav.menuSync\":\"备份与同步\",\"ui.nav.menuSettings\":\"系统设置\",\"ui.nav.menuGit\":\"Git 自动化\",\"ui.nav.menuFiles\":\"附件管理\",\"ui.nav.menuShares\":\"分享管理\",\"ui.nav.menuSettingsBrowser\":\"笔记库配置文件\",\"ui.nav.menuSyncLogs\":\"笔记库更新日志\",\"ui.nav.mainNavigation\":\"主导航\",\"ui.vault.vault\":\"笔记库\",\"ui.vault.title\":\"笔记库管理\",\"ui.vault.add\":\"新增笔记库\",\"ui.vault.edit\":\"编辑笔记库\",\"ui.vault.delete\":\"删除笔记库\",\"ui.vault.name\":\"笔记库名称\",\"ui.vault.nameRequired\":\"笔记库名称不能为空\",\"ui.vault.confirmDelete\":\"确定要删除这个笔记库吗？此操作将无法撤销！\",\"ui.vault.noVaults\":\"目前还没有任何笔记库\",\"ui.vault.count\":\"共 {{count}} 个笔记库\",\"ui.vault.searchPlaceholder\":\"搜索笔记库...\",\"ui.vault.note\":\"笔记\",\"ui.vault.attachmentCount\":\"附件\",\"ui.vault.totalSize\":\"总容量: {{size}}\",\"ui.vault.authTokenConfig\":\"授权客户端\",\"ui.vault.copyConfig\":\"复制连接配置\",\"ui.vault.copyConfigSuccess\":\"配置已复制到剪贴板\",\"ui.vault.copyConfigError\":\"复制失败，请手动选择复制\",\"ui.vault.oneClickImport\":\"一键授权 Obsidian\",\"ui.vault.pleaseCreateVault\":\"请新建笔记库 或 授权客户端到 Obsidian 自动创建\",\"ui.vault.createVaultFirst\":\"请先创建一个笔记库,然后再进行管理\",\"ui.vault.goToVaultManagement\":\"前往笔记库管理\",\"ui.vault.setAsDefault\":\"设为默认\",\"ui.note.note\":\"笔记\",\"ui.note.notes\":\"笔记列表\",\"ui.note.newNote\":\"新建笔记\",\"ui.note.noNotes\":\"暂无笔记\",\"ui.note.viewNote\":\"查看笔记\",\"ui.note.editNote\":\"编辑笔记\",\"ui.note.search\":\"搜索\",\"ui.note.searchPlaceholder\":\"搜索笔记...\",\"ui.note.noteTitlePlaceholder\":\"笔记标题 (例如: note.md)\",\"ui.note.noteContentPlaceholder\":\"请输入笔记内容...\",\"ui.note.noteCount\":\"笔记数量\",\"ui.note.noteTitleRequired\":\"标题不能为空\",\"ui.note.results\":\"条笔记\",\"ui.note.saving\":\"保存中...\",\"ui.note.lastSavedAt\":\"上次保存\",\"ui.note.renameNote\":\"重命名笔记\",\"ui.note.renameNotePlaceholder\":\"请输入新的笔记名称 (例如: new-note.md)\",\"ui.note.renameSuccess\":\"笔记重命名成功\",\"ui.note.deleteNoteConfirm\":'确定要删除笔记 \"{{title}}\" 吗？',\"ui.note.permanentDeleteConfirm\":'确定要永久删除笔记 \"{{title}}\" 吗？此操作无法撤销！',\"ui.note.restoreNoteConfirm\":'确定要恢复笔记 \"{{title}}\" 吗？',\"ui.note.clearRecycleConfirm\":\"确定要清空笔记回收站吗？此操作无法撤销！\",\"ui.note.noVaultsForNotes\":\"还没有任何笔记库\",\"ui.note.searchPath\":\"路径\",\"ui.note.searchContentMode\":\"内容\",\"ui.note.sortByBy\":\"排序\",\"ui.note.sortByMtime\":\"修改时间\",\"ui.note.sortByCtime\":\"创建时间\",\"ui.note.sortByPath\":\"路径\",\"ui.note.sortAsc\":\"升序\",\"ui.note.sortDesc\":\"降序\",\"ui.note.viewFlat\":\"平铺浏览\",\"ui.note.viewFolder\":\"目录浏览\",\"ui.note.editorHr\":\"分割线\",\"ui.note.exportPdfPlanned\":\"PDF 导出功能开发中...\",\"ui.note.undo\":\"撤销\",\"ui.note.redo\":\"重做\",\"ui.note.cut\":\"剪切\",\"ui.note.copy\":\"复制\",\"ui.note.paste\":\"粘贴\",\"ui.note.selectAll\":\"全选\",\"ui.note.fullscreen\":\"全屏\",\"ui.note.exitFullscreen\":\"退出全屏\",\"ui.note.contextMenu\":\"上下文菜单\",\"ui.note.loadingEditor\":\"加载编辑器...\",\"ui.note.unsavedContentWithoutTitle\":\"内容未保存，因为标题为空。\",\"ui.note.wikiLinkNotFound\":'未找到笔记 \"{{target}}\"',\"ui.history.title\":\"笔记历史\",\"ui.history.description\":\"查看和恢复笔记的历史版本\",\"ui.history.version\":\"版本\",\"ui.history.versionLabel\":\"版本\",\"ui.history.time\":\"更新时间\",\"ui.history.action\":\"操作\",\"ui.history.view\":\"查看\",\"ui.history.count\":\"共 {{count}} 条历史记录\",\"ui.history.loading\":\"加载中...\",\"ui.history.noHistory\":\"暂无历史记录\",\"ui.history.clientSource\":\"客户端\",\"ui.history.diffDetails\":\"版本 v{{version}} 差异详情\",\"ui.history.showDiffOnly\":\"只看差异\",\"ui.history.showOriginalContent\":\"修改前内容\",\"ui.history.diffLegendAdd\":\"新增\",\"ui.history.diffLegendDel\":\"删除\",\"ui.history.restore\":\"还原\",\"ui.history.restoreToVersion\":\"恢复到此版本\",\"ui.history.restoreVersionConfirmTitle\":\"确认恢复\",\"ui.history.restoreVersionConfirmDesc\":\"确定要将笔记恢复到版本 v{{version}} 吗？当前内容将被覆盖，但会自动保存为新的历史版本。\",\"ui.history.restoring\":\"恢复中...\",\"ui.file.file\":\"附件\",\"ui.file.files\":\"附件列表\",\"ui.file.noFiles\":\"暂无附件\",\"ui.file.searchFilePlaceholder\":\"搜索附件...\",\"ui.file.deleteFileConfirm\":'确定要删除附件 \"{{title}}\" 吗？',\"ui.file.restoreFileConfirm\":'确定要恢复附件 \"{{title}}\" 吗？',\"ui.file.permanentDeleteConfirm\":'确定要永久删除附件 \"{{title}}\" 吗？此操作无法撤销！',\"ui.file.clearRecycleConfirm\":\"确定要清空附件回收站吗？此操作无法撤销！\",\"ui.file.attachmentCount\":\"附件\",\"ui.file.results\":\"条附件\",\"ui.file.searchPlaceholder\":\"搜索附件...\",\"ui.file.totalSize\":\"共计 {{size}}\",\"ui.file.renameFile\":\"重命名附件\",\"ui.file.renameFilePlaceholder\":\"请输入新的附件名称\",\"ui.file.renameSuccess\":\"附件重命名成功\",\"ui.file.size\":\"占用空间\",\"ui.file.fileDetail\":\"附件详情\",\"ui.file.imagePreview\":\"图片预览\",\"ui.file.audioPreview\":\"音频播放\",\"ui.file.videoPreview\":\"视频播放\",\"ui.file.pdfPreview\":\"PDF 文档\",\"ui.file.codePreview\":\"脚本代码\",\"ui.file.unsupportedPreview\":\"该文件类型暂不支持直接预览\",\"ui.file.openInNewWindow\":\"在新窗口中打开\",\"ui.file.browserDownload\":\"浏览器下载\",\"ui.file.batchRestore\":\"批量恢复\",\"ui.file.batchPermanentDelete\":\"批量永久删除\",\"ui.file.selectedCount\":\"已选择 {{count}} 项\",\"ui.file.batchRestoreConfirm\":\"确定要恢复选中的 {{count}} 项吗？\",\"ui.file.batchPermanentDeleteConfirm\":\"确定要永久删除选中的 {{count}} 项吗？此操作无法撤销！\",\"ui.file.noVaultsForFiles\":\"还没有任何笔记库\",\"ui.settings.systemConfig\":\"分享短链\",\"ui.settings.securityConfig\":\"安全令牌\",\"ui.settings.noteRelatedConfig\":\"笔记附件\",\"ui.settings.fontConfig\":\"常规\",\"ui.settings.saveSettings\":\"保存设置\",\"ui.settings.saveSuccess\":\"设置保存成功\",\"ui.settings.saveFailed\":\"设置保存失败\",\"ui.settings.fontSet\":\"WebGui字体设置\",\"ui.settings.authTokenKey\":\"用户服务令牌加密混淆字符\",\"ui.settings.tokenExpiry\":\"用户服务令牌过期时间\",\"ui.settings.shareTokenKey\":\"分享令牌加密混淆字符\",\"ui.settings.shareTokenExpiry\":\"分享令牌过期时间\",\"ui.settings.registerIsEnable\":\"开放注册\",\"ui.settings.fileChunkSize\":\"附件上传分片大小\",\"ui.settings.softDeleteRetentionTime\":\"回收站(软删除)保留时间\",\"ui.settings.uploadSessionTimeout\":\"附件上传会话超时\",\"ui.settings.historyKeepVersions\":\"笔记历史记录保留版本数\",\"ui.settings.historySaveDelay\":\"笔记历史记录保存延迟\",\"ui.settings.historySaveDelayFormatError\":\"历史记录保存延迟格式无效\",\"ui.settings.adminUid\":\"系统设置访问限制 ( 用户 UID )\",\"ui.settings.adminUidDesc\":\"指定管理权限的 UID，0 表示所有登录用户均可管理，默认：0\",\"ui.settings.onlyAdminAccess\":\"只有管理员可以访问此页面\",\"ui.settings.fontSetDesc\":\"设置 WebGui 界面字体，留空则使用系统默认字体。\\n支持 <u>远程 CSS 字体样式表</u> 或 <u>预设 CSS 样式表关键字</u> 或 <u>字体网络地址</u> 以及 <u>本地字体</u>（ 放在 storage/user_static/ 目录下 ）默认：空。\\n\\n示例: \\n&nbsp;&nbsp;- 远程 CSS 字体样式表: <b>https://fonts.googleapis.com/css2?family=Noto+Sans+SC&display=swap</b>\\n&nbsp;&nbsp;- 预设 CSS 样式表关键字: <b>local</b> 或 <b>remote</b>\\n&nbsp;&nbsp;- 本地预设 CSS 字体样式表: <b>/static/fonts/font.css</b>\\n&nbsp;&nbsp;- 字体网络地址: <b>https://ik.imagekit.io/name/your-font.woff2</b>\\n&nbsp;&nbsp;- 本地字体: <b>/user_static/your-font.woff2</b>, 需要将字体文件 <u>your-font.woff2</u> 放在 storage/user_static/ 目录下\",\"ui.settings.authTokenKeyDesc\":\"本设置项参与所有用户的服务令牌生成过程，首次部署之后为了服务端的安全性，<u><b>强烈建议修改本项</b></u>，一旦修改原有令牌立刻失效，WebGui 也需要重新登录。\",\"ui.settings.tokenExpiryDesc\":\"用户服务令牌的过期时间，支持格式：7d（天）、24h（小时）、30m（分钟），默认：<b>365d</b>\",\"ui.settings.shareTokenKeyDesc\":\"用于生成分享令牌的加密混淆 Key，修改后原有分享链接将失效，默认：<b>fns</b>\",\"ui.settings.shareTokenExpiryDesc\":\"分享令牌的过期时间，支持格式：7d（天）、24h（小时）、30m（分钟），默认：<b>30d</b>\",\"ui.settings.registerIsEnableDesc\":\"是否允许新用户在此服务上创建账户，默认：<b>开启</b>\",\"ui.settings.fileChunkSizeDesc\":\"上传时分块大小（如 1MB, 512KB），建议 512KB-2MB，默认：<b>512KB</b>\",\"ui.settings.softDeleteRetentionTimeDesc\":\"删除笔记和附件后的保留时长（如 30d, 24h），0 为不自动清理，本设置过短会导致离线设备无法同步删除操作，请谨慎设置，默认：7d\",\"ui.settings.uploadSessionTimeoutDesc\":\"文件分块上传会话的有效期（如 1h, 30m），默认：<b>1d</b>\",\"ui.settings.historyKeepVersionsDesc\":\"每个笔记保留的历史版本数量，超过此数量时自动删除最旧的版本，最小值：100，默认：<b>100</b>\",\"ui.settings.historySaveDelayDesc\":\"历史记录保存的延迟时间，用于防止频繁编辑产生过多历史版本（如 10s, 1m），默认：<b>10s</b>\",\"ui.settings.toastPosition\":\"通知显示位置\",\"ui.settings.position.top-left\":\"左上角\",\"ui.settings.position.top-center\":\"顶部居中\",\"ui.settings.position.top-right\":\"右上角\",\"ui.settings.position.bottom-left\":\"左下角\",\"ui.settings.position.bottom-center\":\"底部居中\",\"ui.settings.position.bottom-right\":\"右下角\",\"ui.settings.colorScheme\":\"配色方案\",\"ui.settings.colorSchemeSwitched\":\"已切换到 {{scheme}} 配色\",\"ui.settings.colorScheme.default\":\"标准\",\"ui.settings.colorScheme.green\":\"绿色\",\"ui.settings.colorScheme.blue\":\"蓝色\",\"ui.settings.colorScheme.skyBlue\":\"天蓝\",\"ui.settings.colorScheme.purple\":\"紫色\",\"ui.settings.colorScheme.orange\":\"橙色\",\"ui.settings.colorScheme.rose\":\"玫瑰\",\"ui.settings.colorScheme.teal\":\"青色\",\"ui.settings.themeAuto\":\"自动 (18:00-06:00 暗色)\",\"ui.settings.themeLight\":\"浅色\",\"ui.settings.themeDark\":\"深色\",\"ui.settings.cloudflaredTestRequired\":\"请先点击 隧道程序下载 按钮\",\"ui.settings.downloadSuccess\":\"下载成功\",\"ui.settings.downloadFailed\":\"下载失败\",\"ui.settings.tunnelGatewayConfig\":\"中继网关\",\"ui.settings.tunnelGatewayDesc\":\"<b>FNS</b> 集成的内网穿透服务，使您的本地 <b>FNS</b> 服务可以直接通过公网安全访问, 不用再配置繁琐的 HTTPS 和 WebSocket 代理, 就可以享受安全访问服务。\\n支持通过 Ngrok 或 Cloudflare 隧道接入。\",\"ui.settings.ngrokDesc\":'使用 Ngrok 内网穿透隧道，将本地服务安全地暴露到公网。\\n官方申请入口: <a href=\"https://dashboard.ngrok.com/get-started/setup\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary hover:underline\">https://dashboard.ngrok.com/get-started/setup</a>\\n <b>注意</b>: Ngrok 免费账户有各维度限制, 具体请查阅 <a href=\"https://ngrok.com/docs/pricing-limits/free-plan-limits\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary hover:underline\">https://ngrok.com/docs/pricing-limits/free-plan-limits</a>',\"ui.settings.customDomain\":\"自定义域名\",\"ui.settings.customDomainDesc\":\"可选, 需要 ngrok 付费账户\",\"ui.settings.saveNgrok\":\"保存设置\",\"ui.settings.cloudflareDesc\":'基于 Cloudflare 零信任网络构建的反向隧道，提供更高安全性和稳定性。\\n官方申请入口: <a href=\"https://one.dash.cloudflare.com\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary hover:underline\">https://one.dash.cloudflare.com</a>\\n在保存设置前 , 需要您先进行 <b>隧道程序下载</b>',\"ui.settings.enableLog\":\"记录日志\",\"ui.settings.cloudflareLogDesc\":\"日志记录在 storage/logs/cloudflared_tunnel.log\",\"ui.settings.saveCloudflare\":\"保存设置\",\"ui.settings.downloadCloudflared\":\"隧道程序下载\",\"ui.settings.pullSource\":\"版本检测源\",\"ui.settings.pullSourceDesc\":\"选择版本检测&更新时的来源仓库。\",\"ui.settings.pullSource.auto\":\"自动检测\",\"ui.settings.pullSource.github\":\"github.com\",\"ui.settings.pullSource.cnb\":\"腾讯 cnb.cool\",\"ui.settings.userDatabaseConfig\":\"数据库增强\",\"ui.settings.userDatabaseDesc\":\"默认 <b>SQLite</b>（免维护，支持并发读/单写），适合个人及小规模部署，能满足个人用户大部分需求。<br/><div class='mt-2 pt-2 border-t border-border/50 opacity-90'><div class='mb-2 text-amber-500 font-medium'>建议改用专业数据库 (PostgreSQL / MySQL) 的场景：</div><ul class='list-disc list-inside space-y-1 mb-1'><li>多用户、多个笔记库且规模极大</li><li>笔记数量或附件体积庞大</li><li>经常遇到并发相关错误</li></ul><div>专业数据库在并发与大规模数据处理上更可靠，但会提高硬件要求与运维成本（需具备运维能力）。</div><div class='mt-2 pt-2 border-t border-border/50 text-xs text-rose-500 font-medium italic'>注意：当修改数据库之后，原数据不会迁移到新数据库，您需要重新强制同步笔记库；设置必须先通过连接测试才能更改数据库类型。</div></div>\",\"ui.settings.databaseType\":\"数据库类型\",\"ui.settings.databaseType.sqlite\":\"SQLite (内嵌)\",\"ui.settings.databaseType.mysql\":\"MySQL\",\"ui.settings.databaseType.postgres\":\"PostgreSQL\",\"ui.settings.databaseHost\":\"数据库地址 (Host)\",\"ui.settings.databasePort\":\"端口 (Port)\",\"ui.settings.databaseUser\":\"用户名 (User)\",\"ui.settings.databasePassword\":\"密码 (Password)\",\"ui.settings.databaseName\":\"数据库名 (Database)\",\"ui.settings.databaseMaxIdleConns\":\"最大空闲连接数 (MaxIdle)\",\"ui.settings.databaseMaxOpenConns\":\"最大打开连接数 (MaxOpen)\",\"ui.settings.databaseConnMaxLifetime\":\"连接最大存活时间 (MaxLifetime)\",\"ui.settings.databaseConnMaxIdleTime\":\"连接最大空闲时间 (MaxIdleTime)\",\"ui.settings.databaseMaxWriteConcurrency\":\"最大写入并发数 (MaxWriteConcurrency)\",\"ui.settings.databaseSchema\":\"数据库 Schema (Postgres)\",\"ui.settings.databaseSslMode\":\"SSL 模式 (SSL Mode)\",\"ui.settings.mysqlPermissionWarning\":\"注意：当使用 MySQL 时，提供的账号必须具有创建数据库 (CREATE DATABASE) 的权限，用于用户信息隔离。\",\"ui.settings.postgresPermissionWarning\":\"注意：当使用 PostgreSQL 时，提供的账号必须具有创建数据库 (CREATE DATABASE) 的权限，用于用户信息隔离。\",\"ui.settings.testConnection\":\"连接测试\",\"ui.settings.testSuccess\":\"连接测试成功\",\"ui.settings.testFailed\":\"连接测试失败\",\"ui.settings.testRequiredBeforeSave\":\"请先进行连接测试并确保成功后再保存\",\"ui.settingsBrowser.title\":\"笔记库配置文件\",\"ui.settingsBrowser.description\":\"管理笔记库的配置信息（如 笔记库配置、插件、主题等）\",\"ui.settingsBrowser.add\":\"新增配置\",\"ui.settingsBrowser.edit\":\"编辑配置\",\"ui.settingsBrowser.key\":\"路径\",\"ui.settingsBrowser.value\":\"大小\",\"ui.settingsBrowser.content\":\"内容\",\"ui.settingsBrowser.keyRequired\":\"路径不能为空\",\"ui.settingsBrowser.confirmDelete\":'确定要删除配置项 \"{{key}}\" 吗？',\"ui.settingsBrowser.rename\":\"重命名路径\",\"ui.settingsBrowser.newKey\":\"新路径\",\"ui.settingsBrowser.renameSuccess\":\"配置项重命名成功\",\"ui.settingsBrowser.noSettings\":\"暂无配置项\",\"ui.obsidian.authTokenConfig\":\"授权客户端\",\"ui.obsidian.authTokenConfigTo\":\"一键授权到 Obsidian\",\"ui.obsidian.oneClickImport\":\"一键授权到 Obsidian\",\"ui.obsidian.copyConfigSuccess\":\"授权信息已复制, 请返回 Obsidian 插件粘贴配置\",\"ui.obsidian.copyConfigError\":\"非 HTTPS 页面，无法使用剪贴板功能，请手动复制授权配置\",\"ui.system.serviceInfo\":\"服务信息\",\"ui.system.versionInfo\":\"版本信息\",\"ui.system.repo\":\"项目仓库\",\"ui.system.githubRepo\":\"GitHub 仓库\",\"ui.system.cnbMirror\":\"CNB 镜像库\",\"ui.system.currentVersion\":\"当前版本\",\"ui.system.checkUpdate\":\"检查更新\",\"ui.system.checkNow\":\"立即检查\",\"ui.system.checking\":\"检查中...\",\"ui.system.newVersionAvailable\":\"发现新版本\",\"ui.system.alreadyLatest\":\"已是最新版本\",\"ui.system.viewRelease\":\"查看发布页\",\"ui.system.upgradeNow\":\"立即升级\",\"ui.system.upgrading\":\"升级中...\",\"ui.system.upgradeSuccess\":\"升级触发成功，页面即将刷新\",\"ui.system.upgradeFailed\":\"升级触发失败\",\"ui.system.upgradeRefreshTimeout\":\"升级已完成，但服务尚未恢复可用，请手动刷新页面。\",\"ui.system.viewChangelog\":\"查看更新日志\",\"ui.system.getVersionError\":\"获取版本信息失败\",\"ui.system.getWebGuiConfigError\":\"获取WebGui配置失败:\",\"ui.system.restartService\":\"重启服务\",\"ui.system.restartServiceConfirm\":\"确定要立即重启服务吗？重启过程中连接将会断开。\",\"ui.system.manualGC\":\"内存回收\",\"ui.system.manualGCConfirm\":\"确定要立即执行内存回收 (GC) 吗？\",\"ui.system.manualGCSuccess\":\"手动内存回收 (GC) 触发成功\",\"ui.system.serverSystemInfo\":\"服务器信息\",\"ui.system.modelName\":\"处理器型号\",\"ui.system.hostInfo\":\"系统信息\",\"ui.system.systemTime\":\"系统时间\",\"ui.system.runtimeInfo\":\"服务信息\",\"ui.system.startTime\":\"启动时间\",\"ui.system.serviceUptime\":\"运行时间\",\"ui.system.physicalCores\":\"核心 (逻辑/物理)\",\"ui.system.cpuLoad\":\"平均负载 (1/5/15)\",\"ui.system.totalMemory\":\"总内存\",\"ui.system.usedMemory\":\"已用内存\",\"ui.system.memoryUsage\":\"内存使用率\",\"ui.system.os\":\"操作系统\",\"ui.system.kernelVersion\":\"内核版本\",\"ui.system.uptime\":\"运行时间\",\"ui.system.goVersion\":\"运行时版本\",\"ui.system.goroutines\":\"协程数量\",\"ui.system.heapMemory\":\"堆内存 / 总占用\",\"ui.system.numGc\":\"GC 次数\",\"ui.system.createdAt\":\"创建时间\",\"ui.system.updatedAt\":\"更新时间\",\"ui.system.websocketClients\":\"在线客户端\",\"ui.system.wsNickname\":\"昵称\",\"ui.system.wsClientName\":\"客户端\",\"ui.system.wsClientType\":\"类型\",\"ui.system.wsRemoteAddr\":\"地址\",\"ui.system.wsStartTime\":\"连接时间\",\"ui.system.wsTraceId\":\"追踪 ID\",\"ui.system.wsNoClients\":\"暂无在线客户端\",\"ui.storage.management\":\"存储配置\",\"ui.storage.add\":\"新增存储\",\"ui.storage.edit\":\"编辑存储\",\"ui.storage.noStorage\":\"暂无存储配置\",\"ui.storage.type\":\"类型\",\"ui.storage.storageType.oss\":\"阿里云 OSS\",\"ui.storage.storageType.s3\":\"AWS S3\",\"ui.storage.storageType.r2\":\"Cloudflare R2\",\"ui.storage.storageType.minio\":\"MinIO\",\"ui.storage.storageType.localfs\":\"本地存储\",\"ui.storage.storageType.webdav\":\"WebDAV\",\"ui.storage.selectType\":\"选择存储类型\",\"ui.storage.endpoint\":\"终端节点 ( Endpoint )\",\"ui.storage.region\":\"区域 ( S3 Region )\",\"ui.storage.accountId\":\"账户ID ( R2 Account ID )\",\"ui.storage.bucketName\":\"存储桶\",\"ui.storage.accessKeyId\":\"密钥 ID ( AccessKey ID )\",\"ui.storage.accessKeySecret\":\"密钥 ( AccessKey Secret )\",\"ui.storage.webdavUrl\":\"WebDAV 地址\",\"ui.storage.webdavUser\":\"用户名\",\"ui.storage.webdavPassword\":\"密码\",\"ui.storage.customPath\":\"自定义路径\",\"ui.storage.accessUrlPrefix\":\"访问 URL 前缀\",\"ui.storage.placeholder.endpoint.oss\":\"oss-cn-hangzhou.aliyuncs.com\",\"ui.storage.placeholder.endpoint.minio\":\"http://192.168.1.100:9000\",\"ui.storage.placeholder.region\":\"us-east-1\",\"ui.storage.placeholder.accountId\":\"your-account-id\",\"ui.storage.placeholder.bucketName\":\"my-bucket\",\"ui.storage.placeholder.accessKeyId\":\"\",\"ui.storage.placeholder.accessKeySecret\":\"\",\"ui.storage.placeholder.webdavUrl\":\"http://192.168.1.100:5244/dav\",\"ui.storage.placeholder.webdavUser\":\"admin\",\"ui.storage.placeholder.webdavPassword\":\"\",\"ui.storage.placeholder.customPath\":\"data/obsidian\",\"ui.storage.placeholder.accessUrlPrefix\":\"http://192.168.1.100:5244\",\"ui.storage.help.endpoint.oss\":\"阿里云 OSS 的 Endpoint 地址，不含 Bucket 名称\",\"ui.storage.help.endpoint.minio\":\"MinIO 服务地址，需包含协议前缀和端口\",\"ui.storage.help.region\":\"S3 存储桶所在区域\",\"ui.storage.help.accountId\":\"Cloudflare 账户 ID，可在控制台右侧栏找到\",\"ui.storage.help.webdavUrl\":\"需包含协议前缀 (http:// 或 https://) 和完整路径\",\"ui.storage.help.webdavUser\":\"WebDAV 服务的登录用户名\",\"ui.storage.help.webdavPassword\":\"WebDAV 服务的登录密码\",\"ui.storage.help.customPath\":\"文件存放的子目录路径，无需首尾斜杠\",\"ui.storage.help.accessUrlPrefix\":\"用于生成文件访问链接的前缀地址，仅展示用途\",\"ui.storage.confirmDelete\":\"确定要删除这个存储配置吗？\",\"ui.storage.validate.title\":\"测试连接\",\"ui.storage.validate.loading\":\"测试中...\",\"ui.backup.management\":\"任务管理\",\"ui.backup.add\":\"新增任务\",\"ui.backup.edit\":\"编辑任务\",\"ui.backup.noBackup\":\"暂无备份与同步任务\",\"ui.backup.selectType\":\"请选择备份类型\",\"ui.backup.confirmDelete\":\"确定要删除这个备份任务吗？\",\"ui.backup.vault\":\"笔记库\",\"ui.backup.selectVault\":\"选择笔记库\",\"ui.backup.type\":\"备份类型\",\"ui.backup.backupType.full\":\"全量备份\",\"ui.backup.backupType.incremental\":\"增量备份\",\"ui.backup.backupType.sync\":\"镜像同步\",\"ui.backup.cronStrategy\":\"定时策略\",\"ui.backup.strategy.daily\":\"每日备份 (00:00)\",\"ui.backup.strategy.weekly\":\"每周备份 (周一 00:00)\",\"ui.backup.strategy.monthly\":\"每月备份 (1号 00:00)\",\"ui.backup.strategy.custom\":\"自定义 Cron\",\"ui.backup.cronExpression\":\"Cron 表达式\",\"ui.backup.retentionDays\":\"保留天数\",\"ui.backup.fileCountUnit\":\"个文件\",\"ui.backup.retentionDays.sync\":\"历史保留天数(0:不清理历史, -1:不保留历史记录)\",\"ui.backup.retentionDays.backup\":\"备份包保留天数(0:不清理备份, -1:不保留历史备份)\",\"ui.backup.includeVaultName.label\":\"包含笔记库名\",\"ui.backup.includeVaultName.tooltip\":\"关闭: {customPath}/notes/xxx.md\\n开启: {customPath}/{vaultName}/notes/xxx.md\",\"ui.backup.noAvailableStorage\":\"暂无可用存储配置\",\"ui.backup.addStorageTip\":\"请先在存储管理中添加并启用存储后端\",\"ui.backup.storages\":\"存储选择\",\"ui.backup.validation.vaultRequired\":\"请选择笔记库\",\"ui.backup.validation.typeRequired\":\"请选择备份类型\",\"ui.backup.validation.strategyRequired\":\"请选择定时策略\",\"ui.backup.validation.storageRequired\":\"请至少选择一个存储\",\"ui.backup.validation.retentionDaysMin\":\"保留天数须 ≥ -1\",\"ui.backup.validation.cronExpressionRequired\":\"自定义策略下 Cron 表达式不能为空\",\"ui.backup.lastRunTime\":\"上次运行\",\"ui.backup.nextRunTime\":\"下次运行\",\"ui.backup.executeNow\":\"立即执行\",\"ui.backup.executeSuccess\":\"手动触发备份成功\",\"ui.backup.status.0\":\"等待中\",\"ui.backup.status.1\":\"进行中\",\"ui.backup.status.2\":\"成功\",\"ui.backup.status.3\":\"失败\",\"ui.backup.status.4\":\"取消\",\"ui.backup.status.5\":\"无更新\",\"ui.backup.history.title\":\"任务记录\",\"ui.backup.history.startTime\":\"任务执行时间\",\"ui.backup.history.storage\":\"存储\",\"ui.backup.history.status\":\"状态\",\"ui.backup.history.backupStats\":\"备份统计\",\"ui.backup.history.syncStats\":\"同步统计\",\"ui.backup.history.backupFile\":\"备份文件\",\"ui.backup.history.message\":\"消息\",\"ui.backup.history.copyError\":\"复制错误信息\",\"ui.backup.history.noData\":\"暂无任务记录\",\"ui.git.title\":\"Git 自动化管理\",\"ui.git.config\":\"Git 仓库配置\",\"ui.git.repoUrl\":\"Git 仓库地址 (不支持 ssh 地址)\",\"ui.git.status\":\"Git 状态概览\",\"ui.git.history\":\"自动化提交记录\",\"ui.git.comingSoon\":\"Git 自动化功能开发中...\",\"ui.git.configDesc\":\"配置您的 Git 仓库地址、分支以及身份验证信息。\",\"ui.git.statusDesc\":\"实时监控当前仓库的提交状态与同步进度。\",\"ui.git.historyDesc\":\"查看最近的自动提交记录。\",\"ui.git.retentionDays\":\"历史保留天数\",\"ui.git.retentionDaysDesc\":\"0: 不清理历史，-1: 不保留历史记录\",\"ui.git.addConfig\":\"新增 Git 同步配置\",\"ui.git.editConfig\":\"编辑 Git 同步配置\",\"ui.git.loading\":\"正在加载配置...\",\"ui.git.noConfig\":\"暂无 Git 同步配置\",\"ui.git.addFirst\":\"立即添加第一个仓库\",\"ui.git.lastCommit\":\"上次提交\",\"ui.git.checkTime\":\"上次检测\",\"ui.git.neverRun\":\"从未执行\",\"ui.git.execute.title\":\"立即触发同步\",\"ui.git.clean.title\":\"清理工作区\",\"ui.git.clean.confirm\":\"确定要清理此 Git 工作区吗？这将删除本地克隆并重新初始化。\",\"ui.git.delete.confirm\":\"确定要删除此 Git 配置吗？此操作不可撤销。\",\"ui.git.form.branch\":\"分支名称\",\"ui.git.form.delay\":\"自动同步延迟（秒）\",\"ui.git.history.title\":\"提交记录\",\"ui.git.history.configId\":\"配置 ID\",\"ui.git.history.startTime\":\"提交时间\",\"ui.git.history.endTime\":\"结束时间\",\"ui.git.history.status\":\"状态\",\"ui.git.history.message\":\"消息\",\"ui.git.history.noData\":\"暂无提交记录\",\"ui.git.history.duration\":\"提交耗时\",\"ui.git.history.vault\":\"笔记库\",\"ui.git.status.0\":\"待机\",\"ui.git.status.1\":\"检测中\",\"ui.git.status.2\":\"成功\",\"ui.git.status.3\":\"失败\",\"ui.git.status.4\":\"已停止\",\"ui.git.validate.title\":\"检测连接\",\"ui.git.validate.loading\":\"检测中...\",\"ui.validation.git.vaultRequired\":\"请选择笔记本\",\"ui.validation.git.repoUrlRequired\":\"请输入仓库地址\",\"ui.validation.git.repoUrlInvalid\":\"请输入有效的 URL\",\"ui.validation.git.branchRequired\":\"请输入分支名称\",\"ui.validation.git.delayMin\":\"延迟时间不能为负数\",\"ui.validation.git.retentionDaysMin\":\"保留天数须 ≥ -1\",\"ui.validation.storage.typeRequired\":\"请选择存储类型\",\"ui.validation.storage.accessUrlPrefixRequired\":\"访问地址前缀不能为空\",\"ui.validation.vault.nameRequired\":\"仓库名称不能为空\",\"api.git.list.error\":\"获取 Git 配置列表失败\",\"api.git.save.success\":\"保存配置成功\",\"api.git.save.error\":\"保存配置失败\",\"api.git.delete.success\":\"删除配置成功\",\"api.git.delete.error\":\"删除配置失败\",\"api.git.execute.success\":\"已触发同步任务\",\"api.git.execute.error\":\"触发同步失败\",\"api.git.clean.success\":\"清理工作区成功\",\"api.git.clean.error\":\"清理工作区失败\",\"api.git.history.error\":\"获取历史记录失败\",\"api.git.validate.success\":\"Git 仓库连接测试成功\",\"api.git.validate.error\":\"Git 仓库连接测试失败\",\"api.backup.configList.error\":\"获取备份配置列表失败\",\"api.backup.delete.success\":\"删除成功\",\"api.backup.delete.error\":\"删除备份配置失败\",\"api.backup.save.success\":\"保存成功\",\"api.backup.save.error\":\"保存备份配置失败\",\"api.backup.execute.success\":\"手动触发成功\",\"api.backup.execute.error\":\"触发备份失败\",\"api.backup.history.error\":\"获取备份历史失败\",\"api.system.restart.success\":\"服务重启已触发\",\"api.system.restart.error\":\"重启服务请求失败\",\"api.system.gc.success\":\"内存回收已触发\",\"api.system.gc.error\":\"内存回收请求失败\",\"api.storage.list.error\":\"获取存储配置列表失败\",\"api.storage.delete.success\":\"删除成功\",\"api.storage.delete.error\":\"删除存储配置失败\",\"api.storage.save.error\":\"保存存储配置失败\",\"api.storage.types.error\":\"获取存储类型失败\",\"api.storage.validate.success\":\"存储连接测试成功\",\"api.storage.validate.error\":\"存储连接测试失败\",\"error.storage.webdav.unauthorized\":\"WebDAV 认证失败，请检查用户名和密码\",\"error.storage.webdav.forbidden\":\"WebDAV 权限不足，请检查账户写入权限\",\"error.storage.webdav.notFound\":\"WebDAV 路径不存在，请检查自定义路径配置\",\"error.storage.webdav.methodNotAllowed\":\"WebDAV 方法不被允许，请确认服务端已启用 WebDAV\",\"error.storage.webdav.unreachable\":\"WebDAV 地址无法访问，请检查存储配置中的地址\",\"error.storage.webdav.connectionRefused\":\"WebDAV 连接被拒绝，请确认服务是否运行\",\"error.storage.webdav.timeout\":\"WebDAV 连接超时，请检查网络或服务状态\",\"error.storage.webdav.generic\":\"WebDAV 操作失败，请检查存储配置\",\"error.storage.s3.noSuchBucket\":\"S3 存储桶不存在，请检查桶名配置\",\"error.storage.s3.accessDenied\":\"S3 访问被拒绝，请检查 Access Key 权限\",\"error.storage.s3.unreachable\":\"S3 端点无法访问，请检查 Endpoint 地址\",\"error.storage.s3.timeout\":\"S3 连接超时，请检查网络或端点配置\",\"error.storage.oss.noSuchBucket\":\"OSS 存储桶不存在，请检查 Bucket 名称\",\"error.storage.oss.accessDenied\":\"OSS 访问被拒绝，请检查 AccessKey 权限\",\"error.storage.oss.unreachable\":\"OSS 端点无法访问，请检查 Endpoint 地址\",\"error.storage.local.noPermission\":\"本地存储无写入权限\",\"error.storage.local.createDirFailed\":\"本地存储目录创建失败\",\"error.storage.local.permissionDenied\":\"本地存储权限被拒绝\",\"error.backup.partialFailure\":\"部分文件同步失败，请查看详细错误信息\",\"error.backup.uploadFailed\":\"备份文件上传失败\",\"error.backup.openFileFailed\":\"备份文件打开失败\",\"error.backup.vaultNotExist\":\"笔记库不存在，请检查配置\",\"error.network.unreachable\":\"目标地址无法访问，请检查网络配置\",\"error.network.connectionRefused\":\"连接被拒绝，请确认目标服务是否运行\",\"error.network.timeout\":\"连接超时，请检查网络状态\",\"ui.support.title\":\"支持该项目\",\"ui.support.supportRequest\":\"如果这个项目帮助到您，并且想要它继续开发，请在以下方式支持我们，感谢您对开源软件的支持！\",\"ui.support.listTitle\":\"已支持清单\",\"ui.support.noData\":\"暂无记录\",\"ui.support.item\":\"项目\",\"ui.support.amount\":\"金额\",\"ui.support.time\":\"时间\",\"ui.support.message\":\"留言\",\"ui.support.thanks\":\"感谢大家对 Fast Note Sync 的支持！已支持列表会不定期更新\",\"ui.support.sortDefault\":\"默认 (金额)\",\"ui.support.sortTime\":\"按时间\",\"ui.support.sort\":\"排序\",\"ui.support.buyMeACoffee\":\"请作者喝杯咖啡\",\"ui.support.wechatReward\":\"微信打赏支持\",\"ui.share.invalidLink\":\"无效的分享链接。缺少 ID 或 Token。\",\"ui.share.errorTitle\":\"分享错误\",\"ui.share.noteNotFound\":\"找不到分享的笔记。\",\"ui.share.poweredByPrefix\":\"由 \",\"ui.share.poweredBySuffix\":\" 提供支持\",\"ui.share.version\":\"版本\",\"ui.share.tabActive\":\"分享中\",\"ui.share.noShares\":\"暂无分享记录\",\"ui.share.viewShare\":\"查看分享页\",\"ui.share.cancelShare\":\"取消分享\",\"ui.share.cancelConfirm\":\"确定要取消该笔记的分享吗？取消后分享链接将立即失效。\",\"ui.share.shareNotFound\":\"未找到分享记录\",\"ui.share.title\":\"分享笔记\",\"ui.share.checking\":\"正在检查分享状态...\",\"ui.share.create\":\"开启分享\",\"ui.share.creating\":\"正在开启...\",\"ui.share.success\":\"分享成功\",\"ui.share.shortLinkCreate\":\"生成短链接\",\"ui.share.link\":\"分享链接\",\"ui.share.shortLink\":\"短链接\",\"ui.share.copy\":\"复制链接\",\"ui.share.copySuccess\":\"链接已复制到剪贴板\",\"ui.share.shortLinkCopy\":\"复制短链接\",\"ui.share.cancelSuccess\":\"已取消分享\",\"ui.share.buttonCreating\":\"正在开启...\",\"ui.share.passwordRequired\":\"需要密码\",\"ui.share.passwordHint\":\"该分享受密码保护，请输入密码以查看内容。\",\"ui.share.passwordPlaceholder\":\"请输入密码...\",\"ui.share.preview\":\"内容预览\",\"ui.syncLog.title\":\"笔记库更新日志\",\"ui.syncLog.vault\":\"笔记库\",\"ui.syncLog.type\":\"类型\",\"ui.syncLog.action\":\"操作\",\"ui.syncLog.path\":\"文件路径\",\"ui.syncLog.size\":\"大小\",\"ui.syncLog.client\":\"客户端\",\"ui.syncLog.status\":\"状态\",\"ui.syncLog.message\":\"详细信息\",\"ui.syncLog.time\":\"记录时间\",\"ui.syncLog.changedFields\":\"变更内容\",\"ui.syncLog.noLogs\":\"暂无同步记录\",\"ui.syncLog.noLogsDescription\":\"没有找到任何更新记录\",\"ui.syncLog.description\":\"追溯所有笔记库中笔记、附件、目录及配置文件的同步变更记录\",\"ui.syncLog.allVaults\":\"所有笔记库\",\"ui.syncLog.allTypes\":\"所有类型\",\"ui.syncLog.allActions\":\"所有操作\",\"ui.syncLog.resetFilters\":\"重置筛选\",\"ui.syncLog.statusSuccess\":\"成功\",\"ui.syncLog.statusFailed\":\"失败\",\"ui.syncLog.type.note\":\"笔记\",\"ui.syncLog.type.file\":\"附件\",\"ui.syncLog.type.setting\":\"配置\",\"ui.syncLog.type.folder\":\"目录\",\"ui.syncLog.action.create\":\"新建\",\"ui.syncLog.action.modify\":\"修改\",\"ui.syncLog.action.soft_delete\":\"软删除\",\"ui.syncLog.action.delete\":\"永久删除\",\"ui.syncLog.action.rename\":\"重命名\",\"ui.syncLog.action.restore\":\"恢复\"};export{e as default};\n"
  },
  {
    "path": "frontend/assets/zh-TW-DGtNjFz9.js",
    "content": "const e={\"ui.common.title\":\"Fast Note Sync\",\"ui.common.subtitle\":\"高性能、低延遲的筆記同步, 管理, REST 服務\",\"ui.common.footerTitle\":\"基於 Golang + Websocket + Sqlite + React 構建\\n需要 <a href='https://github.com/haierkeys/obsidian-fast-note-sync' target='_blank'>Obsidian Fast Note Sync Plugin</a> 配合使用\",\"ui.common.loading\":\"載入中...\",\"ui.common.downloading\":\"下載中...\",\"ui.common.actions\":\"操作\",\"ui.common.save\":\"儲存\",\"ui.common.add\":\"新增\",\"ui.common.edit\":\"編輯\",\"ui.common.view\":\"查看\",\"ui.common.viewDetail\":\"查看詳情\",\"ui.common.delete\":\"刪除\",\"ui.common.restore\":\"還原\",\"ui.common.permanentDelete\":\"永久刪除\",\"ui.common.clear\":\"清空\",\"ui.common.batchPermanentDelete\":\"批量永久刪除\",\"ui.common.batchPermanentDeleteConfirm\":\"確定要永久刪除選中的 {{count}} 個項目嗎？此操作不可恢復！\",\"ui.common.search\":\"搜尋\",\"ui.common.refresh\":\"刷新\",\"ui.common.retry\":\"重試\",\"ui.common.refreshSuccess\":\"重新整理成功\",\"ui.common.close\":\"關閉\",\"ui.common.cancel\":\"取消\",\"ui.common.confirm\":\"確定\",\"ui.common.success\":\"成功\",\"ui.common.error\":\"錯誤\",\"ui.common.warning\":\"警告\",\"ui.common.info\":\"資訊\",\"ui.common.total\":\"總計\",\"ui.common.isEnabled\":\"啟用\",\"ui.common.isDisabled\":\"已停用\",\"ui.common.switchLanguage\":\"切換語言\",\"ui.common.comingSoon\":\"敬請期待...\",\"ui.common.comingSoonDescription\":\"此功能正在開發中，敬請期待...\",\"ui.common.helpAndSupport\":\"幫助與建議\",\"ui.common.githubIssue\":\"反饋\",\"ui.common.githubIssueDesc\":\"提交 Bug 或 功能建議\",\"ui.common.telegramGroup\":\"社群交流\",\"ui.common.telegramGroupDesc\":\"Telegram 社群討論與獲取幫助\",\"ui.common.planned\":\"待開發\",\"ui.common.unknown\":\"未知\",\"ui.common.copied\":\"已複製\",\"ui.common.restoring\":\"還原中...\",\"ui.common.rename\":\"重新命名\",\"ui.common.toggleTheme\":\"切換主題\",\"ui.common.previous\":\"上一頁\",\"ui.common.next\":\"下一頁\",\"ui.common.page\":\"第\",\"ui.common.of\":\"共\",\"ui.common.to\":\"到\",\"ui.common.perPage\":\"每頁\",\"ui.common.copy\":\"複製\",\"ui.common.createdAt\":\"建立時間\",\"ui.common.updatedAt\":\"更新時間\",\"ui.common.noSearchResults\":\"沒有找到符合的結果\",\"ui.common.selectAll\":\"全選\",\"ui.common.items\":\"條\",\"ui.common.count\":\"個\",\"ui.common.saveSuccess\":\"儲存成功\",\"ui.common.selectVault\":\"選擇筆記庫\",\"ui.common.sourceCode\":\"原始碼\",\"ui.common.wideMode\":\"寬螢幕模式\",\"ui.common.narrowMode\":\"正常寬度\",\"ui.common.fold\":\"收起\",\"ui.common.noChange\":\"內容未發生變化\",\"ui.common.na\":\"N/A\",\"ui.common.name\":\"名稱\",\"ui.auth.login\":\"登入\",\"ui.auth.logout\":\"登出\",\"ui.auth.register\":\"註冊\",\"ui.auth.registerButton\":\"註冊\",\"ui.auth.registerClosed\":\"用戶註冊已關閉\",\"ui.auth.credentials\":\"用戶名 ( 或 郵箱 )\",\"ui.auth.username\":\"用戶名\",\"ui.auth.password\":\"密碼\",\"ui.auth.remember\":\"記住我\",\"ui.auth.confirmPassword\":\"確認密碼\",\"ui.auth.email\":\"郵箱\",\"ui.auth.credentialsPlaceholder\":\"請輸入用戶名 ( 或 郵箱 )\",\"ui.auth.usernamePlaceholder\":\"請輸入用戶名\",\"ui.auth.passwordPlaceholder\":\"請輸入密碼\",\"ui.auth.emailPlaceholder\":\"請輸入郵箱\",\"ui.auth.confirmPasswordPlaceholder\":\"請確認密碼\",\"ui.auth.credentialsRequired\":\"用戶名（或郵箱）不能為空\",\"ui.auth.passwordMinLength\":\"密碼至少需要 6 個字符\",\"ui.auth.usernameMinLength\":\"用戶名至少 3 個字符\",\"ui.auth.emailInvalid\":\"請輸入有效的郵箱地址\",\"ui.auth.passwordMismatch\":\"兩次輸入的密碼不一致\",\"ui.auth.changePassword\":\"修改密碼\",\"ui.auth.currentPassword\":\"當前密碼\",\"ui.auth.newPassword\":\"新密碼\",\"ui.auth.confirmNewPassword\":\"確認新密碼\",\"ui.auth.passwordChangedSuccess\":\"密碼修改成功\",\"ui.auth.passwordChangeFailed\":\"密碼修改失敗\",\"ui.auth.sessionExpired\":\"用戶登錄狀態失效，請重新登錄\",\"ui.auth.submitting\":\"提交中...\",\"ui.auth.unknownUser\":\"未知用戶\",\"ui.auth.userUid\":\"用戶 UID: {{uid}}\",\"ui.auth.loginRequestFailed\":\"登錄請求失敗，請檢查網絡狀態\",\"ui.auth.registerRequestFailed\":\"註冊失敗，請重試\",\"ui.user.password\":\"訪問密碼\",\"ui.nav.navigation\":\"功能導航\",\"ui.nav.menuDashboard\":\"看板\",\"ui.nav.menuVaults\":\"筆記庫\",\"ui.nav.menuNotes\":\"筆記管理\",\"ui.nav.menuTrash\":\"回收站\",\"ui.nav.menuSync\":\"備份與同步\",\"ui.nav.menuSettings\":\"系統設置\",\"ui.nav.menuGit\":\"Git 自動化\",\"ui.nav.menuFiles\":\"附件管理\",\"ui.nav.menuShares\":\"分享管理\",\"ui.nav.menuSettingsBrowser\":\"筆記庫配置文件\",\"ui.nav.menuSyncLogs\":\"筆記庫更新日誌\",\"ui.nav.mainNavigation\":\"主導航\",\"ui.vault.vault\":\"筆記庫\",\"ui.vault.title\":\"筆記庫管理\",\"ui.vault.add\":\"新增筆記庫\",\"ui.vault.edit\":\"編輯筆記庫\",\"ui.vault.delete\":\"刪除筆記庫\",\"ui.vault.name\":\"筆記庫名稱\",\"ui.vault.nameRequired\":\"筆記庫名稱不能為空\",\"ui.vault.confirmDelete\":\"確定要刪除這個筆記庫嗎？此操作將無法撤銷！\",\"ui.vault.noVaults\":\"目前還沒有任何筆記庫\",\"ui.vault.count\":\"共 {{count}} 個筆記庫\",\"ui.vault.searchPlaceholder\":\"搜索筆記庫...\",\"ui.vault.note\":\"筆記\",\"ui.vault.attachmentCount\":\"附件\",\"ui.vault.totalSize\":\"總容量: {{size}}\",\"ui.vault.authTokenConfig\":\"授權配置\",\"ui.vault.copyConfigSuccess\":\"配置已複製到剪貼板\",\"ui.vault.copyConfigError\":\"複製失敗，請手動選擇複製\",\"ui.vault.oneClickImport\":\"一鍵導入 Obsidian\",\"ui.vault.pleaseCreateVault\":\"請新建筆記庫 或 授權配置到 Obsidian 自動創建\",\"ui.vault.createVaultFirst\":\"請先創建一個筆記庫，然後再進行管理\",\"ui.vault.goToVaultManagement\":\"前往筆記庫管理\",\"ui.vault.setAsDefault\":\"設為默認\",\"ui.note.note\":\"筆記\",\"ui.note.notes\":\"筆記列表\",\"ui.note.newNote\":\"新建筆記\",\"ui.note.noNotes\":\"暫無筆記\",\"ui.note.viewNote\":\"查看筆記\",\"ui.note.editNote\":\"編輯筆記\",\"ui.note.search\":\"搜索\",\"ui.note.searchPlaceholder\":\"搜索筆記...\",\"ui.note.noteTitlePlaceholder\":\"筆記標題 (例如: note.md)\",\"ui.note.noteContentPlaceholder\":\"請輸入筆記內容...\",\"ui.note.noteCount\":\"筆記數量\",\"ui.note.noteTitleRequired\":\"標題不能為空\",\"ui.note.results\":\"條筆記\",\"ui.note.saving\":\"保存中...\",\"ui.note.lastSavedAt\":\"上次保存\",\"ui.note.renameNote\":\"重命名筆記\",\"ui.note.renameNotePlaceholder\":\"請輸入新的筆記名稱 (例如: new-note.md)\",\"ui.note.renameSuccess\":\"筆記重命名成功\",\"ui.note.deleteNoteConfirm\":'確定要刪除筆記 \"{{title}}\" 嗎？',\"ui.note.permanentDeleteConfirm\":'確定要永久刪除筆記 \"{{title}}\" 嗎？此操作無法撤銷！',\"ui.note.restoreNoteConfirm\":'確定要恢復筆記 \"{{title}}\" 嗎？',\"ui.note.clearRecycleConfirm\":\"確定要清空筆記回收站嗎？此操作無法撤銷！\",\"ui.note.noVaultsForNotes\":\"還沒有任何筆記庫\",\"ui.note.searchPath\":\"路徑\",\"ui.note.searchContentMode\":\"內容\",\"ui.note.sortByBy\":\"排序\",\"ui.note.sortByMtime\":\"修改時間\",\"ui.note.sortByCtime\":\"創建時間\",\"ui.note.sortByPath\":\"路徑\",\"ui.note.sortAsc\":\"升序\",\"ui.note.sortDesc\":\"降序\",\"ui.note.viewFlat\":\"平鋪瀏覽\",\"ui.note.viewFolder\":\"目錄瀏覽\",\"ui.note.editorHr\":\"分割線\",\"ui.note.exportPdfPlanned\":\"PDF 導出功能開發中...\",\"ui.note.undo\":\"撤銷\",\"ui.note.redo\":\"重做\",\"ui.note.cut\":\"剪貼\",\"ui.note.copy\":\"複製\",\"ui.note.paste\":\"貼上\",\"ui.note.selectAll\":\"全選\",\"ui.note.fullscreen\":\"全螢幕\",\"ui.note.exitFullscreen\":\"退出全螢幕\",\"ui.note.contextMenu\":\"上下文選單\",\"ui.note.loadingEditor\":\"載入編輯器...\",\"ui.note.unsavedContentWithoutTitle\":\"內容未保存，因為標題為空。\",\"ui.note.wikiLinkNotFound\":'未找到筆記 \"{{target}}\"',\"ui.history.title\":\"筆記歷史\",\"ui.history.description\":\"查看和恢復筆記的歷史版本\",\"ui.history.version\":\"版本\",\"ui.history.versionLabel\":\"版本\",\"ui.history.time\":\"更新時間\",\"ui.history.action\":\"操作\",\"ui.history.view\":\"查看\",\"ui.history.count\":\"共 {{count}} 條歷史記錄\",\"ui.history.loading\":\"載入中...\",\"ui.history.noHistory\":\"暫無歷史記錄\",\"ui.history.clientSource\":\"客戶端\",\"ui.history.diffDetails\":\"版本 v{{version}} 差異詳情\",\"ui.history.showDiffOnly\":\"只看差異\",\"ui.history.showOriginalContent\":\"修改前內容\",\"ui.history.diffLegendAdd\":\"新增\",\"ui.history.diffLegendDel\":\"刪除\",\"ui.history.restore\":\"還原\",\"ui.history.restoreToVersion\":\"恢復到此版本\",\"ui.history.restoreVersionConfirmTitle\":\"確認恢復\",\"ui.history.restoreVersionConfirmDesc\":\"確定要將筆記恢復到版本 v{{version}} 嗎？當前內容將被覆蓋，但會自動保存為新的歷史版本。\",\"ui.history.restoring\":\"恢復中...\",\"ui.file.file\":\"附件\",\"ui.file.files\":\"附件列表\",\"ui.file.noFiles\":\"暫無附件\",\"ui.file.searchFilePlaceholder\":\"搜索附件...\",\"ui.file.deleteFileConfirm\":'確定要刪除附件 \"{{title}}\" 嗎？',\"ui.file.restoreFileConfirm\":'確定要恢復附件 \"{{title}}\" 嗎？',\"ui.file.permanentDeleteConfirm\":'確定要永久刪除附件 \"{{title}}\" 嗎？此操作無法撤銷！',\"ui.file.clearRecycleConfirm\":\"確定要清空附件回收站嗎？此操作無法撤銷！\",\"ui.file.attachmentCount\":\"附件\",\"ui.file.results\":\"條附件\",\"ui.file.searchPlaceholder\":\"搜尋附件...\",\"ui.file.totalSize\":\"共計 {{size}}\",\"ui.file.renameFile\":\"重新命名附件\",\"ui.file.renameFilePlaceholder\":\"請輸入新的附件名稱\",\"ui.file.renameSuccess\":\"附件重新命名成功\",\"ui.file.size\":\"佔用空間\",\"ui.file.fileDetail\":\"附件詳情\",\"ui.file.imagePreview\":\"圖片預覽\",\"ui.file.audioPreview\":\"音訊播放\",\"ui.file.videoPreview\":\"影片播放\",\"ui.file.pdfPreview\":\"PDF 文件\",\"ui.file.codePreview\":\"腳本程式碼\",\"ui.file.unsupportedPreview\":\"該檔案類型暫不支援直接預覽\",\"ui.file.openInNewWindow\":\"在新視窗中開啟\",\"ui.file.browserDownload\":\"瀏覽器下載\",\"ui.file.batchRestore\":\"批次還原\",\"ui.file.batchPermanentDelete\":\"批次永久刪除\",\"ui.file.selectedCount\":\"已選擇 {{count}} 項\",\"ui.file.batchRestoreConfirm\":\"確定要還原選中的 {{count}} 項嗎？\",\"ui.file.batchPermanentDeleteConfirm\":\"確定要永久刪除選中的 {{count}} 項嗎？此操作無法撤銷！\",\"ui.file.noVaultsForFiles\":\"還沒有任何筆記庫\",\"ui.settings.systemConfig\":\"分享短鏈\",\"ui.settings.securityConfig\":\"安全令牌\",\"ui.settings.noteRelatedConfig\":\"筆記附件\",\"ui.settings.fontConfig\":\"常規\",\"ui.settings.saveSettings\":\"儲存設定\",\"ui.settings.saveSuccess\":\"設定儲存成功\",\"ui.settings.saveFailed\":\"設定儲存失敗\",\"ui.settings.fontSet\":\"WebGui 字體設定\",\"ui.settings.authTokenKey\":\"用戶服務令牌加密混淆字元\",\"ui.settings.tokenExpiry\":\"用戶服務令牌過期時間\",\"ui.settings.shareTokenKey\":\"分享令牌加密混淆字元\",\"ui.settings.shareTokenExpiry\":\"分享令牌過期時間\",\"ui.settings.registerIsEnable\":\"開放註冊\",\"ui.settings.fileChunkSize\":\"附件上傳分片大小\",\"ui.settings.softDeleteRetentionTime\":\"回收站 (軟刪除) 保留時間\",\"ui.settings.uploadSessionTimeout\":\"附件上傳會話超時\",\"ui.settings.historyKeepVersions\":\"筆記歷史記錄保留版本數\",\"ui.settings.historySaveDelay\":\"筆記歷史記錄儲存延遲\",\"ui.settings.historySaveDelayFormatError\":\"歷史記錄儲存延遲格式無效\",\"ui.settings.adminUid\":\"系統設定存取限制 ( 用戶 UID )\",\"ui.settings.adminUidDesc\":\"指定管理權限的 UID，0 表示所有登入用戶均可管理，預設：0\",\"ui.settings.onlyAdminAccess\":\"只有管理員可以存取此頁面\",\"ui.settings.fontSetDesc\":\"設定 WebGui 介面字體，留空則使用系統預設字體。\\n支援 <u>遠端 CSS 字體樣式表</u> 或 <u>預設 CSS 樣式表關鍵字</u> 或 <u>字體網路地址</u> 以及 <u>本機字體</u>（ 放在 storage/user_static/ 目錄下 ）預設：空。\\n\\n範例: \\n&nbsp;&nbsp;- 遠端 CSS 字體樣式表: <b>https://fonts.googleapis.com/css2?family=Noto+Sans+SC&display=swap</b>\\n&nbsp;&nbsp;- 預設 CSS 樣式表關鍵字: <b>local</b> 或 <b>remote</b>\\n&nbsp;&nbsp;- 本機預設 CSS 字體樣式表: <b>/static/fonts/font.css</b>\\n&nbsp;&nbsp;- 字體網路地址: <b>https://ik.imagekit.io/name/your-font.woff2</b>\\n&nbsp;&nbsp;- 本機字體: <b>/user_static/your-font.woff2</b>, 需要將字體檔案 <u>your-font.woff2</u> 放在 storage/user_static/ 目錄下\",\"ui.settings.authTokenKeyDesc\":\"本設定項參與所有用戶的服務令牌生成過程，首次部署之後為了服務端的安全性，<u><b>強烈建議修改本項</b></u>，一旦修改原有令牌立刻失效，WebGui 也需要重新登入。\",\"ui.settings.tokenExpiryDesc\":\"用戶服務令牌的過期時間，支援格式：7d（天）、24h（小時）、30m（分鐘），預設：<b>365d</b>\",\"ui.settings.shareTokenKeyDesc\":\"用於生成分享令牌的加密混淆 Key，修改後原有分享連結將失效，預設：<b>fns</b>\",\"ui.settings.shareTokenExpiryDesc\":\"分享令牌的過期時間，支援格式：7d（天）、24h（小時）、30m（分鐘），預設：<b>30d</b>\",\"ui.settings.registerIsEnableDesc\":\"是否允許新用戶在此服務上建立帳戶，預設：<b>開啟</b>\",\"ui.settings.fileChunkSizeDesc\":\"上傳時分塊大小（如 1MB, 512KB），建議 512KB-2MB，預設：<b>512KB</b>\",\"ui.settings.softDeleteRetentionTimeDesc\":\"刪除筆記和附件後的保留時長（如 30d, 24h），0 為不自動清理，本設定過短會導致離線設備無法同步刪除操作，請謹慎設定，預設：7d\",\"ui.settings.uploadSessionTimeoutDesc\":\"檔案分塊上傳會話的有效期（如 1h, 30m），預設：<b>1d</b>\",\"ui.settings.historyKeepVersionsDesc\":\"每個筆記保留的歷史版本數量，超過此數量時自動刪除最舊的版本，最小值：100，預設：<b>100</b>\",\"ui.settings.historySaveDelayDesc\":\"歷史記錄儲存的延遲時間，用於防止頻繁編輯產生過多歷史版本（如 10s, 1m），預設：<b>10s</b>\",\"ui.settings.toastPosition\":\"通知顯示位置\",\"ui.settings.position.top-left\":\"左上角\",\"ui.settings.position.top-center\":\"頂部居中\",\"ui.settings.position.top-right\":\"右上角\",\"ui.settings.position.bottom-left\":\"左下角\",\"ui.settings.position.bottom-center\":\"底部居中\",\"ui.settings.position.bottom-right\":\"右下角\",\"ui.settings.colorScheme\":\"配色方案\",\"ui.settings.colorSchemeSwitched\":\"已切換到 {{scheme}} 配色\",\"ui.settings.colorScheme.default\":\"標準\",\"ui.settings.colorScheme.green\":\"綠色\",\"ui.settings.colorScheme.blue\":\"藍色\",\"ui.settings.colorScheme.skyBlue\":\"天藍\",\"ui.settings.colorScheme.purple\":\"紫色\",\"ui.settings.colorScheme.orange\":\"橙色\",\"ui.settings.colorScheme.rose\":\"玫瑰\",\"ui.settings.colorScheme.teal\":\"青色\",\"ui.settings.themeAuto\":\"自動 (18:00-06:00 暗色)\",\"ui.settings.themeLight\":\"淺色\",\"ui.settings.themeDark\":\"深色\",\"ui.settings.cloudflaredTestRequired\":\"請先點擊 隧道程序下載 按鈕\",\"ui.settings.downloadSuccess\":\"下載成功\",\"ui.settings.downloadFailed\":\"下載失敗\",\"ui.settings.tunnelGatewayConfig\":\"中繼網關\",\"ui.settings.tunnelGatewayDesc\":\"<b>FNS</b> 集成的內網穿透服務，使您的本地 <b>FNS</b> 服務可以直接通過公網安全訪問，不用再配置繁瑣的 HTTPS 和 WebSocket 代理，就可以享受安全訪問服務。\\n支持通過 Ngrok 或 Cloudflare 隧道接入。\",\"ui.settings.ngrokDesc\":'使用 Ngrok 內網穿透隧道，將本地服務安全地暴露到公網。\\n官方申請入口: <a href=\"https://dashboard.ngrok.com/get-started/setup\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary hover:underline\">https://dashboard.ngrok.com/get-started/setup</a>\\n <b>注意</b>: Ngrok 免費帳戶有各維度限制，具體請查閱 <a href=\"https://ngrok.com/docs/pricing-limits/free-plan-limits\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary hover:underline\">https://ngrok.com/docs/pricing-limits/free-plan-limits</a>',\"ui.settings.customDomain\":\"自定義域名\",\"ui.settings.customDomainDesc\":\"可選，需要 ngrok 付費帳戶\",\"ui.settings.saveNgrok\":\"保存設置\",\"ui.settings.cloudflareDesc\":'基於 Cloudflare 零信任網絡構建的反向隧道，提供更高安全性和穩定性。\\n官方申請入口: <a href=\"https://one.dash.cloudflare.com\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-primary hover:underline\">https://one.dash.cloudflare.com</a>\\n在保存設置前 , 需要您先進行 <b>隧道程序下載</b>',\"ui.settings.enableLog\":\"記錄日誌\",\"ui.settings.cloudflareLogDesc\":\"日誌記錄在 storage/logs/cloudflared_tunnel.log\",\"ui.settings.saveCloudflare\":\"保存設置\",\"ui.settings.downloadCloudflared\":\"隧道程序下載\",\"ui.settings.pullSource\":\"版本檢測源\",\"ui.settings.pullSourceDesc\":\"選擇版本檢測&更新時的來源倉庫。\",\"ui.settings.pullSource.auto\":\"自動檢測\",\"ui.settings.pullSource.github\":\"github.com\",\"ui.settings.pullSource.cnb\":\"騰訊 cnb.cool\",\"ui.settings.userDatabaseConfig\":\"數據庫增強\",\"ui.settings.userDatabaseDesc\":\"默認 <b>SQLite</b>（免維護，支持並發讀/單寫），適合個人及小規模部署，能滿足個人用戶大部分需求。<br/><div class='mt-2 pt-2 border-t border-border/50 opacity-90'><div class='mb-2 text-amber-500 font-medium'>建議改用專業數據庫 (PostgreSQL / MySQL) 的場景：</div><ul class='list-disc list-inside space-y-1 mb-1'><li>多用戶、多個筆記庫且規模極大</li><li>筆記數量或附件體積龐大</li><li>經常遇到並發相關錯誤</li></ul><div>專業數據庫在並發與大規模數據處理上更可靠，但會提高硬件要求與運維成本（需具備運維能力）。</div><div class='mt-2 pt-2 border-t border-border/50 text-xs text-rose-500 font-medium italic'>注意：當修改數據庫之後，原數據不會遷移到新數據庫，您需要重新強制同步筆記庫；設置必須先通過連接測試才能更改數據庫類型。</div></div>\",\"ui.settings.databaseType\":\"數據庫類型\",\"ui.settings.databaseType.sqlite\":\"SQLite (內嵌)\",\"ui.settings.databaseType.mysql\":\"MySQL\",\"ui.settings.databaseType.postgres\":\"PostgreSQL\",\"ui.settings.databaseHost\":\"數據庫地址 (Host)\",\"ui.settings.databasePort\":\"端口 (Port)\",\"ui.settings.databaseUser\":\"用戶名 (User)\",\"ui.settings.databasePassword\":\"密碼 (Password)\",\"ui.settings.databaseName\":\"數據庫名 (Database)\",\"ui.settings.databaseMaxIdleConns\":\"最大空闲連接數 (MaxIdle)\",\"ui.settings.databaseMaxOpenConns\":\"最大打開連接數 (MaxOpen)\",\"ui.settings.databaseConnMaxLifetime\":\"連接最大存活時間 (MaxLifetime)\",\"ui.settings.databaseConnMaxIdleTime\":\"連接最大空闲時間 (MaxIdleTime)\",\"ui.settings.databaseMaxWriteConcurrency\":\"最大寫入並發數 (MaxWriteConcurrency)\",\"ui.settings.databaseSchema\":\"資料庫 Schema (Postgres)\",\"ui.settings.databaseSslMode\":\"SSL 模式 (SSL Mode)\",\"ui.settings.mysqlPermissionWarning\":\"注意：當使用 MySQL 時，提供的帳號必須具有創建數據庫 (CREATE DATABASE) 的權限，用於用戶信息隔離。\",\"ui.settings.postgresPermissionWarning\":\"注意：當使用 PostgreSQL 時，提供的帳號必須具有創建數據庫 (CREATE DATABASE) 的權限，用於用戶信息隔離。\",\"ui.settings.testConnection\":\"連接測試\",\"ui.settings.testSuccess\":\"連接測試成功\",\"ui.settings.testFailed\":\"連接測試失敗\",\"ui.settings.testRequiredBeforeSave\":\"請先進行連接測試並確保成功後再保存\",\"ui.settingsBrowser.title\":\"筆記庫配置文件\",\"ui.settingsBrowser.description\":\"管理筆記庫的配置資訊（如 筆記庫配置、插件、主題等）\",\"ui.settingsBrowser.add\":\"新增配置\",\"ui.settingsBrowser.edit\":\"編輯配置\",\"ui.settingsBrowser.key\":\"路徑\",\"ui.settingsBrowser.value\":\"大小\",\"ui.settingsBrowser.content\":\"內容\",\"ui.settingsBrowser.keyRequired\":\"路徑不能為空\",\"ui.settingsBrowser.confirmDelete\":'確定要刪除配置項 \"{{key}}\" 嗎？',\"ui.settingsBrowser.rename\":\"重新命名路徑\",\"ui.settingsBrowser.newKey\":\"新路徑\",\"ui.settingsBrowser.renameSuccess\":\"配置項重命名成功\",\"ui.settingsBrowser.noSettings\":\"暫無配置項\",\"ui.obsidian.authTokenConfig\":\"授權配置\",\"ui.obsidian.authTokenConfigTo\":\"一鍵授權到 Obsidian\",\"ui.obsidian.oneClickImport\":\"一鍵授權到 Obsidian\",\"ui.obsidian.copyConfigSuccess\":\"授權配置已複製，請返回 Obsidian 插件粘貼配置\",\"ui.obsidian.copyConfigError\":\"非 HTTPS 頁面，無法使用剪貼板功能，請手動複製授權配置\",\"ui.system.serviceInfo\":\"服務信息\",\"ui.system.versionInfo\":\"版本信息\",\"ui.system.repo\":\"項目倉庫\",\"ui.system.githubRepo\":\"GitHub 倉庫\",\"ui.system.cnbMirror\":\"CNB 鏡像庫\",\"ui.system.currentVersion\":\"當前版本\",\"ui.system.checkUpdate\":\"檢查更新\",\"ui.system.checkNow\":\"立即檢查\",\"ui.system.checking\":\"檢查中...\",\"ui.system.newVersionAvailable\":\"發現新版本\",\"ui.system.alreadyLatest\":\"已是最新版本\",\"ui.system.viewRelease\":\"查看發布頁\",\"ui.system.upgradeNow\":\"立即升級\",\"ui.system.upgrading\":\"升級中...\",\"ui.system.upgradeSuccess\":\"升級觸發成功，頁面即將刷新\",\"ui.system.upgradeFailed\":\"升級觸發失敗\",\"ui.system.upgradeRefreshTimeout\":\"升級已完成，但服務尚未恢復可用，請手動重新整理頁面。\",\"ui.system.viewChangelog\":\"查看更新日誌\",\"ui.system.getVersionError\":\"獲取版本資訊失敗\",\"ui.system.getWebGuiConfigError\":\"獲取WebGui配置失敗:\",\"ui.system.restartService\":\"重啟服務\",\"ui.system.restartServiceConfirm\":\"確定要立即重啟服務嗎？重啟過程中連接將會斷開。\",\"ui.system.manualGC\":\"記憶體回收\",\"ui.system.manualGCConfirm\":\"確定要立即執行記憶體回收 (GC) 嗎？\",\"ui.system.manualGCSuccess\":\"手動記憶體回收 (GC) 觸發成功\",\"ui.system.serverSystemInfo\":\"伺服器資訊\",\"ui.system.modelName\":\"處理器型號\",\"ui.system.hostInfo\":\"系統資訊\",\"ui.system.systemTime\":\"系統時間\",\"ui.system.runtimeInfo\":\"服務資訊\",\"ui.system.startTime\":\"啟動時間\",\"ui.system.serviceUptime\":\"運行時間\",\"ui.system.physicalCores\":\"核心 (邏輯/物理)\",\"ui.system.cpuLoad\":\"平均負載 (1/5/15)\",\"ui.system.totalMemory\":\"總記憶體\",\"ui.system.usedMemory\":\"已用記憶體\",\"ui.system.memoryUsage\":\"記憶體使用率\",\"ui.system.os\":\"作業系統\",\"ui.system.kernelVersion\":\"核心版本\",\"ui.system.uptime\":\"運行時間\",\"ui.system.goVersion\":\"運行時版本\",\"ui.system.goroutines\":\"協程數量\",\"ui.system.heapMemory\":\"堆記憶體 / 總佔用\",\"ui.system.numGc\":\"GC 次數\",\"ui.system.createdAt\":\"創建時間\",\"ui.system.updatedAt\":\"更新時間\",\"ui.system.websocketClients\":\"線上客戶端\",\"ui.system.wsNickname\":\"暱稱\",\"ui.system.wsClientName\":\"客戶端\",\"ui.system.wsClientType\":\"類型\",\"ui.system.wsRemoteAddr\":\"地址\",\"ui.system.wsStartTime\":\"連接時間\",\"ui.system.wsTraceId\":\"追蹤 ID\",\"ui.system.wsNoClients\":\"暫無線上客戶端\",\"ui.storage.management\":\"儲存配置\",\"ui.storage.add\":\"新增儲存\",\"ui.storage.edit\":\"編輯儲存\",\"ui.storage.noStorage\":\"暫無儲存配置\",\"ui.storage.type\":\"類型\",\"ui.storage.storageType.oss\":\"阿里雲 OSS\",\"ui.storage.storageType.s3\":\"AWS S3\",\"ui.storage.storageType.r2\":\"Cloudflare R2\",\"ui.storage.storageType.minio\":\"MinIO\",\"ui.storage.storageType.localfs\":\"本地儲存\",\"ui.storage.storageType.webdav\":\"WebDAV\",\"ui.storage.selectType\":\"選擇儲存類型\",\"ui.storage.endpoint\":\"終端節點 ( Endpoint )\",\"ui.storage.region\":\"區域 ( S3 Region )\",\"ui.storage.accountId\":\"帳戶ID ( R2 Account ID )\",\"ui.storage.bucketName\":\"儲存桶\",\"ui.storage.accessKeyId\":\"金鑰 ID ( AccessKey ID )\",\"ui.storage.accessKeySecret\":\"金鑰 ( AccessKey Secret )\",\"ui.storage.webdavUrl\":\"WebDAV 地址\",\"ui.storage.webdavUser\":\"用戶名\",\"ui.storage.webdavPassword\":\"密碼\",\"ui.storage.customPath\":\"自定義路徑\",\"ui.storage.accessUrlPrefix\":\"訪問 URL 前綴\",\"ui.storage.placeholder.endpoint.oss\":\"oss-cn-hangzhou.aliyuncs.com\",\"ui.storage.placeholder.endpoint.minio\":\"http://192.168.1.100:9000\",\"ui.storage.placeholder.region\":\"us-east-1\",\"ui.storage.placeholder.accountId\":\"your-account-id\",\"ui.storage.placeholder.bucketName\":\"my-bucket\",\"ui.storage.placeholder.accessKeyId\":\"\",\"ui.storage.placeholder.accessKeySecret\":\"\",\"ui.storage.placeholder.webdavUrl\":\"http://192.168.1.100:5244/dav\",\"ui.storage.placeholder.webdavUser\":\"admin\",\"ui.storage.placeholder.webdavPassword\":\"\",\"ui.storage.placeholder.customPath\":\"data/obsidian\",\"ui.storage.placeholder.accessUrlPrefix\":\"http://192.168.1.100:5244\",\"ui.storage.help.endpoint.oss\":\"阿里雲 OSS 的 Endpoint 地址，不含 Bucket 名稱\",\"ui.storage.help.endpoint.minio\":\"MinIO 服務地址，需包含協議前綴和端口\",\"ui.storage.help.region\":\"S3 儲存桶所在區域\",\"ui.storage.help.accountId\":\"Cloudflare 帳戶 ID，可在控制台右側欄找到\",\"ui.storage.help.webdavUrl\":\"需包含協議前綴 (http:// 或 https://) 和完整路徑\",\"ui.storage.help.webdavUser\":\"WebDAV 服務的登入使用者名稱\",\"ui.storage.help.webdavPassword\":\"WebDAV 服務的登入密碼\",\"ui.storage.help.customPath\":\"檔案存放的子目錄路徑，無需首尾斜線\",\"ui.storage.help.accessUrlPrefix\":\"用於生成檔案存取連結的前綴地址，僅展示用途\",\"ui.storage.confirmDelete\":\"確定要刪除這個儲存配置嗎？\",\"ui.storage.validate.title\":\"測試連線\",\"ui.storage.validate.loading\":\"測試中...\",\"ui.backup.management\":\"任務管理\",\"ui.backup.add\":\"新增任務\",\"ui.backup.edit\":\"編輯任務\",\"ui.backup.noBackup\":\"暫無備份與同步任務\",\"ui.backup.selectType\":\"請選擇備份類型\",\"ui.backup.confirmDelete\":\"確定要刪除這個備份任務嗎？\",\"ui.backup.vault\":\"筆記庫\",\"ui.backup.selectVault\":\"選擇筆記庫\",\"ui.backup.type\":\"備份類型\",\"ui.backup.backupType.full\":\"全量備份\",\"ui.backup.backupType.incremental\":\"增量備份\",\"ui.backup.backupType.sync\":\"鏡像同步\",\"ui.backup.cronStrategy\":\"定時策略\",\"ui.backup.strategy.daily\":\"每日備份 (00:00)\",\"ui.backup.strategy.weekly\":\"每週備份 (週一 00:00)\",\"ui.backup.strategy.monthly\":\"每月備份 (1 號 00:00)\",\"ui.backup.strategy.custom\":\"自定義 Cron\",\"ui.backup.cronExpression\":\"Cron 表達式\",\"ui.backup.retentionDays\":\"保留天數\",\"ui.backup.fileCountUnit\":\"個檔案\",\"ui.backup.retentionDays.sync\":\"歷史保留天數 (0:不清理歷史, -1:不保留歷史記錄)\",\"ui.backup.retentionDays.backup\":\"備份包保留天數 (0:不清理備份, -1:不保留歷史備份)\",\"ui.backup.includeVaultName.label\":\"包含筆記庫名\",\"ui.backup.includeVaultName.tooltip\":\"關閉: {customPath}/notes/xxx.md\\n開啟: {customPath}/{vaultName}/notes/xxx.md\",\"ui.backup.noAvailableStorage\":\"暫無可用儲存配置\",\"ui.backup.addStorageTip\":\"請先在儲存管理中添加並啟用儲存後端\",\"ui.backup.storages\":\"儲存選擇\",\"ui.backup.validation.vaultRequired\":\"請選擇筆記庫\",\"ui.backup.validation.typeRequired\":\"請選擇備份類型\",\"ui.backup.validation.strategyRequired\":\"請選擇定時策略\",\"ui.backup.validation.storageRequired\":\"請至少選擇一個儲存\",\"ui.backup.validation.retentionDaysMin\":\"保留天數須 ≥ -1\",\"ui.backup.validation.cronExpressionRequired\":\"自定義策略下 Cron 表達式不能為空\",\"ui.backup.lastRunTime\":\"上次執行\",\"ui.backup.nextRunTime\":\"下次執行\",\"ui.backup.executeNow\":\"立即執行\",\"ui.backup.executeSuccess\":\"手動觸發備份成功\",\"ui.backup.status.0\":\"等待中\",\"ui.backup.status.1\":\"進行中\",\"ui.backup.status.2\":\"成功\",\"ui.backup.status.3\":\"失敗\",\"ui.backup.status.4\":\"取消\",\"ui.backup.status.5\":\"無更新\",\"ui.backup.history.title\":\"任務記錄\",\"ui.backup.history.startTime\":\"任務執行時間\",\"ui.backup.history.storage\":\"儲存\",\"ui.backup.history.status\":\"狀態\",\"ui.backup.history.backupStats\":\"備份統計\",\"ui.backup.history.syncStats\":\"同步統計\",\"ui.backup.history.backupFile\":\"備份檔案\",\"ui.backup.history.message\":\"訊息\",\"ui.backup.history.copyError\":\"複製錯誤訊息\",\"ui.backup.history.noData\":\"暫無任務記錄\",\"ui.git.title\":\"Git 自動化管理\",\"ui.git.config\":\"Git 倉庫配置\",\"ui.git.repoUrl\":\"Git 倉庫地址 (不支援 ssh 地址)\",\"ui.git.status\":\"Git 狀態概覽\",\"ui.git.history\":\"自動化提交記錄\",\"ui.git.comingSoon\":\"Git 自動化功能開發中...\",\"ui.git.configDesc\":\"配置您的 Git 倉庫地址、分支以及身份驗證資訊。\",\"ui.git.statusDesc\":\"即時監控當前倉庫的提交狀態與同步進度。\",\"ui.git.historyDesc\":\"檢視最近的自動提交記錄。\",\"ui.git.retentionDays\":\"歷史保留天數\",\"ui.git.retentionDaysDesc\":\"0: 不清理歷史，-1: 不保留歷史記錄\",\"ui.git.addConfig\":\"新增 Git 同步配置\",\"ui.git.editConfig\":\"編輯 Git 同步配置\",\"ui.git.loading\":\"正在載入配置...\",\"ui.git.noConfig\":\"暫無 Git 同步配置\",\"ui.git.addFirst\":\"立即新增第一個倉庫\",\"ui.git.lastCommit\":\"上次提交\",\"ui.git.checkTime\":\"上次檢測\",\"ui.git.neverRun\":\"從未執行\",\"ui.git.execute.title\":\"立即觸發同步\",\"ui.git.clean.title\":\"清理工作區\",\"ui.git.clean.confirm\":\"確定要清理此 Git 工作區嗎？這將刪除本地克隆並重新初始化。\",\"ui.git.delete.confirm\":\"確定要刪除此 Git 配置嗎？此操作不可撤銷。\",\"ui.git.form.branch\":\"分支名稱\",\"ui.git.form.delay\":\"自動同步延遲（秒）\",\"ui.git.history.title\":\"提交記錄\",\"ui.git.history.configId\":\"配置 ID\",\"ui.git.history.startTime\":\"提交時間\",\"ui.git.history.endTime\":\"結束時間\",\"ui.git.history.status\":\"狀態\",\"ui.git.history.message\":\"訊息\",\"ui.git.history.noData\":\"暫無提交記錄\",\"ui.git.history.duration\":\"提交耗時\",\"ui.git.history.vault\":\"筆記庫\",\"ui.git.status.0\":\"待機\",\"ui.git.status.1\":\"檢測中\",\"ui.git.status.2\":\"成功\",\"ui.git.status.3\":\"失敗\",\"ui.git.status.4\":\"已停止\",\"ui.git.validate.title\":\"檢測連線\",\"ui.git.validate.loading\":\"檢測中...\",\"ui.validation.git.vaultRequired\":\"請選擇筆記本\",\"ui.validation.git.repoUrlRequired\":\"請輸入倉庫地址\",\"ui.validation.git.repoUrlInvalid\":\"請輸入有效的 URL\",\"ui.validation.git.branchRequired\":\"請輸入分支名稱\",\"ui.validation.git.delayMin\":\"延遲時間不能為負數\",\"ui.validation.git.retentionDaysMin\":\"保留天數須 ≥ -1\",\"ui.validation.storage.typeRequired\":\"請選擇儲存類型\",\"ui.validation.storage.accessUrlPrefixRequired\":\"存取地址前綴不能為空\",\"ui.validation.vault.nameRequired\":\"倉庫名稱不能為空\",\"api.git.list.error\":\"取得 Git 配置列表失敗\",\"api.git.save.success\":\"儲存配置成功\",\"api.git.save.error\":\"儲存配置失敗\",\"api.git.delete.success\":\"刪除配置成功\",\"api.git.delete.error\":\"刪除配置失敗\",\"api.git.execute.success\":\"已觸發同步任務\",\"api.git.execute.error\":\"觸發同步失敗\",\"api.git.clean.success\":\"清理工作區成功\",\"api.git.clean.error\":\"清理工作區失敗\",\"api.git.history.error\":\"取得歷史記錄失敗\",\"api.git.validate.success\":\"Git 倉庫連線測試成功\",\"api.git.validate.error\":\"Git 倉庫連線測試失敗\",\"api.backup.configList.error\":\"取得備份配置列表失敗\",\"api.backup.delete.success\":\"刪除成功\",\"api.backup.delete.error\":\"刪除備份配置失敗\",\"api.backup.save.success\":\"保存成功\",\"api.backup.save.error\":\"保存備份配置失敗\",\"api.backup.execute.success\":\"手動觸發成功\",\"api.backup.execute.error\":\"觸發備份失敗\",\"api.backup.history.error\":\"獲取備份歷史失敗\",\"api.system.restart.success\":\"服務重啟已觸發\",\"api.system.restart.error\":\"重啟服務請求失敗\",\"api.system.gc.success\":\"記憶體回收已觸發\",\"api.system.gc.error\":\"記憶體回收請求失敗\",\"api.storage.list.error\":\"獲取儲存配置列表失敗\",\"api.storage.delete.success\":\"刪除成功\",\"api.storage.delete.error\":\"刪除儲存配置失敗\",\"api.storage.save.error\":\"保存儲存配置失敗\",\"api.storage.types.error\":\"獲取儲存類型失敗\",\"api.storage.validate.success\":\"儲存連接測試成功\",\"api.storage.validate.error\":\"儲存連接測試失敗\",\"error.storage.webdav.unauthorized\":\"WebDAV 認證失敗，請檢查用戶名和密碼\",\"error.storage.webdav.forbidden\":\"WebDAV 權限不足，請檢查賬戶寫入權限\",\"error.storage.webdav.notFound\":\"WebDAV 路徑不存在，請檢查自定義路徑配置\",\"error.storage.webdav.methodNotAllowed\":\"WebDAV 方法不被允許，請確認服務端已啟用 WebDAV\",\"error.storage.webdav.unreachable\":\"WebDAV 地址無法訪問，請檢查儲存配置中的地址\",\"error.storage.webdav.connectionRefused\":\"WebDAV 連接被拒絕，請確認服務是否運行\",\"error.storage.webdav.timeout\":\"WebDAV 連接超時，請檢查網絡或服務狀態\",\"error.storage.webdav.generic\":\"WebDAV 操作失敗，請檢查儲存配置\",\"error.storage.s3.noSuchBucket\":\"S3 儲存桶不存在，請檢查桶名配置\",\"error.storage.s3.accessDenied\":\"S3 訪問被拒絕，請檢查 Access Key 權限\",\"error.storage.s3.unreachable\":\"S3 端點無法訪問，請檢查 Endpoint 地址\",\"error.storage.s3.timeout\":\"S3 連接超時，請檢查網絡或端點配置\",\"error.storage.oss.noSuchBucket\":\"OSS 儲存桶不存在，請檢查 Bucket 名稱\",\"error.storage.oss.accessDenied\":\"OSS 訪問被拒絕，請檢查 AccessKey 權限\",\"error.storage.oss.unreachable\":\"OSS 端點無法訪問，請檢查 Endpoint 地址\",\"error.storage.local.noPermission\":\"本地儲存無寫入權限\",\"error.storage.local.createDirFailed\":\"本地儲存目錄創建失敗\",\"error.storage.local.permissionDenied\":\"本地儲存權限被拒絕\",\"error.backup.partialFailure\":\"部分文件同步失敗，請查看詳細錯誤信息\",\"error.backup.uploadFailed\":\"備份文件上傳失敗\",\"error.backup.openFileFailed\":\"備份文件打開失敗\",\"error.backup.vaultNotExist\":\"筆記庫不存在，請檢查配置\",\"error.network.unreachable\":\"目標地址無法訪問，請檢查網絡配置\",\"error.network.connectionRefused\":\"連接被拒絕，請確認目標服務是否運行\",\"error.network.timeout\":\"連接超時，請檢查網絡狀態\",\"ui.support.title\":\"支持該項目\",\"ui.support.supportRequest\":\"如果這個項目幫助到您，並且想要它繼續開發，請在以下方式支持我們，感謝您對開源軟件的支持！\",\"ui.support.listTitle\":\"已支持清單\",\"ui.support.noData\":\"暫無記錄\",\"ui.support.item\":\"項目\",\"ui.support.amount\":\"金額\",\"ui.support.time\":\"時間\",\"ui.support.message\":\"留言\",\"ui.support.thanks\":\"感謝大家對 Fast Note Sync 的支持！已支持列表會不定期更新\",\"ui.support.sortDefault\":\"默認 (金額)\",\"ui.support.sortTime\":\"按時間\",\"ui.support.sort\":\"排序\",\"ui.support.buyMeACoffee\":\"請作者喝杯咖啡\",\"ui.support.wechatReward\":\"微信打賞支持\",\"ui.share.invalidLink\":\"無效的分享鏈接。缺少 ID 或 Token。\",\"ui.share.errorTitle\":\"分享錯誤\",\"ui.share.noteNotFound\":\"找不到分享的筆記。\",\"ui.share.poweredByPrefix\":\"由 \",\"ui.share.poweredBySuffix\":\" 提供支持\",\"ui.share.version\":\"版本\",\"ui.share.tabActive\":\"分享中\",\"ui.share.noShares\":\"暫無分享記錄\",\"ui.share.viewShare\":\"查看分享頁\",\"ui.share.cancelShare\":\"取消分享\",\"ui.share.cancelConfirm\":\"確定要取消該筆記的分享嗎？取消後分享鏈接將立即失效。\",\"ui.share.shareNotFound\":\"未找到分享記錄\",\"ui.share.title\":\"分享筆記\",\"ui.share.checking\":\"正在檢查分享狀態...\",\"ui.share.create\":\"開啟分享\",\"ui.share.creating\":\"正在開啟...\",\"ui.share.success\":\"分享成功\",\"ui.share.shortLinkCreate\":\"生成短連結\",\"ui.share.link\":\"分享連結\",\"ui.share.shortLink\":\"短連結\",\"ui.share.copy\":\"複製連結\",\"ui.share.copySuccess\":\"連結已複製到剪貼簿\",\"ui.share.shortLinkCopy\":\"複製短連結\",\"ui.share.cancelSuccess\":\"已取消分享\",\"ui.share.buttonCreating\":\"正在開啟...\",\"ui.share.passwordRequired\":\"需要密碼\",\"ui.share.passwordHint\":\"該分享受密碼保護，請輸入密碼以查看內容。\",\"ui.share.passwordPlaceholder\":\"請輸入密碼...\",\"ui.share.preview\":\"內容預覽\",\"ui.syncLog.title\":\"筆記庫更新日誌\",\"ui.syncLog.vault\":\"筆記庫\",\"ui.syncLog.type\":\"類型\",\"ui.syncLog.action\":\"操作\",\"ui.syncLog.path\":\"檔案路徑\",\"ui.syncLog.size\":\"大小\",\"ui.syncLog.client\":\"客戶端\",\"ui.syncLog.status\":\"狀態\",\"ui.syncLog.message\":\"詳細資訊\",\"ui.syncLog.time\":\"記錄時間\",\"ui.syncLog.changedFields\":\"變更內容\",\"ui.syncLog.noLogs\":\"暫無同步記錄\",\"ui.syncLog.noLogsDescription\":\"沒有找到任何更新記錄\",\"ui.syncLog.description\":\"追溯所有筆記庫中筆記、附件、目錄及配置文件的同步變更記錄\",\"ui.syncLog.allVaults\":\"所有筆記庫\",\"ui.syncLog.allTypes\":\"所有類型\",\"ui.syncLog.allActions\":\"所有操作\",\"ui.syncLog.resetFilters\":\"重置篩選\",\"ui.syncLog.statusSuccess\":\"成功\",\"ui.syncLog.statusFailed\":\"失敗\",\"ui.syncLog.type.note\":\"筆記\",\"ui.syncLog.type.file\":\"附件\",\"ui.syncLog.type.setting\":\"配置\",\"ui.syncLog.type.folder\":\"目錄\",\"ui.syncLog.action.create\":\"新建\",\"ui.syncLog.action.modify\":\"修改\",\"ui.syncLog.action.soft_delete\":\"軟刪除\",\"ui.syncLog.action.delete\":\"永久刪除\",\"ui.syncLog.action.rename\":\"重新命名\",\"ui.syncLog.action.restore\":\"還原\"};export{e as default};\n"
  },
  {
    "path": "frontend/assets/zod-B54Zg8Xp.js",
    "content": "import{V as e}from\"./font-loader-CIrh3KnA.js\";var t,s,r;(s=t||(t={})).assertEqual=e=>{},s.assertIs=function(e){},s.assertNever=function(e){throw new Error},s.arrayToEnum=e=>{const t={};for(const s of e)t[s]=s;return t},s.getValidEnumValues=e=>{const t=s.objectKeys(e).filter(t=>\"number\"!=typeof e[e[t]]),r={};for(const s of t)r[s]=e[s];return s.objectValues(r)},s.objectValues=e=>s.objectKeys(e).map(function(t){return e[t]}),s.objectKeys=\"function\"==typeof Object.keys?e=>Object.keys(e):e=>{const t=[];for(const s in e)Object.prototype.hasOwnProperty.call(e,s)&&t.push(s);return t},s.find=(e,t)=>{for(const s of e)if(t(s))return s},s.isInteger=\"function\"==typeof Number.isInteger?e=>Number.isInteger(e):e=>\"number\"==typeof e&&Number.isFinite(e)&&Math.floor(e)===e,s.joinValues=function(e,t=\" | \"){return e.map(e=>\"string\"==typeof e?`'${e}'`:e).join(t)},s.jsonStringifyReplacer=(e,t)=>\"bigint\"==typeof t?t.toString():t,(r||(r={})).mergeShapes=(e,t)=>({...e,...t});const a=t.arrayToEnum([\"string\",\"nan\",\"number\",\"integer\",\"float\",\"boolean\",\"date\",\"bigint\",\"symbol\",\"function\",\"undefined\",\"null\",\"array\",\"object\",\"unknown\",\"promise\",\"void\",\"never\",\"map\",\"set\"]),i=e=>{switch(typeof e){case\"undefined\":return a.undefined;case\"string\":return a.string;case\"number\":return Number.isNaN(e)?a.nan:a.number;case\"boolean\":return a.boolean;case\"function\":return a.function;case\"bigint\":return a.bigint;case\"symbol\":return a.symbol;case\"object\":return Array.isArray(e)?a.array:null===e?a.null:e.then&&\"function\"==typeof e.then&&e.catch&&\"function\"==typeof e.catch?a.promise:\"undefined\"!=typeof Map&&e instanceof Map?a.map:\"undefined\"!=typeof Set&&e instanceof Set?a.set:\"undefined\"!=typeof Date&&e instanceof Date?a.date:a.object;default:return a.unknown}},n=t.arrayToEnum([\"invalid_type\",\"invalid_literal\",\"custom\",\"invalid_union\",\"invalid_union_discriminator\",\"invalid_enum_value\",\"unrecognized_keys\",\"invalid_arguments\",\"invalid_return_type\",\"invalid_date\",\"invalid_string\",\"too_small\",\"too_big\",\"invalid_intersection_types\",\"not_multiple_of\",\"not_finite\"]);class o extends Error{get errors(){return this.issues}constructor(e){super(),this.issues=[],this.addIssue=e=>{this.issues=[...this.issues,e]},this.addIssues=(e=[])=>{this.issues=[...this.issues,...e]};const t=new.target.prototype;Object.setPrototypeOf?Object.setPrototypeOf(this,t):this.__proto__=t,this.name=\"ZodError\",this.issues=e}format(e){const t=e||function(e){return e.message},s={_errors:[]},r=e=>{for(const a of e.issues)if(\"invalid_union\"===a.code)a.unionErrors.map(r);else if(\"invalid_return_type\"===a.code)r(a.returnTypeError);else if(\"invalid_arguments\"===a.code)r(a.argumentsError);else if(0===a.path.length)s._errors.push(t(a));else{let e=s,r=0;for(;r<a.path.length;){const s=a.path[r];r===a.path.length-1?(e[s]=e[s]||{_errors:[]},e[s]._errors.push(t(a))):e[s]=e[s]||{_errors:[]},e=e[s],r++}}};return r(this),s}static assert(e){if(!(e instanceof o))throw new Error(`Not a ZodError: ${e}`)}toString(){return this.message}get message(){return JSON.stringify(this.issues,t.jsonStringifyReplacer,2)}get isEmpty(){return 0===this.issues.length}flatten(e=e=>e.message){const t={},s=[];for(const r of this.issues)if(r.path.length>0){const s=r.path[0];t[s]=t[s]||[],t[s].push(e(r))}else s.push(e(r));return{formErrors:s,fieldErrors:t}}get formErrors(){return this.flatten()}}o.create=e=>new o(e);const d=(e,s)=>{let r;switch(e.code){case n.invalid_type:r=e.received===a.undefined?\"Required\":`Expected ${e.expected}, received ${e.received}`;break;case n.invalid_literal:r=`Invalid literal value, expected ${JSON.stringify(e.expected,t.jsonStringifyReplacer)}`;break;case n.unrecognized_keys:r=`Unrecognized key(s) in object: ${t.joinValues(e.keys,\", \")}`;break;case n.invalid_union:r=\"Invalid input\";break;case n.invalid_union_discriminator:r=`Invalid discriminator value. Expected ${t.joinValues(e.options)}`;break;case n.invalid_enum_value:r=`Invalid enum value. Expected ${t.joinValues(e.options)}, received '${e.received}'`;break;case n.invalid_arguments:r=\"Invalid function arguments\";break;case n.invalid_return_type:r=\"Invalid function return type\";break;case n.invalid_date:r=\"Invalid date\";break;case n.invalid_string:\"object\"==typeof e.validation?\"includes\"in e.validation?(r=`Invalid input: must include \"${e.validation.includes}\"`,\"number\"==typeof e.validation.position&&(r=`${r} at one or more positions greater than or equal to ${e.validation.position}`)):\"startsWith\"in e.validation?r=`Invalid input: must start with \"${e.validation.startsWith}\"`:\"endsWith\"in e.validation?r=`Invalid input: must end with \"${e.validation.endsWith}\"`:t.assertNever(e.validation):r=\"regex\"!==e.validation?`Invalid ${e.validation}`:\"Invalid\";break;case n.too_small:r=\"array\"===e.type?`Array must contain ${e.exact?\"exactly\":e.inclusive?\"at least\":\"more than\"} ${e.minimum} element(s)`:\"string\"===e.type?`String must contain ${e.exact?\"exactly\":e.inclusive?\"at least\":\"over\"} ${e.minimum} character(s)`:\"number\"===e.type||\"bigint\"===e.type?`Number must be ${e.exact?\"exactly equal to \":e.inclusive?\"greater than or equal to \":\"greater than \"}${e.minimum}`:\"date\"===e.type?`Date must be ${e.exact?\"exactly equal to \":e.inclusive?\"greater than or equal to \":\"greater than \"}${new Date(Number(e.minimum))}`:\"Invalid input\";break;case n.too_big:r=\"array\"===e.type?`Array must contain ${e.exact?\"exactly\":e.inclusive?\"at most\":\"less than\"} ${e.maximum} element(s)`:\"string\"===e.type?`String must contain ${e.exact?\"exactly\":e.inclusive?\"at most\":\"under\"} ${e.maximum} character(s)`:\"number\"===e.type?`Number must be ${e.exact?\"exactly\":e.inclusive?\"less than or equal to\":\"less than\"} ${e.maximum}`:\"bigint\"===e.type?`BigInt must be ${e.exact?\"exactly\":e.inclusive?\"less than or equal to\":\"less than\"} ${e.maximum}`:\"date\"===e.type?`Date must be ${e.exact?\"exactly\":e.inclusive?\"smaller than or equal to\":\"smaller than\"} ${new Date(Number(e.maximum))}`:\"Invalid input\";break;case n.custom:r=\"Invalid input\";break;case n.invalid_intersection_types:r=\"Intersection results could not be merged\";break;case n.not_multiple_of:r=`Number must be a multiple of ${e.multipleOf}`;break;case n.not_finite:r=\"Number must be finite\";break;default:r=s.defaultError,t.assertNever(e)}return{message:r}};let u=d;function l(e,t){const s=u,r=(e=>{const{data:t,path:s,errorMaps:r,issueData:a}=e,i=[...s,...a.path||[]],n={...a,path:i};if(void 0!==a.message)return{...a,path:i,message:a.message};let o=\"\";const d=r.filter(e=>!!e).slice().reverse();for(const u of d)o=u(n,{data:t,defaultError:o}).message;return{...a,path:i,message:o}})({issueData:t,data:e.data,path:e.path,errorMaps:[e.common.contextualErrorMap,e.schemaErrorMap,s,s===d?void 0:d].filter(e=>!!e)});e.common.issues.push(r)}class c{constructor(){this.value=\"valid\"}dirty(){\"valid\"===this.value&&(this.value=\"dirty\")}abort(){\"aborted\"!==this.value&&(this.value=\"aborted\")}static mergeArray(e,t){const s=[];for(const r of t){if(\"aborted\"===r.status)return h;\"dirty\"===r.status&&e.dirty(),s.push(r.value)}return{status:e.value,value:s}}static async mergeObjectAsync(e,t){const s=[];for(const r of t){const e=await r.key,t=await r.value;s.push({key:e,value:t})}return c.mergeObjectSync(e,s)}static mergeObjectSync(e,t){const s={};for(const r of t){const{key:t,value:a}=r;if(\"aborted\"===t.status)return h;if(\"aborted\"===a.status)return h;\"dirty\"===t.status&&e.dirty(),\"dirty\"===a.status&&e.dirty(),\"__proto__\"===t.value||void 0===a.value&&!r.alwaysSet||(s[t.value]=a.value)}return{status:e.value,value:s}}}const h=Object.freeze({status:\"aborted\"}),f=e=>({status:\"dirty\",value:e}),m=e=>({status:\"valid\",value:e}),p=e=>\"aborted\"===e.status,y=e=>\"dirty\"===e.status,v=e=>\"valid\"===e.status,_=e=>\"undefined\"!=typeof Promise&&e instanceof Promise;var g,b;(b=g||(g={})).errToObj=e=>\"string\"==typeof e?{message:e}:e||{},b.toString=e=>\"string\"==typeof e?e:null==e?void 0:e.message;class k{constructor(e,t,s,r){this._cachedPath=[],this.parent=e,this.data=t,this._path=s,this._key=r}get path(){return this._cachedPath.length||(Array.isArray(this._key)?this._cachedPath.push(...this._path,...this._key):this._cachedPath.push(...this._path,this._key)),this._cachedPath}}const x=(e,t)=>{if(v(t))return{success:!0,data:t.value};if(!e.common.issues.length)throw new Error(\"Validation failed but no issues detected.\");return{success:!1,get error(){if(this._error)return this._error;const t=new o(e.common.issues);return this._error=t,this._error}}};function w(e){if(!e)return{};const{errorMap:t,invalid_type_error:s,required_error:r,description:a}=e;if(t&&(s||r))throw new Error('Can\\'t use \"invalid_type_error\" or \"required_error\" in conjunction with custom error map.');if(t)return{errorMap:t,description:a};return{errorMap:(t,a)=>{const{message:i}=e;return\"invalid_enum_value\"===t.code?{message:i??a.defaultError}:void 0===a.data?{message:i??r??a.defaultError}:\"invalid_type\"!==t.code?{message:a.defaultError}:{message:i??s??a.defaultError}},description:a}}class A{get description(){return this._def.description}_getType(e){return i(e.data)}_getOrReturnCtx(e,t){return t||{common:e.parent.common,data:e.data,parsedType:i(e.data),schemaErrorMap:this._def.errorMap,path:e.path,parent:e.parent}}_processInputParams(e){return{status:new c,ctx:{common:e.parent.common,data:e.data,parsedType:i(e.data),schemaErrorMap:this._def.errorMap,path:e.path,parent:e.parent}}}_parseSync(e){const t=this._parse(e);if(_(t))throw new Error(\"Synchronous parse encountered promise.\");return t}_parseAsync(e){const t=this._parse(e);return Promise.resolve(t)}parse(e,t){const s=this.safeParse(e,t);if(s.success)return s.data;throw s.error}safeParse(e,t){const s={common:{issues:[],async:(null==t?void 0:t.async)??!1,contextualErrorMap:null==t?void 0:t.errorMap},path:(null==t?void 0:t.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:i(e)},r=this._parseSync({data:e,path:s.path,parent:s});return x(s,r)}\"~validate\"(e){var t,s;const r={common:{issues:[],async:!!this[\"~standard\"].async},path:[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:i(e)};if(!this[\"~standard\"].async)try{const t=this._parseSync({data:e,path:[],parent:r});return v(t)?{value:t.value}:{issues:r.common.issues}}catch(a){(null==(s=null==(t=null==a?void 0:a.message)?void 0:t.toLowerCase())?void 0:s.includes(\"encountered\"))&&(this[\"~standard\"].async=!0),r.common={issues:[],async:!0}}return this._parseAsync({data:e,path:[],parent:r}).then(e=>v(e)?{value:e.value}:{issues:r.common.issues})}async parseAsync(e,t){const s=await this.safeParseAsync(e,t);if(s.success)return s.data;throw s.error}async safeParseAsync(e,t){const s={common:{issues:[],contextualErrorMap:null==t?void 0:t.errorMap,async:!0},path:(null==t?void 0:t.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:i(e)},r=this._parse({data:e,path:s.path,parent:s}),a=await(_(r)?r:Promise.resolve(r));return x(s,a)}refine(e,t){const s=e=>\"string\"==typeof t||void 0===t?{message:t}:\"function\"==typeof t?t(e):t;return this._refinement((t,r)=>{const a=e(t),i=()=>r.addIssue({code:n.custom,...s(t)});return\"undefined\"!=typeof Promise&&a instanceof Promise?a.then(e=>!!e||(i(),!1)):!!a||(i(),!1)})}refinement(e,t){return this._refinement((s,r)=>!!e(s)||(r.addIssue(\"function\"==typeof t?t(s,r):t),!1))}_refinement(e){return new xe({schema:this,typeName:Ze.ZodEffects,effect:{type:\"refinement\",refinement:e}})}superRefine(e){return this._refinement(e)}constructor(e){this.spa=this.safeParseAsync,this._def=e,this.parse=this.parse.bind(this),this.safeParse=this.safeParse.bind(this),this.parseAsync=this.parseAsync.bind(this),this.safeParseAsync=this.safeParseAsync.bind(this),this.spa=this.spa.bind(this),this.refine=this.refine.bind(this),this.refinement=this.refinement.bind(this),this.superRefine=this.superRefine.bind(this),this.optional=this.optional.bind(this),this.nullable=this.nullable.bind(this),this.nullish=this.nullish.bind(this),this.array=this.array.bind(this),this.promise=this.promise.bind(this),this.or=this.or.bind(this),this.and=this.and.bind(this),this.transform=this.transform.bind(this),this.brand=this.brand.bind(this),this.default=this.default.bind(this),this.catch=this.catch.bind(this),this.describe=this.describe.bind(this),this.pipe=this.pipe.bind(this),this.readonly=this.readonly.bind(this),this.isNullable=this.isNullable.bind(this),this.isOptional=this.isOptional.bind(this),this[\"~standard\"]={version:1,vendor:\"zod\",validate:e=>this[\"~validate\"](e)}}optional(){return we.create(this,this._def)}nullable(){return Ae.create(this,this._def)}nullish(){return this.nullable().optional()}array(){return oe.create(this)}promise(){return ke.create(this,this._def)}or(e){return le.create([this,e],this._def)}and(e){return he.create(this,e,this._def)}transform(e){return new xe({...w(this._def),schema:this,typeName:Ze.ZodEffects,effect:{type:\"transform\",transform:e}})}default(e){const t=\"function\"==typeof e?e:()=>e;return new Oe({...w(this._def),innerType:this,defaultValue:t,typeName:Ze.ZodDefault})}brand(){return new Ce({typeName:Ze.ZodBranded,type:this,...w(this._def)})}catch(e){const t=\"function\"==typeof e?e:()=>e;return new Se({...w(this._def),innerType:this,catchValue:t,typeName:Ze.ZodCatch})}describe(e){return new(0,this.constructor)({...this._def,description:e})}pipe(e){return Te.create(this,e)}readonly(){return Fe.create(this)}isOptional(){return this.safeParse(void 0).success}isNullable(){return this.safeParse(null).success}}const O=/^c[^\\s-]{8,}$/i,S=/^[0-9a-z]+$/,V=/^[0-9A-HJKMNP-TV-Z]{26}$/i,C=/^[0-9a-fA-F]{8}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{12}$/i,T=/^[a-z0-9_-]{21}$/i,F=/^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]*$/,Z=/^[-+]?P(?!$)(?:(?:[-+]?\\d+Y)|(?:[-+]?\\d+[.,]\\d+Y$))?(?:(?:[-+]?\\d+M)|(?:[-+]?\\d+[.,]\\d+M$))?(?:(?:[-+]?\\d+W)|(?:[-+]?\\d+[.,]\\d+W$))?(?:(?:[-+]?\\d+D)|(?:[-+]?\\d+[.,]\\d+D$))?(?:T(?=[\\d+-])(?:(?:[-+]?\\d+H)|(?:[-+]?\\d+[.,]\\d+H$))?(?:(?:[-+]?\\d+M)|(?:[-+]?\\d+[.,]\\d+M$))?(?:[-+]?\\d+(?:[.,]\\d+)?S)?)??$/,N=/^(?!\\.)(?!.*\\.\\.)([A-Z0-9_'+\\-\\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\\-]*\\.)+[A-Z]{2,}$/i;let j;const E=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,D=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\/(3[0-2]|[12]?[0-9])$/,I=/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/,R=/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/,P=/^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/,$=/^([0-9a-zA-Z-_]{4})*(([0-9a-zA-Z-_]{2}(==)?)|([0-9a-zA-Z-_]{3}(=)?))?$/,L=\"((\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\\\d|30)|(02)-(0[1-9]|1\\\\d|2[0-8])))\",M=new RegExp(`^${L}$`);function U(e){let t=\"[0-5]\\\\d\";e.precision?t=`${t}\\\\.\\\\d{${e.precision}}`:null==e.precision&&(t=`${t}(\\\\.\\\\d+)?`);return`([01]\\\\d|2[0-3]):[0-5]\\\\d(:${t})${e.precision?\"+\":\"?\"}`}function z(e){return new RegExp(`^${U(e)}$`)}function B(e){let t=`${L}T${U(e)}`;const s=[];return s.push(e.local?\"Z?\":\"Z\"),e.offset&&s.push(\"([+-]\\\\d{2}:?\\\\d{2})\"),t=`${t}(${s.join(\"|\")})`,new RegExp(`^${t}$`)}function W(e,t){return!(\"v4\"!==t&&t||!E.test(e))||!(\"v6\"!==t&&t||!I.test(e))}function K(e,t){if(!F.test(e))return!1;try{const[s]=e.split(\".\");if(!s)return!1;const r=s.replace(/-/g,\"+\").replace(/_/g,\"/\").padEnd(s.length+(4-s.length%4)%4,\"=\"),a=JSON.parse(atob(r));return\"object\"==typeof a&&null!==a&&((!(\"typ\"in a)||\"JWT\"===(null==a?void 0:a.typ))&&(!!a.alg&&(!t||a.alg===t)))}catch{return!1}}function q(e,t){return!(\"v4\"!==t&&t||!D.test(e))||!(\"v6\"!==t&&t||!R.test(e))}class H extends A{_parse(e){this._def.coerce&&(e.data=String(e.data));if(this._getType(e)!==a.string){const t=this._getOrReturnCtx(e);return l(t,{code:n.invalid_type,expected:a.string,received:t.parsedType}),h}const s=new c;let r;for(const a of this._def.checks)if(\"min\"===a.kind)e.data.length<a.value&&(r=this._getOrReturnCtx(e,r),l(r,{code:n.too_small,minimum:a.value,type:\"string\",inclusive:!0,exact:!1,message:a.message}),s.dirty());else if(\"max\"===a.kind)e.data.length>a.value&&(r=this._getOrReturnCtx(e,r),l(r,{code:n.too_big,maximum:a.value,type:\"string\",inclusive:!0,exact:!1,message:a.message}),s.dirty());else if(\"length\"===a.kind){const t=e.data.length>a.value,i=e.data.length<a.value;(t||i)&&(r=this._getOrReturnCtx(e,r),t?l(r,{code:n.too_big,maximum:a.value,type:\"string\",inclusive:!0,exact:!0,message:a.message}):i&&l(r,{code:n.too_small,minimum:a.value,type:\"string\",inclusive:!0,exact:!0,message:a.message}),s.dirty())}else if(\"email\"===a.kind)N.test(e.data)||(r=this._getOrReturnCtx(e,r),l(r,{validation:\"email\",code:n.invalid_string,message:a.message}),s.dirty());else if(\"emoji\"===a.kind)j||(j=new RegExp(\"^(\\\\p{Extended_Pictographic}|\\\\p{Emoji_Component})+$\",\"u\")),j.test(e.data)||(r=this._getOrReturnCtx(e,r),l(r,{validation:\"emoji\",code:n.invalid_string,message:a.message}),s.dirty());else if(\"uuid\"===a.kind)C.test(e.data)||(r=this._getOrReturnCtx(e,r),l(r,{validation:\"uuid\",code:n.invalid_string,message:a.message}),s.dirty());else if(\"nanoid\"===a.kind)T.test(e.data)||(r=this._getOrReturnCtx(e,r),l(r,{validation:\"nanoid\",code:n.invalid_string,message:a.message}),s.dirty());else if(\"cuid\"===a.kind)O.test(e.data)||(r=this._getOrReturnCtx(e,r),l(r,{validation:\"cuid\",code:n.invalid_string,message:a.message}),s.dirty());else if(\"cuid2\"===a.kind)S.test(e.data)||(r=this._getOrReturnCtx(e,r),l(r,{validation:\"cuid2\",code:n.invalid_string,message:a.message}),s.dirty());else if(\"ulid\"===a.kind)V.test(e.data)||(r=this._getOrReturnCtx(e,r),l(r,{validation:\"ulid\",code:n.invalid_string,message:a.message}),s.dirty());else if(\"url\"===a.kind)try{new URL(e.data)}catch{r=this._getOrReturnCtx(e,r),l(r,{validation:\"url\",code:n.invalid_string,message:a.message}),s.dirty()}else if(\"regex\"===a.kind){a.regex.lastIndex=0;a.regex.test(e.data)||(r=this._getOrReturnCtx(e,r),l(r,{validation:\"regex\",code:n.invalid_string,message:a.message}),s.dirty())}else if(\"trim\"===a.kind)e.data=e.data.trim();else if(\"includes\"===a.kind)e.data.includes(a.value,a.position)||(r=this._getOrReturnCtx(e,r),l(r,{code:n.invalid_string,validation:{includes:a.value,position:a.position},message:a.message}),s.dirty());else if(\"toLowerCase\"===a.kind)e.data=e.data.toLowerCase();else if(\"toUpperCase\"===a.kind)e.data=e.data.toUpperCase();else if(\"startsWith\"===a.kind)e.data.startsWith(a.value)||(r=this._getOrReturnCtx(e,r),l(r,{code:n.invalid_string,validation:{startsWith:a.value},message:a.message}),s.dirty());else if(\"endsWith\"===a.kind)e.data.endsWith(a.value)||(r=this._getOrReturnCtx(e,r),l(r,{code:n.invalid_string,validation:{endsWith:a.value},message:a.message}),s.dirty());else if(\"datetime\"===a.kind){B(a).test(e.data)||(r=this._getOrReturnCtx(e,r),l(r,{code:n.invalid_string,validation:\"datetime\",message:a.message}),s.dirty())}else if(\"date\"===a.kind){M.test(e.data)||(r=this._getOrReturnCtx(e,r),l(r,{code:n.invalid_string,validation:\"date\",message:a.message}),s.dirty())}else if(\"time\"===a.kind){z(a).test(e.data)||(r=this._getOrReturnCtx(e,r),l(r,{code:n.invalid_string,validation:\"time\",message:a.message}),s.dirty())}else\"duration\"===a.kind?Z.test(e.data)||(r=this._getOrReturnCtx(e,r),l(r,{validation:\"duration\",code:n.invalid_string,message:a.message}),s.dirty()):\"ip\"===a.kind?W(e.data,a.version)||(r=this._getOrReturnCtx(e,r),l(r,{validation:\"ip\",code:n.invalid_string,message:a.message}),s.dirty()):\"jwt\"===a.kind?K(e.data,a.alg)||(r=this._getOrReturnCtx(e,r),l(r,{validation:\"jwt\",code:n.invalid_string,message:a.message}),s.dirty()):\"cidr\"===a.kind?q(e.data,a.version)||(r=this._getOrReturnCtx(e,r),l(r,{validation:\"cidr\",code:n.invalid_string,message:a.message}),s.dirty()):\"base64\"===a.kind?P.test(e.data)||(r=this._getOrReturnCtx(e,r),l(r,{validation:\"base64\",code:n.invalid_string,message:a.message}),s.dirty()):\"base64url\"===a.kind?$.test(e.data)||(r=this._getOrReturnCtx(e,r),l(r,{validation:\"base64url\",code:n.invalid_string,message:a.message}),s.dirty()):t.assertNever(a);return{status:s.value,value:e.data}}_regex(e,t,s){return this.refinement(t=>e.test(t),{validation:t,code:n.invalid_string,...g.errToObj(s)})}_addCheck(e){return new H({...this._def,checks:[...this._def.checks,e]})}email(e){return this._addCheck({kind:\"email\",...g.errToObj(e)})}url(e){return this._addCheck({kind:\"url\",...g.errToObj(e)})}emoji(e){return this._addCheck({kind:\"emoji\",...g.errToObj(e)})}uuid(e){return this._addCheck({kind:\"uuid\",...g.errToObj(e)})}nanoid(e){return this._addCheck({kind:\"nanoid\",...g.errToObj(e)})}cuid(e){return this._addCheck({kind:\"cuid\",...g.errToObj(e)})}cuid2(e){return this._addCheck({kind:\"cuid2\",...g.errToObj(e)})}ulid(e){return this._addCheck({kind:\"ulid\",...g.errToObj(e)})}base64(e){return this._addCheck({kind:\"base64\",...g.errToObj(e)})}base64url(e){return this._addCheck({kind:\"base64url\",...g.errToObj(e)})}jwt(e){return this._addCheck({kind:\"jwt\",...g.errToObj(e)})}ip(e){return this._addCheck({kind:\"ip\",...g.errToObj(e)})}cidr(e){return this._addCheck({kind:\"cidr\",...g.errToObj(e)})}datetime(e){return\"string\"==typeof e?this._addCheck({kind:\"datetime\",precision:null,offset:!1,local:!1,message:e}):this._addCheck({kind:\"datetime\",precision:void 0===(null==e?void 0:e.precision)?null:null==e?void 0:e.precision,offset:(null==e?void 0:e.offset)??!1,local:(null==e?void 0:e.local)??!1,...g.errToObj(null==e?void 0:e.message)})}date(e){return this._addCheck({kind:\"date\",message:e})}time(e){return\"string\"==typeof e?this._addCheck({kind:\"time\",precision:null,message:e}):this._addCheck({kind:\"time\",precision:void 0===(null==e?void 0:e.precision)?null:null==e?void 0:e.precision,...g.errToObj(null==e?void 0:e.message)})}duration(e){return this._addCheck({kind:\"duration\",...g.errToObj(e)})}regex(e,t){return this._addCheck({kind:\"regex\",regex:e,...g.errToObj(t)})}includes(e,t){return this._addCheck({kind:\"includes\",value:e,position:null==t?void 0:t.position,...g.errToObj(null==t?void 0:t.message)})}startsWith(e,t){return this._addCheck({kind:\"startsWith\",value:e,...g.errToObj(t)})}endsWith(e,t){return this._addCheck({kind:\"endsWith\",value:e,...g.errToObj(t)})}min(e,t){return this._addCheck({kind:\"min\",value:e,...g.errToObj(t)})}max(e,t){return this._addCheck({kind:\"max\",value:e,...g.errToObj(t)})}length(e,t){return this._addCheck({kind:\"length\",value:e,...g.errToObj(t)})}nonempty(e){return this.min(1,g.errToObj(e))}trim(){return new H({...this._def,checks:[...this._def.checks,{kind:\"trim\"}]})}toLowerCase(){return new H({...this._def,checks:[...this._def.checks,{kind:\"toLowerCase\"}]})}toUpperCase(){return new H({...this._def,checks:[...this._def.checks,{kind:\"toUpperCase\"}]})}get isDatetime(){return!!this._def.checks.find(e=>\"datetime\"===e.kind)}get isDate(){return!!this._def.checks.find(e=>\"date\"===e.kind)}get isTime(){return!!this._def.checks.find(e=>\"time\"===e.kind)}get isDuration(){return!!this._def.checks.find(e=>\"duration\"===e.kind)}get isEmail(){return!!this._def.checks.find(e=>\"email\"===e.kind)}get isURL(){return!!this._def.checks.find(e=>\"url\"===e.kind)}get isEmoji(){return!!this._def.checks.find(e=>\"emoji\"===e.kind)}get isUUID(){return!!this._def.checks.find(e=>\"uuid\"===e.kind)}get isNANOID(){return!!this._def.checks.find(e=>\"nanoid\"===e.kind)}get isCUID(){return!!this._def.checks.find(e=>\"cuid\"===e.kind)}get isCUID2(){return!!this._def.checks.find(e=>\"cuid2\"===e.kind)}get isULID(){return!!this._def.checks.find(e=>\"ulid\"===e.kind)}get isIP(){return!!this._def.checks.find(e=>\"ip\"===e.kind)}get isCIDR(){return!!this._def.checks.find(e=>\"cidr\"===e.kind)}get isBase64(){return!!this._def.checks.find(e=>\"base64\"===e.kind)}get isBase64url(){return!!this._def.checks.find(e=>\"base64url\"===e.kind)}get minLength(){let e=null;for(const t of this._def.checks)\"min\"===t.kind&&(null===e||t.value>e)&&(e=t.value);return e}get maxLength(){let e=null;for(const t of this._def.checks)\"max\"===t.kind&&(null===e||t.value<e)&&(e=t.value);return e}}function J(e,t){const s=(e.toString().split(\".\")[1]||\"\").length,r=(t.toString().split(\".\")[1]||\"\").length,a=s>r?s:r;return Number.parseInt(e.toFixed(a).replace(\".\",\"\"))%Number.parseInt(t.toFixed(a).replace(\".\",\"\"))/10**a}H.create=e=>new H({checks:[],typeName:Ze.ZodString,coerce:(null==e?void 0:e.coerce)??!1,...w(e)});class Y extends A{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte,this.step=this.multipleOf}_parse(e){this._def.coerce&&(e.data=Number(e.data));if(this._getType(e)!==a.number){const t=this._getOrReturnCtx(e);return l(t,{code:n.invalid_type,expected:a.number,received:t.parsedType}),h}let s;const r=new c;for(const a of this._def.checks)if(\"int\"===a.kind)t.isInteger(e.data)||(s=this._getOrReturnCtx(e,s),l(s,{code:n.invalid_type,expected:\"integer\",received:\"float\",message:a.message}),r.dirty());else if(\"min\"===a.kind){(a.inclusive?e.data<a.value:e.data<=a.value)&&(s=this._getOrReturnCtx(e,s),l(s,{code:n.too_small,minimum:a.value,type:\"number\",inclusive:a.inclusive,exact:!1,message:a.message}),r.dirty())}else if(\"max\"===a.kind){(a.inclusive?e.data>a.value:e.data>=a.value)&&(s=this._getOrReturnCtx(e,s),l(s,{code:n.too_big,maximum:a.value,type:\"number\",inclusive:a.inclusive,exact:!1,message:a.message}),r.dirty())}else\"multipleOf\"===a.kind?0!==J(e.data,a.value)&&(s=this._getOrReturnCtx(e,s),l(s,{code:n.not_multiple_of,multipleOf:a.value,message:a.message}),r.dirty()):\"finite\"===a.kind?Number.isFinite(e.data)||(s=this._getOrReturnCtx(e,s),l(s,{code:n.not_finite,message:a.message}),r.dirty()):t.assertNever(a);return{status:r.value,value:e.data}}gte(e,t){return this.setLimit(\"min\",e,!0,g.toString(t))}gt(e,t){return this.setLimit(\"min\",e,!1,g.toString(t))}lte(e,t){return this.setLimit(\"max\",e,!0,g.toString(t))}lt(e,t){return this.setLimit(\"max\",e,!1,g.toString(t))}setLimit(e,t,s,r){return new Y({...this._def,checks:[...this._def.checks,{kind:e,value:t,inclusive:s,message:g.toString(r)}]})}_addCheck(e){return new Y({...this._def,checks:[...this._def.checks,e]})}int(e){return this._addCheck({kind:\"int\",message:g.toString(e)})}positive(e){return this._addCheck({kind:\"min\",value:0,inclusive:!1,message:g.toString(e)})}negative(e){return this._addCheck({kind:\"max\",value:0,inclusive:!1,message:g.toString(e)})}nonpositive(e){return this._addCheck({kind:\"max\",value:0,inclusive:!0,message:g.toString(e)})}nonnegative(e){return this._addCheck({kind:\"min\",value:0,inclusive:!0,message:g.toString(e)})}multipleOf(e,t){return this._addCheck({kind:\"multipleOf\",value:e,message:g.toString(t)})}finite(e){return this._addCheck({kind:\"finite\",message:g.toString(e)})}safe(e){return this._addCheck({kind:\"min\",inclusive:!0,value:Number.MIN_SAFE_INTEGER,message:g.toString(e)})._addCheck({kind:\"max\",inclusive:!0,value:Number.MAX_SAFE_INTEGER,message:g.toString(e)})}get minValue(){let e=null;for(const t of this._def.checks)\"min\"===t.kind&&(null===e||t.value>e)&&(e=t.value);return e}get maxValue(){let e=null;for(const t of this._def.checks)\"max\"===t.kind&&(null===e||t.value<e)&&(e=t.value);return e}get isInt(){return!!this._def.checks.find(e=>\"int\"===e.kind||\"multipleOf\"===e.kind&&t.isInteger(e.value))}get isFinite(){let e=null,t=null;for(const s of this._def.checks){if(\"finite\"===s.kind||\"int\"===s.kind||\"multipleOf\"===s.kind)return!0;\"min\"===s.kind?(null===t||s.value>t)&&(t=s.value):\"max\"===s.kind&&(null===e||s.value<e)&&(e=s.value)}return Number.isFinite(t)&&Number.isFinite(e)}}Y.create=e=>new Y({checks:[],typeName:Ze.ZodNumber,coerce:(null==e?void 0:e.coerce)||!1,...w(e)});class G extends A{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte}_parse(e){if(this._def.coerce)try{e.data=BigInt(e.data)}catch{return this._getInvalidInput(e)}if(this._getType(e)!==a.bigint)return this._getInvalidInput(e);let s;const r=new c;for(const a of this._def.checks)if(\"min\"===a.kind){(a.inclusive?e.data<a.value:e.data<=a.value)&&(s=this._getOrReturnCtx(e,s),l(s,{code:n.too_small,type:\"bigint\",minimum:a.value,inclusive:a.inclusive,message:a.message}),r.dirty())}else if(\"max\"===a.kind){(a.inclusive?e.data>a.value:e.data>=a.value)&&(s=this._getOrReturnCtx(e,s),l(s,{code:n.too_big,type:\"bigint\",maximum:a.value,inclusive:a.inclusive,message:a.message}),r.dirty())}else\"multipleOf\"===a.kind?e.data%a.value!==BigInt(0)&&(s=this._getOrReturnCtx(e,s),l(s,{code:n.not_multiple_of,multipleOf:a.value,message:a.message}),r.dirty()):t.assertNever(a);return{status:r.value,value:e.data}}_getInvalidInput(e){const t=this._getOrReturnCtx(e);return l(t,{code:n.invalid_type,expected:a.bigint,received:t.parsedType}),h}gte(e,t){return this.setLimit(\"min\",e,!0,g.toString(t))}gt(e,t){return this.setLimit(\"min\",e,!1,g.toString(t))}lte(e,t){return this.setLimit(\"max\",e,!0,g.toString(t))}lt(e,t){return this.setLimit(\"max\",e,!1,g.toString(t))}setLimit(e,t,s,r){return new G({...this._def,checks:[...this._def.checks,{kind:e,value:t,inclusive:s,message:g.toString(r)}]})}_addCheck(e){return new G({...this._def,checks:[...this._def.checks,e]})}positive(e){return this._addCheck({kind:\"min\",value:BigInt(0),inclusive:!1,message:g.toString(e)})}negative(e){return this._addCheck({kind:\"max\",value:BigInt(0),inclusive:!1,message:g.toString(e)})}nonpositive(e){return this._addCheck({kind:\"max\",value:BigInt(0),inclusive:!0,message:g.toString(e)})}nonnegative(e){return this._addCheck({kind:\"min\",value:BigInt(0),inclusive:!0,message:g.toString(e)})}multipleOf(e,t){return this._addCheck({kind:\"multipleOf\",value:e,message:g.toString(t)})}get minValue(){let e=null;for(const t of this._def.checks)\"min\"===t.kind&&(null===e||t.value>e)&&(e=t.value);return e}get maxValue(){let e=null;for(const t of this._def.checks)\"max\"===t.kind&&(null===e||t.value<e)&&(e=t.value);return e}}G.create=e=>new G({checks:[],typeName:Ze.ZodBigInt,coerce:(null==e?void 0:e.coerce)??!1,...w(e)});class X extends A{_parse(e){this._def.coerce&&(e.data=Boolean(e.data));if(this._getType(e)!==a.boolean){const t=this._getOrReturnCtx(e);return l(t,{code:n.invalid_type,expected:a.boolean,received:t.parsedType}),h}return m(e.data)}}X.create=e=>new X({typeName:Ze.ZodBoolean,coerce:(null==e?void 0:e.coerce)||!1,...w(e)});class Q extends A{_parse(e){this._def.coerce&&(e.data=new Date(e.data));if(this._getType(e)!==a.date){const t=this._getOrReturnCtx(e);return l(t,{code:n.invalid_type,expected:a.date,received:t.parsedType}),h}if(Number.isNaN(e.data.getTime())){return l(this._getOrReturnCtx(e),{code:n.invalid_date}),h}const s=new c;let r;for(const a of this._def.checks)\"min\"===a.kind?e.data.getTime()<a.value&&(r=this._getOrReturnCtx(e,r),l(r,{code:n.too_small,message:a.message,inclusive:!0,exact:!1,minimum:a.value,type:\"date\"}),s.dirty()):\"max\"===a.kind?e.data.getTime()>a.value&&(r=this._getOrReturnCtx(e,r),l(r,{code:n.too_big,message:a.message,inclusive:!0,exact:!1,maximum:a.value,type:\"date\"}),s.dirty()):t.assertNever(a);return{status:s.value,value:new Date(e.data.getTime())}}_addCheck(e){return new Q({...this._def,checks:[...this._def.checks,e]})}min(e,t){return this._addCheck({kind:\"min\",value:e.getTime(),message:g.toString(t)})}max(e,t){return this._addCheck({kind:\"max\",value:e.getTime(),message:g.toString(t)})}get minDate(){let e=null;for(const t of this._def.checks)\"min\"===t.kind&&(null===e||t.value>e)&&(e=t.value);return null!=e?new Date(e):null}get maxDate(){let e=null;for(const t of this._def.checks)\"max\"===t.kind&&(null===e||t.value<e)&&(e=t.value);return null!=e?new Date(e):null}}Q.create=e=>new Q({checks:[],coerce:(null==e?void 0:e.coerce)||!1,typeName:Ze.ZodDate,...w(e)});class ee extends A{_parse(e){if(this._getType(e)!==a.symbol){const t=this._getOrReturnCtx(e);return l(t,{code:n.invalid_type,expected:a.symbol,received:t.parsedType}),h}return m(e.data)}}ee.create=e=>new ee({typeName:Ze.ZodSymbol,...w(e)});class te extends A{_parse(e){if(this._getType(e)!==a.undefined){const t=this._getOrReturnCtx(e);return l(t,{code:n.invalid_type,expected:a.undefined,received:t.parsedType}),h}return m(e.data)}}te.create=e=>new te({typeName:Ze.ZodUndefined,...w(e)});class se extends A{_parse(e){if(this._getType(e)!==a.null){const t=this._getOrReturnCtx(e);return l(t,{code:n.invalid_type,expected:a.null,received:t.parsedType}),h}return m(e.data)}}se.create=e=>new se({typeName:Ze.ZodNull,...w(e)});class re extends A{constructor(){super(...arguments),this._any=!0}_parse(e){return m(e.data)}}re.create=e=>new re({typeName:Ze.ZodAny,...w(e)});class ae extends A{constructor(){super(...arguments),this._unknown=!0}_parse(e){return m(e.data)}}ae.create=e=>new ae({typeName:Ze.ZodUnknown,...w(e)});class ie extends A{_parse(e){const t=this._getOrReturnCtx(e);return l(t,{code:n.invalid_type,expected:a.never,received:t.parsedType}),h}}ie.create=e=>new ie({typeName:Ze.ZodNever,...w(e)});class ne extends A{_parse(e){if(this._getType(e)!==a.undefined){const t=this._getOrReturnCtx(e);return l(t,{code:n.invalid_type,expected:a.void,received:t.parsedType}),h}return m(e.data)}}ne.create=e=>new ne({typeName:Ze.ZodVoid,...w(e)});class oe extends A{_parse(e){const{ctx:t,status:s}=this._processInputParams(e),r=this._def;if(t.parsedType!==a.array)return l(t,{code:n.invalid_type,expected:a.array,received:t.parsedType}),h;if(null!==r.exactLength){const e=t.data.length>r.exactLength.value,a=t.data.length<r.exactLength.value;(e||a)&&(l(t,{code:e?n.too_big:n.too_small,minimum:a?r.exactLength.value:void 0,maximum:e?r.exactLength.value:void 0,type:\"array\",inclusive:!0,exact:!0,message:r.exactLength.message}),s.dirty())}if(null!==r.minLength&&t.data.length<r.minLength.value&&(l(t,{code:n.too_small,minimum:r.minLength.value,type:\"array\",inclusive:!0,exact:!1,message:r.minLength.message}),s.dirty()),null!==r.maxLength&&t.data.length>r.maxLength.value&&(l(t,{code:n.too_big,maximum:r.maxLength.value,type:\"array\",inclusive:!0,exact:!1,message:r.maxLength.message}),s.dirty()),t.common.async)return Promise.all([...t.data].map((e,s)=>r.type._parseAsync(new k(t,e,t.path,s)))).then(e=>c.mergeArray(s,e));const i=[...t.data].map((e,s)=>r.type._parseSync(new k(t,e,t.path,s)));return c.mergeArray(s,i)}get element(){return this._def.type}min(e,t){return new oe({...this._def,minLength:{value:e,message:g.toString(t)}})}max(e,t){return new oe({...this._def,maxLength:{value:e,message:g.toString(t)}})}length(e,t){return new oe({...this._def,exactLength:{value:e,message:g.toString(t)}})}nonempty(e){return this.min(1,e)}}function de(e){if(e instanceof ue){const t={};for(const s in e.shape){const r=e.shape[s];t[s]=we.create(de(r))}return new ue({...e._def,shape:()=>t})}return e instanceof oe?new oe({...e._def,type:de(e.element)}):e instanceof we?we.create(de(e.unwrap())):e instanceof Ae?Ae.create(de(e.unwrap())):e instanceof fe?fe.create(e.items.map(e=>de(e))):e}oe.create=(e,t)=>new oe({type:e,minLength:null,maxLength:null,exactLength:null,typeName:Ze.ZodArray,...w(t)});class ue extends A{constructor(){super(...arguments),this._cached=null,this.nonstrict=this.passthrough,this.augment=this.extend}_getCached(){if(null!==this._cached)return this._cached;const e=this._def.shape(),s=t.objectKeys(e);return this._cached={shape:e,keys:s},this._cached}_parse(e){if(this._getType(e)!==a.object){const t=this._getOrReturnCtx(e);return l(t,{code:n.invalid_type,expected:a.object,received:t.parsedType}),h}const{status:t,ctx:s}=this._processInputParams(e),{shape:r,keys:i}=this._getCached(),o=[];if(!(this._def.catchall instanceof ie&&\"strip\"===this._def.unknownKeys))for(const a in s.data)i.includes(a)||o.push(a);const d=[];for(const a of i){const e=r[a],t=s.data[a];d.push({key:{status:\"valid\",value:a},value:e._parse(new k(s,t,s.path,a)),alwaysSet:a in s.data})}if(this._def.catchall instanceof ie){const e=this._def.unknownKeys;if(\"passthrough\"===e)for(const t of o)d.push({key:{status:\"valid\",value:t},value:{status:\"valid\",value:s.data[t]}});else if(\"strict\"===e)o.length>0&&(l(s,{code:n.unrecognized_keys,keys:o}),t.dirty());else if(\"strip\"!==e)throw new Error(\"Internal ZodObject error: invalid unknownKeys value.\")}else{const e=this._def.catchall;for(const t of o){const r=s.data[t];d.push({key:{status:\"valid\",value:t},value:e._parse(new k(s,r,s.path,t)),alwaysSet:t in s.data})}}return s.common.async?Promise.resolve().then(async()=>{const e=[];for(const t of d){const s=await t.key,r=await t.value;e.push({key:s,value:r,alwaysSet:t.alwaysSet})}return e}).then(e=>c.mergeObjectSync(t,e)):c.mergeObjectSync(t,d)}get shape(){return this._def.shape()}strict(e){return g.errToObj,new ue({...this._def,unknownKeys:\"strict\",...void 0!==e?{errorMap:(t,s)=>{var r,a;const i=(null==(a=(r=this._def).errorMap)?void 0:a.call(r,t,s).message)??s.defaultError;return\"unrecognized_keys\"===t.code?{message:g.errToObj(e).message??i}:{message:i}}}:{}})}strip(){return new ue({...this._def,unknownKeys:\"strip\"})}passthrough(){return new ue({...this._def,unknownKeys:\"passthrough\"})}extend(e){return new ue({...this._def,shape:()=>({...this._def.shape(),...e})})}merge(e){return new ue({unknownKeys:e._def.unknownKeys,catchall:e._def.catchall,shape:()=>({...this._def.shape(),...e._def.shape()}),typeName:Ze.ZodObject})}setKey(e,t){return this.augment({[e]:t})}catchall(e){return new ue({...this._def,catchall:e})}pick(e){const s={};for(const r of t.objectKeys(e))e[r]&&this.shape[r]&&(s[r]=this.shape[r]);return new ue({...this._def,shape:()=>s})}omit(e){const s={};for(const r of t.objectKeys(this.shape))e[r]||(s[r]=this.shape[r]);return new ue({...this._def,shape:()=>s})}deepPartial(){return de(this)}partial(e){const s={};for(const r of t.objectKeys(this.shape)){const t=this.shape[r];e&&!e[r]?s[r]=t:s[r]=t.optional()}return new ue({...this._def,shape:()=>s})}required(e){const s={};for(const r of t.objectKeys(this.shape))if(e&&!e[r])s[r]=this.shape[r];else{let e=this.shape[r];for(;e instanceof we;)e=e._def.innerType;s[r]=e}return new ue({...this._def,shape:()=>s})}keyof(){return _e(t.objectKeys(this.shape))}}ue.create=(e,t)=>new ue({shape:()=>e,unknownKeys:\"strip\",catchall:ie.create(),typeName:Ze.ZodObject,...w(t)}),ue.strictCreate=(e,t)=>new ue({shape:()=>e,unknownKeys:\"strict\",catchall:ie.create(),typeName:Ze.ZodObject,...w(t)}),ue.lazycreate=(e,t)=>new ue({shape:e,unknownKeys:\"strip\",catchall:ie.create(),typeName:Ze.ZodObject,...w(t)});class le extends A{_parse(e){const{ctx:t}=this._processInputParams(e),s=this._def.options;if(t.common.async)return Promise.all(s.map(async e=>{const s={...t,common:{...t.common,issues:[]},parent:null};return{result:await e._parseAsync({data:t.data,path:t.path,parent:s}),ctx:s}})).then(function(e){for(const t of e)if(\"valid\"===t.result.status)return t.result;for(const r of e)if(\"dirty\"===r.result.status)return t.common.issues.push(...r.ctx.common.issues),r.result;const s=e.map(e=>new o(e.ctx.common.issues));return l(t,{code:n.invalid_union,unionErrors:s}),h});{let e;const r=[];for(const i of s){const s={...t,common:{...t.common,issues:[]},parent:null},a=i._parseSync({data:t.data,path:t.path,parent:s});if(\"valid\"===a.status)return a;\"dirty\"!==a.status||e||(e={result:a,ctx:s}),s.common.issues.length&&r.push(s.common.issues)}if(e)return t.common.issues.push(...e.ctx.common.issues),e.result;const a=r.map(e=>new o(e));return l(t,{code:n.invalid_union,unionErrors:a}),h}}get options(){return this._def.options}}function ce(e,s){const r=i(e),n=i(s);if(e===s)return{valid:!0,data:e};if(r===a.object&&n===a.object){const r=t.objectKeys(s),a=t.objectKeys(e).filter(e=>-1!==r.indexOf(e)),i={...e,...s};for(const t of a){const r=ce(e[t],s[t]);if(!r.valid)return{valid:!1};i[t]=r.data}return{valid:!0,data:i}}if(r===a.array&&n===a.array){if(e.length!==s.length)return{valid:!1};const t=[];for(let r=0;r<e.length;r++){const a=ce(e[r],s[r]);if(!a.valid)return{valid:!1};t.push(a.data)}return{valid:!0,data:t}}return r===a.date&&n===a.date&&+e===+s?{valid:!0,data:e}:{valid:!1}}le.create=(e,t)=>new le({options:e,typeName:Ze.ZodUnion,...w(t)});class he extends A{_parse(e){const{status:t,ctx:s}=this._processInputParams(e),r=(e,r)=>{if(p(e)||p(r))return h;const a=ce(e.value,r.value);return a.valid?((y(e)||y(r))&&t.dirty(),{status:t.value,value:a.data}):(l(s,{code:n.invalid_intersection_types}),h)};return s.common.async?Promise.all([this._def.left._parseAsync({data:s.data,path:s.path,parent:s}),this._def.right._parseAsync({data:s.data,path:s.path,parent:s})]).then(([e,t])=>r(e,t)):r(this._def.left._parseSync({data:s.data,path:s.path,parent:s}),this._def.right._parseSync({data:s.data,path:s.path,parent:s}))}}he.create=(e,t,s)=>new he({left:e,right:t,typeName:Ze.ZodIntersection,...w(s)});class fe extends A{_parse(e){const{status:t,ctx:s}=this._processInputParams(e);if(s.parsedType!==a.array)return l(s,{code:n.invalid_type,expected:a.array,received:s.parsedType}),h;if(s.data.length<this._def.items.length)return l(s,{code:n.too_small,minimum:this._def.items.length,inclusive:!0,exact:!1,type:\"array\"}),h;!this._def.rest&&s.data.length>this._def.items.length&&(l(s,{code:n.too_big,maximum:this._def.items.length,inclusive:!0,exact:!1,type:\"array\"}),t.dirty());const r=[...s.data].map((e,t)=>{const r=this._def.items[t]||this._def.rest;return r?r._parse(new k(s,e,s.path,t)):null}).filter(e=>!!e);return s.common.async?Promise.all(r).then(e=>c.mergeArray(t,e)):c.mergeArray(t,r)}get items(){return this._def.items}rest(e){return new fe({...this._def,rest:e})}}fe.create=(e,t)=>{if(!Array.isArray(e))throw new Error(\"You must pass an array of schemas to z.tuple([ ... ])\");return new fe({items:e,typeName:Ze.ZodTuple,rest:null,...w(t)})};class me extends A{get keySchema(){return this._def.keyType}get valueSchema(){return this._def.valueType}_parse(e){const{status:t,ctx:s}=this._processInputParams(e);if(s.parsedType!==a.map)return l(s,{code:n.invalid_type,expected:a.map,received:s.parsedType}),h;const r=this._def.keyType,i=this._def.valueType,o=[...s.data.entries()].map(([e,t],a)=>({key:r._parse(new k(s,e,s.path,[a,\"key\"])),value:i._parse(new k(s,t,s.path,[a,\"value\"]))}));if(s.common.async){const e=new Map;return Promise.resolve().then(async()=>{for(const s of o){const r=await s.key,a=await s.value;if(\"aborted\"===r.status||\"aborted\"===a.status)return h;\"dirty\"!==r.status&&\"dirty\"!==a.status||t.dirty(),e.set(r.value,a.value)}return{status:t.value,value:e}})}{const e=new Map;for(const s of o){const r=s.key,a=s.value;if(\"aborted\"===r.status||\"aborted\"===a.status)return h;\"dirty\"!==r.status&&\"dirty\"!==a.status||t.dirty(),e.set(r.value,a.value)}return{status:t.value,value:e}}}}me.create=(e,t,s)=>new me({valueType:t,keyType:e,typeName:Ze.ZodMap,...w(s)});class pe extends A{_parse(e){const{status:t,ctx:s}=this._processInputParams(e);if(s.parsedType!==a.set)return l(s,{code:n.invalid_type,expected:a.set,received:s.parsedType}),h;const r=this._def;null!==r.minSize&&s.data.size<r.minSize.value&&(l(s,{code:n.too_small,minimum:r.minSize.value,type:\"set\",inclusive:!0,exact:!1,message:r.minSize.message}),t.dirty()),null!==r.maxSize&&s.data.size>r.maxSize.value&&(l(s,{code:n.too_big,maximum:r.maxSize.value,type:\"set\",inclusive:!0,exact:!1,message:r.maxSize.message}),t.dirty());const i=this._def.valueType;function o(e){const s=new Set;for(const r of e){if(\"aborted\"===r.status)return h;\"dirty\"===r.status&&t.dirty(),s.add(r.value)}return{status:t.value,value:s}}const d=[...s.data.values()].map((e,t)=>i._parse(new k(s,e,s.path,t)));return s.common.async?Promise.all(d).then(e=>o(e)):o(d)}min(e,t){return new pe({...this._def,minSize:{value:e,message:g.toString(t)}})}max(e,t){return new pe({...this._def,maxSize:{value:e,message:g.toString(t)}})}size(e,t){return this.min(e,t).max(e,t)}nonempty(e){return this.min(1,e)}}pe.create=(e,t)=>new pe({valueType:e,minSize:null,maxSize:null,typeName:Ze.ZodSet,...w(t)});class ye extends A{get schema(){return this._def.getter()}_parse(e){const{ctx:t}=this._processInputParams(e);return this._def.getter()._parse({data:t.data,path:t.path,parent:t})}}ye.create=(e,t)=>new ye({getter:e,typeName:Ze.ZodLazy,...w(t)});class ve extends A{_parse(e){if(e.data!==this._def.value){const t=this._getOrReturnCtx(e);return l(t,{received:t.data,code:n.invalid_literal,expected:this._def.value}),h}return{status:\"valid\",value:e.data}}get value(){return this._def.value}}function _e(e,t){return new ge({values:e,typeName:Ze.ZodEnum,...w(t)})}ve.create=(e,t)=>new ve({value:e,typeName:Ze.ZodLiteral,...w(t)});class ge extends A{_parse(e){if(\"string\"!=typeof e.data){const s=this._getOrReturnCtx(e),r=this._def.values;return l(s,{expected:t.joinValues(r),received:s.parsedType,code:n.invalid_type}),h}if(this._cache||(this._cache=new Set(this._def.values)),!this._cache.has(e.data)){const t=this._getOrReturnCtx(e),s=this._def.values;return l(t,{received:t.data,code:n.invalid_enum_value,options:s}),h}return m(e.data)}get options(){return this._def.values}get enum(){const e={};for(const t of this._def.values)e[t]=t;return e}get Values(){const e={};for(const t of this._def.values)e[t]=t;return e}get Enum(){const e={};for(const t of this._def.values)e[t]=t;return e}extract(e,t=this._def){return ge.create(e,{...this._def,...t})}exclude(e,t=this._def){return ge.create(this.options.filter(t=>!e.includes(t)),{...this._def,...t})}}ge.create=_e;class be extends A{_parse(e){const s=t.getValidEnumValues(this._def.values),r=this._getOrReturnCtx(e);if(r.parsedType!==a.string&&r.parsedType!==a.number){const e=t.objectValues(s);return l(r,{expected:t.joinValues(e),received:r.parsedType,code:n.invalid_type}),h}if(this._cache||(this._cache=new Set(t.getValidEnumValues(this._def.values))),!this._cache.has(e.data)){const e=t.objectValues(s);return l(r,{received:r.data,code:n.invalid_enum_value,options:e}),h}return m(e.data)}get enum(){return this._def.values}}be.create=(e,t)=>new be({values:e,typeName:Ze.ZodNativeEnum,...w(t)});class ke extends A{unwrap(){return this._def.type}_parse(e){const{ctx:t}=this._processInputParams(e);if(t.parsedType!==a.promise&&!1===t.common.async)return l(t,{code:n.invalid_type,expected:a.promise,received:t.parsedType}),h;const s=t.parsedType===a.promise?t.data:Promise.resolve(t.data);return m(s.then(e=>this._def.type.parseAsync(e,{path:t.path,errorMap:t.common.contextualErrorMap})))}}ke.create=(e,t)=>new ke({type:e,typeName:Ze.ZodPromise,...w(t)});class xe extends A{innerType(){return this._def.schema}sourceType(){return this._def.schema._def.typeName===Ze.ZodEffects?this._def.schema.sourceType():this._def.schema}_parse(e){const{status:s,ctx:r}=this._processInputParams(e),a=this._def.effect||null,i={addIssue:e=>{l(r,e),e.fatal?s.abort():s.dirty()},get path(){return r.path}};if(i.addIssue=i.addIssue.bind(i),\"preprocess\"===a.type){const e=a.transform(r.data,i);if(r.common.async)return Promise.resolve(e).then(async e=>{if(\"aborted\"===s.value)return h;const t=await this._def.schema._parseAsync({data:e,path:r.path,parent:r});return\"aborted\"===t.status?h:\"dirty\"===t.status||\"dirty\"===s.value?f(t.value):t});{if(\"aborted\"===s.value)return h;const t=this._def.schema._parseSync({data:e,path:r.path,parent:r});return\"aborted\"===t.status?h:\"dirty\"===t.status||\"dirty\"===s.value?f(t.value):t}}if(\"refinement\"===a.type){const e=e=>{const t=a.refinement(e,i);if(r.common.async)return Promise.resolve(t);if(t instanceof Promise)throw new Error(\"Async refinement encountered during synchronous parse operation. Use .parseAsync instead.\");return e};if(!1===r.common.async){const t=this._def.schema._parseSync({data:r.data,path:r.path,parent:r});return\"aborted\"===t.status?h:(\"dirty\"===t.status&&s.dirty(),e(t.value),{status:s.value,value:t.value})}return this._def.schema._parseAsync({data:r.data,path:r.path,parent:r}).then(t=>\"aborted\"===t.status?h:(\"dirty\"===t.status&&s.dirty(),e(t.value).then(()=>({status:s.value,value:t.value}))))}if(\"transform\"===a.type){if(!1===r.common.async){const e=this._def.schema._parseSync({data:r.data,path:r.path,parent:r});if(!v(e))return h;const t=a.transform(e.value,i);if(t instanceof Promise)throw new Error(\"Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.\");return{status:s.value,value:t}}return this._def.schema._parseAsync({data:r.data,path:r.path,parent:r}).then(e=>v(e)?Promise.resolve(a.transform(e.value,i)).then(e=>({status:s.value,value:e})):h)}t.assertNever(a)}}xe.create=(e,t,s)=>new xe({schema:e,typeName:Ze.ZodEffects,effect:t,...w(s)}),xe.createWithPreprocess=(e,t,s)=>new xe({schema:t,effect:{type:\"preprocess\",transform:e},typeName:Ze.ZodEffects,...w(s)});class we extends A{_parse(e){return this._getType(e)===a.undefined?m(void 0):this._def.innerType._parse(e)}unwrap(){return this._def.innerType}}we.create=(e,t)=>new we({innerType:e,typeName:Ze.ZodOptional,...w(t)});class Ae extends A{_parse(e){return this._getType(e)===a.null?m(null):this._def.innerType._parse(e)}unwrap(){return this._def.innerType}}Ae.create=(e,t)=>new Ae({innerType:e,typeName:Ze.ZodNullable,...w(t)});class Oe extends A{_parse(e){const{ctx:t}=this._processInputParams(e);let s=t.data;return t.parsedType===a.undefined&&(s=this._def.defaultValue()),this._def.innerType._parse({data:s,path:t.path,parent:t})}removeDefault(){return this._def.innerType}}Oe.create=(e,t)=>new Oe({innerType:e,typeName:Ze.ZodDefault,defaultValue:\"function\"==typeof t.default?t.default:()=>t.default,...w(t)});class Se extends A{_parse(e){const{ctx:t}=this._processInputParams(e),s={...t,common:{...t.common,issues:[]}},r=this._def.innerType._parse({data:s.data,path:s.path,parent:{...s}});return _(r)?r.then(e=>({status:\"valid\",value:\"valid\"===e.status?e.value:this._def.catchValue({get error(){return new o(s.common.issues)},input:s.data})})):{status:\"valid\",value:\"valid\"===r.status?r.value:this._def.catchValue({get error(){return new o(s.common.issues)},input:s.data})}}removeCatch(){return this._def.innerType}}Se.create=(e,t)=>new Se({innerType:e,typeName:Ze.ZodCatch,catchValue:\"function\"==typeof t.catch?t.catch:()=>t.catch,...w(t)});class Ve extends A{_parse(e){if(this._getType(e)!==a.nan){const t=this._getOrReturnCtx(e);return l(t,{code:n.invalid_type,expected:a.nan,received:t.parsedType}),h}return{status:\"valid\",value:e.data}}}Ve.create=e=>new Ve({typeName:Ze.ZodNaN,...w(e)});class Ce extends A{_parse(e){const{ctx:t}=this._processInputParams(e),s=t.data;return this._def.type._parse({data:s,path:t.path,parent:t})}unwrap(){return this._def.type}}class Te extends A{_parse(e){const{status:t,ctx:s}=this._processInputParams(e);if(s.common.async){return(async()=>{const e=await this._def.in._parseAsync({data:s.data,path:s.path,parent:s});return\"aborted\"===e.status?h:\"dirty\"===e.status?(t.dirty(),f(e.value)):this._def.out._parseAsync({data:e.value,path:s.path,parent:s})})()}{const e=this._def.in._parseSync({data:s.data,path:s.path,parent:s});return\"aborted\"===e.status?h:\"dirty\"===e.status?(t.dirty(),{status:\"dirty\",value:e.value}):this._def.out._parseSync({data:e.value,path:s.path,parent:s})}}static create(e,t){return new Te({in:e,out:t,typeName:Ze.ZodPipeline})}}class Fe extends A{_parse(e){const t=this._def.innerType._parse(e),s=e=>(v(e)&&(e.value=Object.freeze(e.value)),e);return _(t)?t.then(e=>s(e)):s(t)}unwrap(){return this._def.innerType}}var Ze,Ne;Fe.create=(e,t)=>new Fe({innerType:e,typeName:Ze.ZodReadonly,...w(t)}),(Ne=Ze||(Ze={})).ZodString=\"ZodString\",Ne.ZodNumber=\"ZodNumber\",Ne.ZodNaN=\"ZodNaN\",Ne.ZodBigInt=\"ZodBigInt\",Ne.ZodBoolean=\"ZodBoolean\",Ne.ZodDate=\"ZodDate\",Ne.ZodSymbol=\"ZodSymbol\",Ne.ZodUndefined=\"ZodUndefined\",Ne.ZodNull=\"ZodNull\",Ne.ZodAny=\"ZodAny\",Ne.ZodUnknown=\"ZodUnknown\",Ne.ZodNever=\"ZodNever\",Ne.ZodVoid=\"ZodVoid\",Ne.ZodArray=\"ZodArray\",Ne.ZodObject=\"ZodObject\",Ne.ZodUnion=\"ZodUnion\",Ne.ZodDiscriminatedUnion=\"ZodDiscriminatedUnion\",Ne.ZodIntersection=\"ZodIntersection\",Ne.ZodTuple=\"ZodTuple\",Ne.ZodRecord=\"ZodRecord\",Ne.ZodMap=\"ZodMap\",Ne.ZodSet=\"ZodSet\",Ne.ZodFunction=\"ZodFunction\",Ne.ZodLazy=\"ZodLazy\",Ne.ZodLiteral=\"ZodLiteral\",Ne.ZodEnum=\"ZodEnum\",Ne.ZodEffects=\"ZodEffects\",Ne.ZodNativeEnum=\"ZodNativeEnum\",Ne.ZodOptional=\"ZodOptional\",Ne.ZodNullable=\"ZodNullable\",Ne.ZodDefault=\"ZodDefault\",Ne.ZodCatch=\"ZodCatch\",Ne.ZodPromise=\"ZodPromise\",Ne.ZodBranded=\"ZodBranded\",Ne.ZodPipeline=\"ZodPipeline\",Ne.ZodReadonly=\"ZodReadonly\";const je=H.create,Ee=Y.create;G.create;const De=X.create;Q.create,ie.create,oe.create;const Ie=ue.create,Re=le.create;he.create,fe.create;const Pe=ge.create;ke.create,we.create,Ae.create;const $e={string:e=>H.create({...e,coerce:!0}),number:e=>Y.create({...e,coerce:!0}),boolean:e=>X.create({...e,coerce:!0}),bigint:e=>G.create({...e,coerce:!0}),date:e=>Q.create({...e,coerce:!0})};var Le=e=>\"checkbox\"===e.type,Me=e=>e instanceof Date,Ue=e=>null==e;const ze=e=>\"object\"==typeof e;var Be=e=>!Ue(e)&&!Array.isArray(e)&&ze(e)&&!Me(e),We=\"undefined\"!=typeof window&&void 0!==window.HTMLElement&&\"undefined\"!=typeof document;function Ke(e){if(e instanceof Date)return new Date(e);const t=\"undefined\"!=typeof FileList&&e instanceof FileList;if(We&&(e instanceof Blob||t))return e;const s=Array.isArray(e);if(!(s||Be(e)&&(e=>{const t=e.constructor&&e.constructor.prototype;return Be(t)&&t.hasOwnProperty(\"isPrototypeOf\")})(e)))return e;const r=s?[]:Object.create(Object.getPrototypeOf(e));for(const a in e)Object.prototype.hasOwnProperty.call(e,a)&&(r[a]=Ke(e[a]));return r}var qe=e=>/^\\w*$/.test(e),He=e=>void 0===e,Je=e=>Array.isArray(e)?e.filter(Boolean):[],Ye=e=>Je(e.replace(/[\"|']|\\]/g,\"\").split(/\\.|\\[/)),Ge=(e,t,s)=>{if(!t||!Be(e))return s;const r=(qe(t)?[t]:Ye(t)).reduce((e,t)=>Ue(e)?e:e[t],e);return He(r)||r===e?He(e[t])?s:e[t]:r},Xe=e=>\"boolean\"==typeof e,Qe=e=>\"function\"==typeof e,et=(e,t,s)=>{let r=-1;const a=qe(t)?[t]:Ye(t),i=a.length,n=i-1;for(;++r<i;){const t=a[r];let i=s;if(r!==n){const s=e[t];i=Be(s)||Array.isArray(s)?s:isNaN(+a[r+1])?{}:[]}if(\"__proto__\"===t||\"constructor\"===t||\"prototype\"===t)return;e[t]=i,e=e[t]}};const tt=\"blur\",st=\"focusout\",rt=\"onBlur\",at=\"onChange\",it=\"onSubmit\",nt=\"onTouched\",ot=\"all\",dt=\"max\",ut=\"min\",lt=\"maxLength\",ct=\"minLength\",ht=\"pattern\",ft=\"required\",mt=\"validate\";e.createContext(null).displayName=\"HookFormContext\";const pt=\"undefined\"!=typeof window?e.useLayoutEffect:e.useEffect;var yt=e=>\"string\"==typeof e,vt=e=>Ue(e)||!ze(e);function _t(e,t,s=new WeakSet){if(vt(e)||vt(t))return Object.is(e,t);if(Me(e)&&Me(t))return e.getTime()===t.getTime();const r=Object.keys(e),a=Object.keys(t);if(r.length!==a.length)return!1;if(s.has(e)||s.has(t))return!0;s.add(e),s.add(t);for(const i of r){const r=e[i];if(!a.includes(i))return!1;if(\"ref\"!==i){const e=t[i];if(Me(r)&&Me(e)||Be(r)&&Be(e)||Array.isArray(r)&&Array.isArray(e)?!_t(r,e,s):!Object.is(r,e))return!1}}return!0}var gt=(e,t,s,r,a)=>t?{...s[e],types:{...s[e]&&s[e].types?s[e].types:{},[r]:a||!0}}:{},bt=e=>Array.isArray(e)?e:[e],kt=()=>{let e=[];return{get observers(){return e},next:t=>{for(const s of e)s.next&&s.next(t)},subscribe:t=>(e.push(t),{unsubscribe:()=>{e=e.filter(e=>e!==t)}}),unsubscribe:()=>{e=[]}}};function xt(e,t){const s={};for(const r in e)if(e.hasOwnProperty(r)){const a=e[r],i=t[r];if(a&&Be(a)&&i){const e=xt(a,i);Be(e)&&(s[r]=e)}else e[r]&&(s[r]=i)}return s}var wt=e=>Be(e)&&!Object.keys(e).length,At=e=>\"file\"===e.type,Ot=e=>{if(!We)return!1;const t=e?e.ownerDocument:0;return e instanceof(t&&t.defaultView?t.defaultView.HTMLElement:HTMLElement)},St=e=>\"select-multiple\"===e.type,Vt=e=>\"radio\"===e.type,Ct=e=>Ot(e)&&e.isConnected;function Tt(e,t){const s=Array.isArray(t)?t:qe(t)?[t]:Ye(t),r=1===s.length?e:function(e,t){const s=t.slice(0,-1).length;let r=0;for(;r<s;)e=He(e)?r++:e[t[r++]];return e}(e,s),a=s.length-1,i=s[a];return r&&delete r[i],0!==a&&(Be(r)&&wt(r)||Array.isArray(r)&&function(e){for(const t in e)if(e.hasOwnProperty(t)&&!He(e[t]))return!1;return!0}(r))&&Tt(e,s.slice(0,-1)),e}function Ft(e){return Array.isArray(e)||Be(e)&&!(e=>{for(const t in e)if(Qe(e[t]))return!0;return!1})(e)}function Zt(e,t={}){for(const s in e){const r=e[s];Ft(r)?(t[s]=Array.isArray(r)?[]:{},Zt(r,t[s])):He(r)||(t[s]=!0)}return t}function Nt(e,t,s){s||(s=Zt(t));for(const r in e){const a=e[r];if(Ft(a))He(t)||vt(s[r])?s[r]=Zt(a,Array.isArray(a)?[]:{}):Nt(a,Ue(t)?{}:t[r],s[r]);else{const e=t[r];s[r]=!_t(a,e)}}return s}const jt={value:!1,isValid:!1},Et={value:!0,isValid:!0};var Dt=e=>{if(Array.isArray(e)){if(e.length>1){const t=e.filter(e=>e&&e.checked&&!e.disabled).map(e=>e.value);return{value:t,isValid:!!t.length}}return e[0].checked&&!e[0].disabled?e[0].attributes&&!He(e[0].attributes.value)?He(e[0].value)||\"\"===e[0].value?Et:{value:e[0].value,isValid:!0}:Et:jt}return jt},It=(e,{valueAsNumber:t,valueAsDate:s,setValueAs:r})=>He(e)?e:t?\"\"===e?NaN:e?+e:e:s&&yt(e)?new Date(e):r?r(e):e;const Rt={isValid:!1,value:null};var Pt=e=>Array.isArray(e)?e.reduce((e,t)=>t&&t.checked&&!t.disabled?{isValid:!0,value:t.value}:e,Rt):Rt;function $t(e){const t=e.ref;return At(t)?t.files:Vt(t)?Pt(e.refs).value:St(t)?[...t.selectedOptions].map(({value:e})=>e):Le(t)?Dt(e.refs).value:It(He(t.value)?e.ref.value:t.value,e)}var Lt=e=>e instanceof RegExp,Mt=e=>He(e)?e:Lt(e)?e.source:Be(e)?Lt(e.value)?e.value.source:e.value:e,Ut=e=>({isOnSubmit:!e||e===it,isOnBlur:e===rt,isOnChange:e===at,isOnAll:e===ot,isOnTouch:e===nt});const zt=\"AsyncFunction\";var Bt=e=>!!e&&!!e.validate&&!!(Qe(e.validate)&&e.validate.constructor.name===zt||Be(e.validate)&&Object.values(e.validate).find(e=>e.constructor.name===zt)),Wt=(e,t,s)=>!s&&(t.watchAll||t.watch.has(e)||[...t.watch].some(t=>e.startsWith(t)&&/^\\.\\w+/.test(e.slice(t.length))));const Kt=(e,t,s,r)=>{for(const a of s||Object.keys(e)){const s=Ge(e,a);if(s){const{_f:e,...i}=s;if(e){if(e.refs&&e.refs[0]&&t(e.refs[0],a)&&!r)return!0;if(e.ref&&t(e.ref,e.name)&&!r)return!0;if(Kt(i,t))break}else if(Be(i)&&Kt(i,t))break}}};function qt(e,t,s){const r=Ge(e,s);if(r||qe(s))return{error:r,name:s};const a=s.split(\".\");for(;a.length;){const r=a.join(\".\"),i=Ge(t,r),n=Ge(e,r);if(i&&!Array.isArray(i)&&s!==r)return{name:s};if(n&&n.type)return{name:r,error:n};if(n&&n.root&&n.root.type)return{name:`${r}.root`,error:n.root};a.pop()}return{name:s}}var Ht=(e,t,s)=>{const r=bt(Ge(e,s));return et(r,\"root\",t[s]),et(e,s,r),e};function Jt(e,t,s=\"validate\"){if(yt(e)||Array.isArray(e)&&e.every(yt)||Xe(e)&&!e)return{type:s,message:yt(e)?e:\"\",ref:t}}var Yt=e=>Be(e)&&!Lt(e)?e:{value:e,message:\"\"},Gt=async(e,t,s,r,a,i)=>{const{ref:n,refs:o,required:d,maxLength:u,minLength:l,min:c,max:h,pattern:f,validate:m,name:p,valueAsNumber:y,mount:v}=e._f,_=Ge(s,p);if(!v||t.has(p))return{};const g=o?o[0]:n,b=e=>{a&&g.reportValidity&&(g.setCustomValidity(Xe(e)?\"\":e||\"\"),g.reportValidity())},k={},x=Vt(n),w=Le(n),A=x||w,O=(y||At(n))&&He(n.value)&&He(_)||Ot(n)&&\"\"===n.value||\"\"===_||Array.isArray(_)&&!_.length,S=gt.bind(null,p,r,k),V=(e,t,s,r=lt,a=ct)=>{const i=e?t:s;k[p]={type:e?r:a,message:i,ref:n,...S(e?r:a,i)}};if(i?!Array.isArray(_)||!_.length:d&&(!A&&(O||Ue(_))||Xe(_)&&!_||w&&!Dt(o).isValid||x&&!Pt(o).isValid)){const{value:e,message:t}=yt(d)?{value:!!d,message:d}:Yt(d);if(e&&(k[p]={type:ft,message:t,ref:g,...S(ft,t)},!r))return b(t),k}if(!(O||Ue(c)&&Ue(h))){let e,t;const s=Yt(h),a=Yt(c);if(Ue(_)||isNaN(_)){const r=n.valueAsDate||new Date(_),i=e=>new Date((new Date).toDateString()+\" \"+e),o=\"time\"==n.type,d=\"week\"==n.type;yt(s.value)&&_&&(e=o?i(_)>i(s.value):d?_>s.value:r>new Date(s.value)),yt(a.value)&&_&&(t=o?i(_)<i(a.value):d?_<a.value:r<new Date(a.value))}else{const r=n.valueAsNumber||(_?+_:_);Ue(s.value)||(e=r>s.value),Ue(a.value)||(t=r<a.value)}if((e||t)&&(V(!!e,s.message,a.message,dt,ut),!r))return b(k[p].message),k}if((u||l)&&!O&&(yt(_)||i&&Array.isArray(_))){const e=Yt(u),t=Yt(l),s=!Ue(e.value)&&_.length>+e.value,a=!Ue(t.value)&&_.length<+t.value;if((s||a)&&(V(s,e.message,t.message),!r))return b(k[p].message),k}if(f&&!O&&yt(_)){const{value:e,message:t}=Yt(f);if(Lt(e)&&!_.match(e)&&(k[p]={type:ht,message:t,ref:n,...S(ht,t)},!r))return b(t),k}if(m)if(Qe(m)){const e=Jt(await m(_,s),g);if(e&&(k[p]={...e,...S(mt,e.message)},!r))return b(e.message),k}else if(Be(m)){let e={};for(const t in m){if(!wt(e)&&!r)break;const a=Jt(await m[t](_,s),g,t);a&&(e={...a,...S(t,a.message)},b(a.message),r&&(k[p]=e))}if(!wt(e)&&(k[p]={ref:g,...e},!r))return k}return b(!0),k};const Xt={mode:it,reValidateMode:at,shouldFocusError:!0};function Qt(e={}){let t,s={...Xt,...e},r={submitCount:0,isDirty:!1,isReady:!1,isLoading:Qe(s.defaultValues),isValidating:!1,isSubmitted:!1,isSubmitting:!1,isSubmitSuccessful:!1,isValid:!1,touchedFields:{},dirtyFields:{},validatingFields:{},errors:s.errors||{},disabled:s.disabled||!1},a={},i=(Be(s.defaultValues)||Be(s.values))&&Ke(s.defaultValues||s.values)||{},n=s.shouldUnregister?{}:Ke(i),o={action:!1,mount:!1,watch:!1,keepIsValid:!1},d={mount:new Set,disabled:new Set,unMount:new Set,array:new Set,watch:new Set},u=0;const l={isDirty:!1,dirtyFields:!1,validatingFields:!1,touchedFields:!1,isValidating:!1,isValid:!1,errors:!1},c={...l};let h={...c};const f={array:kt(),state:kt()},m=s.criteriaMode===ot,p=async e=>{if(!o.keepIsValid&&!s.disabled&&(c.isValid||h.isValid||e)){let e;s.resolver?(e=wt((await b()).errors),y()):e=await k(a,!0),e!==r.isValid&&f.state.next({isValid:e})}},y=(e,t)=>{!s.disabled&&(c.isValidating||c.validatingFields||h.isValidating||h.validatingFields)&&((e||Array.from(d.mount)).forEach(e=>{e&&(t?et(r.validatingFields,e,t):Tt(r.validatingFields,e))}),f.state.next({validatingFields:r.validatingFields,isValidating:!wt(r.validatingFields)}))},v=(e,t,s,r)=>{const d=Ge(a,e);if(d){const a=Ge(n,e,He(s)?Ge(i,e):s);He(a)||r&&r.defaultChecked||t?et(n,e,t?a:$t(d._f)):A(e,a),o.mount&&!o.action&&p()}},_=(e,t,a,n,o)=>{let d=!1,u=!1;const l={name:e};if(!s.disabled){if(!a||n){(c.isDirty||h.isDirty)&&(u=r.isDirty,r.isDirty=l.isDirty=x(),d=u!==l.isDirty);const s=_t(Ge(i,e),t);u=!!Ge(r.dirtyFields,e),s?Tt(r.dirtyFields,e):et(r.dirtyFields,e,!0),l.dirtyFields=r.dirtyFields,d=d||(c.dirtyFields||h.dirtyFields)&&u!==!s}if(a){const t=Ge(r.touchedFields,e);t||(et(r.touchedFields,e,a),l.touchedFields=r.touchedFields,d=d||(c.touchedFields||h.touchedFields)&&t!==a)}d&&o&&f.state.next(l)}return d?l:{}},g=(e,a,i,n)=>{const o=Ge(r.errors,e),d=(c.isValid||h.isValid)&&Xe(a)&&r.isValid!==a;var l;if(s.delayError&&i?(l=()=>((e,t)=>{et(r.errors,e,t),f.state.next({errors:r.errors})})(e,i),t=e=>{clearTimeout(u),u=setTimeout(l,e)},t(s.delayError)):(clearTimeout(u),t=null,i?et(r.errors,e,i):Tt(r.errors,e)),(i?!_t(o,i):o)||!wt(n)||d){const t={...n,...d&&Xe(a)?{isValid:a}:{},errors:r.errors,name:e};r={...r,...t},f.state.next(t)}},b=async e=>{y(e,!0);const t=await s.resolver(n,s.context,((e,t,s,r)=>{const a={};for(const i of e){const e=Ge(t,i);e&&et(a,i,e._f)}return{criteriaMode:s,names:[...e],fields:a,shouldUseNativeValidation:r}})(e||d.mount,a,s.criteriaMode,s.shouldUseNativeValidation));return t},k=async(e,t,a={valid:!0})=>{for(const i in e){const o=e[i];if(o){const{_f:e,...i}=o;if(e){const i=d.array.has(e.name),u=o._f&&Bt(o._f);u&&c.validatingFields&&y([e.name],!0);const l=await Gt(o,d.disabled,n,m,s.shouldUseNativeValidation&&!t,i);if(u&&c.validatingFields&&y([e.name]),l[e.name]&&(a.valid=!1,t))break;!t&&(Ge(l,e.name)?i?Ht(r.errors,l,e.name):et(r.errors,e.name,l[e.name]):Tt(r.errors,e.name))}!wt(i)&&await k(i,t,a)}}return a.valid},x=(e,t)=>!s.disabled&&(e&&t&&et(n,e,t),!_t(F(),i)),w=(e,t,s)=>((e,t,s,r,a)=>yt(e)?(r&&t.watch.add(e),Ge(s,e,a)):Array.isArray(e)?e.map(e=>(r&&t.watch.add(e),Ge(s,e))):(r&&(t.watchAll=!0),s))(e,d,{...o.mount?n:He(t)?i:yt(e)?{[e]:t}:t},s,t),A=(e,t,s={})=>{const r=Ge(a,e);let i=t;if(r){const s=r._f;s&&(!s.disabled&&et(n,e,It(t,s)),i=Ot(s.ref)&&Ue(t)?\"\":t,St(s.ref)?[...s.ref.options].forEach(e=>e.selected=i.includes(e.value)):s.refs?Le(s.ref)?s.refs.forEach(e=>{e.defaultChecked&&e.disabled||(Array.isArray(i)?e.checked=!!i.find(t=>t===e.value):e.checked=i===e.value||!!i)}):s.refs.forEach(e=>e.checked=e.value===i):At(s.ref)?s.ref.value=\"\":(s.ref.value=i,s.ref.type||f.state.next({name:e,values:Ke(n)})))}(s.shouldDirty||s.shouldTouch)&&_(e,i,s.shouldTouch,s.shouldDirty,!0),s.shouldValidate&&T(e)},O=(e,t,s)=>{for(const r in t){if(!t.hasOwnProperty(r))return;const i=t[r],n=e+\".\"+r,o=Ge(a,n);(d.array.has(e)||Be(i)||o&&!o._f)&&!Me(i)?O(n,i,s):A(n,i,s)}},S=(e,t,s={})=>{const u=Ge(a,e),l=d.array.has(e),m=Ke(t);et(n,e,m),l?(f.array.next({name:e,values:Ke(n)}),(c.isDirty||c.dirtyFields||h.isDirty||h.dirtyFields)&&s.shouldDirty&&f.state.next({name:e,dirtyFields:Nt(i,n),isDirty:x(e,m)})):!u||u._f||Ue(m)?A(e,m,s):O(e,m,s),Wt(e,d)&&f.state.next({...r,name:e}),f.state.next({name:o.mount?e:void 0,values:Ke(n)})},V=async e=>{o.mount=!0;const i=e.target;let u=i.name,l=!0;const v=Ge(a,u),x=e=>{l=Number.isNaN(e)||Me(e)&&isNaN(e.getTime())||_t(e,Ge(n,u,e))},w=Ut(s.mode),A=Ut(s.reValidateMode);if(v){let o,S;const V=i.type?$t(v._f):(e=>Be(e)&&e.target?Le(e.target)?e.target.checked:e.target.value:e)(e),C=e.type===tt||e.type===st,F=!((O=v._f).mount&&(O.required||O.min||O.max||O.maxLength||O.minLength||O.pattern||O.validate)||s.resolver||Ge(r.errors,u)||v._f.deps)||((e,t,s,r,a)=>!a.isOnAll&&(!s&&a.isOnTouch?!(t||e):(s?r.isOnBlur:a.isOnBlur)?!e:!(s?r.isOnChange:a.isOnChange)||e))(C,Ge(r.touchedFields,u),r.isSubmitted,A,w),Z=Wt(u,d,C);et(n,u,V),C?i&&i.readOnly||(v._f.onBlur&&v._f.onBlur(e),t&&t(0)):v._f.onChange&&v._f.onChange(e);const N=_(u,V,C),j=!wt(N)||Z;if(!C&&f.state.next({name:u,type:e.type,values:Ke(n)}),F)return(c.isValid||h.isValid)&&(\"onBlur\"===s.mode?C&&p():C||p()),j&&f.state.next({name:u,...Z?{}:N});if(!C&&Z&&f.state.next({...r}),s.resolver){const{errors:e}=await b([u]);if(y([u]),x(V),l){const t=qt(r.errors,a,u),s=qt(e,a,t.name||u);o=s.error,u=s.name,S=wt(e)}}else y([u],!0),o=(await Gt(v,d.disabled,n,m,s.shouldUseNativeValidation))[u],y([u]),x(V),l&&(o?S=!1:(c.isValid||h.isValid)&&(S=await k(a,!0)));l&&(v._f.deps&&(!Array.isArray(v._f.deps)||v._f.deps.length>0)&&T(v._f.deps),g(u,S,o,N))}var O},C=(e,t)=>{if(Ge(r.errors,t)&&e.focus)return e.focus(),1},T=async(e,t={})=>{let i,n;const o=bt(e);if(s.resolver){const t=await(async e=>{const{errors:t}=await b(e);if(y(e),e)for(const s of e){const e=Ge(t,s);e?et(r.errors,s,e):Tt(r.errors,s)}else r.errors=t;return t})(He(e)?e:o);i=wt(t),n=e?!o.some(e=>Ge(t,e)):i}else e?(n=(await Promise.all(o.map(async e=>{const t=Ge(a,e);return await k(t&&t._f?{[e]:t}:t)}))).every(Boolean),(n||r.isValid)&&p()):n=i=await k(a);return f.state.next({...!yt(e)||(c.isValid||h.isValid)&&i!==r.isValid?{}:{name:e},...s.resolver||!e?{isValid:i}:{},errors:r.errors}),t.shouldFocus&&!n&&Kt(a,C,e?o:d.mount),n},F=(e,t)=>{let s={...o.mount?n:i};return t&&(s=xt(t.dirtyFields?r.dirtyFields:r.touchedFields,s)),He(e)?s:yt(e)?Ge(s,e):e.map(e=>Ge(s,e))},Z=(e,t)=>({invalid:!!Ge((t||r).errors,e),isDirty:!!Ge((t||r).dirtyFields,e),error:Ge((t||r).errors,e),isValidating:!!Ge(r.validatingFields,e),isTouched:!!Ge((t||r).touchedFields,e)}),N=(e,t,s)=>{const i=(Ge(a,e,{_f:{}})._f||{}).ref,n=Ge(r.errors,e)||{},{ref:o,message:d,type:u,...l}=n;et(r.errors,e,{...l,...t,ref:i}),f.state.next({name:e,errors:r.errors,isValid:!1}),s&&s.shouldFocus&&i&&i.focus&&i.focus()},j=e=>f.state.subscribe({next:t=>{var s,a,o;s=e.name,a=t.name,o=e.exact,s&&a&&s!==a&&!bt(s).some(e=>e&&(o?e===a:e.startsWith(a)||a.startsWith(e)))||!((e,t,s,r)=>{s(e);const{name:a,...i}=e;return wt(i)||Object.keys(i).length>=Object.keys(t).length||Object.keys(i).find(e=>t[e]===(!r||ot))})(t,e.formState||c,M,e.reRenderRoot)||e.callback({values:{...n},...r,...t,defaultValues:i})}}).unsubscribe,E=(e,t={})=>{for(const o of e?bt(e):d.mount)d.mount.delete(o),d.array.delete(o),t.keepValue||(Tt(a,o),Tt(n,o)),!t.keepError&&Tt(r.errors,o),!t.keepDirty&&Tt(r.dirtyFields,o),!t.keepTouched&&Tt(r.touchedFields,o),!t.keepIsValidating&&Tt(r.validatingFields,o),!s.shouldUnregister&&!t.keepDefaultValue&&Tt(i,o);f.state.next({values:Ke(n)}),f.state.next({...r,...t.keepDirty?{isDirty:x()}:{}}),!t.keepIsValid&&p()},D=({disabled:e,name:t})=>{(Xe(e)&&o.mount||e||d.disabled.has(t))&&(e?d.disabled.add(t):d.disabled.delete(t))},I=(e,t={})=>{let r=Ge(a,e);const n=Xe(t.disabled)||Xe(s.disabled);return et(a,e,{...r||{},_f:{...r&&r._f?r._f:{ref:{name:e}},name:e,mount:!0,...t}}),d.mount.add(e),r?D({disabled:Xe(t.disabled)?t.disabled:s.disabled,name:e}):v(e,!0,t.value),{...n?{disabled:t.disabled||s.disabled}:{},...s.progressive?{required:!!t.required,min:Mt(t.min),max:Mt(t.max),minLength:Mt(t.minLength),maxLength:Mt(t.maxLength),pattern:Mt(t.pattern)}:{},name:e,onChange:V,onBlur:V,ref:n=>{if(n){I(e,t),r=Ge(a,e);const s=He(n.value)&&n.querySelectorAll&&n.querySelectorAll(\"input,select,textarea\")[0]||n,o=(e=>Vt(e)||Le(e))(s),d=r._f.refs||[];if(o?d.find(e=>e===s):s===r._f.ref)return;et(a,e,{_f:{...r._f,...o?{refs:[...d.filter(Ct),s,...Array.isArray(Ge(i,e))?[{}]:[]],ref:{type:s.type,name:e}}:{ref:s}}}),v(e,!1,void 0,s)}else r=Ge(a,e,{}),r._f&&(r._f.mount=!1),(s.shouldUnregister||t.shouldUnregister)&&(!((e,t)=>e.has((e=>e.substring(0,e.search(/\\.\\d+(\\.|$)/))||e)(t)))(d.array,e)||!o.action)&&d.unMount.add(e)}}},R=()=>s.shouldFocusError&&Kt(a,C,d.mount),P=(e,t)=>async i=>{let o;i&&(i.preventDefault&&i.preventDefault(),i.persist&&i.persist());let u=Ke(n);if(f.state.next({isSubmitting:!0}),s.resolver){const{errors:e,values:t}=await b();y(),r.errors=e,u=Ke(t)}else await k(a);if(d.disabled.size)for(const e of d.disabled)Tt(u,e);if(Tt(r.errors,\"root\"),wt(r.errors)){f.state.next({errors:{}});try{await e(u,i)}catch(l){o=l}}else t&&await t({...r.errors},i),R(),setTimeout(R);if(f.state.next({isSubmitted:!0,isSubmitting:!1,isSubmitSuccessful:wt(r.errors)&&!o,submitCount:r.submitCount+1,errors:r.errors}),o)throw o},$=(e,t={})=>{const u=e?Ke(e):i,l=Ke(u),h=wt(e),m=h?i:l;if(t.keepDefaultValues||(i=u),!t.keepValues){if(t.keepDirtyValues){const e=new Set([...d.mount,...Object.keys(Nt(i,n))]);for(const t of Array.from(e))Ge(r.dirtyFields,t)?et(m,t,Ge(n,t)):S(t,Ge(m,t))}else{if(We&&He(e))for(const e of d.mount){const t=Ge(a,e);if(t&&t._f){const e=Array.isArray(t._f.refs)?t._f.refs[0]:t._f.ref;if(Ot(e)){const t=e.closest(\"form\");if(t){t.reset();break}}}}if(t.keepFieldsRef)for(const e of d.mount)S(e,Ge(m,e));else a={}}n=s.shouldUnregister?t.keepDefaultValues?Ke(i):{}:Ke(m),f.array.next({values:{...m}}),f.state.next({values:{...m}})}d={mount:t.keepDirtyValues?d.mount:new Set,unMount:new Set,array:new Set,disabled:new Set,watch:new Set,watchAll:!1,focus:\"\"},o.mount=!c.isValid||!!t.keepIsValid||!!t.keepDirtyValues||!s.shouldUnregister&&!wt(m),o.watch=!!s.shouldUnregister,o.keepIsValid=!!t.keepIsValid,o.action=!1,t.keepErrors||(r.errors={}),f.state.next({submitCount:t.keepSubmitCount?r.submitCount:0,isDirty:!h&&(t.keepDirty?r.isDirty:!(!t.keepDefaultValues||_t(e,i))),isSubmitted:!!t.keepIsSubmitted&&r.isSubmitted,dirtyFields:h?{}:t.keepDirtyValues?t.keepDefaultValues&&n?Nt(i,n):r.dirtyFields:t.keepDefaultValues&&e?Nt(i,e):t.keepDirty?r.dirtyFields:{},touchedFields:t.keepTouched?r.touchedFields:{},errors:t.keepErrors?r.errors:{},isSubmitSuccessful:!!t.keepIsSubmitSuccessful&&r.isSubmitSuccessful,isSubmitting:!1,defaultValues:i})},L=(e,t)=>$(Qe(e)?e(n):e,{...s.resetOptions,...t}),M=e=>{r={...r,...e}},U={control:{register:I,unregister:E,getFieldState:Z,handleSubmit:P,setError:N,_subscribe:j,_runSchema:b,_updateIsValidating:y,_focusError:R,_getWatch:w,_getDirty:x,_setValid:p,_setFieldArray:(e,t=[],d,u,l=!0,m=!0)=>{if(u&&d&&!s.disabled){if(o.action=!0,m&&Array.isArray(Ge(a,e))){const t=d(Ge(a,e),u.argA,u.argB);l&&et(a,e,t)}if(m&&Array.isArray(Ge(r.errors,e))){const t=d(Ge(r.errors,e),u.argA,u.argB);l&&et(r.errors,e,t),((e,t)=>{!Je(Ge(e,t)).length&&Tt(e,t)})(r.errors,e)}if((c.touchedFields||h.touchedFields)&&m&&Array.isArray(Ge(r.touchedFields,e))){const t=d(Ge(r.touchedFields,e),u.argA,u.argB);l&&et(r.touchedFields,e,t)}(c.dirtyFields||h.dirtyFields)&&(r.dirtyFields=Nt(i,n)),f.state.next({name:e,isDirty:x(e,t),dirtyFields:r.dirtyFields,errors:r.errors,isValid:r.isValid})}else et(n,e,t)},_setDisabledField:D,_setErrors:e=>{r.errors=e,f.state.next({errors:r.errors,isValid:!1})},_getFieldArray:e=>Je(Ge(o.mount?n:i,e,s.shouldUnregister?Ge(i,e,[]):[])),_reset:$,_resetDefaultValues:()=>Qe(s.defaultValues)&&s.defaultValues().then(e=>{L(e,s.resetOptions),f.state.next({isLoading:!1})}),_removeUnmounted:()=>{for(const e of d.unMount){const t=Ge(a,e);t&&(t._f.refs?t._f.refs.every(e=>!Ct(e)):!Ct(t._f.ref))&&E(e)}d.unMount=new Set},_disableForm:e=>{Xe(e)&&(f.state.next({disabled:e}),Kt(a,(t,s)=>{const r=Ge(a,s);r&&(t.disabled=r._f.disabled||e,Array.isArray(r._f.refs)&&r._f.refs.forEach(t=>{t.disabled=r._f.disabled||e}))},0,!1))},_subjects:f,_proxyFormState:c,get _fields(){return a},get _formValues(){return n},get _state(){return o},set _state(e){o=e},get _defaultValues(){return i},get _names(){return d},set _names(e){d=e},get _formState(){return r},get _options(){return s},set _options(e){s={...s,...e}}},subscribe:e=>(o.mount=!0,h={...h,...e.formState},j({...e,formState:{...l,...e.formState}})),trigger:T,register:I,handleSubmit:P,watch:(e,t)=>Qe(e)?f.state.subscribe({next:s=>\"values\"in s&&e(w(void 0,t),s)}):w(e,t,!0),setValue:S,getValues:F,reset:L,resetField:(e,t={})=>{Ge(a,e)&&(He(t.defaultValue)?S(e,Ke(Ge(i,e))):(S(e,t.defaultValue),et(i,e,Ke(t.defaultValue))),t.keepTouched||Tt(r.touchedFields,e),t.keepDirty||(Tt(r.dirtyFields,e),r.isDirty=t.defaultValue?x(e,Ke(Ge(i,e))):x()),t.keepError||(Tt(r.errors,e),c.isValid&&p()),f.state.next({...r}))},clearErrors:e=>{e&&bt(e).forEach(e=>Tt(r.errors,e)),f.state.next({errors:e?r.errors:{}})},unregister:E,setError:N,setFocus:(e,t={})=>{const s=Ge(a,e),r=s&&s._f;if(r){const e=r.refs?r.refs[0]:r.ref;e.focus&&setTimeout(()=>{e.focus(),t.shouldSelect&&Qe(e.select)&&e.select()})}},getFieldState:Z};return{...U,formControl:U}}function es(t={}){const s=e.useRef(void 0),r=e.useRef(void 0),[a,i]=e.useState({isDirty:!1,isValidating:!1,isLoading:Qe(t.defaultValues),isSubmitted:!1,isSubmitting:!1,isSubmitSuccessful:!1,isValid:!1,submitCount:0,dirtyFields:{},touchedFields:{},validatingFields:{},errors:t.errors||{},disabled:t.disabled||!1,isReady:!1,defaultValues:Qe(t.defaultValues)?void 0:t.defaultValues});if(!s.current)if(t.formControl)s.current={...t.formControl,formState:a},t.defaultValues&&!Qe(t.defaultValues)&&t.formControl.reset(t.defaultValues,t.resetOptions);else{const{formControl:e,...r}=Qt(t);s.current={...r,formState:a}}const n=s.current.control;return n._options=t,pt(()=>{const e=n._subscribe({formState:n._proxyFormState,callback:()=>i({...n._formState}),reRenderRoot:!0});return i(e=>({...e,isReady:!0})),n._formState.isReady=!0,e},[n]),e.useEffect(()=>n._disableForm(t.disabled),[n,t.disabled]),e.useEffect(()=>{t.mode&&(n._options.mode=t.mode),t.reValidateMode&&(n._options.reValidateMode=t.reValidateMode)},[n,t.mode,t.reValidateMode]),e.useEffect(()=>{t.errors&&(n._setErrors(t.errors),n._focusError())},[n,t.errors]),e.useEffect(()=>{t.shouldUnregister&&n._subjects.state.next({values:n._getWatch()})},[n,t.shouldUnregister]),e.useEffect(()=>{if(n._proxyFormState.isDirty){const e=n._getDirty();e!==a.isDirty&&n._subjects.state.next({isDirty:e})}},[n,a.isDirty]),e.useEffect(()=>{var e;t.values&&!_t(t.values,r.current)?(n._reset(t.values,{keepFieldsRef:!0,...n._options.resetOptions}),(null===(e=n._options.resetOptions)||void 0===e?void 0:e.keepIsValid)||n._setValid(),r.current=t.values,i(e=>({...e}))):n._resetDefaultValues()},[n,t.values]),e.useEffect(()=>{n._state.mount||(n._setValid(),n._state.mount=!0),n._state.watch&&(n._state.watch=!1,n._subjects.state.next({...n._formState})),n._removeUnmounted()}),s.current.formState=((e,t,s,r=!0)=>{const a={defaultValues:t._defaultValues};for(const i in e)Object.defineProperty(a,i,{get:()=>{const s=i;return t._proxyFormState[s]!==ot&&(t._proxyFormState[s]=!r||ot),e[s]}});return a})(a,n),s.current}const ts=(e,t,s)=>{if(e&&\"reportValidity\"in e){const r=Ge(s,t);e.setCustomValidity(r&&r.message||\"\"),e.reportValidity()}},ss=(e,t)=>{for(const s in t.fields){const r=t.fields[s];r&&r.ref&&\"reportValidity\"in r.ref?ts(r.ref,s,e):r.refs&&r.refs.forEach(t=>ts(t,s,e))}},rs=(e,t)=>{t.shouldUseNativeValidation&&ss(e,t);const s={};for(const r in e){const a=Ge(t.fields,r),i=Object.assign(e[r]||{},{ref:a&&a.ref});if(as(t.names||Object.keys(e),r)){const e=Object.assign({},Ge(s,r));et(e,\"root\",i),et(s,r,e)}else et(s,r,i)}return s},as=(e,t)=>e.some(e=>e.startsWith(t+\".\"));var is=function(e,t){for(var s={};e.length;){var r=e[0],a=r.code,i=r.message,n=r.path.join(\".\");if(!s[n])if(\"unionErrors\"in r){var o=r.unionErrors[0].errors[0];s[n]={message:o.message,type:o.code}}else s[n]={message:i,type:a};if(\"unionErrors\"in r&&r.unionErrors.forEach(function(t){return t.errors.forEach(function(t){return e.push(t)})}),t){var d=s[n].types,u=d&&d[r.code];s[n]=gt(n,t,s,a,u?[].concat(u,r.message):r.message)}e.shift()}return s},ns=function(e,t,s){return void 0===s&&(s={}),function(r,a,i){try{return Promise.resolve(function(a,n){try{var o=Promise.resolve(e[\"sync\"===s.mode?\"parse\":\"parseAsync\"](r,t)).then(function(e){return i.shouldUseNativeValidation&&ss({},i),{errors:{},values:s.raw?r:e}})}catch(d){return n(d)}return o&&o.then?o.then(void 0,n):o}(0,function(e){if(t=e,Array.isArray(null==t?void 0:t.errors))return{values:{},errors:rs(is(e.errors,!i.shouldUseNativeValidation&&\"all\"===i.criteriaMode),i)};var t;throw e}))}catch(n){return Promise.reject(n)}}};export{Re as a,De as b,$e as c,Pe as e,Ee as n,Ie as o,je as s,ns as t,es as u};\n"
  },
  {
    "path": "frontend/index.html",
    "content": "<!DOCTYPE html>\n<html translate=\"no\">\n\n<head>\n  <meta charset=\"UTF-8\" />\n  <link id=\"favicon\" rel=\"icon\" type=\"image/svg+xml\" href=\"/static/images/icon.svg\">\n  <link id=\"shortcut-icon\" rel=\"shortcut icon\" href=\"/static/images/icon.svg\">\n  <link id=\"apple-touch-icon\" rel=\"apple-touch-icon\" href=\"/static/images/icon.svg\">\n  <link rel=\"manifest\" href=\"/static/images/site.webmanifest\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n  <meta name=\"google\" content=\"notranslate\" />\n  <title>Fast Note Sync</title>\n  <script type=\"module\" crossorigin src=\"/assets/main-BIi-kGYY.js\"></script>\n  <link rel=\"modulepreload\" crossorigin href=\"/assets/font-loader-CIrh3KnA.js\">\n  <link rel=\"stylesheet\" crossorigin href=\"/assets/font-loader-B-ynJ_1p.css\">\n</head>\n\n<body>\n  <div id=\"root\"></div>\n</body>\n\n</html>"
  },
  {
    "path": "frontend/share.html",
    "content": "<!DOCTYPE html>\n<html translate=\"no\">\n\n<head>\n    <meta charset=\"UTF-8\" />\n    <link id=\"favicon\" rel=\"icon\" type=\"image/svg+xml\" href=\"/static/images/icon.svg\">\n    <link id=\"shortcut-icon\" rel=\"shortcut icon\" href=\"/static/images/icon.svg\">\n    <link id=\"apple-touch-icon\" rel=\"apple-touch-icon\" href=\"/static/images/icon.svg\">\n    <link rel=\"manifest\" href=\"/static/images/site.webmanifest\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <meta name=\"google\" content=\"notranslate\" />\n    <title>Shared Note - Fast Note Sync</title>\n  <script type=\"module\" crossorigin src=\"/assets/share-CN7oeKGv.js\"></script>\n  <link rel=\"modulepreload\" crossorigin href=\"/assets/font-loader-CIrh3KnA.js\">\n  <link rel=\"modulepreload\" crossorigin href=\"/assets/note-handle-IK8dQjtF.js\">\n  <link rel=\"modulepreload\" crossorigin href=\"/assets/tooltip-Dr-qRlmI.js\">\n  <link rel=\"modulepreload\" crossorigin href=\"/assets/index-JfsWWBj_.js\">\n  <link rel=\"modulepreload\" crossorigin href=\"/assets/zap-CLLhzk_y.js\">\n  <link rel=\"modulepreload\" crossorigin href=\"/assets/pencil-DqQhr35g.js\">\n  <link rel=\"modulepreload\" crossorigin href=\"/assets/markdown-editor-CX5kQlgI.js\">\n  <link rel=\"modulepreload\" crossorigin href=\"/assets/share-2-BVJjAadJ.js\">\n  <link rel=\"modulepreload\" crossorigin href=\"/assets/refresh-cw-BxIJAPy3.js\">\n  <link rel=\"modulepreload\" crossorigin href=\"/assets/format-CdHm7RWL.js\">\n  <link rel=\"stylesheet\" crossorigin href=\"/assets/font-loader-B-ynJ_1p.css\">\n  <link rel=\"stylesheet\" crossorigin href=\"/assets/markdown-editor-DMUawZD_.css\">\n</head>\n\n<body>\n    <div id=\"root\"></div>\n</body>\n\n</html>\n"
  },
  {
    "path": "frontend/static/fonts/local.css",
    "content": "@font-face {\n  font-family: \"CustomFont\";\n  src: url(\"./font.woff2\") format(\"woff2\");\n  font-weight: normal;\n  font-style: normal;\n  font-display: swap;\n}\n\n:root {\n  --font-sans: \"CustomFont\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n}\n\nbody {\n  font-family: var(--font-sans) !important;\n}\n\n/* 确保编辑器区域也应用该字体 */\n.cm-editor,\n.cm-content,\n.markdown-preview {\n  font-family: var(--font-sans) !important;\n}\n"
  },
  {
    "path": "frontend/static/fonts/remote.css",
    "content": "@font-face {\n  font-family: \"CustomFont\";\n  src: url(\"https://ik.imagekit.io/haierkeys/LXGWWenKai-Light.woff2\") format(\"woff2\");\n  font-weight: normal;\n  font-style: normal;\n  font-display: swap;\n}\n\n:root {\n  --font-sans: \"CustomFont\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n}\n\nbody {\n  font-family: var(--font-sans) !important;\n}\n\n/* 确保编辑器区域也应用该字体 */\n.cm-editor,\n.cm-content,\n.markdown-preview {\n  font-family: var(--font-sans) !important;\n}\n"
  },
  {
    "path": "frontend/static/images/site.webmanifest",
    "content": "{\n  \"name\": \"Fast Note Sync\",\n  \"short_name\": \"FastNoteSync\",\n  \"icons\": [\n    {\n      \"src\": \"/static/images/icon.svg\",\n      \"sizes\": \"any\",\n      \"type\": \"image/svg+xml\"\n    }\n  ],\n  \"theme_color\": \"#ffffff\",\n  \"background_color\": \"#ffffff\",\n  \"display\": \"standalone\"\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/haierkeys/fast-note-sync-service\n\ngo 1.26.2\n\nrequire (\n\tgithub.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible\n\tgithub.com/aws/aws-sdk-go-v2 v1.41.6\n\tgithub.com/aws/aws-sdk-go-v2/config v1.32.16\n\tgithub.com/aws/aws-sdk-go-v2/credentials v1.19.15\n\tgithub.com/aws/aws-sdk-go-v2/feature/s3/transfermanager v0.1.18\n\tgithub.com/aws/aws-sdk-go-v2/service/s3 v1.100.0\n\tgithub.com/bytedance/sonic v1.15.1\n\tgithub.com/creasty/defaults v1.8.0\n\tgithub.com/denisbrodbeck/machineid v1.0.1\n\tgithub.com/gin-gonic/gin v1.12.0\n\tgithub.com/glebarez/sqlite v1.11.0\n\tgithub.com/go-git/go-git/v5 v5.18.0\n\tgithub.com/go-playground/locales v0.14.1\n\tgithub.com/go-playground/universal-translator v0.18.1\n\tgithub.com/go-playground/validator/v10 v10.30.2\n\tgithub.com/golang-jwt/jwt/v5 v5.3.1\n\tgithub.com/google/uuid v1.6.0\n\tgithub.com/gookit/goutil v0.7.4\n\tgithub.com/haierkeys/gormTracing v0.0.0-20250102131738-31ab6d84a1ab\n\tgithub.com/jinzhu/copier v0.4.0\n\tgithub.com/juju/ratelimit v1.0.2\n\tgithub.com/leanovate/gopter v0.2.11\n\tgithub.com/lxzan/gws v1.9.1\n\tgithub.com/mark3labs/mcp-go v0.49.0\n\tgithub.com/opentracing/opentracing-go v1.2.0\n\tgithub.com/pkg/errors v0.9.1\n\tgithub.com/prometheus/client_golang v1.23.2\n\tgithub.com/radovskyb/watcher v1.0.7\n\tgithub.com/robfig/cron/v3 v3.0.1\n\tgithub.com/sergi/go-diff v1.4.0\n\tgithub.com/shirou/gopsutil/v4 v4.26.3\n\tgithub.com/spf13/cobra v1.10.2\n\tgithub.com/stretchr/testify v1.11.1\n\tgithub.com/studio-b12/gowebdav v0.12.0\n\tgithub.com/swaggo/files v1.0.1\n\tgithub.com/swaggo/gin-swagger v1.6.1\n\tgithub.com/swaggo/swag v1.16.6\n\tgithub.com/uber/jaeger-client-go v2.30.0+incompatible\n\tgithub.com/w3liu/go-common v0.0.0-20210108072342-826b2f3582be\n\tgo.uber.org/zap v1.27.1\n\tgolang.ngrok.com/ngrok/v2 v2.1.4\n\tgolang.org/x/crypto v0.50.0\n\tgolang.org/x/mod v0.35.0\n\tgolang.org/x/sync v0.20.0\n\tgolang.org/x/text v0.36.0\n\tgolang.org/x/tools v0.44.0\n\tgopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df\n\tgopkg.in/yaml.v3 v3.0.1\n\tgorm.io/driver/mysql v1.6.0\n\tgorm.io/driver/postgres v1.6.0\n\tgorm.io/driver/sqlite v1.6.0\n\tgorm.io/gen v0.3.27\n\tgorm.io/gorm v1.31.1\n\tgorm.io/plugin/dbresolver v1.6.2\n)\n\nrequire (\n\tdario.cat/mergo v1.0.2 // indirect\n\tfilippo.io/edwards25519 v1.2.0 // indirect\n\tgithub.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect\n\tgithub.com/KyleBanks/depth v1.2.1 // indirect\n\tgithub.com/Microsoft/go-winio v0.6.2 // indirect\n\tgithub.com/ProtonMail/go-crypto v1.4.1 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.9 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/internal/v4a v1.4.23 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.14 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.22 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/signin v1.0.10 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/sso v1.30.16 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.20 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/sts v1.42.0 // indirect\n\tgithub.com/aws/smithy-go v1.25.1 // indirect\n\tgithub.com/beorn7/perks v1.0.1 // indirect\n\tgithub.com/bytedance/gopkg v0.1.4 // indirect\n\tgithub.com/bytedance/sonic/loader v0.5.1 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/cloudflare/circl v1.6.3 // indirect\n\tgithub.com/cloudwego/base64x v0.1.6 // indirect\n\tgithub.com/cyphar/filepath-securejoin v0.6.1 // indirect\n\tgithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect\n\tgithub.com/dustin/go-humanize v1.0.1 // indirect\n\tgithub.com/ebitengine/purego v0.10.0 // indirect\n\tgithub.com/emirpasic/gods v1.18.1 // indirect\n\tgithub.com/gabriel-vasile/mimetype v1.4.13 // indirect\n\tgithub.com/gin-contrib/sse v1.1.1 // indirect\n\tgithub.com/glebarez/go-sqlite v1.22.0 // indirect\n\tgithub.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect\n\tgithub.com/go-git/go-billy/v5 v5.8.0 // indirect\n\tgithub.com/go-ole/go-ole v1.3.0 // indirect\n\tgithub.com/go-openapi/jsonpointer v0.23.1 // indirect\n\tgithub.com/go-openapi/jsonreference v0.21.5 // indirect\n\tgithub.com/go-openapi/spec v0.22.4 // indirect\n\tgithub.com/go-openapi/swag/conv v0.26.0 // indirect\n\tgithub.com/go-openapi/swag/jsonname v0.26.0 // indirect\n\tgithub.com/go-openapi/swag/jsonutils v0.26.0 // indirect\n\tgithub.com/go-openapi/swag/loading v0.26.0 // indirect\n\tgithub.com/go-openapi/swag/stringutils v0.26.0 // indirect\n\tgithub.com/go-openapi/swag/typeutils v0.26.0 // indirect\n\tgithub.com/go-openapi/swag/yamlutils v0.26.0 // indirect\n\tgithub.com/go-sql-driver/mysql v1.9.3 // indirect\n\tgithub.com/goccy/go-json v0.10.6 // indirect\n\tgithub.com/goccy/go-yaml v1.19.2 // indirect\n\tgithub.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect\n\tgithub.com/google/jsonschema-go v0.4.2 // indirect\n\tgithub.com/google/pprof v0.0.0-20250418163039-24c5476c6587 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/jackc/pgpassfile v1.0.0 // indirect\n\tgithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect\n\tgithub.com/jackc/pgx/v5 v5.9.2 // indirect\n\tgithub.com/jackc/puddle/v2 v2.2.2 // indirect\n\tgithub.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect\n\tgithub.com/jinzhu/inflection v1.0.0 // indirect\n\tgithub.com/jinzhu/now v1.1.5 // indirect\n\tgithub.com/jpillora/backoff v1.0.0 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/kevinburke/ssh_config v1.6.0 // indirect\n\tgithub.com/klauspost/compress v1.18.5 // indirect\n\tgithub.com/klauspost/cpuid/v2 v2.3.0 // indirect\n\tgithub.com/leodido/go-urn v1.4.0 // indirect\n\tgithub.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e // indirect\n\tgithub.com/mattn/go-isatty v0.0.22 // indirect\n\tgithub.com/mattn/go-sqlite3 v1.14.42 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect\n\tgithub.com/modern-go/reflect2 v1.0.2 // indirect\n\tgithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect\n\tgithub.com/ncruces/go-strftime v1.0.0 // indirect\n\tgithub.com/pelletier/go-toml/v2 v2.3.0 // indirect\n\tgithub.com/pjbgf/sha1cd v0.5.0 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect\n\tgithub.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect\n\tgithub.com/prometheus/client_model v0.6.2 // indirect\n\tgithub.com/prometheus/common v0.67.5 // indirect\n\tgithub.com/prometheus/procfs v0.20.1 // indirect\n\tgithub.com/quic-go/qpack v0.6.0 // indirect\n\tgithub.com/quic-go/quic-go v0.59.0 // indirect\n\tgithub.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect\n\tgithub.com/skeema/knownhosts v1.3.2 // indirect\n\tgithub.com/spf13/cast v1.10.0 // indirect\n\tgithub.com/spf13/pflag v1.0.10 // indirect\n\tgithub.com/stretchr/objx v0.5.3 // indirect\n\tgithub.com/tklauser/go-sysconf v0.3.16 // indirect\n\tgithub.com/tklauser/numcpus v0.11.0 // indirect\n\tgithub.com/twitchyliquid64/golang-asm v0.15.1 // indirect\n\tgithub.com/uber/jaeger-lib v2.4.1+incompatible // indirect\n\tgithub.com/ugorji/go/codec v1.3.1 // indirect\n\tgithub.com/xanzy/ssh-agent v0.3.3 // indirect\n\tgithub.com/yosida95/uritemplate/v3 v3.0.2 // indirect\n\tgithub.com/yusufpapurcu/wmi v1.2.4 // indirect\n\tgo.mongodb.org/mongo-driver/v2 v2.5.1 // indirect\n\tgo.uber.org/atomic v1.11.0 // indirect\n\tgo.uber.org/multierr v1.11.0 // indirect\n\tgo.yaml.in/yaml/v2 v2.4.4 // indirect\n\tgo.yaml.in/yaml/v3 v3.0.4 // indirect\n\tgolang.ngrok.com/muxado/v2 v2.0.1 // indirect\n\tgolang.org/x/arch v0.26.0 // indirect\n\tgolang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect\n\tgolang.org/x/net v0.53.0 // indirect\n\tgolang.org/x/sys v0.43.0 // indirect\n\tgolang.org/x/term v0.42.0 // indirect\n\tgolang.org/x/time v0.15.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect\n\tgopkg.in/warnings.v0 v0.1.2 // indirect\n\tgorm.io/datatypes v1.2.7 // indirect\n\tgorm.io/hints v1.1.2 // indirect\n\tmodernc.org/libc v1.72.1 // indirect\n\tmodernc.org/mathutil v1.7.1 // indirect\n\tmodernc.org/memory v1.11.0 // indirect\n\tmodernc.org/sqlite v1.50.0 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=\ncloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=\ncloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=\ncloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=\ncloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=\ncloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=\ncloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=\ncloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=\ncloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=\ncloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=\ncloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=\ncloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=\ncloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=\ncloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=\ncloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=\ncloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=\ncloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=\ncloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=\ncloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=\ncloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ncloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=\ncloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=\ncloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=\ncloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=\ndario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=\ndario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\nfilippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=\nfilippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=\ngitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=\ngithub.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM=\ngithub.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=\ngithub.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=\ngithub.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=\ngithub.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=\ngithub.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=\ngithub.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=\ngithub.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM=\ngithub.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=\ngithub.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=\ngithub.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=\ngithub.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g=\ngithub.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=\ngithub.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=\ngithub.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=\ngithub.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=\ngithub.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=\ngithub.com/aws/aws-sdk-go-v2 v1.41.6 h1:1AX0AthnBQzMx1vbmir3Y4WsnJgiydmnJjiLu+LvXOg=\ngithub.com/aws/aws-sdk-go-v2 v1.41.6/go.mod h1:dy0UzBIfwSeot4grGvY1AqFWN5zgziMmWGzysDnHFcQ=\ngithub.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.9 h1:adBsCIIpLbLmYnkQU+nAChU5yhVTvu5PerROm+/Kq2A=\ngithub.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.9/go.mod h1:uOYhgfgThm/ZyAuJGNQ5YgNyOlYfqnGpTHXvk3cpykg=\ngithub.com/aws/aws-sdk-go-v2/config v1.32.16 h1:Q0iQ7quUgJP0F/SCRTieScnaMdXr9h/2+wze1u3cNeM=\ngithub.com/aws/aws-sdk-go-v2/config v1.32.16/go.mod h1:duCCnJEFqpt2RC6no1iK6q+8HpwOAkiUua0pY507dQc=\ngithub.com/aws/aws-sdk-go-v2/credentials v1.19.15 h1:fyvgWTszojq8hEnMi8PPBTvZdTtEVmAVyo+NFLHBhH4=\ngithub.com/aws/aws-sdk-go-v2/credentials v1.19.15/go.mod h1:gJiYyMOjNg8OEdRWOf3CrFQxM2a98qmrtjx1zuiQfB8=\ngithub.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22 h1:IOGsJ1xVWhsi+ZO7/NW8OuZZBtMJLZbk4P5HDjJO0jQ=\ngithub.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22/go.mod h1:b+hYdbU+jGKfXE8kKM6g1+h+L/Go3vMvzlxBsiuGsxg=\ngithub.com/aws/aws-sdk-go-v2/feature/s3/transfermanager v0.1.18 h1:RtBqcC84feV8kTqFVq1uX9q+Sd9bL9jctEWjF02Tss0=\ngithub.com/aws/aws-sdk-go-v2/feature/s3/transfermanager v0.1.18/go.mod h1:B5OSVaJ4Qqvpt6eZTxjFejGOuW8RpMV0IRm+4x+Ksls=\ngithub.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 h1:GmLa5Kw1ESqtFpXsx5MmC84QWa/ZrLZvlJGa2y+4kcQ=\ngithub.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22/go.mod h1:6sW9iWm9DK9YRpRGga/qzrzNLgKpT2cIxb7Vo2eNOp0=\ngithub.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 h1:dY4kWZiSaXIzxnKlj17nHnBcXXBfac6UlsAx2qL6XrU=\ngithub.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22/go.mod h1:KIpEUx0JuRZLO7U6cbV204cWAEco2iC3l061IxlwLtI=\ngithub.com/aws/aws-sdk-go-v2/internal/v4a v1.4.23 h1:FPXsW9+gMuIeKmz7j6ENWcWtBGTe1kH8r9thNt5Uxx4=\ngithub.com/aws/aws-sdk-go-v2/internal/v4a v1.4.23/go.mod h1:7J8iGMdRKk6lw2C+cMIphgAnT8uTwBwNOsGkyOCm80U=\ngithub.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8 h1:HtOTYcbVcGABLOVuPYaIihj6IlkqubBwFj10K5fxRek=\ngithub.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8/go.mod h1:VsK9abqQeGlzPgUr+isNWzPlK2vKe9INMLWnY65f5Xs=\ngithub.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.14 h1:xnvDEnw+pnj5mctWiYuFbigrEzSm35x7k4KS/ZkCANg=\ngithub.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.14/go.mod h1:yS5rNogD8e0Wu9+l3MUwr6eENBzEeGejvINpN5PAYfY=\ngithub.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22 h1:PUmZeJU6Y1Lbvt9WFuJ0ugUK2xn6hIWUBBbKuOWF30s=\ngithub.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22/go.mod h1:nO6egFBoAaoXze24a2C0NjQCvdpk8OueRoYimvEB9jo=\ngithub.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.22 h1:SE+aQ4DEqG53RRCAIHlCf//B2ycxGH7jFkpnAh/kKPM=\ngithub.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.22/go.mod h1:ES3ynECd7fYeJIL6+oax+uIEljmfps0S70BaQzbMd/o=\ngithub.com/aws/aws-sdk-go-v2/service/s3 v1.100.0 h1:7G26Sae6PMKn4kMcU5JzNfrm1YrKwyOhowXPYR2WiWY=\ngithub.com/aws/aws-sdk-go-v2/service/s3 v1.100.0/go.mod h1:Fw9aqhJicIVee1VytBBjH+l+5ov6/PhbtIK/u3rt/ls=\ngithub.com/aws/aws-sdk-go-v2/service/signin v1.0.10 h1:a1Fq/KXn75wSzoJaPQTgZO0wHGqE9mjFnylnqEPTchA=\ngithub.com/aws/aws-sdk-go-v2/service/signin v1.0.10/go.mod h1:p6+MXNxW7IA6dMgHfTAzljuwSKD0NCm/4lbS4t6+7vI=\ngithub.com/aws/aws-sdk-go-v2/service/sso v1.30.16 h1:x6bKbmDhsgSZwv6q19wY/u3rLk/3FGjJWyqKcIRufpE=\ngithub.com/aws/aws-sdk-go-v2/service/sso v1.30.16/go.mod h1:CudnEVKRtLn0+3uMV0yEXZ+YZOKnAtUJ5DmDhilVnIw=\ngithub.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.20 h1:oK/njaL8GtyEihkWMD4k3VgHCT64RQKkZwh0DG5j8ak=\ngithub.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.20/go.mod h1:JHs8/y1f3zY7U5WcuzoJ/yAYGYtNIVPKLIbp61euvmg=\ngithub.com/aws/aws-sdk-go-v2/service/sts v1.42.0 h1:ks8KBcZPh3PYISr5dAiXCM5/Thcuxk8l+PG4+A0exds=\ngithub.com/aws/aws-sdk-go-v2/service/sts v1.42.0/go.mod h1:pFw33T0WLvXU3rw1WBkpMlkgIn54eCB5FYLhjDc9Foo=\ngithub.com/aws/smithy-go v1.25.1 h1:J8ERsGSU7d+aCmdQur5Txg6bVoYelvQJgtZehD12GkI=\ngithub.com/aws/smithy-go v1.25.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=\ngithub.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=\ngithub.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM=\ngithub.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4=\ngithub.com/bytedance/sonic v1.15.1 h1:nJD5PmM0vY7J8CT6MxoqbVAAMhkSmV2HgRAUrrpLoOw=\ngithub.com/bytedance/sonic v1.15.1/go.mod h1:mT2NbXunuaEbnZ+mRIX/vYqKISmgEuHFDI4UzmKx2SA=\ngithub.com/bytedance/sonic/loader v0.5.1 h1:Ygpfa9zwRCCKSlrp5bBP/b/Xzc3VxsAW+5NIYXrOOpI=\ngithub.com/bytedance/sonic/loader v0.5.1/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=\ngithub.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=\ngithub.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=\ngithub.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=\ngithub.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYKk=\ngithub.com/creasty/defaults v1.8.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM=\ngithub.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=\ngithub.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=\ngithub.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=\ngithub.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=\ngithub.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=\ngithub.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=\ngithub.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU=\ngithub.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=\ngithub.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=\ngithub.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=\ngithub.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=\ngithub.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=\ngithub.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=\ngithub.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=\ngithub.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=\ngithub.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=\ngithub.com/gin-contrib/sse v1.1.1 h1:uGYpNwTacv5R68bSGMapo62iLTRa9l5zxGCps4hK6ko=\ngithub.com/gin-contrib/sse v1.1.1/go.mod h1:QXzuVkA0YO7o/gun03UI1Q+FTI8ZV/n5t03kIQAI89s=\ngithub.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=\ngithub.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=\ngithub.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=\ngithub.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=\ngithub.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=\ngithub.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=\ngithub.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=\ngithub.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=\ngithub.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=\ngithub.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=\ngithub.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0=\ngithub.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY=\ngithub.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=\ngithub.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=\ngithub.com/go-git/go-git/v5 v5.18.0 h1:O831KI+0PR51hM2kep6T8k+w0/LIAD490gvqMCvL5hM=\ngithub.com/go-git/go-git/v5 v5.18.0/go.mod h1:pW/VmeqkanRFqR6AljLcs7EA7FbZaN5MQqO7oZADXpo=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=\ngithub.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=\ngithub.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=\ngithub.com/go-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0ry4d1GWg4=\ngithub.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY=\ngithub.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE=\ngithub.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw=\ngithub.com/go-openapi/spec v0.22.4 h1:4pxGjipMKu0FzFiu/DPwN3CTBRlVM2yLf/YTWorYfDQ=\ngithub.com/go-openapi/spec v0.22.4/go.mod h1:WQ6Ai0VPWMZgMT4XySjlRIE6GP1bGQOtEThn3gcWLtQ=\ngithub.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=\ngithub.com/go-openapi/swag/conv v0.26.0 h1:5yGGsPYI1ZCva93U0AoKi/iZrNhaJEjr324YVsiD89I=\ngithub.com/go-openapi/swag/conv v0.26.0/go.mod h1:tpAmIL7X58VPnHHiSO4uE3jBeRamGsFsfdDeDtb5ECE=\ngithub.com/go-openapi/swag/jsonname v0.26.0 h1:gV1NFX9M8avo0YSpmWogqfQISigCmpaiNci8cGECU5w=\ngithub.com/go-openapi/swag/jsonname v0.26.0/go.mod h1:urBBR8bZNoDYGr653ynhIx+gTeIz0ARZxHkAPktJK2M=\ngithub.com/go-openapi/swag/jsonutils v0.26.0 h1:FawFML2iAXsPqmERscuMPIHmFsoP1tOqWkxBaKNMsnA=\ngithub.com/go-openapi/swag/jsonutils v0.26.0/go.mod h1:2VmA0CJlyFqgawOaPI9psnjFDqzyivIqLYN34t9p91E=\ngithub.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0 h1:apqeINu/ICHouqiRZbyFvuDge5jCmmLTqGQ9V95EaOM=\ngithub.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0/go.mod h1:AyM6QT8uz5IdKxk5akv0y6u4QvcL9GWERt0Jx/F/R8Y=\ngithub.com/go-openapi/swag/loading v0.26.0 h1:Apg6zaKhCJurpJer0DCxq99qwmhFddBhaMX7kilDcko=\ngithub.com/go-openapi/swag/loading v0.26.0/go.mod h1:dBxQ/6V2uBaAQdevN18VELE6xSpJWZxLX4txe12JwDg=\ngithub.com/go-openapi/swag/stringutils v0.26.0 h1:qZQngLxs5s7SLijc3N2ZO+fUq2o8LjuWAASSrJuh+xg=\ngithub.com/go-openapi/swag/stringutils v0.26.0/go.mod h1:sWn5uY+QIIspwPhvgnqJsH8xqFT2ZbYcvbcFanRyhFE=\ngithub.com/go-openapi/swag/typeutils v0.26.0 h1:2kdEwdiNWy+JJdOvu5MA2IIg2SylWAFuuyQIKYybfq4=\ngithub.com/go-openapi/swag/typeutils v0.26.0/go.mod h1:oovDuIUvTrEHVMqWilQzKzV4YlSKgyZmFh7AlfABNVE=\ngithub.com/go-openapi/swag/yamlutils v0.26.0 h1:H7O8l/8NJJQ/oiReEN+oMpnGMyt8G0hl460nRZxhLMQ=\ngithub.com/go-openapi/swag/yamlutils v0.26.0/go.mod h1:1evKEGAtP37Pkwcc7EWMF0hedX0/x3Rkvei2wtG/TbU=\ngithub.com/go-openapi/testify/enable/yaml/v2 v2.4.2 h1:5zRca5jw7lzVREKCZVNBpysDNBjj74rBh0N2BGQbSR0=\ngithub.com/go-openapi/testify/enable/yaml/v2 v2.4.2/go.mod h1:XVevPw5hUXuV+5AkI1u1PeAm27EQVrhXTTCPAF85LmE=\ngithub.com/go-openapi/testify/v2 v2.4.2 h1:tiByHpvE9uHrrKjOszax7ZvKB7QOgizBWGBLuq0ePx4=\ngithub.com/go-openapi/testify/v2 v2.4.2/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw=\ngithub.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=\ngithub.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=\ngithub.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=\ngithub.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=\ngithub.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=\ngithub.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=\ngithub.com/go-playground/validator/v10 v10.30.2 h1:JiFIMtSSHb2/XBUbWM4i/MpeQm9ZK2xqPNk8vgvu5JQ=\ngithub.com/go-playground/validator/v10 v10.30.2/go.mod h1:mAf2pIOVXjTEBrwUMGKkCWKKPs9NheYGabeB04txQSc=\ngithub.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=\ngithub.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=\ngithub.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=\ngithub.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=\ngithub.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=\ngithub.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=\ngithub.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=\ngithub.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=\ngithub.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=\ngithub.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=\ngithub.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=\ngithub.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=\ngithub.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=\ngithub.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\ngithub.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8=\ngithub.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20250418163039-24c5476c6587 h1:b/8HpQhvKLSNzH5oTXN2WkNcMl6YB5K3FRbb+i+Ml34=\ngithub.com/google/pprof v0.0.0-20250418163039-24c5476c6587/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gookit/goutil v0.7.4 h1:OWgUngToNz+bPlX5aP+EMG31DraEU63uvKMwwT3vseM=\ngithub.com/gookit/goutil v0.7.4/go.mod h1:vJS9HXctYTCLtCsZot5L5xF+O1oR17cDYO9R0HxBmnU=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/haierkeys/gormTracing v0.0.0-20250102131738-31ab6d84a1ab h1:6DqHkHRuo4LtbsyE3rBtUWr16PL5KEhgbyqOPuAVlD0=\ngithub.com/haierkeys/gormTracing v0.0.0-20250102131738-31ab6d84a1ab/go.mod h1:qtpL5e72mszhPuBF83WbQmnCXTFgr3OpUPNiWOvpKRc=\ngithub.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=\ngithub.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=\ngithub.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=\ngithub.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=\ngithub.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=\ngithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=\ngithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=\ngithub.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw=\ngithub.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=\ngithub.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=\ngithub.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=\ngithub.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=\ngithub.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=\ngithub.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=\ngithub.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=\ngithub.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=\ngithub.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=\ngithub.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=\ngithub.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=\ngithub.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=\ngithub.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=\ngithub.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=\ngithub.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=\ngithub.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=\ngithub.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY=\ngithub.com/kevinburke/ssh_config v1.6.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=\ngithub.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=\ngithub.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=\ngithub.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=\ngithub.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=\ngithub.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=\ngithub.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=\ngithub.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=\ngithub.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=\ngithub.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=\ngithub.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=\ngithub.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e h1:Q6MvJtQK/iRcRtzAscm/zF23XxJlbECiGPyRicsX+Ak=\ngithub.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=\ngithub.com/lxzan/gws v1.9.1 h1:4lbIp4cW0hOLP3ejFHR/uWRy741AURx7oKkNNi2OT9o=\ngithub.com/lxzan/gws v1.9.1/go.mod h1:gXHSCPmTGryWJ4icuqy8Yho32E4YIMHH0fkDRYJRbdc=\ngithub.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=\ngithub.com/mark3labs/mcp-go v0.49.0 h1:7Ssx4d7/T86qnWoJIdye7wEEvUzv39UIbnZb/FqUZMY=\ngithub.com/mark3labs/mcp-go v0.49.0/go.mod h1:BflTAZAzXlrTpiO44gmjMu89n2FO56rJ9m31fp4zd5k=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=\ngithub.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=\ngithub.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=\ngithub.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=\ngithub.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=\ngithub.com/mattn/go-sqlite3 v1.14.42 h1:MigqEP4ZmHw3aIdIT7T+9TLa90Z6smwcthx+Azv4Cgo=\ngithub.com/mattn/go-sqlite3 v1.14.42/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ=\ngithub.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA=\ngithub.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=\ngithub.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\ngithub.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=\ngithub.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=\ngithub.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=\ngithub.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=\ngithub.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=\ngithub.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=\ngithub.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=\ngithub.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=\ngithub.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM=\ngithub.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=\ngithub.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=\ngithub.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=\ngithub.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=\ngithub.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=\ngithub.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=\ngithub.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=\ngithub.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=\ngithub.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=\ngithub.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=\ngithub.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=\ngithub.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=\ngithub.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=\ngithub.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=\ngithub.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=\ngithub.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE=\ngithub.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=\ngithub.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=\ngithub.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=\ngithub.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=\ngithub.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=\ngithub.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=\ngithub.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=\ngithub.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=\ngithub.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc=\ngithub.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=\ngithub.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=\ngithub.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\ngithub.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=\ngithub.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg=\ngithub.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow=\ngithub.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=\ngithub.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=\ngithub.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=\ngithub.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=\ngithub.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=\ngithub.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=\ngithub.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=\ngithub.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=\ngithub.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=\ngithub.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=\ngithub.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/studio-b12/gowebdav v0.12.0 h1:kFRtQECt8jmVAvA6RHBz3geXUGJHUZA6/IKpOVUs5kM=\ngithub.com/studio-b12/gowebdav v0.12.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=\ngithub.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=\ngithub.com/swaggo/gin-swagger v1.6.1 h1:Ri06G4gc9N4t4k8hekMigJ9zKTFSlqj/9paAQCQs7cY=\ngithub.com/swaggo/gin-swagger v1.6.1/go.mod h1:LQ+hJStHakCWRiK/YNYtJOu4mR2FP+pxLnILT/qNiTw=\ngithub.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=\ngithub.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=\ngithub.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=\ngithub.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=\ngithub.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=\ngithub.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=\ngithub.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=\ngithub.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=\ngithub.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=\ngithub.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=\ngithub.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=\ngithub.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=\ngithub.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=\ngithub.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=\ngithub.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=\ngithub.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=\ngithub.com/w3liu/go-common v0.0.0-20210108072342-826b2f3582be h1:NW489IqqgOz/+fV4oDC2NJqQFH+gYYQt8WRLj4v94Ok=\ngithub.com/w3liu/go-common v0.0.0-20210108072342-826b2f3582be/go.mod h1:yHAS/DWXivtrBrO4K45DpIFjQ6LgOi4bUBz7A1iClsE=\ngithub.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=\ngithub.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=\ngithub.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=\ngithub.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=\ngithub.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=\ngithub.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngithub.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=\ngithub.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=\ngithub.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=\ngo.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=\ngo.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=\ngo.mongodb.org/mongo-driver v1.2.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=\ngo.mongodb.org/mongo-driver/v2 v2.5.1 h1:j2U/Qp+wvueSpqitLCSZPT/+ZpVc1xzuwdHWwl7d8ro=\ngo.mongodb.org/mongo-driver/v2 v2.5.1/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=\ngo.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=\ngo.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=\ngo.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=\ngo.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=\ngo.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=\ngo.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=\ngo.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=\ngo.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=\ngo.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=\ngo.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=\ngo.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=\ngo.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=\ngo.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=\ngo.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=\ngolang.ngrok.com/muxado/v2 v2.0.1 h1:jM9i6Pom6GGmnPrHKNR6OJRrUoHFkSZlJ3/S0zqdVpY=\ngolang.ngrok.com/muxado/v2 v2.0.1/go.mod h1:wzxJYX4xiAtmwumzL+QsukVwFRXmPNv86vB8RPpOxyM=\ngolang.ngrok.com/ngrok/v2 v2.1.4 h1:0JQZRqzVGBYluIi5MuhxNYx653qxpN7AiNwNJzoa9DQ=\ngolang.ngrok.com/ngrok/v2 v2.1.4/go.mod h1:1bwK0+ZB4RJCJdqaXs2mvdsjeSk+x4YrrLn8IqOrIGo=\ngolang.org/x/arch v0.26.0 h1:jZ6dpec5haP/fUv1kLCbuJy6dnRrfX6iVK08lZBFpk4=\ngolang.org/x/arch v0.26.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=\ngolang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=\ngolang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\ngolang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA=\ngolang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ=\ngolang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=\ngolang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=\ngolang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=\ngolang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=\ngolang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=\ngolang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=\ngolang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=\ngolang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=\ngolang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=\ngolang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=\ngolang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=\ngolang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=\ngolang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=\ngolang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=\ngolang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=\ngolang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=\ngolang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=\ngolang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=\ngolang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=\ngolang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=\ngolang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=\ngonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=\ngonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=\ngonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=\ngoogle.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=\ngoogle.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=\ngoogle.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=\ngoogle.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=\ngoogle.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=\ngoogle.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=\ngoogle.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=\ngoogle.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=\ngoogle.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=\ngoogle.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=\ngopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=\ngopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=\ngopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=\ngopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngorm.io/datatypes v1.2.7 h1:ww9GAhF1aGXZY3EB3cJPJ7//JiuQo7DlQA7NNlVaTdk=\ngorm.io/datatypes v1.2.7/go.mod h1:M2iO+6S3hhi4nAyYe444Pcb0dcIiOMJ7QHaUXxyiNZY=\ngorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=\ngorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=\ngorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=\ngorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=\ngorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I=\ngorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=\ngorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=\ngorm.io/driver/sqlserver v1.6.0 h1:VZOBQVsVhkHU/NzNhRJKoANt5pZGQAS1Bwc6m6dgfnc=\ngorm.io/driver/sqlserver v1.6.0/go.mod h1:WQzt4IJo/WHKnckU9jXBLMJIVNMVeTu25dnOzehntWw=\ngorm.io/gen v0.3.27 h1:ziocAFLpE7e0g4Rum69pGfB9S6DweTxK8gAun7cU8as=\ngorm.io/gen v0.3.27/go.mod h1:9zquz2xD1f3Eb/eHq4oLn2z6vDVvQlCY5S3uMBLv4EA=\ngorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=\ngorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=\ngorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=\ngorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=\ngorm.io/hints v1.1.2 h1:b5j0kwk5p4+3BtDtYqqfY+ATSxjj+6ptPgVveuynn9o=\ngorm.io/hints v1.1.2/go.mod h1:/ARdpUHAtyEMCh5NNi3tI7FsGh+Cj/MIUlvNxCNCFWg=\ngorm.io/plugin/dbresolver v1.6.2 h1:F4b85TenghUeITqe3+epPSUtHH7RIk3fXr5l83DF8Pc=\ngorm.io/plugin/dbresolver v1.6.2/go.mod h1:tctw63jdrOezFR9HmrKnPkmig3m5Edem9fdxk9bQSzM=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhonnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nmodernc.org/cc/v4 v4.28.1 h1:XpLbkYVQ24E8tX5u8+yWGvaxerxkR/S4zqxI8ZoSBuc=\nmodernc.org/cc/v4 v4.28.1/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=\nmodernc.org/ccgo/v4 v4.33.0 h1:dspBCm75jsj8Y/ufwAMVfe375L2iYdMyQ2QG/v3hL54=\nmodernc.org/ccgo/v4 v4.33.0/go.mod h1:+RhXBoRYzRwaH21mV/aj6XvQRDtfjcZfAlPMsQo8CR0=\nmodernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=\nmodernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=\nmodernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=\nmodernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=\nmodernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo=\nmodernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=\nmodernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=\nmodernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=\nmodernc.org/libc v1.72.1 h1:db1xwJ6u1kE3KHTFTTbe2GCrczHPKzlURP0aDC4NGD0=\nmodernc.org/libc v1.72.1/go.mod h1:HRMiC/PhPGLIPM7GzAFCbI+oSgE3dhZ8FWftmRrHVlY=\nmodernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=\nmodernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=\nmodernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=\nmodernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=\nmodernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=\nmodernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=\nmodernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=\nmodernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=\nmodernc.org/sqlite v1.50.0 h1:eMowQSWLK0MeiQTdmz3lqoF5dqclujdlIKeJA11+7oM=\nmodernc.org/sqlite v1.50.0/go.mod h1:m0w8xhwYUVY3H6pSDwc3gkJ/irZT/0YEXwBlhaxQEew=\nmodernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=\nmodernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=\nmodernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=\nmodernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\nxorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=\nxorm.io/xorm v1.0.6/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=\n"
  },
  {
    "path": "internal/app/app.go",
    "content": "// Package app provides application container, encapsulates all dependencies and services\n// Package app 提供应用容器，封装所有依赖和服务\npackage app\n\nimport (\n\t\"context\"\n\t\"embed\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/workerpool\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/writequeue\"\n\t\"golang.org/x/mod/semver\"\n\n\t\"github.com/gin-gonic/gin/binding\"\n\t\"go.uber.org/zap\"\n\t\"gorm.io/gorm\"\n)\n\n// App application container, encapsulates all dependencies and services\n// App 应用容器，封装所有依赖和服务\ntype App struct {\n\t// Embedded sub-containers\n\t*Infra\n\t*Repositories\n\t*Services\n\n\t// App-level state and control\n\tshutdownCh       chan struct{}\n\tUpgradeSignal    chan string\n\tStartTime        time.Time\n\twg               sync.WaitGroup\n\tcheckVersionMu   sync.RWMutex\n\tcheckVersion     pkgapp.CheckVersionInfo\n\tsupportRecordsMu sync.RWMutex\n\tsupportRecords   map[string][]pkgapp.SupportRecord\n\twss              *pkgapp.WebsocketServer // WebSocket server reference // WebSocket 服务器引用\n}\n\n// NewApp creates application container instance\n// NewApp 创建应用容器实例\n// Initializes all dependencies and performs dependency injection\n// 初始化所有依赖并进行依赖注入\n// cfg: application configuration (required)\n// cfg: 应用配置（必须）\n// logger: zap logger (required)\n// logger: zap 日志器（必须）\n// db: database connection (required)\n// db: 数据库连接（必须）\n// efs: frontend files embedded file system\n// efs: 前端文件嵌入文件系统\nfunc NewApp(cfg *AppConfig, logger *zap.Logger, db *gorm.DB, efs embed.FS) (*App, error) {\n\tif cfg == nil || logger == nil || db == nil {\n\t\treturn nil, fmt.Errorf(\"config, logger and db are required\")\n\t}\n\n\t// 1. Initialize Infrastructure\n\tinfra, err := initInfra(cfg, logger, db)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to initialize infra: %w\", err)\n\t}\n\n\t// 2. Initialize Repositories\n\trepos := initRepositories(infra.Dao)\n\n\t// 3. Initialize App shell\n\ta := &App{\n\t\tInfra:         infra,\n\t\tRepositories:  repos,\n\t\tshutdownCh:    make(chan struct{}),\n\t\tUpgradeSignal: make(chan string, 1),\n\t\tStartTime:     time.Now(),\n\t}\n\n\t// 4. Initialize Services (needs app context for some reason? No, it's just wiring)\n\ta.Services = initServices(cfg, infra, repos, logger)\n\n\t// Load support records\n\ta.loadSupportRecords(efs)\n\n\tlogger.Info(\"App container initialized successfully\")\n\treturn a, nil\n}\n\n// Close releases resources held by application container\n// Close 释放应用容器持有的资源\nfunc (a *App) Close() error {\n\tif a.DB != nil {\n\t\tsqlDB, err := a.DB.DB()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to get sql.DB: %w\", err)\n\t\t}\n\t\tif err := sqlDB.Close(); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to close database: %w\", err)\n\t\t}\n\t\ta.logger.Info(\"Database connection closed\")\n\t}\n\treturn nil\n}\n\n// Config gets application configuration\n// Config 获取应用配置\nfunc (a *App) Config() *AppConfig {\n\treturn a.config\n}\n\n// Logger gets logger\n// Logger 获取日志器\nfunc (a *App) Logger() *zap.Logger {\n\treturn a.logger\n}\n\n// SubmitTask submits task to Worker Pool\n// SubmitTask 提交任务到 Worker Pool\n// returns error if pool is full or closed\n// 返回错误如果池已满或已关闭\nfunc (a *App) SubmitTask(ctx context.Context, task func(context.Context) error) error {\n\treturn a.workerPool.Submit(ctx, task)\n}\n\n// SubmitTaskAsync asynchronously submits task to Worker Pool (does not wait for results)\n// SubmitTaskAsync 异步提交任务到 Worker Pool（不等待结果）\n// returns error if pool is full or closed\n// 返回错误如果池已满或已关闭\nfunc (a *App) SubmitTaskAsync(ctx context.Context, task func(context.Context) error) error {\n\treturn a.workerPool.SubmitAsync(ctx, task)\n}\n\n// Version gets version information\n// Version 获取版本信息\nfunc (a *App) Version() pkgapp.VersionInfo {\n\treturn pkgapp.VersionInfo{\n\t\tVersion:   Version,\n\t\tGitTag:    GitTag,\n\t\tBuildTime: BuildTime,\n\t\tChangelog: strings.ReplaceAll(Changelog, \"@@@\", \"\\n\"),\n\t}\n}\n\n// CheckVersion gets version information\n// CheckVersion 获取版本信息\nfunc (a *App) CheckVersion(pluginVersion string) pkgapp.CheckVersionInfo {\n\ta.checkVersionMu.RLock()\n\tdefer a.checkVersionMu.RUnlock()\n\n\tcv := a.checkVersion\n\n\t// Compare plugin versions\n\t// 比较插件版本\n\tif pluginVersion != \"\" && cv.PluginVersionNewName != \"\" {\n\t\tv1 := pluginVersion\n\t\tif !strings.HasPrefix(v1, \"v\") {\n\t\t\tv1 = \"v\" + v1\n\t\t}\n\t\tv2 := cv.PluginVersionNewName\n\t\tif !strings.HasPrefix(v2, \"v\") {\n\t\t\tv2 = \"v\" + v2\n\t\t}\n\t\tcv.PluginVersionIsNew = semver.Compare(v2, v1) > 0\n\t}\n\n\t// Version number returned to client does not have v prefix\n\t// 返回给客户端的版本号不带 v 前缀\n\tcv.VersionNewName = strings.TrimPrefix(cv.VersionNewName, \"v\")\n\tcv.PluginVersionNewName = strings.TrimPrefix(cv.PluginVersionNewName, \"v\")\n\t// Returns the link information as-is from setting (already set by task)\n\t// 直接返回设置中的链接信息（已由任务设置）\n\treturn cv\n}\n\n// SetCheckVersionInfo sets version check information\n// SetCheckVersionInfo 设置版本检查信息\nfunc (a *App) SetCheckVersionInfo(info pkgapp.CheckVersionInfo) {\n\ta.checkVersionMu.Lock()\n\tdefer a.checkVersionMu.Unlock()\n\ta.checkVersion = info\n}\n\n// SetWSS sets WebSocket server reference\n// SetWSS 设置 WebSocket 服务器引用\nfunc (a *App) SetWSS(wss *pkgapp.WebsocketServer) {\n\ta.wss = wss\n}\n\n// BroadcastClientInfo broadcasts version information to all connected clients\n// BroadcastClientInfo 向所有连接的客户端广播版本信息\nfunc (a *App) BroadcastClientInfo() {\n\tif a.wss != nil {\n\t\ta.wss.BroadcastClientInfo()\n\t}\n}\n\n// Validator gets validator\n// Validator 获取验证器\nfunc (a *App) Validator() pkgapp.ValidatorInterface {\n\tif binding.Validator == nil {\n\t\treturn nil\n\t}\n\tif v, ok := binding.Validator.(pkgapp.ValidatorInterface); ok {\n\t\treturn v\n\t}\n\treturn nil\n}\n\n// IsReturnSuccess whether to return success response\n// IsReturnSuccess 是否返回成功响应\nfunc (a *App) IsReturnSuccess() bool {\n\treturn a.config.App.IsReturnSussess\n}\n\n// GetAuthTokenKey gets Token key\n// GetAuthTokenKey 获取 Token 密钥\nfunc (a *App) GetAuthTokenKey() string {\n\treturn a.config.Security.AuthTokenKey\n}\n\n// IsProductionMode whether it is production mode\n// IsProductionMode 是否为生产模式\n// Judge based on Production field in log configuration\n// 根据日志配置中的 Production 字段判断\nfunc (a *App) IsProductionMode() bool {\n\treturn a.config.Log.Production\n}\n\n// IsPullFromGitHub returns whether current source is GitHub\n// IsPullFromGitHub 返回当前拉取源是否为 GitHub\nfunc (a *App) IsPullFromGitHub() bool {\n\treturn a.sourceSelector.IsGitHub()\n}\n\n// ExecuteWrite executes write operation (serialized through Write Queue)\n// ExecuteWrite 执行写操作（通过 Write Queue 串行化）\n// uid: user ID, used to determine write queue\n// uid: 用户 ID，用于确定写队列\n// fn: write operation function\n// fn: 写操作函数\nfunc (a *App) ExecuteWrite(ctx context.Context, uid int64, fn func() error) error {\n\treturn a.writeQueueMgr.Execute(ctx, strconv.FormatInt(uid, 10), fn)\n}\n\n// WorkerPool gets Worker Pool (for advanced operations)\n// WorkerPool 获取 Worker Pool（用于高级操作）\nfunc (a *App) WorkerPool() *workerpool.Pool {\n\treturn a.workerPool\n}\n\n// WriteQueueManager gets Write Queue Manager (for advanced operations)\n// WriteQueueManager 获取 Write Queue Manager（用于高级操作）\nfunc (a *App) WriteQueueManager() *writequeue.Manager {\n\treturn a.writeQueueMgr\n}\n\n// GetNoteService gets NoteService, supports setting client info\n// GetNoteService 获取 NoteService，支持设置客户端信息\nfunc (a *App) GetNoteService(clientType, clientName, clientVersion string) service.NoteService {\n\tif clientType != \"\" || clientName != \"\" || clientVersion != \"\" {\n\t\treturn a.NoteService.WithClient(clientType, clientName, clientVersion)\n\t}\n\treturn a.NoteService\n}\n\n// GetFolderService returns FolderService instance with client information\n// GetFolderService 返回带有客户端信息的 FolderService 示例\nfunc (a *App) GetFolderService(clientType, clientName, clientVersion string) service.FolderService {\n\treturn a.FolderService.WithClient(clientType, clientName, clientVersion)\n}\n\n// GetFileService gets FileService, supports setting client info\n// GetFileService 获取 FileService，支持设置客户端信息\nfunc (a *App) GetFileService(clientType, clientName, clientVersion string) service.FileService {\n\tif clientType != \"\" || clientName != \"\" || clientVersion != \"\" {\n\t\treturn a.FileService.WithClient(clientType, clientName, clientVersion)\n\t}\n\treturn a.FileService\n}\n\n// GetSettingService gets SettingService, supports setting client info\n// GetSettingService 获取 SettingService，支持设置客户端信息\nfunc (a *App) GetSettingService(clientType, clientName, clientVersion string) service.SettingService {\n\tif clientType != \"\" || clientName != \"\" || clientVersion != \"\" {\n\t\treturn a.SettingService.WithClient(clientType, clientName, clientVersion)\n\t}\n\treturn a.SettingService\n}\n\n// loadSupportRecords loads support records from embedded file system\n// loadSupportRecords 从嵌入文件系统加载打赏记录\nfunc (a *App) loadSupportRecords(efs embed.FS) {\n\ta.supportRecordsMu.Lock()\n\tdefer a.supportRecordsMu.Unlock()\n\ta.supportRecords = make(map[string][]pkgapp.SupportRecord)\n\n\tdocsPath := \"docs\"\n\tentries, err := fs.ReadDir(efs, docsPath)\n\tif err != nil {\n\t\ta.logger.Warn(\"Failed to read docs directory from embedded FS\", zap.Error(err))\n\t\treturn\n\t}\n\n\tfor _, entry := range entries {\n\t\tname := entry.Name()\n\t\tif !entry.IsDir() && strings.HasPrefix(name, \"Support.\") && strings.HasSuffix(name, \".json\") {\n\n\t\t\t// Extract language from Support.{lang}.json\n\t\t\t// 从 Support.{lang}.json 提取语言\n\t\t\tparts := strings.Split(name, \".\")\n\t\t\tif len(parts) < 3 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlang := strings.ToLower(parts[1])\n\n\t\t\tdata, err := efs.ReadFile(docsPath + \"/\" + name)\n\t\t\tif err != nil {\n\t\t\t\ta.logger.Warn(\"Failed to read support record file\", zap.String(\"file\", name), zap.Error(err))\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tvar records []pkgapp.SupportRecord\n\t\t\tif err := json.Unmarshal(data, &records); err != nil {\n\t\t\t\ta.logger.Warn(\"Failed to unmarshal support records\", zap.String(\"file\", name), zap.Error(err))\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\ta.supportRecords[lang] = records\n\t\t\ta.logger.Debug(\"Loaded support records\", zap.String(\"lang\", lang), zap.Int(\"count\", len(records)))\n\t\t}\n\t}\n\n}\n\n// GetSupportRecords gets all support records\n// GetSupportRecords 获取所有打赏记录\nfunc (a *App) GetSupportRecords() map[string][]pkgapp.SupportRecord {\n\ta.supportRecordsMu.RLock()\n\tdefer a.supportRecordsMu.RUnlock()\n\treturn a.supportRecords\n}\n\n// GetSupportRecordsPage gets support records with pagination and sorting\n// GetSupportRecordsPage 分页并排序获取打赏记录\nfunc (a *App) GetSupportRecordsPage(lang, sortBy, sortOrder string, page, pageSize int) ([]pkgapp.SupportRecord, int) {\n\ta.supportRecordsMu.RLock()\n\tdefer a.supportRecordsMu.RUnlock()\n\n\tlang = strings.ToLower(lang)\n\tif lang == \"\" {\n\t\tlang = \"en\"\n\t}\n\n\trecords, ok := a.supportRecords[lang]\n\tif !ok {\n\t\trecords = a.supportRecords[\"en\"]\n\t}\n\n\ttotal := len(records)\n\tif total == 0 {\n\t\treturn []pkgapp.SupportRecord{}, 0\n\t}\n\n\tsortedRecords := make([]pkgapp.SupportRecord, total)\n\tcopy(sortedRecords, records)\n\n\tif sortBy != \"\" {\n\t\tisDesc := strings.ToLower(sortOrder) == \"desc\"\n\t\tsort.SliceStable(sortedRecords, func(i, j int) bool {\n\t\t\tvar less bool\n\t\t\tswitch sortBy {\n\t\t\tcase \"amount\":\n\t\t\t\tamountI, _ := strconv.ParseFloat(sortedRecords[i].Amount, 64)\n\t\t\t\tamountJ, _ := strconv.ParseFloat(sortedRecords[j].Amount, 64)\n\t\t\t\tif amountI == amountJ {\n\t\t\t\t\treturn sortedRecords[i].Time > sortedRecords[j].Time\n\t\t\t\t}\n\t\t\t\tless = amountI < amountJ\n\t\t\tcase \"name\":\n\t\t\t\tless = sortedRecords[i].Name < sortedRecords[j].Name\n\t\t\tcase \"item\":\n\t\t\t\tless = sortedRecords[i].Item < sortedRecords[j].Item\n\t\t\tcase \"time\":\n\t\t\t\tfallthrough\n\t\t\tdefault:\n\t\t\t\tless = sortedRecords[i].Time < sortedRecords[j].Time\n\t\t\t}\n\t\t\tif isDesc {\n\t\t\t\treturn !less\n\t\t\t}\n\t\t\treturn less\n\t\t})\n\t}\n\n\toffset := (page - 1) * pageSize\n\tif offset < 0 {\n\t\toffset = 0\n\t}\n\tif offset >= total {\n\t\treturn []pkgapp.SupportRecord{}, total\n\t}\n\n\tend := offset + pageSize\n\tif end > total {\n\t\tend = total\n\t}\n\n\treturn sortedRecords[offset:end], total\n}\n\n// UpdateSupportRecords updates support records for a specific language\n// UpdateSupportRecords 更新特定语言的打赏记录\nfunc (a *App) UpdateSupportRecords(lang string, records []pkgapp.SupportRecord) {\n\tif lang == \"\" {\n\t\treturn\n\t}\n\tlang = strings.ToLower(lang)\n\ta.supportRecordsMu.Lock()\n\tdefer a.supportRecordsMu.Unlock()\n\tif a.supportRecords == nil {\n\t\ta.supportRecords = make(map[string][]pkgapp.SupportRecord)\n\t}\n\ta.supportRecords[lang] = records\n\ta.logger.Debug(\"Updated support records via background task\", zap.String(\"lang\", lang), zap.Int(\"count\", len(records)))\n}\n\n// TriggerUpgrade triggers the upgrade process\n// TriggerUpgrade 触发升级流程\nfunc (a *App) TriggerUpgrade(newBinaryPath string) {\n\ta.logger.Info(\"Triggering upgrade\", zap.String(\"path\", newBinaryPath))\n\tselect {\n\tcase a.UpgradeSignal <- newBinaryPath:\n\tdefault:\n\t\ta.logger.Warn(\"Upgrade signal already sent\")\n\t}\n}\n\n// DefaultShutdownTimeout default shutdown timeout duration\n// DefaultShutdownTimeout 默认关闭超时时间\nconst DefaultShutdownTimeout = 30 * time.Second\n\n// Shutdown gracefully shuts down application container\n// Shutdown 优雅关闭应用容器\n// Close in order: Worker Pool -> Write Queue Manager -> Database\n// 按顺序关闭：Worker Pool -> Write Queue Manager -> Database\n// ctx used to control shutdown timeout, if nil use default 30 seconds timeout\n// ctx 用于控制关闭超时，如果为 nil 则使用默认 30 秒超时\nfunc (a *App) Shutdown(ctx context.Context) error {\n\ta.logger.Info(\"App container shutting down...\")\n\n\t// If no context provided, use default timeout\n\t// 如果没有提供 context，使用默认超时\n\tif ctx == nil {\n\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithTimeout(context.Background(), DefaultShutdownTimeout)\n\t\tdefer cancel()\n\t}\n\n\t// Mark shutdown\n\t// 标记关闭\n\tselect {\n\tcase <-a.shutdownCh:\n\t\t// Already shut down\n\t\t// 已经关闭\n\t\treturn nil\n\tdefault:\n\t\tclose(a.shutdownCh)\n\t}\n\n\tvar errs []error\n\n\t// 0. Shutdown ShareService (sync final statistics)\n\t// 0. 关闭 ShareService（同步最后的统计数据）\n\tif a.ShareService != nil {\n\t\ta.logger.Info(\"Shutting down share service...\")\n\t\tif err := a.ShareService.Shutdown(ctx); err != nil {\n\t\t\ta.logger.Warn(\"Share service shutdown error\", zap.Error(err))\n\t\t}\n\t}\n\n\t// 0.1 Shutdown NgrokService\n\tif a.NgrokService != nil {\n\t\ta.logger.Info(\"Shutting down ngrok service...\")\n\t\tif err := a.NgrokService.Stop(ctx); err != nil {\n\t\t\ta.logger.Warn(\"Ngrok service shutdown error\", zap.Error(err))\n\t\t}\n\t}\n\n\t// 0.2 Shutdown CloudflareService\n\tif a.CloudflareService != nil {\n\t\ta.logger.Info(\"Shutting down cloudflare service...\")\n\t\tif err := a.CloudflareService.Stop(ctx); err != nil {\n\t\t\ta.logger.Warn(\"Cloudflare service shutdown error\", zap.Error(err))\n\t\t}\n\t}\n\n\t// 0.3 Shutdown GitSyncService (wait for all sync goroutines to finish)\n\t// 0.3 关闭 GitSyncService（等待所有同步 goroutine 结束）\n\tif a.GitSyncService != nil {\n\t\ta.logger.Info(\"Shutting down git sync service...\")\n\t\tif err := a.GitSyncService.Shutdown(ctx); err != nil {\n\t\t\ta.logger.Warn(\"Git sync service shutdown error\", zap.Error(err))\n\t\t} else {\n\t\t\ta.logger.Info(\"Git sync service shutdown completed\")\n\t\t}\n\t}\n\n\t// 0.4 Shutdown BackupService (wait for all backup goroutines to finish)\n\t// 0.4 关闭 BackupService（等待所有备份 goroutine 结束）\n\tif a.BackupService != nil {\n\t\ta.logger.Info(\"Shutting down backup service...\")\n\t\tif err := a.BackupService.Shutdown(ctx); err != nil {\n\t\t\ta.logger.Warn(\"Backup service shutdown error\", zap.Error(err))\n\t\t} else {\n\t\t\ta.logger.Info(\"Backup service shutdown completed\")\n\t\t}\n\t}\n\n\t// 1. Shutdown Worker Pool (stop accepting new tasks, wait for existing tasks to complete)\n\t// 1. 关闭 Worker Pool（停止接受新任务，等待现有任务完成）\n\tif a.workerPool != nil {\n\t\ta.logger.Info(\"Shutting down worker pool...\")\n\t\tif err := a.workerPool.Shutdown(ctx); err != nil {\n\t\t\ta.logger.Warn(\"Worker pool shutdown error\", zap.Error(err))\n\t\t\terrs = append(errs, fmt.Errorf(\"worker pool shutdown: %w\", err))\n\t\t} else {\n\t\t\ta.logger.Info(\"Worker pool shutdown completed\")\n\t\t}\n\t}\n\n\t// 2. Shutdown Write Queue Manager (drain all queues)\n\t// 2. 关闭 Write Queue Manager（排空所有队列）\n\tif a.writeQueueMgr != nil {\n\t\ta.logger.Info(\"Shutting down write queue manager...\")\n\t\tif err := a.writeQueueMgr.Shutdown(ctx); err != nil {\n\t\t\ta.logger.Warn(\"write queue manager shutdown error\", zap.Error(err))\n\t\t\terrs = append(errs, fmt.Errorf(\"write queue manager shutdown: %w\", err))\n\t\t} else {\n\t\t\ta.logger.Info(\"write queue manager shutdown completed\")\n\t\t}\n\t}\n\n\t// 3. Wait for all background operations to complete\n\t// 3. 等待所有后台操作完成\n\tdone := make(chan struct{})\n\tgo func() {\n\t\ta.wg.Wait()\n\t\tclose(done)\n\t}()\n\n\tselect {\n\tcase <-done:\n\t\ta.logger.Info(\"All background operations completed\")\n\tcase <-ctx.Done():\n\t\ta.logger.Warn(\"Shutdown timeout waiting for background operations\")\n\t\terrs = append(errs, fmt.Errorf(\"background operations timeout: %w\", ctx.Err()))\n\t}\n\n\t// 4. Close database connection\n\t// 4. 关闭数据库连接\n\tif err := a.Close(); err != nil {\n\t\terrs = append(errs, err)\n\t}\n\n\tif len(errs) > 0 {\n\t\ta.logger.Warn(\"App container shutdown completed with errors\",\n\t\t\tzap.Int(\"errorCount\", len(errs)))\n\t\treturn fmt.Errorf(\"shutdown completed with %d errors: %v\", len(errs), errs)\n\t}\n\n\ta.logger.Info(\"App container shutdown completed successfully\")\n\treturn nil\n}\n\n// IsShuttingDown checks if application is shutting down\n// IsShuttingDown 检查应用是否正在关闭\nfunc (a *App) IsShuttingDown() bool {\n\tselect {\n\tcase <-a.shutdownCh:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// ShutdownCh returns shutdown signal channel (used for listening to shutdown events)\n// ShutdownCh 返回关闭信号通道（用于监听关闭事件）\nfunc (a *App) ShutdownCh() <-chan struct{} {\n\treturn a.shutdownCh\n}\n\n// TrackOperation tracks background operations (used to wait during graceful shutdown)\n// TrackOperation 跟踪后台操作（用于优雅关闭时等待）\n// returns a function to be called when operation is complete\n// 返回一个函数，在操作完成时调用\nfunc (a *App) TrackOperation() func() {\n\ta.wg.Add(1)\n\treturn func() {\n\t\ta.wg.Done()\n\t}\n}\n"
  },
  {
    "path": "internal/app/config.go",
    "content": "// Package app provides application container, encapsulates all dependencies and services\n// Package app 提供应用容器，封装所有依赖和服务\npackage app\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/workerpool\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/writequeue\"\n\n\t\"github.com/creasty/defaults\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/config\"\n\t\"github.com/pkg/errors\"\n\t\"gopkg.in/yaml.v3\"\n)\n\n// AppConfig application configuration\n// AppConfig 应用配置\ntype AppConfig struct {\n\tFile string `yaml:\"-\"` // config file path, not serialized\n\t// 配置文件路径, 不序列化\n\n\tServer       config.ServerConfig     `yaml:\"server\"`\n\tApp          config.AppSettings      `yaml:\"app\"`\n\tSecurity     config.SecurityConfig   `yaml:\"security\"`\n\tDatabase     config.DatabaseConfig   `yaml:\"database\"`\n\tUserDatabase config.DatabaseConfig   `yaml:\"user-database\"`\n\tLog          config.LogConfig        `yaml:\"log\"`\n\tUser         config.UserConfig       `yaml:\"user\"`\n\tTracer       config.TracerConfig     `yaml:\"tracer\"`\n\tShortLink    config.ShortLinkConfig  `yaml:\"short-link\"`\n\tStorage      config.StorageConfig    `yaml:\"storage\"`\n\tGit          config.GitConfig        `yaml:\"git\"`\n\tWebGUI       config.WebGUIConfig     `yaml:\"webgui\"`\n\tNgrok        config.NgrokConfig      `yaml:\"ngrok\"`\n\tCloudflare   config.CloudflareConfig `yaml:\"cloudflare\"`\n}\n\n// LoadConfig loads configuration from file\n// LoadConfig 从文件加载配置\n// returns configuration instance and absolute path of configuration file\n// 返回配置实例和配置文件的绝对路径\nfunc LoadConfig(f string) (*AppConfig, string, error) {\n\trealpath, err := filepath.Abs(f)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\trealpath = filepath.Clean(realpath)\n\n\tc := new(AppConfig)\n\tc.File = realpath\n\n\t// Set default values\n\t// 设置默认值\n\tif err := defaults.Set(c); err != nil {\n\t\treturn nil, realpath, errors.Wrap(err, \"set default config failed\")\n\t}\n\n\tfile, err := os.ReadFile(realpath)\n\tif err != nil {\n\t\treturn nil, realpath, errors.Wrap(err, \"read config file failed\")\n\t}\n\n\terr = yaml.Unmarshal(file, c)\n\tif err != nil {\n\t\treturn nil, realpath, errors.Wrap(err, \"parse config file failed\")\n\t}\n\n\t// Set default values again to fill fields that exist in YAML but have empty values\n\t// 再次设置默认值，以填充 YAML 中存在但值为空的字段\n\t// defaults.Set filled only when the field is the zero value of the type\n\t// defaults.Set 只有在字段为该类型的零值时才会填充\n\tif err := defaults.Set(c); err != nil {\n\t\treturn nil, realpath, errors.Wrap(err, \"re-set default config failed\")\n\t}\n\n\treturn c, realpath, nil\n}\n\n// Save saves configuration to file\n// Save 保存配置到文件\nfunc (c *AppConfig) Save() error {\n\tdata, err := yaml.Marshal(c)\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"marshal config failed\")\n\t}\n\n\terr = os.WriteFile(c.File, data, 0644)\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"write config file failed\")\n\t}\n\n\treturn nil\n}\n\n// GetWorkerPoolConfig gets Worker Pool configuration\n// GetWorkerPoolConfig 获取 Worker Pool 配置\nfunc (c *AppConfig) GetWorkerPoolConfig() workerpool.Config {\n\tcfg := workerpool.DefaultConfig()\n\n\tif c.App.WorkerPoolMaxWorkers > 0 {\n\t\tcfg.MaxWorkers = c.App.WorkerPoolMaxWorkers\n\t}\n\tif c.App.WorkerPoolQueueSize > 0 {\n\t\tcfg.QueueSize = c.App.WorkerPoolQueueSize\n\t}\n\n\treturn cfg\n}\n\n// GetWriteQueueConfig gets Write Queue configuration\n// GetWriteQueueConfig 获取 Write Queue 配置\nfunc (c *AppConfig) GetWriteQueueConfig() writequeue.Config {\n\tcfg := writequeue.DefaultConfig()\n\n\tif c.App.WriteQueueCapacity > 0 {\n\t\tcfg.QueueCapacity = c.App.WriteQueueCapacity\n\t}\n\tif c.App.WriteQueueTimeout != \"\" {\n\t\tif timeout, err := util.ParseDuration(c.App.WriteQueueTimeout); err == nil {\n\t\t\tcfg.WriteTimeout = timeout\n\t\t}\n\t}\n\tif c.App.WriteQueueIdleTime != \"\" {\n\t\tif idleTime, err := util.ParseDuration(c.App.WriteQueueIdleTime); err == nil {\n\t\t\tcfg.IdleTimeout = idleTime\n\t\t}\n\t}\n\n\treturn cfg\n}\n\n// GetTokenExpiry gets Token expiry duration\n// GetTokenExpiry 获取 Token 过期时间\nfunc (c *AppConfig) GetTokenExpiry() time.Duration {\n\tif expiry, err := util.ParseDuration(c.Security.TokenExpiry); err == nil {\n\t\treturn expiry\n\t}\n\treturn 365 * 24 * time.Hour // Theoretically will not reach here because of default values\n\t// 理论上不会走到这里，因为有默认值\n}\n\n// GetShareTokenExpiry gets share Token expiry duration\n// GetShareTokenExpiry 获取分享 Token 过期时间\nfunc (c *AppConfig) GetShareTokenExpiry() time.Duration {\n\tif expiry, err := util.ParseDuration(c.Security.ShareTokenExpiry); err == nil {\n\t\treturn expiry\n\t}\n\treturn 30 * 24 * time.Hour // Theoretically will not reach here because of default values\n\t// 理论上不会走到这里，因为有默认值\n}\n"
  },
  {
    "path": "internal/app/infra.go",
    "content": "package app\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dao\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/fileurl\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/workerpool\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/writequeue\"\n\t\"go.uber.org/zap\"\n\t\"gorm.io/gorm\"\n)\n\n// Infra encapsulates infrastructure dependencies\ntype Infra struct {\n\tconfig         *AppConfig\n\tlogger         *zap.Logger\n\tDB             *gorm.DB\n\tDao            *dao.Dao\n\tworkerPool     *workerpool.Pool\n\twriteQueueMgr  *writequeue.Manager\n\tTokenManager   pkgapp.TokenManager\n\tsourceSelector *fileurl.SourceSelector\n}\n\n// initInfra initializes infrastructure components\nfunc initInfra(cfg *AppConfig, logger *zap.Logger, db *gorm.DB) (*Infra, error) {\n\tinfra := &Infra{\n\t\tconfig:         cfg,\n\t\tlogger:         logger,\n\t\tDB:             db,\n\t\tsourceSelector: fileurl.NewSourceSelector(cfg.App.PullSource),\n\t}\n\n\t// Worker Pool\n\twpConfig := cfg.GetWorkerPoolConfig()\n\tinfra.workerPool = workerpool.New(&wpConfig, logger)\n\n\t// Write Queue Manager\n\twqConfig := cfg.GetWriteQueueConfig()\n\tinfra.writeQueueMgr = writequeue.New(&wqConfig, logger)\n\n\t// DAO\n\t// DAO\n\tdbCfg := cfg.Database\n\tdbCfg.RunMode = cfg.Server.RunMode\n\n\tuserDbCfg := cfg.UserDatabase\n\tuserDbCfg.RunMode = cfg.Server.RunMode\n\n\tinfra.Dao = dao.New(db, context.Background(),\n\t\tdao.WithConfig(&dbCfg),\n\t\tdao.WithUserDatabaseConfig(&userDbCfg),\n\t\tdao.WithLogger(logger),\n\t\tdao.WithWriteQueueManager(infra.writeQueueMgr),\n\t)\n\n\t// TokenManager\n\ttokenConfig := pkgapp.TokenConfig{\n\t\tSecretKey:     cfg.Security.AuthTokenKey,\n\t\tIssuer:        \"fast-note-sync-service\",\n\t\tExpiry:        cfg.GetTokenExpiry(),\n\t\tShareTokenKey: cfg.Security.ShareTokenKey,\n\t\tShareExpiry:   cfg.GetShareTokenExpiry(),\n\t}\n\tinfra.TokenManager = pkgapp.NewTokenManager(tokenConfig)\n\n\treturn infra, nil\n}\n"
  },
  {
    "path": "internal/app/repos.go",
    "content": "package app\n\nimport (\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dao\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n)\n\n// Repositories encapsulates all repository instances\ntype Repositories struct {\n\tNoteRepo        domain.NoteRepository\n\tVaultRepo       domain.VaultRepository\n\tUserRepo        domain.UserRepository\n\tFileRepo        domain.FileRepository\n\tSettingRepo     domain.SettingRepository\n\tNoteHistoryRepo domain.NoteHistoryRepository\n\tNoteLinkRepo    domain.NoteLinkRepository\n\tShareRepo       domain.UserShareRepository\n\tFolderRepo      domain.FolderRepository\n\tStorageRepo     domain.StorageRepository\n\tBackupRepo      domain.BackupRepository\n\tGitSyncRepo     domain.GitSyncRepository\n\tSyncLogRepo     domain.SyncLogRepository\n\tNoteFTSRepo     domain.NoteFTSRepository\n}\n\n// initRepositories initializes all repositories\nfunc initRepositories(d *dao.Dao) *Repositories {\n\treturn &Repositories{\n\t\tNoteRepo:        dao.NewNoteRepository(d),\n\t\tVaultRepo:       dao.NewVaultRepository(d),\n\t\tUserRepo:        dao.NewUserRepository(d),\n\t\tFileRepo:        dao.NewFileRepository(d),\n\t\tSettingRepo:     dao.NewSettingRepository(d),\n\t\tNoteHistoryRepo: dao.NewNoteHistoryRepository(d),\n\t\tNoteLinkRepo:    dao.NewNoteLinkRepository(d),\n\t\tShareRepo:       dao.NewUserShareRepository(d),\n\t\tFolderRepo:      dao.NewFolderRepository(d),\n\t\tStorageRepo:     dao.NewStorageRepository(d),\n\t\tBackupRepo:      dao.NewBackupRepository(d),\n\t\tGitSyncRepo:     dao.NewGitSyncRepository(d),\n\t\tSyncLogRepo:     dao.NewSyncLogRepository(d),\n\t\tNoteFTSRepo:     dao.NewNoteFTSRepository(d),\n\t}\n}\n"
  },
  {
    "path": "internal/app/restart_linux.go",
    "content": "//go:build ignore\n\npackage app\n"
  },
  {
    "path": "internal/app/restart_unix.go",
    "content": "//go:build !windows\n\npackage app\n\nimport (\n\t\"syscall\"\n)\n\n// RestartProcess restarts the current process using syscall.Exec\n// RestartProcess 使用 syscall.Exec 重启当前进程\nfunc RestartProcess(argv0 string, args []string, env []string) error {\n\treturn syscall.Exec(argv0, args, env)\n}\n"
  },
  {
    "path": "internal/app/restart_windows.go",
    "content": "//go:build windows\n\npackage app\n\nimport (\n\t\"os\"\n\t\"os/exec\"\n)\n\n// RestartProcess restarts the current process by starting a new one and exiting\n// RestartProcess 通过启动新进程并退出来重启当前进程\nfunc RestartProcess(argv0 string, args []string, env []string) error {\n\tcmd := exec.Command(argv0, args[1:]...)\n\tcmd.Stdout = os.Stdout\n\tcmd.Stderr = os.Stderr\n\tcmd.Env = env\n\tif err := cmd.Start(); err != nil {\n\t\treturn err\n\t}\n\tos.Exit(0)\n\treturn nil\n}\n"
  },
  {
    "path": "internal/app/services.go",
    "content": "package app\n\nimport (\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\t\"go.uber.org/zap\"\n)\n\n// Services encapsulates all business service instances\ntype Services struct {\n\tVaultService       service.VaultService\n\tNoteService        service.NoteService\n\tUserService        service.UserService\n\tFileService        service.FileService\n\tSettingService     service.SettingService\n\tNoteHistoryService service.NoteHistoryService\n\tConflictService    service.ConflictService\n\tShareService       service.ShareService\n\tNoteLinkService    service.NoteLinkService\n\tFolderService      service.FolderService\n\tStorageService     service.StorageService\n\tBackupService      service.BackupService\n\tGitSyncService     service.GitSyncService\n\tNgrokService       service.NgrokService\n\tCloudflareService  service.CloudflareService\n\tSyncLogService     service.SyncLogService\n}\n\n// initServices initializes all services\nfunc initServices(cfg *AppConfig, infra *Infra, repos *Repositories, logger *zap.Logger) *Services {\n\tsvcConfig := &service.ServiceConfig{\n\t\tUser: service.UserServiceConfig{\n\t\t\tRegisterIsEnable: cfg.User.RegisterIsEnable,\n\t\t},\n\t\tApp: service.AppServiceConfig{\n\t\t\tSoftDeleteRetentionTime: cfg.App.SoftDeleteRetentionTime,\n\t\t\tHistoryKeepVersions:     cfg.App.HistoryKeepVersions,\n\t\t\tHistorySaveDelay:        cfg.App.HistorySaveDelay,\n\t\t\tShareTokenExpiry:        cfg.Security.ShareTokenExpiry,\n\t\t\tShortLink: service.ShortLinkServiceConfig{\n\t\t\t\tBaseURL:  cfg.ShortLink.BaseURL,\n\t\t\t\tAPIKey:   cfg.ShortLink.APIKey,\n\t\t\t\tPassword: cfg.ShortLink.Password,\n\t\t\t\tCloaking: cfg.ShortLink.Cloaking,\n\t\t\t},\n\t\t},\n\t}\n\n\ts := &Services{}\n\ts.VaultService = service.NewVaultService(\n\t\trepos.VaultRepo,\n\t\trepos.NoteRepo,\n\t\trepos.FileRepo,\n\t\trepos.FolderRepo,\n\t\trepos.SyncLogRepo,\n\t\trepos.NoteHistoryRepo,\n\t\trepos.NoteLinkRepo,\n\t\trepos.SettingRepo,\n\t\trepos.NoteFTSRepo,\n\t\trepos.ShareRepo,\n\t\trepos.GitSyncRepo,\n\t\trepos.BackupRepo,\n\t\tlogger,\n\t)\n\ts.StorageService = service.NewStorageService(repos.StorageRepo, &cfg.Storage)\n\ts.BackupService = service.NewBackupService(repos.BackupRepo, repos.NoteRepo, repos.FolderRepo, repos.FileRepo, repos.VaultRepo, s.StorageService, &cfg.Storage, logger)\n\ts.GitSyncService = service.NewGitSyncService(repos.GitSyncRepo, repos.NoteRepo, repos.FolderRepo, repos.FileRepo, repos.VaultRepo, &cfg.Git, logger)\n\n\t// Initialize SyncLogService first, as NoteService/FileService/SettingService depend on it\n\t// SyncLogService 必须最先初始化，因为其他服务依赖它\n\ts.SyncLogService = service.NewSyncLogService(repos.SyncLogRepo)\n\n\ts.FolderService = service.NewFolderService(repos.FolderRepo, repos.NoteRepo, repos.FileRepo, s.VaultService, s.BackupService, s.SyncLogService, infra.workerPool)\n\ts.NoteService = service.NewNoteService(repos.NoteRepo, repos.NoteLinkRepo, repos.FileRepo, repos.ShareRepo, s.VaultService, s.FolderService, s.BackupService, s.GitSyncService, s.SyncLogService, svcConfig)\n\ts.UserService = service.NewUserService(repos.UserRepo, infra.TokenManager, logger, svcConfig)\n\ts.FileService = service.NewFileService(repos.FileRepo, repos.NoteRepo, s.VaultService, s.FolderService, s.BackupService, s.GitSyncService, s.SyncLogService, svcConfig)\n\ts.SettingService = service.NewSettingService(repos.SettingRepo, s.VaultService, s.SyncLogService, svcConfig)\n\ts.NoteHistoryService = service.NewNoteHistoryService(repos.NoteHistoryRepo, repos.NoteRepo, repos.UserRepo, s.VaultService, s.FolderService, s.NoteService, s.BackupService, s.GitSyncService, logger, &svcConfig.App)\n\ts.ConflictService = service.NewConflictService(repos.NoteRepo, s.VaultService, logger)\n\ts.ShareService = service.NewShareService(repos.ShareRepo, infra.TokenManager, repos.NoteRepo, repos.FileRepo, repos.VaultRepo, logger, svcConfig)\n\ts.NoteLinkService = service.NewNoteLinkService(repos.NoteLinkRepo, repos.NoteRepo, s.VaultService)\n\ts.NgrokService = service.NewNgrokService(logger, cfg.Ngrok.AuthToken, cfg.Ngrok.Domain)\n\ts.CloudflareService = service.NewCloudflareService(logger)\n\n\treturn s\n}\n"
  },
  {
    "path": "internal/app/testing.go",
    "content": "// Package app provides application container, encapsulates all dependencies and services\n// Package app 提供应用容器，封装所有依赖和服务\npackage app\n\nimport (\n\t\"go.uber.org/zap\"\n\t\"gorm.io/gorm\"\n)\n\n// NewTestApp creates a minimal App instance for unit testing.\n// NewTestApp 创建用于单元测试的最小 App 实例。\n// Only the Services field and a nop logger are initialized;\n// 仅初始化 Services 字段和 nop logger；\n// all other infrastructure fields remain zero/nil.\n// 所有其他基础设施字段保持零值/nil。\nfunc NewTestApp(svcs *Services, dbs ...*gorm.DB) *App {\n\tvar db *gorm.DB\n\tif len(dbs) > 0 {\n\t\tdb = dbs[0]\n\t}\n\n\treturn &App{\n\t\tInfra: &Infra{\n\t\t\tlogger: zap.NewNop(), // safe nop logger, prevents Logger() panic // 安全的 nop logger，防止 Logger() panic\n\t\t\tconfig: &AppConfig{},\n\t\t\tDB:     db,\n\t\t},\n\t\tServices: svcs,\n\t}\n}\n"
  },
  {
    "path": "internal/app/version.go",
    "content": "// Package app provides application container, encapsulates all dependencies and services\n// Package app 提供应用容器，封装所有依赖和服务\npackage app\n\n// Version information variables, injected during build\n// 版本信息变量，由构建时注入\nvar (\n\tVersion   string = \"2.13.2\"\n\tGitTag    string = \"2000.01.01.release\"\n\tBuildTime string = \"2000-01-01T00:00:00+0800\"\n\tChangelog string = \"\"\n)\n\n// Application name constants\n// 应用名称常量\nconst (\n\t// Name application name\n\t// Name 应用名称\n\tName = \"Fast Note Sync Service\"\n)\n"
  },
  {
    "path": "internal/config/app.go",
    "content": "package config\n\n// AppSettings application settings\n// AppSettings 应用设置\ntype AppSettings struct {\n\t// DefaultPageSize default page size\n\t// DefaultPageSize 默认页面大小\n\tDefaultPageSize int `yaml:\"default-page-size\" default:\"10\"`\n\t// MaxPageSize maximum page size\n\t// MaxPageSize 最大页面大小\n\tMaxPageSize int `yaml:\"max-page-size\" default:\"100\"`\n\t// DefaultContextTimeout default context timeout duration\n\t// DefaultContextTimeout 默认上下文超时时间\n\tDefaultContextTimeout int `yaml:\"default-context-timeout\" default:\"60\"`\n\t// LogSavePath log save path\n\t// LogSavePath 日志保存路径\n\tLogSavePath string `yaml:\"log-save-fileurl\"`\n\t// LogFile log filename\n\t// LogFile 日志文件名\n\tLogFile string `yaml:\"log-file\"`\n\t// TempPath upload temporary path\n\t// TempPath 上传临时路径\n\tTempPath string `yaml:\"temp-path\" default:\"storage/temp\"`\n\t// IsReturnSussess whether to return success info\n\t// IsReturnSussess 是否返回成功信息\n\tIsReturnSussess bool `yaml:\"is-return-sussess\" default:\"false\"`\n\t// SoftDeleteRetentionTime retention time for soft deleted notes\n\t// SoftDeleteRetentionTime 软删除笔记保留时间\n\tSoftDeleteRetentionTime string `yaml:\"soft-delete-retention-time\" default:\"7d\"`\n\t// SyncLogRetentionTime retention time for sync logs\n\t// SyncLogRetentionTime 同步日志保留时间\n\tSyncLogRetentionTime string `yaml:\"sync-log-retention-time\" default:\"30d\"`\n\t// HistoryKeepVersions number of historical versions to keep, default 100\n\t// HistoryKeepVersions 历史记录保留版本数，默认 100\n\tHistoryKeepVersions int `yaml:\"history-keep-versions\" default:\"100\"`\n\t// HistorySaveDelay historical record save delay time, supports format: 10s (seconds), 1m (minutes), default 10s\n\t// HistorySaveDelay历史记录保存延迟时间，支持格式：10s（秒）、1m（分钟），默认 10s\n\tHistorySaveDelay string `yaml:\"history-save-delay\" default:\"10s\"`\n\t// UploadSessionTimeout file upload session timeout duration\n\t// UploadSessionTimeout 文件上传会话超时时间\n\tUploadSessionTimeout string `yaml:\"upload-session-timeout\" default:\"1d\"`\n\t// FileChunkSize file chunk size\n\t// FileChunkSize 文件分片大小\n\tFileChunkSize string `yaml:\"file-chunk-size\" default:\"512KB\"`\n\t// DownloadSessionTimeout file chunk download timeout duration\n\t// DownloadSessionTimeout 文件分片下载超时时间\n\tDownloadSessionTimeout string `yaml:\"download-session-timeout\" default:\"1h\"`\n\n\t// Worker Pool configurations\n\t// Worker Pool 配置\n\tWorkerPoolMaxWorkers int `yaml:\"worker-pool-max-workers\" default:\"100\"`\n\tWorkerPoolQueueSize  int `yaml:\"worker-pool-queue-size\" default:\"1000\"`\n\n\t// Write Queue configurations\n\t// Write Queue 配置\n\tWriteQueueCapacity int    `yaml:\"write-queue-capacity\" default:\"1000\"`\n\tWriteQueueTimeout  string `yaml:\"write-queue-timeout\" default:\"30s\"`\n\tWriteQueueIdleTime string `yaml:\"write-queue-idle-time\" default:\"10m\"`\n\n\t// WebSocket configurations\n\t// WebSocket 配置\n\tWebSocketReadMaxPayloadSize   string `yaml:\"ws-read-max-payload-size\" default:\"128MB\"`\n\tWebSocketWriteMaxPayloadSize  string `yaml:\"ws-write-max-payload-size\" default:\"128MB\"`\n\tWebSocketParallelEnabled      bool   `yaml:\"ws-parallel-enabled\" default:\"true\"`\n\tWebSocketParallelGolimit      int    `yaml:\"ws-parallel-golimit\" default:\"8\"`\n\tWebSocketCheckUtf8Enabled     bool   `yaml:\"ws-check-utf8-enabled\" default:\"true\"`\n\tWebSocketCompressionEnabled   bool   `yaml:\"ws-compression-enabled\" default:\"true\"`\n\tWebSocketCompressionLevel     int    `yaml:\"ws-compression-level\" default:\"1\"`\n\tWebSocketCompressionThreshold int    `yaml:\"ws-compression-threshold\" default:\"512\"`\n\t// PullSource data pull source: auto | github | cnb\n\t// PullSource 数据拉取源：auto | github | cnb\n\tPullSource string `yaml:\"pull-source\" default:\"auto\"`\n\n\t// ShortLink configurations\n\t// 短链配置\n\tShortLink ShortLinkConfig `yaml:\"short-link\"`\n}\n"
  },
  {
    "path": "internal/config/database.go",
    "content": "package config\n\n// DatabaseConfig database configuration\n// DatabaseConfig 数据库配置\ntype DatabaseConfig struct {\n\tType                string `yaml:\"type\" default:\"sqlite\"`                      // database type (mysql, postgres, sqlite) // 数据库类型 (mysql, postgres, sqlite)\n\tPath                string `yaml:\"path\" default:\"storage/database/db.sqlite3\"` // SQLite database file path // SQLite 数据库文件路径\n\tUserName            string `yaml:\"username\"`                                   // database login username // 数据库登录用户名\n\tPassword            string `yaml:\"password\"`                                   // database login password // 数据库登录密码\n\tHost                string `yaml:\"host\"`                                       // database host // 数据库主机地址\n\tPort                int    `yaml:\"port\"`                                       // database port // 数据库端口\n\tName                string `yaml:\"name\"`                                       // database name // 数据库名\n\tSSLMode             string `yaml:\"ssl-mode\"`                                   // SSL mode (postgres only) // SSL 模式 (仅限 postgres)\n\tTablePrefix         string `yaml:\"table-prefix\"`                               // database table prefix // 数据库表前缀\n\tSchema              string `yaml:\"schema\"`                                     // database schema (postgres only) // 数据库 Schema (仅限 postgres)\n\tAutoMigrate         bool   `yaml:\"auto-migrate\" default:\"true\"`                // whether to enable automatic migration // 是否启用自动迁移\n\tCharset             string `yaml:\"charset\"`                                    // database charset // 数据库字符集\n\tParseTime           bool   `yaml:\"parse-time\"`                                 // whether to parse time // 是否解析时间\n\tMaxIdleConns        int    `yaml:\"max-idle-conns\" default:\"10\"`                // maximum number of idle connections // 最大闲置连接数，默认 10\n\tMaxOpenConns        int    `yaml:\"max-open-conns\" default:\"100\"`               // maximum number of open connections // 最大打开连接数，默认 100\n\tConnMaxLifetime     string `yaml:\"conn-max-lifetime\" default:\"30m\"`            // maximum connection lifetime // 连接最大生命周期\n\tConnMaxIdleTime     string `yaml:\"conn-max-idle-time\" default:\"10m\"`           // maximum idle connection lifetime // 空闲连接最大生命周期\n\tEnableWriteQueue    *bool  `yaml:\"enable-write-queue\" default:\"true\"`          // whether to enable write queue // 是否启用写队列，默认值为真\n\tMaxWriteConcurrency int    `yaml:\"max-write-concurrency\"`                      // maximum concurrent write operations when write queue is disabled // 当 EnableWriteQueue 为 false 时，最大并发写入数，0 或负数表示不限制\n\tRunMode             string `yaml:\"-\"`                                          // run mode (integrated from dao layer) // 运行模式 (从 dao 层整合)\n}\n"
  },
  {
    "path": "internal/config/git.go",
    "content": "package config\n\n// GitConfig Git commit configuration\n// GitConfig Git 提交配置\ntype GitConfig struct {\n\t// Name author name for git commit\n\t// Name git 提交的作者名称\n\tName string `yaml:\"name\" default:\"FNS Service\"`\n\t// Email author email for git commit\n\t// Email git 提交的作者邮箱\n\tEmail string `yaml:\"email\" default:\"fns@email.com\"`\n}\n"
  },
  {
    "path": "internal/config/log.go",
    "content": "package config\n\n// LogConfig log configuration\n// LogConfig 日志配置\ntype LogConfig struct {\n\t// Level log level, see zapcore.ParseLevel\n\t// Level 日志级别，参见 zapcore.ParseLevel\n\tLevel string `yaml:\"level\" default:\"warn\"`\n\t// File log file path, default stderr\n\t// File 日志文件路径，默认为 stderr\n\tFile string `yaml:\"file\" default:\"storage/logs/log.log\"`\n\t// Production whether to enable JSON output\n\t// Production 是否启用 JSON 输出\n\tProduction bool `yaml:\"production\"`\n}\n"
  },
  {
    "path": "internal/config/security.go",
    "content": "package config\n\n// SecurityConfig security configuration\n// SecurityConfig 安全配置\ntype SecurityConfig struct {\n\tAuthTokenKey string `yaml:\"auth-token-key\" default:\"fast-note-sync-Auth-Token\"`\n\tTokenExpiry  string `yaml:\"token-expiry\" default:\"365d\"` // Token expiry, supports format: 7d (days), 24h (hours), 30m (minutes)\n\t// Token 过期时间，支持格式：7d（天）、24h（小时）、30m（分钟）\n\tShareTokenKey string `yaml:\"share-token-key\" default:\"fns\"`\n\t// ShareTokenExpiry share Token expiry\n\t// ShareTokenExpiry 分享 Token 过期时间\n\tShareTokenExpiry string `yaml:\"share-token-expiry\" default:\"30d\"`\n}\n"
  },
  {
    "path": "internal/config/server.go",
    "content": "package config\n\n// ServerConfig server configuration\n// ServerConfig 服务器配置\ntype ServerConfig struct {\n\t// RunMode run mode\n\t// RunMode 运行模式\n\tRunMode string `yaml:\"run-mode\" default:\"release\"`\n\t// HttpPort HTTP port\n\t// HttpPort HTTP 端口\n\tHttpPort string `yaml:\"http-port\" default:\":9000\"`\n\t// ReadTimeout read timeout (seconds)\n\t// ReadTimeout 读取超时（秒）\n\tReadTimeout int `yaml:\"read-timeout\" default:\"60\"`\n\t// WriteTimeout write timeout (seconds)\n\t// WriteTimeout 写入超时（秒）\n\tWriteTimeout int `yaml:\"write-timeout\" default:\"60\"`\n\t// PrivateHttpListen private HTTP listen address\n\t// PrivateHttpListen 私有 HTTP 监听地址\n\tPrivateHttpListen string `yaml:\"private-http-listen\"`\n\t// MCPSSEPingInterval MCP SSE ping interval (seconds)\n\t// MCPSSEPingInterval MCP SSE 保活心跳间隔（秒）\n\tMCPSSEPingInterval int `yaml:\"mcp-sse-ping-interval\" default:\"30\"`\n}\n"
  },
  {
    "path": "internal/config/short_link.go",
    "content": "package config\n\n// ShortLinkConfig short link configuration\n// ShortLinkConfig 短链配置\ntype ShortLinkConfig struct {\n\tBaseURL  string `yaml:\"base-url\" default:\"https://sink.cool\"`\n\tAPIKey   string `yaml:\"api-key\" default:\"SinkCool\"`\n\tPassword string `yaml:\"password\" default:\"\"`\n\tCloaking bool   `yaml:\"cloaking\" default:\"false\"`\n}\n"
  },
  {
    "path": "internal/config/storage.go",
    "content": "package config\n\n// StorageConfig Storage configuration\n// StorageConfig 存储配置\ntype StorageConfig struct {\n\tLocalFS      StorageLocalFSConfig `yaml:\"local-fs\"`\n\tAliyunOSS    StorageBaseConfig    `yaml:\"aliyun-oss\"`\n\tAwsS3        StorageBaseConfig    `yaml:\"aws-s3\"`\n\tCloudflareR2 StorageBaseConfig    `yaml:\"cloudflare-r2\"`\n\tMinIO        StorageBaseConfig    `yaml:\"minio\"`\n\tWebDAV       StorageBaseConfig    `yaml:\"webdav\"`\n}\n\n// StorageLocalFSConfig Local file system storage configuration\n// StorageLocalFSConfig 本地文件系统存储配置\ntype StorageLocalFSConfig struct {\n\tIsEnabled      bool   `yaml:\"is-enable\" default:\"false\"`       // Default false as per user requirement, but logically might need enabled\n\tHttpfsIsEnable bool   `yaml:\"httpfs-is-enable\" default:\"true\"` // Default true\n\tSavePath       string `yaml:\"save-path\" default:\"storage/uploads\"`\n}\n\n// StorageBaseConfig Base configuration for cloud storages\n// StorageBaseConfig 云存储基础配置\ntype StorageBaseConfig struct {\n\tIsEnabled bool `yaml:\"is-enable\" default:\"true\"` // Default enabled\n}\n"
  },
  {
    "path": "internal/config/tracer.go",
    "content": "package config\n\n// TracerConfig request tracing configuration\n// TracerConfig 请求追踪配置\ntype TracerConfig struct {\n\t// Enabled whether tracing is enabled\n\t// Enabled 是否启用追踪\n\tEnabled bool `yaml:\"enabled\" default:\"true\"`\n\t// Header tracing ID request header name, default X-Trace-ID\n\t// Header 追踪 ID 请求头名称，默认 X-Trace-ID\n\tHeader string `yaml:\"header\" default:\"X-Trace-ID\"`\n}\n"
  },
  {
    "path": "internal/config/tunnel.go",
    "content": "package config\n\n// NgrokConfig ngrok configuration\n// NgrokConfig ngrok 配置\ntype NgrokConfig struct {\n\t// Enabled whether to enable ngrok tunnel\n\tEnabled bool `yaml:\"enabled\" default:\"false\"`\n\t// AuthToken ngrok auth token\n\tAuthToken string `yaml:\"auth-token\"`\n\t// Domain ngrok custom domain (optional)\n\tDomain string `yaml:\"domain\"`\n}\n\n// CloudflareConfig cloudflare configuration\n// CloudflareConfig cloudflare 配置\ntype CloudflareConfig struct {\n\t// Enabled whether to enable cloudflare tunnel\n\tEnabled bool `yaml:\"enabled\" default:\"false\"`\n\t// Token cloudflare tunnel token\n\tToken string `yaml:\"token\"`\n\t// LogEnabled whether to enable cloudflare tunnel logging\n\tLogEnabled bool `yaml:\"log-enabled\" default:\"false\"`\n}\n"
  },
  {
    "path": "internal/config/user.go",
    "content": "package config\n\n// UserConfig user configuration\n// UserConfig 用户配置\ntype UserConfig struct {\n\t// RegisterIsEnable whether registration is enabled\n\t// RegisterIsEnable 注册是否启用\n\tRegisterIsEnable bool `yaml:\"register-is-enable\"`\n\t// AdminUID admin UID, 0 means no restriction on admin access\n\t// AdminUID 管理员 UID，0 表示不限制管理员访问\n\tAdminUID int `yaml:\"admin-uid\" default:\"0\"`\n}\n"
  },
  {
    "path": "internal/config/webgui.go",
    "content": "package config\n\n// WebGUIConfig Web GUI configuration\n// WebGUIConfig Web GUI 配置\ntype WebGUIConfig struct {\n\tFontSet string `yaml:\"font-set\" json:\"fontSet\" default:\"\"`\n}\n"
  },
  {
    "path": "internal/dao/backup_repository.go",
    "content": "package dao\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/query\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"gorm.io/gorm\"\n)\n\ntype backupRepository struct {\n\tdao *Dao\n}\n\n// NewBackupRepository creates BackupRepository instance\n// NewBackupRepository 创建 BackupRepository 实例\nfunc NewBackupRepository(dao *Dao) domain.BackupRepository {\n\treturn &backupRepository{dao: dao}\n}\n\nfunc (r *backupRepository) GetKey(uid int64) string {\n\treturn \"user_backup_\" + fmt.Sprintf(\"%d\", uid)\n}\n\nfunc init() {\n\tfactory := func(d *Dao) daoDBCustomKey {\n\t\treturn NewBackupRepository(d).(daoDBCustomKey)\n\t}\n\tRegisterModel(ModelConfig{\n\t\tName:        \"BackupConfig\",\n\t\tRepoFactory: factory,\n\t})\n\tRegisterModel(ModelConfig{\n\t\tName:        \"BackupHistory\",\n\t\tRepoFactory: factory,\n\t})\n}\n\nfunc (r *backupRepository) backup(uid int64) *query.Query {\n\treturn r.dao.QueryWithOnceInit(func(g *gorm.DB) {\n\t\tmodel.AutoMigrate(g, \"BackupConfig\")\n\t\tmodel.AutoMigrate(g, \"BackupHistory\")\n\t}, r.GetKey(uid)+\"#backup\", r.GetKey(uid))\n}\n\nfunc (r *backupRepository) configToDomain(m *model.BackupConfig) *domain.BackupConfig {\n\tif m == nil {\n\t\treturn nil\n\t}\n\treturn &domain.BackupConfig{\n\t\tID:               m.ID,\n\t\tUID:              m.UID,\n\t\tVaultID:          m.VaultID,\n\t\tType:             m.Type,\n\t\tStorageIds:       m.StorageIds,\n\t\tIsEnabled:        m.IsEnabled == 1,\n\t\tCronStrategy:     m.CronStrategy,\n\t\tCronExpression:   m.CronExpression,\n\t\tIncludeVaultName: m.IncludeVaultName == 1,\n\t\tRetentionDays:    int(m.RetentionDays),\n\t\tLastRunTime:      m.LastRunTime,\n\t\tNextRunTime:      m.NextRunTime,\n\t\tLastStatus:       int(m.LastStatus),\n\t\tLastMessage:      m.LastMessage,\n\t\tCreatedAt:        time.Time(m.CreatedAt),\n\t\tUpdatedAt:        time.Time(m.UpdatedAt),\n\t}\n}\n\nfunc (r *backupRepository) configToModel(d *domain.BackupConfig) *model.BackupConfig {\n\tif d == nil {\n\t\treturn nil\n\t}\n\tisEnabled := int64(0)\n\tif d.IsEnabled {\n\t\tisEnabled = 1\n\t}\n\tincludeVaultName := int64(0)\n\tif d.IncludeVaultName {\n\t\tincludeVaultName = 1\n\t}\n\treturn &model.BackupConfig{\n\t\tID:               d.ID,\n\t\tUID:              d.UID,\n\t\tVaultID:          d.VaultID,\n\t\tType:             d.Type,\n\t\tStorageIds:       d.StorageIds,\n\t\tIsEnabled:        isEnabled,\n\t\tCronStrategy:     d.CronStrategy,\n\t\tCronExpression:   d.CronExpression,\n\t\tIncludeVaultName: includeVaultName,\n\t\tRetentionDays:    int64(d.RetentionDays),\n\t\tLastRunTime:      d.LastRunTime,\n\t\tNextRunTime:      d.NextRunTime,\n\t\tLastStatus:       int64(d.LastStatus),\n\t\tLastMessage:      d.LastMessage,\n\t\tCreatedAt:        timex.Time(d.CreatedAt),\n\t\tUpdatedAt:        timex.Time(d.UpdatedAt),\n\t}\n}\n\nfunc (r *backupRepository) historyToDomain(m *model.BackupHistory) *domain.BackupHistory {\n\tif m == nil {\n\t\treturn nil\n\t}\n\treturn &domain.BackupHistory{\n\t\tID:        m.ID,\n\t\tUID:       m.UID,\n\t\tConfigID:  m.ConfigID,\n\t\tStorageID: m.StorageID,\n\t\tType:      m.Type,\n\t\tStartTime: m.StartTime,\n\t\tEndTime:   m.EndTime,\n\t\tStatus:    int(m.Status),\n\t\tFileSize:  m.FileSize,\n\t\tFileCount: m.FileCount,\n\t\tMessage:   m.Message,\n\t\tFilePath:  m.FilePath,\n\t\tCreatedAt: time.Time(m.CreatedAt),\n\t\tUpdatedAt: time.Time(m.UpdatedAt),\n\t}\n}\n\nfunc (r *backupRepository) historyToModel(d *domain.BackupHistory) *model.BackupHistory {\n\tif d == nil {\n\t\treturn nil\n\t}\n\treturn &model.BackupHistory{\n\t\tID:        d.ID,\n\t\tUID:       d.UID,\n\t\tConfigID:  d.ConfigID,\n\t\tStorageID: d.StorageID,\n\t\tType:      d.Type,\n\t\tStartTime: d.StartTime,\n\t\tEndTime:   d.EndTime,\n\t\tStatus:    int64(d.Status),\n\t\tFileSize:  d.FileSize,\n\t\tFileCount: d.FileCount,\n\t\tMessage:   d.Message,\n\t\tFilePath:  d.FilePath,\n\t\tCreatedAt: timex.Time(d.CreatedAt),\n\t\tUpdatedAt: timex.Time(d.UpdatedAt),\n\t}\n}\n\nfunc (r *backupRepository) GetByID(ctx context.Context, id, uid int64) (*domain.BackupConfig, error) {\n\tq := r.backup(uid).BackupConfig\n\tm, err := q.WithContext(ctx).Where(q.UID.Eq(uid), q.ID.Eq(id)).First()\n\tif err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn nil, err\n\t}\n\treturn r.configToDomain(m), nil\n}\n\nfunc (r *backupRepository) DeleteConfig(ctx context.Context, id, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tq := r.backup(uid).BackupConfig\n\t\t// Limit to UID for safety\n\t\t_, err := q.WithContext(ctx).Where(q.UID.Eq(uid), q.ID.Eq(id)).Delete()\n\t\treturn err\n\t})\n}\n\nfunc (r *backupRepository) ListConfigs(ctx context.Context, uid int64) ([]*domain.BackupConfig, error) {\n\tq := r.backup(uid).BackupConfig\n\tconfigs, err := q.WithContext(ctx).Where(q.UID.Eq(uid)).Order(q.ID.Desc()).Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar result []*domain.BackupConfig\n\tfor _, m := range configs {\n\t\tresult = append(result, r.configToDomain(m))\n\t}\n\treturn result, nil\n}\n\nfunc (r *backupRepository) SaveConfig(ctx context.Context, config *domain.BackupConfig, uid int64) (*domain.BackupConfig, error) {\n\tvar result *domain.BackupConfig\n\terr := r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tq := r.backup(uid).BackupConfig\n\t\tm := r.configToModel(config)\n\t\tm.UID = uid\n\n\t\t// If ID > 0, execute update logic\n\t\t// 如果 ID > 0，执行更新逻辑\n\t\tif config.ID > 0 {\n\t\t\t// Check if ID belongs to the current user\n\t\t\t// 检查 ID 是否属于当前用户\n\t\t\told, err := q.WithContext(ctx).Where(q.UID.Eq(uid), q.ID.Eq(config.ID)).First()\n\t\t\tif err != nil {\n\t\t\t\treturn err // RecordNotFound or other error\n\t\t\t}\n\t\t\tm.CreatedAt = old.CreatedAt\n\t\t\tm.UpdatedAt = timex.Now()\n\t\t\t// Update columns\n\t\t\tif err := q.WithContext(ctx).Save(m); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\t// ID == 0, execute create new config logic\n\t\t\t// ID == 0，执行创建新配置逻辑\n\t\t\tm.CreatedAt = timex.Now()\n\t\t\tm.UpdatedAt = timex.Now()\n\t\t\tif err := q.WithContext(ctx).Create(m); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tresult = r.configToDomain(m)\n\t\treturn nil\n\t})\n\treturn result, err\n}\n\nfunc (r *backupRepository) ListEnabledConfigs(ctx context.Context) ([]*domain.BackupConfig, error) {\n\t// This is a cross-database operation, requiring external iteration over all users\n\t// 这是一个跨库操作，需要在外部循环所有用户。\n\t// But in the Repository layer, we only implement operations for specific databases\n\t// 但在 Repository 层，我们只实现针对特定库的操作。\n\t// There is a bit of a contradiction here because the semantics of ListEnabledConfigs is usually \"global\"\n\t// 这里其实有点矛盾，因为 ListEnabledConfigs 的语义通常是“全局”。\n\t// According to the logic of dao.go, we can first get all UIDs and then check them one by one\n\t// 按照 dao.go 的逻辑，我们可以先获取所有 UID，然后逐个查。\n\tuids, err := r.dao.GetAllUserUIDs()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar allConfigs []*domain.BackupConfig\n\tfor _, uid := range uids {\n\t\tq := r.backup(uid).BackupConfig\n\t\tconfigs, err := q.WithContext(ctx).Where(q.UID.Eq(uid), q.IsEnabled.Eq(1)).Find()\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, m := range configs {\n\t\t\tallConfigs = append(allConfigs, r.configToDomain(m))\n\t\t}\n\t}\n\treturn allConfigs, nil\n}\n\nfunc (r *backupRepository) UpdateNextRunTime(ctx context.Context, id, uid int64, nextRun time.Time) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tq := r.backup(uid).BackupConfig\n\t\t_, err := q.WithContext(ctx).Where(q.ID.Eq(id)).Update(q.NextRunTime, nextRun)\n\t\treturn err\n\t})\n}\n\n// Modify interface definition to support calls without UID (if ID is included in Config)\n// 修改接口定义以支持无 UID 调用 (如果 ID 包含在 Config 中)\nfunc (r *backupRepository) CreateHistory(ctx context.Context, history *domain.BackupHistory, uid int64) (*domain.BackupHistory, error) {\n\tvar result *domain.BackupHistory\n\terr := r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tq := r.backup(uid).BackupHistory\n\t\tm := r.historyToModel(history)\n\t\tm.UID = uid\n\t\tm.CreatedAt = timex.Now()\n\t\tm.UpdatedAt = timex.Now()\n\t\tif err := q.WithContext(ctx).Save(m); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresult = r.historyToDomain(m)\n\t\treturn nil\n\t})\n\treturn result, err\n}\n\nfunc (r *backupRepository) ListHistory(ctx context.Context, uid int64, configID int64, page, pageSize int) ([]*domain.BackupHistory, int64, error) {\n\tq := r.backup(uid).BackupHistory\n\toffset := (page - 1) * pageSize\n\tmodelList, count, err := q.WithContext(ctx).Where(q.UID.Eq(uid), q.ConfigID.Eq(configID)).Order(q.ID.Desc()).FindByPage(offset, pageSize)\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tvar list []*domain.BackupHistory\n\tfor _, m := range modelList {\n\t\tlist = append(list, r.historyToDomain(m))\n\t}\n\treturn list, count, nil\n}\n\nfunc (r *backupRepository) ListOldHistory(ctx context.Context, uid int64, configID int64, cutoffTime time.Time) ([]*domain.BackupHistory, error) {\n\tq := r.backup(uid).BackupHistory\n\tmodelList, err := q.WithContext(ctx).Where(q.ConfigID.Eq(configID), q.UID.Eq(uid), q.CreatedAt.Lt(timex.Time(cutoffTime))).Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar list []*domain.BackupHistory\n\tfor _, m := range modelList {\n\t\tlist = append(list, r.historyToDomain(m))\n\t}\n\treturn list, nil\n}\n\nfunc (r *backupRepository) DeleteOldHistory(ctx context.Context, uid int64, configID int64, cutoffTime time.Time) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tq := r.backup(uid).BackupHistory\n\t\t// Delete history records created before cutoffTime\n\t\t_, err := q.WithContext(ctx).Where(q.ConfigID.Eq(configID), q.UID.Eq(uid), q.CreatedAt.Lt(timex.Time(cutoffTime))).Delete()\n\t\treturn err\n\t})\n}\n\n// DisableByVaultID 禁用仓库下的备份任务\nfunc (r *backupRepository) DisableByVaultID(ctx context.Context, vaultID, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tq := r.backup(uid).BackupConfig\n\t\t_, err := q.WithContext(ctx).Where(q.VaultID.Eq(vaultID), q.UID.Eq(uid)).UpdateSimple(q.IsEnabled.Value(0))\n\t\treturn err\n\t})\n}\n\n// Ensure backupRepository implements domain.BackupRepository interface\n// 确保 backupRepository 实现了 domain.BackupRepository 接口\nvar _ domain.BackupRepository = (*backupRepository)(nil)\n"
  },
  {
    "path": "internal/dao/dao.go",
    "content": "package dao\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/config\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/query\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/fileurl\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/writequeue\"\n\n\t\"github.com/glebarez/sqlite\"\n\t\"github.com/haierkeys/gormTracing\"\n\t\"gorm.io/driver/mysql\"\n\t\"gorm.io/driver/postgres\"\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/logger\"\n\t\"gorm.io/gorm/schema\"\n\n\t\"go.uber.org/zap\"\n\t\"golang.org/x/sync/semaphore\"\n)\n\n// DatabaseConfig database configuration (for dependency injection)\n// DatabaseConfig 数据库配置（用于依赖注入）\n// DatabaseConfig is now imported from internal/config\n\ntype dbEntry struct {\n\tdb       *gorm.DB\n\tlastUsed time.Time\n}\n\n// Dao data access object, encapsulates database operations\n// Dao 数据访问对象，封装数据库操作\ntype Dao struct {\n\tDb       *gorm.DB\n\tKeyDb    map[string]*dbEntry\n\tctx      context.Context\n\tonceKeys sync.Map\n\tmu       sync.RWMutex // protects concurrent access to KeyDb // 保护 KeyDb 的并发访问\n\n\tpoolSemaphores sync.Map // map[string]*semaphore.Weighted 针对不同配置的并发控制\n\n\t// 注入的依赖\n\tconfig        *config.DatabaseConfig\n\tuserConfig    *config.DatabaseConfig\n\tlogger        *zap.Logger\n\twriteQueueMgr *writequeue.Manager\n}\n\n// DaoOption option function for configuring Dao\n// DaoOption 用于配置 Dao 的选项函数\ntype DaoOption func(*Dao)\n\n// WithConfig sets database configuration\n// WithConfig 设置数据库配置\nfunc WithConfig(cfg *config.DatabaseConfig) DaoOption {\n\treturn func(d *Dao) {\n\t\td.config = cfg\n\t}\n}\n\n// WithUserDatabaseConfig sets user database configuration\n// WithUserDatabaseConfig 设置用户数据库配置\nfunc WithUserDatabaseConfig(cfg *config.DatabaseConfig) DaoOption {\n\treturn func(d *Dao) {\n\t\td.userConfig = cfg\n\t}\n}\n\n// WithLogger sets logger\n// WithLogger 设置日志器\nfunc WithLogger(logger *zap.Logger) DaoOption {\n\treturn func(d *Dao) {\n\t\td.logger = logger\n\t}\n}\n\n// WithWriteQueueManager sets write queue manager\n// WithWriteQueueManager 设置写队列管理器\nfunc WithWriteQueueManager(wqm *writequeue.Manager) DaoOption {\n\treturn func(d *Dao) {\n\t\td.writeQueueMgr = wqm\n\t}\n}\n\ntype daoDBCustomKey interface {\n\tGetKey(uid int64) string\n}\n\n// ModelConfig describes the database routing information for a model\n// ModelConfig 描述一个模型的数据库路由信息\ntype ModelConfig struct {\n\tName        string\n\tRepoFactory func(d *Dao) daoDBCustomKey\n\tIsMainDB    bool\n}\n\nvar modelConfigs []ModelConfig\n\n// RegisterModel called by each Repository file in init()\n// RegisterModel 供各 Repository 文件在 init() 中调用\nfunc RegisterModel(cfg ModelConfig) {\n\tmodelConfigs = append(modelConfigs, cfg)\n}\n\n// New creates Dao instance (supports dependency injection)\n// db: Main database connection // db: 主数据库连接\n// ctx: Context // ctx: 上下文\n// opts: Optional configuration items // opts: 可选配置项\nfunc New(db *gorm.DB, ctx context.Context, opts ...DaoOption) *Dao {\n\td := &Dao{\n\t\tDb:    db,\n\t\tctx:   ctx,\n\t\tKeyDb: make(map[string]*dbEntry),\n\t}\n\n\t// 应用选项\n\tfor _, opt := range opts {\n\t\topt(d)\n\t}\n\n\t// 如果没有提供 logger，使用 nop logger\n\tif d.logger == nil {\n\t\td.logger = zap.NewNop()\n\t}\n\n\treturn d\n}\n\n// Logger gets the logger\n// Logger 获取日志器\nfunc (d *Dao) Logger() *zap.Logger {\n\tif d.logger != nil {\n\t\treturn d.logger\n\t}\n\treturn zap.NewNop()\n}\n\n// Config gets the database configuration\n// Config 获取数据库配置\nfunc (d *Dao) Config() *config.DatabaseConfig {\n\treturn d.config\n}\n\n// WriteQueueManager gets the write queue manager\n// WriteQueueManager 获取写队列管理器\nfunc (d *Dao) WriteQueueManager() *writequeue.Manager {\n\treturn d.writeQueueMgr\n}\n\n// QueryWithOnceInit 执行带有单次初始化逻辑的数据库查询\n// QueryWithOnceInit executes a database query with once-init logic.\n// 参数说明:\n//   - f func(*gorm.DB): 初始化函数，仅在 onceKey 首次出现时执行 (如 AutoMigrate)\n//   - onceKey string: 用于确保初始化逻辑仅执行一次的唯一标识\n//   - key ...string: 数据库连接标识（可变参数）。不传或为空时使用主数据库；传入时用于路由到特定租户/用户库\n//\n// Parameters:\n//   - f func(*gorm.DB): Initialization function, executed only the first time onceKey is encountered (e.g., AutoMigrate).\n//   - onceKey string: Unique identifier to ensure initialization logic runs only once.\n//   - key ...string: Database connection identifier (variadic). Uses main DB if omitted/empty; uses provided key for tenant/user DB routing.\nfunc (d *Dao) QueryWithOnceInit(f func(*gorm.DB), onceKey string, key ...string) *query.Query {\n\tdb := d.ResolveDB(key...)\n\tif db == nil {\n\t\tkeyName := \"default\"\n\t\tif len(key) > 0 {\n\t\t\tkeyName = key[0]\n\t\t}\n\t\tpanic(fmt.Sprintf(\"数据库 instance 为 nil (key=%s, onceKey=%s),请检查数据库配置和连接\", keyName, onceKey))\n\t}\n\n\t// Construct library-level unique initialization Key\n\t// 构造库级唯一的初始化 Key\n\t// If a key is provided, it indicates a tenant library, and the key needs to be attached to ensure independent initialization of each tenant library\n\t// 如果提供了 key，说明是租户库，需附加 key 以保证每个租户库独立初始化\n\tactualOnceKey := onceKey\n\tif len(key) > 0 && key[0] != \"\" {\n\t\tactualOnceKey = onceKey + \"@\" + key[0]\n\t}\n\n\tif _, loaded := d.onceKeys.LoadOrStore(actualOnceKey, true); !loaded {\n\t\tf(db)\n\t}\n\treturn query.Use(db)\n}\n\n// CleanupConnections cleans up idle database connections\n// CleanupConnections 清理闲置数据库连接\nfunc (d *Dao) CleanupConnections(maxIdle time.Duration) {\n\td.mu.Lock()\n\tdefer d.mu.Unlock()\n\tnow := time.Now()\n\tfor k, v := range d.KeyDb {\n\t\tif now.Sub(v.lastUsed) > maxIdle {\n\t\t\tdelete(d.KeyDb, k)\n\t\t\tif sqlDB, err := v.db.DB(); err == nil {\n\t\t\t\tsqlDB.Close()\n\t\t\t}\n\t\t\td.Logger().Info(\"cleaned up idle DB connection\", zap.String(\"key\", k))\n\t\t}\n\t}\n}\n\nfunc (d *Dao) ResolveDB(key ...string) *gorm.DB {\n\tif len(key) == 0 || key[0] == \"\" {\n\t\treturn d.Db\n\t}\n\treturn d.GetOrCreateDB(key[0])\n}\n\n// resolveConfig gets database configuration\n// key: Database identifier, tries to get user DB config if non-empty // key: 数据库标识，如果非空则尝试获取用户数据库配置\nfunc (d *Dao) resolveConfig(key string) config.DatabaseConfig {\n\tvar cfg config.DatabaseConfig\n\t// If targeted at specific Key (usually user DB) and independent UserDatabase type is configured\n\t// 如果是针对特定 Key (通常为用户库) 且配置了独立的 UserDatabase 类型\n\tif key != \"\" && d.userConfig != nil && d.userConfig.Type != \"\" {\n\t\tcfg = *d.userConfig\n\t} else if d.config != nil {\n\t\t// Otherwise inherits main database configuration (Fallback mode)\n\t\t// 否则继承主数据库配置 (Fallback 模式)\n\t\tcfg = *d.config\n\t}\n\n\t// Final fallback logic: if no type is configured globally, force default to sqlite\n\t// 最终回退逻辑：如果全局均未配置类型，强制默认为 sqlite\n\tif cfg.Type == \"\" {\n\t\tcfg.Type = \"sqlite\"\n\t\tif cfg.Path == \"\" {\n\t\t\tcfg.Path = \"storage/database/db.sqlite3\"\n\t\t}\n\t}\n\treturn cfg\n}\n\nfunc (d *Dao) GetOrCreateDB(key string) *gorm.DB {\n\t// Use read lock to check if already exists\n\t// 使用读锁检查是否已存在\n\td.mu.RLock()\n\tif entry, ok := d.KeyDb[key]; ok {\n\t\tentry.lastUsed = time.Now()\n\t\td.mu.RUnlock()\n\t\treturn entry.db\n\t}\n\td.mu.RUnlock()\n\n\t// Get configuration\n\t// 获取配置\n\tc := d.resolveConfig(key)\n\n\tif (c.Type == \"postgres\") && key != \"\" {\n\t\t// PostgreSQL: Uniform mapping to user_<uid> Schema, ignoring specific Repo prefixes\n\t\t// PostgreSQL: 统一映射到 user_<uid> Schema，忽略具体的 Repo 前缀\n\t\tschemaName, ok := d.extractUserSchema(key)\n\t\tif !ok {\n\t\t\tschemaName = key // Fallback logic, use original key if resolution fails // 回退逻辑，如果无法解析则使用原始 key\n\t\t}\n\n\t\tif err := d.ensurePostgresSchema(schemaName); err != nil {\n\t\t\td.Logger().Error(\"ensurePostgresSchema failed\", zap.String(\"schema\", schemaName), zap.Error(err))\n\t\t\treturn nil\n\t\t}\n\t\tc.Schema = schemaName\n\t\tc.TablePrefix = \"\" // PostgreSQL 下清空前缀，改用 Schema\n\t} else if (c.Type == \"mysql\") && key != \"\" {\n\t\t// MySQL: Uniform mapping to user_<uid> database, implementing tenant-level DB isolation\n\t\t// MySQL: 统一映射到 user_<uid> 数据库，实现租户级库隔离\n\t\tdbName, ok := d.extractUserSchema(key)\n\t\tif !ok {\n\t\t\tdbName = key // Fallback logic, use original key if resolution fails // 回退逻辑，如果无法解析则使用原始 key\n\t\t}\n\n\t\tif err := d.ensureMysqlDatabase(dbName); err != nil {\n\t\t\td.Logger().Error(\"ensureMysqlDatabase failed\", zap.String(\"db\", dbName), zap.Error(err))\n\t\t\treturn nil\n\t\t}\n\t\tc.Name = dbName\n\t\tc.TablePrefix = \"\" // Clear prefix under MySQL, use database routing instead // MySQL 下清空前缀，改用库路由\n\t} else if c.Type == \"sqlite\" && key != \"\" {\n\t\t// SQLite: Maintain multi-file isolation mode (using full key as filename suffix)\n\t\t// SQLite: 维持多文件隔离模式 (使用完整的 key 作为文件名后缀)\n\t\text := filepath.Ext(c.Path)\n\t\tc.Path = c.Path[:len(c.Path)-len(ext)] + \"_\" + key + ext\n\t}\n\n\tdbNew, err := NewEngine(c, d.Logger())\n\tif err != nil {\n\t\td.Logger().Error(\"GetOrCreateDB failed\", zap.String(\"key\", key), zap.Error(err))\n\t\treturn nil\n\t}\n\n\t// Use write lock for storage\n\t// 使用写锁存储\n\td.mu.Lock()\n\tdefer d.mu.Unlock()\n\t// Double check\n\t// 双重检查\n\tif existingEntry, ok := d.KeyDb[key]; ok {\n\t\t// Close the newly created connection\n\t\t// 关闭新创建的连接\n\t\tif sqlDB, err := dbNew.DB(); err == nil {\n\t\t\tsqlDB.Close()\n\t\t}\n\t\texistingEntry.lastUsed = time.Now()\n\t\treturn existingEntry.db\n\t}\n\n\t// Check cache quantity limit; if more than 100 connections, clean up the least recently used one\n\t// 检查缓存数量限制，如果超过 100 个连接，清理最久未使用的\n\tif len(d.KeyDb) >= 100 {\n\t\tvar oldestKey string\n\t\tvar oldestTime time.Time\n\t\tfor k, v := range d.KeyDb {\n\t\t\tif oldestKey == \"\" || v.lastUsed.Before(oldestTime) {\n\t\t\t\toldestKey = k\n\t\t\t\toldestTime = v.lastUsed\n\t\t\t}\n\t\t}\n\t\tif oldestKey != \"\" {\n\t\t\toldEntry := d.KeyDb[oldestKey]\n\t\t\tdelete(d.KeyDb, oldestKey)\n\t\t\tif sqlDB, err := oldEntry.db.DB(); err == nil {\n\t\t\t\tsqlDB.Close()\n\t\t\t}\n\t\t\td.Logger().Info(\"evicted oldest DB connection\", zap.String(\"key\", oldestKey))\n\t\t}\n\t}\n\n\td.KeyDb[key] = &dbEntry{\n\t\tdb:       dbNew,\n\t\tlastUsed: time.Now(),\n\t}\n\n\treturn dbNew\n}\n\n// NewEngine 创建数据库引擎（支持依赖注入）\n// 函数名: NewEngine\n// 函数使用说明: 根据配置创建并初始化 GORM 数据库引擎,配置连接池参数和日志模式。\n// 参数说明:\n//   - c DatabaseConfig: 数据库配置\n//   - zapLogger *zap.Logger: 日志器（可选，为 nil 时使用默认日志）\n//\n// 返回值说明:\n//   - *gorm.DB: 数据库连接实例\n//   - error: 出错时返回错误\nfunc NewEngine(c config.DatabaseConfig, zapLogger *zap.Logger) (*gorm.DB, error) {\n\t// 如果没有指定类型，则默认为 sqlite\n\tif c.Type == \"\" {\n\t\tc.Type = \"sqlite\"\n\t\tif c.Path == \"\" {\n\t\t\tc.Path = \"storage/database/db.sqlite3\"\n\t\t}\n\t}\n\n\tvar db *gorm.DB\n\tvar err error\n\n\tdb, err = gorm.Open(getDialector(c), &gorm.Config{\n\t\tLogger: logger.Default.LogMode(logger.Info),\n\t\tNamingStrategy: schema.NamingStrategy{\n\t\t\tTablePrefix:   c.TablePrefix, // 表名前缀，`User` 的表名应该是 `t_users`\n\t\t\tSingularTable: true,          // 使用单数表名，启用该选项，此时，`User` 的表名应该是 `t_user`\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// 根据运行模式设置日志级别\n\tif c.RunMode == \"debug\" {\n\t\tdb.Config.Logger = logger.Default.LogMode(logger.Info)\n\t} else {\n\t\tdb.Config.Logger = logger.Default.LogMode(logger.Silent)\n\t}\n\n\t// 获取通用数据库对象 sql.DB ，然后使用其提供的功能\n\tsqlDB, err := db.DB()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// 设置连接池参数（带默认值）\n\t// MaxIdleConns: 默认 10\n\tmaxIdleConns := c.MaxIdleConns\n\tif maxIdleConns == 0 {\n\t\tmaxIdleConns = 10\n\t}\n\tsqlDB.SetMaxIdleConns(maxIdleConns)\n\n\t// MaxOpenConns: 默认 100\n\tmaxOpenConns := c.MaxOpenConns\n\tif maxOpenConns == 0 {\n\t\tmaxOpenConns = 100\n\t}\n\tsqlDB.SetMaxOpenConns(maxOpenConns)\n\n\t// ConnMaxLifetime: 默认 30 分钟\n\tconnMaxLifetime := 30 * time.Minute\n\tif c.ConnMaxLifetime != \"\" {\n\t\tif parsed, err := util.ParseDuration(c.ConnMaxLifetime); err == nil {\n\t\t\tconnMaxLifetime = parsed\n\t\t}\n\t}\n\tsqlDB.SetConnMaxLifetime(connMaxLifetime)\n\n\t// ConnMaxIdleTime: 默认 10 分钟\n\tconnMaxIdleTime := 10 * time.Minute\n\tif c.ConnMaxIdleTime != \"\" {\n\t\tif parsed, err := util.ParseDuration(c.ConnMaxIdleTime); err == nil {\n\t\t\tconnMaxIdleTime = parsed\n\t\t}\n\t}\n\tsqlDB.SetConnMaxIdleTime(connMaxIdleTime)\n\n\t_ = db.Use(&gormTracing.OpentracingPlugin{})\n\n\treturn db, nil\n\n}\n\n// getDialector 获取数据库方言（支持依赖注入）\n// 函数名: getDialector\n// 函数使用说明: 根据数据库配置返回对应的 GORM 方言(MySQL 或 SQLite)。\n// 参数说明:\n//   - c DatabaseConfig: 数据库配置\n//\n// 返回值说明:\n//   - gorm.Dialector: GORM 数据库方言\nfunc getDialector(c config.DatabaseConfig) gorm.Dialector {\n\tif c.Type == \"mysql\" {\n\t\thost := c.Host\n\t\tif c.Port != 0 && !strings.Contains(host, \":\") {\n\t\t\thost = fmt.Sprintf(\"%s:%d\", host, c.Port)\n\t\t}\n\t\treturn mysql.Open(fmt.Sprintf(\"%s:%s@tcp(%s)/%s?charset=%s&parseTime=%t&loc=Local\",\n\t\t\tc.UserName,\n\t\t\tc.Password,\n\t\t\thost,\n\t\t\tc.Name,\n\t\t\tc.Charset,\n\t\t\tc.ParseTime,\n\t\t))\n\t} else if c.Type == \"postgres\" {\n\t\tif c.Port == 0 {\n\t\t\tc.Port = 5432\n\t\t}\n\t\tif c.SSLMode == \"\" {\n\t\t\tc.SSLMode = \"disable\"\n\t\t}\n\t\tdsn := fmt.Sprintf(\"host=%s user=%s password=%s dbname=%s port=%d sslmode=%s TimeZone=Asia/Shanghai\",\n\t\t\tc.Host,\n\t\t\tc.UserName,\n\t\t\tc.Password,\n\t\t\tc.Name,\n\t\t\tc.Port,\n\t\t\tc.SSLMode,\n\t\t)\n\t\tif c.Schema != \"\" {\n\t\t\tdsn = fmt.Sprintf(\"%s search_path=%s\", dsn, c.Schema)\n\t\t}\n\t\treturn postgres.Open(dsn)\n\t} else if c.Type == \"sqlite\" {\n\n\t\tfilepath.Dir(c.Path)\n\n\t\tif !fileurl.IsExist(c.Path) {\n\t\t\tfileurl.CreatePath(c.Path, os.ModePerm)\n\t\t}\n\n\t\tabsDb, err := filepath.Abs(c.Path)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tdbSlash := \"/\" + strings.TrimPrefix(filepath.ToSlash(absDb), \"/\")\n\t\tconnStr := \"file://\" + dbSlash + \"?_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)&_pragma=synchronous(NORMAL)&_pragma=busy_timeout(10000)\"\n\t\t// connStr = \"file:///\" + dbSlash + \"?_foreign_keys=1&_journal_mode=WAL&_synchronous=NORMAL&_busy_timeout=10000&_mutex=no\"\n\t\t// connStr := c.Path + \"?_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)&_pragma=synchronous(NORMAL)&_pragma=busy_timeout(10000)\"\n\n\t\treturn sqlite.Open(connStr)\n\t}\n\treturn nil\n\n}\n\n// WithRetry encapsulates retry logic for database operations, mainly to solve SQLite \"database is locked\" issues\n// WithRetry 封装数据库操作的重试逻辑，主要用于解决 SQLite \"database is locked\" 问题\nfunc (d *Dao) WithRetry(fn func() error) error {\n\tmaxRetries := 5\n\tvar err error\n\tfor i := 0; i < maxRetries; i++ {\n\t\terr = fn()\n\t\tif err == nil {\n\t\t\treturn nil\n\t\t}\n\n\t\t// Check if it's a SQLite lock error\n\t\t// 检查是否为 SQLite 锁定错误\n\t\terrStr := err.Error()\n\t\tif strings.Contains(errStr, \"database is locked\") || strings.Contains(errStr, \"SQLITE_BUSY\") {\n\t\t\t// Exponential backoff or fixed delay // 指数退避或固定延迟\n\t\t\ttime.Sleep(time.Duration(100*(i+1)) * time.Millisecond)\n\t\t\tcontinue\n\t\t}\n\t\treturn err // 其他错误直接返回\n\t}\n\treturn err\n}\n\n// ExecuteWrite executes write operation (serialized through write queue)\n// ExecuteWrite 执行写操作（通过写队列串行化）\n// Write operations will be executed serially, and write operations of the same user will be processed in FIFO order\n// 写操作会被串行化执行，同一用户的写操作按 FIFO 顺序处理\n// ctx: Context for timeout and cancellation control // ctx: 上下文，用于超时和取消控制\n// uid: User ID, used to determine write queue // uid: 用户 ID，用于确定写队列\n// fn: Write operation function, receiving user database connection // fn: 写操作函数，接收用户数据库连接\n// Return value: Error of the write operation // 返回值: 写操作的错误\n// Note: Write queue manager must be injected via WithWriteQueueManager // 注意: 必须通过 WithWriteQueueManager 注入写队列管理器\nfunc (d *Dao) ExecuteWrite(ctx context.Context, uid int64, r daoDBCustomKey, fn func(*gorm.DB) error) error {\n\tdbKey := r.GetKey(uid)\n\tcfg := d.resolveConfig(dbKey)\n\n\t// Determine whether to enable write queue\n\t// 判断是否启用写队列\n\tenableQueue := (cfg.EnableWriteQueue == nil || *cfg.EnableWriteQueue)\n\n\tif enableQueue {\n\t\tif d.writeQueueMgr == nil {\n\t\t\treturn fmt.Errorf(\"writeQueueMgr is nil, must inject via WithWriteQueueManager\")\n\t\t}\n\t\treturn d.writeQueueMgr.Execute(ctx, dbKey, func() error {\n\t\t\tdb := d.ResolveDB(dbKey)\n\t\t\tif db == nil {\n\t\t\t\treturn fmt.Errorf(\"database connection is nil (uid=%d, dbKey=%s)\", uid, dbKey)\n\t\t\t}\n\t\t\treturn fn(db.WithContext(ctx))\n\t\t})\n\t}\n\n\t// When not using write queue and concurrency control is configured, check concurrency limits\n\t// 不使用写队列且配置了并发控制时，检查并发限制\n\tif !enableQueue && cfg.MaxWriteConcurrency > 0 {\n\t\t// Determine the group identifier for configuration (used for sharing the same limiter)\n\t\t// 确定配置的分组标识（用于共享同一个限制器）\n\t\t// Simple handling here: main DB and user DB have independent concurrency limit pools\n\t\t// 这里简单处理：主库和用户库各自拥有独立的并发限制池\n\t\tgroupKey := \"main\"\n\t\tif dbKey != \"\" {\n\t\t\tgroupKey = \"user\"\n\t\t}\n\n\t\tactual, _ := d.poolSemaphores.LoadOrStore(groupKey, semaphore.NewWeighted(int64(cfg.MaxWriteConcurrency)))\n\t\tsem := actual.(*semaphore.Weighted)\n\n\t\tif err := sem.Acquire(ctx, 1); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer sem.Release(1)\n\t}\n\n\t// Execute write operation\n\t// 执行写操作\n\tdb := d.ResolveDB(dbKey)\n\tif db == nil {\n\t\treturn fmt.Errorf(\"database connection is nil (uid=%d)\", uid)\n\t}\n\treturn fn(db.WithContext(ctx))\n}\n\n// ExecuteRead executes read operation (executed directly, not through write queue)\n// ExecuteRead 执行读操作（直接执行，不经过写队列）\n// Read operations do not need serialization and can be executed concurrently\n// 读操作不需要串行化，可以并发执行\n// ctx: Context for timeout and cancellation control // ctx: 上下文，用于超时和取消控制\n// uid: User ID, used to get user database connection // uid: 用户 ID，用于获取用户数据库连接\n// fn: Read operation function, receiving user database connection // fn: 读操作函数，接收用户数据库连接\n// Return value: Error of the read operation // 返回值: 读操作的错误\nfunc (d *Dao) ExecuteRead(ctx context.Context, uid int64, r daoDBCustomKey, fn func(*gorm.DB) error) error {\n\tdb := d.ResolveDB(r.GetKey(uid))\n\tif db == nil {\n\t\treturn fmt.Errorf(\"database connection is nil (uid=%d)\", uid)\n\t}\n\treturn fn(db.WithContext(ctx))\n}\n\n// ExecuteWriteWithRetry executes write operation (serialized through write queue, with retries)\n// ExecuteWriteWithRetry 执行写操作（通过写队列串行化，带重试）\n// Combine write queue and retry logic to handle SQLite concurrent write issues\n// 结合写队列和重试逻辑，用于处理 SQLite 并发写入问题\n// ctx: Context for timeout and cancellation control // ctx: 上下文，用于超时和取消控制\n// uid: User ID, used to determine write queue // uid: 用户 ID，用于确定写队列\n// fn: Write operation function, receiving user database connection // fn: 写操作函数，接收用户数据库连接\n// Return value: Error of the write operation // 返回值: 写操作的错误\nfunc (d *Dao) ExecuteWriteWithRetry(ctx context.Context, uid int64, r daoDBCustomKey, fn func(*gorm.DB) error) error {\n\treturn d.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\treturn d.WithRetry(func() error {\n\t\t\treturn fn(db)\n\t\t})\n\t})\n}\n\n// getModelDBKey gets the corresponding database connection Key based on model name\n// getModelDBKey 根据模型名称获取对应的数据库连接 Key\nfunc (d *Dao) getModelDBKey(uid int64, modelKey string) string {\n\tif uid <= 0 {\n\t\treturn \"\" // Main database // 主数据库\n\t}\n\n\tfor _, cfg := range modelConfigs {\n\t\tif cfg.Name == modelKey {\n\t\t\tif cfg.IsMainDB {\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t\tif cfg.RepoFactory != nil {\n\t\t\t\treturn cfg.RepoFactory(d).GetKey(uid)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn \"\"\n}\n\nfunc (d *Dao) AutoMigrate(uid int64, modelKey string) error {\n\t// 1. If modelKey is empty, it means \"full migration\", route migration separately by model\n\t// 1. 如果 modelKey 为空，说明是“全量迁移”，按模型分别路由迁移\n\tif modelKey == \"\" {\n\t\tfor _, cfg := range modelConfigs {\n\t\t\tif err := d.AutoMigrate(uid, cfg.Name); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\tdbKey := d.getModelDBKey(uid, modelKey)\n\tcfg := d.resolveConfig(dbKey)\n\n\t// 2. Verify the AutoMigrate flag in the configuration\n\t// 2. 校验配置中的 AutoMigrate 标志\n\tif !cfg.AutoMigrate {\n\t\treturn nil\n\t}\n\n\tb := d.ResolveDB(dbKey)\n\n\tif b == nil {\n\t\treturn fmt.Errorf(\"database connection is nil for model %s (uid=%d, dbKey=%s)\", modelKey, uid, dbKey)\n\t}\n\treturn model.AutoMigrate(b, modelKey)\n}\n\n// user gets the user query object (internal method)\n// user 获取用户查询对象（内部方法）\nfunc (d *Dao) user() *query.Query {\n\treturn d.QueryWithOnceInit(func(g *gorm.DB) {\n\t\tmodel.AutoMigrate(g, \"User\")\n\t}, \"user#user\")\n}\n\n// GetAllUserUIDs retrieves UIDs of all users\n// GetAllUserUIDs 获取所有用户的UID\n// Return value description:\n// 返回值说明:\n//   - []int64: User UID list // 用户UID列表\n//   - error: Error on failure // 出错时返回错误\nfunc (d *Dao) GetAllUserUIDs() ([]int64, error) {\n\tvar uids []int64\n\tu := d.user().User\n\terr := u.WithContext(d.ctx).Select(u.UID).Where(u.IsDeleted.Eq(0)).Scan(&uids)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn uids, nil\n}\n\n// ensurePostgresSchema ensures the specified Schema exists in PostgreSQL\n// ensurePostgresSchema 确保 PostgreSQL 中指定的 Schema 存在\nfunc (d *Dao) ensurePostgresSchema(schemaName string) error {\n\tif d.userConfig == nil || d.userConfig.Type != \"postgres\" {\n\t\treturn nil\n\t}\n\n\t// Construct basic connection DSN without schema\n\t// 构造不带 schema 的基础连接 DSN\n\tcfg := *d.userConfig\n\tcfg.Schema = \"\"\n\n\t// Use base connection to create a new Schema\n\t// 使用基础连接来创建新的 Schema\n\tdb, err := NewEngine(cfg, d.Logger())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to open root postgres connection: %w\", err)\n\t}\n\tdefer func() {\n\t\tif sqlDB, err := db.DB(); err == nil {\n\t\t\tsqlDB.Close()\n\t\t}\n\t}()\n\n\t// Execute create Schema statement\n\t// 执行创建 Schema 语句\n\terr = db.Exec(fmt.Sprintf(\"CREATE SCHEMA IF NOT EXISTS %s\", schemaName)).Error\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create schema %s: %w\", schemaName, err)\n\t}\n\n\treturn nil\n}\n\n// extractUserSchema extracts uniform user Schema name (e.g., user_1) from connection Key (e.g., user_vault_1)\n// extractUserSchema 从连接 Key (如 user_vault_1) 中提取统一的用户 Schema 名 (如 user_1)\nfunc (d *Dao) extractUserSchema(key string) (string, bool) {\n\t// Find the last underscore, try to extract UID\n\t// 查找最后一个下划线，尝试提取 UID\n\tlastUnder := strings.LastIndex(key, \"_\")\n\tif lastUnder == -1 {\n\t\treturn \"\", false\n\t}\n\tuidStr := key[lastUnder+1:]\n\t// If the last part is pure digits, we consider it the UID and map it to a uniform Schema: user_<uid>\n\t// 如果最后一部分是纯数字，我们认为它是 UID，并映射到统一的 Schema: user_<uid>\n\tif _, err := strconv.ParseInt(uidStr, 10, 64); err == nil {\n\t\treturn \"user_\" + uidStr, true\n\t}\n\treturn \"\", false\n}\n\n// ensureMysqlDatabase ensures the specified database exists in MySQL\n// ensureMysqlDatabase 确保 MySQL 中指定的数据库存在\nfunc (d *Dao) ensureMysqlDatabase(dbName string) error {\n\tif d.userConfig == nil || d.userConfig.Type != \"mysql\" {\n\t\treturn nil\n\t}\n\n\t// Construct basic connection configuration without database name\n\t// 构造不带数据库名的基础连接配置\n\tcfg := *d.userConfig\n\tcfg.Name = \"\"\n\n\t// Use base connection to connect to MySQL service\n\t// 使用基础连接连接到 MySQL 服务\n\tdb, err := NewEngine(cfg, d.Logger())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to open root mysql connection: %w\", err)\n\t}\n\tdefer func() {\n\t\tif sqlDB, err := db.DB(); err == nil {\n\t\t\tsqlDB.Close()\n\t\t}\n\t}()\n\n\t// Execute create database statement\n\t// 执行创建数据库语句\n\t// Note: MySQL database names cannot contain special characters; user_<uid> is safe\n\t// 注意：MySQL 库名不能包含特殊字符，user_<uid> 是安全的\n\terr = db.Exec(fmt.Sprintf(\"CREATE DATABASE IF NOT EXISTS %s CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci\", dbName)).Error\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create database %s: %w\", dbName, err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "internal/dao/dao_helper.go",
    "content": "package dao\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/fileurl\"\n)\n\n// getContentPath gets the content storage path\n// getContentPath 获取内容存储路径\nfunc (d *Dao) GetNoteFolderPath(uid int64, noteID int64) string {\n\treturn filepath.Join(\"storage\", \"vault\", fmt.Sprintf(\"u_%d\", uid), \"note\", fmt.Sprintf(\"n_%d\", noteID))\n}\n\n// getSettingFolderPath gets the setting storage path\n// getSettingFolderPath 获取配置存储路径\nfunc (d *Dao) GetSettingFolderPath(uid int64, settingID int64) string {\n\treturn filepath.Join(\"storage\", \"vault\", fmt.Sprintf(\"u_%d\", uid), \"setting\", fmt.Sprintf(\"s_%d\", settingID))\n}\n\n// GetFileFolderPath gets the file folder path\n// GetFileFolderPath 获取文件目录路径\nfunc (d *Dao) GetFileFolderPath(uid int64, fileID int64) string {\n\treturn filepath.Join(\"storage\", \"vault\", fmt.Sprintf(\"u_%d\", uid), \"file\", fmt.Sprintf(\"f_%d\", fileID))\n}\n\n// GetNoteHistoryFolderPath gets the note history storage path\n// GetNoteHistoryFolderPath 获取笔记历史存储路径\nfunc (d *Dao) GetNoteHistoryFolderPath(uid int64, historyID int64) string {\n\treturn filepath.Join(\"storage\", \"vault\", fmt.Sprintf(\"u_%d\", uid), \"history\", fmt.Sprintf(\"h_%d\", historyID))\n}\n\n// saveContentToFile saves content to a file\n// saveContentToFile 保存内容到文件\nfunc (d *Dao) SaveContentToFile(folderPath string, fileName string, content string) error {\n\tif err := os.MkdirAll(folderPath, 0755); err != nil {\n\t\treturn err\n\t}\n\tfilePath := filepath.Join(folderPath, fileName)\n\treturn os.WriteFile(filePath, []byte(content), 0644)\n}\n\n// loadContentFromFile loads content from a file\n// loadContentFromFile 从文件加载内容\n// Return values: content, whether it exists, error\n// 返回值: 内容, 是否存在, 错误\nfunc (d *Dao) LoadContentFromFile(folderPath string, fileName string) (string, bool, error) {\n\tfilePath := filepath.Join(folderPath, fileName)\n\tcontent, err := os.ReadFile(filePath)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn \"\", false, nil\n\t\t}\n\t\treturn \"\", false, err\n\t}\n\treturn string(content), true, nil\n}\n\n// removeContentFolder removes the content folder\n// removeContentFolder 删除内容文件夹\nfunc (d *Dao) RemoveContentFolder(folderPath string) error {\n\tif fileurl.IsExist(folderPath) {\n\t\treturn os.RemoveAll(folderPath)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "internal/dao/file_repository.go",
    "content": "// Package dao implements the data access layer\n// Package dao 实现数据访问层\npackage dao\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/query\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"gorm.io/gen/field\"\n\t\"gorm.io/gorm\"\n)\n\n// fileRepository implements domain.FileRepository interface\n// fileRepository 实现 domain.FileRepository 接口\ntype fileRepository struct {\n\tdao             *Dao\n\tcustomPrefixKey string\n}\n\n// NewFileRepository creates FileRepository instance\n// NewFileRepository 创建 FileRepository 实例\nfunc NewFileRepository(dao *Dao) domain.FileRepository {\n\treturn &fileRepository{dao: dao, customPrefixKey: \"user_file_\"}\n}\n\nfunc (r *fileRepository) GetKey(uid int64) string {\n\treturn r.customPrefixKey + strconv.FormatInt(uid, 10)\n}\n\nfunc init() {\n\tRegisterModel(ModelConfig{\n\t\tName: \"File\",\n\t\tRepoFactory: func(d *Dao) daoDBCustomKey {\n\t\t\treturn NewFileRepository(d).(daoDBCustomKey)\n\t\t},\n\t})\n}\n\n// file gets the file query object\n// file 获取文件查询对象\nfunc (r *fileRepository) file(uid int64) *query.Query {\n\treturn r.dao.QueryWithOnceInit(func(g *gorm.DB) {\n\t\tmodel.AutoMigrate(g, \"File\")\n\t}, r.GetKey(uid)+\"#file\", r.GetKey(uid))\n}\n\n// toDomain converts database model to domain model\n// toDomain 将数据库模型转换为领域模型\nfunc (r *fileRepository) toDomain(m *model.File, uid int64) *domain.File {\n\tif m == nil {\n\t\treturn nil\n\t}\n\tfile := &domain.File{\n\t\tID:               m.ID,\n\t\tVaultID:          m.VaultID,\n\t\tAction:           domain.FileAction(m.Action),\n\t\tFID:              m.FID,\n\t\tPath:             m.Path,\n\t\tPathHash:         m.PathHash,\n\t\tContentHash:      m.ContentHash,\n\t\tSavePath:         m.SavePath,\n\t\tRename:           m.Rename,\n\t\tSize:             m.Size,\n\t\tCtime:            m.Ctime,\n\t\tMtime:            m.Mtime,\n\t\tUpdatedTimestamp: m.UpdatedTimestamp,\n\t\tCreatedAt:        time.Time(m.CreatedAt),\n\t\tUpdatedAt:        time.Time(m.UpdatedAt),\n\t}\n\tr.fillFilePath(uid, file)\n\treturn file\n}\n\n// toModel converts domain model to database model\n// toModel 将领域模型转换为数据库模型\nfunc (r *fileRepository) toModel(file *domain.File) *model.File {\n\tif file == nil {\n\t\treturn nil\n\t}\n\treturn &model.File{\n\t\tID:               file.ID,\n\t\tVaultID:          file.VaultID,\n\t\tAction:           string(file.Action),\n\t\tFID:              file.FID,\n\t\tPath:             file.Path,\n\t\tPathHash:         file.PathHash,\n\t\tContentHash:      file.ContentHash,\n\t\tSavePath:         file.SavePath,\n\t\tRename:           file.Rename,\n\t\tSize:             file.Size,\n\t\tCtime:            file.Ctime,\n\t\tMtime:            file.Mtime,\n\t\tUpdatedTimestamp: file.UpdatedTimestamp,\n\t\tCreatedAt:        timex.Time(file.CreatedAt),\n\t\tUpdatedAt:        timex.Time(file.UpdatedAt),\n\t}\n}\n\n// fillFilePath fills file SavePath and handles old file migration\n// fillFilePath 填充文件的保存路径并处理旧文件迁移\nfunc (r *fileRepository) fillFilePath(uid int64, f *domain.File) {\n\tif f == nil {\n\t\treturn\n\t}\n\tfolderPath := r.dao.GetFileFolderPath(uid, f.ID)\n\tstandardPath := filepath.Join(folderPath, \"file.dat\")\n\n\t// Record original SavePath for migration check\n\t// 记录原始 SavePath 以便进行迁移检查\n\toldSavePath := f.SavePath\n\n\t// Update to standard path\n\t// 更新为标准路径\n\tf.SavePath = standardPath\n\n\t// Migrate only if standard path doesn't exist, old path is provided, and old file exists on disk\n\t// 仅在标准路径不存在，且明确给出了旧路径，且旧文件确实存在磁盘上时才执行迁移\n\tif _, err := os.Stat(standardPath); os.IsNotExist(err) && oldSavePath != \"\" && oldSavePath != standardPath {\n\t\tif _, errOld := os.Stat(oldSavePath); errOld == nil {\n\t\t\t// 只有在确定要移动文件时才创建目录\n\t\t\t_ = os.MkdirAll(folderPath, 0755)\n\t\t\t_ = os.Rename(oldSavePath, standardPath)\n\t\t}\n\t}\n}\n\n// GetByID retrieves file by ID\n// GetByID 根据 ID 获取文件\nfunc (r *fileRepository) GetByID(ctx context.Context, id, uid int64) (*domain.File, error) {\n\tu := r.file(uid).File\n\tm, err := u.WithContext(ctx).Where(u.ID.Eq(id)).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m, uid), nil\n}\n\n// GetByPathHash retrieves file by path hash\n// GetByPathHash 根据路径哈希获取文件\nfunc (r *fileRepository) GetByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) (*domain.File, error) {\n\tu := r.file(uid).File\n\tm, err := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.PathHash.Eq(pathHash),\n\t).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m, uid), nil\n}\n\n// ListByPathHash retrieves file list by path hash (handling duplicate records)\n// ListByPathHash 根据路径哈希获取文件列表（处理重复记录）\nfunc (r *fileRepository) ListByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) ([]*domain.File, error) {\n\tu := r.file(uid).File\n\tmList, err := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.PathHash.Eq(pathHash),\n\t).Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar list []*domain.File\n\tfor _, m := range mList {\n\t\tlist = append(list, r.toDomain(m, uid))\n\t}\n\treturn list, nil\n}\n\n// GetByPath retrieves file by path\n// GetByPath 根据路径获取文件\nfunc (r *fileRepository) GetByPath(ctx context.Context, path string, vaultID, uid int64) (*domain.File, error) {\n\tu := r.file(uid).File\n\tm, err := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.Path.Eq(path),\n\t).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m, uid), nil\n}\n\n// GetByPathLike retrieves file by path suffix\n// GetByPathLike 根据路径后缀获取文件\nfunc (r *fileRepository) GetByPathLike(ctx context.Context, path string, vaultID, uid int64) (*domain.File, error) {\n\tu := r.file(uid).File\n\tm, err := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.Path.Like(\"%\"+path),\n\t\tu.Action.Neq(\"delete\"),\n\t).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m, uid), nil\n}\n\n// Create creates a file\n// Create 创建文件\nfunc (r *fileRepository) Create(ctx context.Context, file *domain.File, uid int64) (*domain.File, error) {\n\tvar result *domain.File\n\tvar createErr error\n\n\terr := r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.file(uid).File\n\t\tm := r.toModel(file)\n\n\t\tm.UpdatedTimestamp = timex.Now().UnixMilli()\n\t\tm.CreatedAt = timex.Now()\n\t\tm.UpdatedAt = timex.Now()\n\n\t\ttempSavePath := m.SavePath\n\t\tm.SavePath = \"\" // 不在数据库中保存路径\n\n\t\tcreateErr = u.WithContext(ctx).Create(m)\n\t\tif createErr != nil {\n\t\t\treturn createErr\n\t\t}\n\n\t\t// Move file to Vault directory, fixed naming as file.dat\n\t\t// 移动文件到 Vault 目录，固定命名为 file.dat\n\t\tif tempSavePath != \"\" {\n\t\t\tfolderPath := r.dao.GetFileFolderPath(uid, m.ID)\n\t\t\t_ = os.MkdirAll(folderPath, 0755)\n\t\t\tfinalPath := filepath.Join(folderPath, \"file.dat\")\n\n\t\t\tif err := os.Rename(tempSavePath, finalPath); err != nil {\n\t\t\t\t_ = os.Rename(tempSavePath, finalPath)\n\t\t\t}\n\t\t}\n\n\t\tresult = r.toDomain(m, uid)\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn result, createErr\n}\n\n// Update updates a file\n// Update 更新文件\nfunc (r *fileRepository) Update(ctx context.Context, file *domain.File, uid int64) (*domain.File, error) {\n\tvar result *domain.File\n\tvar updateErr error\n\n\terr := r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.file(uid).File\n\t\tm := r.toModel(file)\n\n\t\tm.UpdatedTimestamp = timex.Now().UnixMilli()\n\t\tm.UpdatedAt = timex.Now()\n\n\t\ttempSavePath := m.SavePath\n\t\tm.SavePath = \"\" // 不在数据库中更新路径\n\n\t\t// If a new temporary path is provided, move it to the fixed file.dat\n\t\t// 如果提供了新的临时路径，则移动到固定的 file.dat\n\t\tif tempSavePath != \"\" {\n\t\t\tfolderPath := r.dao.GetFileFolderPath(uid, m.ID)\n\t\t\t_ = os.MkdirAll(folderPath, 0755)\n\t\t\tfinalPath := filepath.Join(folderPath, \"file.dat\")\n\t\t\t_ = os.Rename(tempSavePath, finalPath)\n\t\t}\n\n\t\tupdateErr = u.WithContext(ctx).Where(\n\t\t\tu.ID.Eq(m.ID),\n\t\t).Save(m)\n\n\t\tif updateErr != nil {\n\t\t\treturn updateErr\n\t\t}\n\t\tresult = r.toDomain(m, uid)\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn result, updateErr\n}\n\n// UpdateMtime updates file modification time\n// UpdateMtime 更新文件修改时间\nfunc (r *fileRepository) UpdateMtime(ctx context.Context, mtime int64, id, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.file(uid).File\n\n\t\t_, err := u.WithContext(ctx).Where(\n\t\t\tu.ID.Eq(id),\n\t\t).UpdateSimple(\n\t\t\tu.Mtime.Value(mtime),\n\t\t\tu.UpdatedTimestamp.Value(timex.Now().UnixMilli()),\n\t\t\tu.UpdatedAt.Value(timex.Now()),\n\t\t)\n\t\treturn err\n\t})\n}\n\n// UpdateActionMtime updates file action and modification time\n// UpdateActionMtime 更新文件类型并修改时间\nfunc (r *fileRepository) UpdateActionMtime(ctx context.Context, action domain.FileAction, mtime int64, id, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.file(uid).File\n\n\t\t_, err := u.WithContext(ctx).Where(\n\t\t\tu.ID.Eq(id),\n\t\t).UpdateSimple(\n\t\t\tu.Action.Value(string(action)),\n\t\t\tu.Mtime.Value(mtime),\n\t\t\tu.UpdatedTimestamp.Value(timex.Now().UnixMilli()),\n\t\t\tu.UpdatedAt.Value(timex.Now()),\n\t\t)\n\t\treturn err\n\t})\n}\n\n// Delete physically deletes a file\n// Delete 物理删除文件\nfunc (r *fileRepository) Delete(ctx context.Context, id, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.file(uid).File\n\t\t_, err := u.WithContext(ctx).Where(u.ID.Eq(id)).Delete()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Delete physical file\n\t\t// 删除物理文件\n\t\tfolderPath := r.dao.GetFileFolderPath(uid, id)\n\t\t_ = r.dao.RemoveContentFolder(folderPath)\n\n\t\treturn nil\n\t})\n}\n\n// DeletePhysicalByTime physically deletes files marked as deleted by time\n// DeletePhysicalByTime 根据时间物理删除已标记删除的文件\nfunc (r *fileRepository) DeletePhysicalByTime(ctx context.Context, timestamp, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.file(uid).File\n\n\t\t// Find records to be deleted to remove folders in the file system\n\t\t// 查找待删除的记录，以便删除文件系统中的文件夹\n\t\tmList, err := u.WithContext(ctx).Where(\n\t\t\tu.Action.Eq(\"delete\"),\n\t\t\tu.UpdatedTimestamp.Lt(timestamp),\n\t\t).Find()\n\n\t\tif err == nil {\n\t\t\tfor _, m := range mList {\n\t\t\t\tfolderPath := r.dao.GetFileFolderPath(uid, m.ID)\n\t\t\t\t_ = r.dao.RemoveContentFolder(folderPath)\n\t\t\t}\n\t\t}\n\n\t\t_, err = u.WithContext(ctx).Where(\n\t\t\tu.Action.Eq(\"delete\"),\n\t\t\tu.UpdatedTimestamp.Lt(timestamp),\n\t\t).Delete()\n\t\treturn err\n\t})\n}\n\n// DeletePhysicalByTimeAll physically deletes files marked as deleted for all users by time\n// DeletePhysicalByTimeAll 根据时间物理删除所有用户的已标记删除的文件\nfunc (r *fileRepository) DeletePhysicalByTimeAll(ctx context.Context, timestamp int64) error {\n\t// Get all user UIDs\n\t// 获取所有用户 UID\n\tuids, err := r.dao.GetAllUserUIDs()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Execute cleanup user by user\n\t// 逐用户执行清理\n\tfor _, uid := range uids {\n\t\tif err := r.DeletePhysicalByTime(ctx, timestamp, uid); err != nil {\n\t\t\t// 记录错误但继续处理其他用户\n\t\t\tcontinue\n\t\t}\n\t}\n\treturn nil\n}\n\n// List retrieves file list by page\n// List 分页获取文件列表\nfunc (r *fileRepository) List(ctx context.Context, vaultID int64, page, pageSize int, uid int64, keyword string, isRecycle bool, sortBy string, sortOrder string) ([]*domain.File, error) {\n\tu := r.file(uid).File\n\tq := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t)\n\n\tif isRecycle {\n\t\tq = q.Where(u.Action.Eq(string(domain.FileActionDelete)), u.Rename.Eq(0))\n\t} else {\n\t\tq = q.Where(u.Action.Neq(string(domain.FileActionDelete)))\n\t}\n\n\tif keyword != \"\" {\n\t\tq = q.Where(u.Path.Like(\"%\" + keyword + \"%\"))\n\t}\n\n\t// Sorting\n\t// 排序\n\tvar sortField field.OrderExpr\n\tswitch sortBy {\n\tcase \"ctime\":\n\t\tsortField = u.Ctime\n\tcase \"path\":\n\t\tsortField = u.Path\n\tcase \"mtime\":\n\t\tfallthrough\n\tdefault:\n\t\tsortField = u.Mtime\n\t}\n\n\tvar orderExpr field.Expr\n\tif strings.ToLower(sortOrder) == \"asc\" {\n\t\torderExpr = sortField\n\t} else {\n\t\torderExpr = sortField.Desc()\n\t}\n\n\torderExprs := []field.Expr{orderExpr}\n\tif sortBy != \"path\" {\n\t\torderExprs = append(orderExprs, u.Path)\n\t}\n\n\tmodelList, err := q.Order(orderExprs...).\n\t\tLimit(pageSize).\n\t\tOffset(app.GetPageOffset(page, pageSize)).\n\t\tFind()\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar list []*domain.File\n\tfor _, m := range modelList {\n\t\tlist = append(list, r.toDomain(m, uid))\n\t}\n\treturn list, nil\n}\n\n// ListCount retrieves file count\n// ListCount 获取文件数量\nfunc (r *fileRepository) ListCount(ctx context.Context, vaultID, uid int64, keyword string, isRecycle bool) (int64, error) {\n\tu := r.file(uid).File\n\tq := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t)\n\n\tif isRecycle {\n\t\tq = q.Where(u.Action.Eq(string(domain.FileActionDelete)), u.Rename.Eq(0))\n\t} else {\n\t\tq = q.Where(u.Action.Neq(string(domain.FileActionDelete)))\n\t}\n\n\tif keyword != \"\" {\n\t\tq = q.Where(u.Path.Like(\"%\" + keyword + \"%\"))\n\t}\n\n\tcount, err := q.Count()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn count, nil\n}\n\n// ListByUpdatedTimestamp retrieves file list by updated timestamp\n// ListByUpdatedTimestamp 根据更新时间戳获取文件列表\nfunc (r *fileRepository) ListByUpdatedTimestamp(ctx context.Context, timestamp, vaultID, uid int64) ([]*domain.File, error) {\n\treturn r.ListByUpdatedTimestampPage(ctx, timestamp, vaultID, uid, 0, 0)\n}\n\n// ListByUpdatedTimestampPage retrieves file list by updated timestamp by page\n// ListByUpdatedTimestampPage 根据更新时间戳分页获取文件列表\nfunc (r *fileRepository) ListByUpdatedTimestampPage(ctx context.Context, timestamp, vaultID, uid int64, offset, limit int) ([]*domain.File, error) {\n\tu := r.file(uid).File\n\tquery := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.UpdatedTimestamp.Gt(timestamp),\n\t).Order(u.UpdatedTimestamp.Desc())\n\n\tvar mList []*model.File\n\tvar err error\n\tif limit > 0 {\n\t\tmList, _, err = query.FindByPage(offset, limit)\n\t} else {\n\t\tmList, err = query.Find()\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar list []*domain.File\n\tfor _, m := range mList {\n\t\tlist = append(list, r.toDomain(m, uid))\n\t}\n\treturn list, nil\n}\n\n// ListByMtime retrieves file list by modification timestamp\n// ListByMtime 根据修改时间戳获取文件列表\nfunc (r *fileRepository) ListByMtime(ctx context.Context, timestamp, vaultID, uid int64) ([]*domain.File, error) {\n\tu := r.file(uid).File\n\tmList, err := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.Mtime.Gt(timestamp),\n\t).Order(u.UpdatedTimestamp.Desc()).\n\t\tFind()\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar list []*domain.File\n\tfor _, m := range mList {\n\t\tlist = append(list, r.toDomain(m, uid))\n\t}\n\treturn list, nil\n}\n\n// CountSizeSum retrieves total file count and size sum\n// CountSizeSum 获取文件数量和大小总和\nfunc (r *fileRepository) CountSizeSum(ctx context.Context, vaultID, uid int64) (*domain.CountSizeResult, error) {\n\tu := r.file(uid).File\n\n\tresult := &struct {\n\t\tSize  int64\n\t\tCount int64\n\t}{}\n\n\terr := u.WithContext(ctx).Select(u.Size.Sum().As(\"size\"), u.Size.Count().As(\"count\")).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.Action.Neq(\"delete\"),\n\t\tu.Rename.Eq(0),\n\t).Scan(result)\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &domain.CountSizeResult{\n\t\tCount: result.Count,\n\t\tSize:  result.Size,\n\t}, nil\n}\n\n// ListByFID retrieves file list by folder ID\n// ListByFID 根据文件夹ID获取文件列表\nfunc (r *fileRepository) ListByFID(ctx context.Context, fid, vaultID, uid int64, page, pageSize int, sortBy, sortOrder string) ([]*domain.File, error) {\n\tu := r.file(uid).File\n\tq := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.FID.Eq(fid),\n\t\tu.Action.Neq(string(domain.FileActionDelete)),\n\t)\n\n\t// Build order clause\n\t// 构建排序语句\n\torderClause := buildFileOrderClause(sortBy, sortOrder)\n\n\tvar modelList []*model.File\n\terr := q.UnderlyingDB().\n\t\tOrder(orderClause).\n\t\tLimit(pageSize).\n\t\tOffset(app.GetPageOffset(page, pageSize)).\n\t\tFind(&modelList).Error\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar list []*domain.File\n\tfor _, m := range modelList {\n\t\tlist = append(list, r.toDomain(m, uid))\n\t}\n\treturn list, nil\n}\n\n// ListByFIDCount retrieves file count by folder ID\n// ListByFIDCount 根据文件夹ID获取文件数量\nfunc (r *fileRepository) ListByFIDCount(ctx context.Context, fid, vaultID, uid int64) (int64, error) {\n\tu := r.file(uid).File\n\tq := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.FID.Eq(fid),\n\t\tu.Action.Neq(string(domain.FileActionDelete)),\n\t)\n\n\treturn q.Count()\n}\n\nfunc (r *fileRepository) ListByFIDs(ctx context.Context, fids []int64, vaultID, uid int64, page, pageSize int, sortBy, sortOrder string) ([]*domain.File, error) {\n\tu := r.file(uid).File\n\tq := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.FID.In(fids...),\n\t\tu.Action.Neq(string(domain.FileActionDelete)),\n\t)\n\n\torderClause := buildFileOrderClause(sortBy, sortOrder)\n\n\tvar modelList []*model.File\n\terr := q.UnderlyingDB().\n\t\tOrder(orderClause).\n\t\tLimit(pageSize).\n\t\tOffset(app.GetPageOffset(page, pageSize)).\n\t\tFind(&modelList).Error\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar list []*domain.File\n\tfor _, m := range modelList {\n\t\tlist = append(list, r.toDomain(m, uid))\n\t}\n\treturn list, nil\n}\n\nfunc (r *fileRepository) ListByFIDsCount(ctx context.Context, fids []int64, vaultID, uid int64) (int64, error) {\n\tu := r.file(uid).File\n\tq := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.FID.In(fids...),\n\t\tu.Action.Neq(string(domain.FileActionDelete)),\n\t)\n\n\treturn q.Count()\n}\n\n// ListByIDs retrieves file list by ID list\n// ListByIDs 根据ID列表获取文件列表\nfunc (r *fileRepository) ListByIDs(ctx context.Context, ids []int64, uid int64) ([]*domain.File, error) {\n\tif len(ids) == 0 {\n\t\treturn []*domain.File{}, nil\n\t}\n\tu := r.file(uid).File\n\tms, err := u.WithContext(ctx).Where(u.ID.In(ids...)).Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []*domain.File\n\tfor _, m := range ms {\n\t\tres = append(res, r.toDomain(m, uid))\n\t}\n\treturn res, nil\n}\n\n// RecycleClear cleans up the recycle bin\n// RecycleClear 清理回收站\nfunc (r *fileRepository) RecycleClear(ctx context.Context, path, pathHash string, vaultID, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.file(uid).File\n\t\tq := u.WithContext(ctx).Where(u.VaultID.Eq(vaultID), u.Action.Eq(string(domain.FileActionDelete)), u.Rename.Eq(0))\n\t\tif pathHash != \"\" {\n\t\t\tq = q.Where(u.PathHash.Eq(pathHash))\n\t\t}\n\t\t_, err := q.UpdateSimple(\n\t\t\tu.Rename.Value(2),\n\t\t\tu.UpdatedTimestamp.Value(timex.Now().UnixMilli()),\n\t\t\tu.UpdatedAt.Value(timex.Now()),\n\t\t)\n\t\treturn err\n\t})\n}\n\n// UpdateFID 仅更新文件的文件夹关联 ID，不更新 updated_timestamp\n// 用于 SyncResourceFID 内部整理，避免污染增量同步时间戳\n// Only updates the folder ID (FID) without touching updated_timestamp\n// Used by SyncResourceFID to avoid polluting incremental sync timestamps\nfunc (r *fileRepository) UpdateFID(ctx context.Context, id, fid, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.file(uid).File\n\t\t_, err := u.WithContext(ctx).Where(u.ID.Eq(id)).UpdateSimple(u.FID.Value(fid))\n\t\treturn err\n\t})\n}\n\n// Ensure fileRepository implements domain.FileRepository interface\n// 确保 fileRepository 实现了 domain.FileRepository 接口\nvar _ domain.FileRepository = (*fileRepository)(nil)\n\nfunc (r *fileRepository) ListByPathPrefix(ctx context.Context, pathPrefix string, vaultID, uid int64) ([]*domain.File, error) {\n\tu := r.file(uid).File\n\t// Use LIKE 'prefix/%'\n\t// 使用 LIKE 'prefix/%'\n\tpattern := pathPrefix + \"/%\"\n\tms, err := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.Path.Like(pattern),\n\t\tu.Action.Neq(string(domain.FileActionDelete)),\n\t).Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []*domain.File\n\tfor _, m := range ms {\n\t\tres = append(res, r.toDomain(m, uid))\n\t}\n\treturn res, nil\n}\n\n// buildFileOrderClause builds file order clause\n// buildFileOrderClause 构建文件排序语句\nfunc buildFileOrderClause(sortBy, sortOrder string) string {\n\t// 默认值\n\tif sortBy == \"\" {\n\t\tsortBy = \"mtime\"\n\t}\n\tif sortOrder == \"\" {\n\t\tsortOrder = \"desc\"\n\t}\n\n\t// 验证排序方向\n\tif sortOrder != \"asc\" && sortOrder != \"desc\" {\n\t\tsortOrder = \"desc\"\n\t}\n\n\t// Map sort field\n\t// 映射排序字段\n\tvar field string\n\tswitch sortBy {\n\tcase \"ctime\":\n\t\tfield = \"ctime\"\n\tcase \"path\":\n\t\tfield = \"path\"\n\tcase \"mtime\":\n\t\tfallthrough\n\tdefault:\n\t\tfield = \"mtime\"\n\t}\n\n\treturn field + \" \" + sortOrder\n}\n\n// DeleteByVaultID physically deletes all files in a vault\n// DeleteByVaultID 物理删除仓库下的所有文件\nfunc (r *fileRepository) DeleteByVaultID(ctx context.Context, vaultID, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.file(uid).File\n\n\t\t// 查找该仓库下的所有文件 ID\n\t\tfiles, err := u.WithContext(ctx).Where(u.VaultID.Eq(vaultID)).Select(u.ID).Find()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif len(files) == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\tvar ids []int64\n\t\tfor _, f := range files {\n\t\t\tids = append(ids, f.ID)\n\t\t}\n\n\t\t// 从数据库删除\n\t\t_, err = u.WithContext(ctx).Where(u.VaultID.Eq(vaultID)).Delete()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// 删除物理文件夹\n\t\tfor _, id := range ids {\n\t\t\tfolderPath := r.dao.GetFileFolderPath(uid, id)\n\t\t\t_ = r.dao.RemoveContentFolder(folderPath)\n\t\t}\n\n\t\treturn nil\n\t})\n}\n"
  },
  {
    "path": "internal/dao/folder_repository.go",
    "content": "package dao\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/query\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"gorm.io/gorm\"\n)\n\ntype folderRepository struct {\n\tDao             *Dao\n\tcustomPrefixKey string\n}\n\nfunc NewFolderRepository(d *Dao) domain.FolderRepository {\n\treturn &folderRepository{Dao: d, customPrefixKey: \"user_folder_\"} // Modified initialization\n}\n\nfunc (r *folderRepository) GetKey(uid int64) string {\n\treturn r.customPrefixKey + strconv.FormatInt(uid, 10)\n}\n\nfunc init() {\n\tRegisterModel(ModelConfig{\n\t\tName: \"Folder\",\n\t\tRepoFactory: func(d *Dao) daoDBCustomKey {\n\t\t\treturn NewFolderRepository(d).(daoDBCustomKey)\n\t\t},\n\t})\n}\n\n// folder returns the query with auto-migration\nfunc (r *folderRepository) folder(uid int64) *query.Query {\n\treturn r.Dao.QueryWithOnceInit(func(g *gorm.DB) {\n\t\tmodel.AutoMigrate(g, \"Folder\")\n\t}, r.GetKey(uid)+\"#folder\", r.GetKey(uid))\n}\n\nfunc (r *folderRepository) GetByID(ctx context.Context, id, uid int64) (*domain.Folder, error) {\n\tf := r.folder(uid).Folder\n\tm, err := f.WithContext(ctx).Where(f.ID.Eq(id)).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.modelToDomain(m), nil\n}\n\nfunc (r *folderRepository) GetByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) (*domain.Folder, error) {\n\tf := r.folder(uid).Folder\n\tm, err := f.WithContext(ctx).Where(f.VaultID.Eq(vaultID), f.PathHash.Eq(pathHash)).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.modelToDomain(m), nil\n}\n\nfunc (r *folderRepository) GetAllByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) ([]*domain.Folder, error) {\n\tf := r.folder(uid).Folder\n\tms, err := f.WithContext(ctx).Where(f.VaultID.Eq(vaultID), f.PathHash.Eq(pathHash), f.Action.Neq(\"delete\")).Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []*domain.Folder\n\tfor _, m := range ms {\n\t\tres = append(res, r.modelToDomain(m))\n\t}\n\treturn res, nil\n}\n\nfunc (r *folderRepository) GetByFID(ctx context.Context, fid int64, vaultID, uid int64) ([]*domain.Folder, error) {\n\tvar ms []*model.Folder\n\tf := r.folder(uid).Folder\n\tms, err := f.WithContext(ctx).Where(f.VaultID.Eq(vaultID), f.FID.Eq(fid), f.Action.Neq(\"delete\")).Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []*domain.Folder\n\tfor _, m := range ms {\n\t\tres = append(res, r.modelToDomain(m))\n\t}\n\treturn res, nil\n}\n\nfunc (r *folderRepository) Create(ctx context.Context, folder *domain.Folder, uid int64) (*domain.Folder, error) {\n\tvar result *domain.Folder\n\terr := r.Dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tm := r.domainToModel(folder)\n\t\tm.CreatedAt = timex.Now()\n\t\tm.UpdatedAt = timex.Now()\n\t\tm.UpdatedTimestamp = timex.Now().UnixMilli()\n\t\tf := r.folder(uid).Folder\n\t\terr := f.WithContext(ctx).Create(m)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresult = r.modelToDomain(m)\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn result, nil\n}\n\nfunc (r *folderRepository) Update(ctx context.Context, folder *domain.Folder, uid int64) (*domain.Folder, error) {\n\tvar result *domain.Folder\n\terr := r.Dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tm := r.domainToModel(folder)\n\t\tm.UpdatedAt = timex.Now()\n\t\tm.UpdatedTimestamp = timex.Now().UnixMilli()\n\t\tf := r.folder(uid).Folder\n\t\t_, err := f.WithContext(ctx).Where(f.ID.Eq(m.ID)).Updates(m)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresult = r.modelToDomain(m)\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn result, nil\n}\n\nfunc (r *folderRepository) Delete(ctx context.Context, id, uid int64) error {\n\treturn r.Dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tf := r.folder(uid).Folder\n\t\t_, err := f.WithContext(ctx).Where(f.ID.Eq(id)).Delete()\n\t\treturn err\n\t})\n}\n\nfunc (r *folderRepository) ListByUpdatedTimestamp(ctx context.Context, timestamp, vaultID, uid int64) ([]*domain.Folder, error) {\n\tvar ms []*model.Folder\n\tf := r.folder(uid).Folder\n\tms, err := f.WithContext(ctx).Where(f.VaultID.Eq(vaultID), f.UpdatedTimestamp.Gt(timestamp)).Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []*domain.Folder\n\tfor _, m := range ms {\n\t\tres = append(res, r.modelToDomain(m))\n\t}\n\treturn res, nil\n}\n\nfunc (r *folderRepository) ListByPathPrefix(ctx context.Context, pathPrefix string, vaultID, uid int64) ([]*domain.Folder, error) {\n\tvar ms []*model.Folder\n\tf := r.folder(uid).Folder\n\t// Use LIKE 'prefix/%' to find all subdirectories\n\t// 使用 LIKE 'prefix/%' 来查找所有子目录\n\tpattern := pathPrefix + \"/%\"\n\tms, err := f.WithContext(ctx).Where(f.VaultID.Eq(vaultID), f.Path.Like(pattern), f.Action.Neq(\"delete\")).Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []*domain.Folder\n\tfor _, m := range ms {\n\t\tres = append(res, r.modelToDomain(m))\n\t}\n\treturn res, nil\n}\n\nfunc (r *folderRepository) modelToDomain(m *model.Folder) *domain.Folder {\n\tif m == nil {\n\t\treturn nil\n\t}\n\treturn &domain.Folder{\n\t\tID:               m.ID,\n\t\tVaultID:          m.VaultID,\n\t\tAction:           domain.FolderAction(m.Action),\n\t\tPath:             m.Path,\n\t\tPathHash:         m.PathHash,\n\t\tLevel:            m.Level,\n\t\tFID:              m.FID,\n\t\tCtime:            m.Ctime,\n\t\tMtime:            m.Mtime,\n\t\tUpdatedTimestamp: m.UpdatedTimestamp,\n\t\tCreatedAt:        time.Time(m.CreatedAt),\n\t\tUpdatedAt:        time.Time(m.UpdatedAt),\n\t}\n}\n\nfunc (r *folderRepository) domainToModel(d *domain.Folder) *model.Folder {\n\tif d == nil {\n\t\treturn nil\n\t}\n\treturn &model.Folder{\n\t\tID:               d.ID,\n\t\tVaultID:          d.VaultID,\n\t\tAction:           string(d.Action),\n\t\tPath:             d.Path,\n\t\tPathHash:         d.PathHash,\n\t\tLevel:            d.Level,\n\t\tFID:              d.FID,\n\t\tCtime:            d.Ctime,\n\t\tMtime:            d.Mtime,\n\t\tUpdatedTimestamp: d.UpdatedTimestamp,\n\t\tCreatedAt:        timex.Time(d.CreatedAt),\n\t\tUpdatedAt:        timex.Time(d.UpdatedAt),\n\t}\n}\nfunc (r *folderRepository) List(ctx context.Context, vaultID int64, uid int64) ([]*domain.Folder, error) {\n\tu := r.folder(uid).Folder\n\tms, err := u.WithContext(ctx).Where(u.VaultID.Eq(vaultID)).Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []*domain.Folder\n\tfor _, m := range ms {\n\t\tres = append(res, r.modelToDomain(m))\n\t}\n\treturn res, nil\n}\n\nfunc (r *folderRepository) ListAll(ctx context.Context, uid int64) ([]*domain.Folder, error) {\n\tu := r.folder(uid).Folder\n\tms, err := u.WithContext(ctx).Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []*domain.Folder\n\tfor _, m := range ms {\n\t\tres = append(res, r.modelToDomain(m))\n\t}\n\treturn res, nil\n}\n// DeleteByVaultID removes all folder records for a specific vault\n// DeleteByVaultID 删除指定仓库的所有文件夹记录\nfunc (r *folderRepository) DeleteByVaultID(ctx context.Context, vaultID, uid int64) error {\n\treturn r.Dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tf := r.folder(uid).Folder\n\t\t_, err := f.WithContext(ctx).Where(f.VaultID.Eq(vaultID)).Delete()\n\t\treturn err\n\t})\n}\n\n// Ensure folderRepository implements domain.FolderRepository interface\n// 确保 folderRepository 实现了 domain.FolderRepository 接口\nvar _ domain.FolderRepository = (*folderRepository)(nil)\n"
  },
  {
    "path": "internal/dao/git_sync_repository.go",
    "content": "package dao\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/query\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"go.uber.org/zap\"\n\t\"gorm.io/gorm\"\n)\n\ntype gitSyncRepository struct {\n\tdao             *Dao\n\tcustomPrefixKey string\n}\n\n// NewGitSyncRepository creates GitSyncRepository instance\n// NewGitSyncRepository 创建 GitSyncRepository 实例\nfunc NewGitSyncRepository(dao *Dao) domain.GitSyncRepository {\n\treturn &gitSyncRepository{dao: dao, customPrefixKey: \"user_git_sync_\"}\n}\n\nfunc (r *gitSyncRepository) GetKey(uid int64) string {\n\treturn r.customPrefixKey + strconv.FormatInt(uid, 10)\n}\n\nfunc init() {\n\tfactory := func(d *Dao) daoDBCustomKey {\n\t\treturn NewGitSyncRepository(d).(daoDBCustomKey)\n\t}\n\tRegisterModel(ModelConfig{\n\t\tName:        \"GitSyncConfig\",\n\t\tRepoFactory: factory,\n\t})\n\tRegisterModel(ModelConfig{\n\t\tName:        \"GitSyncHistory\",\n\t\tRepoFactory: factory,\n\t})\n}\n\nfunc (r *gitSyncRepository) gitSync(uid int64) *query.Query {\n\treturn r.dao.QueryWithOnceInit(func(g *gorm.DB) {\n\t\tif err := model.AutoMigrate(g, \"GitSyncConfig\"); err != nil {\n\t\t\tr.dao.Logger().Error(\"AutoMigrate GitSyncConfig failed\", zap.Int64(\"uid\", uid), zap.Error(err))\n\t\t}\n\t\tif err := model.AutoMigrate(g, \"GitSyncHistory\"); err != nil {\n\t\t\tr.dao.Logger().Error(\"AutoMigrate GitSyncHistory failed\", zap.Int64(\"uid\", uid), zap.Error(err))\n\t\t}\n\t}, r.GetKey(uid)+\"#git_sync\", r.GetKey(uid))\n}\n\nfunc (r *gitSyncRepository) historyToDomain(m *model.GitSyncHistory) *domain.GitSyncHistory {\n\tif m == nil {\n\t\treturn nil\n\t}\n\treturn &domain.GitSyncHistory{\n\t\tID:        m.ID,\n\t\tUID:       m.UID,\n\t\tConfigID:  m.ConfigID,\n\t\tStartTime: m.StartTime,\n\t\tEndTime:   m.EndTime,\n\t\tStatus:    m.Status,\n\t\tMessage:   m.Message,\n\t\tCreatedAt: time.Time(m.CreatedAt),\n\t\tUpdatedAt: time.Time(m.UpdatedAt),\n\t}\n}\n\nfunc (r *gitSyncRepository) historyToModel(d *domain.GitSyncHistory) *model.GitSyncHistory {\n\tif d == nil {\n\t\treturn nil\n\t}\n\treturn &model.GitSyncHistory{\n\t\tID:        d.ID,\n\t\tUID:       d.UID,\n\t\tConfigID:  d.ConfigID,\n\t\tStartTime: d.StartTime,\n\t\tEndTime:   d.EndTime,\n\t\tStatus:    d.Status,\n\t\tMessage:   d.Message,\n\t\tCreatedAt: timex.Time(d.CreatedAt),\n\t\tUpdatedAt: timex.Time(d.UpdatedAt),\n\t}\n}\n\n// ... existing config methods ...\n\nfunc (r *gitSyncRepository) CreateHistory(ctx context.Context, history *domain.GitSyncHistory, uid int64) (*domain.GitSyncHistory, error) {\n\tvar result *domain.GitSyncHistory\n\terr := r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tq := r.gitSync(uid).GitSyncHistory\n\t\tm := r.historyToModel(history)\n\t\tm.UID = uid\n\t\tm.CreatedAt = timex.Now()\n\t\tm.UpdatedAt = timex.Now()\n\t\tif err := q.WithContext(ctx).Save(m); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresult = r.historyToDomain(m)\n\t\treturn nil\n\t})\n\treturn result, err\n}\n\nfunc (r *gitSyncRepository) ListHistory(ctx context.Context, uid int64, configID int64, page, pageSize int) ([]*domain.GitSyncHistory, int64, error) {\n\tq := r.gitSync(uid).GitSyncHistory\n\toffset := (page - 1) * pageSize\n\tdb := q.WithContext(ctx).Where(q.UID.Eq(uid))\n\tif configID > 0 {\n\t\tdb = db.Where(q.ConfigID.Eq(configID))\n\t}\n\tmodelList, count, err := db.Order(q.ID.Desc()).FindByPage(offset, pageSize)\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tvar list []*domain.GitSyncHistory\n\tfor _, m := range modelList {\n\t\tlist = append(list, r.historyToDomain(m))\n\t}\n\treturn list, count, nil\n}\n\nfunc (r *gitSyncRepository) toDomain(m *model.GitSyncConfig) *domain.GitSyncConfig {\n\tif m == nil {\n\t\treturn nil\n\t}\n\tvar lastSyncTime *time.Time\n\tif !m.LastSyncTime.IsZero() {\n\t\tt := m.LastSyncTime\n\t\tlastSyncTime = &t\n\t}\n\treturn &domain.GitSyncConfig{\n\t\tID:            m.ID,\n\t\tUID:           m.UID,\n\t\tVaultID:       m.VaultID,\n\t\tRepoURL:       m.RepoURL,\n\t\tUsername:      m.Username,\n\t\tPassword:      m.Password,\n\t\tBranch:        m.Branch,\n\t\tIsEnabled:     m.IsEnabled == 1,\n\t\tDelay:         m.Delay,\n\t\tRetentionDays: m.RetentionDays,\n\t\tLastSyncTime:  lastSyncTime,\n\t\tLastStatus:    m.LastStatus,\n\t\tLastMessage:   m.LastMessage,\n\t\tCreatedAt:     time.Time(m.CreatedAt),\n\t\tUpdatedAt:     time.Time(m.UpdatedAt),\n\t}\n}\n\nfunc (r *gitSyncRepository) toModel(d *domain.GitSyncConfig) *model.GitSyncConfig {\n\tif d == nil {\n\t\treturn nil\n\t}\n\tisEnabled := int64(0)\n\tif d.IsEnabled {\n\t\tisEnabled = 1\n\t}\n\tvar lastSyncTime time.Time\n\tif d.LastSyncTime != nil {\n\t\tlastSyncTime = *d.LastSyncTime\n\t}\n\treturn &model.GitSyncConfig{\n\t\tID:            d.ID,\n\t\tUID:           d.UID,\n\t\tVaultID:       d.VaultID,\n\t\tRepoURL:       d.RepoURL,\n\t\tUsername:      d.Username,\n\t\tPassword:      d.Password,\n\t\tBranch:        d.Branch,\n\t\tIsEnabled:     isEnabled,\n\t\tDelay:         d.Delay,\n\t\tRetentionDays: d.RetentionDays,\n\t\tLastSyncTime:  lastSyncTime,\n\t\tLastStatus:    d.LastStatus,\n\t\tLastMessage:   d.LastMessage,\n\t\tCreatedAt:     timex.Time(d.CreatedAt),\n\t\tUpdatedAt:     timex.Time(d.UpdatedAt),\n\t}\n}\n\nfunc (r *gitSyncRepository) GetByID(ctx context.Context, id, uid int64) (*domain.GitSyncConfig, error) {\n\tq := r.gitSync(uid).GitSyncConfig\n\tm, err := q.WithContext(ctx).Where(q.ID.Eq(id), q.UID.Eq(uid)).First()\n\tif err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m), nil\n}\n\nfunc (r *gitSyncRepository) GetByVaultID(ctx context.Context, vaultID, uid int64) (*domain.GitSyncConfig, error) {\n\tq := r.gitSync(uid).GitSyncConfig\n\tm, err := q.WithContext(ctx).Where(q.VaultID.Eq(vaultID), q.UID.Eq(uid)).First()\n\tif err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m), nil\n}\n\nfunc (r *gitSyncRepository) Save(ctx context.Context, config *domain.GitSyncConfig, uid int64) (*domain.GitSyncConfig, error) {\n\tvar result *domain.GitSyncConfig\n\terr := r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tq := r.gitSync(uid).GitSyncConfig\n\t\tm := r.toModel(config)\n\t\tm.UID = uid\n\n\t\tif config.ID > 0 {\n\t\t\told, err := q.WithContext(ctx).Where(q.ID.Eq(config.ID), q.UID.Eq(uid)).First()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tm.CreatedAt = old.CreatedAt\n\t\t\tm.UpdatedAt = timex.Now()\n\t\t\tif err := q.WithContext(ctx).Save(m); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tm.CreatedAt = timex.Now()\n\n\t\t\tif err := q.WithContext(ctx).Create(m); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tresult = r.toDomain(m)\n\t\treturn nil\n\t})\n\treturn result, err\n}\n\nfunc (r *gitSyncRepository) Delete(ctx context.Context, id, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tq := r.gitSync(uid).GitSyncConfig\n\t\t_, err := q.WithContext(ctx).Where(q.ID.Eq(id), q.UID.Eq(uid)).Delete()\n\t\treturn err\n\t})\n}\n\nfunc (r *gitSyncRepository) List(ctx context.Context, uid int64) ([]*domain.GitSyncConfig, error) {\n\tq := r.gitSync(uid).GitSyncConfig\n\tms, err := q.WithContext(ctx).Where(q.UID.Eq(uid)).Order(q.ID.Desc()).Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []*domain.GitSyncConfig\n\tfor _, m := range ms {\n\t\tres = append(res, r.toDomain(m))\n\t}\n\treturn res, nil\n}\n\nfunc (r *gitSyncRepository) ListByVaultID(ctx context.Context, vaultID, uid int64) ([]*domain.GitSyncConfig, error) {\n\tq := r.gitSync(uid).GitSyncConfig\n\tms, err := q.WithContext(ctx).Where(q.UID.Eq(uid), q.VaultID.Eq(vaultID)).Order(q.ID.Desc()).Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []*domain.GitSyncConfig\n\tfor _, m := range ms {\n\t\tres = append(res, r.toDomain(m))\n\t}\n\treturn res, nil\n}\n\nfunc (r *gitSyncRepository) ListEnabled(ctx context.Context) ([]*domain.GitSyncConfig, error) {\n\tuids, err := r.dao.GetAllUserUIDs()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar all []*domain.GitSyncConfig\n\tfor _, uid := range uids {\n\t\tq := r.gitSync(uid).GitSyncConfig\n\t\tms, err := q.WithContext(ctx).Where(q.UID.Eq(uid), q.IsEnabled.Eq(1)).Find()\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, m := range ms {\n\t\t\tall = append(all, r.toDomain(m))\n\t\t}\n\t}\n\treturn all, nil\n}\n\nfunc (r *gitSyncRepository) DeleteHistory(ctx context.Context, uid int64, configID int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tq := r.gitSync(uid).GitSyncHistory\n\t\tquery := q.WithContext(ctx).Where(q.UID.Eq(uid))\n\t\tif configID > 0 {\n\t\t\tquery = query.Where(q.ConfigID.Eq(configID))\n\t\t}\n\t\t_, err := query.Delete()\n\t\treturn err\n\t})\n}\n\nfunc (r *gitSyncRepository) DeleteOldHistory(ctx context.Context, uid int64, configID int64, cutoffTime time.Time) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tq := r.gitSync(uid).GitSyncHistory\n\t\tquery := q.WithContext(ctx).Where(q.UID.Eq(uid), q.CreatedAt.Lt(timex.Time(cutoffTime)))\n\t\tif configID > 0 {\n\t\t\tquery = query.Where(q.ConfigID.Eq(configID))\n\t\t}\n\t\t_, err := query.Delete()\n\t\treturn err\n\t})\n}\n\n// DisableByVaultID 禁用仓库下的 Git 同步任务\nfunc (r *gitSyncRepository) DisableByVaultID(ctx context.Context, vaultID, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tq := r.gitSync(uid).GitSyncConfig\n\t\t_, err := q.WithContext(ctx).Where(q.VaultID.Eq(vaultID), q.UID.Eq(uid)).UpdateSimple(q.IsEnabled.Value(0))\n\t\treturn err\n\t})\n}\n\n// Ensure gitSyncRepository implements domain.GitSyncRepository interface\n// 确保 gitSyncRepository 实现了 domain.GitSyncRepository 接口\nvar _ domain.GitSyncRepository = (*gitSyncRepository)(nil)\n"
  },
  {
    "path": "internal/dao/note_fts_repository.go",
    "content": "// Package dao implements the data access layer\n// Package dao 实现数据访问层\npackage dao\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"gorm.io/gorm\"\n)\n\n// noteFTSRepository implements domain.NoteFTSRepository interface\n// noteFTSRepository 实现 domain.NoteFTSRepository 接口\ntype noteFTSRepository struct {\n\tdao             *Dao\n\tcustomPrefixKey string\n}\n\n// NewNoteFTSRepository creates domain.NoteFTSRepository instance\n// NewNoteFTSRepository 创建 domain.NoteFTSRepository 实例\nfunc NewNoteFTSRepository(dao *Dao) domain.NoteFTSRepository {\n\treturn &noteFTSRepository{dao: dao, customPrefixKey: \"user_note_history_\"}\n}\n\nfunc (r *noteFTSRepository) GetKey(uid int64) string {\n\treturn r.customPrefixKey + strconv.FormatInt(uid, 10)\n}\n\n// ensureFTSTable ensures FTS related tables exist\n// ensureFTSTable 确保 FTS 相关表存在\nfunc (r *noteFTSRepository) ensureFTSTable(uid int64) *gorm.DB {\n\tkey := r.GetKey(uid)\n\tdb := r.dao.ResolveDB(key)\n\tif db == nil {\n\t\treturn nil\n\t}\n\n\t// Use onceKeys to ensure it is created only once\n\t// 使用 onceKeys 确保只创建一次\n\tonceKey := key + \"#note_fts_v3\"\n\tif _, loaded := r.dao.onceKeys.LoadOrStore(onceKey, true); !loaded {\n\t\t_ = model.CreateNoteFTSTable(db)\n\t}\n\n\treturn db\n}\n\n// Upsert inserts or updates FTS index\n// Upsert 插入或更新 FTS 索引\nfunc (r *noteFTSRepository) Upsert(ctx context.Context, noteID int64, path, content string, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\t// Ensure table exists\n\t\t// 确保表存在\n\t\t_ = model.CreateNoteFTSTable(db)\n\n\t\t// 1. Update snapshot table\n\t\t// 1. 更新快照表\n\t\tnoteFTS := model.NoteFTS{\n\t\t\tNoteID:  noteID,\n\t\t\tPath:    path,\n\t\t\tContent: content,\n\t\t}\n\t\tif err := db.Save(&noteFTS).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// 2. Update inverted index table\n\t\t// 2. 更新倒排索引表\n\t\t// First delete old index\n\t\t// 先删除旧索引\n\t\tif err := db.Where(\"note_id = ?\", noteID).Delete(&model.NoteFTSToken{}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Tokenization\n\t\t// 分词\n\t\ttokens := util.Tokenize(path + \" \" + content)\n\t\tif len(tokens) == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\t// Batch insert new index\n\t\t// 批量插入新索引\n\t\tvar tokenModels []model.NoteFTSToken\n\t\tfor _, t := range tokens {\n\t\t\ttokenModels = append(tokenModels, model.NoteFTSToken{\n\t\t\t\tNoteID: noteID,\n\t\t\t\tToken:  t,\n\t\t\t})\n\t\t}\n\n\t\treturn db.CreateInBatches(tokenModels, 500).Error\n\t})\n}\n\n// Delete 删除 FTS 索引\nfunc (r *noteFTSRepository) Delete(ctx context.Context, noteID int64, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\t_ = db.Where(\"note_id = ?\", noteID).Delete(&model.NoteFTS{})\n\t\treturn db.Where(\"note_id = ?\", noteID).Delete(&model.NoteFTSToken{}).Error\n\t})\n}\n\n// Search full-text search\n// Search 全文搜索\nfunc (r *noteFTSRepository) Search(ctx context.Context, keyword string, vaultID, uid int64, limit, offset int) ([]int64, error) {\n\tdb := r.ensureFTSTable(uid)\n\tif db == nil {\n\t\treturn nil, nil\n\t}\n\n\ttokens := util.Tokenize(keyword)\n\tif len(tokens) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tvar noteIDs []int64\n\n\t// Build search SQL: find NoteID containing all Token in NoteFTSToken table\n\t// 构建搜索 SQL：在 NoteFTSToken 表中查找包含所有 Token 的 NoteID\n\t// And associate Note table to filter VaultID and Action\n\t// 并关联 Note 表以过滤 VaultID 和 Action\n\tquery := db.Table(\"note_fts_token AS t\").\n\t\tSelect(\"t.note_id\").\n\t\tJoins(\"INNER JOIN note ON t.note_id = note.id\").\n\t\tWhere(\"t.token IN ?\", tokens).\n\t\tWhere(\"note.vault_id = ?\", vaultID).\n\t\tWhere(\"note.action != ?\", \"delete\").\n\t\tGroup(\"t.note_id\").\n\t\tHaving(\"COUNT(DISTINCT t.token) = ?\", len(tokens)).\n\t\tOrder(\"COUNT(t.id) DESC\") // Simple ranking: the higher the frequency, the higher the ranking // 简单的排名：出现频率越高排名越前\n\n\terr := query.WithContext(ctx).Limit(limit).Offset(offset).Scan(&noteIDs).Error\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn noteIDs, nil\n}\n\n// SearchCount 全文搜索计数\nfunc (r *noteFTSRepository) SearchCount(ctx context.Context, keyword string, vaultID, uid int64) (int64, error) {\n\tdb := r.ensureFTSTable(uid)\n\tif db == nil {\n\t\treturn 0, nil\n\t}\n\n\ttokens := util.Tokenize(keyword)\n\tif len(tokens) == 0 {\n\t\treturn 0, nil\n\t}\n\n\tvar count int64\n\n\tsubQuery := db.Table(\"note_fts_token AS t\").\n\t\tSelect(\"t.note_id\").\n\t\tJoins(\"INNER JOIN note ON t.note_id = note.id\").\n\t\tWhere(\"t.token IN ?\", tokens).\n\t\tWhere(\"note.vault_id = ?\", vaultID).\n\t\tWhere(\"note.action != ?\", \"delete\").\n\t\tGroup(\"t.note_id\").\n\t\tHaving(\"COUNT(DISTINCT t.token) = ?\", len(tokens))\n\n\terr := db.Table(\"(?) AS sub\", subQuery).Count(&count).Error\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn count, nil\n}\n\n// RebuildIndex rebuilds index\n// RebuildIndex 重建索引\nfunc (r *noteFTSRepository) RebuildIndex(ctx context.Context, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\t// Drop and rebuild table\n\t\t// 删除并重建表\n\t\tif err := model.DropNoteFTSTable(db); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := model.CreateNoteFTSTable(db); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Get all notes\n\t\t// 获取所有笔记\n\t\tvar notes []model.Note\n\t\tif err := db.Where(\"action != ?\", \"delete\").Find(&notes).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Re-index\n\t\t// 重新索引\n\t\tfor _, note := range notes {\n\t\t\tfolder := r.dao.GetNoteFolderPath(uid, note.ID)\n\t\t\tcontent, exists, err := r.dao.LoadContentFromFile(folder, \"content.txt\")\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif !exists {\n\t\t\t\tcontent = \"\"\n\t\t\t}\n\n\t\t\t// Part that manually calls Upsert logic (since already in transaction)\n\t\t\t// 手动调用 Upsert 逻辑的部分（因为已经在事务里）\n\t\t\tnoteFTS := model.NoteFTS{NoteID: note.ID, Path: note.Path, Content: content}\n\t\t\tdb.Save(&noteFTS)\n\n\t\t\ttokens := util.Tokenize(note.Path + \" \" + content)\n\t\t\tif len(tokens) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tvar tokenModels []model.NoteFTSToken\n\t\t\tfor _, t := range tokens {\n\t\t\t\ttokenModels = append(tokenModels, model.NoteFTSToken{NoteID: note.ID, Token: t})\n\t\t\t}\n\t\t\tdb.CreateInBatches(tokenModels, 500)\n\t\t}\n\n\t\treturn nil\n\t})\n}\n\n// DeleteByVaultID deletes all FTS records for a vault\n// DeleteByVaultID 删除指定仓库的所有 FTS 记录\nfunc (r *noteFTSRepository) DeleteByVaultID(ctx context.Context, vaultID, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\t// 先在 note 表找到该仓库的所有笔记 ID\n\t\tvar noteIDs []int64\n\t\terr := db.Table(\"note\").Where(\"vault_id = ?\", vaultID).Pluck(\"id\", &noteIDs).Error\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif len(noteIDs) == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\t// 从 NoteFTS 删除\n\t\tif err := db.Where(\"note_id IN ?\", noteIDs).Delete(&model.NoteFTS{}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// 从 NoteFTSToken 删除\n\t\treturn db.Where(\"note_id IN ?\", noteIDs).Delete(&model.NoteFTSToken{}).Error\n\t})\n}\n\n// Ensure noteFTSRepository implements domain.NoteFTSRepository interface\n// 确保 noteFTSRepository 实现了 domain.NoteFTSRepository 接口\nvar _ domain.NoteFTSRepository = (*noteFTSRepository)(nil)\n"
  },
  {
    "path": "internal/dao/note_history_repository.go",
    "content": "// Package dao implements the data access layer\n// Package dao 实现数据访问层\npackage dao\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/query\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/logger\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"go.uber.org/zap\"\n\t\"gorm.io/gorm\"\n)\n\n// noteHistoryRepository implements domain.NoteHistoryRepository interface\n// noteHistoryRepository 实现 domain.NoteHistoryRepository 接口\ntype noteHistoryRepository struct {\n\tdao             *Dao\n\tcustomPrefixKey string\n}\n\n// NewNoteHistoryRepository creates NoteHistoryRepository instance\n// NewNoteHistoryRepository 创建 NoteHistoryRepository 实例\nfunc NewNoteHistoryRepository(dao *Dao) domain.NoteHistoryRepository {\n\treturn &noteHistoryRepository{dao: dao, customPrefixKey: \"user_note_history_\"}\n}\n\nfunc (r *noteHistoryRepository) GetKey(uid int64) string {\n\treturn r.customPrefixKey + strconv.FormatInt(uid, 10)\n}\n\nfunc init() {\n\tRegisterModel(ModelConfig{\n\t\tName: \"NoteHistory\",\n\t\tRepoFactory: func(d *Dao) daoDBCustomKey {\n\t\t\treturn NewNoteHistoryRepository(d).(daoDBCustomKey)\n\t\t},\n\t})\n}\n\n// noteHistory gets the note history query object\n// noteHistory 获取笔记历史查询对象\nfunc (r *noteHistoryRepository) noteHistory(uid int64) *query.Query {\n\treturn r.dao.QueryWithOnceInit(func(g *gorm.DB) {\n\t\tmodel.AutoMigrate(g, \"NoteHistory\")\n\t}, r.GetKey(uid)+\"#noteHistory\", r.GetKey(uid))\n}\n\n// toDomain converts database model to domain model\n// toDomain 将数据库模型转换为领域模型\nfunc (r *noteHistoryRepository) toDomain(m *model.NoteHistory, uid int64) (*domain.NoteHistory, error) {\n\tif m == nil {\n\t\treturn nil, nil\n\t}\n\th := &domain.NoteHistory{\n\t\tID:          m.ID,\n\t\tNoteID:      m.NoteID,\n\t\tVaultID:     m.VaultID,\n\t\tPath:        m.Path,\n\t\tDiffPatch:   m.DiffPatch,\n\t\tContent:     m.Content,\n\t\tContentHash: m.ContentHash,\n\t\tClientName:  m.ClientName,\n\t\tClientType:  m.ClientType,\n\t\tClientVersion: m.ClientVersion,\n\t\tVersion:     m.Version,\n\t\tCreatedAt:   time.Time(m.CreatedAt),\n\t\tUpdatedAt:   time.Time(m.UpdatedAt),\n\t}\n\tif err := r.fillHistoryContent(uid, h); err != nil {\n\t\treturn nil, err\n\t}\n\treturn h, nil\n}\n\n// fillHistoryContent fills history record content and patch\n// fillHistoryContent 填充历史记录内容及补丁\nfunc (r *noteHistoryRepository) fillHistoryContent(uid int64, h *domain.NoteHistory) error {\n\tif h == nil {\n\t\treturn nil\n\t}\n\tfolder := r.dao.GetNoteHistoryFolderPath(uid, h.ID)\n\n\t// Load patch\n\t// 加载补丁\n\tpatch, exists, err := r.dao.LoadContentFromFile(folder, \"diff.patch\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif exists {\n\t\th.DiffPatch = patch\n\t} else if h.DiffPatch != \"\" {\n\t\tif err := r.dao.SaveContentToFile(folder, \"diff.patch\", h.DiffPatch); err != nil {\n\t\t\tr.dao.Logger().Warn(\"lazy migration: SaveContentToFile failed for history diff patch\",\n\t\t\t\tzap.Int64(logger.FieldUID, uid),\n\t\t\t\tzap.Int64(\"historyId\", h.ID),\n\t\t\t\tzap.String(logger.FieldMethod, \"noteHistoryRepository.fillHistoryContent\"),\n\t\t\t\tzap.Error(err),\n\t\t\t)\n\t\t}\n\t} else {\n\t\treturn fmt.Errorf(\"history diff patch file not found: %w\", os.ErrNotExist)\n\t}\n\n\t// Load content\n\t// 加载内容\n\tcontent, exists, err := r.dao.LoadContentFromFile(folder, \"content.txt\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif exists {\n\t\th.Content = content\n\t} else if h.Content != \"\" {\n\t\tif err := r.dao.SaveContentToFile(folder, \"content.txt\", h.Content); err != nil {\n\t\t\tr.dao.Logger().Warn(\"lazy migration: SaveContentToFile failed for history content\",\n\t\t\t\tzap.Int64(logger.FieldUID, uid),\n\t\t\t\tzap.Int64(\"historyId\", h.ID),\n\t\t\t\tzap.String(logger.FieldMethod, \"noteHistoryRepository.fillHistoryContent\"),\n\t\t\t\tzap.Error(err),\n\t\t\t)\n\t\t}\n\t} else {\n\t\treturn fmt.Errorf(\"history content file not found: %w\", os.ErrNotExist)\n\t}\n\treturn nil\n}\n\n// GetByID retrieves history record by ID\n// GetByID 根据ID获取历史记录\nfunc (r *noteHistoryRepository) GetByID(ctx context.Context, id, uid int64) (*domain.NoteHistory, error) {\n\tu := r.noteHistory(uid).NoteHistory\n\tm, err := u.WithContext(ctx).Where(u.ID.Eq(id)).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m, uid)\n}\n\n// GetByNoteIDAndHash retrieves history record by note ID and content hash\n// GetByNoteIDAndHash 根据笔记ID和内容哈希获取历史记录\nfunc (r *noteHistoryRepository) GetByNoteIDAndHash(ctx context.Context, noteID int64, contentHash string, uid int64) (*domain.NoteHistory, error) {\n\tu := r.noteHistory(uid).NoteHistory\n\tm, err := u.WithContext(ctx).Where(u.NoteID.Eq(noteID), u.ContentHash.Eq(contentHash)).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m, uid)\n}\n\n// Create creates history record\n// Create 创建历史记录\nfunc (r *noteHistoryRepository) Create(ctx context.Context, history *domain.NoteHistory, uid int64) (*domain.NoteHistory, error) {\n\tvar result *domain.NoteHistory\n\tvar createErr error\n\n\terr := r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.noteHistory(uid).NoteHistory\n\t\tm := &model.NoteHistory{\n\t\t\tNoteID:      history.NoteID,\n\t\t\tVaultID:     history.VaultID,\n\t\t\tPath:        history.Path,\n\t\t\tContentHash: history.ContentHash,\n\t\t\tClientName:  history.ClientName,\n\t\t\tClientType:  history.ClientType,\n\t\t\tClientVersion: history.ClientVersion,\n\t\t\tVersion:     history.Version,\n\t\t\tCreatedAt:   timex.Time(history.CreatedAt),\n\t\t\tUpdatedAt:   timex.Time(history.UpdatedAt),\n\t\t}\n\n\t\t// Temporarily store content for file writing\n\t\t// 暂存内容用于写文件\n\t\tdiffPatch := history.DiffPatch\n\t\tcontent := history.Content\n\n\t\t// Do not save large data in the database\n\t\t// 不在数据库中保存大数据\n\t\tm.DiffPatch = \"\"\n\t\tm.Content = \"\"\n\n\t\tcreateErr = u.WithContext(ctx).Create(m)\n\t\tif createErr != nil {\n\t\t\treturn createErr\n\t\t}\n\n\t\t// Save to file\n\t\t// 保存到文件\n\t\tfolder := r.dao.GetNoteHistoryFolderPath(uid, m.ID)\n\t\tif err := r.dao.SaveContentToFile(folder, \"diff.patch\", diffPatch); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := r.dao.SaveContentToFile(folder, \"content.txt\", content); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\thRes, err := r.toDomain(m, uid)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresult = hRes\n\t\tresult.DiffPatch = diffPatch\n\t\tresult.Content = content\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn result, nil\n}\n\n// ListByNoteID retrieves history record list by note ID\n// ListByNoteID 根据笔记ID获取历史记录列表\nfunc (r *noteHistoryRepository) ListByNoteID(ctx context.Context, noteID int64, page, pageSize int, uid int64) ([]*domain.NoteHistory, int64, error) {\n\tu := r.noteHistory(uid).NoteHistory\n\tq := u.WithContext(ctx).Where(u.NoteID.Eq(noteID))\n\n\tcount, err := q.Count()\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tmodelList, err := q.Order(u.Version.Desc()).\n\t\tLimit(pageSize).\n\t\tOffset(app.GetPageOffset(page, pageSize)).\n\t\tFind()\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tvar results []*domain.NoteHistory\n\tfor _, m := range modelList {\n\t\th, err := r.toDomain(m, uid)\n\t\tif err != nil {\n\t\t\treturn nil, 0, err\n\t\t}\n\t\tresults = append(results, h)\n\t}\n\treturn results, count, nil\n}\n\n// GetLatestVersion retrieves the latest version number of the note\n// GetLatestVersion 获取笔记的最新版本号\nfunc (r *noteHistoryRepository) GetLatestVersion(ctx context.Context, noteID, uid int64) (int64, error) {\n\tu := r.noteHistory(uid).NoteHistory\n\tm, err := u.WithContext(ctx).Where(u.NoteID.Eq(noteID)).Order(u.Version.Desc()).First()\n\tif err != nil {\n\t\tif err == gorm.ErrRecordNotFound {\n\t\t\treturn 0, nil\n\t\t}\n\t\treturn 0, err\n\t}\n\treturn m.Version, nil\n}\n\n// Migrate migrates history records (update NoteID)\n// Migrate 迁移历史记录（更新 NoteID）\nfunc (r *noteHistoryRepository) Migrate(ctx context.Context, oldNoteID, newNoteID, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.noteHistory(uid).NoteHistory\n\t\t_, err := u.WithContext(ctx).Where(u.NoteID.Eq(oldNoteID)).Update(u.NoteID, newNoteID)\n\t\treturn err\n\t})\n}\n\n// GetNoteIDsWithOldHistory retrieves note ID list with old history records\n// GetNoteIDsWithOldHistory 获取有旧历史记录的笔记ID列表\nfunc (r *noteHistoryRepository) GetNoteIDsWithOldHistory(ctx context.Context, cutoffTime int64, uid int64) ([]int64, error) {\n\tu := r.noteHistory(uid).NoteHistory\n\tcutoffTimeValue := timex.Time(time.UnixMilli(cutoffTime))\n\tvar noteIDs []int64\n\terr := u.WithContext(ctx).\n\t\tWhere(u.CreatedAt.Lt(cutoffTimeValue)).\n\t\tDistinct(u.NoteID).\n\t\tPluck(u.NoteID, &noteIDs)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn noteIDs, nil\n}\n\n// DeleteOldVersions deletes old version history records, keeping the most recent N versions\n// DeleteOldVersions 删除旧版本历史记录，保留最近 N 个版本\nfunc (r *noteHistoryRepository) DeleteOldVersions(ctx context.Context, noteID int64, cutoffTime int64, keepVersions int, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.noteHistory(uid).NoteHistory\n\n\t\t// First get the minimum version number of the most recent N versions to be retained\n\t\t// 先获取需要保留的最近 N 个版本的最小版本号\n\t\tvar minKeepVersion int64 = 0\n\t\tif keepVersions > 0 {\n\t\t\thistories, err := u.WithContext(ctx).\n\t\t\t\tWhere(u.NoteID.Eq(noteID)).\n\t\t\t\tOrder(u.Version.Desc()).\n\t\t\t\tLimit(keepVersions).\n\t\t\t\tFind()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif len(histories) > 0 {\n\t\t\t\tminKeepVersion = histories[len(histories)-1].Version\n\t\t\t}\n\t\t}\n\n\t\tcutoffTimeValue := timex.Time(time.UnixMilli(cutoffTime))\n\n\t\t// Query history record IDs to be deleted\n\t\t// Query history record IDs to be deleted\n\t\t// 查询需要删除的历史记录ID\n\t\tvar toDeleteIDs []int64\n\t\tq := u.WithContext(ctx).\n\t\t\tWhere(u.NoteID.Eq(noteID)).\n\t\t\tWhere(u.CreatedAt.Lt(cutoffTimeValue))\n\n\t\tif minKeepVersion > 0 {\n\t\t\tq = q.Where(u.Version.Lt(minKeepVersion))\n\t\t}\n\n\t\thistories, err := q.Find()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfor _, h := range histories {\n\t\t\ttoDeleteIDs = append(toDeleteIDs, h.ID)\n\t\t}\n\n\t\tif len(toDeleteIDs) == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\t// Delete database records\n\t\t// 删除数据库记录\n\t\t_, err = u.WithContext(ctx).Where(u.ID.In(toDeleteIDs...)).Delete()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Delete associated files\n\t\t// 删除关联的文件\n\t\tfor _, id := range toDeleteIDs {\n\t\t\tfolder := r.dao.GetNoteHistoryFolderPath(uid, id)\n\t\t\tif err := r.dao.RemoveContentFolder(folder); err != nil {\n\t\t\t\tr.dao.Logger().Warn(\"failed to delete history folder\",\n\t\t\t\t\tzap.Int64(logger.FieldUID, uid),\n\t\t\t\t\tzap.Int64(\"historyId\", id),\n\t\t\t\t\tzap.String(\"folder\", folder),\n\t\t\t\t\tzap.Error(err),\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t})\n}\n\n// Delete deletes the history record with the specified ID\n// Delete 删除指定ID的历史记录\nfunc (r *noteHistoryRepository) Delete(ctx context.Context, id, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.noteHistory(uid).NoteHistory\n\n\t\t// 删除数据库记录\n\t\t_, err := u.WithContext(ctx).Where(u.ID.Eq(id)).Delete()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Delete associated files\n\t\t// 删除关联的文件\n\t\tfolder := r.dao.GetNoteHistoryFolderPath(uid, id)\n\t\tif err := r.dao.RemoveContentFolder(folder); err != nil {\n\t\t\tr.dao.Logger().Warn(\"failed to delete history folder\",\n\t\t\t\tzap.Int64(logger.FieldUID, uid),\n\t\t\t\tzap.Int64(\"historyId\", id),\n\t\t\t\tzap.String(\"folder\", folder),\n\t\t\t\tzap.Error(err),\n\t\t\t)\n\t\t}\n\n\t\treturn nil\n\t})\n}\n\n// DeleteByVaultID physically deletes all history in a vault\n// DeleteByVaultID 物理删除仓库下的所有历史记录\nfunc (r *noteHistoryRepository) DeleteByVaultID(ctx context.Context, vaultID, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.noteHistory(uid).NoteHistory\n\n\t\t// 查找该仓库下的所有历史记录 ID\n\t\thistories, err := u.WithContext(ctx).Where(u.VaultID.Eq(vaultID)).Select(u.ID).Find()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif len(histories) == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\tvar ids []int64\n\t\tfor _, h := range histories {\n\t\t\tids = append(ids, h.ID)\n\t\t}\n\n\t\t// 从数据库删除\n\t\t_, err = u.WithContext(ctx).Where(u.VaultID.Eq(vaultID)).Delete()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// 删除物理文件夹\n\t\tfor _, id := range ids {\n\t\t\tfolder := r.dao.GetNoteHistoryFolderPath(uid, id)\n\t\t\t_ = r.dao.RemoveContentFolder(folder)\n\t\t}\n\n\t\treturn nil\n\t})\n}\n\n// Ensure noteHistoryRepository implements domain.NoteHistoryRepository interface\n// 确保 noteHistoryRepository 实现了 domain.NoteHistoryRepository 接口\nvar _ domain.NoteHistoryRepository = (*noteHistoryRepository)(nil)\n"
  },
  {
    "path": "internal/dao/note_link_repository.go",
    "content": "// Package dao implements the data access layer\n// Package dao 实现数据访问层\npackage dao\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/query\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/convert\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"gorm.io/gorm\"\n)\n\n// noteLinkRepository implements domain.NoteLinkRepository interface\ntype noteLinkRepository struct {\n\tdao             *Dao\n\tcustomPrefixKey string\n}\n\n// NewNoteLinkRepository creates a NoteLinkRepository instance\nfunc NewNoteLinkRepository(dao *Dao) domain.NoteLinkRepository {\n\treturn &noteLinkRepository{dao: dao, customPrefixKey: \"user_note_link_\"}\n}\n\nfunc (r *noteLinkRepository) GetKey(uid int64) string {\n\treturn r.customPrefixKey + strconv.FormatInt(uid, 10)\n}\n\nfunc init() {\n\tRegisterModel(ModelConfig{\n\t\tName: \"NoteLink\",\n\t\tRepoFactory: func(d *Dao) daoDBCustomKey {\n\t\t\treturn NewNoteLinkRepository(d).(daoDBCustomKey)\n\t\t},\n\t})\n}\n\n// noteLink gets the note link query object\n// noteLink 获取笔记链接查询对象\nfunc (r *noteLinkRepository) noteLink(uid int64) *query.Query {\n\tkey := r.GetKey(uid)\n\treturn r.dao.QueryWithOnceInit(func(g *gorm.DB) {\n\t\tmodel.AutoMigrate(g, \"NoteLink\")\n\t}, key+\"#noteLink\", key)\n}\n\n// toDomain converts database model to domain model\nfunc (r *noteLinkRepository) toDomain(m *model.NoteLink) *domain.NoteLink {\n\tif m == nil {\n\t\treturn nil\n\t}\n\treturn &domain.NoteLink{\n\t\tID:             m.ID,\n\t\tSourceNoteID:   m.SourceNoteID,\n\t\tTargetPath:     m.TargetPath,\n\t\tTargetPathHash: m.TargetPathHash,\n\t\tLinkText:       m.LinkText,\n\t\tIsEmbed:        m.IsEmbed == 1,\n\t\tVaultID:        m.VaultID,\n\t\tCreatedAt:      time.Time(m.CreatedAt),\n\t}\n}\n\n// CreateBatch creates multiple note links in batch\nfunc (r *noteLinkRepository) CreateBatch(ctx context.Context, links []*domain.NoteLink, uid int64) error {\n\tif len(links) == 0 {\n\t\treturn nil\n\t}\n\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tnl := r.noteLink(uid).NoteLink\n\t\tvar models []*model.NoteLink\n\t\tnow := timex.Now()\n\t\tfor _, link := range links {\n\t\t\tmodels = append(models, &model.NoteLink{\n\t\t\t\tSourceNoteID:   link.SourceNoteID,\n\t\t\t\tTargetPath:     link.TargetPath,\n\t\t\t\tTargetPathHash: link.TargetPathHash,\n\t\t\t\tLinkText:       link.LinkText,\n\t\t\t\tIsEmbed:        convert.Bool2Int(link.IsEmbed),\n\t\t\t\tVaultID:        link.VaultID,\n\t\t\t\tUID:            uid,\n\t\t\t\tCreatedAt:      now,\n\t\t\t})\n\t\t}\n\t\treturn nl.WithContext(ctx).CreateInBatches(models, 100)\n\t})\n}\n\n// DeleteBySourceNoteID deletes all links from a source note\nfunc (r *noteLinkRepository) DeleteBySourceNoteID(ctx context.Context, sourceNoteID, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tnl := r.noteLink(uid).NoteLink\n\t\t_, err := nl.WithContext(ctx).Where(nl.SourceNoteID.Eq(sourceNoteID)).Delete()\n\t\treturn err\n\t})\n}\n\n// GetBacklinks gets all notes that link to a target path\nfunc (r *noteLinkRepository) GetBacklinks(ctx context.Context, targetPathHash string, vaultID, uid int64) ([]*domain.NoteLink, error) {\n\tnl := r.noteLink(uid).NoteLink\n\tmodelList, err := nl.WithContext(ctx).\n\t\tWhere(nl.TargetPathHash.Eq(targetPathHash), nl.VaultID.Eq(vaultID)).\n\t\tFind()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar results []*domain.NoteLink\n\tfor _, m := range modelList {\n\t\tresults = append(results, r.toDomain(m))\n\t}\n\treturn results, nil\n}\n\n// GetBacklinksByHashes gets all notes that link to any of the target path hashes.\n// Used for matching path variations (e.g., [[note]], [[folder/note]], [[full/path/note]]).\n// Results are deduplicated by SourceNoteID.\nfunc (r *noteLinkRepository) GetBacklinksByHashes(ctx context.Context, targetPathHashes []string, vaultID, uid int64) ([]*domain.NoteLink, error) {\n\tif len(targetPathHashes) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tnl := r.noteLink(uid).NoteLink\n\tmodelList, err := nl.WithContext(ctx).\n\t\tWhere(nl.TargetPathHash.In(targetPathHashes...), nl.VaultID.Eq(vaultID)).\n\t\tFind()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Deduplicate by SourceNoteID\n\tseen := make(map[int64]bool)\n\tvar results []*domain.NoteLink\n\tfor _, m := range modelList {\n\t\tif !seen[m.SourceNoteID] {\n\t\t\tseen[m.SourceNoteID] = true\n\t\t\tresults = append(results, r.toDomain(m))\n\t\t}\n\t}\n\treturn results, nil\n}\n\n// GetOutlinks gets all links from a source note\nfunc (r *noteLinkRepository) GetOutlinks(ctx context.Context, sourceNoteID, uid int64) ([]*domain.NoteLink, error) {\n\tnl := r.noteLink(uid).NoteLink\n\tmodelList, err := nl.WithContext(ctx).\n\t\tWhere(nl.SourceNoteID.Eq(sourceNoteID)).\n\t\tFind()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar results []*domain.NoteLink\n\tfor _, m := range modelList {\n\t\tresults = append(results, r.toDomain(m))\n\t}\n\treturn results, nil\n}\n\n// DeleteByVaultID deletes all links for a vault\nfunc (r *noteLinkRepository) DeleteByVaultID(ctx context.Context, vaultID, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tnl := r.noteLink(uid).NoteLink\n\t\t_, err := nl.WithContext(ctx).Where(nl.VaultID.Eq(vaultID)).Delete()\n\t\treturn err\n\t})\n}\n\n// Ensure noteLinkRepository implements domain.NoteLinkRepository interface\nvar _ domain.NoteLinkRepository = (*noteLinkRepository)(nil)\n"
  },
  {
    "path": "internal/dao/note_repository.go",
    "content": "// Package dao implements the data access layer\n// Package dao 实现数据访问层\npackage dao\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/query\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/logger\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"go.uber.org/zap\"\n\t\"gorm.io/gorm\"\n)\n\n// noteRepository implements domain.NoteRepository interface\n// noteRepository 实现 domain.NoteRepository 接口\ntype noteRepository struct {\n\tdao             *Dao\n\tcustomPrefixKey string\n}\n\n// NewNoteRepository creates NoteRepository instance\n// NewNoteRepository 创建 NoteRepository 实例\nfunc NewNoteRepository(dao *Dao) domain.NoteRepository {\n\treturn &noteRepository{dao: dao, customPrefixKey: \"user_\"}\n}\n\nfunc (r *noteRepository) GetKey(uid int64) string {\n\treturn r.customPrefixKey + strconv.FormatInt(uid, 10)\n}\n\nfunc init() {\n\tRegisterModel(ModelConfig{\n\t\tName: \"Note\",\n\t\tRepoFactory: func(d *Dao) daoDBCustomKey {\n\t\t\treturn NewNoteRepository(d).(daoDBCustomKey)\n\t\t},\n\t})\n}\n\n// note 获取笔记查询对象\nfunc (r *noteRepository) note(uid int64) *query.Query {\n\treturn r.dao.QueryWithOnceInit(func(g *gorm.DB) {\n\t\tmodel.AutoMigrate(g, \"Note\")\n\t\t// Initialize universal full-text search table\n\t\t// 初始化通用全文搜索表\n\t\t_ = model.CreateNoteFTSTable(g)\n\t}, r.GetKey(uid)+\"#note_v3\", r.GetKey(uid))\n}\n\n// ListByIDs retrieves note list by ID list\n// ListByIDs 根据ID列表获取笔记列表\nfunc (r *noteRepository) ListByIDs(ctx context.Context, ids []int64, uid int64) ([]*domain.Note, error) {\n\tif len(ids) == 0 {\n\t\treturn []*domain.Note{}, nil\n\t}\n\tu := r.note(uid).Note\n\tms, err := u.WithContext(ctx).Where(u.ID.In(ids...)).Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []*domain.Note\n\tfor _, m := range ms {\n\t\tnote, err := r.toDomain(m, uid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tres = append(res, note)\n\t}\n\treturn res, nil\n}\n\n// EnsureFTSIndex ensures FTS index exists (public method, can be called manually)\n// EnsureFTSIndex 确保 FTS 索引存在（公开方法，可手动调用）\nfunc (r *noteRepository) EnsureFTSIndex(ctx context.Context, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tkey := r.GetKey(uid)\n\t\tftsKey := key + \"#fts_indexed\"\n\n\t\t// Use onceKeys to ensure each user is checked only once\n\t\t// 使用 onceKeys 确保每个用户只检查一次\n\t\tif _, loaded := r.dao.onceKeys.LoadOrStore(ftsKey, true); loaded {\n\t\t\treturn nil // Already checked // 已检查过\n\t\t}\n\n\t\t// Ensure FTS table exists (automatically checks version and rebuilds)\n\t\t// 确保 FTS 表存在（会自动检查版本并重建）\n\t\t_ = model.CreateNoteFTSTable(db)\n\n\t\t// Check if FTS index is empty\n\t\t// 检查 FTS 索引是否为空\n\t\tvar ftsCount int64\n\t\tdb.Model(&model.NoteFTSToken{}).Count(&ftsCount)\n\t\tif ftsCount > 0 {\n\t\t\treturn nil // Index already exists // 已有索引\n\t\t}\n\n\t\t// Check if any notes need indexing\n\t\t// 检查是否有笔记需要索引\n\t\tvar noteCount int64\n\t\tdb.Model(&model.Note{}).Where(\"action != ?\", \"delete\").Count(&noteCount)\n\t\tif noteCount == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\t// Synchronously rebuild index\n\t\t// 同步重建索引\n\t\tvar notes []model.Note\n\t\tif err := db.Where(\"action != ?\", \"delete\").Find(&notes).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfor _, note := range notes {\n\t\t\tfolder := r.dao.GetNoteFolderPath(uid, note.ID)\n\t\t\tcontent, exists, err := r.dao.LoadContentFromFile(folder, \"content.txt\")\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif !exists {\n\t\t\t\tcontent = \"\"\n\t\t\t}\n\t\t\tr.upsertFTS(db, note.ID, note.Path, content)\n\t\t}\n\n\t\treturn nil\n\t})\n}\n\n// toDomain converts DAO Note to domain model\n// toDomain 将 DAO Note 转换为领域模型\nfunc (r *noteRepository) toDomain(m *model.Note, uid int64) (*domain.Note, error) {\n\tif m == nil {\n\t\treturn nil, nil\n\t}\n\tnote := &domain.Note{\n\t\tID:                      m.ID,\n\t\tVaultID:                 m.VaultID,\n\t\tAction:                  domain.NoteAction(m.Action),\n\t\tRename:                  m.Rename,\n\t\tFID:                     m.FID,\n\t\tPath:                    m.Path,\n\t\tPathHash:                m.PathHash,\n\t\tContent:                 m.Content,\n\t\tContentHash:             m.ContentHash,\n\t\tContentLastSnapshot:     m.ContentLastSnapshot,\n\t\tContentLastSnapshotHash: m.ContentLastSnapshotHash,\n\t\tVersion:                 m.Version,\n\t\tClientName:              m.ClientName,\n\t\tClientType:              m.ClientType,\n\t\tClientVersion:           m.ClientVersion,\n\t\tSize:                    m.Size,\n\t\tCtime:                   m.Ctime,\n\t\tMtime:                   m.Mtime,\n\t\tUpdatedTimestamp:        m.UpdatedTimestamp,\n\t\tCreatedAt:               time.Time(m.CreatedAt),\n\t\tUpdatedAt:               time.Time(m.UpdatedAt),\n\t}\n\tif err := r.fillNoteContent(uid, note); err != nil {\n\t\treturn nil, err\n\t}\n\treturn note, nil\n}\n\n// toModel converts domain model to database model\n// toModel 将领域模型转换为数据库模型\nfunc (r *noteRepository) toModel(note *domain.Note) *model.Note {\n\tif note == nil {\n\t\treturn nil\n\t}\n\treturn &model.Note{\n\t\tID:                      note.ID,\n\t\tVaultID:                 note.VaultID,\n\t\tAction:                  string(note.Action),\n\t\tRename:                  note.Rename,\n\t\tFID:                     note.FID,\n\t\tPath:                    note.Path,\n\t\tPathHash:                note.PathHash,\n\t\tContent:                 note.Content,\n\t\tContentHash:             note.ContentHash,\n\t\tContentLastSnapshot:     note.ContentLastSnapshot,\n\t\tContentLastSnapshotHash: note.ContentLastSnapshotHash,\n\t\tVersion:                 note.Version,\n\t\tClientName:              note.ClientName,\n\t\tClientType:              note.ClientType,\n\t\tClientVersion:           note.ClientVersion,\n\t\tSize:                    note.Size,\n\t\tCtime:                   note.Ctime,\n\t\tMtime:                   note.Mtime,\n\t\tUpdatedTimestamp:        note.UpdatedTimestamp,\n\t\tCreatedAt:               timex.Time(note.CreatedAt),\n\t\tUpdatedAt:               timex.Time(note.UpdatedAt),\n\t}\n}\n\n// fillNoteContent fills note content\n// fillNoteContent 填充笔记内容\nfunc (r *noteRepository) fillNoteContent(uid int64, n *domain.Note) error {\n\tif n == nil {\n\t\treturn nil\n\t}\n\tfolder := r.dao.GetNoteFolderPath(uid, n.ID)\n\n\t// Load content\n\t// 加载内容\n\tcontent, exists, err := r.dao.LoadContentFromFile(folder, \"content.txt\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif exists {\n\t\tn.Content = content\n\t} else if n.Content != \"\" {\n\t\t// Lazy migration failed, log warning but do not block flow\n\t\t// 懒迁移失败记录警告日志但不阻断流程\n\t\tif err := r.dao.SaveContentToFile(folder, \"content.txt\", n.Content); err != nil {\n\t\t\tr.dao.Logger().Warn(\"lazy migration: SaveContentToFile failed for note content\",\n\t\t\t\tzap.Int64(logger.FieldUID, uid),\n\t\t\t\tzap.Int64(\"noteId\", n.ID),\n\t\t\t\tzap.String(logger.FieldMethod, \"noteRepository.fillNoteContent\"),\n\t\t\t\tzap.Error(err),\n\t\t\t)\n\t\t}\n\t} else {\n\t\t// File does not exist and no content to migrate, return error to prevent data loss (treated as read failure)\n\t\t// 文件不存在且没有可迁移的内容，返回错误以防止数据丢失（视为读取失败）\n\t\treturn fmt.Errorf(\"note content file not found: %w\", os.ErrNotExist)\n\t}\n\n\t// Load snapshot\n\t// 加载快照\n\tsnapshot, exists, err := r.dao.LoadContentFromFile(folder, \"snapshot.txt\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif exists {\n\t\tn.ContentLastSnapshot = snapshot\n\t} else if n.ContentLastSnapshot != \"\" {\n\t\t// Lazy migration failed, log warning but do not block flow\n\t\t// 懒迁移失败记录警告日志但不阻断流程\n\t\tif err := r.dao.SaveContentToFile(folder, \"snapshot.txt\", n.ContentLastSnapshot); err != nil {\n\t\t\tr.dao.Logger().Warn(\"lazy migration: SaveContentToFile failed for note snapshot\",\n\t\t\t\tzap.Int64(logger.FieldUID, uid),\n\t\t\t\tzap.Int64(\"noteId\", n.ID),\n\t\t\t\tzap.String(logger.FieldMethod, \"noteRepository.fillNoteContent\"),\n\t\t\t\tzap.Error(err),\n\t\t\t)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// GetByID retrieves note by ID\n// GetByID 根据ID获取笔记\nfunc (r *noteRepository) GetByID(ctx context.Context, id, uid int64) (*domain.Note, error) {\n\tu := r.note(uid).Note\n\tm, err := u.WithContext(ctx).Where(u.ID.Eq(id)).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m, uid)\n}\n\n// GetByPathHash retrieves note by path hash (excluding deleted)\n// GetByPathHash 根据路径哈希获取笔记（排除已删除）\nfunc (r *noteRepository) GetByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) (*domain.Note, error) {\n\tu := r.note(uid).Note\n\tm, err := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.PathHash.Eq(pathHash),\n\t\tu.Action.Neq(\"delete\"),\n\t).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m, uid)\n}\n\n// GetByPathHashIncludeRecycle retrieves note by path hash (optionally including recycle bin)\n// GetByPathHashIncludeRecycle 根据路径哈希获取笔记（可选包含回收站）\nfunc (r *noteRepository) GetByPathHashIncludeRecycle(ctx context.Context, pathHash string, vaultID, uid int64, isRecycle bool) (*domain.Note, error) {\n\tu := r.note(uid).Note\n\tq := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.PathHash.Eq(pathHash),\n\t)\n\n\tif isRecycle {\n\t\tq = q.Where(u.Action.Eq(\"delete\"), u.Rename.Eq(0))\n\t} else {\n\t\tq = q.Where(u.Action.Neq(\"delete\"))\n\t}\n\n\tm, err := q.First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m, uid)\n}\n\n// GetAllByPathHash retrieves note by path hash (including all statuses)\n// GetAllByPathHash 根据路径哈希获取笔记（包含所有状态）\nfunc (r *noteRepository) GetAllByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) (*domain.Note, error) {\n\tu := r.note(uid).Note\n\tm, err := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.PathHash.Eq(pathHash),\n\t).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m, uid)\n}\n\n// ListByPathHash retrieves note list by path hash (handling duplicate records)\n// ListByPathHash 根据路径哈希获取笔记列表（处理重复记录）\nfunc (r *noteRepository) ListByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) ([]*domain.Note, error) {\n\tu := r.note(uid).Note\n\tms, err := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.PathHash.Eq(pathHash),\n\t).Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []*domain.Note\n\tfor _, m := range ms {\n\t\tnote, err := r.toDomain(m, uid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tres = append(res, note)\n\t}\n\treturn res, nil\n}\n\n// GetByPath retrieves note by path\n// GetByPath 根据路径获取笔记\nfunc (r *noteRepository) GetByPath(ctx context.Context, path string, vaultID, uid int64) (*domain.Note, error) {\n\tu := r.note(uid).Note\n\tm, err := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.Path.Eq(path),\n\t).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m, uid)\n}\n\n// Create creates a note\n// Create 创建笔记\nfunc (r *noteRepository) Create(ctx context.Context, note *domain.Note, uid int64) (*domain.Note, error) {\n\tvar result *domain.Note\n\tvar createErr error\n\n\terr := r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.note(uid).Note\n\t\tm := r.toModel(note)\n\n\t\tm.UpdatedTimestamp = timex.Now().UnixMilli()\n\t\tm.CreatedAt = timex.Now()\n\t\tm.UpdatedAt = timex.Now()\n\n\t\tcontent := m.Content\n\t\tm.Content = \"\"             // Do not store content in database // 不在数据库存储内容\n\t\tm.ContentLastSnapshot = \"\" // Do not store snapshot in database // 不在数据库存储快照\n\n\t\tcreateErr = u.WithContext(ctx).Create(m)\n\t\tif createErr != nil {\n\t\t\treturn createErr\n\t\t}\n\n\t\t// Save content to file\n\t\t// 保存内容到文件\n\t\tfolder := r.dao.GetNoteFolderPath(uid, m.ID)\n\t\tif err := r.dao.SaveContentToFile(folder, \"content.txt\", content); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// 更新 FTS 索引\n\t\tr.upsertFTS(db, m.ID, m.Path, content)\n\n\t\tnoteRes, err := r.toDomain(m, uid)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresult = noteRes\n\n\t\tresult.Content = content\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn result, createErr\n}\n\n// Update updates a note\n// Update 更新笔记\nfunc (r *noteRepository) Update(ctx context.Context, note *domain.Note, uid int64) (*domain.Note, error) {\n\tvar result *domain.Note\n\tvar updateErr error\n\n\terr := r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.note(uid).Note\n\t\tm := r.toModel(note)\n\n\t\tm.UpdatedTimestamp = timex.Now().UnixMilli()\n\t\tm.UpdatedAt = timex.Now()\n\n\t\tcontent := m.Content\n\t\tm.Content = \"\" // Do not update content in database // 不在数据库更新内容\n\n\t\tupdateErr = u.WithContext(ctx).Where(\n\t\t\tu.ID.Eq(m.ID),\n\t\t).Select(\n\t\t\tu.ID,\n\t\t\tu.VaultID,\n\t\t\tu.Action,\n\t\t\tu.Rename,\n\t\t\tu.Path,\n\t\t\tu.PathHash,\n\t\t\tu.Content,\n\t\t\tu.ContentHash,\n\t\t\tu.ClientName,\n\t\t\tu.ClientType,\n\t\t\tu.ClientVersion,\n\t\t\tu.Size,\n\t\t\tu.Ctime,\n\t\t\tu.Mtime,\n\t\t\tu.Version,\n\t\t\tu.UpdatedAt,\n\t\t\tu.UpdatedTimestamp,\n\t\t\tu.FID,\n\t\t).Save(m)\n\n\t\tif updateErr != nil {\n\t\t\treturn updateErr\n\t\t}\n\n\t\t// Save content to file\n\t\t// 保存内容到文件\n\t\tfolder := r.dao.GetNoteFolderPath(uid, m.ID)\n\t\tif err := r.dao.SaveContentToFile(folder, \"content.txt\", content); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// 更新 FTS 索引\n\t\tr.upsertFTS(db, m.ID, m.Path, content)\n\n\t\tnoteRes, err := r.toDomain(m, uid)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresult = noteRes\n\n\t\tresult.Content = content\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn result, updateErr\n}\n\n// UpdateDelete updates note to deleted status\n// UpdateDelete 更新笔记为删除状态\nfunc (r *noteRepository) UpdateDelete(ctx context.Context, note *domain.Note, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.note(uid).Note\n\t\tm := &model.Note{\n\t\t\tID:               note.ID,\n\t\t\tAction:           string(note.Action),\n\t\t\tRename:           note.Rename,\n\t\t\tClientName:       note.ClientName,\n\t\t\tClientType:       note.ClientType,\n\t\t\tClientVersion:    note.ClientVersion,\n\t\t\tMtime:            note.Mtime,\n\t\t\tUpdatedTimestamp: timex.Now().UnixMilli(),\n\t\t}\n\n\t\treturn u.WithContext(ctx).Where(\n\t\t\tu.ID.Eq(m.ID),\n\t\t).Select(\n\t\t\tu.ID,\n\t\t\tu.Action,\n\t\t\tu.Rename,\n\t\t\tu.ClientName,\n\t\t\tu.ClientType,\n\t\t\tu.ClientVersion,\n\t\t\tu.Mtime,\n\t\t\tu.UpdatedTimestamp,\n\t\t).Save(m)\n\t})\n}\n\n// UpdateMtime updates note modification time\n// UpdateMtime 更新笔记修改时间\nfunc (r *noteRepository) UpdateMtime(ctx context.Context, mtime int64, id, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.note(uid).Note\n\n\t\t_, err := u.WithContext(ctx).Where(\n\t\t\tu.ID.Eq(id),\n\t\t).UpdateSimple(\n\t\t\tu.Mtime.Value(mtime),\n\t\t\tu.UpdatedTimestamp.Value(timex.Now().UnixMilli()),\n\t\t\tu.UpdatedAt.Value(timex.Now()),\n\t\t)\n\t\treturn err\n\t})\n}\n\n// UpdateActionMtime updates note modification time\n// UpdateActionMtime 更新笔记修改时间\nfunc (r *noteRepository) UpdateActionMtime(ctx context.Context, action domain.NoteAction, mtime int64, id, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.note(uid).Note\n\n\t\t_, err := u.WithContext(ctx).Where(\n\t\t\tu.ID.Eq(id),\n\t\t).UpdateSimple(\n\t\t\tu.Action.Value(string(action)),\n\t\t\tu.Mtime.Value(mtime),\n\t\t\tu.UpdatedTimestamp.Value(timex.Now().UnixMilli()),\n\t\t\tu.UpdatedAt.Value(timex.Now()),\n\t\t)\n\t\treturn err\n\t})\n}\n\n// UpdateSnapshot updates note snapshot\n// UpdateSnapshot 更新笔记快照\nfunc (r *noteRepository) UpdateSnapshot(ctx context.Context, snapshot, snapshotHash string, version, id, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.note(uid).Note\n\n\t\t// 保存快照到文件\n\t\tfolder := r.dao.GetNoteFolderPath(uid, id)\n\t\tif err := r.dao.SaveContentToFile(folder, \"snapshot.txt\", snapshot); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t_, err := u.WithContext(ctx).Where(u.ID.Eq(id)).UpdateSimple(\n\t\t\tu.ContentLastSnapshot.Value(\"\"),\n\t\t\tu.ContentLastSnapshotHash.Value(snapshotHash),\n\t\t\tu.Version.Value(version),\n\t\t)\n\t\treturn err\n\t})\n}\n\n// Delete physically deletes a note\n// Delete 物理删除笔记\nfunc (r *noteRepository) Delete(ctx context.Context, id, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.note(uid).Note\n\t\t_, err := u.WithContext(ctx).Where(u.ID.Eq(id)).Delete()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Delete physical files\n\t\t// 删除物理文件\n\t\tfolder := r.dao.GetNoteFolderPath(uid, id)\n\t\t_ = r.dao.RemoveContentFolder(folder)\n\n\t\treturn nil\n\t})\n}\n\n// DeletePhysicalByTime physically deletes notes marked as deleted by time\n// DeletePhysicalByTime 根据时间物理删除已标记删除的笔记\nfunc (r *noteRepository) DeletePhysicalByTime(ctx context.Context, timestamp, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.note(uid).Note\n\n\t\t// 先找到要删除的 ID\n\t\tlist, _ := u.WithContext(ctx).Where(\n\t\t\tu.Action.Eq(\"delete\"),\n\t\t\tu.UpdatedTimestamp.Lt(timestamp),\n\t\t).Select(u.ID).Find()\n\n\t\t_, err := u.WithContext(ctx).Where(\n\t\t\tu.Action.Eq(\"delete\"),\n\t\t\tu.UpdatedTimestamp.Lt(timestamp),\n\t\t).Delete()\n\n\t\tif err == nil {\n\t\t\tfor _, m := range list {\n\t\t\t\tfolder := r.dao.GetNoteFolderPath(uid, m.ID)\n\t\t\t\t_ = r.dao.RemoveContentFolder(folder)\n\t\t\t}\n\t\t}\n\t\treturn err\n\t})\n}\n\n// DeletePhysicalByTimeAll physically deletes notes marked as deleted for all users by time\n// DeletePhysicalByTimeAll 根据时间物理删除所有用户的已标记删除的笔记\nfunc (r *noteRepository) DeletePhysicalByTimeAll(ctx context.Context, timestamp int64) error {\n\t// Get all user UIDs\n\t// 获取所有用户 UID\n\tuids, err := r.dao.GetAllUserUIDs()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Execute cleanup user by user\n\t// 逐用户执行清理\n\tfor i, uid := range uids {\n\t\t// 增加错峰延迟，避免瞬间触发大量写事务\n\t\tif i > 0 {\n\t\t\ttime.Sleep(500 * time.Millisecond)\n\t\t}\n\t\tif err := r.DeletePhysicalByTime(ctx, timestamp, uid); err != nil {\n\t\t\t// 记录错误但继续处理其他用户\n\t\t\tcontinue\n\t\t}\n\t}\n\treturn nil\n}\n\n// List retrieves note list by page\n// List 分页获取笔记列表\nfunc (r *noteRepository) List(ctx context.Context, vaultID int64, page, pageSize int, uid int64, keyword string, isRecycle bool, searchMode string, searchContent bool, sortBy string, sortOrder string, paths []string) ([]*domain.Note, error) {\n\tu := r.note(uid).Note\n\tq := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t)\n\n\tif isRecycle {\n\t\tq = q.Where(u.Action.Eq(\"delete\"), u.Rename.Eq(0))\n\t} else {\n\t\tq = q.Where(u.Action.Neq(\"delete\"))\n\t}\n\n\t// 构建排序语句\n\torderClause := buildOrderClause(sortBy, sortOrder)\n\n\tvar modelList []*model.Note\n\tvar err error\n\n\tif len(paths) > 0 {\n\t\t// 精确路径列表查询（分享筛选模式），忽略 keyword\n\t\terr = q.UnderlyingDB().Where(\"path IN ?\", paths).\n\t\t\tOrder(orderClause).\n\t\t\tLimit(pageSize).\n\t\t\tOffset(app.GetPageOffset(page, pageSize)).\n\t\t\tFind(&modelList).Error\n\t} else if keyword != \"\" {\n\t\t// 内容搜索模式：使用 FTS5 全文搜索\n\t\tif searchMode == \"content\" {\n\t\t\t// 使用干净的 DB 连接执行 FTS 查询，避免继承 note 表上下文导致 JOIN 二义性\n\t\t\tftsDB := r.dao.ResolveDB(r.GetKey(uid))\n\n\t\t\t// 确保 FTS 索引存在\n\t\t\tr.EnsureFTSIndex(ctx, uid)\n\n\t\t\tnoteIDs, ftsErr := r.searchFTS(ftsDB, keyword, vaultID, isRecycle, sortBy, sortOrder, pageSize, app.GetPageOffset(page, pageSize))\n\t\t\tif ftsErr != nil {\n\t\t\t\treturn nil, ftsErr\n\t\t\t}\n\n\t\t\tif len(noteIDs) == 0 {\n\t\t\t\treturn []*domain.Note{}, nil\n\t\t\t}\n\n\t\t\t// 根据 FTS 返回的 ID 查询完整笔记，保持 FTS 返回的顺序\n\t\t\terr = q.UnderlyingDB().Where(\"id IN ?\", noteIDs).Order(orderClause).Find(&modelList).Error\n\t\t} else {\n\t\t\t// 路径搜索：使用 LIKE\n\t\t\tkey := \"%\" + keyword + \"%\"\n\t\t\terr = q.UnderlyingDB().Where(\"path LIKE ?\", key).\n\t\t\t\tOrder(orderClause).\n\t\t\t\tLimit(pageSize).\n\t\t\t\tOffset(app.GetPageOffset(page, pageSize)).\n\t\t\t\tFind(&modelList).Error\n\t\t}\n\t} else {\n\t\terr = q.UnderlyingDB().\n\t\t\tOrder(orderClause).\n\t\t\tLimit(pageSize).\n\t\t\tOffset(app.GetPageOffset(page, pageSize)).\n\t\t\tFind(&modelList).Error\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar list []*domain.Note\n\tfor _, m := range modelList {\n\t\tnote, err := r.toDomain(m, uid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlist = append(list, note)\n\n\t}\n\treturn list, nil\n}\n\nfunc (r *noteRepository) ListByPathPrefix(ctx context.Context, pathPrefix string, vaultID, uid int64) ([]*domain.Note, error) {\n\tu := r.note(uid).Note\n\t// Use LIKE 'prefix/%'\n\t// 使用 LIKE 'prefix/%'\n\tpattern := pathPrefix + \"/%\"\n\tms, err := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.Path.Like(pattern),\n\t\tu.Action.Neq(\"delete\"),\n\t).Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar res []*domain.Note\n\tfor _, m := range ms {\n\t\tnote, err := r.toDomain(m, uid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tres = append(res, note)\n\t}\n\treturn res, nil\n}\n\n// getSortField maps sort fields\n// getSortField 映射排序字段\nfunc getSortField(sortBy string) string {\n\tswitch sortBy {\n\tcase \"ctime\":\n\t\treturn \"ctime\"\n\tcase \"path\":\n\t\treturn \"path\"\n\tdefault:\n\t\treturn \"mtime\"\n\t}\n}\n\n// buildOrderClause builds order clause\n// buildOrderClause 构建排序语句\nfunc buildOrderClause(sortBy, sortOrder string) string {\n\t// 默认值\n\tif sortOrder == \"\" {\n\t\tsortOrder = \"desc\"\n\t}\n\n\t// 验证排序方向\n\tif sortOrder != \"asc\" && sortOrder != \"desc\" {\n\t\tsortOrder = \"desc\"\n\t}\n\n\treturn getSortField(sortBy) + \" \" + sortOrder\n}\n\n// ListCount retrieves note count\n// ListCount 获取笔记数量\nfunc (r *noteRepository) ListCount(ctx context.Context, vaultID, uid int64, keyword string, isRecycle bool, searchMode string, searchContent bool, paths []string) (int64, error) {\n\tu := r.note(uid).Note\n\tq := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t)\n\n\tif isRecycle {\n\t\tq = q.Where(u.Action.Eq(\"delete\"), u.Rename.Eq(0))\n\t} else {\n\t\tq = q.Where(u.Action.Neq(\"delete\"))\n\t}\n\n\tvar count int64\n\tvar err error\n\n\tif len(paths) > 0 {\n\t\t// 精确路径列表计数（分享筛选模式）\n\t\terr = q.UnderlyingDB().Where(\"path IN ?\", paths).Count(&count).Error\n\t} else if keyword != \"\" {\n\t\t// 内容搜索模式：使用 FTS5 全文搜索\n\t\tif searchMode == \"content\" {\n\t\t\t// 使用干净的 DB 连接，避免二义性\n\t\t\tftsDB := r.dao.ResolveDB(r.GetKey(uid))\n\t\t\tcount, err = r.searchFTSCount(ftsDB, keyword, vaultID, isRecycle)\n\t\t} else {\n\t\t\t// 路径搜索：使用 LIKE\n\t\t\tkey := \"%\" + keyword + \"%\"\n\t\t\terr = q.UnderlyingDB().Where(\"path LIKE ?\", key).Count(&count).Error\n\t\t}\n\t} else {\n\t\tcount, err = q.Order(u.CreatedAt).Count()\n\t}\n\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn count, nil\n}\n\n// ListByUpdatedTimestamp retrieves note list by updated timestamp\n// ListByUpdatedTimestamp 根据更新时间戳获取笔记列表\nfunc (r *noteRepository) ListByUpdatedTimestamp(ctx context.Context, timestamp, vaultID, uid int64) ([]*domain.Note, error) {\n\treturn r.ListByUpdatedTimestampPage(ctx, timestamp, vaultID, uid, 0, 0)\n}\n\n// ListByUpdatedTimestampPage retrieves note list by updated timestamp by page\n// ListByUpdatedTimestampPage 根据更新时间戳分页获取笔记列表\nfunc (r *noteRepository) ListByUpdatedTimestampPage(ctx context.Context, timestamp, vaultID, uid int64, offset, limit int) ([]*domain.Note, error) {\n\tu := r.note(uid).Note\n\tquery := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.UpdatedTimestamp.Gt(timestamp),\n\t).Order(u.UpdatedTimestamp.Desc())\n\n\tvar mList []*model.Note\n\tvar err error\n\tif limit > 0 {\n\t\tmList, _, err = query.FindByPage(offset, limit)\n\t} else {\n\t\tmList, err = query.Find()\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar list []*domain.Note\n\tfor _, m := range mList {\n\t\tnote, err := r.toDomain(m, uid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlist = append(list, note)\n\n\t}\n\treturn list, nil\n}\n\n// ListContentUnchanged retrieves note list with unchanged content\n// ListContentUnchanged 获取内容未变更的笔记列表\nfunc (r *noteRepository) ListContentUnchanged(ctx context.Context, uid int64) ([]*domain.Note, error) {\n\tu := r.note(uid).Note\n\tvar mList []*model.Note\n\n\terr := u.WithContext(ctx).UnderlyingDB().Where(\n\t\t\"action != ?\", \"delete\",\n\t).Where(\"content_hash != content_last_snapshot_hash\").\n\t\tFind(&mList).Error\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar list []*domain.Note\n\tfor _, m := range mList {\n\t\tnote, err := r.toDomain(m, uid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlist = append(list, note)\n\n\t}\n\treturn list, nil\n}\n\n// CountSizeSum 获取笔记数量和大小总和\nfunc (r *noteRepository) CountSizeSum(ctx context.Context, vaultID, uid int64) (*domain.CountSizeResult, error) {\n\tu := r.note(uid).Note\n\n\tresult := &struct {\n\t\tSize  int64\n\t\tCount int64\n\t}{}\n\n\terr := u.WithContext(ctx).Select(u.Size.Sum().As(\"size\"), u.Size.Count().As(\"count\")).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.Action.Neq(\"delete\"),\n\t\tu.Rename.Eq(0),\n\t).Scan(result)\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &domain.CountSizeResult{\n\t\tCount: result.Count,\n\t\tSize:  result.Size,\n\t}, nil\n}\n\n// ListByFID 根据文件夹ID获取笔记列表\nfunc (r *noteRepository) ListByFID(ctx context.Context, fid, vaultID, uid int64, page, pageSize int, sortBy, sortOrder string) ([]*domain.Note, error) {\n\tu := r.note(uid).Note\n\tq := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.FID.Eq(fid),\n\t\tu.Action.Neq(\"delete\"),\n\t)\n\n\t// 构建排序语句\n\torderClause := buildOrderClause(sortBy, sortOrder)\n\n\tvar modelList []*model.Note\n\terr := q.UnderlyingDB().\n\t\tOrder(orderClause).\n\t\tLimit(pageSize).\n\t\tOffset(app.GetPageOffset(page, pageSize)).\n\t\tFind(&modelList).Error\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar list []*domain.Note\n\tfor _, m := range modelList {\n\t\tnote, err := r.toDomain(m, uid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlist = append(list, note)\n\n\t}\n\treturn list, nil\n}\n\n// ListByFIDCount 根据文件夹ID获取笔记数量\nfunc (r *noteRepository) ListByFIDCount(ctx context.Context, fid, vaultID, uid int64) (int64, error) {\n\tu := r.note(uid).Note\n\tq := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.FID.Eq(fid),\n\t\tu.Action.Neq(\"delete\"),\n\t)\n\n\treturn q.Count()\n}\n\nfunc (r *noteRepository) ListByFIDs(ctx context.Context, fids []int64, vaultID, uid int64, page, pageSize int, sortBy, sortOrder string) ([]*domain.Note, error) {\n\tu := r.note(uid).Note\n\tq := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.FID.In(fids...),\n\t\tu.Action.Neq(\"delete\"),\n\t)\n\n\torderClause := buildOrderClause(sortBy, sortOrder)\n\n\tvar modelList []*model.Note\n\terr := q.UnderlyingDB().\n\t\tOrder(orderClause).\n\t\tLimit(pageSize).\n\t\tOffset(app.GetPageOffset(page, pageSize)).\n\t\tFind(&modelList).Error\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar list []*domain.Note\n\tfor _, m := range modelList {\n\t\tnote, err := r.toDomain(m, uid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlist = append(list, note)\n\n\t}\n\treturn list, nil\n}\n\nfunc (r *noteRepository) ListByFIDsCount(ctx context.Context, fids []int64, vaultID, uid int64) (int64, error) {\n\tu := r.note(uid).Note\n\tq := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.FID.In(fids...),\n\t\tu.Action.Neq(\"delete\"),\n\t)\n\n\treturn q.Count()\n}\n\n// RecycleClear 清理回收站\nfunc (r *noteRepository) RecycleClear(ctx context.Context, path, pathHash string, vaultID, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.note(uid).Note\n\t\tq := u.WithContext(ctx).Where(u.VaultID.Eq(vaultID), u.Action.Eq(string(domain.NoteActionDelete)), u.Rename.Eq(0))\n\t\tif pathHash != \"\" {\n\t\t\tq = q.Where(u.PathHash.Eq(pathHash))\n\t\t}\n\t\t_, err := q.UpdateSimple(\n\t\t\tu.Rename.Value(2),\n\t\t\tu.UpdatedTimestamp.Value(timex.Now().UnixMilli()),\n\t\t\tu.UpdatedAt.Value(timex.Now()),\n\t\t)\n\t\treturn err\n\t})\n}\n\n// UpdateFID 仅更新笔记的文件夹关联 ID，不更新 updated_timestamp\n// 用于 SyncResourceFID 内部整理，避免污染增量同步时间戳\n// Only updates the folder ID (FID) without touching updated_timestamp\n// Used by SyncResourceFID to avoid polluting incremental sync timestamps\nfunc (r *noteRepository) UpdateFID(ctx context.Context, id, fid, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.note(uid).Note\n\t\t_, err := u.WithContext(ctx).Where(u.ID.Eq(id)).UpdateSimple(u.FID.Value(fid))\n\t\treturn err\n\t})\n}\n\n// 确保 noteRepository 实现了 domain.NoteRepository 接口\nvar _ domain.NoteRepository = (*noteRepository)(nil)\n\n// upsertFTS 更新 FTS 索引\nfunc (r *noteRepository) upsertFTS(db *gorm.DB, noteID int64, path, content string) {\n\t// 1. 更新快照表\n\tdb.Save(&model.NoteFTS{NoteID: noteID, Path: path, Content: content})\n\n\t// 2. 更新倒排索引\n\tdb.Where(\"note_id = ?\", noteID).Delete(&model.NoteFTSToken{})\n\ttokens := util.Tokenize(path + \" \" + content)\n\tif len(tokens) == 0 {\n\t\treturn\n\t}\n\tvar tokenModels []model.NoteFTSToken\n\tfor _, t := range tokens {\n\t\ttokenModels = append(tokenModels, model.NoteFTSToken{NoteID: noteID, Token: t})\n\t}\n\tdb.CreateInBatches(tokenModels, 500)\n}\n\n// deleteFTS 删除 FTS 索引\nfunc (r *noteRepository) deleteFTS(db *gorm.DB, noteID int64) {\n\tdb.Where(\"note_id = ?\", noteID).Delete(&model.NoteFTS{})\n\tdb.Where(\"note_id = ?\", noteID).Delete(&model.NoteFTSToken{})\n}\n\n// searchFTS 使用倒排索引搜索内容，返回匹配的 note_id 列表\nfunc (r *noteRepository) searchFTS(db *gorm.DB, keyword string, vaultID int64, isRecycle bool, sortBy, sortOrder string, limit, offset int) ([]int64, error) {\n\ttokens := util.Tokenize(keyword)\n\tif len(tokens) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tvar noteIDs []int64\n\n\t// 构建 action 条件\n\tactionCond := \"note.action != 'delete'\"\n\tif isRecycle {\n\t\tactionCond = \"note.action = 'delete' AND note.rename = 0\"\n\t}\n\n\t// 映射排序字段\n\tsortField := getSortField(sortBy)\n\n\t// 构建排序语句\n\torderClause := \"note.\" + sortField + \" \" + sortOrder\n\n\t// 使用新的倒排索引查询\n\tquery := db.Table(\"note_fts_token AS t\").\n\t\tSelect(\"t.note_id\").\n\t\tJoins(\"INNER JOIN note ON t.note_id = note.id\").\n\t\tWhere(\"t.token IN ?\", tokens).\n\t\tWhere(\"note.vault_id = ?\", vaultID).\n\t\tWhere(actionCond).\n\t\tGroup(\"t.note_id, note.\" + sortField).\n\t\tOrder(orderClause).\n\t\tLimit(limit).\n\t\tOffset(offset)\n\n\terr := query.Scan(&noteIDs).Error\n\treturn noteIDs, err\n}\n\n// searchFTSCount 使用倒排索引搜索计数\nfunc (r *noteRepository) searchFTSCount(db *gorm.DB, keyword string, vaultID int64, isRecycle bool) (int64, error) {\n\ttokens := util.Tokenize(keyword)\n\tif len(tokens) == 0 {\n\t\treturn 0, nil\n\t}\n\n\tvar count int64\n\tactionCond := \"note.action != 'delete'\"\n\tif isRecycle {\n\t\tactionCond = \"note.action = 'delete' AND note.rename = 0\"\n\t}\n\n\tsubQuery := db.Table(\"note_fts_token AS t\").\n\t\tSelect(\"t.note_id\").\n\t\tJoins(\"INNER JOIN note ON t.note_id = note.id\").\n\t\tWhere(\"t.token IN ?\", tokens).\n\t\tWhere(\"note.vault_id = ?\", vaultID).\n\t\tWhere(actionCond).\n\t\tGroup(\"t.note_id\").\n\t\tHaving(\"COUNT(DISTINCT t.token) = ?\", len(tokens))\n\n\terr := db.Table(\"(?) AS sub\", subQuery).Count(&count).Error\n\treturn count, err\n}\n\n// DeleteByVaultID physically deletes all notes in a vault\n// DeleteByVaultID 物理删除仓库下的所有笔记\nfunc (r *noteRepository) DeleteByVaultID(ctx context.Context, vaultID, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.note(uid).Note\n\n\t\t// 查找该仓库下的所有笔记 ID\n\t\tnotes, err := u.WithContext(ctx).Where(u.VaultID.Eq(vaultID)).Select(u.ID).Find()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif len(notes) == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\tvar ids []int64\n\t\tfor _, n := range notes {\n\t\t\tids = append(ids, n.ID)\n\t\t}\n\n\t\t// 从数据库删除\n\t\t_, err = u.WithContext(ctx).Where(u.VaultID.Eq(vaultID)).Delete()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// 删除物理文件夹\n\t\tfor _, id := range ids {\n\t\t\tfolder := r.dao.GetNoteFolderPath(uid, id)\n\t\t\t_ = r.dao.RemoveContentFolder(folder)\n\t\t}\n\n\t\treturn nil\n\t})\n}\n\n// Ensure noteRepository implements domain.NoteRepository interface\n// 确保 noteRepository 实现了 domain.NoteRepository 接口\nvar _ domain.NoteRepository = (*noteRepository)(nil)\n"
  },
  {
    "path": "internal/dao/setting_repository.go",
    "content": "// Package dao implements the data access layer\n// Package dao 实现数据访问层\npackage dao\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/query\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/logger\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"go.uber.org/zap\"\n\t\"gorm.io/gorm\"\n)\n\n// settingRepository implements domain.SettingRepository interface\n// settingRepository 实现 domain.SettingRepository 接口\ntype settingRepository struct {\n\tdao             *Dao\n\tcustomPrefixKey string\n}\n\n// NewSettingRepository creates SettingRepository instance\n// NewSettingRepository 创建 SettingRepository 实例\nfunc NewSettingRepository(dao *Dao) domain.SettingRepository {\n\treturn &settingRepository{dao: dao, customPrefixKey: \"user_setting_\"}\n}\n\nfunc (r *settingRepository) GetKey(uid int64) string {\n\treturn r.customPrefixKey + strconv.FormatInt(uid, 10)\n}\n\nfunc init() {\n\tRegisterModel(ModelConfig{\n\t\tName: \"Setting\",\n\t\tRepoFactory: func(d *Dao) daoDBCustomKey {\n\t\t\treturn NewSettingRepository(d).(daoDBCustomKey)\n\t\t},\n\t})\n}\n\n// setting gets the setting query object\n// setting 获取配置查询对象\nfunc (r *settingRepository) setting(uid int64) *query.Query {\n\tkey := r.GetKey(uid)\n\treturn r.dao.QueryWithOnceInit(func(g *gorm.DB) {\n\t\tmodel.AutoMigrate(g, \"Setting\")\n\t}, key+\"#setting\", key)\n}\n\n// toDomain converts database model to domain model\n// toDomain 将数据库模型转换为领域模型\nfunc (r *settingRepository) toDomain(m *model.Setting, uid int64) (*domain.Setting, error) {\n\tif m == nil {\n\t\treturn nil, nil\n\t}\n\ts := &domain.Setting{\n\t\tID:               m.ID,\n\t\tVaultID:          m.VaultID,\n\t\tAction:           domain.SettingAction(m.Action),\n\t\tPath:             m.Path,\n\t\tPathHash:         m.PathHash,\n\t\tContent:          m.Content,\n\t\tContentHash:      m.ContentHash,\n\t\tSize:             m.Size,\n\t\tRename:           m.Rename,\n\t\tCtime:            m.Ctime,\n\t\tMtime:            m.Mtime,\n\t\tUpdatedTimestamp: m.UpdatedTimestamp,\n\t\tCreatedAt:        time.Time(m.CreatedAt),\n\t\tUpdatedAt:        time.Time(m.UpdatedAt),\n\t}\n\tif err := r.fillSettingContent(uid, s); err != nil {\n\t\treturn nil, err\n\t}\n\treturn s, nil\n}\n\n// fillSettingContent fills setting content\n// fillSettingContent 填充配置内容\nfunc (r *settingRepository) fillSettingContent(uid int64, s *domain.Setting) error {\n\tif s == nil {\n\t\treturn nil\n\t}\n\tfolder := r.dao.GetSettingFolderPath(uid, s.ID)\n\n\tcontent, exists, err := r.dao.LoadContentFromFile(folder, \"content.txt\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif exists {\n\t\ts.Content = content\n\t} else if s.Content != \"\" {\n\t\tif err := r.dao.SaveContentToFile(folder, \"content.txt\", s.Content); err != nil {\n\t\t\tr.dao.Logger().Warn(\"lazy migration: SaveContentToFile failed for setting content\",\n\t\t\t\tzap.Int64(logger.FieldUID, uid),\n\t\t\t\tzap.Int64(\"settingId\", s.ID),\n\t\t\t\tzap.String(logger.FieldMethod, \"settingRepository.fillSettingContent\"),\n\t\t\t\tzap.Error(err),\n\t\t\t)\n\t\t}\n\t} else {\n\t\treturn fmt.Errorf(\"setting content file not found: %w\", os.ErrNotExist)\n\t}\n\treturn nil\n}\n\n// GetByPathHash retrieves setting by path hash\n// GetByPathHash 根据路径哈希获取配置\nfunc (r *settingRepository) GetByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) (*domain.Setting, error) {\n\tu := r.setting(uid).Setting\n\tm, err := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.PathHash.Eq(pathHash),\n\t).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m, uid)\n}\n\n// ListByPathHash retrieves setting list by path hash (handles duplicate records)\n// ListByPathHash 根据路径哈希获取配置列表（处理重复记录）\nfunc (r *settingRepository) ListByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) ([]*domain.Setting, error) {\n\tu := r.setting(uid).Setting\n\tmList, err := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.PathHash.Eq(pathHash),\n\t).Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar results []*domain.Setting\n\tfor _, m := range mList {\n\t\ts, err := r.toDomain(m, uid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, s)\n\t}\n\treturn results, nil\n}\n\n// Create creates a setting\n// Create 创建配置\nfunc (r *settingRepository) Create(ctx context.Context, setting *domain.Setting, uid int64) (*domain.Setting, error) {\n\tvar result *domain.Setting\n\tvar createErr error\n\n\terr := r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.setting(uid).Setting\n\t\tm := &model.Setting{\n\t\t\tVaultID:          setting.VaultID,\n\t\t\tAction:           string(setting.Action),\n\t\t\tPath:             setting.Path,\n\t\t\tPathHash:         setting.PathHash,\n\t\t\tContentHash:      setting.ContentHash,\n\t\t\tSize:             setting.Size,\n\t\t\tRename:           setting.Rename,\n\t\t\tCtime:            setting.Ctime,\n\t\t\tMtime:            setting.Mtime,\n\t\t\tUpdatedTimestamp: timex.Now().UnixMilli(),\n\t\t\tCreatedAt:        timex.Now(),\n\t\t\tUpdatedAt:        timex.Now(),\n\t\t}\n\n\t\tcontent := setting.Content\n\t\tm.Content = \"\" // Do not store content in database // 不在数据库存储内容\n\n\t\tcreateErr = u.WithContext(ctx).Create(m)\n\t\tif createErr != nil {\n\t\t\treturn createErr\n\t\t}\n\n\t\t// 保存到文件存储\n\t\tfolderPath := r.dao.GetSettingFolderPath(uid, m.ID)\n\t\tif err := r.dao.SaveContentToFile(folderPath, \"content.txt\", content); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tsRes, err := r.toDomain(m, uid)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresult = sRes\n\n\t\tresult.Content = content\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn result, nil\n}\n\n// Update updates the setting\n// Update 更新配置\nfunc (r *settingRepository) Update(ctx context.Context, setting *domain.Setting, uid int64) (*domain.Setting, error) {\n\tvar result *domain.Setting\n\tvar updateErr error\n\n\terr := r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.setting(uid).Setting\n\t\tm := &model.Setting{\n\t\t\tID:               setting.ID,\n\t\t\tVaultID:          setting.VaultID,\n\t\t\tAction:           string(setting.Action),\n\t\t\tPath:             setting.Path,\n\t\t\tPathHash:         setting.PathHash,\n\t\t\tContentHash:      setting.ContentHash,\n\t\t\tSize:             setting.Size,\n\t\t\tRename:           setting.Rename,\n\t\t\tCtime:            setting.Ctime,\n\t\t\tMtime:            setting.Mtime,\n\t\t\tUpdatedTimestamp: timex.Now().UnixMilli(),\n\t\t\tUpdatedAt:        timex.Now(),\n\t\t}\n\n\t\tcontent := setting.Content\n\t\tm.Content = \"\" // Do not update content in database // 不在数据库更新内容\n\n\t\tupdateErr = u.WithContext(ctx).Where(u.ID.Eq(setting.ID)).Save(m)\n\t\tif updateErr != nil {\n\t\t\treturn updateErr\n\t\t}\n\n\t\t// 保存到文件存储\n\t\tfolderPath := r.dao.GetSettingFolderPath(uid, setting.ID)\n\t\tif err := r.dao.SaveContentToFile(folderPath, \"content.txt\", content); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tsRes, err := r.toDomain(m, uid)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresult = sRes\n\n\t\tresult.Content = content\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn result, nil\n}\n\n// UpdateMtime updates the setting modification time\n// UpdateMtime 更新配置修改时间\nfunc (r *settingRepository) UpdateMtime(ctx context.Context, mtime int64, id, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.setting(uid).Setting\n\t\t_, err := u.WithContext(ctx).Where(u.ID.Eq(id)).UpdateSimple(\n\t\t\tu.Mtime.Value(mtime),\n\t\t\tu.UpdatedTimestamp.Value(timex.Now().UnixMilli()),\n\t\t\tu.UpdatedAt.Value(timex.Now()),\n\t\t)\n\t\treturn err\n\t})\n}\n\n// UpdateActionMtime updates setting type and modification time\n// UpdateActionMtime 更新配置类型并修改时间\nfunc (r *settingRepository) UpdateActionMtime(ctx context.Context, action domain.SettingAction, mtime int64, id, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.setting(uid).Setting\n\n\t\t_, err := u.WithContext(ctx).Where(\n\t\t\tu.ID.Eq(id),\n\t\t).UpdateSimple(\n\t\t\tu.Action.Value(string(action)),\n\t\t\tu.Mtime.Value(mtime),\n\t\t\tu.UpdatedTimestamp.Value(timex.Now().UnixMilli()),\n\t\t\tu.UpdatedAt.Value(timex.Now()),\n\t\t)\n\t\treturn err\n\t})\n}\n\n// Delete physically deletes the setting\n// Delete 物理删除配置\nfunc (r *settingRepository) Delete(ctx context.Context, id, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.setting(uid).Setting\n\t\t_, err := u.WithContext(ctx).Where(u.ID.Eq(id)).Delete()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Delete physical files\n\t\t// 删除物理文件\n\t\tfolder := r.dao.GetSettingFolderPath(uid, id)\n\t\t_ = r.dao.RemoveContentFolder(folder)\n\n\t\treturn nil\n\t})\n}\n\n// DeletePhysicalByTime physically deletes settings marked for deletion based on time\n// DeletePhysicalByTime 根据时间物理删除已标记删除的配置\nfunc (r *settingRepository) DeletePhysicalByTime(ctx context.Context, timestamp, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.setting(uid).Setting\n\n\t\t// Find records to be physically deleted, clean up files\n\t\t// 查找待物理删除的记录，清理文件\n\t\tmList, err := u.WithContext(ctx).Where(\n\t\t\tu.Action.Eq(\"delete\"),\n\t\t\tu.UpdatedTimestamp.Lt(timestamp),\n\t\t).Find()\n\n\t\tif err == nil {\n\t\t\tfor _, m := range mList {\n\t\t\t\tfolder := r.dao.GetSettingFolderPath(uid, m.ID)\n\t\t\t\t_ = r.dao.RemoveContentFolder(folder)\n\t\t\t}\n\t\t}\n\n\t\t_, err = u.WithContext(ctx).Where(\n\t\t\tu.Action.Eq(\"delete\"),\n\t\t\tu.UpdatedTimestamp.Lt(timestamp),\n\t\t).Delete()\n\n\t\treturn err\n\t})\n}\n\n// DeletePhysicalByTimeAll physically deletes settings marked for deletion for all users based on time\n// DeletePhysicalByTimeAll 根据时间物理删除所有用户的已标记删除的配置\nfunc (r *settingRepository) DeletePhysicalByTimeAll(ctx context.Context, timestamp int64) error {\n\tuids, err := r.dao.GetAllUserUIDs()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, uid := range uids {\n\t\tif err := r.DeletePhysicalByTime(ctx, timestamp, uid); err != nil {\n\t\t\tcontinue\n\t\t}\n\t}\n\treturn nil\n}\n\n// List gets the setting list with pagination\n// List 分页获取配置列表\nfunc (r *settingRepository) List(ctx context.Context, vaultID int64, page, pageSize int, uid int64, keyword string) ([]*domain.Setting, error) {\n\tu := r.setting(uid).Setting\n\tdb := u.WithContext(ctx).Where(u.VaultID.Eq(vaultID), u.Action.Neq(\"delete\"))\n\tif keyword != \"\" {\n\t\tdb = db.Where(u.Path.Like(\"%\" + keyword + \"%\"))\n\t}\n\n\toffset := (page - 1) * pageSize\n\tmList, err := db.Order(u.UpdatedAt.Desc()).Offset(offset).Limit(pageSize).Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar results []*domain.Setting\n\tfor _, m := range mList {\n\t\ts, err := r.toDomain(m, uid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, s)\n\t}\n\treturn results, nil\n}\n\n// ListCount gets the setting count\n// ListCount 获取配置数量\nfunc (r *settingRepository) ListCount(ctx context.Context, vaultID, uid int64, keyword string) (int64, error) {\n\tu := r.setting(uid).Setting\n\tdb := u.WithContext(ctx).Where(u.VaultID.Eq(vaultID), u.Action.Neq(\"delete\"))\n\tif keyword != \"\" {\n\t\tdb = db.Where(u.Path.Like(\"%\" + keyword + \"%\"))\n\t}\n\treturn db.Count()\n}\n\n// ListByUpdatedTimestamp gets the setting list based on updated timestamp\n// ListByUpdatedTimestamp 根据更新时间戳获取配置列表\nfunc (r *settingRepository) ListByUpdatedTimestamp(ctx context.Context, timestamp, vaultID, uid int64) ([]*domain.Setting, error) {\n\tu := r.setting(uid).Setting\n\tmList, err := u.WithContext(ctx).Where(\n\t\tu.VaultID.Eq(vaultID),\n\t\tu.UpdatedTimestamp.Gt(timestamp),\n\t).Order(u.UpdatedTimestamp.Desc()).Find()\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar results []*domain.Setting\n\tfor _, m := range mList {\n\t\ts, err := r.toDomain(m, uid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, s)\n\t}\n\treturn results, nil\n}\n\n// DeleteByVaultID physically deletes all settings of the specified notebook for this user\n// DeleteByVaultID 物理删除该用户指定笔记本的所有配置\nfunc (r *settingRepository) DeleteByVaultID(ctx context.Context, vaultID, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.setting(uid).Setting\n\n\t\t// 1. Find all record IDs under the Vault to clean up files\n\t\t// 1. 查找该 Vault 下的所有记录 ID，以便清理文件\n\t\tmList, err := u.WithContext(ctx).Where(u.VaultID.Eq(vaultID)).Select(u.ID).Find()\n\t\tif err == nil {\n\t\t\tfor _, m := range mList {\n\t\t\t\tfolder := r.dao.GetSettingFolderPath(uid, m.ID)\n\t\t\t\t_ = r.dao.RemoveContentFolder(folder)\n\t\t\t}\n\t\t}\n\n\t\t// 2. Physically delete database records\n\t\t// 2. 物理删除数据库记录\n\t\t_, err = u.WithContext(ctx).Where(u.VaultID.Eq(vaultID)).Delete()\n\t\treturn err\n\t})\n}\n\n// Ensure settingRepository implements domain.SettingRepository interface\n// 确保 settingRepository 实现了 domain.SettingRepository 接口\nvar _ domain.SettingRepository = (*settingRepository)(nil)\n"
  },
  {
    "path": "internal/dao/storage_repository.go",
    "content": "package dao\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/query\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"gorm.io/gorm\"\n)\n\n// storageRepository implements domain.StorageRepository interface\n// storageRepository 实现 domain.StorageRepository 接口\ntype storageRepository struct {\n\tdao *Dao\n}\n\n// NewStorageRepository creates StorageRepository instance\n// NewStorageRepository 创建 StorageRepository 实例\nfunc NewStorageRepository(dao *Dao) domain.StorageRepository {\n\treturn &storageRepository{dao: dao}\n}\n\nfunc (r *storageRepository) GetKey(uid int64) string {\n\treturn \"user_storage_\" + strconv.FormatInt(uid, 10)\n}\n\nfunc init() {\n\tRegisterModel(ModelConfig{\n\t\tName: \"Storage\",\n\t\tRepoFactory: func(d *Dao) daoDBCustomKey {\n\t\t\treturn NewStorageRepository(d).(daoDBCustomKey)\n\t\t},\n\t})\n}\n\n// storage gets the storage configuration query object\n// storage 获取存储配置查询对象\nfunc (r *storageRepository) storage(uid int64) *query.Query {\n\treturn r.dao.QueryWithOnceInit(func(g *gorm.DB) {\n\t\tmodel.AutoMigrate(g, \"Storage\")\n\t}, r.GetKey(uid)+\"#storage\", r.GetKey(uid))\n}\n\n// toDomain converts database model to domain model\n// toDomain 将数据库模型转换为领域模型\nfunc (r *storageRepository) toDomain(m *model.Storage) *domain.Storage {\n\tif m == nil {\n\t\treturn nil\n\t}\n\treturn &domain.Storage{\n\t\tID:              m.ID,\n\t\tUID:             m.UID,\n\t\tType:            m.Type,\n\t\tEndpoint:        m.Endpoint,\n\t\tRegion:          m.Region,\n\t\tAccountID:       m.AccountID,\n\t\tBucketName:      m.BucketName,\n\t\tAccessKeyID:     m.AccessKeyID,\n\t\tAccessKeySecret: m.AccessKeySecret,\n\t\tCustomPath:      m.CustomPath,\n\t\tAccessURLPrefix: m.AccessURLPrefix,\n\t\tUser:            m.User,\n\t\tPassword:        m.Password,\n\t\tIsEnabled:       m.IsEnabled == 1,\n\t\tIsDeleted:       m.IsDeleted == 1,\n\t\tCreatedAt:       time.Time(m.CreatedAt),\n\t\tUpdatedAt:       time.Time(m.UpdatedAt),\n\t}\n}\n\n// toModel converts domain model to database model\n// toModel 将领域模型转换为数据库模型\nfunc (r *storageRepository) toModel(s *domain.Storage) *model.Storage {\n\tif s == nil {\n\t\treturn nil\n\t}\n\tisDeleted := int64(0)\n\tif s.IsDeleted {\n\t\tisDeleted = 1\n\t}\n\tmodelStorage := &model.Storage{\n\t\tID:              s.ID,\n\t\tUID:             s.UID,\n\t\tType:            s.Type,\n\t\tEndpoint:        s.Endpoint,\n\t\tRegion:          s.Region,\n\t\tAccountID:       s.AccountID,\n\t\tBucketName:      s.BucketName,\n\t\tAccessKeyID:     s.AccessKeyID,\n\t\tAccessKeySecret: s.AccessKeySecret,\n\t\tCustomPath:      s.CustomPath,\n\t\tAccessURLPrefix: s.AccessURLPrefix,\n\t\tUser:            s.User,\n\t\tPassword:        s.Password,\n\t\tIsEnabled:       int64(0),\n\t\tIsDeleted:       isDeleted,\n\t\tCreatedAt:       timex.Time(s.CreatedAt),\n\t\tUpdatedAt:       timex.Time(s.UpdatedAt),\n\t}\n\n\tif s.IsEnabled {\n\t\tmodelStorage.IsEnabled = 1\n\t}\n\treturn modelStorage\n}\n\n// GetByID retrieves storage configuration by ID\n// GetByID 根据ID获取存储配置\nfunc (r *storageRepository) GetByID(ctx context.Context, id, uid int64) (*domain.Storage, error) {\n\tu := r.storage(uid).Storage\n\tm, err := u.WithContext(ctx).Where(u.ID.Eq(id), u.UID.Eq(uid), u.IsDeleted.Eq(0)).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m), nil\n}\n\n// Create creates storage configuration\n// Create 创建存储配置\nfunc (r *storageRepository) Create(ctx context.Context, storage *domain.Storage, uid int64) (*domain.Storage, error) {\n\tvar result *domain.Storage\n\tvar createErr error\n\n\terr := r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.storage(uid).Storage\n\t\tm := r.toModel(storage)\n\t\tm.UID = uid\n\t\tm.IsDeleted = 0\n\t\tm.CreatedAt = timex.Now()\n\t\tm.UpdatedAt = timex.Now()\n\n\t\tcreateErr = u.WithContext(ctx).Create(m)\n\t\tif createErr != nil {\n\t\t\treturn createErr\n\t\t}\n\t\tresult = r.toDomain(m)\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn result, createErr\n}\n\n// Update updates storage configuration\n// Update 更新存储配置\nfunc (r *storageRepository) Update(ctx context.Context, storage *domain.Storage, uid int64) (*domain.Storage, error) {\n\tvar result *domain.Storage\n\tvar updateErr error\n\n\terr := r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.storage(uid).Storage\n\n\t\t// Get original record to confirm ownership\n\t\t// 获取原有记录确认归属\n\t\told, err := u.WithContext(ctx).Where(u.ID.Eq(storage.ID), u.UID.Eq(uid), u.IsDeleted.Eq(0)).First()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tm := r.toModel(storage)\n\t\tm.UID = uid\n\t\tm.CreatedAt = old.CreatedAt\n\t\tm.UpdatedAt = timex.Now()\n\n\t\tupdateErr = u.WithContext(ctx).Where(u.ID.Eq(storage.ID)).Save(m)\n\t\tif updateErr != nil {\n\t\t\treturn updateErr\n\t\t}\n\t\tresult = r.toDomain(m)\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn result, updateErr\n}\n\n// List retrieves the user's storage configuration list\n// List 获取用户的存储配置列表\nfunc (r *storageRepository) List(ctx context.Context, uid int64) ([]*domain.Storage, error) {\n\tu := r.storage(uid).Storage\n\tmodelList, err := u.WithContext(ctx).Where(u.UID.Eq(uid), u.IsDeleted.Eq(0)).Order(u.ID.Desc()).Find()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar list []*domain.Storage\n\tfor _, m := range modelList {\n\t\tlist = append(list, r.toDomain(m))\n\t}\n\treturn list, nil\n}\n\n// Delete deletes storage configuration (soft delete)\n// Delete 删除存储配置（软删除）\nfunc (r *storageRepository) Delete(ctx context.Context, id, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := r.storage(uid).Storage\n\t\t_, err := u.WithContext(ctx).Where(u.ID.Eq(id), u.UID.Eq(uid)).UpdateSimple(u.IsDeleted.Value(1), u.DeletedAt.Value(timex.Now()))\n\t\treturn err\n\t})\n}\n\n// Ensure storageRepository implements domain.StorageRepository interface\n// 确保 storageRepository 实现了 domain.StorageRepository 接口\nvar _ domain.StorageRepository = (*storageRepository)(nil)\n"
  },
  {
    "path": "internal/dao/sync_log_repository.go",
    "content": "// Package dao implements the data access layer\n// Package dao 实现数据访问层\npackage dao\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"gorm.io/gorm\"\n)\n\n// syncLogRepository implements domain.SyncLogRepository\n// syncLogRepository 实现 domain.SyncLogRepository 接口\ntype syncLogRepository struct {\n\tdao             *Dao\n\tcustomPrefixKey string\n\tmigrateOnce     sync.Map // tracks per-key migration completion // 记录每个 key 是否已完成 AutoMigrate\n}\n\n// NewSyncLogRepository creates a SyncLogRepository instance\n// NewSyncLogRepository 创建 SyncLogRepository 实例\nfunc NewSyncLogRepository(dao *Dao) domain.SyncLogRepository {\n\treturn &syncLogRepository{dao: dao, customPrefixKey: \"user_sync_log_\"}\n}\n\n// GetKey returns the database routing key for the given user\n// GetKey 返回指定用户的数据库路由键（写入用户库）\nfunc (r *syncLogRepository) GetKey(uid int64) string {\n\treturn r.customPrefixKey + strconv.FormatInt(uid, 10)\n}\n\nfunc init() {\n\tRegisterModel(ModelConfig{\n\t\tName: \"SyncLog\",\n\t\tRepoFactory: func(d *Dao) daoDBCustomKey {\n\t\t\treturn NewSyncLogRepository(d).(daoDBCustomKey)\n\t\t},\n\t\tIsMainDB: false,\n\t})\n}\n\n// db returns the *gorm.DB for sync_log in the user's database, with one-time AutoMigrate\n// db 返回用户库中 sync_log 对应的 *gorm.DB，确保每个用户库只迁移一次\nfunc (r *syncLogRepository) db(uid int64) *gorm.DB {\n\tkey := r.GetKey(uid)\n\tonceKey := key + \"#syncLog\"\n\tif _, loaded := r.migrateOnce.LoadOrStore(onceKey, true); !loaded {\n\t\tdb := r.dao.ResolveDB(key)\n\t\tif db != nil {\n\t\t\tmodel.AutoMigrate(db, \"SyncLog\")\n\t\t}\n\t}\n\treturn r.dao.ResolveDB(key)\n}\n\n// Create stores a new sync log entry\n// Create 存储一条新的同步日志\nfunc (r *syncLogRepository) Create(ctx context.Context, log *domain.SyncLog, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tm := &model.SyncLog{\n\t\t\tUID:           log.UID,\n\t\t\tVaultID:       log.VaultID,\n\t\t\tType:          string(log.Type),\n\t\t\tAction:        string(log.Action),\n\t\t\tChangedFields: log.ChangedFields,\n\t\t\tPath:          log.Path,\n\t\t\tPathHash:      log.PathHash,\n\t\t\tSize:          log.Size,\n\t\t\tClientName:    log.ClientName,\n\t\t\tClientType:    log.ClientType,\n\t\t\tClientVersion: log.ClientVersion,\n\t\t\tStatus:        log.Status,\n\t\t\tMessage:       log.Message,\n\t\t\tCreatedAt:     log.CreatedAt,\n\t\t}\n\t\tif m.CreatedAt.IsZero() {\n\t\t\tm.CreatedAt = timex.Now()\n\t\t}\n\t\treturn r.db(uid).WithContext(ctx).Create(m).Error\n\t})\n}\n\n// List retrieves sync logs for a user with optional filters and pagination\n// List 按条件分页查询用户的同步日志\nfunc (r *syncLogRepository) List(ctx context.Context, uid int64, logType, action string, page, pageSize int) ([]*domain.SyncLog, int64, error) {\n\tdb := r.db(uid)\n\n\tquery := db.WithContext(ctx).Model(&model.SyncLog{})\n\tif logType != \"\" {\n\t\tquery = query.Where(\"type = ?\", logType)\n\t}\n\tif action != \"\" {\n\t\tquery = query.Where(\"action = ?\", action)\n\t}\n\n\tvar total int64\n\tif err := query.Count(&total).Error; err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tif page < 1 {\n\t\tpage = 1\n\t}\n\tif pageSize < 1 {\n\t\tpageSize = 20\n\t}\n\toffset := (page - 1) * pageSize\n\n\tvar rows []*model.SyncLog\n\tif err := query.Order(\"id DESC\").Offset(offset).Limit(pageSize).Find(&rows).Error; err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tresults := make([]*domain.SyncLog, 0, len(rows))\n\tfor _, m := range rows {\n\t\tresults = append(results, &domain.SyncLog{\n\t\t\tID:            m.ID,\n\t\t\tUID:           m.UID,\n\t\t\tVaultID:       m.VaultID,\n\t\t\tType:          domain.SyncLogType(m.Type),\n\t\t\tAction:        domain.SyncLogAction(m.Action),\n\t\t\tChangedFields: m.ChangedFields,\n\t\t\tPath:          m.Path,\n\t\t\tPathHash:      m.PathHash,\n\t\t\tSize:          m.Size,\n\t\t\tClientName:    m.ClientName,\n\t\t\tClientType:    m.ClientType,\n\t\t\tClientVersion: m.ClientVersion,\n\t\t\tStatus:        m.Status,\n\t\t\tMessage:       m.Message,\n\t\t\tCreatedAt:     m.CreatedAt,\n\t\t})\n\t}\n\treturn results, total, nil\n}\n\n// CleanupByTime removes sync logs older than the given timestamp for a specific user\n// CleanupByTime 清理指定用户在指定时间戳之前的同步日志\nfunc (r *syncLogRepository) CleanupByTime(ctx context.Context, timestamp int64, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\treturn r.db(uid).WithContext(ctx).Where(\"created_at < ?\", time.UnixMilli(timestamp)).Delete(&model.SyncLog{}).Error\n\t})\n}\n\n// CleanupByTimeAll removes sync logs older than the given timestamp for all users\n// CleanupByTimeAll 清理所有用户在指定时间戳之前的同步日志\nfunc (r *syncLogRepository) CleanupByTimeAll(ctx context.Context, timestamp int64) error {\n\tuids, err := r.dao.GetAllUserUIDs()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor i, uid := range uids {\n\t\tif i > 0 {\n\t\t\ttime.Sleep(100 * time.Millisecond) // Slight delay to reduce bursts\n\t\t}\n\t\tif err := r.CleanupByTime(ctx, timestamp, uid); err != nil {\n\t\t\tcontinue // Continue with other users even if one fails\n\t\t}\n\t}\n\treturn nil\n}\n\n// DeleteByVaultID removes all sync logs for a specific vault\n// DeleteByVaultID 删除指定仓库的所有同步日志\nfunc (r *syncLogRepository) DeleteByVaultID(ctx context.Context, vaultID, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\treturn r.db(uid).WithContext(ctx).Where(\"vault_id = ?\", vaultID).Delete(&model.SyncLog{}).Error\n\t})\n}\n\n// Ensure syncLogRepository implements domain.SyncLogRepository\n// 确保 syncLogRepository 实现了 domain.SyncLogRepository 接口\nvar _ domain.SyncLogRepository = (*syncLogRepository)(nil)\n"
  },
  {
    "path": "internal/dao/user_repository.go",
    "content": "// Package dao implements the data access layer\n// Package dao 实现数据访问层\npackage dao\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/query\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"gorm.io/gorm\"\n)\n\n// userRepository implements domain.UserRepository interface\n// userRepository 实现 domain.UserRepository 接口\ntype userRepository struct {\n\tdao *Dao\n}\n\n// NewUserRepository creates UserRepository instance\n// NewUserRepository 创建 UserRepository 实例\nfunc NewUserRepository(dao *Dao) domain.UserRepository {\n\treturn &userRepository{dao: dao}\n}\n\nfunc (r *userRepository) GetKey(uid int64) string {\n\treturn \"\"\n}\n\nfunc init() {\n\tRegisterModel(ModelConfig{\n\t\tName:     \"User\",\n\t\tIsMainDB: true,\n\t})\n}\n\n// user gets the user query object\n// user 获取用户查询对象\nfunc (r *userRepository) user() *query.Query {\n\treturn r.dao.QueryWithOnceInit(func(g *gorm.DB) {\n\t\tmodel.AutoMigrate(g, \"User\")\n\t}, \"user#user\")\n}\n\n// toDomain converts database model to domain model\n// toDomain 将数据库模型转换为领域模型\nfunc (r *userRepository) toDomain(m *model.User) *domain.User {\n\tif m == nil {\n\t\treturn nil\n\t}\n\treturn &domain.User{\n\t\tUID:       m.UID,\n\t\tEmail:     m.Email,\n\t\tUsername:  m.Username,\n\t\tPassword:  m.Password,\n\t\tSalt:      m.Salt,\n\t\tToken:     m.Token,\n\t\tAvatar:    m.Avatar,\n\t\tIsDeleted: m.IsDeleted == 1,\n\t\tCreatedAt: time.Time(m.CreatedAt),\n\t\tUpdatedAt: time.Time(m.UpdatedAt),\n\t\tDeletedAt: time.Time(m.DeletedAt),\n\t}\n}\n\n// toModel converts domain model to database model\n// toModel 将领域模型转换为数据库模型\nfunc (r *userRepository) toModel(user *domain.User) *model.User {\n\tif user == nil {\n\t\treturn nil\n\t}\n\tisDeleted := int64(0)\n\tif user.IsDeleted {\n\t\tisDeleted = 1\n\t}\n\treturn &model.User{\n\t\tUID:       user.UID,\n\t\tEmail:     user.Email,\n\t\tUsername:  user.Username,\n\t\tPassword:  user.Password,\n\t\tSalt:      user.Salt,\n\t\tToken:     user.Token,\n\t\tAvatar:    user.Avatar,\n\t\tIsDeleted: isDeleted,\n\t\tCreatedAt: timex.Time(user.CreatedAt),\n\t\tUpdatedAt: timex.Time(user.UpdatedAt),\n\t\tDeletedAt: timex.Time(user.DeletedAt),\n\t}\n}\n\n// GetByUID retrieves user by UID\n// GetByUID 根据UID获取用户\nfunc (r *userRepository) GetByUID(ctx context.Context, uid int64) (*domain.User, error) {\n\tu := r.user().User\n\tm, err := u.WithContext(ctx).Where(u.UID.Eq(uid), u.IsDeleted.Eq(0)).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m), nil\n}\n\n// GetByEmail retrieves user by email\n// GetByEmail 根据邮箱获取用户\nfunc (r *userRepository) GetByEmail(ctx context.Context, email string) (*domain.User, error) {\n\tu := r.user().User\n\tm, err := u.WithContext(ctx).Where(u.Email.Eq(email), u.IsDeleted.Eq(0)).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m), nil\n}\n\n// GetByUsername retrieves user by username\n// GetByUsername 根据用户名获取用户\nfunc (r *userRepository) GetByUsername(ctx context.Context, username string) (*domain.User, error) {\n\tu := r.user().User\n\tm, err := u.WithContext(ctx).Where(u.Username.Eq(username), u.IsDeleted.Eq(0)).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m), nil\n}\n\n// Create creates a user\n// Create 创建用户\nfunc (r *userRepository) Create(ctx context.Context, user *domain.User) (*domain.User, error) {\n\tu := r.user().User\n\tm := r.toModel(user)\n\tm.CreatedAt = timex.Now()\n\tm.UpdatedAt = timex.Now()\n\n\terr := u.WithContext(ctx).Create(m)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m), nil\n}\n\n// UpdatePassword updates user password\n// UpdatePassword 更新用户密码\nfunc (r *userRepository) UpdatePassword(ctx context.Context, password string, uid int64) error {\n\tu := r.user().User\n\n\t_, err := u.WithContext(ctx).Where(\n\t\tu.UID.Eq(uid),\n\t).UpdateSimple(\n\t\tu.Password.Value(password),\n\t\tu.UpdatedAt.Value(timex.Now()),\n\t)\n\treturn err\n}\n\n// GetAllUIDs retrieves all user UIDs\n// GetAllUIDs 获取所有用户UID\nfunc (r *userRepository) GetAllUIDs(ctx context.Context) ([]int64, error) {\n\tvar uids []int64\n\tu := r.user().User\n\terr := u.WithContext(ctx).Select(u.UID).Where(u.IsDeleted.Eq(0)).Scan(&uids)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn uids, nil\n}\n\n// Ensure userRepository implements domain.UserRepository interface\n// 确保 userRepository 实现了 domain.UserRepository 接口\nvar _ domain.UserRepository = (*userRepository)(nil)\n"
  },
  {
    "path": "internal/dao/user_share_repository.go",
    "content": "package dao\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/query\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"gorm.io/gorm\"\n)\n\n// userShareRepository implements domain.UserShareRepository interface\n// userShareRepository 实现 domain.UserShareRepository 接口\ntype userShareRepository struct {\n\tdao             *Dao\n\tcustomPrefixKey string\n}\n\n// NewUserShareRepository creates UserShareRepository instance\n// NewUserShareRepository 创建 UserShareRepository 实例\nfunc NewUserShareRepository(dao *Dao) domain.UserShareRepository {\n\treturn &userShareRepository{dao: dao, customPrefixKey: \"user_share_\"}\n}\n\nfunc (r *userShareRepository) GetKey(uid int64) string {\n\treturn r.customPrefixKey + strconv.FormatInt(uid, 10)\n}\n\nfunc init() {\n\tRegisterModel(ModelConfig{\n\t\tName: \"UserShare\",\n\t\tRepoFactory: func(d *Dao) daoDBCustomKey {\n\t\t\treturn NewUserShareRepository(d).(daoDBCustomKey)\n\t\t},\n\t})\n}\n\n// userShare gets the share query object\n// userShare 获取分享查询对象\nfunc (r *userShareRepository) userShare(uid int64) *query.Query {\n\tkey := r.GetKey(uid)\n\treturn r.dao.QueryWithOnceInit(func(g *gorm.DB) {\n\t\tmodel.AutoMigrate(g, \"UserShare\")\n\t}, key+\"#userShare\", key)\n}\n\n// toDomain converts database model to domain model\n// toDomain 将数据库模型转换为领域模型\nfunc (r *userShareRepository) toDomain(m *model.UserShare) *domain.UserShare {\n\tif m == nil {\n\t\treturn nil\n\t}\n\tvar res map[string][]string\n\t_ = json.Unmarshal([]byte(m.Res), &res)\n\n\treturn &domain.UserShare{\n\t\tID:           m.ID,\n\t\tUID:          m.UID,\n\t\tResType:      m.ResType,\n\t\tResID:        m.ResID,\n\t\tResources:    res,\n\t\tStatus:       m.Status,\n\t\tViewCount:    m.ViewCount,\n\t\tLastViewedAt: m.LastViewedAt,\n\t\tExpiresAt:    m.ExpiresAt,\n\t\tPassword:     m.Password,\n\t\tShortLink:    m.ShortLink,\n\t\tCreatedAt:    time.Time(m.CreatedAt),\n\t\tUpdatedAt:    time.Time(m.UpdatedAt),\n\t}\n}\n\n// toModel converts domain model to database model\n// toModel 将领域模型转换为数据库模型\nfunc (r *userShareRepository) toModel(d *domain.UserShare) *model.UserShare {\n\tif d == nil {\n\t\treturn nil\n\t}\n\tresBytes, _ := json.Marshal(d.Resources)\n\n\treturn &model.UserShare{\n\t\tID:           d.ID,\n\t\tUID:          d.UID,\n\t\tResType:      d.ResType,\n\t\tResID:        d.ResID,\n\t\tRes:          string(resBytes),\n\t\tStatus:       d.Status,\n\t\tViewCount:    d.ViewCount,\n\t\tLastViewedAt: d.LastViewedAt,\n\t\tExpiresAt:    d.ExpiresAt,\n\t\tPassword:     d.Password,\n\t\tShortLink:    d.ShortLink,\n\t\tCreatedAt:    timex.Time(d.CreatedAt),\n\t\tUpdatedAt:    timex.Time(d.UpdatedAt),\n\t}\n}\n\nfunc (r *userShareRepository) Create(ctx context.Context, uid int64, share *domain.UserShare) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tus := r.userShare(uid).UserShare\n\t\tm := r.toModel(share)\n\t\tif err := us.WithContext(ctx).Create(m); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tshare.ID = m.ID // Backfill generated ID // 回填生成的 ID\n\t\treturn nil\n\t})\n}\n\nfunc (r *userShareRepository) GetByID(ctx context.Context, uid int64, id int64) (*domain.UserShare, error) {\n\tus := r.userShare(uid).UserShare\n\tm, err := us.WithContext(ctx).Where(us.ID.Eq(id)).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m), nil\n}\n\nfunc (r *userShareRepository) GetByPath(ctx context.Context, uid int64, vaultID int64, pathHash string) (*domain.UserShare, error) {\n\t// Get Note (Triggers Notes migration via noteRepo)\n\tnoteRepo := NewNoteRepository(r.dao)\n\tnote, err := noteRepo.GetByPathHash(ctx, pathHash, vaultID, uid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif note == nil {\n\t\treturn nil, nil\n\t}\n\n\t// 3. Get UserShare (Triggers UserShare migration via GetByRes -> userShare(uid))\n\t// 3. Get UserShare (通过 GetByRes -> userShare(uid) 触发 UserShare 迁移)\n\treturn r.GetByRes(ctx, uid, \"note\", note.ID)\n}\n\nfunc (r *userShareRepository) GetByRes(ctx context.Context, uid int64, resType string, resID int64) (*domain.UserShare, error) {\n\tus := r.userShare(uid).UserShare\n\tm, err := us.WithContext(ctx).Where(us.ResType.Eq(resType), us.ResID.Eq(resID), us.Status.Eq(domain.UserShareStatusActive)).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m), nil\n}\n\nfunc (r *userShareRepository) UpdateResources(ctx context.Context, uid int64, id int64, resources map[string][]string) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tus := r.userShare(uid).UserShare\n\t\tresBytes, err := json.Marshal(resources)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = us.WithContext(ctx).Where(us.ID.Eq(id)).Update(us.Res, string(resBytes))\n\t\treturn err\n\t})\n}\n\nfunc (r *userShareRepository) UpdateStatus(ctx context.Context, uid int64, id int64, status int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tus := r.userShare(uid).UserShare\n\t\t_, err := us.WithContext(ctx).Where(us.ID.Eq(id)).Update(us.Status, status)\n\t\treturn err\n\t})\n}\n\nfunc (r *userShareRepository) UpdateStatusByRes(ctx context.Context, uid int64, resType string, resID int64, status int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tus := r.userShare(uid).UserShare\n\t\t_, err := us.WithContext(ctx).Where(us.UID.Eq(uid), us.ResType.Eq(resType), us.ResID.Eq(resID), us.Status.Eq(domain.UserShareStatusActive)).Update(us.Status, status)\n\t\treturn err\n\t})\n}\n\nfunc (r *userShareRepository) UpdateViewStats(ctx context.Context, uid int64, id int64, viewCountIncr int64, lastViewedAt time.Time) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tus := r.userShare(uid).UserShare\n\t\t_, err := us.WithContext(ctx).Where(us.ID.Eq(id)).Updates(map[string]interface{}{\n\t\t\t\"view_count\":     gorm.Expr(\"view_count + ?\", viewCountIncr),\n\t\t\t\"last_viewed_at\": lastViewedAt,\n\t\t})\n\t\treturn err\n\t})\n}\n\nfunc (r *userShareRepository) ListByUID(ctx context.Context, uid int64, sortBy string, sortOrder string, offset, limit int) ([]*domain.UserShare, error) {\n\tus := r.userShare(uid).UserShare\n\n\t// Whitelist sorting field validation\n\t// 白名单验证排序字段\n\tallowedFields := map[string]string{\n\t\t\"created_at\": \"created_at\",\n\t\t\"updated_at\": \"updated_at\",\n\t\t\"expires_at\": \"expires_at\",\n\t}\n\tfield, ok := allowedFields[sortBy]\n\tif !ok {\n\t\tfield = \"created_at\"\n\t}\n\n\t// Validate sorting order\n\t// 验证排序方向\n\tif sortOrder != \"asc\" && sortOrder != \"desc\" {\n\t\tsortOrder = \"desc\"\n\t}\n\n\torderClause := field + \" \" + sortOrder\n\tvar ms []*model.UserShare\n\tq := us.WithContext(ctx).Where(us.UID.Eq(uid), us.Status.Eq(domain.UserShareStatusActive))\n\tif limit > 0 {\n\t\tq = q.Limit(limit).Offset(offset)\n\t}\n\terr := q.UnderlyingDB().Order(orderClause).Find(&ms).Error\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar ds []*domain.UserShare\n\tfor _, m := range ms {\n\t\tds = append(ds, r.toDomain(m))\n\t}\n\treturn ds, nil\n}\n\nfunc (r *userShareRepository) UpdateShortLink(ctx context.Context, uid int64, id int64, shortLink string) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tus := r.userShare(uid).UserShare\n\t\t_, err := us.WithContext(ctx).Where(us.ID.Eq(id)).Update(us.ShortLink, shortLink)\n\t\treturn err\n\t})\n}\n\nfunc (r *userShareRepository) UpdatePassword(ctx context.Context, uid int64, id int64, password string) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tus := r.userShare(uid).UserShare\n\t\t_, err := us.WithContext(ctx).Where(us.ID.Eq(id)).Update(us.Password, password)\n\t\treturn err\n\t})\n}\n\nfunc (r *userShareRepository) CountByUID(ctx context.Context, uid int64) (int64, error) {\n\tus := r.userShare(uid).UserShare\n\treturn us.WithContext(ctx).Where(us.UID.Eq(uid), us.Status.Eq(domain.UserShareStatusActive)).Count()\n}\n\n// ListActiveNoteResIDs returns note res_ids for all active shares of a user\n// ListActiveNoteResIDs retrieves note res_id list for all active shares of a user (queries only user_shares table, no cross-database JOIN)\n// ListActiveNoteResIDs 查询该用户所有有效分享中 res_type='note' 的 res_id 列表（只查 user_shares 表，无跨库 JOIN）\nfunc (r *userShareRepository) ListActiveNoteResIDs(ctx context.Context, uid int64) ([]int64, error) {\n\tus := r.userShare(uid).UserShare\n\tms, err := us.WithContext(ctx).\n\t\tWhere(us.UID.Eq(uid), us.ResType.Eq(\"note\"), us.Status.Eq(domain.UserShareStatusActive)).\n\t\tFind()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tids := make([]int64, 0, len(ms))\n\tfor _, m := range ms {\n\t\tids = append(ids, m.ResID)\n\t}\n\treturn ids, nil\n}\n\n// ListChangedNoteResIDs returns note share res_ids changed after since, grouped by status\n// ListChangedNoteResIDs 返回 updated_at > since 的 note 分享记录，按状态分组\n// ListChangedNoteResIDs returns note share res_ids changed after since, grouped by status\nfunc (r *userShareRepository) ListChangedNoteResIDs(ctx context.Context, uid int64, since time.Time) ([]int64, []int64, error) {\n\tus := r.userShare(uid).UserShare\n\tms, err := us.WithContext(ctx).\n\t\tWhere(us.UID.Eq(uid), us.ResType.Eq(\"note\"), us.UpdatedAt.Gt(timex.Time(since))).\n\t\tFind()\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tvar active, revoked []int64\n\tfor _, m := range ms {\n\t\tswitch m.Status {\n\t\tcase domain.UserShareStatusActive:\n\t\t\tactive = append(active, m.ResID)\n\t\tcase domain.UserShareStatusRevoked:\n\t\t\trevoked = append(revoked, m.ResID)\n\t\t}\n\t}\n\treturn active, revoked, nil\n}\n\n// MigrateResID updates res_id and resources JSON when a note/file is renamed (old ID → new ID).\n// MigrateResID updates res_id and resources JSON when a note/file is renamed (old ID -> new ID).\n// MigrateResID 在笔记/文件重命名时更新分享记录的资源 ID 和资源列表（旧 ID -> 新 ID）。\nfunc (r *userShareRepository) MigrateResID(ctx context.Context, uid int64, oldResID int64, newResID int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tus := r.userShare(uid).UserShare\n\n\t\t// 1. Update res_id for all active shares pointing to oldResID\n\t\t// 1. Update res_id for all active shares pointing to oldResID\n\t\t// 1. 更新所有指向旧 ID 的有效分享的 res_id\n\t\t_, err := us.WithContext(ctx).\n\t\t\tWhere(us.UID.Eq(uid), us.ResID.Eq(oldResID), us.Status.Eq(domain.UserShareStatusActive)).\n\t\t\tUpdate(us.ResID, newResID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// 2. Update resources JSON: replace oldResID with newResID in all note/file arrays\n\t\t// 2. Update resources JSON: replace oldResID with newResID in all note/file arrays\n\t\t// 2. 更新资源 JSON：在 note/file 数组中将旧 ID 替换为新 ID\n\t\toldIDStr := strconv.FormatInt(oldResID, 10)\n\t\tnewIDStr := strconv.FormatInt(newResID, 10)\n\n\t\tshares, err := us.WithContext(ctx).\n\t\t\tWhere(us.UID.Eq(uid), us.Status.Eq(domain.UserShareStatusActive)).\n\t\t\tFind()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfor _, share := range shares {\n\t\t\tvar res map[string][]string\n\t\t\tif err := json.Unmarshal([]byte(share.Res), &res); err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tchanged := false\n\t\t\tfor _, ids := range res {\n\t\t\t\tfor i, id := range ids {\n\t\t\t\t\tif id == oldIDStr {\n\t\t\t\t\t\tids[i] = newIDStr\n\t\t\t\t\t\tchanged = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif changed {\n\t\t\t\tresBytes, _ := json.Marshal(res)\n\t\t\t\t_, err := us.WithContext(ctx).\n\t\t\t\t\tWhere(us.ID.Eq(share.ID)).\n\t\t\t\t\tUpdate(us.Res, string(resBytes))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t})\n}\n\n// DeleteByVaultID deletes all shares belonging to a vault (notes/files in that vault)\n// DeleteByVaultID 删除属于该仓库的所有分享记录（仓库下的笔记或文件）\nfunc (r *userShareRepository) DeleteByVaultID(ctx context.Context, vaultID, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tus := r.userShare(uid).UserShare\n\n\t\t// 子查询：找到该仓库下的所有笔记 ID\n\t\tsubNote := db.Table(\"note\").Select(\"id\").Where(\"vault_id = ?\", vaultID)\n\t\t// 子查询：找到该仓库下的所有文件 ID\n\t\tsubFile := db.Table(\"file\").Select(\"id\").Where(\"vault_id = ?\", vaultID)\n\n\t\t// 删除笔记分享\n\t\tif err := us.WithContext(ctx).UnderlyingDB().Where(\"res_type = ? AND res_id IN (?)\", \"note\", subNote).Delete(&model.UserShare{}).Error; err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// 删除文件分享\n\t\treturn us.WithContext(ctx).UnderlyingDB().Where(\"res_type = ? AND res_id IN (?)\", \"file\", subFile).Delete(&model.UserShare{}).Error\n\t})\n}\n\n// Ensure userShareRepository implements domain.UserShareRepository interface\n// 确保 userShareRepository 实现了 domain.UserShareRepository 接口\nvar _ domain.UserShareRepository = (*userShareRepository)(nil)\n"
  },
  {
    "path": "internal/dao/vault_repository.go",
    "content": "// Package dao implements the data access layer\n// Package dao 实现数据访问层\npackage dao\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/query\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"gorm.io/gorm\"\n)\n\n// vaultRepository implements domain.VaultRepository interface\n// vaultRepository 实现 domain.VaultRepository 接口\ntype vaultRepository struct {\n\tdao             *Dao\n\tcustomPrefixKey string\n}\n\n// NewVaultRepository creates VaultRepository instance\n// NewVaultRepository 创建 VaultRepository 实例\nfunc NewVaultRepository(dao *Dao) domain.VaultRepository {\n\treturn &vaultRepository{dao: dao, customPrefixKey: \"user_vault_\"}\n}\n\nfunc (r *vaultRepository) GetKey(uid int64) string {\n\treturn r.customPrefixKey + strconv.FormatInt(uid, 10)\n}\n\nfunc init() {\n\tRegisterModel(ModelConfig{\n\t\tName: \"Vault\",\n\t\tRepoFactory: func(d *Dao) daoDBCustomKey {\n\t\t\treturn NewVaultRepository(d).(daoDBCustomKey)\n\t\t},\n\t})\n}\n\n// vault gets the vault query object\n// vault 获取保险库查询对象\nfunc (r *vaultRepository) vault(uid int64) *query.Query {\n\treturn r.dao.QueryWithOnceInit(func(g *gorm.DB) {\n\t\tmodel.AutoMigrate(g, \"Vault\")\n\t}, r.GetKey(uid)+\"#vault\", r.GetKey(uid))\n}\n\n// toDomain converts database model to domain model\n// toDomain 将数据库模型转换为领域模型\nfunc (r *vaultRepository) toDomain(m *model.Vault) *domain.Vault {\n\tif m == nil {\n\t\treturn nil\n\t}\n\treturn &domain.Vault{\n\t\tID:        m.ID,\n\t\tUID:       0, // Field UID not in model, provided by context // 模型中没有 UID 字段，由上下文提供\n\t\tName:      m.Vault,\n\t\tNoteCount: m.NoteCount,\n\t\tNoteSize:  m.NoteSize,\n\t\tFileCount: m.FileCount,\n\t\tFileSize:  m.FileSize,\n\t\tIsDeleted: m.IsDeleted == 1,\n\t\tCreatedAt: time.Time(m.CreatedAt),\n\t\tUpdatedAt: time.Time(m.UpdatedAt),\n\t}\n}\n\n// toModel converts domain model to database model\n// toModel 将领域模型转换为数据库模型\nfunc (r *vaultRepository) toModel(vault *domain.Vault) *model.Vault {\n\tif vault == nil {\n\t\treturn nil\n\t}\n\tisDeleted := int64(0)\n\tif vault.IsDeleted {\n\t\tisDeleted = 1\n\t}\n\treturn &model.Vault{\n\t\tID:        vault.ID,\n\t\tVault:     vault.Name,\n\t\tNoteCount: vault.NoteCount,\n\t\tNoteSize:  vault.NoteSize,\n\t\tFileCount: vault.FileCount,\n\t\tFileSize:  vault.FileSize,\n\t\tIsDeleted: isDeleted,\n\t\tCreatedAt: timex.Time(vault.CreatedAt),\n\t\tUpdatedAt: timex.Time(vault.UpdatedAt),\n\t}\n}\n\n// GetByID retrieves vault by ID\n// GetByID 根据ID获取仓库\nfunc (r *vaultRepository) GetByID(ctx context.Context, id, uid int64) (*domain.Vault, error) {\n\tu := r.vault(uid).Vault\n\tm, err := u.WithContext(ctx).Where(u.ID.Eq(id), u.IsDeleted.Eq(0)).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m), nil\n}\n\n// GetByName retrieves vault by name\n// GetByName 根据名称获取仓库\nfunc (r *vaultRepository) GetByName(ctx context.Context, name string, uid int64) (*domain.Vault, error) {\n\tu := r.vault(uid).Vault\n\tm, err := u.WithContext(ctx).Where(u.Vault.Eq(name), u.IsDeleted.Eq(0)).First()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.toDomain(m), nil\n}\n\n// Create creates a vault\n// Create 创建仓库\nfunc (r *vaultRepository) Create(ctx context.Context, vault *domain.Vault, uid int64) (*domain.Vault, error) {\n\tvar result *domain.Vault\n\tvar createErr error\n\n\terr := r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := query.Use(db).Vault\n\t\tm := &model.Vault{\n\t\t\tVault:     vault.Name,\n\t\t\tNoteCount: vault.NoteCount,\n\t\t\tNoteSize:  vault.NoteSize,\n\t\t\tFileCount: vault.FileCount,\n\t\t\tFileSize:  vault.FileSize,\n\t\t\tIsDeleted: 0,\n\t\t\tCreatedAt: timex.Now(),\n\t\t\tUpdatedAt: timex.Now(),\n\t\t}\n\n\t\tcreateErr = u.WithContext(ctx).Create(m)\n\t\tif createErr != nil {\n\t\t\treturn createErr\n\t\t}\n\t\tresult = r.toDomain(m)\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn result, createErr\n}\n\n// Update updates a vault\n// Update 更新仓库\nfunc (r *vaultRepository) Update(ctx context.Context, vault *domain.Vault, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := query.Use(db).Vault\n\t\tm := r.toModel(vault)\n\t\tm.UpdatedAt = timex.Now()\n\n\t\treturn u.WithContext(ctx).Where(u.ID.Eq(vault.ID)).Save(m)\n\t})\n}\n\n// UpdateNoteCountSize updates the note count and size of the vault\n// UpdateNoteCountSize 更新仓库的笔记数量和大小\nfunc (r *vaultRepository) UpdateNoteCountSize(ctx context.Context, noteSize, noteCount, vaultID, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := query.Use(db).Vault\n\n\t\t_, err := u.WithContext(ctx).Where(\n\t\t\tu.ID.Eq(vaultID),\n\t\t).UpdateSimple(\n\t\t\tu.NoteSize.Value(noteSize),\n\t\t\tu.NoteCount.Value(noteCount),\n\t\t\tu.UpdatedAt.Value(timex.Now()),\n\t\t)\n\t\treturn err\n\t})\n}\n\n// UpdateFileCountSize updates the file count and size of the vault\n// UpdateFileCountSize 更新仓库的文件数量和大小\nfunc (r *vaultRepository) UpdateFileCountSize(ctx context.Context, fileSize, fileCount, vaultID, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := query.Use(db).Vault\n\n\t\t_, err := u.WithContext(ctx).Where(\n\t\t\tu.ID.Eq(vaultID),\n\t\t).UpdateSimple(\n\t\t\tu.FileSize.Value(fileSize),\n\t\t\tu.FileCount.Value(fileCount),\n\t\t\tu.UpdatedAt.Value(timex.Now()),\n\t\t)\n\t\treturn err\n\t})\n}\n\n// List retrieves the vault list\n// List 获取仓库列表\nfunc (r *vaultRepository) List(ctx context.Context, uid int64) ([]*domain.Vault, error) {\n\tu := r.vault(uid).Vault\n\n\tmodelList, err := u.WithContext(ctx).\n\t\tWhere(u.IsDeleted.Eq(0)).\n\t\tOrder(u.CreatedAt).\n\t\tLimit(100).\n\t\tOrder(u.UpdatedAt).\n\t\tFind()\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar list []*domain.Vault\n\tfor _, m := range modelList {\n\t\tlist = append(list, r.toDomain(m))\n\t}\n\treturn list, nil\n}\n\n// Delete deletes the vault (soft delete)\n// Delete 删除仓库（软删除）\nfunc (r *vaultRepository) Delete(ctx context.Context, id, uid int64) error {\n\treturn r.dao.ExecuteWrite(ctx, uid, r, func(db *gorm.DB) error {\n\t\tu := query.Use(db).Vault\n\n\t\t_, err := u.WithContext(ctx).Where(\n\t\t\tu.ID.Eq(id),\n\t\t).UpdateSimple(\n\t\t\tu.IsDeleted.Value(1),\n\t\t)\n\t\treturn err\n\t})\n}\n\n// Ensure vaultRepository implements domain.VaultRepository interface\n// 确保 vaultRepository 实现了 domain.VaultRepository 接口\nvar _ domain.VaultRepository = (*vaultRepository)(nil)\n"
  },
  {
    "path": "internal/domain/domain_backup.go",
    "content": "package domain\n\nimport (\n\t\"context\"\n\t\"time\"\n)\n\n\nconst (\n\tBackupStatusIdle     = 0\n\tBackupStatusRunning  = 1\n\tBackupStatusSuccess  = 2\n\tBackupStatusFailed   = 3\n\tBackupStatusStopped  = 4\n\tBackupStatusNoUpdate = 5\n)\n\n// BackupConfig 备份配置领域模型\ntype BackupConfig struct {\n\tID               int64\n\tUID              int64\n\tVaultID          int64     // 关联库 ID (0 表示所有库)\n\tType             string    // full, incremental, sync\n\tStorageIds       string    // JSON 数组，如 \"[1, 2]\"\n\tIsEnabled        bool      // 是否启用\n\tCronStrategy     string    // daily, weekly, monthly, custom\n\tCronExpression   string    // Cron 表达式\n\tIncludeVaultName bool      // 同步路径是否包含仓库名前缀\n\tRetentionDays    int       // 保留天数\n\tLastRunTime      time.Time // 上次运行时间\n\tNextRunTime      time.Time // 下次运行时间\n\tLastStatus       int       // 上次状态 (0: Idle, 1: Running, 2: Success, 3: Failed, 4: Stopped, 5: SuccessNoUpdate)\n\tLastMessage      string    // 上次运行结果消息\n\tCreatedAt        time.Time\n\tUpdatedAt        time.Time\n}\n\n// BackupHistory 备份历史领域模型\ntype BackupHistory struct {\n\tID        int64\n\tUID       int64\n\tConfigID  int64\n\tStorageID int64\n\tType      string // full, incremental, sync\n\tStartTime time.Time\n\tEndTime   time.Time\n\tStatus    int // 0: Idle, 1: Running, 2: Success, 3: Failed, 4: Stopped, 5: SuccessNoUpdate\n\tFileSize  int64\n\tFileCount int64\n\tMessage   string\n\tFilePath  string\n\tCreatedAt time.Time\n\tUpdatedAt time.Time\n}\n\n// BackupRepository 备份仓储接口\ntype BackupRepository interface {\n\t// ListConfigs 获取用户的备份配置列表\n\tListConfigs(ctx context.Context, uid int64) ([]*BackupConfig, error)\n\t// GetByID 根据ID获取备份配置\n\tGetByID(ctx context.Context, id, uid int64) (*BackupConfig, error)\n\t// DeleteConfig 删除备份配置\n\tDeleteConfig(ctx context.Context, id, uid int64) error\n\t// SaveConfig 保存备份配置\n\tSaveConfig(ctx context.Context, config *BackupConfig, uid int64) (*BackupConfig, error)\n\t// ListEnabledConfigs 获取所有已启用的备份配置\n\tListEnabledConfigs(ctx context.Context) ([]*BackupConfig, error)\n\t// UpdateNextRunTime 更新下次执行时间\n\tUpdateNextRunTime(ctx context.Context, id, uid int64, nextRun time.Time) error\n\n\t// CreateHistory 创建备份历史记录\n\tCreateHistory(ctx context.Context, history *BackupHistory, uid int64) (*BackupHistory, error)\n\t// ListHistory 分页获取备份历史记录\n\tListHistory(ctx context.Context, uid int64, configID int64, page, pageSize int) ([]*BackupHistory, int64, error)\n\t// ListOldHistory List old history records created before cutoffTime\n\t// 获取早于 cutoffTime 的历史记录\n\tListOldHistory(ctx context.Context, uid int64, configID int64, cutoffTime time.Time) ([]*BackupHistory, error)\n\t// DeleteOldHistory Delete old history records created before cutoffTime\n\t// 删除早于 cutoffTime 的历史记录\n\tDeleteOldHistory(ctx context.Context, uid int64, configID int64, cutoffTime time.Time) error\n\n\t// DisableByVaultID 禁用仓库下的备份任务\n\tDisableByVaultID(ctx context.Context, vaultID, uid int64) error\n}\n\n"
  },
  {
    "path": "internal/domain/domain_file.go",
    "content": "package domain\n\nimport (\n\t\"context\"\n\t\"time\"\n)\n\n\n// FileAction 定义文件操作类型\ntype FileAction string\n\nconst (\n\tFileActionCreate FileAction = \"create\"\n\tFileActionModify FileAction = \"modify\"\n\tFileActionDelete FileAction = \"delete\"\n)\n\n// File 文件领域模型\ntype File struct {\n\tID               int64\n\tVaultID          int64\n\tAction           FileAction\n\tFID              int64\n\tPath             string\n\tPathHash         string\n\tContentHash      string\n\tSavePath         string\n\tRename           int64\n\tSize             int64\n\tCtime            int64\n\tMtime            int64\n\tUpdatedTimestamp int64\n\tCreatedAt        time.Time\n\tUpdatedAt        time.Time\n}\n\n// IsDeleted 判断文件是否已删除\nfunc (f *File) IsDeleted() bool {\n\treturn f.Action == FileActionDelete\n}\n\n// IsCreated 判断文件是否为新建\nfunc (f *File) IsCreated() bool {\n\treturn f.Action == FileActionCreate\n}\n\n// IsModified 判断文件是否已修改\nfunc (f *File) IsModified() bool {\n\treturn f.Action == FileActionModify\n}\n\n// FileRepository 文件仓储接口\ntype FileRepository interface {\n\t// GetByID 根据 ID 获取文件\n\tGetByID(ctx context.Context, id, uid int64) (*File, error)\n\n\t// GetByPathHash 根据路径哈希获取文件\n\tGetByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) (*File, error)\n\n\t// ListByPathHash 根据路径哈希获取文件列表（处理重复记录）\n\tListByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) ([]*File, error)\n\n\t// GetByPath 根据路径获取文件\n\tGetByPath(ctx context.Context, path string, vaultID, uid int64) (*File, error)\n\n\t// GetByPathLike 根据路径后缀获取文件\n\tGetByPathLike(ctx context.Context, path string, vaultID, uid int64) (*File, error)\n\n\t// Create 创建文件\n\tCreate(ctx context.Context, file *File, uid int64) (*File, error)\n\n\t// Update 更新文件\n\tUpdate(ctx context.Context, file *File, uid int64) (*File, error)\n\n\t// UpdateMtime 更新文件修改时间\n\tUpdateMtime(ctx context.Context, mtime int64, id, uid int64) error\n\n\t// UpdateActionMtime 更新文件类型并修改时间\n\tUpdateActionMtime(ctx context.Context, action FileAction, mtime int64, id, uid int64) error\n\n\t// UpdateFID 仅更新文件的文件夹关联 ID，不更新 updated_timestamp\n\t// 用于 SyncResourceFID 内部整理，避免污染增量同步时间戳\n\t// Only updates the folder ID (FID), does not update updated_timestamp\n\t// Used by SyncResourceFID to avoid polluting incremental sync timestamps\n\tUpdateFID(ctx context.Context, id, fid, uid int64) error\n\n\t// Delete 物理删除文件\n\tDelete(ctx context.Context, id, uid int64) error\n\n\t// DeletePhysicalByTime 根据时间物理删除已标记删除的文件\n\tDeletePhysicalByTime(ctx context.Context, timestamp, uid int64) error\n\n\t// DeletePhysicalByTimeAll 根据时间物理删除所有用户的已标记删除的文件\n\tDeletePhysicalByTimeAll(ctx context.Context, timestamp int64) error\n\n\t// List 分页获取文件列表\n\tList(ctx context.Context, vaultID int64, page, pageSize int, uid int64, keyword string, isRecycle bool, sortBy string, sortOrder string) ([]*File, error)\n\n\t// ListCount 获取文件数量\n\tListCount(ctx context.Context, vaultID, uid int64, keyword string, isRecycle bool) (int64, error)\n\n\t// ListByUpdatedTimestamp 根据更新时间戳获取文件列表\n\tListByUpdatedTimestamp(ctx context.Context, timestamp, vaultID, uid int64) ([]*File, error)\n\n\t// ListByUpdatedTimestampPage 根据更新时间戳分页获取文件列表\n\tListByUpdatedTimestampPage(ctx context.Context, timestamp, vaultID, uid int64, offset, limit int) ([]*File, error)\n\n\t// ListByMtime 根据修改时间戳获取文件列表\n\tListByMtime(ctx context.Context, timestamp, vaultID, uid int64) ([]*File, error)\n\n\t// CountSizeSum 获取文件数量和大小总和\n\tCountSizeSum(ctx context.Context, vaultID, uid int64) (*CountSizeResult, error)\n\n\t// ListByFID 根据文件夹ID获取文件列表\n\tListByFID(ctx context.Context, fid, vaultID, uid int64, page, pageSize int, sortBy, sortOrder string) ([]*File, error)\n\n\t// ListByFIDCount 根据文件夹ID获取文件数量\n\tListByFIDCount(ctx context.Context, fid, vaultID, uid int64) (int64, error)\n\n\t// ListByFIDs 根据多个文件夹ID获取文件列表（处理重复文件夹记录）\n\tListByFIDs(ctx context.Context, fids []int64, vaultID, uid int64, page, pageSize int, sortBy, sortOrder string) ([]*File, error)\n\n\t// ListByFIDsCount 根据多个文件夹ID获取文件数量\n\tListByFIDsCount(ctx context.Context, fids []int64, vaultID, uid int64) (int64, error)\n\n\t// ListByIDs 根据ID列表获取文件列表\n\tListByIDs(ctx context.Context, ids []int64, uid int64) ([]*File, error)\n\n\t// ListByPathPrefix 根据路径前缀获取文件列表\n\tListByPathPrefix(ctx context.Context, pathPrefix string, vaultID, uid int64) ([]*File, error)\n\n\t// RecycleClear 清理回收站\n\tRecycleClear(ctx context.Context, path, pathHash string, vaultID, uid int64) error\n\n\t// DeleteByVaultID 物理删除仓库下的所有文件\n\tDeleteByVaultID(ctx context.Context, vaultID, uid int64) error\n}\n\n"
  },
  {
    "path": "internal/domain/domain_folder.go",
    "content": "package domain\n\nimport (\n\t\"context\"\n\t\"time\"\n)\n\n\n// FolderAction 定义文件夹操作类型\ntype FolderAction string\n\nconst (\n\tFolderActionCreate FolderAction = \"create\"\n\tFolderActionModify FolderAction = \"modify\"\n\tFolderActionDelete FolderAction = \"delete\"\n)\n\n// Folder 文件夹领域模型\ntype Folder struct {\n\tID               int64\n\tVaultID          int64\n\tAction           FolderAction\n\tPath             string\n\tPathHash         string\n\tLevel            int64\n\tFID              int64\n\tCtime            int64\n\tMtime            int64\n\tUpdatedTimestamp int64\n\tCreatedAt        time.Time\n\tUpdatedAt        time.Time\n}\n\n// IsDeleted 判断文件夹是否已删除\nfunc (f *Folder) IsDeleted() bool {\n\treturn f.Action == FolderActionDelete\n}\n\n// FolderRepository 文件夹仓储接口\ntype FolderRepository interface {\n\t// GetByID 根据ID获取文件夹\n\tGetByID(ctx context.Context, id, uid int64) (*Folder, error)\n\n\t// GetByPathHash 根据路径哈希获取文件夹\n\tGetByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) (*Folder, error)\n\n\t// GetAllByPathHash 根据路径哈希获取所有匹配的文件夹（处理重复记录）\n\tGetAllByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) ([]*Folder, error)\n\n\t// GetByFID 根据父级ID获取文件夹列表\n\tGetByFID(ctx context.Context, fid int64, vaultID, uid int64) ([]*Folder, error)\n\n\t// Create 创建文件夹\n\tCreate(ctx context.Context, folder *Folder, uid int64) (*Folder, error)\n\n\t// Update 更新文件夹\n\tUpdate(ctx context.Context, folder *Folder, uid int64) (*Folder, error)\n\n\t// Delete 物理删除文件夹\n\tDelete(ctx context.Context, id, uid int64) error\n\n\t// ListByUpdatedTimestamp 根据更新时间戳获取文件夹列表\n\tListByUpdatedTimestamp(ctx context.Context, timestamp, vaultID, uid int64) ([]*Folder, error)\n\n\t// List 获取指定仓库下的所有文件夹\n\tList(ctx context.Context, vaultID int64, uid int64) ([]*Folder, error)\n\t// ListAll 获取该用户所有的文件夹\n\tListAll(ctx context.Context, uid int64) ([]*Folder, error)\n\n\t// DeleteByVaultID 删除仓库下的所有文件夹记录\n\tDeleteByVaultID(ctx context.Context, vaultID, uid int64) error\n}\n\n"
  },
  {
    "path": "internal/domain/domain_git_sync.go",
    "content": "package domain\n\nimport (\n\t\"context\"\n\t\"time\"\n)\n\n\nconst (\n\tGitSyncStatusIdle     = 0\n\tGitSyncStatusRunning  = 1\n\tGitSyncStatusSuccess  = 2\n\tGitSyncStatusFailed   = 3\n\tGitSyncStatusShutdown = 4\n)\n\n// GitSyncConfig Git 仓库同步任务\ntype GitSyncConfig struct {\n\tID            int64      `json:\"id\"`\n\tUID           int64      `json:\"uid\"`\n\tVaultID       int64      `json:\"vaultId\"`\n\tRepoURL       string     `json:\"repoUrl\"`\n\tUsername      string     `json:\"username\"`\n\tPassword      string     `json:\"password\"`\n\tBranch        string     `json:\"branch\"`\n\tIsEnabled     bool       `json:\"isEnabled\"`\n\tDelay         int64      `json:\"delay\"` // 延迟时间（秒）\n\tRetentionDays int64      `json:\"retentionDays\"`\n\tLastSyncTime  *time.Time `json:\"lastSyncTime\"`\n\tLastStatus    int64      `json:\"lastStatus\"` // 0: 闲置, 1: 运行中, 2: 成功, 3: 失败, 4: 系统关闭\n\tLastMessage   string     `json:\"lastMessage\"`\n\tCreatedAt     time.Time  `json:\"createdAt\"`\n\tUpdatedAt     time.Time  `json:\"updatedAt\"`\n}\n\n// GitSyncHistory Git 同步历史\ntype GitSyncHistory struct {\n\tID        int64     `json:\"id\"`\n\tUID       int64     `json:\"uid\"`\n\tConfigID  int64     `json:\"configId\"`\n\tStartTime time.Time `json:\"startTime\"`\n\tEndTime   time.Time `json:\"endTime\"`\n\tStatus    int64     `json:\"status\"` // 0: 闲置, 1: 运行中, 2: 成功, 3: 失败, 4: 系统关闭\n\tMessage   string    `json:\"message\"`\n\tCreatedAt time.Time `json:\"createdAt\"`\n\tUpdatedAt time.Time `json:\"updatedAt\"`\n}\n\n// GitSyncRepository Git 同步任务仓储接口\ntype GitSyncRepository interface {\n\t// GetByID 根据ID获取 Git 同步任务\n\tGetByID(ctx context.Context, id, uid int64) (*GitSyncConfig, error)\n\t// GetByVaultID 根据 VaultID 获取 Git 同步任务\n\tGetByVaultID(ctx context.Context, vaultID, uid int64) (*GitSyncConfig, error)\n\t// Save 保存 (创建或更新) Git 同步任务\n\tSave(ctx context.Context, config *GitSyncConfig, uid int64) (*GitSyncConfig, error)\n\t// Delete 删除 Git 同步任务\n\tDelete(ctx context.Context, id, uid int64) error\n\t// List 获取用户的 Git 同步任务列表\n\tList(ctx context.Context, uid int64) ([]*GitSyncConfig, error)\n\t// ListByVaultID 根据笔记仓库ID获取关联的 Git 同步任务列表\n\tListByVaultID(ctx context.Context, vaultID, uid int64) ([]*GitSyncConfig, error)\n\t// ListEnabled 获取所有已启用的 Git 同步任务 (跨用户)\n\tListEnabled(ctx context.Context) ([]*GitSyncConfig, error)\n\n\t// CreateHistory 创建 Git 同步历史记录\n\tCreateHistory(ctx context.Context, history *GitSyncHistory, uid int64) (*GitSyncHistory, error)\n\t// ListHistory 分页获取 Git 同步历史记录\n\tListHistory(ctx context.Context, uid int64, configID int64, page, pageSize int) ([]*GitSyncHistory, int64, error)\n\t// DeleteHistory 删除 Git 同步历史记录\n\tDeleteHistory(ctx context.Context, uid int64, configID int64) error\n\t// DeleteOldHistory 删除指定时间之前的同步历史记录\n\tDeleteOldHistory(ctx context.Context, uid int64, configID int64, cutoffTime time.Time) error\n\n\t// DisableByVaultID 禁用仓库下的 Git 同步任务\n\tDisableByVaultID(ctx context.Context, vaultID, uid int64) error\n}\n\n"
  },
  {
    "path": "internal/domain/domain_note.go",
    "content": "// Package domain 定义领域模型和接口\npackage domain\n\nimport (\n\t\"context\"\n\t\"time\"\n)\n\n\n// NoteAction 定义笔记操作类型\ntype NoteAction string\n\nconst (\n\tNoteActionCreate NoteAction = \"create\"\n\tNoteActionModify NoteAction = \"modify\"\n\tNoteActionDelete NoteAction = \"delete\"\n)\n\n// Note 笔记领域模型\ntype Note struct {\n\tID                      int64\n\tVaultID                 int64\n\tAction                  NoteAction\n\tRename                  int64\n\tFID                     int64\n\tPath                    string\n\tPathHash                string\n\tContent                 string\n\tContentHash             string\n\tContentLastSnapshot     string\n\tContentLastSnapshotHash string\n\tVersion                 int64\n\tClientName              string\n\tClientType              string\n\tClientVersion           string\n\tSize                    int64\n\tCtime                   int64\n\tMtime                   int64\n\tUpdatedTimestamp        int64\n\tCreatedAt               time.Time\n\tUpdatedAt               time.Time\n}\n\n// CountSizeResult 统计结果\ntype CountSizeResult struct {\n\tCount int64\n\tSize  int64\n}\n\n// IsDeleted 判断笔记是否已删除\nfunc (n *Note) IsDeleted() bool {\n\treturn n.Action == NoteActionDelete\n}\n\n// IsCreated 判断笔记是否为新建\nfunc (n *Note) IsCreated() bool {\n\treturn n.Action == NoteActionCreate\n}\n\n// IsModified 判断笔记是否已修改\nfunc (n *Note) IsModified() bool {\n\treturn n.Action == NoteActionModify\n}\n\n// NoteRepository 笔记仓储接口\ntype NoteRepository interface {\n\t// GetByID 根据ID获取笔记\n\tGetByID(ctx context.Context, id, uid int64) (*Note, error)\n\n\t// GetByPathHash 根据路径哈希获取笔记（排除已删除）\n\tGetByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) (*Note, error)\n\n\t// GetByPathHashIncludeRecycle 根据路径哈希获取笔记（可选包含回收站）\n\tGetByPathHashIncludeRecycle(ctx context.Context, pathHash string, vaultID, uid int64, isRecycle bool) (*Note, error)\n\n\t// GetAllByPathHash 根据路径哈希获取笔记（包含所有状态）\n\tGetAllByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) (*Note, error)\n\n\t// ListByPathHash 根据路径哈希获取笔记列表（处理重复记录）\n\tListByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) ([]*Note, error)\n\n\t// GetByPath 根据路径获取笔记\n\tGetByPath(ctx context.Context, path string, vaultID, uid int64) (*Note, error)\n\n\t// Create 创建笔记\n\tCreate(ctx context.Context, note *Note, uid int64) (*Note, error)\n\n\t// Update 更新笔记\n\tUpdate(ctx context.Context, note *Note, uid int64) (*Note, error)\n\n\t// UpdateDelete 更新笔记为删除状态\n\tUpdateDelete(ctx context.Context, note *Note, uid int64) error\n\n\t// UpdateMtime 更新笔记修改时间\n\tUpdateMtime(ctx context.Context, mtime int64, id, uid int64) error\n\n\t// UpdateActionMtime 更新笔记类型并修改时间\n\tUpdateActionMtime(ctx context.Context, action NoteAction, mtime int64, id, uid int64) error\n\n\t// UpdateFID 仅更新笔记的文件夹关联 ID，不更新 updated_timestamp\n\t// 用于 SyncResourceFID 内部整理，避免污染增量同步时间戳\n\t// Only updates the folder ID (FID), does not update updated_timestamp\n\t// Used by SyncResourceFID to avoid polluting incremental sync timestamps\n\tUpdateFID(ctx context.Context, id, fid, uid int64) error\n\n\t// UpdateSnapshot 更新笔记快照\n\tUpdateSnapshot(ctx context.Context, snapshot, snapshotHash string, version, id, uid int64) error\n\n\t// Delete 物理删除笔记\n\tDelete(ctx context.Context, id, uid int64) error\n\n\t// DeletePhysicalByTime 根据时间物理删除已标记删除的笔记\n\tDeletePhysicalByTime(ctx context.Context, timestamp, uid int64) error\n\n\t// DeletePhysicalByTimeAll 根据时间物理删除所有用户的已标记删除的笔记\n\tDeletePhysicalByTimeAll(ctx context.Context, timestamp int64) error\n\n\t// List 分页获取笔记列表\n\t// searchMode: path(默认), content, regex\n\t// sortBy: mtime(默认), ctime, path\n\t// sortOrder: desc(默认), asc\n\t// paths: 逗号分隔的精确路径列表，非空时忽略 keyword 做 IN 查询\n\tList(ctx context.Context, vaultID int64, page, pageSize int, uid int64, keyword string, isRecycle bool, searchMode string, searchContent bool, sortBy string, sortOrder string, paths []string) ([]*Note, error)\n\n\t// ListCount 获取笔记数量\n\t// searchMode: path(默认), content, regex\n\tListCount(ctx context.Context, vaultID, uid int64, keyword string, isRecycle bool, searchMode string, searchContent bool, paths []string) (int64, error)\n\n\t// ListByUpdatedTimestamp 根据更新时间戳获取笔记列表\n\tListByUpdatedTimestamp(ctx context.Context, timestamp, vaultID, uid int64) ([]*Note, error)\n\n\t// ListByUpdatedTimestampPage 根据更新时间戳分页获取笔记列表\n\tListByUpdatedTimestampPage(ctx context.Context, timestamp, vaultID, uid int64, offset, limit int) ([]*Note, error)\n\n\t// ListContentUnchanged 获取内容未变更的笔记列表\n\tListContentUnchanged(ctx context.Context, uid int64) ([]*Note, error)\n\n\t// CountSizeSum 获取笔记数量和大小总和\n\tCountSizeSum(ctx context.Context, vaultID, uid int64) (*CountSizeResult, error)\n\n\t// ListByFID 根据文件夹ID获取笔记列表\n\tListByFID(ctx context.Context, fid, vaultID, uid int64, page, pageSize int, sortBy, sortOrder string) ([]*Note, error)\n\n\t// ListByFIDCount 根据文件夹ID获取笔记数量\n\tListByFIDCount(ctx context.Context, fid, vaultID, uid int64) (int64, error)\n\n\t// ListByFIDs 根据多个文件夹ID获取笔记列表（处理重复文件夹记录）\n\tListByFIDs(ctx context.Context, fids []int64, vaultID, uid int64, page, pageSize int, sortBy, sortOrder string) ([]*Note, error)\n\n\t// ListByFIDsCount 根据多个文件夹ID获取笔记数量\n\tListByFIDsCount(ctx context.Context, fids []int64, vaultID, uid int64) (int64, error)\n\n\t// ListByIDs 根据ID列表获取笔记列表\n\tListByIDs(ctx context.Context, ids []int64, uid int64) ([]*Note, error)\n\n\t// ListByPathPrefix 根据路径前缀获取笔记列表\n\tListByPathPrefix(ctx context.Context, pathPrefix string, vaultID, uid int64) ([]*Note, error)\n\n\t// RecycleClear 清理回收站\n\tRecycleClear(ctx context.Context, path, pathHash string, vaultID, uid int64) error\n\n\t// DeleteByVaultID 物理删除仓库下的所有笔记\n\tDeleteByVaultID(ctx context.Context, vaultID, uid int64) error\n}\n\n"
  },
  {
    "path": "internal/domain/domain_note_fts.go",
    "content": "package domain\n\nimport (\n\t\"context\"\n)\n\n// NoteFTSRepository FTS full-text search repository interface\n// NoteFTSRepository FTS 全文搜索仓库接口\ntype NoteFTSRepository interface {\n\t// Upsert inserts or updates FTS index\n\t// Upsert 插入或更新 FTS 索引\n\tUpsert(ctx context.Context, noteID int64, path, content string, uid int64) error\n\t// Delete deletes FTS index\n\t// Delete 删除 FTS 索引\n\tDelete(ctx context.Context, noteID int64, uid int64) error\n\t// Search full-text search, returns list of matching note_id\n\t// Search 全文搜索，返回匹配的 note_id 列表\n\tSearch(ctx context.Context, keyword string, vaultID, uid int64, limit, offset int) ([]int64, error)\n\t// SearchCount full-text search count\n\t// SearchCount 全文搜索计数\n\tSearchCount(ctx context.Context, keyword string, vaultID, uid int64) (int64, error)\n\t// RebuildIndex rebuilds index (reads all note content from file system)\n\t// RebuildIndex 重建索引（从文件系统读取所有笔记内容）\n\tRebuildIndex(ctx context.Context, uid int64) error\n\t// DeleteByVaultID deletes all FTS records for a vault\n\t// DeleteByVaultID 删除指定仓库的所有 FTS 记录\n\tDeleteByVaultID(ctx context.Context, vaultID, uid int64) error\n}\n"
  },
  {
    "path": "internal/domain/domain_note_history.go",
    "content": "// Package domain 定义领域模型和接口\npackage domain\n\nimport (\n\t\"context\"\n\t\"time\"\n)\n\n\n// NoteHistory 笔记历史领域模型\ntype NoteHistory struct {\n\tID          int64\n\tNoteID      int64\n\tVaultID     int64\n\tPath        string\n\tDiffPatch   string\n\tContent     string\n\tContentHash string\n\tClientName  string\n\tClientType  string\n\tClientVersion string\n\tVersion     int64\n\tCreatedAt   time.Time\n\tUpdatedAt   time.Time\n}\n\n// NoteHistoryRepository 笔记历史仓储接口\ntype NoteHistoryRepository interface {\n\t// GetByID 根据ID获取历史记录\n\tGetByID(ctx context.Context, id, uid int64) (*NoteHistory, error)\n\n\t// GetByNoteIDAndHash 根据笔记ID和内容哈希获取历史记录\n\tGetByNoteIDAndHash(ctx context.Context, noteID int64, contentHash string, uid int64) (*NoteHistory, error)\n\n\t// Create 创建历史记录\n\tCreate(ctx context.Context, history *NoteHistory, uid int64) (*NoteHistory, error)\n\n\t// ListByNoteID 根据笔记ID获取历史记录列表\n\tListByNoteID(ctx context.Context, noteID int64, page, pageSize int, uid int64) ([]*NoteHistory, int64, error)\n\n\t// GetLatestVersion 获取笔记的最新版本号\n\tGetLatestVersion(ctx context.Context, noteID, uid int64) (int64, error)\n\n\t// Migrate 迁移历史记录（更新 NoteID）\n\tMigrate(ctx context.Context, oldNoteID, newNoteID, uid int64) error\n\n\t// GetNoteIDsWithOldHistory 获取有旧历史记录的笔记ID列表\n\t// cutoffTime: 截止时间戳（毫秒），返回有早于此时间历史记录的笔记ID\n\tGetNoteIDsWithOldHistory(ctx context.Context, cutoffTime int64, uid int64) ([]int64, error)\n\n\t// DeleteOldVersions 删除旧版本历史记录，保留最近 N 个版本\n\t// noteID: 笔记ID\n\t// cutoffTime: 截止时间戳（毫秒），删除早于此时间的记录\n\t// keepVersions: 保留的最近版本数量\n\tDeleteOldVersions(ctx context.Context, noteID int64, cutoffTime int64, keepVersions int, uid int64) error\n\n\t// Delete 删除指定ID的历史记录\n\tDelete(ctx context.Context, id, uid int64) error\n\n\t// DeleteByVaultID 删除仓库下的所有历史记录（包含物理目录）\n\tDeleteByVaultID(ctx context.Context, vaultID, uid int64) error\n}\n\n"
  },
  {
    "path": "internal/domain/domain_note_link.go",
    "content": "// Package domain defines domain models and interfaces\npackage domain\n\nimport (\n\t\"context\"\n\t\"time\"\n)\n\n\n// NoteLink represents a wiki-style link between notes\ntype NoteLink struct {\n\tID             int64\n\tSourceNoteID   int64\n\tTargetPath     string\n\tTargetPathHash string\n\tLinkText       string // alias from [[link|alias]]\n\tIsEmbed        bool   // true if embed (![[...]]) vs regular link ([[...]])\n\tVaultID        int64\n\tCreatedAt      time.Time\n}\n\n// NoteLinkRepository note link repository interface\ntype NoteLinkRepository interface {\n\t// CreateBatch creates multiple note links in batch\n\tCreateBatch(ctx context.Context, links []*NoteLink, uid int64) error\n\n\t// DeleteBySourceNoteID deletes all links from a source note\n\tDeleteBySourceNoteID(ctx context.Context, sourceNoteID, uid int64) error\n\n\t// GetBacklinks gets all notes that link to a target path\n\tGetBacklinks(ctx context.Context, targetPathHash string, vaultID, uid int64) ([]*NoteLink, error)\n\n\t// GetBacklinksByHashes gets all notes that link to any of the target path hashes\n\t// Used for matching path variations (e.g., [[note]], [[folder/note]], [[full/path/note]])\n\tGetBacklinksByHashes(ctx context.Context, targetPathHashes []string, vaultID, uid int64) ([]*NoteLink, error)\n\n\t// GetOutlinks gets all links from a source note\n\tGetOutlinks(ctx context.Context, sourceNoteID, uid int64) ([]*NoteLink, error)\n\n\t// DeleteByVaultID deletes all links for a vault\n\tDeleteByVaultID(ctx context.Context, vaultID, uid int64) error\n}\n\n"
  },
  {
    "path": "internal/domain/domain_setting.go",
    "content": "// Package domain 定义领域模型和接口\npackage domain\n\nimport (\n\t\"context\"\n\t\"time\"\n)\n\n\n// SettingAction 定义配置操作类型\ntype SettingAction string\n\nconst (\n\tSettingActionCreate SettingAction = \"create\"\n\tSettingActionModify SettingAction = \"modify\"\n\tSettingActionDelete SettingAction = \"delete\"\n)\n\n// Setting 配置领域模型\ntype Setting struct {\n\tID               int64\n\tVaultID          int64\n\tAction           SettingAction\n\tPath             string\n\tPathHash         string\n\tContent          string\n\tContentHash      string\n\tSize             int64\n\tRename           int64\n\tCtime            int64\n\tMtime            int64\n\tUpdatedTimestamp int64\n\tCreatedAt        time.Time\n\tUpdatedAt        time.Time\n}\n\n// IsDeleted 判断配置是否已删除\nfunc (s *Setting) IsDeleted() bool {\n\treturn s.Action == SettingActionDelete\n}\n\n// IsCreated 判断配置是否为新建\nfunc (s *Setting) IsCreated() bool {\n\treturn s.Action == SettingActionCreate\n}\n\n// IsModified 判断配置是否已修改\nfunc (s *Setting) IsModified() bool {\n\treturn s.Action == SettingActionModify\n}\n\n// SettingRepository 配置仓储接口\ntype SettingRepository interface {\n\t// GetByPathHash 根据路径哈希获取配置\n\tGetByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) (*Setting, error)\n\n\t// ListByPathHash 根据路径哈希获取配置列表（处理重复记录）\n\tListByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) ([]*Setting, error)\n\n\t// Create 创建配置\n\tCreate(ctx context.Context, setting *Setting, uid int64) (*Setting, error)\n\n\t// Update 更新配置\n\tUpdate(ctx context.Context, setting *Setting, uid int64) (*Setting, error)\n\n\t// UpdateMtime 更新配置修改时间\n\tUpdateMtime(ctx context.Context, mtime int64, id, uid int64) error\n\n\t// UpdateActionMtime 更新配置类型并修改时间\n\tUpdateActionMtime(ctx context.Context, action SettingAction, mtime int64, id, uid int64) error\n\n\t// Delete 物理删除配置\n\tDelete(ctx context.Context, id, uid int64) error\n\n\t// DeletePhysicalByTime 根据时间物理删除已标记删除的配置\n\tDeletePhysicalByTime(ctx context.Context, timestamp, uid int64) error\n\n\t// DeletePhysicalByTimeAll 根据时间物理删除所有用户的已标记删除的配置\n\tDeletePhysicalByTimeAll(ctx context.Context, timestamp int64) error\n\n\t// List 分页获取配置列表\n\tList(ctx context.Context, vaultID int64, page, pageSize int, uid int64, keyword string) ([]*Setting, error)\n\n\t// ListCount 获取配置数量\n\tListCount(ctx context.Context, vaultID, uid int64, keyword string) (int64, error)\n\n\t// ListByUpdatedTimestamp 根据更新时间戳获取配置列表\n\tListByUpdatedTimestamp(ctx context.Context, timestamp, vaultID, uid int64) ([]*Setting, error)\n\n\t// DeleteByVaultID 物理删除该用户指定笔记本的所有配置\n\tDeleteByVaultID(ctx context.Context, vaultID, uid int64) error\n}\n\n"
  },
  {
    "path": "internal/domain/domain_storage.go",
    "content": "package domain\n\nimport (\n\t\"context\"\n\t\"time\"\n)\n\n\n// Storage 存储配置领域模型\ntype Storage struct {\n\tID              int64\n\tUID             int64\n\tType            string\n\tEndpoint        string\n\tRegion          string\n\tAccountID       string\n\tBucketName      string\n\tAccessKeyID     string\n\tAccessKeySecret string\n\tCustomPath      string\n\tAccessURLPrefix string\n\tUser            string\n\tPassword        string\n\tIsEnabled       bool\n\tIsDeleted       bool\n\tCreatedAt       time.Time\n\tUpdatedAt       time.Time\n}\n\n// StorageRepository 存储仓储接口\ntype StorageRepository interface {\n\t// GetByID 根据ID获取存储配置\n\tGetByID(ctx context.Context, id, uid int64) (*Storage, error)\n\n\t// Create 创建存储配置\n\tCreate(ctx context.Context, storage *Storage, uid int64) (*Storage, error)\n\n\t// Update 更新存储配置\n\tUpdate(ctx context.Context, storage *Storage, uid int64) (*Storage, error)\n\n\t// List 获取用户的存储配置列表\n\tList(ctx context.Context, uid int64) ([]*Storage, error)\n\n\t// Delete 删除存储配置（软删除）\n\tDelete(ctx context.Context, id, uid int64) error\n}\n\n"
  },
  {
    "path": "internal/domain/domain_sync_log.go",
    "content": "// Package domain defines the core business domain models and repository interfaces\n// Package domain 定义核心业务领域模型和仓储接口\npackage domain\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n)\n\n// SyncLogType represents the type of resource being synchronized\n// SyncLogType 表示同步的资源类型\ntype SyncLogType string\n\n// SyncLogAction represents the type of synchronization action\n// SyncLogAction 表示同步操作类型\ntype SyncLogAction string\n\nconst (\n\t// SyncLogTypeNote represents a note resource\n\t// SyncLogTypeNote 表示笔记资源\n\tSyncLogTypeNote SyncLogType = \"note\"\n\n\t// SyncLogTypeFile represents a file (attachment) resource\n\t// SyncLogTypeFile 表示文件（附件）资源\n\tSyncLogTypeFile SyncLogType = \"file\"\n\n\t// SyncLogTypeSetting represents a configuration resource\n\t// SyncLogTypeSetting 表示配置资源\n\tSyncLogTypeSetting SyncLogType = \"setting\"\n\n\t// SyncLogTypeFolder represents a folder resource\n\t// SyncLogTypeFolder 表示文件夹资源\n\tSyncLogTypeFolder SyncLogType = \"folder\"\n\n\t// SyncLogActionCreate represents a create action\n\t// SyncLogActionCreate 表示新建操作\n\tSyncLogActionCreate SyncLogAction = \"create\"\n\n\t// SyncLogActionModify represents a modify action (content or mtime changed)\n\t// SyncLogActionModify 表示修改操作（内容或时间戳变更）\n\tSyncLogActionModify SyncLogAction = \"modify\"\n\n\t// SyncLogActionSoftDelete represents moving a resource to the recycle bin\n\t// SyncLogActionSoftDelete 表示将资源移至回收站（软删除）\n\tSyncLogActionSoftDelete SyncLogAction = \"soft_delete\"\n\n\t// SyncLogActionDelete represents permanently deleting a resource from the recycle bin\n\t// SyncLogActionDelete 表示从回收站彻底删除（物理删除）\n\tSyncLogActionDelete SyncLogAction = \"delete\"\n\n\t// SyncLogActionRename represents renaming a resource\n\t// SyncLogActionRename 表示重命名操作\n\tSyncLogActionRename SyncLogAction = \"rename\"\n\n\t// SyncLogActionRestore represents restoring a resource from the recycle bin\n\t// SyncLogActionRestore 表示从回收站恢复\n\tSyncLogActionRestore SyncLogAction = \"restore\"\n)\n\n// SyncLog represents a synchronization log entry\n// SyncLog 同步日志领域模型\ntype SyncLog struct {\n\tID            int64         // Record ID // 记录 ID\n\tUID           int64         // User ID // 用户 ID\n\tVaultID       int64         // Vault ID // 笔记本 ID\n\tType          SyncLogType   // Resource type: note / file / setting // 资源类型\n\tAction        SyncLogAction // Action type: create / modify / soft_delete / delete / rename / restore // 操作类型\n\tChangedFields string        // Comma-separated changed fields, e.g. \"content,mtime\" / \"mtime\" / \"path\" // 逗号分隔的变更字段\n\tPath          string        // Resource path // 资源路径\n\tPathHash      string        // Resource path hash // 资源路径哈希\n\tSize          int64         // Resource size in bytes // 资源大小（字节）\n\tClientName    string        // Client name that initiated the sync // 发起同步的客户端名称\n\tClientType    string        // Client type // 客户端类型\n\tClientVersion string        // Client version // 客户端版本\n\tStatus        int           // 1: success, 2: failed // 状态：1 成功，2 失败\n\tMessage       string        // Additional message or error detail // 附加消息或错误详情\n\tCreatedAt     timex.Time    // Log creation time // 日志创建时间\n}\n\n// SyncLogRepository defines the data access interface for sync logs\n// SyncLogRepository 定义同步日志的数据访问接口\ntype SyncLogRepository interface {\n\t// Create stores a new sync log entry\n\t// Create 存储一条新的同步日志\n\tCreate(ctx context.Context, log *SyncLog, uid int64) error\n\n\t// List retrieves sync logs for a user with filtering and pagination\n\t// List 按条件分页查询用户的同步日志\n\tList(ctx context.Context, uid int64, logType, action string, page, pageSize int) ([]*SyncLog, int64, error)\n\n\t// CleanupByTime removes sync logs older than the given timestamp for a specific user\n\t// CleanupByTime 清理指定用户在指定时间戳之前的同步日志\n\tCleanupByTime(ctx context.Context, timestamp int64, uid int64) error\n\n\t// CleanupByTimeAll removes sync logs older than the given timestamp for all users\n\t// CleanupByTimeAll 清理所有用户在指定时间戳之前的同步日志\n\tCleanupByTimeAll(ctx context.Context, timestamp int64) error\n\n\t// DeleteByVaultID 删除指定仓库的所有同步日志\n\tDeleteByVaultID(ctx context.Context, vaultID, uid int64) error\n}\n"
  },
  {
    "path": "internal/domain/domain_user.go",
    "content": "package domain\n\nimport (\n\t\"context\"\n\t\"time\"\n)\n\n\n// User 用户领域模型\ntype User struct {\n\tUID       int64\n\tEmail     string\n\tUsername  string\n\tPassword  string\n\tSalt      string\n\tToken     string\n\tAvatar    string\n\tIsDeleted bool\n\tCreatedAt time.Time\n\tUpdatedAt time.Time\n\tDeletedAt time.Time\n}\n\n// HasEmail 判断用户是否有邮箱\nfunc (u *User) HasEmail() bool {\n\treturn u.Email != \"\"\n}\n\n// HasAvatar 判断用户是否有头像\nfunc (u *User) HasAvatar() bool {\n\treturn u.Avatar != \"\"\n}\n\n// IsActive 判断用户是否活跃（未删除）\nfunc (u *User) IsActive() bool {\n\treturn !u.IsDeleted\n}\n\n// UserRepository 用户仓储接口\ntype UserRepository interface {\n\t// GetByUID 根据UID获取用户\n\tGetByUID(ctx context.Context, uid int64) (*User, error)\n\n\t// GetByEmail 根据邮箱获取用户\n\tGetByEmail(ctx context.Context, email string) (*User, error)\n\n\t// GetByUsername 根据用户名获取用户\n\tGetByUsername(ctx context.Context, username string) (*User, error)\n\n\t// Create 创建用户\n\tCreate(ctx context.Context, user *User) (*User, error)\n\n\t// UpdatePassword 更新用户密码\n\tUpdatePassword(ctx context.Context, password string, uid int64) error\n\n\t// GetAllUIDs 获取所有用户UID\n\tGetAllUIDs(ctx context.Context) ([]int64, error)\n}\n\n"
  },
  {
    "path": "internal/domain/domain_user_share.go",
    "content": "package domain\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"time\"\n)\n\nvar (\n\tErrShareCancelled        = errors.New(\"share has been cancelled\")\n\tErrShareExpired          = errors.New(\"share has expired\")\n\tErrSharePasswordRequired = errors.New(\"share password required\")\n\tErrSharePasswordInvalid  = errors.New(\"share password invalid\")\n)\n\nconst (\n\tUserShareStatusActive  int64 = 1 // 有效\n\tUserShareStatusRevoked int64 = 2 // 已撤销\n)\n\n// UserShare 笔记分享领域模型\ntype UserShare struct {\n\tID           int64               `json:\"id\"`\n\tUID          int64               `json:\"uid\"`            // 创建者 ID\n\tResType      string              `json:\"res_type\"`       // 资源类型: note, file\n\tResID        int64               `json:\"res_id\"`         // 资源 ID (note.id 或 file.id)\n\tResources    map[string][]string `json:\"res\"`            // 资源授权列表 (JSON: {\"note\":[\"id1\"],\"file\":[\"id2\"]})\n\tStatus       int64               `json:\"status\"`         // 状态: 1-有效, 2-已撤销\n\tViewCount    int64               `json:\"view_count\"`     // 统计：访问次数\n\tLastViewedAt time.Time           `json:\"last_viewed_at\"` // 统计：最后访问时间\n\tExpiresAt    time.Time           `json:\"expires_at\"`     // 过期时间\n\tPassword     string              `json:\"-\"`              // 分享密码 (MD5)\n\tShortLink    string              `json:\"short_link\"`     // 短链接\n\tCreatedAt    time.Time           `json:\"created_at\"`\n\tUpdatedAt    time.Time           `json:\"updated_at\"`\n}\n\n// UserShareRepository 用户分享持久化接口\ntype UserShareRepository interface {\n\tCreate(ctx context.Context, uid int64, share *UserShare) error\n\tGetByID(ctx context.Context, uid int64, id int64) (*UserShare, error)\n\tGetByPath(ctx context.Context, uid int64, vaultID int64, pathHash string) (*UserShare, error)\n\tGetByRes(ctx context.Context, uid int64, resType string, resID int64) (*UserShare, error)\n\tUpdateResources(ctx context.Context, uid int64, id int64, resources map[string][]string) error\n\tUpdateStatus(ctx context.Context, uid int64, id int64, status int64) error\n\tUpdateStatusByRes(ctx context.Context, uid int64, resType string, resID int64, status int64) error\n\tUpdateViewStats(ctx context.Context, uid int64, id int64, viewCountIncr int64, lastViewedAt time.Time) error\n\tUpdatePassword(ctx context.Context, uid int64, id int64, password string) error\n\tUpdateShortLink(ctx context.Context, uid int64, id int64, shortLink string) error\n\tListByUID(ctx context.Context, uid int64, sortBy string, sortOrder string, offset, limit int) ([]*UserShare, error)\n\tCountByUID(ctx context.Context, uid int64) (int64, error)\n\t// ListActiveNoteResIDs returns note res_ids for all active shares of a user\n\t// ListActiveNoteResIDs 返回该用户所有有效分享中 res_type='note' 的 res_id 列表\n\tListActiveNoteResIDs(ctx context.Context, uid int64) ([]int64, error)\n\n\t// ListChangedNoteResIDs returns res_ids of note shares whose status changed after since,\n\t// split into active (added) and revoked (removed) slices.\n\t// ListChangedNoteResIDs 返回 updated_at > since 的 note 分享记录的 res_id，\n\t// 按状态分为 active（新增）和 revoked（取消）两组。\n\tListChangedNoteResIDs(ctx context.Context, uid int64, since time.Time) (active []int64, revoked []int64, err error)\n\t// MigrateResID updates res_id and resources JSON for a share when a note/file is renamed.\n\t// MigrateResID 在笔记/文件重命名时更新分享记录的资源 ID 和资源列表。\n\tMigrateResID(ctx context.Context, uid int64, oldResID int64, newResID int64) error\n\n\t// DeleteByVaultID deletes all shares belonging to a vault (notes/files in that vault)\n\t// DeleteByVaultID 删除属于该仓库的所有分享记录（仓库下的笔记或文件）\n\tDeleteByVaultID(ctx context.Context, vaultID, uid int64) error\n}\n"
  },
  {
    "path": "internal/domain/domain_vault.go",
    "content": "package domain\n\nimport (\n\t\"context\"\n\t\"time\"\n)\n\n\n// Vault 仓库领域模型\ntype Vault struct {\n\tID        int64\n\tUID       int64\n\tName      string\n\tNoteCount int64\n\tNoteSize  int64\n\tFileCount int64\n\tFileSize  int64\n\tIsDeleted bool\n\tCreatedAt time.Time\n\tUpdatedAt time.Time\n}\n\n// IsEmpty 判断仓库是否为空\nfunc (v *Vault) IsEmpty() bool {\n\treturn v.NoteCount == 0 && v.FileCount == 0\n}\n\n// TotalSize 获取仓库总大小\nfunc (v *Vault) TotalSize() int64 {\n\treturn v.NoteSize + v.FileSize\n}\n\n// TotalCount 获取仓库总数量\nfunc (v *Vault) TotalCount() int64 {\n\treturn v.NoteCount + v.FileCount\n}\n\n// VaultRepository 仓库仓储接口\ntype VaultRepository interface {\n\t// GetByID 根据ID获取仓库\n\tGetByID(ctx context.Context, id, uid int64) (*Vault, error)\n\n\t// GetByName 根据名称获取仓库\n\tGetByName(ctx context.Context, name string, uid int64) (*Vault, error)\n\n\t// Create 创建仓库\n\tCreate(ctx context.Context, vault *Vault, uid int64) (*Vault, error)\n\n\t// Update 更新仓库\n\tUpdate(ctx context.Context, vault *Vault, uid int64) error\n\n\t// UpdateNoteCountSize 更新仓库的笔记数量和大小\n\tUpdateNoteCountSize(ctx context.Context, noteSize, noteCount, vaultID, uid int64) error\n\n\t// UpdateFileCountSize 更新仓库的文件数量和大小\n\tUpdateFileCountSize(ctx context.Context, fileSize, fileCount, vaultID, uid int64) error\n\n\t// List 获取仓库列表\n\tList(ctx context.Context, uid int64) ([]*Vault, error)\n\n\t// Delete 删除仓库（软删除）\n\tDelete(ctx context.Context, id, uid int64) error\n}\n\n"
  },
  {
    "path": "internal/domain/mocks/mock_backup_repository.go",
    "content": "// Package mocks provides testify/mock implementations for domain Repository interfaces.\n// Package mocks 提供 domain Repository 接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockBackupRepository is a testify mock for domain.BackupRepository.\n// MockBackupRepository 是 domain.BackupRepository 的 testify mock 实现。\ntype MockBackupRepository struct {\n\tmock.Mock\n}\n\nfunc (m *MockBackupRepository) ListConfigs(ctx context.Context, uid int64) ([]*domain.BackupConfig, error) {\n\targs := m.Called(ctx, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.BackupConfig), args.Error(1)\n}\n\nfunc (m *MockBackupRepository) GetByID(ctx context.Context, id, uid int64) (*domain.BackupConfig, error) {\n\targs := m.Called(ctx, id, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.BackupConfig), args.Error(1)\n}\n\nfunc (m *MockBackupRepository) DeleteConfig(ctx context.Context, id, uid int64) error {\n\targs := m.Called(ctx, id, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockBackupRepository) SaveConfig(ctx context.Context, config *domain.BackupConfig, uid int64) (*domain.BackupConfig, error) {\n\targs := m.Called(ctx, config, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.BackupConfig), args.Error(1)\n}\n\nfunc (m *MockBackupRepository) ListEnabledConfigs(ctx context.Context) ([]*domain.BackupConfig, error) {\n\targs := m.Called(ctx)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.BackupConfig), args.Error(1)\n}\n\nfunc (m *MockBackupRepository) UpdateNextRunTime(ctx context.Context, id, uid int64, nextRun time.Time) error {\n\targs := m.Called(ctx, id, uid, nextRun)\n\treturn args.Error(0)\n}\n\nfunc (m *MockBackupRepository) CreateHistory(ctx context.Context, history *domain.BackupHistory, uid int64) (*domain.BackupHistory, error) {\n\targs := m.Called(ctx, history, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.BackupHistory), args.Error(1)\n}\n\nfunc (m *MockBackupRepository) ListHistory(ctx context.Context, uid int64, configID int64, page, pageSize int) ([]*domain.BackupHistory, int64, error) {\n\targs := m.Called(ctx, uid, configID, page, pageSize)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Get(1).(int64), args.Error(2)\n\t}\n\treturn args.Get(0).([]*domain.BackupHistory), args.Get(1).(int64), args.Error(2)\n}\n\nfunc (m *MockBackupRepository) ListOldHistory(ctx context.Context, uid int64, configID int64, cutoffTime time.Time) ([]*domain.BackupHistory, error) {\n\targs := m.Called(ctx, uid, configID, cutoffTime)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.BackupHistory), args.Error(1)\n}\n\nfunc (m *MockBackupRepository) DeleteOldHistory(ctx context.Context, uid int64, configID int64, cutoffTime time.Time) error {\n\targs := m.Called(ctx, uid, configID, cutoffTime)\n\treturn args.Error(0)\n}\n\nfunc (m *MockBackupRepository) DisableByVaultID(ctx context.Context, vaultID, uid int64) error {\n\targs := m.Called(ctx, vaultID, uid)\n\treturn args.Error(0)\n}\n\n// Compile-time check: MockBackupRepository must implement domain.BackupRepository.\n// 编译时检查：MockBackupRepository 必须实现 domain.BackupRepository 接口。\nvar _ domain.BackupRepository = (*MockBackupRepository)(nil)\n"
  },
  {
    "path": "internal/domain/mocks/mock_file_repository.go",
    "content": "// Package mocks provides testify/mock implementations for domain Repository interfaces.\n// Package mocks 提供 domain Repository 接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockFileRepository is a testify mock for domain.FileRepository.\n// MockFileRepository 是 domain.FileRepository 的 testify mock 实现。\ntype MockFileRepository struct {\n\tmock.Mock\n}\n\nfunc (m *MockFileRepository) GetByID(ctx context.Context, id, uid int64) (*domain.File, error) {\n\targs := m.Called(ctx, id, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.File), args.Error(1)\n}\n\nfunc (m *MockFileRepository) GetByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) (*domain.File, error) {\n\targs := m.Called(ctx, pathHash, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.File), args.Error(1)\n}\n\nfunc (m *MockFileRepository) ListByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) ([]*domain.File, error) {\n\targs := m.Called(ctx, pathHash, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.File), args.Error(1)\n}\n\nfunc (m *MockFileRepository) GetByPath(ctx context.Context, path string, vaultID, uid int64) (*domain.File, error) {\n\targs := m.Called(ctx, path, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.File), args.Error(1)\n}\n\nfunc (m *MockFileRepository) GetByPathLike(ctx context.Context, path string, vaultID, uid int64) (*domain.File, error) {\n\targs := m.Called(ctx, path, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.File), args.Error(1)\n}\n\nfunc (m *MockFileRepository) Create(ctx context.Context, file *domain.File, uid int64) (*domain.File, error) {\n\targs := m.Called(ctx, file, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.File), args.Error(1)\n}\n\nfunc (m *MockFileRepository) Update(ctx context.Context, file *domain.File, uid int64) (*domain.File, error) {\n\targs := m.Called(ctx, file, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.File), args.Error(1)\n}\n\nfunc (m *MockFileRepository) UpdateMtime(ctx context.Context, mtime int64, id, uid int64) error {\n\targs := m.Called(ctx, mtime, id, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockFileRepository) UpdateActionMtime(ctx context.Context, action domain.FileAction, mtime int64, id, uid int64) error {\n\targs := m.Called(ctx, action, mtime, id, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockFileRepository) UpdateFID(ctx context.Context, id, fid, uid int64) error {\n\targs := m.Called(ctx, id, fid, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockFileRepository) Delete(ctx context.Context, id, uid int64) error {\n\targs := m.Called(ctx, id, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockFileRepository) DeletePhysicalByTime(ctx context.Context, timestamp, uid int64) error {\n\targs := m.Called(ctx, timestamp, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockFileRepository) DeletePhysicalByTimeAll(ctx context.Context, timestamp int64) error {\n\targs := m.Called(ctx, timestamp)\n\treturn args.Error(0)\n}\n\nfunc (m *MockFileRepository) List(ctx context.Context, vaultID int64, page, pageSize int, uid int64, keyword string, isRecycle bool, sortBy string, sortOrder string) ([]*domain.File, error) {\n\targs := m.Called(ctx, vaultID, page, pageSize, uid, keyword, isRecycle, sortBy, sortOrder)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.File), args.Error(1)\n}\n\nfunc (m *MockFileRepository) ListCount(ctx context.Context, vaultID, uid int64, keyword string, isRecycle bool) (int64, error) {\n\targs := m.Called(ctx, vaultID, uid, keyword, isRecycle)\n\treturn args.Get(0).(int64), args.Error(1)\n}\n\nfunc (m *MockFileRepository) ListByUpdatedTimestamp(ctx context.Context, timestamp, vaultID, uid int64) ([]*domain.File, error) {\n\targs := m.Called(ctx, timestamp, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.File), args.Error(1)\n}\n\nfunc (m *MockFileRepository) ListByUpdatedTimestampPage(ctx context.Context, timestamp, vaultID, uid int64, offset, limit int) ([]*domain.File, error) {\n\targs := m.Called(ctx, timestamp, vaultID, uid, offset, limit)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.File), args.Error(1)\n}\n\nfunc (m *MockFileRepository) ListByMtime(ctx context.Context, timestamp, vaultID, uid int64) ([]*domain.File, error) {\n\targs := m.Called(ctx, timestamp, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.File), args.Error(1)\n}\n\nfunc (m *MockFileRepository) CountSizeSum(ctx context.Context, vaultID, uid int64) (*domain.CountSizeResult, error) {\n\targs := m.Called(ctx, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.CountSizeResult), args.Error(1)\n}\n\nfunc (m *MockFileRepository) ListByFID(ctx context.Context, fid, vaultID, uid int64, page, pageSize int, sortBy, sortOrder string) ([]*domain.File, error) {\n\targs := m.Called(ctx, fid, vaultID, uid, page, pageSize, sortBy, sortOrder)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.File), args.Error(1)\n}\n\nfunc (m *MockFileRepository) ListByFIDCount(ctx context.Context, fid, vaultID, uid int64) (int64, error) {\n\targs := m.Called(ctx, fid, vaultID, uid)\n\treturn args.Get(0).(int64), args.Error(1)\n}\n\nfunc (m *MockFileRepository) ListByFIDs(ctx context.Context, fids []int64, vaultID, uid int64, page, pageSize int, sortBy, sortOrder string) ([]*domain.File, error) {\n\targs := m.Called(ctx, fids, vaultID, uid, page, pageSize, sortBy, sortOrder)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.File), args.Error(1)\n}\n\nfunc (m *MockFileRepository) ListByFIDsCount(ctx context.Context, fids []int64, vaultID, uid int64) (int64, error) {\n\targs := m.Called(ctx, fids, vaultID, uid)\n\treturn args.Get(0).(int64), args.Error(1)\n}\n\nfunc (m *MockFileRepository) ListByIDs(ctx context.Context, ids []int64, uid int64) ([]*domain.File, error) {\n\targs := m.Called(ctx, ids, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.File), args.Error(1)\n}\n\nfunc (m *MockFileRepository) ListByPathPrefix(ctx context.Context, pathPrefix string, vaultID, uid int64) ([]*domain.File, error) {\n\targs := m.Called(ctx, pathPrefix, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.File), args.Error(1)\n}\n\nfunc (m *MockFileRepository) DeleteByVaultID(ctx context.Context, vaultID, uid int64) error {\n\targs := m.Called(ctx, vaultID, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockFileRepository) RecycleClear(ctx context.Context, path, pathHash string, vaultID, uid int64) error {\n\targs := m.Called(ctx, path, pathHash, vaultID, uid)\n\treturn args.Error(0)\n}\n\n// Compile-time check: MockFileRepository must implement domain.FileRepository.\n// 编译时检查：MockFileRepository 必须实现 domain.FileRepository 接口。\nvar _ domain.FileRepository = (*MockFileRepository)(nil)\n"
  },
  {
    "path": "internal/domain/mocks/mock_folder_repository.go",
    "content": "// Package mocks provides testify/mock implementations for domain Repository interfaces.\n// Package mocks 提供 domain Repository 接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockFolderRepository is a testify mock for domain.FolderRepository.\n// MockFolderRepository 是 domain.FolderRepository 的 testify mock 实现。\ntype MockFolderRepository struct {\n\tmock.Mock\n}\n\nfunc (m *MockFolderRepository) GetByID(ctx context.Context, id, uid int64) (*domain.Folder, error) {\n\targs := m.Called(ctx, id, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Folder), args.Error(1)\n}\n\nfunc (m *MockFolderRepository) GetByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) (*domain.Folder, error) {\n\targs := m.Called(ctx, pathHash, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Folder), args.Error(1)\n}\n\nfunc (m *MockFolderRepository) GetAllByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) ([]*domain.Folder, error) {\n\targs := m.Called(ctx, pathHash, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.Folder), args.Error(1)\n}\n\nfunc (m *MockFolderRepository) GetByFID(ctx context.Context, fid int64, vaultID, uid int64) ([]*domain.Folder, error) {\n\targs := m.Called(ctx, fid, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.Folder), args.Error(1)\n}\n\nfunc (m *MockFolderRepository) Create(ctx context.Context, folder *domain.Folder, uid int64) (*domain.Folder, error) {\n\targs := m.Called(ctx, folder, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Folder), args.Error(1)\n}\n\nfunc (m *MockFolderRepository) Update(ctx context.Context, folder *domain.Folder, uid int64) (*domain.Folder, error) {\n\targs := m.Called(ctx, folder, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Folder), args.Error(1)\n}\n\nfunc (m *MockFolderRepository) Delete(ctx context.Context, id, uid int64) error {\n\targs := m.Called(ctx, id, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockFolderRepository) ListByUpdatedTimestamp(ctx context.Context, timestamp, vaultID, uid int64) ([]*domain.Folder, error) {\n\targs := m.Called(ctx, timestamp, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.Folder), args.Error(1)\n}\n\nfunc (m *MockFolderRepository) List(ctx context.Context, vaultID int64, uid int64) ([]*domain.Folder, error) {\n\targs := m.Called(ctx, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.Folder), args.Error(1)\n}\n\nfunc (m *MockFolderRepository) ListAll(ctx context.Context, uid int64) ([]*domain.Folder, error) {\n\targs := m.Called(ctx, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.Folder), args.Error(1)\n}\n\nfunc (m *MockFolderRepository) DeleteByVaultID(ctx context.Context, vaultID, uid int64) error {\n\targs := m.Called(ctx, vaultID, uid)\n\treturn args.Error(0)\n}\n\n// Compile-time check: MockFolderRepository must implement domain.FolderRepository.\n// 编译时检查：MockFolderRepository 必须实现 domain.FolderRepository 接口。\nvar _ domain.FolderRepository = (*MockFolderRepository)(nil)\n"
  },
  {
    "path": "internal/domain/mocks/mock_git_sync_repository.go",
    "content": "// Package mocks provides testify/mock implementations for domain Repository interfaces.\n// Package mocks 提供 domain Repository 接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockGitSyncRepository is a testify mock for domain.GitSyncRepository.\n// MockGitSyncRepository 是 domain.GitSyncRepository 的 testify mock 实现。\ntype MockGitSyncRepository struct {\n\tmock.Mock\n}\n\nfunc (m *MockGitSyncRepository) GetByID(ctx context.Context, id, uid int64) (*domain.GitSyncConfig, error) {\n\targs := m.Called(ctx, id, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.GitSyncConfig), args.Error(1)\n}\n\nfunc (m *MockGitSyncRepository) GetByVaultID(ctx context.Context, vaultID, uid int64) (*domain.GitSyncConfig, error) {\n\targs := m.Called(ctx, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.GitSyncConfig), args.Error(1)\n}\n\nfunc (m *MockGitSyncRepository) Save(ctx context.Context, config *domain.GitSyncConfig, uid int64) (*domain.GitSyncConfig, error) {\n\targs := m.Called(ctx, config, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.GitSyncConfig), args.Error(1)\n}\n\nfunc (m *MockGitSyncRepository) Delete(ctx context.Context, id, uid int64) error {\n\targs := m.Called(ctx, id, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockGitSyncRepository) List(ctx context.Context, uid int64) ([]*domain.GitSyncConfig, error) {\n\targs := m.Called(ctx, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.GitSyncConfig), args.Error(1)\n}\n\nfunc (m *MockGitSyncRepository) ListByVaultID(ctx context.Context, vaultID, uid int64) ([]*domain.GitSyncConfig, error) {\n\targs := m.Called(ctx, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.GitSyncConfig), args.Error(1)\n}\n\nfunc (m *MockGitSyncRepository) ListEnabled(ctx context.Context) ([]*domain.GitSyncConfig, error) {\n\targs := m.Called(ctx)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.GitSyncConfig), args.Error(1)\n}\n\nfunc (m *MockGitSyncRepository) CreateHistory(ctx context.Context, history *domain.GitSyncHistory, uid int64) (*domain.GitSyncHistory, error) {\n\targs := m.Called(ctx, history, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.GitSyncHistory), args.Error(1)\n}\n\nfunc (m *MockGitSyncRepository) ListHistory(ctx context.Context, uid int64, configID int64, page, pageSize int) ([]*domain.GitSyncHistory, int64, error) {\n\targs := m.Called(ctx, uid, configID, page, pageSize)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Get(1).(int64), args.Error(2)\n\t}\n\treturn args.Get(0).([]*domain.GitSyncHistory), args.Get(1).(int64), args.Error(2)\n}\n\nfunc (m *MockGitSyncRepository) DeleteHistory(ctx context.Context, uid int64, configID int64) error {\n\targs := m.Called(ctx, uid, configID)\n\treturn args.Error(0)\n}\n\nfunc (m *MockGitSyncRepository) DeleteOldHistory(ctx context.Context, uid int64, configID int64, cutoffTime time.Time) error {\n\targs := m.Called(ctx, uid, configID, cutoffTime)\n\treturn args.Error(0)\n}\n\nfunc (m *MockGitSyncRepository) DisableByVaultID(ctx context.Context, vaultID, uid int64) error {\n\targs := m.Called(ctx, vaultID, uid)\n\treturn args.Error(0)\n}\n\n// Compile-time check: MockGitSyncRepository must implement domain.GitSyncRepository.\n// 编译时检查：MockGitSyncRepository 必须实现 domain.GitSyncRepository 接口。\nvar _ domain.GitSyncRepository = (*MockGitSyncRepository)(nil)\n"
  },
  {
    "path": "internal/domain/mocks/mock_note_fts_repository.go",
    "content": "package mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockNoteFTSRepository is a testify mock for domain.NoteFTSRepository.\ntype MockNoteFTSRepository struct {\n\tmock.Mock\n}\n\nfunc (m *MockNoteFTSRepository) Upsert(ctx context.Context, noteID int64, path, content string, uid int64) error {\n\targs := m.Called(ctx, noteID, path, content, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteFTSRepository) Delete(ctx context.Context, noteID int64, uid int64) error {\n\targs := m.Called(ctx, noteID, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteFTSRepository) Search(ctx context.Context, keyword string, vaultID, uid int64, limit, offset int) ([]int64, error) {\n\targs := m.Called(ctx, keyword, vaultID, uid, limit, offset)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]int64), args.Error(1)\n}\n\nfunc (m *MockNoteFTSRepository) SearchCount(ctx context.Context, keyword string, vaultID, uid int64) (int64, error) {\n\targs := m.Called(ctx, keyword, vaultID, uid)\n\treturn args.Get(0).(int64), args.Error(1)\n}\n\nfunc (m *MockNoteFTSRepository) RebuildIndex(ctx context.Context, uid int64) error {\n\targs := m.Called(ctx, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteFTSRepository) DeleteByVaultID(ctx context.Context, vaultID, uid int64) error {\n\targs := m.Called(ctx, vaultID, uid)\n\treturn args.Error(0)\n}\n\nvar _ domain.NoteFTSRepository = (*MockNoteFTSRepository)(nil)\n"
  },
  {
    "path": "internal/domain/mocks/mock_note_history_repository.go",
    "content": "// Package mocks provides testify/mock implementations for domain Repository interfaces.\n// Package mocks 提供 domain Repository 接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockNoteHistoryRepository is a testify mock for domain.NoteHistoryRepository.\n// MockNoteHistoryRepository 是 domain.NoteHistoryRepository 的 testify mock 实现。\ntype MockNoteHistoryRepository struct {\n\tmock.Mock\n}\n\nfunc (m *MockNoteHistoryRepository) GetByID(ctx context.Context, id, uid int64) (*domain.NoteHistory, error) {\n\targs := m.Called(ctx, id, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.NoteHistory), args.Error(1)\n}\n\nfunc (m *MockNoteHistoryRepository) GetByNoteIDAndHash(ctx context.Context, noteID int64, contentHash string, uid int64) (*domain.NoteHistory, error) {\n\targs := m.Called(ctx, noteID, contentHash, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.NoteHistory), args.Error(1)\n}\n\nfunc (m *MockNoteHistoryRepository) Create(ctx context.Context, history *domain.NoteHistory, uid int64) (*domain.NoteHistory, error) {\n\targs := m.Called(ctx, history, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.NoteHistory), args.Error(1)\n}\n\nfunc (m *MockNoteHistoryRepository) ListByNoteID(ctx context.Context, noteID int64, page, pageSize int, uid int64) ([]*domain.NoteHistory, int64, error) {\n\targs := m.Called(ctx, noteID, page, pageSize, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Get(1).(int64), args.Error(2)\n\t}\n\treturn args.Get(0).([]*domain.NoteHistory), args.Get(1).(int64), args.Error(2)\n}\n\nfunc (m *MockNoteHistoryRepository) GetLatestVersion(ctx context.Context, noteID, uid int64) (int64, error) {\n\targs := m.Called(ctx, noteID, uid)\n\treturn args.Get(0).(int64), args.Error(1)\n}\n\nfunc (m *MockNoteHistoryRepository) Migrate(ctx context.Context, oldNoteID, newNoteID, uid int64) error {\n\targs := m.Called(ctx, oldNoteID, newNoteID, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteHistoryRepository) GetNoteIDsWithOldHistory(ctx context.Context, cutoffTime int64, uid int64) ([]int64, error) {\n\targs := m.Called(ctx, cutoffTime, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]int64), args.Error(1)\n}\n\nfunc (m *MockNoteHistoryRepository) DeleteOldVersions(ctx context.Context, noteID int64, cutoffTime int64, keepVersions int, uid int64) error {\n\targs := m.Called(ctx, noteID, cutoffTime, keepVersions, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteHistoryRepository) Delete(ctx context.Context, id, uid int64) error {\n\targs := m.Called(ctx, id, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteHistoryRepository) DeleteByVaultID(ctx context.Context, vaultID, uid int64) error {\n\targs := m.Called(ctx, vaultID, uid)\n\treturn args.Error(0)\n}\n\n// Compile-time check: MockNoteHistoryRepository must implement domain.NoteHistoryRepository.\n// 编译时检查：MockNoteHistoryRepository 必须实现 domain.NoteHistoryRepository 接口。\nvar _ domain.NoteHistoryRepository = (*MockNoteHistoryRepository)(nil)\n"
  },
  {
    "path": "internal/domain/mocks/mock_note_link_repository.go",
    "content": "// Package mocks provides testify/mock implementations for domain Repository interfaces.\n// Package mocks 提供 domain Repository 接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockNoteLinkRepository is a testify mock for domain.NoteLinkRepository.\n// MockNoteLinkRepository 是 domain.NoteLinkRepository 的 testify mock 实现。\ntype MockNoteLinkRepository struct {\n\tmock.Mock\n}\n\nfunc (m *MockNoteLinkRepository) CreateBatch(ctx context.Context, links []*domain.NoteLink, uid int64) error {\n\targs := m.Called(ctx, links, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteLinkRepository) DeleteBySourceNoteID(ctx context.Context, sourceNoteID, uid int64) error {\n\targs := m.Called(ctx, sourceNoteID, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteLinkRepository) GetBacklinks(ctx context.Context, targetPathHash string, vaultID, uid int64) ([]*domain.NoteLink, error) {\n\targs := m.Called(ctx, targetPathHash, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.NoteLink), args.Error(1)\n}\n\nfunc (m *MockNoteLinkRepository) GetBacklinksByHashes(ctx context.Context, targetPathHashes []string, vaultID, uid int64) ([]*domain.NoteLink, error) {\n\targs := m.Called(ctx, targetPathHashes, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.NoteLink), args.Error(1)\n}\n\nfunc (m *MockNoteLinkRepository) GetOutlinks(ctx context.Context, sourceNoteID, uid int64) ([]*domain.NoteLink, error) {\n\targs := m.Called(ctx, sourceNoteID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.NoteLink), args.Error(1)\n}\n\nfunc (m *MockNoteLinkRepository) DeleteByVaultID(ctx context.Context, vaultID, uid int64) error {\n\targs := m.Called(ctx, vaultID, uid)\n\treturn args.Error(0)\n}\n\n// Compile-time check: MockNoteLinkRepository must implement domain.NoteLinkRepository.\n// 编译时检查：MockNoteLinkRepository 必须实现 domain.NoteLinkRepository 接口。\nvar _ domain.NoteLinkRepository = (*MockNoteLinkRepository)(nil)\n"
  },
  {
    "path": "internal/domain/mocks/mock_note_repository.go",
    "content": "// Package mocks provides testify/mock implementations for domain Repository interfaces.\n// Package mocks 提供 domain Repository 接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockNoteRepository is a testify mock for domain.NoteRepository.\n// MockNoteRepository 是 domain.NoteRepository 的 testify mock 实现。\ntype MockNoteRepository struct {\n\tmock.Mock\n}\n\nfunc (m *MockNoteRepository) GetByID(ctx context.Context, id, uid int64) (*domain.Note, error) {\n\targs := m.Called(ctx, id, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Note), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) GetByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) (*domain.Note, error) {\n\targs := m.Called(ctx, pathHash, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Note), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) GetByPathHashIncludeRecycle(ctx context.Context, pathHash string, vaultID, uid int64, isRecycle bool) (*domain.Note, error) {\n\targs := m.Called(ctx, pathHash, vaultID, uid, isRecycle)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Note), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) GetAllByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) (*domain.Note, error) {\n\targs := m.Called(ctx, pathHash, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Note), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) ListByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) ([]*domain.Note, error) {\n\targs := m.Called(ctx, pathHash, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.Note), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) GetByPath(ctx context.Context, path string, vaultID, uid int64) (*domain.Note, error) {\n\targs := m.Called(ctx, path, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Note), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) Create(ctx context.Context, note *domain.Note, uid int64) (*domain.Note, error) {\n\targs := m.Called(ctx, note, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Note), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) Update(ctx context.Context, note *domain.Note, uid int64) (*domain.Note, error) {\n\targs := m.Called(ctx, note, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Note), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) UpdateDelete(ctx context.Context, note *domain.Note, uid int64) error {\n\targs := m.Called(ctx, note, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteRepository) UpdateMtime(ctx context.Context, mtime int64, id, uid int64) error {\n\targs := m.Called(ctx, mtime, id, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteRepository) UpdateActionMtime(ctx context.Context, action domain.NoteAction, mtime int64, id, uid int64) error {\n\targs := m.Called(ctx, action, mtime, id, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteRepository) UpdateFID(ctx context.Context, id, fid, uid int64) error {\n\targs := m.Called(ctx, id, fid, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteRepository) UpdateSnapshot(ctx context.Context, snapshot, snapshotHash string, version, id, uid int64) error {\n\targs := m.Called(ctx, snapshot, snapshotHash, version, id, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteRepository) Delete(ctx context.Context, id, uid int64) error {\n\targs := m.Called(ctx, id, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteRepository) DeletePhysicalByTime(ctx context.Context, timestamp, uid int64) error {\n\targs := m.Called(ctx, timestamp, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteRepository) DeletePhysicalByTimeAll(ctx context.Context, timestamp int64) error {\n\targs := m.Called(ctx, timestamp)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteRepository) List(ctx context.Context, vaultID int64, page, pageSize int, uid int64, keyword string, isRecycle bool, searchMode string, searchContent bool, sortBy string, sortOrder string, paths []string) ([]*domain.Note, error) {\n\targs := m.Called(ctx, vaultID, page, pageSize, uid, keyword, isRecycle, searchMode, searchContent, sortBy, sortOrder, paths)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.Note), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) ListCount(ctx context.Context, vaultID, uid int64, keyword string, isRecycle bool, searchMode string, searchContent bool, paths []string) (int64, error) {\n\targs := m.Called(ctx, vaultID, uid, keyword, isRecycle, searchMode, searchContent, paths)\n\treturn args.Get(0).(int64), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) ListByUpdatedTimestamp(ctx context.Context, timestamp, vaultID, uid int64) ([]*domain.Note, error) {\n\targs := m.Called(ctx, timestamp, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.Note), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) ListByUpdatedTimestampPage(ctx context.Context, timestamp, vaultID, uid int64, offset, limit int) ([]*domain.Note, error) {\n\targs := m.Called(ctx, timestamp, vaultID, uid, offset, limit)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.Note), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) ListContentUnchanged(ctx context.Context, uid int64) ([]*domain.Note, error) {\n\targs := m.Called(ctx, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.Note), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) CountSizeSum(ctx context.Context, vaultID, uid int64) (*domain.CountSizeResult, error) {\n\targs := m.Called(ctx, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.CountSizeResult), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) ListByFID(ctx context.Context, fid, vaultID, uid int64, page, pageSize int, sortBy, sortOrder string) ([]*domain.Note, error) {\n\targs := m.Called(ctx, fid, vaultID, uid, page, pageSize, sortBy, sortOrder)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.Note), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) ListByFIDCount(ctx context.Context, fid, vaultID, uid int64) (int64, error) {\n\targs := m.Called(ctx, fid, vaultID, uid)\n\treturn args.Get(0).(int64), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) ListByFIDs(ctx context.Context, fids []int64, vaultID, uid int64, page, pageSize int, sortBy, sortOrder string) ([]*domain.Note, error) {\n\targs := m.Called(ctx, fids, vaultID, uid, page, pageSize, sortBy, sortOrder)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.Note), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) ListByFIDsCount(ctx context.Context, fids []int64, vaultID, uid int64) (int64, error) {\n\targs := m.Called(ctx, fids, vaultID, uid)\n\treturn args.Get(0).(int64), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) ListByIDs(ctx context.Context, ids []int64, uid int64) ([]*domain.Note, error) {\n\targs := m.Called(ctx, ids, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.Note), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) ListByPathPrefix(ctx context.Context, pathPrefix string, vaultID, uid int64) ([]*domain.Note, error) {\n\targs := m.Called(ctx, pathPrefix, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.Note), args.Error(1)\n}\n\nfunc (m *MockNoteRepository) RecycleClear(ctx context.Context, path, pathHash string, vaultID, uid int64) error {\n\targs := m.Called(path, pathHash, vaultID, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteRepository) DeleteByVaultID(ctx context.Context, vaultID, uid int64) error {\n\targs := m.Called(ctx, vaultID, uid)\n\treturn args.Error(0)\n}\n\n// Compile-time check: MockNoteRepository must implement domain.NoteRepository.\n// 编译时检查：MockNoteRepository 必须实现 domain.NoteRepository 接口。\nvar _ domain.NoteRepository = (*MockNoteRepository)(nil)\n\n// suppress unused import warning for time\nvar _ = time.Now\n"
  },
  {
    "path": "internal/domain/mocks/mock_setting_repository.go",
    "content": "// Package mocks provides testify/mock implementations for domain Repository interfaces.\n// Package mocks 提供 domain Repository 接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockSettingRepository is a testify mock for domain.SettingRepository.\n// MockSettingRepository 是 domain.SettingRepository 的 testify mock 实现。\ntype MockSettingRepository struct {\n\tmock.Mock\n}\n\nfunc (m *MockSettingRepository) GetByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) (*domain.Setting, error) {\n\targs := m.Called(ctx, pathHash, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Setting), args.Error(1)\n}\n\nfunc (m *MockSettingRepository) ListByPathHash(ctx context.Context, pathHash string, vaultID, uid int64) ([]*domain.Setting, error) {\n\targs := m.Called(ctx, pathHash, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.Setting), args.Error(1)\n}\n\nfunc (m *MockSettingRepository) Create(ctx context.Context, setting *domain.Setting, uid int64) (*domain.Setting, error) {\n\targs := m.Called(ctx, setting, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Setting), args.Error(1)\n}\n\nfunc (m *MockSettingRepository) Update(ctx context.Context, setting *domain.Setting, uid int64) (*domain.Setting, error) {\n\targs := m.Called(ctx, setting, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Setting), args.Error(1)\n}\n\nfunc (m *MockSettingRepository) UpdateMtime(ctx context.Context, mtime int64, id, uid int64) error {\n\targs := m.Called(ctx, mtime, id, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockSettingRepository) UpdateActionMtime(ctx context.Context, action domain.SettingAction, mtime int64, id, uid int64) error {\n\targs := m.Called(ctx, action, mtime, id, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockSettingRepository) Delete(ctx context.Context, id, uid int64) error {\n\targs := m.Called(ctx, id, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockSettingRepository) DeletePhysicalByTime(ctx context.Context, timestamp, uid int64) error {\n\targs := m.Called(ctx, timestamp, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockSettingRepository) DeletePhysicalByTimeAll(ctx context.Context, timestamp int64) error {\n\targs := m.Called(ctx, timestamp)\n\treturn args.Error(0)\n}\n\nfunc (m *MockSettingRepository) List(ctx context.Context, vaultID int64, page, pageSize int, uid int64, keyword string) ([]*domain.Setting, error) {\n\targs := m.Called(ctx, vaultID, page, pageSize, uid, keyword)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.Setting), args.Error(1)\n}\n\nfunc (m *MockSettingRepository) ListCount(ctx context.Context, vaultID, uid int64, keyword string) (int64, error) {\n\targs := m.Called(ctx, vaultID, uid, keyword)\n\treturn args.Get(0).(int64), args.Error(1)\n}\n\nfunc (m *MockSettingRepository) ListByUpdatedTimestamp(ctx context.Context, timestamp, vaultID, uid int64) ([]*domain.Setting, error) {\n\targs := m.Called(ctx, timestamp, vaultID, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.Setting), args.Error(1)\n}\n\nfunc (m *MockSettingRepository) DeleteByVaultID(ctx context.Context, vaultID, uid int64) error {\n\targs := m.Called(ctx, vaultID, uid)\n\treturn args.Error(0)\n}\n\n// Compile-time check: MockSettingRepository must implement domain.SettingRepository.\n// 编译时检查：MockSettingRepository 必须实现 domain.SettingRepository 接口。\nvar _ domain.SettingRepository = (*MockSettingRepository)(nil)\n"
  },
  {
    "path": "internal/domain/mocks/mock_storage_repository.go",
    "content": "// Package mocks provides testify/mock implementations for domain Repository interfaces.\n// Package mocks 提供 domain Repository 接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockStorageRepository is a testify mock for domain.StorageRepository.\n// MockStorageRepository 是 domain.StorageRepository 的 testify mock 实现。\ntype MockStorageRepository struct {\n\tmock.Mock\n}\n\nfunc (m *MockStorageRepository) GetByID(ctx context.Context, id, uid int64) (*domain.Storage, error) {\n\targs := m.Called(ctx, id, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Storage), args.Error(1)\n}\n\nfunc (m *MockStorageRepository) Create(ctx context.Context, storage *domain.Storage, uid int64) (*domain.Storage, error) {\n\targs := m.Called(ctx, storage, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Storage), args.Error(1)\n}\n\nfunc (m *MockStorageRepository) Update(ctx context.Context, storage *domain.Storage, uid int64) (*domain.Storage, error) {\n\targs := m.Called(ctx, storage, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Storage), args.Error(1)\n}\n\nfunc (m *MockStorageRepository) List(ctx context.Context, uid int64) ([]*domain.Storage, error) {\n\targs := m.Called(ctx, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.Storage), args.Error(1)\n}\n\nfunc (m *MockStorageRepository) Delete(ctx context.Context, id, uid int64) error {\n\targs := m.Called(ctx, id, uid)\n\treturn args.Error(0)\n}\n\n// Compile-time check: MockStorageRepository must implement domain.StorageRepository.\n// 编译时检查：MockStorageRepository 必须实现 domain.StorageRepository 接口。\nvar _ domain.StorageRepository = (*MockStorageRepository)(nil)\n"
  },
  {
    "path": "internal/domain/mocks/mock_sync_log_repository.go",
    "content": "package mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockSyncLogRepository is a testify mock for domain.SyncLogRepository.\ntype MockSyncLogRepository struct {\n\tmock.Mock\n}\n\nfunc (m *MockSyncLogRepository) Create(ctx context.Context, log *domain.SyncLog, uid int64) error {\n\targs := m.Called(ctx, log, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockSyncLogRepository) List(ctx context.Context, uid int64, logType, action string, page, pageSize int) ([]*domain.SyncLog, int64, error) {\n\targs := m.Called(ctx, uid, logType, action, page, pageSize)\n\tif args.Get(0) == nil {\n\t\treturn nil, 0, args.Error(2)\n\t}\n\treturn args.Get(0).([]*domain.SyncLog), args.Get(1).(int64), args.Error(2)\n}\n\nfunc (m *MockSyncLogRepository) CleanupByTime(ctx context.Context, timestamp int64, uid int64) error {\n\targs := m.Called(ctx, timestamp, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockSyncLogRepository) CleanupByTimeAll(ctx context.Context, timestamp int64) error {\n\targs := m.Called(ctx, timestamp)\n\treturn args.Error(0)\n}\n\nfunc (m *MockSyncLogRepository) DeleteByVaultID(ctx context.Context, vaultID, uid int64) error {\n\targs := m.Called(ctx, vaultID, uid)\n\treturn args.Error(0)\n}\n\nvar _ domain.SyncLogRepository = (*MockSyncLogRepository)(nil)\n"
  },
  {
    "path": "internal/domain/mocks/mock_user_repository.go",
    "content": "// Package mocks provides testify/mock implementations for domain Repository interfaces.\n// Package mocks 提供 domain Repository 接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockUserRepository is a testify mock for domain.UserRepository.\n// MockUserRepository 是 domain.UserRepository 的 testify mock 实现。\ntype MockUserRepository struct {\n\tmock.Mock\n}\n\n// GetByUID retrieves a user by UID.\n// GetByUID 根据 UID 获取用户。\nfunc (m *MockUserRepository) GetByUID(ctx context.Context, uid int64) (*domain.User, error) {\n\targs := m.Called(ctx, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.User), args.Error(1)\n}\n\n// GetByEmail retrieves a user by email.\n// GetByEmail 根据邮箱获取用户。\nfunc (m *MockUserRepository) GetByEmail(ctx context.Context, email string) (*domain.User, error) {\n\targs := m.Called(ctx, email)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.User), args.Error(1)\n}\n\n// GetByUsername retrieves a user by username.\n// GetByUsername 根据用户名获取用户。\nfunc (m *MockUserRepository) GetByUsername(ctx context.Context, username string) (*domain.User, error) {\n\targs := m.Called(ctx, username)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.User), args.Error(1)\n}\n\n// Create creates a new user.\n// Create 创建新用户。\nfunc (m *MockUserRepository) Create(ctx context.Context, user *domain.User) (*domain.User, error) {\n\targs := m.Called(ctx, user)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.User), args.Error(1)\n}\n\n// UpdatePassword updates the user's password.\n// UpdatePassword 更新用户密码。\nfunc (m *MockUserRepository) UpdatePassword(ctx context.Context, password string, uid int64) error {\n\targs := m.Called(ctx, password, uid)\n\treturn args.Error(0)\n}\n\n// GetAllUIDs retrieves all user UIDs.\n// GetAllUIDs 获取所有用户的 UID 列表。\nfunc (m *MockUserRepository) GetAllUIDs(ctx context.Context) ([]int64, error) {\n\targs := m.Called(ctx)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]int64), args.Error(1)\n}\n\n// Compile-time check: MockUserRepository must implement domain.UserRepository.\n// 编译时检查：MockUserRepository 必须实现 domain.UserRepository 接口。\nvar _ domain.UserRepository = (*MockUserRepository)(nil)\n"
  },
  {
    "path": "internal/domain/mocks/mock_user_share_repository.go",
    "content": "// Package mocks provides testify/mock implementations for domain Repository interfaces.\n// Package mocks 提供 domain Repository 接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockUserShareRepository is a testify mock for domain.UserShareRepository.\n// MockUserShareRepository 是 domain.UserShareRepository 的 testify mock 实现。\ntype MockUserShareRepository struct {\n\tmock.Mock\n}\n\nfunc (m *MockUserShareRepository) Create(ctx context.Context, uid int64, share *domain.UserShare) error {\n\targs := m.Called(ctx, uid, share)\n\treturn args.Error(0)\n}\n\nfunc (m *MockUserShareRepository) GetByID(ctx context.Context, uid int64, id int64) (*domain.UserShare, error) {\n\targs := m.Called(ctx, uid, id)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.UserShare), args.Error(1)\n}\n\nfunc (m *MockUserShareRepository) GetByPath(ctx context.Context, uid int64, vaultID int64, pathHash string) (*domain.UserShare, error) {\n\targs := m.Called(ctx, uid, vaultID, pathHash)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.UserShare), args.Error(1)\n}\n\nfunc (m *MockUserShareRepository) GetByRes(ctx context.Context, uid int64, resType string, resID int64) (*domain.UserShare, error) {\n\targs := m.Called(ctx, uid, resType, resID)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.UserShare), args.Error(1)\n}\n\nfunc (m *MockUserShareRepository) UpdateResources(ctx context.Context, uid int64, id int64, resources map[string][]string) error {\n\targs := m.Called(ctx, uid, id, resources)\n\treturn args.Error(0)\n}\n\nfunc (m *MockUserShareRepository) UpdateStatus(ctx context.Context, uid int64, id int64, status int64) error {\n\targs := m.Called(ctx, uid, id, status)\n\treturn args.Error(0)\n}\n\nfunc (m *MockUserShareRepository) UpdateStatusByRes(ctx context.Context, uid int64, resType string, resID int64, status int64) error {\n\targs := m.Called(ctx, uid, resType, resID, status)\n\treturn args.Error(0)\n}\n\nfunc (m *MockUserShareRepository) UpdateViewStats(ctx context.Context, uid int64, id int64, viewCountIncr int64, lastViewedAt time.Time) error {\n\targs := m.Called(ctx, uid, id, viewCountIncr, lastViewedAt)\n\treturn args.Error(0)\n}\n\nfunc (m *MockUserShareRepository) UpdatePassword(ctx context.Context, uid int64, id int64, password string) error {\n\targs := m.Called(ctx, uid, id, password)\n\treturn args.Error(0)\n}\n\nfunc (m *MockUserShareRepository) UpdateShortLink(ctx context.Context, uid int64, id int64, shortLink string) error {\n\targs := m.Called(ctx, uid, id, shortLink)\n\treturn args.Error(0)\n}\n\nfunc (m *MockUserShareRepository) ListByUID(ctx context.Context, uid int64, sortBy string, sortOrder string, offset, limit int) ([]*domain.UserShare, error) {\n\targs := m.Called(ctx, uid, sortBy, sortOrder, offset, limit)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.UserShare), args.Error(1)\n}\n\nfunc (m *MockUserShareRepository) CountByUID(ctx context.Context, uid int64) (int64, error) {\n\targs := m.Called(ctx, uid)\n\treturn args.Get(0).(int64), args.Error(1)\n}\n\nfunc (m *MockUserShareRepository) ListActiveNoteResIDs(ctx context.Context, uid int64) ([]int64, error) {\n\targs := m.Called(ctx, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]int64), args.Error(1)\n}\n\nfunc (m *MockUserShareRepository) ListChangedNoteResIDs(ctx context.Context, uid int64, since time.Time) (active []int64, revoked []int64, err error) {\n\targs := m.Called(ctx, uid, since)\n\tvar a, r []int64\n\tif args.Get(0) != nil {\n\t\ta = args.Get(0).([]int64)\n\t}\n\tif args.Get(1) != nil {\n\t\tr = args.Get(1).([]int64)\n\t}\n\treturn a, r, args.Error(2)\n}\n\nfunc (m *MockUserShareRepository) MigrateResID(ctx context.Context, uid int64, oldResID int64, newResID int64) error {\n\targs := m.Called(ctx, uid, oldResID, newResID)\n\treturn args.Error(0)\n}\n\nfunc (m *MockUserShareRepository) DeleteByVaultID(ctx context.Context, vaultID, uid int64) error {\n\targs := m.Called(ctx, vaultID, uid)\n\treturn args.Error(0)\n}\n\n// Compile-time check: MockUserShareRepository must implement domain.UserShareRepository.\n// 编译时检查：MockUserShareRepository 必须实现 domain.UserShareRepository 接口。\nvar _ domain.UserShareRepository = (*MockUserShareRepository)(nil)\n"
  },
  {
    "path": "internal/domain/mocks/mock_vault_repository.go",
    "content": "// Package mocks provides testify/mock implementations for domain Repository interfaces.\n// Package mocks 提供 domain Repository 接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockVaultRepository is a testify mock for domain.VaultRepository.\n// MockVaultRepository 是 domain.VaultRepository 的 testify mock 实现。\ntype MockVaultRepository struct {\n\tmock.Mock\n}\n\n// GetByID retrieves vault by ID and UID.\n// GetByID 根据 ID 和 UID 获取 Vault。\nfunc (m *MockVaultRepository) GetByID(ctx context.Context, id, uid int64) (*domain.Vault, error) {\n\targs := m.Called(ctx, id, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Vault), args.Error(1)\n}\n\n// GetByName retrieves vault by name and UID.\n// GetByName 根据名称和 UID 获取 Vault。\nfunc (m *MockVaultRepository) GetByName(ctx context.Context, name string, uid int64) (*domain.Vault, error) {\n\targs := m.Called(ctx, name, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Vault), args.Error(1)\n}\n\n// Create creates a new vault.\n// Create 创建新的 Vault。\nfunc (m *MockVaultRepository) Create(ctx context.Context, vault *domain.Vault, uid int64) (*domain.Vault, error) {\n\targs := m.Called(ctx, vault, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Vault), args.Error(1)\n}\n\n// Update updates an existing vault.\n// Update 更新现有的 Vault。\nfunc (m *MockVaultRepository) Update(ctx context.Context, vault *domain.Vault, uid int64) error {\n\targs := m.Called(ctx, vault, uid)\n\treturn args.Error(0)\n}\n\n// UpdateNoteCountSize updates note count and size for a vault.\n// UpdateNoteCountSize 更新 Vault 的笔记数量和大小。\nfunc (m *MockVaultRepository) UpdateNoteCountSize(ctx context.Context, noteSize, noteCount, vaultID, uid int64) error {\n\targs := m.Called(ctx, noteSize, noteCount, vaultID, uid)\n\treturn args.Error(0)\n}\n\n// UpdateFileCountSize updates file count and size for a vault.\n// UpdateFileCountSize 更新 Vault 的文件数量和大小。\nfunc (m *MockVaultRepository) UpdateFileCountSize(ctx context.Context, fileSize, fileCount, vaultID, uid int64) error {\n\targs := m.Called(ctx, fileSize, fileCount, vaultID, uid)\n\treturn args.Error(0)\n}\n\n// List retrieves all vaults for a user.\n// List 获取用户的所有 Vault 列表。\nfunc (m *MockVaultRepository) List(ctx context.Context, uid int64) ([]*domain.Vault, error) {\n\targs := m.Called(ctx, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*domain.Vault), args.Error(1)\n}\n\n// Delete soft-deletes a vault.\n// Delete 软删除 Vault。\nfunc (m *MockVaultRepository) Delete(ctx context.Context, id, uid int64) error {\n\targs := m.Called(ctx, id, uid)\n\treturn args.Error(0)\n}\n\n// Compile-time check: MockVaultRepository must implement domain.VaultRepository.\n// 编译时检查：MockVaultRepository 必须实现 domain.VaultRepository 接口。\nvar _ domain.VaultRepository = (*MockVaultRepository)(nil)\n"
  },
  {
    "path": "internal/dto/admin_dto.go",
    "content": "package dto\n\nimport \"time\"\n\n// AdminWebGUIConfig WebGUI configuration response structure (public interface)\n// AdminWebGUIConfig WebGUI 配置响应结构（公开接口）\ntype AdminWebGUIConfig struct {\n\tFontSet          string `json:\"fontSet\"`          // Font set // 字体设置\n\tRegisterIsEnable bool   `json:\"registerIsEnable\"` // Registration enablement // 是否开启注册\n\tAdminUID         int    `json:\"adminUid\"`         // Admin UID // 管理员 UID\n}\n\n// AdminCheckResponse Admin check response structure\n// AdminCheckResponse 管理员权限检查响应结构\ntype AdminCheckResponse struct {\n\tIsAdmin bool `json:\"isAdmin\"` // Whether have admin privileges // 是否具有管理员权限\n}\n\n// AdminConfig Admin configuration structure (admin interface)\n// AdminConfig 管理员配置结构（管理员接口）\ntype AdminConfig struct {\n\tFontSet                 string `json:\"fontSet\" form:\"fontSet\"`                                           // Font set // 字体设置\n\tRegisterIsEnable        bool   `json:\"registerIsEnable\" form:\"registerIsEnable\"`                         // Registration enablement // 是否开启注册\n\tFileChunkSize           string `json:\"fileChunkSize,omitempty\" form:\"fileChunkSize\"`                     // File chunk size // 文件分块大小\n\tSoftDeleteRetentionTime string `json:\"softDeleteRetentionTime,omitempty\" form:\"softDeleteRetentionTime\"` // Soft delete retention time // 软删除保留时间\n\tUploadSessionTimeout    string `json:\"uploadSessionTimeout,omitempty\" form:\"uploadSessionTimeout\"`       // Upload session timeout // 上传会话超时时间\n\tHistoryKeepVersions     int    `json:\"historyKeepVersions,omitempty\" form:\"historyKeepVersions\"`         // History versions to keep // 历史版本保留数\n\tHistorySaveDelay        string `json:\"historySaveDelay,omitempty\" form:\"historySaveDelay\"`               // History save delay // 历史保存延迟\n\tDefaultAPIFolder        string `json:\"defaultApiFolder,omitempty\" form:\"defaultApiFolder\"`               // Default API folder // 默认 API 目录\n\tAdminUID                int    `json:\"adminUid\" form:\"adminUid\"`                                         // Admin UID // 管理员 UID\n\tAuthTokenKey            string `json:\"authTokenKey\" form:\"authTokenKey\"`                                 // Auth token key // 认证 Token 密钥\n\tTokenExpiry             string `json:\"tokenExpiry\" form:\"tokenExpiry\"`                                   // Token expiry // Token 有效期\n\tShareTokenKey           string `json:\"shareTokenKey\" form:\"shareTokenKey\"`                               // Share token key // 分享 Token 密钥\n\tShareTokenExpiry        string `json:\"shareTokenExpiry\" form:\"shareTokenExpiry\"`                         // Share token expiry // 分享 Token 有效期\n\tPullSource              string `json:\"pullSource\" form:\"pullSource\"`                                     // Data pull source: auto | github | cnb // 数据拉取源：auto | github | cnb\n}\n\n// AdminUserDatabaseConfig User database configuration structure\n// AdminUserDatabaseConfig 用户数据库配置结构\ntype AdminUserDatabaseConfig struct {\n\tType                string `json:\"type\" form:\"type\" binding:\"omitempty,oneof=mysql postgres sqlite\"` // Database type (mysql, postgres, sqlite) // 数据库类型\n\tPath                string `json:\"path\" form:\"path\"`                                                 // SQLite database file path // SQLite 数据库文件路径\n\tUserName            string `json:\"userName\" form:\"userName\"`                                         // Username // 用户名\n\tPassword            string `json:\"password\" form:\"password\"`                                         // Password // 密码\n\tHost                string `json:\"host\" form:\"host\"`                                                 // Host // 主机\n\tPort                int    `json:\"port\" form:\"port\"`                                                 // Port // 端口\n\tName                string `json:\"name\" form:\"name\"`                                                 // Database name // 数据库名\n\tSSLMode             string `json:\"sslMode\" form:\"sslMode\"`                                           // SSL mode (postgres only) // SSL 模式\n\tSchema              string `json:\"schema\" form:\"schema\"`                                             // Database schema (postgres only) // 数据库 Schema\n\tMaxIdleConns        int    `json:\"maxIdleConns\" form:\"maxIdleConns\"`                                 // Max idle connections // 最大闲置连接数\n\tMaxOpenConns        int    `json:\"maxOpenConns\" form:\"maxOpenConns\"`                                 // Max open connections // 最大打开连接数\n\tConnMaxLifetime     string `json:\"connMaxLifetime\" form:\"connMaxLifetime\"`                           // Connection max lifetime // 连接最大生命周期\n\tConnMaxIdleTime     string `json:\"connMaxIdleTime\" form:\"connMaxIdleTime\"`                           // Connection max idle time // 空闲连接最大生命周期\n\tMaxWriteConcurrency int    `json:\"maxWriteConcurrency\" form:\"maxWriteConcurrency\"`                   // Max write concurrency // 最大并发写入数\n\tCharset             string `json:\"charset\" form:\"charset\"`                                           // Charset // 字符集\n\tParseTime           bool   `json:\"parseTime\" form:\"parseTime\"`                                       // Parse time // 是否解析时间\n}\n\n// AdminNgrokConfig Ngrok tunnel configuration\n// AdminNgrokConfig Ngrok 隧道配置\ntype AdminNgrokConfig struct {\n\tEnabled   bool   `json:\"enabled\" form:\"enabled\"`     // Whether to enable ngrok tunnel // 是否启用 ngrok 隧道\n\tAuthToken string `json:\"authToken\" form:\"authToken\"` // ngrok auth token // ngrok 认证令牌\n\tDomain    string `json:\"domain\" form:\"domain\"`       // Custom domain // 自定义域名\n}\n\n// AdminCloudflareConfig Cloudflare tunnel configuration\n// AdminCloudflareConfig Cloudflare 隧道配置\ntype AdminCloudflareConfig struct {\n\tEnabled    bool   `json:\"enabled\" form:\"enabled\"`       // Whether to enable cloudflare tunnel // 是否启用 cloudflare 隧道\n\tToken      string `json:\"token\" form:\"token\"`           // cloudflare tunnel token // cloudflare 隧道令牌\n\tLogEnabled bool   `json:\"logEnabled\" form:\"logEnabled\"` // Whether to enable cloudflare tunnel logging // 是否开启 cloudflare 隧道日志\n}\n\n// AdminSystemInfo system information response structure\n// AdminSystemInfo 系统信息响应结构\ntype AdminSystemInfo struct {\n\tStartTime     time.Time        `json:\"startTime\"`     // Start time // 启动时间\n\tUptime        float64          `json:\"uptime\"`        // Uptime (seconds) // 运行时间（秒）\n\tRuntimeStatus AdminRuntimeInfo `json:\"runtimeStatus\"` // Go runtime status // Go 运行时状态\n\tCPU           AdminCPUInfo     `json:\"cpu\"`           // CPU information // CPU 信息\n\tMemory        AdminMemoryInfo  `json:\"memory\"`        // Memory information // 内存信息\n\tHost          AdminHostInfo    `json:\"host\"`          // Host information // 主机信息\n\tProcess       AdminProcessInfo `json:\"process\"`       // Process information // 进程信息\n}\n\n// AdminCPUInfo CPU information\n// AdminCPUInfo CPU 信息\ntype AdminCPUInfo struct {\n\tModelName     string         `json:\"modelName\"`     // Model name // 型号\n\tPhysicalCores int            `json:\"physicalCores\"` // Physical cores // 物理核心数\n\tLogicalCores  int            `json:\"logicalCores\"`  // Logical cores // 逻辑核心数\n\tPercent       []float64      `json:\"percent\"`       // Usage percentage per core // 每个核心的使用率\n\tLoadAvg       *AdminLoadInfo `json:\"loadAvg\"`       // Load average // 平均负载\n}\n\n// AdminLoadInfo system load information\n// AdminLoadInfo 系统负载信息\ntype AdminLoadInfo struct {\n\tLoad1  float64 `json:\"load1\"`  // Load 1 min // 1分钟负载\n\tLoad5  float64 `json:\"load5\"`  // Load 5 min // 5分钟负载\n\tLoad15 float64 `json:\"load15\"` // Load 15 min // 15分钟负载\n}\n\n// AdminMemoryInfo memory information\n// AdminMemoryInfo 内存信息\ntype AdminMemoryInfo struct {\n\tTotal           uint64  `json:\"total\"`           // Total physical memory // 系统总内存\n\tAvailable       uint64  `json:\"available\"`       // Available memory // 可用内存\n\tUsed            uint64  `json:\"used\"`            // Used memory // 已用内存\n\tUsedPercent     float64 `json:\"usedPercent\"`     // Memory usage percentage // 内存使用率\n\tSwapTotal       uint64  `json:\"swapTotal\"`       // Total swap space // 交换区总量\n\tSwapUsed        uint64  `json:\"swapUsed\"`        // Used swap space // 交换区已用\n\tSwapUsedPercent float64 `json:\"swapUsedPercent\"` // Swap usage percentage // 交换区使用率\n}\n\n// AdminHostInfo host identification information\n// AdminHostInfo 主机标识信息\ntype AdminHostInfo struct {\n\tHostname       string    `json:\"hostname\"`       // Hostname // 主机名\n\tOS             string    `json:\"os\"`             // Operating system // 操作系统\n\tOSPretty       string    `json:\"osPretty\"`       // Detailed OS name // 详细操作系统名称\n\tPlatform       string    `json:\"platform\"`       // Platform name // 平台\n\tArch           string    `json:\"arch\"`           // Architecture // 架构\n\tKernelVersion  string    `json:\"kernelVersion\"`  // Kernel version // 内核版本\n\tUptime         uint64    `json:\"uptime\"`         // System uptime // 系统运行时间\n\tCurrentTime    time.Time `json:\"currentTime\"`    // Current system time // 当前系统时间\n\tTimeZone       string    `json:\"timezone\"`       // Time zone name // 时区名称\n\tTimeZoneOffset int       `json:\"timezoneOffset\"` // Time zone offset in seconds // 时区偏移（秒）\n}\n\n// AdminProcessInfo current process information\n// AdminProcessInfo 当前进程信息\ntype AdminProcessInfo struct {\n\tPID           int32   `json:\"pid\"`           // Process ID // 进程 ID\n\tPPID          int32   `json:\"ppid\"`          // Parent Process ID // 父进程 ID\n\tName          string  `json:\"name\"`          // Process Name // 进程名称\n\tCPUPercent    float64 `json:\"cpuPercent\"`    // CPU Usage percentage // CPU 使用率\n\tMemoryPercent float32 `json:\"memoryPercent\"` // Memory Usage percentage // 内存使用率\n}\n\n// AdminRuntimeInfo Go runtime information\n// AdminRuntimeInfo Go 运行时信息\ntype AdminRuntimeInfo struct {\n\tNumGoroutine int    `json:\"numGoroutine\"` // Number of goroutines // Goroutine 数量\n\tMemAlloc     uint64 `json:\"memAlloc\"`     // Allocated memory (bytes) // 已分配内存（字节）\n\tMemTotal     uint64 `json:\"memTotal\"`     // Total memory allocated (bytes) // 累计分配内存（字节）\n\tMemSys       uint64 `json:\"memSys\"`       // Memory obtained from system (bytes) // 从系统获取的内存（字节）\n\tHeapSys      uint64 `json:\"heapSys\"`      // Memory obtained from system for heap (bytes) // 堆占用的系统内存\n\tHeapIdle     uint64 `json:\"heapIdle\"`     // Memory in idle spans (bytes) // 空闲 Span 占用的内存\n\tHeapInuse    uint64 `json:\"heapInuse\"`    // Memory in in-use spans (bytes) // 正在使用的 Span 占用的内存\n\tHeapReleased uint64 `json:\"heapReleased\"` // Memory released to OS (bytes) // 释放回操作系统的内存（字节）\n\tStackSys     uint64 `json:\"stackSys\"`     // Memory obtained from system for stack (bytes) // 栈占用的系统内存\n\tMSpanSys     uint64 `json:\"mSpanSys\"`     // Memory obtained from system for mspan (bytes) // mspan 占用的系统内存\n\tMCacheSys    uint64 `json:\"mCacheSys\"`    // Memory obtained from system for mcache (bytes) // mcache 占用的系统内存\n\tBuckHashSys  uint64 `json:\"buckHashSys\"`  // Memory obtained from system for profiling bucket hash table (bytes) // 分析桶哈希表占用的系统内存\n\tGCSys        uint64 `json:\"gcSys\"`        // Memory obtained from system for metadata for GC (bytes) // GC 元数据占用的系统内存\n\tOtherSys     uint64 `json:\"otherSys\"`     // Other system memory (bytes) // 其他系统内存\n\tNextGC       uint64 `json:\"nextGc\"`       // Target heap size for the next GC cycle // 下次 GC 的目标堆大小\n\tNumGC        uint32 `json:\"numGc\"`        // Number of completed GC cycles // GC 次数\n}\n"
  },
  {
    "path": "internal/dto/app_dto.go",
    "content": "// Package dto Defines data transfer objects (request parameters and response structs)\n// Package dto 定义数据传输对象（请求参数和响应结构体）\npackage dto\n\n// VersionDTO version information for API response\n// VersionDTO 版本信息 API 响应对象\ntype VersionDTO struct {\n\tVersion                          string `json:\"version\"`                          // Current version // 当前版本\n\tGitTag                           string `json:\"gitTag\"`                           // Git tag // Git 标签\n\tBuildTime                        string `json:\"buildTime\"`                        // Build time // 构建时间\n\tVersionIsNew                     bool   `json:\"versionIsNew\"`                     // Is there a new version // 是否有新版本\n\tVersionNewName                   string `json:\"versionNewName\"`                   // New version name // 新版本名称\n\tVersionNewLink                   string `json:\"versionNewLink\"`                   // New version download link // 新版本下载链接\n\tVersionNewChangelog              string `json:\"versionNewChangelog\"`              // New version changelog link // 新版本更新日志链接\n\tVersionNewChangelogContent       string `json:\"versionNewChangelogContent\"`       // New version changelog content // 新版本更新日志内容\n\tPluginVersionNewName             string `json:\"pluginVersionNewName\"`             // New plugin version name // 插件新版本名称\n\tPluginVersionNewLink             string `json:\"pluginVersionNewLink\"`             // New plugin version link // 插件新版本链接\n\tPluginVersionNewChangelog        string `json:\"pluginVersionNewChangelog\"`        // New plugin version changelog link // 插件新版本更新日志链接\n\tPluginVersionNewChangelogContent string `json:\"pluginVersionNewChangelogContent\"` // New plugin version changelog content // 插件新版本更新日志内容\n}\n\n// UpgradeRequest upgrade request parameters\n// UpgradeRequest 升级请求参数\ntype UpgradeRequest struct {\n\tVersion string `form:\"version\" binding:\"required\"` // Version to upgrade (e.g. 2.0.10 or latest) // 升级版本\n}\n"
  },
  {
    "path": "internal/dto/backup.go",
    "content": "package dto\n\nimport \"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\n// BackupConfigRequest backup configuration request\n// BackupConfigRequest 备份配置请求\ntype BackupConfigRequest struct {\n\tID               int64  `json:\"id\" form:\"id\" example:\"1\"`                                             // ID // ID\n\tVault            string `json:\"vault\" form:\"vault\" example:\"test\"`                                    // Vault name // 仓库名称\n\tType             string `json:\"type\" form:\"type\" binding:\"required,oneof=full incremental sync\" example:\"sync\"` // Backup type // 备份类型\n\tStorageIds       string `json:\"storageIds\" form:\"storageIds\" binding:\"required\" example:\"[1, 2]\"`     // Storage IDs // 存储 ID 列表\n\tIsEnabled        bool   `json:\"isEnabled\" form:\"isEnabled\" example:\"true\"`                            // Is enabled // 是否启用\n\tCronStrategy     string `json:\"cronStrategy\" form:\"cronStrategy\" binding:\"required,oneof=daily weekly monthly custom\" example:\"daily\"` // Cron strategy // 定时策略\n\tCronExpression   string `json:\"cronExpression\" form:\"cronExpression\" example:\"0 0 * * *\"`              // Cron expression // Cron 表达式\n\tRetentionDays    int    `json:\"retentionDays\" form:\"retentionDays\" binding:\"min=-1\" example:\"7\"`      // Retention days // 保留天数\n\tIncludeVaultName bool   `json:\"includeVaultName\" form:\"includeVaultName\" example:\"false\"`              // Include vault name // 同步路径是否包含仓库名\n}\n\n// BackupExecuteRequest backup execution request\n// BackupExecuteRequest 备份执行请求\ntype BackupExecuteRequest struct {\n\tID int64 `json:\"id\" form:\"id\" example:\"1\"` // ID // ID\n}\n\n// BackupHistoryListRequest backup history list request\n// BackupHistoryListRequest 备份历史列表请求\ntype BackupHistoryListRequest struct {\n\tConfigID int64 `json:\"configId\" form:\"configId\" binding:\"required\" example:\"1\"` // Config ID // 配置 ID\n\tPage     int   `json:\"page\" form:\"page\" example:\"1\"`                           // Page number // 页码\n\tPageSize int   `json:\"pageSize\" form:\"pageSize\" example:\"10\"`                  // Page size // 每页大小\n}\n\n// BackupConfigDTO backup configuration DTO\n// BackupConfigDTO 备份配置 DTO\ntype BackupConfigDTO struct {\n\tID               int64      `json:\"id\"`               // Config ID // 配置ID\n\tUID              int64      `json:\"uid\"`              // User UID // 用户ID\n\tVault            string     `json:\"vault\"`            // Associated vault name // 关联库名称\n\tType             string     `json:\"type\"`             // Backup type (full, incremental, sync) // 备份类型 (full, incremental, sync)\n\tStorageIds       string     `json:\"storageIds\"`       // Storage ID list // 存储ID列表\n\tIsEnabled        bool       `json:\"isEnabled\"`        // Is enabled // 是否启用\n\tCronStrategy     string     `json:\"cronStrategy\"`     // Cron strategy // 定时策略\n\tCronExpression   string     `json:\"cronExpression\"`   // Cron expression // Cron表达式\n\tRetentionDays    int        `json:\"retentionDays\"`    // Retention days // 保留天数\n\tIncludeVaultName bool       `json:\"includeVaultName\"` // Whether sync path includes vault name // 同步路径是否包含仓库名\n\tLastRunTime      timex.Time `json:\"lastRunTime\"`      // Last run time // 上次运行时间\n\tNextRunTime      timex.Time `json:\"nextRunTime\"`      // Next run time // 下次运行时间\n\tLastStatus       int        `json:\"lastStatus\"`       // Last status (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Stopped) // 上次状态 (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Stopped)\n\tLastMessage      string     `json:\"lastMessage\"`      // Last run result message // 上次运行结果消息\n\tCreatedAt        timex.Time `json:\"createdAt\"`        // Created at // 创建时间\n\tUpdatedAt        timex.Time `json:\"updatedAt\"`        // Updated at // 更新时间\n}\n\n// BackupHistoryDTO backup history DTO\n// BackupHistoryDTO 备份历史 DTO\ntype BackupHistoryDTO struct {\n\tID        int64      `json:\"id\"`        // History record ID // 历史记录ID\n\tUID       int64      `json:\"uid\"`       // User UID // 用户ID\n\tConfigID  int64      `json:\"configId\"`  // Config ID // 配置ID\n\tStorageID int64      `json:\"storageId\"` // Storage ID // 存储ID\n\tType      string     `json:\"type\"`      // Backup type // 备份类型\n\tStartTime timex.Time `json:\"startTime\"` // Start time // 开始时间\n\tEndTime   timex.Time `json:\"endTime\"`   // End time // 结束时间\n\tStatus    int        `json:\"status\"`    // Status (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Stopped) // 状态 (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Stopped)\n\tFileSize  int64      `json:\"fileSize\"`  // File size // 文件大小\n\tFileCount int64      `json:\"fileCount\"` // File count // 文件数量\n\tMessage   string     `json:\"message\"`   // Result message // 结果消息\n\tFilePath  string     `json:\"filePath\"`  // File path // 文件路径\n\tCreatedAt timex.Time `json:\"createdAt\"` // Created at // 创建时间\n\tUpdatedAt timex.Time `json:\"updatedAt\"` // Updated at // 更新时间\n}\n"
  },
  {
    "path": "internal/dto/conflict_dto.go",
    "content": "// Package dto Defines data transfer objects (request parameters and response structs)\n// Package dto 定义数据传输对象（请求参数和响应结构体）\npackage dto\n\n// ConflictFileRequest Request parameters for creating a conflict file\n// 创建冲突文件的请求参数\ntype ConflictFileRequest struct {\n\tVault             string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`                          // Vault name // 保险库名称\n\tOriginalPath      string `json:\"originalPath\" form:\"originalPath\" binding:\"required\" example:\"ReadMe.md\"`          // Original file path // 原始文件路径\n\tClientContent     string `json:\"clientContent\" form:\"clientContent\" binding:\"required\" example:\"Conflict content\"` // Client side content // 客户端内容\n\tClientContentHash string `json:\"clientContentHash\" form:\"clientContentHash\" binding:\"required\" example:\"hash123\"`  // Client side content hash // 客户端内容哈希\n\tCtime             int64  `json:\"ctime\" form:\"ctime\" example:\"1700000000\"`                                          // Creation timestamp // 创建时间戳\n\tMtime             int64  `json:\"mtime\" form:\"mtime\" example:\"1700000000\"`                                          // Modification timestamp // 修改时间戳\n}\n\n// ---------------- DTO / Response ----------------\n\n// ConflictFileResponse Response for creating a conflict file\n// ConflictFileResponse 创建冲突文件的响应\ntype ConflictFileResponse struct {\n\tConflictPath string `json:\"conflictPath\"` // Path of the created conflict file // 创建的冲突文件路径\n\tMessage      string `json:\"message\"`      // Result message // 结果消息\n\tNoteID       int64  `json:\"noteId\"`       // Note ID // 笔记 ID\n}\n"
  },
  {
    "path": "internal/dto/file_dto.go",
    "content": "// Package dto Defines data transfer objects (request parameters and response structs)\n// Package dto 定义数据传输对象（请求参数和响应结构体）\npackage dto\n\nimport \"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\n// FileUpdateCheckRequest Client request parameters for checking if updates are needed\n// 客户端用于检查是否需要更新的请求参数\ntype FileUpdateCheckRequest struct {\n\tVault       string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`        // Vault name // 保险库名称\n\tPath        string `json:\"path\" form:\"path\" binding:\"required\" example:\"Image.png\"`        // File path // 文件路径\n\tPathHash    string `json:\"pathHash\" form:\"pathHash\" binding:\"required\" example:\"fhash123\"` // Path hash // 路径哈希\n\tContentHash string `json:\"contentHash\" form:\"contentHash\" binding:\"\" example:\"chash456\"`   // Content hash // 内容哈希\n\tSize        int64  `json:\"size\" form:\"size\" binding:\"\" example:\"1024\"`                     // File size // 文件大小\n\tCtime       int64  `json:\"ctime\" form:\"ctime\" binding:\"required\" example:\"1700000000\"`     // Creation timestamp // 创建时间戳\n\tMtime       int64  `json:\"mtime\" form:\"mtime\" binding:\"required\" example:\"1700000000\"`     // Modification timestamp // 修改时间戳\n}\n\n// FileUpdateRequest Request parameters for creating or modifying a file\n// 用于创建或修改文件的请求参数\ntype FileUpdateRequest struct {\n\tVault       string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`          // Vault name // 保险库名称\n\tPath        string `json:\"path\" form:\"path\" binding:\"required\" example:\"Image.png\"`          // File path // 文件路径\n\tPathHash    string `json:\"pathHash\" form:\"pathHash\" example:\"fhash123\"`                      // Path hash // 路径哈希\n\tContentHash string `json:\"contentHash\" form:\"contentHash\" binding:\"\" example:\"chash456\"`     // Content hash // 内容哈希\n\tSavePath    string `json:\"savePath\" form:\"savePath\" binding:\"\" example:\"/uploads/Image.png\"` // Save path on server // 服务器保存路径\n\tSize        int64  `json:\"size\" form:\"size\" example:\"1024\"`                                  // File size // 文件大小\n\tCtime       int64  `json:\"ctime\" form:\"ctime\" example:\"1700000000\"`                          // Creation timestamp // 创建时间戳\n\tMtime       int64  `json:\"mtime\" form:\"mtime\" example:\"1700000000\"`                          // Modification timestamp // 修改时间戳\n}\n\n// FileDeleteRequest Parameters required for deleting a file\n// 删除文件所需参数\ntype FileDeleteRequest struct {\n\tVault    string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`        // Vault name // 保险库名称\n\tPath     string `json:\"path\" form:\"path\" binding:\"required\" example:\"Image.png\"`        // File path // 文件路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" binding:\"required\" example:\"fhash123\"` // Path hash // 路径哈希\n}\n\n// FileRestoreRequest parameters for restoring a file\n// FileRestoreRequest 恢复文件请求参数\ntype FileRestoreRequest struct {\n\tVault    string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tPath     string `json:\"path\" form:\"path\" binding:\"required\" example:\"Image.png\"` // File path // 文件路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" example:\"fhash123\"`             // Path hash // 路径哈希\n}\n\n// FileRecycleClearRequest clean recycle bin request\n// FileRecycleClearRequest 清理回收站请求\ntype FileRecycleClearRequest struct {\n\tVault    string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tPath     string `json:\"path\" form:\"path\" example:\"path/to/file.png\"`             // File path, empty for all // 文件路径，为空则清理全部\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" example:\"fhash123\"`             // Path hash // 路径哈希\n}\n\n// FileSyncCheckRequest/ Parameters for checking synchronization of a single record\n// 同步检查单条记录的参数\ntype FileSyncCheckRequest struct {\n\tPath        string `json:\"path\" form:\"path\" example:\"Image.png\"`                           // File path // 文件路径\n\tPathHash    string `json:\"pathHash\" form:\"pathHash\" binding:\"required\" example:\"fhash123\"` // Path hash // 路径哈希\n\tContentHash string `json:\"contentHash\" form:\"contentHash\" binding:\"\" example:\"chash456\"`   // Content hash // 内容哈希\n\tMtime       int64  `json:\"mtime\" form:\"mtime\" binding:\"required\" example:\"1700000000\"`     // Modification timestamp // 修改时间戳\n\tCtime       int64  `json:\"ctime\" form:\"ctime\" example:\"1700000000\"`                        // Creation timestamp // 创建时间戳\n\tSize        int64  `json:\"size\" form:\"size\" example:\"1024\"`                                // File size // 文件大小\n}\n\n// FileSyncDelFile parameters for deleting a file during sync\n// 同步删除文件参数\ntype FileSyncDelFile struct {\n\tPath     string `json:\"path\" form:\"path\" binding:\"required\" example:\"DeletedFile.png\"`   // File path // 文件路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" binding:\"required\" example:\"dfhash789\"` // Path hash // 路径哈希\n}\n\n// FileSyncRequest Synchronization request body\n// 同步请求主体\ntype FileSyncRequest struct {\n\tContext      string                 `json:\"context\" form:\"context\" binding:\"required\" example:\"task123\"` // Context // 上下文\n\tVault        string                 `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`     // Vault name // 保险库名称\n\tLastTime     int64                  `json:\"lastTime\" form:\"lastTime\" example:\"1700000000\"`               // Last sync time // 最后同步时间\n\tFiles        []FileSyncCheckRequest `json:\"files\" form:\"files\"`                                          // Files to check // 待检查文件列表\n\tDelFiles     []FileSyncDelFile      `json:\"delFiles\" form:\"delFiles\"`                                    // Files to delete // 待删除文件列表\n\tMissingFiles []FileSyncDelFile      `json:\"missingFiles\" form:\"missingFiles\"`                            // Missing files // 缺失文件列表\n}\n\n// FileUploadCompleteRequest Parameters for file upload completion\n// 文件上传完成参数\ntype FileUploadCompleteRequest struct {\n\tSessionID string `json:\"sessionId\" binding:\"required\" example:\"sess_123456\"` // Upload session ID // 上传会话 ID\n}\n\n// FileGetRequest Request parameters for retrieving a single file\n// FileGetRequest 用于获取单条文件的请求参数\ntype FileGetRequest struct {\n\tVault     string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tPath      string `json:\"path\" form:\"path\" binding:\"required\" example:\"Image.png\"` // File path // 文件路径\n\tPathHash  string `json:\"pathHash\" form:\"pathHash\" example:\"fhash123\"`             // Path hash // 路径哈希\n\tIsRecycle bool   `json:\"isRecycle\" form:\"isRecycle\" example:\"false\"`              // Is in recycle bin // 是否在回收站\n}\n\n// FileListRequest Pagination parameters for retrieving the file list\n// FileListRequest 获取文件列表的分页参数\ntype FileListRequest struct {\n\tVault     string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tKeyword   string `json:\"keyword\" form:\"keyword\" example:\"vacation\"`               // Search keyword // 搜索关键词\n\tIsRecycle bool   `json:\"isRecycle\" form:\"isRecycle\" example:\"false\"`              // Is in recycle bin // 是否在回收站\n\tSortBy    string `json:\"sortBy\" form:\"sortBy\" example:\"mtime\"`                    // Sort by field // 排序字段\n\tSortOrder string `json:\"sortOrder\" form:\"sortOrder\" example:\"desc\"`               // Sort order // 排序顺序\n}\n\n// FileRenameRequest Parameters required for renaming a file\n// FileRenameRequest 重命名文件所需参数\ntype FileRenameRequest struct {\n\tVault       string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`          // Vault name // 保险库名称\n\tPath        string `json:\"path\" form:\"path\" binding:\"required\" example:\"NewImage.png\"`       // New path // 新路径\n\tPathHash    string `json:\"pathHash\" form:\"pathHash\" example:\"nfhash123\"`                     // New path hash // 新路径哈希\n\tOldPath     string `json:\"oldPath\" form:\"oldPath\" binding:\"required\" example:\"OldImage.png\"` // Old path // 旧路径\n\tOldPathHash string `json:\"oldPathHash\" form:\"oldPathHash\" example:\"ofhash456\"`               // Old path hash // 旧路径哈希\n}\n\n// ---------------- DTO / Response ----------------\n\n// FileDTO File Data Transfer Object\n// FileDTO 文件数据传输对象\ntype FileDTO struct {\n\tID               int64      `json:\"-\"`                                // File ID // 文件 ID\n\tAction           string     `json:\"-\"`                                // Action // 动作\n\tPath             string     `json:\"path\" form:\"path\"`                 // File path // 文件路径\n\tPathHash         string     `json:\"pathHash\" form:\"pathHash\"`         // Path hash // 路径哈希\n\tContentHash      string     `json:\"contentHash\" form:\"contentHash\"`   // Content hash // 内容哈希\n\tSavePath         string     `json:\"-\"`                                // Internal save path // 内部保存路径\n\tRename           int64      `json:\"rename\"`                           // Rename flag // 重命名标记\n\tSize             int64      `json:\"size\" form:\"size\"`                 // File size // 文件大小\n\tCtime            int64      `json:\"ctime\" form:\"ctime\"`               // Creation timestamp // 创建时间戳\n\tMtime            int64      `json:\"mtime\" form:\"mtime\"`               // Modification timestamp // 修改时间戳\n\tUpdatedTimestamp int64      `json:\"lastTime\" form:\"updatedTimestamp\"` // Updated timestamp // 更新时间戳\n\tUpdatedAt        timex.Time `json:\"updatedAt\"`                        // Updated at time // 更新时间\n\tCreatedAt        timex.Time `json:\"createdAt\"`                        // Created at time // 创建时间\n}\n"
  },
  {
    "path": "internal/dto/file_dto_ws.go",
    "content": "package dto\n\n// FileSyncModifyMessage message content for file modification or creation\n// FileSyncModifyMessage 文件修改或创建的消息内容\ntype FileSyncModifyMessage struct {\n\tPath             string `json:\"path\" form:\"path\" example:\"Image.png\"`                  // File path // 文件路径\n\tPathHash         string `json:\"pathHash\" form:\"pathHash\" example:\"fhash123\"`           // Path hash // 路径哈希值\n\tContentHash      string `json:\"contentHash\" form:\"contentHash\" example:\"chash456\"`     // Content hash // 内容哈希\n\tSize             int64  `json:\"size\" form:\"size\" example:\"1024\"`                       // File size // 文件大小\n\tCtime            int64  `json:\"ctime\" form:\"ctime\" example:\"1700000000\"`               // Creation timestamp // 创建时间戳\n\tMtime            int64  `json:\"mtime\" form:\"mtime\" example:\"1700000000\"`               // Modification timestamp // 修改时间戳\n\tUpdatedTimestamp int64  `json:\"lastTime\" form:\"updatedTimestamp\" example:\"1700000000\"` // Record update timestamp // 记录更新时间戳\n}\n\n// FileSyncEndMessage defines the message structure when file sync ends\n// FileSyncEndMessage 定义文件同步结束时的消息结构\ntype FileSyncEndMessage struct {\n\tLastTime           int64 `json:\"lastTime\" form:\"lastTime\" example:\"1700000000\"`            // Last sync time // 最后同步时间\n\tNeedUploadCount    int64 `json:\"needUploadCount\" form:\"needUploadCount\" example:\"5\"`       // Number of items needing upload // 需要上传的数量\n\tNeedModifyCount    int64 `json:\"needModifyCount\" form:\"needModifyCount\" example:\"2\"`       // Number of items needing modification // 需要修改的数量\n\tNeedSyncMtimeCount int64 `json:\"needSyncMtimeCount\" form:\"needSyncMtimeCount\" example:\"1\"` // Number of items needing mtime sync // 需要同步修改时间的数量\n\tNeedDeleteCount    int64 `json:\"needDeleteCount\" form:\"needDeleteCount\" example:\"0\"`       // Number of items needing deletion // 需要删除的数量\n}\n\n// FileSyncUploadMessage defines the message structure informing client that file upload is needed\n// FileSyncUploadMessage 定义服务端通知客户端需要上传文件的消息结构\ntype FileSyncUploadMessage struct {\n\tPath      string `json:\"path\" example:\"Image.png\"`        // File path // 文件路径\n\tSessionID string `json:\"sessionId\" example:\"sess_123456\"` // Session ID // 会话 ID\n\tChunkSize int64  `json:\"chunkSize\" example:\"1048576\"`     // Chunk size // 分块大小\n}\n\n// FileSyncDownloadMessage defines the message structure informing client that file download is ready\n// FileSyncDownloadMessage 定义服务端通知客户端准备下载文件的消息结构\ntype FileSyncDownloadMessage struct {\n\tPath        string `json:\"path\" example:\"Image.png\"`        // File path // 文件路径\n\tCtime       int64  `json:\"ctime\" example:\"1700000000\"`      // Creation time // 创建时间\n\tMtime       int64  `json:\"mtime\" example:\"1700000000\"`      // Modification time // 修改时间\n\tSessionID   string `json:\"sessionId\" example:\"sess_789012\"` // Session ID // 会话 ID\n\tChunkSize   int64  `json:\"chunkSize\" example:\"1048576\"`     // Chunk size // 分块大小\n\tTotalChunks int64  `json:\"totalChunks\" example:\"10\"`        // Total chunks // 总分块数\n\tSize        int64  `json:\"size\" example:\"10485760\"`         // Total file size // 文件总大小\n}\n\n// FileSyncMtimeMessage defines the message structure for file metadata update\n// FileSyncMtimeMessage 定义文件元数据更新消息结构\ntype FileSyncMtimeMessage struct {\n\tPath             string `json:\"path\" example:\"Image.png\"`                              // File path // 文件路径\n\tCtime            int64  `json:\"ctime\" example:\"1700000000\"`                            // Creation timestamp // 创建时间戳\n\tMtime            int64  `json:\"mtime\" example:\"1700000000\"`                            // Modification timestamp // 修改时间戳\n\tUpdatedTimestamp int64  `json:\"lastTime\" form:\"updatedTimestamp\" example:\"1700000000\"` // Record update timestamp // 记录更新时间戳\n}\n\n// FileSyncDeleteMessage defines the message structure for file deletion during sync\n// FileSyncDeleteMessage 定义同步期间文件删除的消息结构\ntype FileSyncDeleteMessage struct {\n\tPath             string `json:\"path\" form:\"path\" example:\"DeletedFile.png\"`            // File path // 文件路径\n\tPathHash         string `json:\"pathHash\" form:\"pathHash\" example:\"dfhash123\"`          // Path hash // 路径哈希值\n\tCtime            int64  `json:\"ctime\" form:\"ctime\" example:\"1700000000\"`               // Creation timestamp // 创建时间戳\n\tMtime            int64  `json:\"mtime\" form:\"mtime\" example:\"1700000000\"`               // Modification timestamp // 修改时间戳\n\tSize             int64  `json:\"size\" form:\"size\" example:\"1024\"`                       // File size // 文件大小\n\tUpdatedTimestamp int64  `json:\"lastTime\" form:\"updatedTimestamp\" example:\"1700000000\"` // Record update timestamp // 记录更新时间戳\n}\n\n// FileRenameAckMessage ack message for file rename operation, carries server timestamp\n// FileRenameAckMessage 文件重命名操作 ack 消息，携带服务端时间戳\ntype FileRenameAckMessage struct {\n\tLastTime int64  `json:\"lastTime\"` // Server timestamp after rename // 重命名后的服务端时间戳\n\tPath     string `json:\"path\"`     // New file path after rename // 重命名后的文件新路径\n\tPathHash string `json:\"pathHash\"` // Path hash // 路径哈希值\n}\n\n// FileUploadAckMessage ack message for file upload complete, carries server timestamp and file path\n// FileUploadAckMessage 文件上传完成 ack 消息，携带服务端时间戳和文件路径\ntype FileUploadAckMessage struct {\n\tLastTime int64  `json:\"lastTime\"` // Server timestamp after upload // 上传完成后的服务端时间戳\n\tPath     string `json:\"path\"`     // File path // 文件路径\n\tPathHash string `json:\"pathHash\"` // Path hash // 路径哈希值\n}\n\n// FileDeleteAckMessage file delete operation ACK, sent back to sender after server processes FileDelete\n// FileDeleteAckMessage 文件删除操作 ACK，服务端处理完 FileDelete 后回发给发送方\ntype FileDeleteAckMessage struct {\n\tLastTime int64  `json:\"lastTime\"` // Server write timestamp // 服务端写入时间戳\n\tPath     string `json:\"path\"`     // File path // 文件路径\n\tPathHash string `json:\"pathHash\"` // Path hash // 路径哈希值\n}\n\n// FileSyncRenameMessage message structure for file rename during sync\n// FileSyncRenameMessage 同步过程中文件重命名的消息结构\ntype FileSyncRenameMessage struct {\n\tPath             string `json:\"path\" form:\"path\" binding:\"required\" example:\"NewImage.png\"` // New path // 新路径\n\tPathHash         string `json:\"pathHash\" form:\"pathHash\" example:\"nfhash123\"`               // New path hash // 新路径哈希\n\tContentHash      string `json:\"contentHash\" form:\"contentHash\" example:\"chash456\"`          // Content hash // 内容哈希\n\tCtime            int64  `json:\"ctime\" form:\"ctime\" example:\"1700000000\"`                    // Creation timestamp // 创建时间戳\n\tMtime            int64  `json:\"mtime\" form:\"mtime\" example:\"1700000000\"`                    // Modification timestamp // 修改时间戳\n\tSize             int64  `json:\"size\" form:\"size\" example:\"1024\"`                            // File size // 文件大小\n\tUpdatedTimestamp int64  `json:\"lastTime\" form:\"updatedTimestamp\" example:\"1700000000\"`      // Record update timestamp // 记录更新时间戳\n\tOldPath          string `json:\"oldPath\" form:\"oldPath\" example:\"OldImage.png\"`              // Old path // 旧路径\n\tOldPathHash      string `json:\"oldPathHash\" form:\"oldPathHash\" example:\"ofhash456\"`         // Old path hash // 旧路径哈希\n}\n"
  },
  {
    "path": "internal/dto/folder_dto.go",
    "content": "package dto\n\nimport \"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\n// FolderGetRequest Request parameters for retrieving a folder\n// 获取文件夹的请求参数\ntype FolderGetRequest struct {\n\tVault    string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tPath     string `json:\"path\" form:\"path\" example:\"Projects/Work\"`                // Folder path // 文件夹路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" example:\"fhash123\"`             // Path hash // 路径哈希\n}\n\n// FolderListRequest Request parameters for retrieving a folder list\n// 获取文件夹列表的请求参数\ntype FolderListRequest struct {\n\tVault    string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tPath     string `json:\"path\" form:\"path\" example:\"Projects\"`                     // Folder path // 文件夹路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" example:\"fhash123\"`             // Path hash // 路径哈希\n}\n\n// FolderCreateRequest Request parameters for creating a folder\n// 创建文件夹请求参数\ntype FolderCreateRequest struct {\n\tVault    string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tPath     string `json:\"path\" form:\"path\" binding:\"required\" example:\"NewFolder\"` // Folder path // 文件夹路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" example:\"fhash456\"`             // Path hash // 路径哈希\n}\n\n// FolderDeleteRequest Request parameters for deleting a folder\n// 删除文件夹请求参数\ntype FolderDeleteRequest struct {\n\tVault    string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tPath     string `json:\"path\" form:\"path\" binding:\"required\" example:\"OldFolder\"` // Folder path // 文件夹路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" example:\"fhash789\"`             // Path hash // 路径哈希\n}\n\n// FolderSyncCheckRequest Parameters for single record check during synchronization\n// 同步检查单条记录的参数\ntype FolderSyncCheckRequest struct {\n\tPath     string `json:\"path\" form:\"path\" example:\"FolderA\"`                             // Folder path // 文件夹路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" binding:\"required\" example:\"fhash012\"` // Path hash // 路径哈希\n\tMtime    int64  `json:\"mtime\" form:\"mtime\" binding:\"required\" example:\"1700000000\"`     // Modification timestamp // 修改时间戳\n}\n\n// FolderSyncDelFolder Parameters for deleting/missing folder during synchronization\n// 同步删除/缺失文件夹的参数\ntype FolderSyncDelFolder struct {\n\tPath     string `json:\"path\" form:\"path\" binding:\"required\" example:\"DeletedFolder\"`     // Folder path // 文件夹路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" binding:\"required\" example:\"dfhash345\"` // Path hash // 路径哈希\n}\n\n// FolderSyncRequest Synchronization request body\n// 同步请求主体\ntype FolderSyncRequest struct {\n\tContext        string                   `json:\"context\" form:\"context\" example:\"task123\"`                // Context // 上下文\n\tVault          string                   `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tLastTime       int64                    `json:\"lastTime\" form:\"lastTime\" example:\"1700000000\"`           // Last sync time // 最后同步时间\n\tFolders        []FolderSyncCheckRequest `json:\"folders\" form:\"folders\"`                                  // Folders to check // 待检查文件夹列表\n\tDelFolders     []FolderSyncDelFolder    `json:\"delFolders\" form:\"delFolders\"`                            // Folders to delete // 待删除文件夹列表\n\tMissingFolders []FolderSyncDelFolder    `json:\"missingFolders\" form:\"missingFolders\"`                    // Missing folders // 缺失文件夹列表\n}\n\n// FolderRenameRequest Request parameters for folder renaming\n// 文件夹重命名请求参数\ntype FolderRenameRequest struct {\n\tVault       string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`               // Vault name // 保险库名称\n\tPath        string `json:\"path\" form:\"path\" binding:\"required\" example:\"NewFolder\"`               // Current path // 当前路径\n\tPathHash    string `json:\"pathHash\" form:\"pathHash\" binding:\"required\" example:\"nfhash123\"`       // Current path hash // 当前路径哈希\n\tOldPath     string `json:\"oldPath\" form:\"oldPath\" binding:\"required\" example:\"OldFolder\"`         // Old path // 旧路径\n\tOldPathHash string `json:\"oldPathHash\" form:\"oldPathHash\" binding:\"required\" example:\"ofhash456\"` // Old path hash // 旧路径哈希\n}\n\n// FolderContentRequest Request parameters for retrieving folder contents\n// 获取文件夹内容的请求参数\ntype FolderContentRequest struct {\n\tVault     string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tPath      string `json:\"path\" form:\"path\" example:\"Projects\"`                     // Folder path // 文件夹路径\n\tPathHash  string `json:\"pathHash\" form:\"pathHash\" example:\"fhash123\"`             // Path hash // 路径哈希\n\tSortBy    string `json:\"sortBy\" form:\"sortBy\" example:\"mtime\"`                    // Sort by field // 排序字段\n\tSortOrder string `json:\"sortOrder\" form:\"sortOrder\" example:\"desc\"`               // Sort order // 排序顺序\n}\n\n// FolderTreeRequest Request parameters for retrieving the folder tree\n// 获取文件夹树的请求参数\ntype FolderTreeRequest struct {\n\tVault string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tDepth int    `json:\"depth\" form:\"depth\" example:\"3\"`                          // Tree depth // 树深度\n}\n\n// ---------------- DTO / Response ----------------\n\n// FolderDTO Folder data transfer object\n// FolderDTO 文件夹数据传输对象\ntype FolderDTO struct {\n\tID               int64      `json:\"-\" form:\"id\"`                      // Folder ID // 文件夹 ID\n\tAction           string     `json:\"-\" form:\"action\"`                  // Action // 动作\n\tPath             string     `json:\"path\" form:\"path\"`                 // Folder path // 文件夹路径\n\tPathHash         string     `json:\"pathHash\" form:\"pathHash\"`         // Path hash // 路径哈希值\n\tLevel            int64      `json:\"-\" form:\"level\"`                   // Level // 层级\n\tFID              int64      `json:\"-\" form:\"fid\"`                     // Parent ID // 父 ID\n\tCtime            int64      `json:\"ctime\" form:\"ctime\"`               // Creation timestamp // 创建时间戳\n\tMtime            int64      `json:\"mtime\" form:\"mtime\"`               // Modification timestamp // 修改时间戳\n\tUpdatedTimestamp int64      `json:\"lastTime\" form:\"updatedTimestamp\"` // Record update timestamp // 记录更新时间戳\n\tUpdatedAt        timex.Time `json:\"updatedAt\"`                        // Updated at time // 更新时间\n\tCreatedAt        timex.Time `json:\"createdAt\"`                        // Created at time // 创建时间\n}\n\n// FolderTreeNode Folder tree node\n// FolderTreeNode 文件夹树节点\ntype FolderTreeNode struct {\n\tPath      string            `json:\"path\"`               // Node path // 节点路径\n\tName      string            `json:\"name\"`               // Node name // 节点名称\n\tNoteCount int               `json:\"noteCount\"`          // Note count // 笔记数量\n\tFileCount int               `json:\"fileCount\"`          // File count // 文件数量\n\tChildren  []*FolderTreeNode `json:\"children,omitempty\"` // Child nodes // 子节点\n}\n\n// FolderTreeResponse Folder tree response structure\n// FolderTreeResponse 文件夹树响应结构体\ntype FolderTreeResponse struct {\n\tFolders       []*FolderTreeNode `json:\"folders\"`       // Folder tree // 文件夹树\n\tRootNoteCount int               `json:\"rootNoteCount\"` // Note count in root // 根目录中的笔记数量\n\tRootFileCount int               `json:\"rootFileCount\"` // File count in root // 根目录中的文件数量\n}\n"
  },
  {
    "path": "internal/dto/folder_dto_ws.go",
    "content": "package dto\n\n// FolderSyncEndMessage defines the folder sync end message structure\n// FolderSyncEndMessage 定义文件夹同步结束的消息结构\ntype FolderSyncEndMessage struct {\n\tLastTime        int64 `json:\"lastTime\" example:\"1700000000\"` // Current sync update time // 本次同步更新时间\n\tNeedModifyCount int64 `json:\"needModifyCount\" example:\"3\"`   // Number of folders needing modification // 需要修改的文件夹数量\n\tNeedDeleteCount int64 `json:\"needDeleteCount\" example:\"0\"`   // Number of folders needing deletion // 需要删除的文件夹数量\n}\n\n// FolderSyncRenameMessage message structure for folder rename during sync\n// FolderSyncRenameMessage 同步过程中文件夹重命名的消息结构\ntype FolderSyncRenameMessage struct {\n\tPath        string `json:\"path\" form:\"path\" binding:\"required\" example:\"NewFolder\"` // New path // 新路径\n\tPathHash    string `json:\"pathHash\" form:\"pathHash\" example:\"nfhash123\"`            // New path hash // 新路径哈希\n\tCtime       int64  `json:\"ctime\" form:\"ctime\" example:\"1700000000\"`                 // Creation timestamp // 创建时间戳\n\tMtime       int64  `json:\"mtime\" form:\"mtime\" example:\"1700000000\"`                 // Modification timestamp // 修改时间戳\n\tOldPath     string `json:\"oldPath\" form:\"oldPath\" example:\"OldFolder\"`              // Old path // 旧路径\n\tOldPathHash string `json:\"oldPathHash\" form:\"oldPathHash\" example:\"ofhash456\"`      // Old path hash // 旧路径哈希\n}\n\n// FolderSyncDeleteMessage message structure for folder deletion during sync\n// FolderSyncDeleteMessage 同步期间文件夹删除的消息结构\ntype FolderSyncDeleteMessage struct {\n\tPath     string `json:\"path\" form:\"path\" example:\"DeletedFolder\"`     // Folder path // 文件夹路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" example:\"dfhash789\"` // Path hash // 路径哈希值\n\tCtime    int64  `json:\"ctime\" form:\"ctime\" example:\"1700000000\"`      // Creation timestamp // 创建时间戳\n\tMtime    int64  `json:\"mtime\" form:\"mtime\" example:\"1700000000\"`      // Modification timestamp // 修改时间戳\n}\n\n// FolderSyncModifyMessage message content for folder modification or creation during sync\n// FolderSyncModifyMessage 同步期间文件夹修改或创建的消息内容\ntype FolderSyncModifyMessage struct {\n\tPath             string `json:\"path\" form:\"path\" example:\"Projects\"`                   // Folder path // 文件夹路径\n\tPathHash         string `json:\"pathHash\" form:\"pathHash\" example:\"fhash123\"`           // Path hash // 路径哈希值\n\tCtime            int64  `json:\"ctime\" form:\"ctime\" example:\"1700000000\"`               // Creation timestamp // 创建时间戳\n\tMtime            int64  `json:\"mtime\" form:\"mtime\" example:\"1700000000\"`               // Modification timestamp // 修改时间戳\n\tUpdatedTimestamp int64  `json:\"lastTime\" form:\"updatedTimestamp\" example:\"1700000000\"` // Record update timestamp // 记录更新时间戳\n}\n// FolderModifyAckMessage folder modify operation ACK\n// FolderModifyAckMessage 文件夹修改操作 ACK\ntype FolderModifyAckMessage struct {\n\tLastTime int64  `json:\"lastTime\"` // Server write timestamp // 服务端写入时间戳\n\tPath     string `json:\"path\"`     // Folder path // 文件夹路径\n\tPathHash string `json:\"pathHash\"` // Path hash // 路径哈希值\n}\n\n// FolderRenameAckMessage folder rename operation ACK\n// FolderRenameAckMessage 文件夹重命名操作 ACK\ntype FolderRenameAckMessage struct {\n\tLastTime int64  `json:\"lastTime\"` // Server write timestamp // 服务端写入时间戳\n\tPath     string `json:\"path\"`     // New folder path // 文件夹新路径\n\tPathHash string `json:\"pathHash\"` // Path hash // 路径哈希值\n}\n\n// FolderDeleteAckMessage folder delete operation ACK\n// FolderDeleteAckMessage 文件夹删除操作 ACK\ntype FolderDeleteAckMessage struct {\n\tLastTime int64  `json:\"lastTime\"` // Server write timestamp // 服务端写入时间戳\n\tPath     string `json:\"path\"`     // Folder path // 文件夹路径\n\tPathHash string `json:\"pathHash\"` // Path hash // 路径哈希值\n}\n"
  },
  {
    "path": "internal/dto/git_sync_dto.go",
    "content": "package dto\n\nimport \"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\n// GitSyncConfigRequest git repository sync task creation/update request\n// GitSyncConfigRequest git 仓库同步任务创建/更新请求\ntype GitSyncConfigRequest struct {\n\tID            int64  `json:\"id\" form:\"id\"`\n\tVault         string `json:\"vault\" form:\"vault\"` // Associated vault name // 关联笔记本名称\n\tRepoURL       string `json:\"repoUrl\" form:\"repoUrl\" binding:\"required\"`\n\tUsername      string `json:\"username\" form:\"username\"`\n\tPassword      string `json:\"password\" form:\"password\"`\n\tBranch        string `json:\"branch\" form:\"branch\"`\n\tIsEnabled     bool   `json:\"isEnabled\" form:\"isEnabled\"`\n\tDelay         int64  `json:\"delay\" form:\"delay\"` // Delay time (seconds) // 延迟时间（秒）\n\tRetentionDays int64  `json:\"retentionDays\" form:\"retentionDays\"`\n}\n\n// GitSyncValidateRequest git repository sync task parameter validation request\n// GitSyncValidateRequest git 仓库同步任务参数验证请求\ntype GitSyncValidateRequest struct {\n\tRepoURL  string `json:\"repoUrl\" form:\"repoUrl\" binding:\"required\"`\n\tUsername string `json:\"username\" form:\"username\"`\n\tPassword string `json:\"password\" form:\"password\"`\n\tBranch   string `json:\"branch\" form:\"branch\"`\n}\n\n// GitSyncExecuteRequest manually execute git repository sync task request\n// GitSyncExecuteRequest 手动执行 git 仓库同步任务请求\ntype GitSyncExecuteRequest struct {\n\tID int64 `json:\"id\" form:\"id\" binding:\"required\"`\n}\n\n// GitSyncCleanRequest cleanup git repository sync task workspace request\n// GitSyncCleanRequest 清理 git 仓库同步任务工作区请求\ntype GitSyncCleanRequest struct {\n\tConfigID int64 `json:\"configId\" form:\"configId\"`\n}\n\n// GitSyncDeleteRequest delete git repository sync task request\n// GitSyncDeleteRequest 删除 git 仓库同步任务请求\ntype GitSyncDeleteRequest struct {\n\tID int64 `json:\"id\" form:\"id\" binding:\"required\"`\n}\n\n// GitSyncConfigDTO git repository sync task DTO\n// GitSyncConfigDTO git 仓库同步任务 DTO\ntype GitSyncConfigDTO struct {\n\tID            int64      `json:\"id\"`            // Task ID // 任务ID\n\tUID           int64      `json:\"uid\"`           // User ID // 用户ID\n\tVault         string     `json:\"vault\"`         // Associated vault name // 关联库名称\n\tRepoURL       string     `json:\"repoUrl\"`       // Repository URL // 仓库地址\n\tUsername      string     `json:\"username\"`      // Username // 用户名\n\tPassword      string     `json:\"password\"`      // Password // 密码\n\tBranch        string     `json:\"branch\"`        // Branch // 分支\n\tIsEnabled     bool       `json:\"isEnabled\"`     // Is enabled // 是否启用\n\tDelay         int64      `json:\"delay\"`         // Delay time (seconds) // 延迟时间（秒）\n\tRetentionDays int64      `json:\"retentionDays\"` // History retention days // 历史记录保留天数\n\tLastSyncTime  timex.Time `json:\"lastSyncTime\"`  // Last sync time // 上次同步时间\n\tLastStatus    int64      `json:\"lastStatus\"`    // Last status (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Shutdown) // 上次状态 (0:Idle, 1:Running, 2:Success, 3:Failed, 4:Shutdown)\n\tLastMessage   string     `json:\"lastMessage\"`   // Last run result message // 上次运行结果消息\n\tCreatedAt     timex.Time `json:\"createdAt\"`     // Created at // 创建时间\n\tUpdatedAt     timex.Time `json:\"updatedAt\"`     // Updated at // 更新时间\n}\n\n// GitSyncHistoryRequest get sync history request\n// GitSyncHistoryRequest 获取同步历史请求\ntype GitSyncHistoryRequest struct {\n\tConfigID int64 `json:\"configId\" form:\"configId\"`\n\tPage     int   `json:\"page\" form:\"page\"`\n\tPageSize int   `json:\"pageSize\" form:\"pageSize\"`\n}\n\n// GitSyncHistoryDTO git sync history DTO\n// GitSyncHistoryDTO git 同步历史 DTO\ntype GitSyncHistoryDTO struct {\n\tID        int64      `json:\"id\"`\n\tConfigID  int64      `json:\"configId\"`\n\tStartTime timex.Time `json:\"startTime\"`\n\tEndTime   timex.Time `json:\"endTime\"`\n\tStatus    int64      `json:\"status\"` // 0:Idle, 1:Running, 2:Success, 3:Failed, 4:Shutdown\n\tMessage   string     `json:\"message\"`\n\tCreatedAt timex.Time `json:\"createdAt\"`\n}\n"
  },
  {
    "path": "internal/dto/note_dto.go",
    "content": "// Package dto Defines data transfer objects (request parameters and response structs)\n// Package dto 定义数据传输对象（请求参数和响应结构体）\npackage dto\n\nimport (\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"github.com/sergi/go-diff/diffmatchpatch\"\n)\n\n// NoteUpdateCheckRequest Client request parameters for checking if updates are needed\n// 客户端用于检查是否需要更新的请求参数\ntype NoteUpdateCheckRequest struct {\n\tVault       string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`       // Vault name // 保险库名称\n\tPath        string `json:\"path\" form:\"path\" binding:\"required\" example:\"ReadMe.md\"`       // Note path // 笔记路径\n\tPathHash    string `json:\"pathHash\" form:\"pathHash\" binding:\"required\" example:\"hash123\"` // Path hash // 路径哈希\n\tContentHash string `json:\"contentHash\" form:\"contentHash\" binding:\"\" example:\"chash456\"`  // Content hash // 内容哈希\n\tCtime       int64  `json:\"ctime\" form:\"ctime\" binding:\"required\" example:\"1700000000\"`    // Creation timestamp // 创建时间戳\n\tMtime       int64  `json:\"mtime\" form:\"mtime\" binding:\"required\" example:\"1700000000\"`    // Modification timestamp // 修改时间戳\n}\n\n// NoteModifyOrCreateRequest Request parameters for creating or modifying a note\n// 用于创建或修改笔记的请求参数\ntype NoteModifyOrCreateRequest struct {\n\tVault           string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`      // Vault name // 保险库名称\n\tPath            string `json:\"path\" form:\"path\" binding:\"required\" example:\"ReadMe.md\"`      // Note path // 笔记路径\n\tPathHash        string `json:\"pathHash\" form:\"pathHash\" example:\"hash123\"`                   // Path hash // 路径哈希\n\tBaseHash        string `json:\"baseHash\" form:\"baseHash\" binding:\"\" example:\"bhash789\"`       // Base hash for sync // 同步基准哈希\n\tBaseHashMissing bool   `json:\"baseHashMissing\" form:\"baseHashMissing\" example:\"false\"`       // Marks if baseHash is unavailable // 标记基准哈希是否缺失\n\tContent         string `json:\"content\" form:\"content\" binding:\"\" example:\"# Hello World\"`    // Note content // 笔记内容\n\tContentHash     string `json:\"contentHash\" form:\"contentHash\" binding:\"\" example:\"chash012\"` // Content hash // 内容哈希\n\tCtime           int64  `json:\"ctime\" form:\"ctime\" example:\"1700000000\"`                      // Creation timestamp // 创建时间戳\n\tMtime           int64  `json:\"mtime\" form:\"mtime\" example:\"1700000000\"`                      // Modification timestamp // 修改时间戳\n\tCreateOnly      bool   `json:\"createOnly\" form:\"createOnly\" example:\"false\"`                 // If true, fail if note already exists // 如果为 true，笔记已存在则失败\n}\n\n// ContentModifyRequest Request parameters for modifying content only\n// 专用于只修改内容的请求参数\ntype ContentModifyRequest struct {\n\tVault       string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`              // Vault name // 保险库名称\n\tPath        string `json:\"path\" form:\"path\" binding:\"required\" example:\"ReadMe.md\"`              // Note path // 笔记路径\n\tPathHash    string `json:\"pathHash\" form:\"pathHash\" binding:\"required\" example:\"hash123\"`        // Path hash // 路径哈希\n\tContent     string `json:\"content\" form:\"content\" binding:\"required\" example:\"Updated content\"`  // Note content // 笔记内容\n\tContentHash string `json:\"contentHash\" form:\"contentHash\" binding:\"required\" example:\"chash456\"` // Content hash // 内容哈希\n\tCtime       int64  `json:\"ctime\" form:\"ctime\" binding:\"required\" example:\"1700000000\"`           // Creation timestamp // 创建时间戳\n\tMtime       int64  `json:\"mtime\" form:\"mtime\" binding:\"required\" example:\"1700000000\"`           // Modification timestamp // 修改时间戳\n}\n\n// NoteDeleteRequest Parameters required for deleting a note\n// 删除笔记所需参数\ntype NoteDeleteRequest struct {\n\tVault    string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tPath     string `json:\"path\" form:\"path\" binding:\"required\" example:\"ReadMe.md\"` // Note path // 笔记路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" example:\"hash123\"`              // Path hash // 路径哈希\n}\n\n// NoteRestoreRequest parameters for restoring a note\n// NoteRestoreRequest 恢复笔记请求参数\ntype NoteRestoreRequest struct {\n\tVault    string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tPath     string `json:\"path\" form:\"path\" binding:\"required\" example:\"ReadMe.md\"` // Note path // 笔记路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" example:\"hash123\"`              // Path hash // 路径哈希\n}\n\n// NoteRecycleClearRequest clean recycle bin request\n// NoteRecycleClearRequest 清理回收站请求\ntype NoteRecycleClearRequest struct {\n\tVault    string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tPath     string `json:\"path\" form:\"path\" example:\"path/to/note.md\"`              // Note path, empty for all // 笔记路径，为空则清理全部\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" example:\"hash123\"`              // Path hash // 路径哈希\n}\n\n// NotePatchFrontmatterRequest parameters for patching note frontmatter\n// NotePatchFrontmatterRequest 修改笔记 Frontmatter 请求参数\ntype NotePatchFrontmatterRequest struct {\n\tVault    string                 `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`                                               // Vault name // 保险库名称\n\tPath     string                 `json:\"path\" form:\"path\" binding:\"required\" example:\"ReadMe.md\"`                                               // Note path // 笔记路径\n\tPathHash string                 `json:\"pathHash\" form:\"pathHash\" example:\"hash123\"`                                                            // Path hash // 路径哈希\n\tUpdates  map[string]interface{} `json:\"updates\" form:\"updates\" swaggertype:\"object,array,string\" `                                             // Fields to update // 待更新字段\n\tRemove   []string               `json:\"remove\" form:\"remove\" swaggertype:\"array,string\" swagexample:\"[\\\"item1\\\",\\\"item2\\\"]\" example:\"old_tag\"` // Fields to remove // 待移除字段\n}\n\n// NoteAppendRequest parameters for appending content to a note\n// NoteAppendRequest 追加笔记内容请求参数\ntype NoteAppendRequest struct {\n\tVault    string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`              // Vault name // 保险库名称\n\tPath     string `json:\"path\" form:\"path\" binding:\"required\" example:\"ReadMe.md\"`              // Note path // 笔记路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" example:\"hash123\"`                           // Path hash // 路径哈希\n\tContent  string `json:\"content\" form:\"content\" binding:\"required\" example:\"Appended content\"` // Content to append // 追加内容\n}\n\n// NotePrependRequest parameters for prepending content to a note\n// NotePrependRequest 在笔记头部添加内容请求参数\ntype NotePrependRequest struct {\n\tVault    string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`                 // Vault name // 保险库名称\n\tPath     string `json:\"path\" form:\"path\" binding:\"required\" example:\"ReadMe.md\"`                 // Note path // 笔记路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" example:\"hash123\"`                              // Path hash // 路径哈希\n\tContent  string `json:\"content\" form:\"content\" binding:\"required\" example:\"Prepended content\\n\"` // Content to prepend // 头部添加内容\n}\n\n// NoteReplaceRequest parameters for find/replace in a note\n// NoteReplaceRequest 笔记查找替换请求参数\ntype NoteReplaceRequest struct {\n\tVault         string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tPath          string `json:\"path\" form:\"path\" binding:\"required\" example:\"ReadMe.md\"` // Note path // 笔记路径\n\tPathHash      string `json:\"pathHash\" form:\"pathHash\" example:\"hash123\"`              // Path hash // 路径哈希\n\tFind          string `json:\"find\" form:\"find\" binding:\"required\" example:\"old text\"`  // String to find // 查找内容\n\tReplace       string `json:\"replace\" form:\"replace\" example:\"new text\"`               // String to replace with // 替换内容\n\tRegex         bool   `json:\"regex\" form:\"regex\" example:\"false\"`                      // Use regex // 使用正则\n\tAll           bool   `json:\"all\" form:\"all\" example:\"true\"`                           // Replace all matches // 替换所有\n\tFailIfNoMatch bool   `json:\"failIfNoMatch\" form:\"failIfNoMatch\" example:\"true\"`       // Fail if no match found // 若无匹配则失败\n}\n\n// NoteMoveRequest parameters for moving a note\n// NoteMoveRequest 移动笔记请求参数\ntype NoteMoveRequest struct {\n\tVault       string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`                      // Vault name // 保险库名称\n\tPath        string `json:\"path\" form:\"path\" binding:\"required\" example:\"Source.md\"`                      // Current path // 当前路径\n\tPathHash    string `json:\"pathHash\" form:\"pathHash\" example:\"src_hash123\"`                               // Current path hash // 当前路径哈希\n\tDestination string `json:\"destination\" form:\"destination\" binding:\"required\" example:\"Folder/Source.md\"` // Destination path // 目标路径\n\tOverwrite   bool   `json:\"overwrite\" form:\"overwrite\" example:\"false\"`                                   // Overwrite existing // 覆盖现有\n}\n\n// NoteLinkQueryRequest parameters for backlinks/outlinks query\n// NoteLinkQueryRequest 反向链接/出链查询请求参数\ntype NoteLinkQueryRequest struct {\n\tVault    string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tPath     string `json:\"path\" form:\"path\" binding:\"required\" example:\"ReadMe.md\"` // Note path // 笔记路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" example:\"hash123\"`              // Path hash // 路径哈希\n}\n\n// NoteSyncCheckRequest Parameters for checking synchronization of a single record\n// NoteSyncCheckRequest 同步检查单条记录的参数\ntype NoteSyncCheckRequest struct {\n\tPath        string `json:\"path\" form:\"path\" example:\"ReadMe.md\"`                          // Note path // 笔记路径\n\tPathHash    string `json:\"pathHash\" form:\"pathHash\" binding:\"required\" example:\"hash123\"` // Path hash // 路径哈希\n\tContentHash string `json:\"contentHash\" form:\"contentHash\" binding:\"\" example:\"chash456\"`  // Content hash // 内容哈希\n\tMtime       int64  `json:\"mtime\" form:\"mtime\" binding:\"required\" example:\"1700000000\"`    // Modification timestamp // 修改时间戳\n\tCtime       int64  `json:\"ctime\" form:\"ctime\" example:\"1700000000\"`                       // Creation timestamp // 创建时间戳\n}\n\n// NoteSyncDelNote parameters for deleting a note during sync\n// 同步删除笔记参数\ntype NoteSyncDelNote struct {\n\tPath     string `json:\"path\" form:\"path\" binding:\"required\" example:\"DeletedNote.md\"`   // Note path // 笔记路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" binding:\"required\" example:\"dhash789\"` // Path hash // 路径哈希\n}\n\n// NoteSyncRequest Synchronization request body\n// NoteSyncRequest 同步请求主体\ntype NoteSyncRequest struct {\n\tContext      string                 `json:\"context\" form:\"context\" example:\"task123\"`                // Context // 上下文\n\tVault        string                 `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tLastTime     int64                  `json:\"lastTime\" form:\"lastTime\" example:\"1700000000\"`           // Last sync time // 最后同步时间\n\tNotes        []NoteSyncCheckRequest `json:\"notes\" form:\"notes\"`                                      // Notes to check // 待检查笔记列表\n\tDelNotes     []NoteSyncDelNote      `json:\"delNotes\" form:\"delNotes\"`                                // Notes to delete // 待删除笔记列表\n\tMissingNotes []NoteSyncDelNote      `json:\"missingNotes\" form:\"missingNotes\"`                        // Missing notes // 缺失笔记列表\n}\n\n// ModifyMtimeFilesRequest Request for querying modified files by mtime\n// ModifyMtimeFilesRequest 用于按 mtime 查询修改文件\ntype ModifyMtimeFilesRequest struct {\n\tVault string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tMtime int64  `json:\"mtime\" form:\"mtime\" example:\"1700000000\"`                 // Threshold modification timestamp // 修改时间戳阈值\n}\n\n// NoteGetRequest Request parameters for retrieving a single note\n// NoteGetRequest 用于获取单条笔记的请求参数\ntype NoteGetRequest struct {\n\tVault     string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tPath      string `json:\"path\" form:\"path\" binding:\"required\" example:\"ReadMe.md\"` // Note path // 笔记路径\n\tPathHash  string `json:\"pathHash\" form:\"pathHash\" example:\"hash123\"`              // Path hash // 路径哈希\n\tIsRecycle bool   `json:\"isRecycle\" form:\"isRecycle\" example:\"false\"`              // Is in recycle bin // 是否在回收站\n}\n\n// NoteRenameRequest Parameters required for renaming a note\n// NoteRenameRequest 重命名笔记所需参数\ntype NoteRenameRequest struct {\n\tVault       string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`        // Vault name // 保险库名称\n\tPath        string `json:\"path\" form:\"path\" binding:\"required\" example:\"NewName.md\"`       // New path // 新路径\n\tPathHash    string `json:\"pathHash\" form:\"pathHash\" example:\"nhash123\"`                    // New path hash // 新路径哈希\n\tOldPath     string `json:\"oldPath\" form:\"oldPath\" binding:\"required\" example:\"OldName.md\"` // Old path // 旧路径\n\tOldPathHash string `json:\"oldPathHash\" form:\"oldPathHash\" example:\"ohash456\"`              // Old path hash // 旧路径哈希\n}\n\n// NoteListRequest Pagination parameters for retrieving the note list\n// NoteListRequest 获取笔记列表的分页参数\ntype NoteListRequest struct {\n\tVault         string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tKeyword       string `json:\"keyword\" form:\"keyword\" example:\"todo\"`                   // Search keyword // 搜索关键词\n\tIsRecycle     bool   `json:\"isRecycle\" form:\"isRecycle\" example:\"false\"`              // Is in recycle bin // 是否在回收站\n\tSearchMode    string `json:\"searchMode\" form:\"searchMode\" example:\"content\"`          // Search mode (path, content) // 搜索模式（路径、内容）\n\tSearchContent bool   `json:\"searchContent\" form:\"searchContent\" example:\"true\"`       // Whether to search content // 是否搜索内容\n\tSortBy        string `json:\"sortBy\" form:\"sortBy\" example:\"mtime\"`                    // Sort by field // 排序字段\n\tSortOrder     string `json:\"sortOrder\" form:\"sortOrder\" example:\"desc\"`               // Sort order // 排序顺序\n\tPaths         string `json:\"paths\" form:\"paths\" example:\"note1.md,note2.md\"`          // Comma-separated exact path list for share filter // 逗号分隔的精确路径列表，用于分享筛选\n}\n\n// NoteHistoryListRequest Note history list request parameters\n// NoteHistoryListRequest 笔记历史列表请求参数\ntype NoteHistoryListRequest struct {\n\tVault     string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tPath      string `json:\"path\" form:\"path\" binding:\"required\" example:\"ReadMe.md\"` // Note path // 笔记路径\n\tPathHash  string `json:\"pathHash\" form:\"pathHash\" example:\"hash123\"`              // Path hash // 路径哈希\n\tIsRecycle bool   `json:\"isRecycle\" form:\"isRecycle\" example:\"false\"`              // Is in recycle bin // 是否在回收站\n}\n\n// NoteHistoryRestoreRequest Request parameters for restoring a historical version\n// NoteHistoryRestoreRequest 历史版本恢复请求参数\ntype NoteHistoryRestoreRequest struct {\n\tVault     string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`   // Vault name // 保险库名称\n\tHistoryID int64  `json:\"historyId\" form:\"historyId\" binding:\"required\" example:\"1\"` // History version ID // 历史版本 ID\n}\n\n// ---------------- DTO / Response ----------------\n\n// NoteDTO Note data transfer object\n// NoteDTO 笔记数据传输对象\ntype NoteDTO struct {\n\tID               int64      `json:\"-\" form:\"id\"`                    // Note ID // 笔记 ID\n\tAction           string     `json:\"-\" form:\"action\"`                // Action // 动作\n\tPath             string     `json:\"path\" form:\"path\"`               // Note path // 笔记路径\n\tPathHash         string     `json:\"pathHash\" form:\"pathHash\"`       // Path hash // 路径哈希\n\tContent          string     `json:\"content\" form:\"content\"`         // Note content // 笔记内容\n\tContentHash      string     `json:\"contentHash\" form:\"contentHash\"` // Content hash // 内容哈希\n\tVersion          int64      `json:\"version\" form:\"version\"`         // Version number // 版本号\n\tCtime            int64      `json:\"ctime\" form:\"ctime\"`             // Creation timestamp // 创建时间戳\n\tMtime            int64      `json:\"mtime\" form:\"mtime\"`             // Modification timestamp // 修改时间戳\n\tSize             int64      `json:\"size\" form:\"size\"`               // Note size // 笔记大小\n\tClientName       string     `json:\"clientName\"`                     // Client name // 客户端名称\n\tClientType       string     `json:\"clientType\"`                     // Client type // 客户端类型\n\tClientVersion    string     `json:\"clientVersion\"`                  // Client version // 客户端版本\n\tUpdatedTimestamp int64      `json:\"lastTime\"`                       // Record update timestamp // 记录更新时间戳\n\tUpdatedAt        timex.Time `json:\"updatedAt\"`                      // Updated at time // 更新时间\n\tCreatedAt        timex.Time `json:\"createdAt\"`                      // Created at time // 创建时间\n}\n\n// NoteNoContentDTO Note DTO without content\n// NoteNoContentDTO 不包含内容的笔记 DTO\ntype NoteNoContentDTO struct {\n\tID               int64      `json:\"-\" form:\"id\"`                      // Note ID // 笔记 ID\n\tAction           string     `json:\"-\" form:\"action\"`                  // Action // 动作\n\tPath             string     `json:\"path\" form:\"path\"`                 // Note path // 笔记路径\n\tPathHash         string     `json:\"pathHash\" form:\"pathHash\"`         // Path hash // 路径哈希\n\tVersion          int64      `json:\"version\" form:\"version\"`           // Version number // 版本号\n\tCtime            int64      `json:\"ctime\" form:\"ctime\"`               // Creation timestamp // 创建时间戳\n\tMtime            int64      `json:\"mtime\" form:\"mtime\"`               // Modification timestamp // 修改时间戳\n\tSize             int64      `json:\"size\" form:\"size\"`                 // Note size // 笔记大小\n\tClientName       string     `json:\"clientName\"`                       // Client name // 客户端名称\n\tClientType       string     `json:\"clientType\"`                       // Client type // 客户端类型\n\tClientVersion    string     `json:\"clientVersion\"`                    // Client version // 客户端版本\n\tUpdatedTimestamp int64      `json:\"lastTime\" form:\"updatedTimestamp\"` // Record update timestamp // 记录更新时间戳\n\tUpdatedAt        timex.Time `json:\"updatedAt\"`                        // Updated at time // 更新时间\n\tCreatedAt        timex.Time `json:\"createdAt\"`                        // Created at time // 创建时间\n}\n\n// NoteReplaceResponse response for replace operation\n// NoteReplaceResponse 替换操作响应\ntype NoteReplaceResponse struct {\n\tMatchCount int      `json:\"matchCount\"` // Number of matches found // 匹配数量\n\tNote       *NoteDTO `json:\"note\"`       // Updated note data // 更新后的笔记数据\n}\n\n// NoteLinkItem represents a link in backlinks/outlinks response\n// NoteLinkItem 代表反向链接/出链响应中的链接项\ntype NoteLinkItem struct {\n\tPath     string `json:\"path\"`               // Target path // 目标路径\n\tLinkText string `json:\"linkText,omitempty\"` // Raw link text (optional) // 原始链接文本（可选）\n\tContext  string `json:\"context,omitempty\"`  // Text context around link // 链接文本上下文\n\tIsEmbed  bool   `json:\"isEmbed\"`            // Is it an embed (![[...]]) // 是否为嵌入\n}\n\n// NoteWithFileLinksResponse Note response structure with file links\n// NoteWithFileLinksResponse 带有文件链接的笔记响应结构体\ntype NoteWithFileLinksResponse struct {\n\tID               int64             `json:\"-\"`           // Note ID // 笔记 ID\n\tPath             string            `json:\"path\"`        // Note path // 笔记路径\n\tPathHash         string            `json:\"pathHash\"`    // Path hash // 路径哈希\n\tContent          string            `json:\"content\"`     // Note content // 笔记内容\n\tContentHash      string            `json:\"contentHash\"` // Content hash // 内容哈希\n\tFileLinks        map[string]string `json:\"fileLinks\"`   // Map of file link to actual path // 文件链接到实际路径的映射\n\tVersion          int64             `json:\"version\"`     // Version number // 版本号\n\tCtime            int64             `json:\"ctime\"`       // Creation timestamp // 创建时间戳\n\tMtime            int64             `json:\"mtime\"`       // Modification timestamp // 修改时间戳\n\tUpdatedTimestamp int64             `json:\"lastTime\"`    // Record update timestamp // 记录更新时间戳\n\tUpdatedAt        interface{}       `json:\"updatedAt\"`   // Updated at time // 更新时间\n\tCreatedAt        interface{}       `json:\"createdAt\"`   // Created at time // 创建时间\n}\n\n// NoteHistoryDTO Note history data transfer object\n// NoteHistoryDTO 笔记历史数据传输对象\ntype NoteHistoryDTO struct {\n\tID          int64                 `json:\"id\" form:\"id\"`                   // History entry ID // 历史项 ID\n\tNoteID      int64                 `json:\"noteId\" form:\"noteId\"`           // Associated note ID // 笔记 ID\n\tVaultID     int64                 `json:\"vaultId\" form:\"vaultId\"`         // Associated vault ID // 保险库 ID\n\tPath        string                `json:\"path\" form:\"path\"`               // Note path at that time // 当时的笔记路径\n\tDiffs       []diffmatchpatch.Diff `json:\"diffs\"`                          // Text differences // 文本差异内容\n\tContent     string                `json:\"content\" form:\"content\"`         // Full historical content // 完整历史内容\n\tContentHash string                `json:\"contentHash\" form:\"contentHash\"` // Content hash // 内容哈希\n\tClientName  string                `json:\"clientName\" form:\"clientName\"`   // Client that made changes // 产生变更的客户端\n\tClientType  string                `json:\"clientType\" form:\"clientType\"`   // Client type // 客户端类型\n\tClientVersion string              `json:\"clientVersion\" form:\"clientVersion\"` // Client version // 客户端版本\n\tVersion     int64                 `json:\"version\" form:\"version\"`         // Historical version number // 历史版本号\n\tCreatedAt   timex.Time            `json:\"createdAt\" form:\"createdAt\"`     // Creation time of this version // 此版本的创建时间\n}\n\n// NoteHistoryNoContentDTO Note history DTO without content\n// NoteHistoryNoContentDTO 不包含内容的笔记历史 DTO\ntype NoteHistoryNoContentDTO struct {\n\tID         int64      `json:\"id\" form:\"id\"`                 // History entry ID // 历史项 ID\n\tNoteID     int64      `json:\"noteId\" form:\"noteId\"`         // Associated note ID // 笔记 ID\n\tVaultID    int64      `json:\"vaultId\" form:\"vaultId\"`       // Associated vault ID // 保险库 ID\n\tPath       string     `json:\"path\" form:\"path\"`             // Note path at that time // 当时的笔记路径\n\tClientName string     `json:\"clientName\" form:\"clientName\"` // Client that made changes // 产生变更的客户端\n\tClientType string     `json:\"clientType\" form:\"clientType\"` // Client type // 客户端类型\n\tClientVersion string  `json:\"clientVersion\" form:\"clientVersion\"` // Client version // 客户端版本\n\tVersion    int64      `json:\"version\" form:\"version\"`       // Historical version number // 历史版本号\n\tCreatedAt  timex.Time `json:\"createdAt\" form:\"createdAt\"`   // Creation time of this version // 此版本的创建时间\n}\n"
  },
  {
    "path": "internal/dto/note_dto_ws.go",
    "content": "package dto\n\n// NoteSyncRenameMessage message structure for note rename during sync\n// NoteSyncRenameMessage 同步过程中笔记重命名的消息结构\ntype NoteSyncRenameMessage struct {\n\tPath             string `json:\"path\" form:\"path\" binding:\"required\" example:\"NewName.md\"` // New path // 新路径\n\tPathHash         string `json:\"pathHash\" form:\"pathHash\" example:\"nfhash123\"`             // New path hash // 新路径哈希\n\tContentHash      string `json:\"contentHash\" form:\"contentHash\" example:\"chash456\"`        // Content hash // 内容哈希\n\tCtime            int64  `json:\"ctime\" form:\"ctime\" example:\"1700000000\"`                  // Creation timestamp // 创建时间戳\n\tMtime            int64  `json:\"mtime\" form:\"mtime\" example:\"1700000000\"`                  // Modification timestamp // 修改时间戳\n\tSize             int64  `json:\"size\" form:\"size\" example:\"1024\"`                          // File size // 文件大小\n\tOldPath          string `json:\"oldPath\" form:\"oldPath\" example:\"OldName.md\"`              // Old path // 旧路径\n\tOldPathHash      string `json:\"oldPathHash\" form:\"oldPathHash\" example:\"ofhash456\"`       // Old path hash // 旧路径哈希\n\tUpdatedTimestamp int64  `json:\"lastTime\" form:\"updatedTimestamp\" example:\"1700000000\"`    // Record update timestamp // 记录更新时间戳\n}\n\n// NoteSyncModifyMessage message content for note modification or creation\n// NoteSyncModifyMessage 笔记修改或创建的消息内容\ntype NoteSyncModifyMessage struct {\n\tPath             string `json:\"path\" form:\"path\" example:\"ReadMe.md\"`                  // Note path // 笔记路径\n\tPathHash         string `json:\"pathHash\" form:\"pathHash\" example:\"nhash123\"`           // Path hash // 路径哈希值\n\tContent          string `json:\"content\" form:\"content\" example:\"# Hello World\"`        // Note content // 笔记内容\n\tContentHash      string `json:\"contentHash\" form:\"contentHash\" example:\"chash456\"`     // Content hash // 内容哈希\n\tCtime            int64  `json:\"ctime\" form:\"ctime\" example:\"1700000000\"`               // Creation timestamp // 创建时间戳\n\tMtime            int64  `json:\"mtime\" form:\"mtime\" example:\"1700000000\"`               // Modification timestamp // 修改时间戳\n\tUpdatedTimestamp int64  `json:\"lastTime\" form:\"updatedTimestamp\" example:\"1700000000\"` // Record update timestamp // 记录更新时间戳\n}\n\n// NoteSyncEndMessage message structure returned when sync ends\n// NoteSyncEndMessage 同步结束时返回的信息结构\ntype NoteSyncEndMessage struct {\n\tLastTime           int64 `json:\"lastTime\" form:\"lastTime\" example:\"1700000000\"`            // Current sync update time // 本次同步更新时间\n\tNeedUploadCount    int64 `json:\"needUploadCount\" form:\"needUploadCount\" example:\"10\"`      // Number of notes needing upload // 需要上传的笔记数量\n\tNeedModifyCount    int64 `json:\"needModifyCount\" form:\"needModifyCount\" example:\"5\"`       // Number of notes needing modification // 需要修改的数量\n\tNeedSyncMtimeCount int64 `json:\"needSyncMtimeCount\" form:\"needSyncMtimeCount\" example:\"2\"` // Number of notes needing mtime sync // 需要同步修改时间的数量\n\tNeedDeleteCount    int64 `json:\"needDeleteCount\" form:\"needDeleteCount\" example:\"0\"`       // Number of notes needing deletion // 需要删除的数量\n}\n\n// NoteSyncNeedPushMessage server informs client of file info needing push\n// NoteSyncNeedPushMessage 服务端告知客户端需要推送的文件信息\ntype NoteSyncNeedPushMessage struct {\n\tPath     string `json:\"path\" form:\"path\" example:\"ReadMe.md\"`        // Note path // 笔记路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" example:\"nhash123\"` // Path hash // 路径哈希值\n}\n\n// NoteSyncMtimeMessage message structure for updating mtime during sync\n// NoteSyncMtimeMessage 同步时用于更新 mtime 的消息结构\ntype NoteSyncMtimeMessage struct {\n\tPath             string `json:\"path\" form:\"path\" example:\"ReadMe.md\"`                  // Note path // 笔记路径\n\tCtime            int64  `json:\"ctime\" form:\"ctime\" example:\"1700000000\"`               // Creation timestamp // 创建时间戳\n\tMtime            int64  `json:\"mtime\" form:\"mtime\" example:\"1700000000\"`               // Modification timestamp // 修改时间戳\n\tUpdatedTimestamp int64  `json:\"lastTime\" form:\"updatedTimestamp\" example:\"1700000000\"` // Record update timestamp // 记录更新时间戳\n}\n\n// NoteSyncDeleteMessage message structure for note deletion\n// NoteSyncDeleteMessage 笔记删除的消息结构\ntype NoteSyncDeleteMessage struct {\n\tPath             string `json:\"path\" form:\"path\" example:\"DeletedNote.md\"`             // Note path // 笔记路径\n\tPathHash         string `json:\"pathHash\" form:\"pathHash\" example:\"dnhash789\"`          // Path hash // 路径哈希值\n\tCtime            int64  `json:\"ctime\" form:\"ctime\" example:\"1700000000\"`               // Creation timestamp // 创建时间戳\n\tMtime            int64  `json:\"mtime\" form:\"mtime\" example:\"1700000000\"`               // Modification timestamp // 修改时间戳\n\tSize             int64  `json:\"size\" form:\"size\" example:\"1024\"`                       // File size // 文件大小\n\tUpdatedTimestamp int64  `json:\"lastTime\" form:\"updatedTimestamp\" example:\"1700000000\"` // Record update timestamp // 记录更新时间戳\n}\n\n// NoteModifyAckMessage note modify operation ACK, sent back to sender after server processes NoteModify\n// NoteModifyAckMessage 笔记修改操作 ACK，服务端处理完 NoteModify 后回发给发送方\ntype NoteModifyAckMessage struct {\n\tLastTime int64  `json:\"lastTime\"` // Server write timestamp // 服务端写入时间戳\n\tPath     string `json:\"path\"`     // Note path // 笔记路径\n\tPathHash string `json:\"pathHash\"` // Path hash // 路径哈希值\n}\n\n// NoteRenameAckMessage note rename operation ACK, sent back to sender after server processes NoteRename\n// NoteRenameAckMessage 笔记重命名操作 ACK，服务端处理完 NoteRename 后回发给发送方\ntype NoteRenameAckMessage struct {\n\tLastTime int64  `json:\"lastTime\"` // Server write timestamp // 服务端写入时间戳\n\tPath     string `json:\"path\"`     // New note path after rename // 重命名后的笔记新路径\n\tPathHash string `json:\"pathHash\"` // Path hash // 路径哈希值\n}\n\n// NoteDeleteAckMessage note delete operation ACK, sent back to sender after server processes NoteDelete\n// NoteDeleteAckMessage 笔记删除操作 ACK，服务端处理完 NoteDelete 后回发给发送方\ntype NoteDeleteAckMessage struct {\n\tLastTime int64  `json:\"lastTime\"` // Server write timestamp // 服务端写入时间戳\n\tPath     string `json:\"path\"`     // Note path // 笔记路径\n\tPathHash string `json:\"pathHash\"` // Path hash // 路径哈希值\n}\n"
  },
  {
    "path": "internal/dto/setting_dto.go",
    "content": "// Package dto Defines data transfer objects (request parameters and response structs)\n// Package dto 定义数据传输对象（请求参数和响应结构体）\npackage dto\n\nimport \"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\n// SettingUpdateCheckRequest Client request parameters for checking setting updates\n// 客户端检查更新请求参数\ntype SettingUpdateCheckRequest struct {\n\tVault       string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`       // Vault name // 保险库名称\n\tPath        string `json:\"path\" form:\"path\" binding:\"required\" example:\"User/Theme\"`      // Setting path // 配置路径\n\tPathHash    string `json:\"pathHash\" form:\"pathHash\" binding:\"required\" example:\"hash123\"` // Path hash // 路径哈希\n\tContentHash string `json:\"contentHash\" form:\"contentHash\" example:\"chash456\"`             // Content hash // 内容哈希\n\tCtime       int64  `json:\"ctime\" form:\"ctime\" binding:\"required\" example:\"1700000000\"`    // Creation timestamp // 创建时间戳\n\tMtime       int64  `json:\"mtime\" form:\"mtime\" binding:\"required\" example:\"1700000000\"`    // Modification timestamp // 修改时间戳\n}\n\n// SettingModifyOrCreateRequest Request parameters for creating or modifying settings\n// 修改或创建配置参数\ntype SettingModifyOrCreateRequest struct {\n\tVault       string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`  // Vault name // 保险库名称\n\tPath        string `json:\"path\" form:\"path\" binding:\"required\" example:\"User/Theme\"` // Setting path // 配置路径\n\tPathHash    string `json:\"pathHash\" form:\"pathHash\" example:\"hash123\"`               // Path hash // 路径哈希\n\tContent     string `json:\"content\" form:\"content\" example:\"dark\"`                    // Setting content // 配置内容\n\tContentHash string `json:\"contentHash\" form:\"contentHash\" example:\"chash456\"`        // Content hash // 内容哈希\n\tCtime       int64  `json:\"ctime\" form:\"ctime\" example:\"1700000000\"`                  // Creation timestamp // 创建时间戳\n\tMtime       int64  `json:\"mtime\" form:\"mtime\" example:\"1700000000\"`                  // Modification timestamp // 修改时间戳\n}\n\n// SettingDeleteRequest Parameters for deleting settings\n// 删除配置参数\ntype SettingDeleteRequest struct {\n\tVault    string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`  // Vault name // 保险库名称\n\tPath     string `json:\"path\" form:\"path\" binding:\"required\" example:\"User/Theme\"` // Setting path // 配置路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" example:\"hash123\"`               // Path hash // 路径哈希\n}\n\n// SettingClearRequest Parameters for clearing settings\n// 清除配置参数\ntype SettingClearRequest struct {\n\tVault string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n}\n\n// SettingListRequest Parameters for listing settings\n// 获取配置列表参数\ntype SettingListRequest struct {\n\tVault   string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tKeyword string `json:\"keyword\" form:\"keyword\" example:\"User/\"`                  // Keyword // 关键词\n}\n\n// SettingRenameRequest Parameters for renaming settings\n// 重命名配置参数\ntype SettingRenameRequest struct {\n\tVault       string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"`      // Vault name // 保险库名称\n\tOldPath     string `json:\"oldPath\" form:\"oldPath\" binding:\"required\" example:\"Old/Path\"` // Old path // 旧路径\n\tOldPathHash string `json:\"oldPathHash\" form:\"oldPathHash\" example:\"oldhash123\"`          // Old path hash // 旧路径哈希\n\tNewPath     string `json:\"newPath\" form:\"newPath\" binding:\"required\" example:\"New/Path\"` // New path // 新路径\n\tNewPathHash string `json:\"newPathHash\" form:\"newPathHash\" example:\"newhash456\"`          // New path hash // 新路径哈希\n}\n\n// SettingGetRequest Parameters for retrieving a single setting\n// 获取单条配置参数\ntype SettingGetRequest struct {\n\tVault    string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tPath     string `json:\"path\" form:\"path\" example:\"User/Theme\"`                   // Setting path // 配置路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" example:\"hash123\"`              // Path hash // 路径哈希\n}\n\n// SettingSyncCheckRequest Parameters for checking synchronization of a single setting\n// 单条同步检查参数\ntype SettingSyncCheckRequest struct {\n\tPath        string `json:\"path\" form:\"path\" example:\"User/Theme\"`                         // Setting path // 配置路径\n\tPathHash    string `json:\"pathHash\" form:\"pathHash\" binding:\"required\" example:\"hash123\"` // Path hash // 路径哈希\n\tContentHash string `json:\"contentHash\" form:\"contentHash\" example:\"chash456\"`             // Content hash // 内容哈希\n\tMtime       int64  `json:\"mtime\" form:\"mtime\" binding:\"required\" example:\"1700000000\"`    // Modification timestamp // 修改时间戳\n}\n\n// SettingSyncDelSetting Parameters for deleting sets during sync\n// 同步删除配置参数\ntype SettingSyncDelSetting struct {\n\tPath     string `json:\"path\" form:\"path\" binding:\"required\" example:\"DeletedSetting\"`   // Setting path // 配置路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" binding:\"required\" example:\"dhash789\"` // Path hash // 路径哈希\n}\n\n// SettingSyncRequest Synchronization request parameters\n// 同步请求参数\ntype SettingSyncRequest struct {\n\tContext         string                    `json:\"context\" form:\"context\" example:\"task123\"`                // Context // 上下文\n\tVault           string                    `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tLastTime        int64                     `json:\"lastTime\" form:\"lastTime\" example:\"1700000000\"`           // Last sync time // 最后同步时间\n\tCover           bool                      `json:\"cover\" form:\"cover\" example:\"false\"`                      // Whether to cover existing // 是否覆盖现有配置\n\tSettings        []SettingSyncCheckRequest `json:\"settings\" form:\"settings\"`                                // Settings to check // 待检查配置列表\n\tDelSettings     []SettingSyncDelSetting   `json:\"delSettings\" form:\"delSettings\"`                          // Settings to delete // 待删除配置列表\n\tMissingSettings []SettingSyncDelSetting   `json:\"missingFiles\" form:\"missingFiles\"`                        // Missing settings // 缺失配置列表\n}\n\n// ---------------- DTO / Response ----------------\n\n// SettingDTO Setting data transfer object\n// SettingDTO 配置数据传输对象\ntype SettingDTO struct {\n\tID               int64      `json:\"id\" form:\"id\"`                     // Setting ID // 配置 ID\n\tAction           string     `json:\"-\" form:\"action\"`                  // Action // 动作\n\tPath             string     `json:\"path\" form:\"path\"`                 // Setting path // 配置路径\n\tPathHash         string     `json:\"pathHash\" form:\"pathHash\"`         // Path hash // 路径哈希值\n\tContent          string     `json:\"content\" form:\"content\"`           // Setting content // 配置内容\n\tContentHash      string     `json:\"contentHash\" form:\"contentHash\"`   // Content hash // 内容哈希\n\tCtime            int64      `json:\"ctime\" form:\"ctime\"`               // Creation timestamp // 创建时间戳\n\tMtime            int64      `json:\"mtime\" form:\"mtime\"`               // Modification timestamp // 修改时间戳\n\tUpdatedTimestamp int64      `json:\"lastTime\" form:\"updatedTimestamp\"` // Record update timestamp // 记录更新时间戳\n\tUpdatedAt        timex.Time `json:\"updatedAt\"`                        // Updated at time // 更新时间\n\tCreatedAt        timex.Time `json:\"createdAt\"`                        // Created at time // 创建时间\n}\n"
  },
  {
    "path": "internal/dto/setting_dto_ws.go",
    "content": "package dto\n\n// SettingSyncModifyMessage message content for setting modification or creation during sync\n// 同步期间配置修改或创建的消息内容\ntype SettingSyncModifyMessage struct {\n\tVault            string `json:\"vault\" form:\"vault\" example:\"MyVault\"`                  // Vault name // 保险库名称\n\tPath             string `json:\"path\" form:\"path\" example:\"User/Theme\"`                 // Setting path // 配置路径\n\tPathHash         string `json:\"pathHash\" form:\"pathHash\" example:\"shash123\"`           // Path hash // 路径哈希值\n\tContent          string `json:\"content\" form:\"content\" example:\"dark\"`                 // Setting content // 配置内容\n\tContentHash      string `json:\"contentHash\" form:\"contentHash\" example:\"chash456\"`     // Content hash // 内容哈希\n\tCtime            int64  `json:\"ctime\" form:\"ctime\" example:\"1700000000\"`               // Creation timestamp // 创建时间戳\n\tMtime            int64  `json:\"mtime\" form:\"mtime\" example:\"1700000000\"`               // Modification timestamp // 修改时间戳\n\tUpdatedTimestamp int64  `json:\"lastTime\" form:\"updatedTimestamp\" example:\"1700000000\"` // Record update timestamp // 记录更新时间戳\n}\n\n// SettingSyncEndMessage defines the setting sync end message structure\n// SettingSyncEndMessage 定义配置同步结束的消息结构\ntype SettingSyncEndMessage struct {\n\tLastTime           int64 `json:\"lastTime\" form:\"lastTime\" example:\"1700000000\"`            // Last sync time // 最后同步时间\n\tNeedUploadCount    int64 `json:\"needUploadCount\" form:\"needUploadCount\" example:\"3\"`       // Number of settings needing upload // 需要上传的数量\n\tNeedModifyCount    int64 `json:\"needModifyCount\" form:\"needModifyCount\" example:\"1\"`       // Number of settings needing modification // 需要修改的数量\n\tNeedSyncMtimeCount int64 `json:\"needSyncMtimeCount\" form:\"needSyncMtimeCount\" example:\"1\"` // Number of settings needing mtime sync // 需要同步修改时间的数量\n\tNeedDeleteCount    int64 `json:\"needDeleteCount\" form:\"needDeleteCount\" example:\"0\"`       // Number of settings needing deletion // 需要删除的数量\n}\n\n// SettingSyncNeedUploadMessage defines the message structure informing client that setting upload is needed during sync\n// SettingSyncNeedUploadMessage 同步期间服务端通知客户端需要上传配置的消息结构\ntype SettingSyncNeedUploadMessage struct {\n\tPath string `json:\"path\" form:\"path\" example:\"User/Theme\"` // Setting path // 配置路径\n}\n\n// SettingSyncMtimeMessage defines the message structure for setting modification time sync during sync\n// SettingSyncMtimeMessage 同步期间配置元数据更新消息结构\ntype SettingSyncMtimeMessage struct {\n\tPath             string `json:\"path\" form:\"path\" example:\"User/Theme\"`                 // Setting path // 配置路径\n\tCtime            int64  `json:\"ctime\" form:\"ctime\" example:\"1700000000\"`               // Creation timestamp // 创建时间戳\n\tMtime            int64  `json:\"mtime\" form:\"mtime\" example:\"1700000000\"`               // Modification timestamp // 修改时间戳\n\tUpdatedTimestamp int64  `json:\"lastTime\" form:\"updatedTimestamp\" example:\"1700000000\"` // Record update timestamp // 记录更新时间戳\n}\n\n// SettingSyncDeleteMessage defines the message structure for setting deletion during sync\n// SettingSyncDeleteMessage 同步期间配置删除的消息结构\ntype SettingSyncDeleteMessage struct {\n\tPath             string `json:\"path\" form:\"path\" example:\"DeletedSetting\"`             // Setting path // 配置路径\n\tPathHash         string `json:\"pathHash\" form:\"pathHash\" example:\"shash789\"`           // Path hash // 路径哈希值\n\tCtime            int64  `json:\"ctime\" form:\"ctime\" example:\"1700000000\"`               // Creation timestamp // 创建时间戳\n\tMtime            int64  `json:\"mtime\" form:\"mtime\" example:\"1700000000\"`               // Modification timestamp // 修改时间戳\n\tUpdatedTimestamp int64  `json:\"lastTime\" form:\"updatedTimestamp\" example:\"1700000000\"` // Record update timestamp // 记录更新时间戳\n}\n// SettingModifyAckMessage setting modify operation ACK\n// SettingModifyAckMessage 配置修改操作 ACK\ntype SettingModifyAckMessage struct {\n\tLastTime int64  `json:\"lastTime\"` // Server write timestamp // 服务端写入时间戳\n\tPath     string `json:\"path\"`     // Setting path // 配置路径\n\tPathHash string `json:\"pathHash\"` // Path hash // 路径哈希值\n}\n\n// SettingDeleteAckMessage setting delete operation ACK\n// SettingDeleteAckMessage 配置删除操作 ACK\ntype SettingDeleteAckMessage struct {\n\tLastTime int64  `json:\"lastTime\"` // Server write timestamp // 服务端写入时间戳\n\tPath     string `json:\"path\"`     // Setting path // 配置路径\n\tPathHash string `json:\"pathHash\"` // Path hash // 路径哈希值\n}\n"
  },
  {
    "path": "internal/dto/share_dto.go",
    "content": "package dto\n\nimport (\n\t\"time\"\n\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n)\n\n// ShareCreateRequest Request parameters for creating a share\n// 创建分享请求\ntype ShareCreateRequest struct {\n\tVault    string `json:\"vault\" binding:\"required\" example:\"defaultVault\"` // Vault name // 保险库名称\n\tPath     string `json:\"path\" binding:\"required\" example:\"ReadMe.md\"`     // Resource path // 资源路径\n\tPathHash string `json:\"pathHash\" binding:\"required\" example:\"hash123\"`   // Resource path Hash // 资源路径哈希\n\tPassword string `json:\"password\" example:\"123456\"`                       // Share password // 分享密码\n}\n\n// ShareQueryRequest Request parameters for querying a share\n// 查询分享请求\ntype ShareQueryRequest struct {\n\tVault    string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"defaultVault\"`  // Vault name // 保险库名称\n\tPath     string `json:\"path\" form:\"path\" binding:\"required\" example:\"ReadMe.md\"`       // Resource path // 资源路径\n\tPathHash string `json:\"pathHash\" form:\"pathHash\" binding:\"required\" example:\"hash123\"` // Resource path Hash // 资源路径哈希\n}\n\n// ShareCancelRequest Request parameters for cancelling a share\n// 取消分享请求\ntype ShareCancelRequest struct {\n\tVault    string `json:\"vault\" binding:\"required\" example:\"defaultVault\"` // Vault name // 保险库名称\n\tID       int64  `json:\"id\" example:\"1\"`                                  // Share ID (optional) // 分享 ID (可选)\n\tPath     string `json:\"path\" example:\"ReadMe.md\"`                        // Resource path (optional) // 资源路径 (可选)\n\tPathHash string `json:\"pathHash\" example:\"hash123\"`                      // Resource path Hash (optional) // 资源路径哈希 (可选)\n}\n\n// ShareResourceRequest Request parameters for retrieving a shared resource\n// 分享资源获取请求\ntype ShareResourceRequest struct {\n\tID       int64  `json:\"id\" form:\"id\" binding:\"required\" example:\"1\"` // Resource ID // 资源 ID\n\tPassword string `json:\"password\" form:\"password\" example:\"123456\"`   // Share password // 分享密码\n}\n\n// SharePasswordUpdateRequest Request parameters for updating share password\n// 更新分享密码请求\ntype SharePasswordUpdateRequest struct {\n\tVault    string `json:\"vault\" binding:\"required\" example:\"test\"`          // Vault name // 保险库名称\n\tPath     string `json:\"path\" binding:\"required\" example:\"未命名.md\"`         // Resource path // 资源路径\n\tPathHash string `json:\"pathHash\" binding:\"required\" example:\"-677306325\"` // Resource path Hash // 资源路径哈希\n\tPassword string `json:\"password\" example:\"123456\"`                        // New password // 新密码\n}\n\n// ShareShortLinkCreateRequest Request parameters for creating a short link\n// 创建短链请求\ntype ShareShortLinkCreateRequest struct {\n\tVault    string `json:\"vault\" binding:\"required\" example:\"work\"`                            // Vault name // 库名\n\tPath     string `json:\"path\" binding:\"required\" example:\"notes/todo.md\"`                    // Path // 路径\n\tPathHash string `json:\"pathHash\" binding:\"required\" example:\"...\"`                          // Path hash // 路径哈希\n\tURL      string `json:\"url\" example:\"https://example.com/share/129/CNmkmQlq0s-4elT3NuZG2w\"` // Full share URL from client; if provided, used directly without regenerating token // 客户端传入的完整分享链接，非空时直接使用，不重新生成 token\n\tIsForce  bool   `json:\"is_force\" example:\"false\"`                                           // Whether to force regeneration // 是否强制重新生成\n}\n\n// ShareListRequest Request parameters for listing shares\n// 分享列表请求\ntype ShareListRequest struct {\n\tpkgapp.PaginationRequest\n\tSortBy    string `json:\"sort_by\" form:\"sort_by\" example:\"created_at\"` // Sort field: created_at, updated_at, expires_at // 排序字段: created_at, updated_at, expires_at\n\tSortOrder string `json:\"sort_order\" form:\"sort_order\" example:\"desc\"` // Sort direction: asc or desc // 排序方向: asc 或 desc\n}\n\n// ---------------- DTO / Response ----------------\n\n// ShareCreateResponse Response for creating a share\n// 创建分享响应\ntype ShareCreateResponse struct {\n\tID         int64     `json:\"id\"`         // ID of the note or file table (primary resource ID) // 笔记或文件表 ID（主资源 ID）\n\tType       string    `json:\"type\"`       // Resource type: note or file // 资源类型：笔记（note）或文件（file）\n\tToken      string    `json:\"token\"`      // Share Token // 分享 Token\n\tIsPassword bool      `json:\"isPassword\"` // Whether password is set // 是否设置了密码\n\tExpiresAt  time.Time `json:\"expiresAt\"`  // Expiration time // 过期时间\n\tShortLink  string    `json:\"shortLink\"`  // Short link // 短链\n}\n\n// ShareListItem Represents a share item in list\n// ShareListItem 分享列表项\ntype ShareListItem struct {\n\tID           int64               `json:\"id\"`           // Share ID // 分享记录 ID\n\tUID          int64               `json:\"uid\"`          // User ID // 用户 ID\n\tTitle        string              `json:\"title\"`        // Resource title (note title or file name) // 资源标题（笔记标题或文件名）\n\tURL          string              `json:\"url\"`          // Share URL (path format: /id/token) // 分享 URL (路径格式: /id/token)\n\tResources    map[string][]string `json:\"res\"`          // Authorized resources // 资源授权列表\n\tNotePath     string              `json:\"notePath\"`     // Note path, for frontend share filter matching // 笔记路径，用于前端分享筛选匹配\n\tVaultName    string              `json:\"vaultName\"`    // Vault name where the note belongs // 笔记所属仓库名\n\tIsPassword   bool                `json:\"isPassword\"`   // Whether password is set // 是否设置了密码\n\tStatus       int64               `json:\"status\"`       // Status: 1-Active, 2-Cancelled // 状态: 1-有效, 2-已撤销\n\tViewCount    int64               `json:\"viewCount\"`    // View count // 访问次数\n\tLastViewedAt time.Time           `json:\"lastViewedAt\"` // Last viewed time // 最后访问时间\n\tExpiresAt    time.Time           `json:\"expiresAt\"`    // Expiration time // 过期时间\n\tShortLink    string              `json:\"shortLink\"`    // Short link // 短链\n\tCreatedAt    time.Time           `json:\"createdAt\"` // Created at // 创建时间\n\tUpdatedAt    time.Time           `json:\"updatedAt\"` // Updated at // 更新时间\n}\n\n// ShareListResponse Response for listing shares\n// 分享列表响应\ntype ShareListResponse struct {\n\tItems []*ShareListItem `json:\"items\"` // Share list // 分享列表\n}\n"
  },
  {
    "path": "internal/dto/storage_dto.go",
    "content": "package dto\n\nimport \"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\n// StorageDTO Storage configuration DTO\n// StorageDTO 存储配置 DTO\ntype StorageDTO struct {\n\tID              int64      `json:\"id\"`              // ID // ID\n\tUID             int64      `json:\"-\"`               // User UID // 用户 ID\n\tType            string     `json:\"type\"`            // Storage type // 存储类型\n\tEndpoint        string     `json:\"endpoint\"`        // Endpoint // 访问端点\n\tRegion          string     `json:\"region\"`          // Region // 区域\n\tAccountID       string     `json:\"accountId\"`       // Account ID // 账户 ID\n\tBucketName      string     `json:\"bucketName\"`      // Bucket name // 存储桶名称\n\tAccessKeyID     string     `json:\"accessKeyId\"`     // Access key ID // 访问密钥 ID\n\tAccessKeySecret string     `json:\"accessKeySecret\"` // Access key secret // 访问密钥秘密\n\tCustomPath      string     `json:\"customPath\"`      // Custom path // 自定义路径\n\tAccessURLPrefix string     `json:\"accessUrlPrefix\"` // Access URL prefix // 访问地址前缀\n\tUser            string     `json:\"user\"`            // Username // 用户名\n\tPassword        string     `json:\"password\"`        // Password // 密码\n\tIsEnabled       bool       `json:\"isEnabled\"`       // Is enabled // 是否启用\n\tIsDeleted       bool       `json:\"-\"`               // Is deleted // 是否已删除\n\tCreatedAt       timex.Time `json:\"createdAt\"`       // Created at // 创建时间\n\tUpdatedAt       timex.Time `json:\"updatedAt\"`       // Updated at // 更新时间\n}\n\n// StoragePostRequest Storage configuration create/update request\n// StoragePostRequest 存储配置创建/更新请求\ntype StoragePostRequest struct {\n\tID              int64  `form:\"id\" example:\"1\"`                                                              // ID // ID\n\tType            string `form:\"type\" binding:\"required,gte=1\" example:\"local-fs\"`                            // Storage type // 类型\n\tEndpoint        string `form:\"endpoint\" example:\"oss-cn-hangzhou.aliyuncs.com\"`                             // Endpoint (OSS) // 端点 oss\n\tRegion          string `form:\"region\" example:\"us-east-1\"`                                                  // Region (S3) // 区域 s3\n\tAccountID       string `form:\"accountId\" example:\"123456789\"`                                               // Account ID (R2) // 账户ID r2\n\tBucketName      string `form:\"bucketName\" example:\"my-bucket\"`                                              // Bucket name // 存储桶名称\n\tAccessKeyID     string `form:\"accessKeyId\" example:\"\"`                                                      // Access key ID // 访问密钥ID\n\tAccessKeySecret string `form:\"accessKeySecret\" example:\"\"`                                                  // Access key secret // 访问密钥秘密\n\tCustomPath      string `form:\"customPath\" example:\"/backups\"`                                               // Custom path // 自定义路径\n\tAccessURLPrefix string `form:\"accessUrlPrefix\"  binding:\"required,min=2,max=100\" example:\"https://cdn.com\"` // Access URL prefix // 访问地址前缀\n\tUser            string `form:\"user\" example:\"admin\"`                                                        // Username // 访问用户名\n\tPassword        string `form:\"password\" example:\"secret_password\"`                                          // Password // 密码\n\tIsEnabled       int64  `form:\"isEnabled\" example:\"1\"`                                                       // Is enabled // 是否启用\n}\n\n// StorageGetRequest Storage configuration retrieval request\n// StorageGetRequest 存储配置获取请求\ntype StorageGetRequest struct {\n\tID int64 `json:\"id\" form:\"id\" binding:\"required\" example:\"1\"` // ID // ID\n}\n"
  },
  {
    "path": "internal/dto/sync_log_dto.go",
    "content": "// Package dto Defines data transfer objects (request parameters and response structs)\n// Package dto 定义数据传输对象（请求参数和响应结构体）\npackage dto\n\nimport \"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\n// SyncLogListRequest Request parameters for listing sync logs\n// SyncLogListRequest 查询同步日志列表的请求参数\ntype SyncLogListRequest struct {\n\tVault  string `json:\"vault\" form:\"vault\" example:\"MyVault\"`  // Vault name (optional filter) // 保险库名称（可选过滤）\n\tType   string `json:\"type\" form:\"type\" example:\"note\"`       // Resource type: note / file / setting / folder // 资源类型\n\tAction string `json:\"action\" form:\"action\" example:\"modify\"` // Action type // 操作类型\n}\n\n// SyncLogDTO Sync log data transfer object\n// SyncLogDTO 同步日志数据传输对象\ntype SyncLogDTO struct {\n\tID            int64     `json:\"-\"`             // Record ID // 记录 ID\n\tVaultID       int64     `json:\"vaultId\"`       // Vault ID // 笔记本 ID\n\tType          string    `json:\"type\"`          // Resource type // 资源类型\n\tAction        string    `json:\"action\"`        // Action type // 操作类型\n\tChangedFields string    `json:\"changedFields\"` // Changed fields // 变更字段\n\tPath          string    `json:\"path\"`          // Resource path // 资源路径\n\tPathHash      string    `json:\"pathHash\"`      // Resource path hash // 路径哈希\n\tSize          int64     `json:\"size\"`          // Size in bytes // 大小（字节）\n\tClientName    string    `json:\"clientName\"`    // Client name // 客户端名称\n\tClientType    string    `json:\"clientType\"`    // Client type // 客户端类型\n\tClientVersion string    `json:\"clientVersion\"` // Client version // 客户端版本\n\tStatus        int       `json:\"status\"`        // Status: 1 success, 2 failed // 状态\n\tMessage       string    `json:\"message\"`       // Additional message // 附加消息\n\tCreatedAt     timex.Time `json:\"createdAt\"`     // Log creation time // 创建时间\n}\n"
  },
  {
    "path": "internal/dto/user_dto.go",
    "content": "// Package dto Defines data transfer objects (request parameters and response structs)\n// Package dto 定义数据传输对象（请求参数和响应结构体）\npackage dto\n\nimport \"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\n// UserCreateRequest User registration request parameters\n// 用户注册请求参数\ntype UserCreateRequest struct {\n\tEmail           string `json:\"email\" form:\"email\" binding:\"required,email\" example:\"user@example.com\"`          // User email // 用户邮件\n\tUsername        string `json:\"username\" form:\"username\" binding:\"required\" example:\"username123\"`               // User name // 用户名\n\tPassword        string `json:\"password\" form:\"password\" binding:\"required\" example:\"password123\"`               // User password // 用户密码\n\tConfirmPassword string `json:\"confirmPassword\" form:\"confirmPassword\" binding:\"required\" example:\"password123\"` // Confirm password // 校验密码\n}\n\n// UserLoginRequest User login request parameters\n// 用户登录请求参数\ntype UserLoginRequest struct {\n\tCredentials string `form:\"credentials\" binding:\"required\" example:\"user@example.com\"` // Username or Email // 登录凭证（用户名或邮件）\n\tPassword    string `form:\"password\" binding:\"required\" example:\"password123\"`         // Password // 密码\n}\n\n// UserRegisterSendEmailRequest Request parameters for sending registration email\n// 发送注册邮件请求参数\ntype UserRegisterSendEmailRequest struct {\n\tEmail string `json:\"email\" form:\"email\" binding:\"required,email\" example:\"user@example.com\"` // User email // 用户邮件\n}\n\n// UserChangePasswordRequest Request parameters for changing password\n// 修改密码请求参数\ntype UserChangePasswordRequest struct {\n\tOldPassword     string `json:\"oldPassword\" form:\"oldPassword\" binding:\"required\" example:\"old_password123\"`         // Old password // 旧密码\n\tPassword        string `json:\"password\" form:\"password\" binding:\"required\" example:\"new_password123\"`               // New password // 新密码\n\tConfirmPassword string `json:\"confirmPassword\" form:\"confirmPassword\" binding:\"required\" example:\"new_password123\"` // Confirm password // 校验密码\n}\n\n// ---------------- DTO / Response ----------------\n\n// UserDTO User data transfer object\n// UserDTO 用户数据传输对象\ntype UserDTO struct {\n\tUID       int64      `json:\"uid\"`       // User ID (primary key) // 用户唯一标识（主键）\n\tEmail     string     `json:\"email\"`     // Email address // 邮件地址\n\tUsername  string     `json:\"username\"`  // Username // 用户名\n\tToken     string     `json:\"token\"`     // Authentication Token // 认证 Token\n\tAvatar    string     `json:\"avatar\"`    // Avatar URL or handle // 头像路径或名称\n\tUpdatedAt timex.Time `json:\"updatedAt\"` // Last updated time // 最后更新时间\n\tCreatedAt timex.Time `json:\"createdAt\"` // Account created time // 账号创建时间\n}\n"
  },
  {
    "path": "internal/dto/vault_dto.go",
    "content": "// Package dto Defines data transfer objects (request parameters and response structs)\n// Package dto 定义数据传输对象（请求参数和响应结构体）\npackage dto\n\n// VaultPostRequest Request parameters for creating or updating a vault\n// 创建或更新保险库的请求参数\ntype VaultPostRequest struct {\n\tVault string `json:\"vault\" form:\"vault\" binding:\"required\" example:\"MyVault\"` // Vault name // 保险库名称\n\tID    int64  `json:\"id\" form:\"id\" example:\"1\"`                                // Vault ID (optional for update) // 保险库 ID（可选，用于更新）\n}\n\n// VaultGetRequest Request parameters for retrieving a vault\n// 获取保险库的请求参数\ntype VaultGetRequest struct {\n\tID int64 `form:\"id\" binding:\"required,gte=1\" example:\"1\"` // Vault ID // 保险库 ID\n}\n\n// ---------------- DTO / Response ----------------\n// ---------------- DTO / 响应参数 ----------------\n\n// VaultDTO Vault data transfer object\n// VaultDTO Vault 数据传输对象\ntype VaultDTO struct {\n\tID        int64  `json:\"id\"`        // Vault ID // 保险库 ID\n\tName      string `json:\"vault\"`     // Vault name // 保险库名称\n\tNoteCount int64  `json:\"noteCount\"` // Number of notes // 笔记数量\n\tNoteSize  int64  `json:\"noteSize\"`  // Size of notes // 笔记大小\n\tFileCount int64  `json:\"fileCount\"` // Number of files // 文件数量\n\tFileSize  int64  `json:\"fileSize\"`  // Size of files // 文件大小\n\tSize      int64  `json:\"size\"`      // Total size // 总大小\n\tCreatedAt string `json:\"createdAt\"` // Creation time // 创建时间\n\tUpdatedAt string `json:\"updatedAt\"` // Updated time // 更新时间\n}\n"
  },
  {
    "path": "internal/dto/ws_dto.go",
    "content": "package dto\n\n// WebSocketMsgType WebSocket Binary message type\n// WebSocket 二进制消息类型\ntype WebSocketMsgType = string\n\n// VaultFileMsgType vault attachment message\n// 笔记库附件消息\nconst VaultFileMsgType WebSocketMsgType = \"00\"\n\n// WebSocketReceiveAction WebSocket text receive action type\n// WebSocket 文本接收动作类型\ntype WebSocketReceiveAction = string\n\n// WebSocketSendAction WebSocket text send action type\n// WebSocket 文本发送动作类型\ntype WebSocketSendAction = string\n\nconst (\n\n\t// ---------------- Folder ----------------\n\n\t// FolderReceiveSync folder synchronization request\n\t// FolderReceiveSync 文件夹同步请求\n\tFolderReceiveSync WebSocketReceiveAction = \"FolderSync\"\n\t// FolderReceiveModify folder modify or create request\n\t// FolderReceiveModify 文件夹修改或创建请求\n\tFolderReceiveModify WebSocketReceiveAction = \"FolderModify\"\n\t// FolderReceiveDelete folder delete request\n\t// FolderReceiveDelete 文件夹删除请求\n\tFolderReceiveDelete WebSocketReceiveAction = \"FolderDelete\"\n\t// FolderReceiveRename folder rename request\n\t// FolderReceiveRename 文件夹重命名请求\n\tFolderReceiveRename WebSocketReceiveAction = \"FolderRename\"\n\n\t// ---------------- Note ----------------\n\n\t// NoteReceiveSync note synchronization request\n\t// NoteReceiveSync 笔记同步请求\n\tNoteReceiveSync WebSocketReceiveAction = \"NoteSync\"\n\t// NoteReceiveModify note modify or create request\n\t// NoteReceiveModify 笔记修改或创建请求\n\tNoteReceiveModify WebSocketReceiveAction = \"NoteModify\"\n\t// NoteReceiveDelete note delete request\n\t// NoteReceiveDelete 笔记删除请求\n\tNoteReceiveDelete WebSocketReceiveAction = \"NoteDelete\"\n\t// NoteReceiveRename note rename request\n\t// NoteReceiveRename 笔记重命名请求\n\tNoteReceiveRename WebSocketReceiveAction = \"NoteRename\"\n\t// NoteReceiveCheck note modification check request\n\t// NoteReceiveCheck 笔记修改检查请求\n\tNoteReceiveCheck WebSocketReceiveAction = \"NoteCheck\"\n\t// NoteReceiveRePush Note missing pull request\n\t// NoteReceiveRePush 笔记缺失请求拉取\n\tNoteReceiveRePush WebSocketReceiveAction = \"NoteRePush\"\n\n\t// ---------------- File ----------------\n\n\t// FileReceiveSync file synchronization request\n\t// FileReceiveSync 文件同步请求\n\tFileReceiveSync WebSocketReceiveAction = \"FileSync\"\n\t// FileReceiveUploadCheck file upload pre-check request\n\t// FileReceiveUploadCheck 文件上传前检查请求\n\tFileReceiveUploadCheck WebSocketReceiveAction = \"FileUploadCheck\"\n\t// FileReceiveDelete file delete request\n\t// FileReceiveDelete 文件删除请求\n\tFileReceiveDelete WebSocketReceiveAction = \"FileDelete\"\n\t// FileReceiveRename file rename request\n\t// FileReceiveRename 文件重命名请求\n\tFileReceiveRename WebSocketReceiveAction = \"FileRename\"\n\t// FileReceiveChunkDownload file chunk download request\n\t// FileReceiveChunkDownload 文件分片下载请求\n\tFileReceiveChunkDownload WebSocketReceiveAction = \"FileChunkDownload\"\n\t// FileReceiveRePush file missing pull request\n\t// FileReceiveRePush 文件缺失请求拉取\n\tFileReceiveRePush WebSocketReceiveAction = \"FileRePush\"\n\n\t// ---------------- Setting ----------------\n\n\t// SettingReceiveSync setting synchronization request\n\t// SettingReceiveSync 设置同步请求\n\tSettingReceiveSync WebSocketReceiveAction = \"SettingSync\"\n\t// SettingReceiveModify setting modify or create request\n\t// SettingReceiveModify 设置修改或创建请求\n\tSettingReceiveModify WebSocketReceiveAction = \"SettingModify\"\n\t// SettingReceiveDelete setting delete request\n\t// SettingReceiveDelete 设置删除请求\n\tSettingReceiveDelete WebSocketReceiveAction = \"SettingDelete\"\n\t// SettingReceiveCheck setting modification check request\n\t// SettingReceiveCheck 设置修改检查请求\n\tSettingReceiveCheck WebSocketReceiveAction = \"SettingCheck\"\n\t// SettingReceiveClear clear all settings request\n\t// SettingReceiveClear 清理所有设置请求\n\tSettingReceiveClear WebSocketReceiveAction = \"SettingClear\"\n)\n\nconst (\n\t// ---------------- Folder ----------------\n\n\t// FolderSyncModify folder synchronization modification\n\t// FolderSyncModify 文件夹同步修改\n\tFolderSyncModify WebSocketSendAction = \"FolderSyncModify\"\n\t// FolderSyncDelete folder synchronization deletion\n\t// FolderSyncDelete 文件夹同步删除\n\tFolderSyncDelete WebSocketSendAction = \"FolderSyncDelete\"\n\t// FolderSyncEnd folder synchronization finished\n\t// FolderSyncEnd 文件夹同步结束\n\tFolderSyncEnd WebSocketSendAction = \"FolderSyncEnd\"\n\t// FolderRename folder rename action\n\t// FolderRename 文件夹重命名动作\n\tFolderSyncRename WebSocketSendAction = \"FolderSyncRename\"\n\t// FolderModifyAck folder modify operation ack\n\t// FolderModifyAck 文件夹修改操作 ack\n\tFolderModifyAck WebSocketSendAction = \"FolderModifyAck\"\n\t// FolderRenameAck folder rename operation ack\n\t// FolderRenameAck 文件夹重命名操作 ack\n\tFolderRenameAck WebSocketSendAction = \"FolderRenameAck\"\n\t// FolderDeleteAck folder delete operation ack\n\t// FolderDeleteAck 文件夹删除操作 ack\n\tFolderDeleteAck WebSocketSendAction = \"FolderDeleteAck\"\n\n\t// ---------------- Note ----------------\n\n\t// NoteSyncModify note synchronization modification\n\t// NoteSyncModify 笔记同步修改\n\tNoteSyncModify WebSocketSendAction = \"NoteSyncModify\"\n\t// NoteSyncDelete note synchronization deletion\n\t// NoteSyncDelete 笔记同步删除\n\tNoteSyncDelete WebSocketSendAction = \"NoteSyncDelete\"\n\t// NoteSyncRename note synchronization rename\n\t// NoteSyncRename 笔记同步重命名\n\tNoteSyncRename WebSocketSendAction = \"NoteSyncRename\"\n\t// NoteSyncMtime note modification time synchronization\n\t// NoteSyncMtime 笔记修改时间同步\n\tNoteSyncMtime WebSocketSendAction = \"NoteSyncMtime\"\n\t// NoteSyncEnd note synchronization finished\n\t// NoteSyncEnd 笔记同步结束\n\tNoteSyncEnd WebSocketSendAction = \"NoteSyncEnd\"\n\t// NoteSyncNeedPush indicates client needs to push note content\n\t// NoteSyncNeedPush 表示客户端需要推送笔记内容\n\tNoteSyncNeedPush WebSocketSendAction = \"NoteSyncNeedPush\"\n\t// NoteModifyAck note modify operation ack\n\t// NoteModifyAck 笔记修改操作 ack\n\tNoteModifyAck WebSocketSendAction = \"NoteModifyAck\"\n\t// NoteRenameAck note rename operation ack\n\t// NoteRenameAck 笔记重命名操作 ack\n\tNoteRenameAck WebSocketSendAction = \"NoteRenameAck\"\n\t// NoteDeleteAck note delete operation ack\n\t// NoteDeleteAck 笔记删除操作 ack\n\tNoteDeleteAck WebSocketSendAction = \"NoteDeleteAck\"\n\n\t// ---------------- File ----------------\n\n\t// FileSyncUpdate file synchronization update\n\t// FileSyncUpdate 文件同步更新\n\tFileSyncUpdate WebSocketSendAction = \"FileSyncUpdate\"\n\t// FileSyncDelete file synchronization deletion\n\t// FileSyncDelete 文件同步删除\n\tFileSyncDelete WebSocketSendAction = \"FileSyncDelete\"\n\t// FileSyncRename file synchronization rename\n\t// FileSyncRename 文件同步重命名\n\tFileSyncRename WebSocketSendAction = \"FileSyncRename\"\n\t// FileSyncMtime file modification time synchronization\n\t// FileSyncMtime 文件修改时间同步\n\tFileSyncMtime WebSocketSendAction = \"FileSyncMtime\"\n\t// FileSyncEnd file synchronization finished\n\t// FileSyncEnd 文件同步结束\n\tFileSyncEnd WebSocketSendAction = \"FileSyncEnd\"\n\t// FileUpload file upload action\n\t// FileUpload 文件上传动作\n\tFileUpload WebSocketSendAction = \"FileUpload\"\n\t// FileSyncChunkDownload file chunk download for sync\n\t// FileSyncChunkDownload 同步时的文件块下载\n\tFileSyncChunkDownload WebSocketSendAction = \"FileSyncChunkDownload\"\n\t// FileRenameAck file rename operation ack\n\t// FileRenameAck 文件重命名操作 ack\n\tFileRenameAck WebSocketSendAction = \"FileRenameAck\"\n\t// FileUploadAck file upload complete ack\n\t// FileUploadAck 文件上传完成 ack\n\tFileUploadAck WebSocketSendAction = \"FileUploadAck\"\n\t// FileDeleteAck file delete operation ack\n\t// FileDeleteAck 文件删除操作 ack\n\tFileDeleteAck WebSocketSendAction = \"FileDeleteAck\"\n\n\t// ---------------- Setting ----------------\n\n\t// SettingSyncModify setting synchronization modification\n\t// SettingSyncModify 设置同步修改\n\tSettingSyncModify WebSocketSendAction = \"SettingSyncModify\"\n\t// SettingSyncDelete setting synchronization deletion\n\t// SettingSyncDelete 设置同步删除\n\tSettingSyncDelete WebSocketSendAction = \"SettingSyncDelete\"\n\t// SettingSyncMtime setting modification time synchronization\n\t// SettingSyncMtime 设置修改时间同步\n\tSettingSyncMtime WebSocketSendAction = \"SettingSyncMtime\"\n\t// SettingSyncEnd setting synchronization finished\n\t// SettingSyncEnd 设置同步结束\n\tSettingSyncEnd WebSocketSendAction = \"SettingSyncEnd\"\n\t// SettingSyncNeedUpload indicates client needs to upload setting\n\t// SettingSyncNeedUpload 表示客户端需要上传设置\n\tSettingSyncNeedUpload WebSocketSendAction = \"SettingSyncNeedUpload\"\n\t// SettingSyncClear sync clear all settings\n\t// SettingSyncClear 同步清理所有设置\n\tSettingSyncClear WebSocketSendAction = \"SettingSyncClear\"\n\t// SettingModifyAck setting modify operation ack\n\t// SettingModifyAck 设置修改操作 ack\n\tSettingModifyAck WebSocketSendAction = \"SettingModifyAck\"\n\t// SettingDeleteAck setting delete operation ack\n\t// SettingDeleteAck 设置删除操作 ack\n\tSettingDeleteAck WebSocketSendAction = \"SettingDeleteAck\"\n\n\t// ---------------- Share ----------------\n\n\t// ShareSyncRefresh notify clients to refresh share state\n\t// ShareSyncRefresh 通知客户端刷新分享状态\n\tShareSyncRefresh WebSocketSendAction = \"ShareSyncRefresh\"\n)\n\n// WSQueuedMessage represents a message item to be sent\n// WSQueuedMessage used to collect messages during sync process, and sent together in SyncEnd message\n// WSQueuedMessage 表示待发送的消息项\n// WSQueuedMessage 用于在同步过程中收集消息,在 SyncEnd 消息中统一合并发送\ntype WSQueuedMessage struct {\n\tAction  string `json:\"action\"`  // Message action/type // 消息动作/类型\n\tData    any    `json:\"data\"`    // Message data payload // 消息数据负载\n\tContext string `json:\"context\"` // Context // 上下文\n}\n"
  },
  {
    "path": "internal/middleware/404nofound.go",
    "content": "package middleware\n\nimport (\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// NoFound 404 handler\n// NoFound 404 处理\nfunc NoFound() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tresponse := app.NewResponse(c)\n\t\tresponse.ToResponse(code.ErrorNotFoundAPI)\n\t\tc.Abort()\n\t}\n}\n"
  },
  {
    "path": "internal/middleware/access_log.go",
    "content": "package middleware\n\nimport (\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"go.uber.org/zap\"\n)\n\n// AccessLogWithLogger creates access log middleware with logger (supports dependency injection)\n// AccessLogWithLogger 创建带日志器的访问日志中间件（支持依赖注入）\nfunc AccessLogWithLogger(logger *zap.Logger) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\n\t\tpath := c.Request.URL.Path\n\t\tquery := c.Request.URL.RawQuery\n\n\t\tstartTime := time.Now()\n\t\tc.Next()\n\n\t\ttimeCost := time.Since(startTime)\n\n\t\tlogger.Info(path,\n\t\t\tzap.String(\"method\", c.Request.Method),\n\t\t\tzap.String(\"url\", path+\"?\"+query),\n\t\t\tzap.String(\"start-time\", startTime.Format(\"2006-01-02 15:04:05\")),\n\t\t\tzap.Duration(\"time-cost\", timeCost),\n\t\t\tzap.String(\"ip\", c.ClientIP()),\n\t\t\tzap.String(\"user-agent\", c.Request.UserAgent()),\n\t\t\tzap.String(\"errors\", c.Errors.ByType(gin.ErrorTypePrivate).String()),\n\t\t)\n\t}\n}\n"
  },
  {
    "path": "internal/middleware/app_info.go",
    "content": "package middleware\n\nimport (\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// AppInfoWithConfig creates middleware to set application information (supports dependency injection)\n// AppInfoWithConfig 创建设置应用信息的中间件（支持依赖注入）\nfunc AppInfoWithConfig(appName, appVersion string) gin.HandlerFunc {\n\n\treturn func(c *gin.Context) {\n\t\tc.Set(\"app_name\", appName)\n\t\tc.Set(\"app_version\", appVersion)\n\t\tc.Set(\"access_host\", app.GetAccessHost(c))\n\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "internal/middleware/context_timeout.go",
    "content": "package middleware\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// ContextTimeout creates middleware to set context timeout (supports dependency injection)\n// ContextTimeout 创建设置上下文超时的中间件（支持依赖注入）\nfunc ContextTimeout(timeout time.Duration) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tctx, cancel := context.WithTimeout(c.Request.Context(), timeout)\n\t\tdefer cancel()\n\n\t\tc.Request = c.Request.WithContext(ctx)\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "internal/middleware/cors.go",
    "content": "package middleware\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// Cors creates CORS middleware\n// Cors 创建跨域中间件\nfunc Cors() gin.HandlerFunc {\n\n\treturn func(c *gin.Context) {\n\n\t\torigin := c.GetHeader(\"Origin\")\n\t\tallowedOrigin := \"\"\n\t\tif origin != \"\" {\n\t\t\tif strings.HasPrefix(origin, \"app://\") {\n\t\t\t\tallowedOrigin = origin\n\t\t\t} else if strings.HasPrefix(origin, \"http://\") || strings.HasPrefix(origin, \"https://\") {\n\t\t\t\tif origin == c.Request.URL.Scheme+\"://\"+c.Request.Host {\n\t\t\t\t\tallowedOrigin = origin\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tc.Header(\"Access-Control-Allow-Credentials\", \"true\")\n\t\tc.Header(\"Access-Control-Allow-Methods\", \"GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS\")\n\t\tc.Header(\"Access-Control-Allow-Headers\", \"Origin, X-Requested-With, X-CSRF-Token, X-Client, X-Client-Name, X-Client-Version, X-Default-Vault-Name, AccessToken, Authorization, Debug, Domain, Token, Share-Token, Lang, Content-Type, Content-Length, Accept\")\n\n\t\tif allowedOrigin != \"\" {\n\t\t\tc.Header(\"Access-Control-Allow-Origin\", allowedOrigin)\n\t\t}\n\n\t\t// Allow OPTIONS requests to pass\n\t\t// 允许放行OPTIONS请求\n\t\tif c.Request.Method == \"OPTIONS\" {\n\t\t\tc.AbortWithStatus(http.StatusNoContent)\n\t\t}\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "internal/middleware/lang.go",
    "content": "package middleware\n\nimport (\n\t\"strings\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\n\t\"github.com/gin-gonic/gin\"\n\tut \"github.com/go-playground/universal-translator\"\n)\n\n// LangWithTranslator creates language middleware with translator (supports dependency injection)\n// LangWithTranslator 创建带翻译器的语言中间件（支持依赖注入）\nfunc LangWithTranslator(uni *ut.UniversalTranslator) gin.HandlerFunc {\n\n\treturn func(c *gin.Context) {\n\n\t\tvar lang string\n\n\t\tif s, exist := c.GetQuery(\"lang\"); exist {\n\t\t\tlang = s\n\t\t} else if s = c.GetHeader(\"lang\"); len(s) != 0 {\n\t\t\tlang = s\n\t\t}\n\n\t\tlang = strings.ToLower(strings.ReplaceAll(lang, \"-\", \"_\"))\n\n\t\ttrans, found := uni.GetTranslator(lang)\n\n\t\tif found {\n\t\t\tc.Set(\"trans\", trans)\n\t\t} else {\n\t\t\ttrans, _ := uni.GetTranslator(\"en\")\n\t\t\tc.Set(\"trans\", trans)\n\t\t}\n\n\t\tcode.SetGlobalDefaultLang(lang)\n\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "internal/middleware/limiter.go",
    "content": "package middleware\n\nimport (\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/limiter\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// RateLimiter creates rate limiting middleware (supports dependency injection)\n// RateLimiter 创建限流中间件（支持依赖注入）\nfunc RateLimiter(l limiter.Face) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tkey := l.Key(c)\n\t\tif bucket, ok := l.GetBucket(key); ok {\n\t\t\tcount := bucket.TakeAvailable(1)\n\t\t\tif count == 0 {\n\t\t\t\tresponse := app.NewResponse(c)\n\t\t\t\tresponse.ToResponse(code.ErrorTooManyRequests)\n\t\t\t\tc.Abort()\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "internal/middleware/proxy.go",
    "content": "package middleware\n\nimport (\n\t\"crypto/tls\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// Proxy handles proxy headers and restores original request information\n// Proxy 处理代理头部并恢复原始请求信息\nfunc Proxy() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\t// Detect protocol from X-Forwarded-Proto\n\t\t// 从 X-Forwarded-Proto 检测协议\n\t\tproto := c.GetHeader(\"X-Forwarded-Proto\")\n\t\tif proto == \"\" {\n\t\t\tif c.Request.TLS != nil {\n\t\t\t\tproto = \"https\"\n\t\t\t} else {\n\t\t\t\tproto = \"http\"\n\t\t\t}\n\t\t}\n\n\t\t// Update Request URL Scheme\n\t\t// 更新请求 URL 的 Scheme\n\t\tc.Request.URL.Scheme = proto\n\n\t\t// If protocol is https but TLS is nil (due to proxy termination),\n\t\t// we \"fake\" a TLS state to satisfy libraries that check r.TLS != nil\n\t\t// 如果协议是 https 但 TLS 为 nil（由于代理终止），\n\t\t// 我们“伪造”一个 TLS 状态以满足检查 r.TLS != nil 的库\n\t\tif proto == \"https\" && c.Request.TLS == nil {\n\t\t\tc.Request.TLS = &tls.ConnectionState{}\n\t\t}\n\n\t\t// Detect host from X-Forwarded-Host\n\t\t// 从 X-Forwarded-Host 检测主机名\n\t\tif host := c.GetHeader(\"X-Forwarded-Host\"); host != \"\" {\n\t\t\tc.Request.Host = host\n\t\t}\n\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "internal/middleware/recovery.go",
    "content": "package middleware\n\nimport (\n\t\"fmt\"\n\t\"runtime/debug\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"go.uber.org/zap\"\n)\n\n// RecoveryWithLogger creates recovery middleware with logger (supports dependency injection)\n// RecoveryWithLogger 创建带日志器的恢复中间件（支持依赖注入）\nfunc RecoveryWithLogger(logger *zap.Logger) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tpath := c.Request.URL.Path\n\t\tquery := c.Request.URL.RawQuery\n\t\tdefer func() {\n\t\t\tif err := recover(); err != nil {\n\t\t\t\tvar errorMsg string\n\t\t\t\tswitch val := err.(type) {\n\t\t\t\tcase string:\n\t\t\t\t\terrorMsg = val\n\t\t\t\tcase error:\n\t\t\t\t\t// Record error type errors\n\t\t\t\t\t// 记录 error 类型的错误\n\t\t\t\t\tlogger.Error(\"Recovered from panic\",\n\t\t\t\t\t\tzap.Int(\"status\", c.Writer.Status()),\n\t\t\t\t\t\tzap.String(\"router\", path),\n\t\t\t\t\t\tzap.String(\"method\", c.Request.Method),\n\t\t\t\t\t\tzap.String(\"query\", query),\n\t\t\t\t\t\tzap.String(\"ip\", c.ClientIP()),\n\t\t\t\t\t\tzap.String(\"user-agent\", c.Request.UserAgent()),\n\t\t\t\t\t\tzap.String(\"request\", c.Request.PostForm.Encode()),\n\t\t\t\t\t\tzap.String(\"errors\", c.Errors.ByType(gin.ErrorTypePrivate).String()), // Record error context\n\t\t\t\t\t\t// 记录错误的上下文\n\t\t\t\t\t\tzap.Error(val), // Error info\n\t\t\t\t\t\tzap.String(\"stack\", string(debug.Stack())), // Error stack\n\t\t\t\t\t\t// 错误堆栈\n\t\t\t\t\t)\n\t\t\t\t\terrorMsg = val.Error()\n\t\t\t\tdefault:\n\t\t\t\t\t// Other types of panic (non-error type panic)\n\t\t\t\t\t// 如果是其它类型的 panic（如非错误类型的 panic）\n\t\t\t\t\tlogger.Error(\"Recovered from unknown panic\",\n\t\t\t\t\t\tzap.Int(\"status\", c.Writer.Status()),\n\t\t\t\t\t\tzap.String(\"router\", path),\n\t\t\t\t\t\tzap.String(\"method\", c.Request.Method),\n\t\t\t\t\t\tzap.String(\"query\", query),\n\t\t\t\t\t\tzap.String(\"ip\", c.ClientIP()),\n\t\t\t\t\t\tzap.String(\"user-agent\", c.Request.UserAgent()),\n\t\t\t\t\t\tzap.String(\"request\", c.Request.PostForm.Encode()),\n\t\t\t\t\t\tzap.String(\"panic_value\", fmt.Sprintf(\"%v\", val)), // Record panic value\n\t\t\t\t\t\t// 记录 panic 的值\n\t\t\t\t\t\tzap.String(\"stack\", string(debug.Stack())), // Error stack\n\t\t\t\t\t\t// 错误堆栈\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\t// Return unified error response\n\t\t\t\t// 返回统一的错误响应\n\t\t\t\tapp.NewResponse(c).ToResponse(code.ErrorServerInternal.WithDetails(errorMsg))\n\t\t\t}\n\t\t}()\n\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "internal/middleware/share_auth_token.go",
    "content": "package middleware\n\nimport (\n\t\"strings\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// ShareAuthToken share Token authentication middleware\n// ShareAuthToken 分享 Token 认证中间件\n// Try to get Token by priority: Header -> Query -> PostForm\n// 按优先级尝试获取 Token：Header -> Query -> PostForm\nfunc ShareAuthToken(shareService service.ShareService) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tresponse := app.NewResponse(c)\n\t\tvar token string\n\n\t\ttoken = c.GetHeader(\"Share-Token\") // 支持自定义头\n\n\t\t// 2. Try parsing from URL parameters (GET)\n\t\t// 2. 尝试从 URL 参数解析 (GET)\n\t\tif token == \"\" {\n\t\t\ttoken = c.Query(\"shareToken\")\n\t\t}\n\n\t\tif token == \"\" {\n\t\t\ttoken = c.Query(\"share_token\")\n\t\t}\n\n\t\t// 3. Try parsing from form parameters (POST)\n\t\t// 3. 尝试从表单参数解析 (POST)\n\t\tif token == \"\" {\n\t\t\ttoken = c.PostForm(\"shareToken\")\n\t\t}\n\t\tif token == \"\" {\n\t\t\ttoken = c.PostForm(\"share_token\")\n\t\t}\n\n\t\tif token == \"\" {\n\t\t\tresponse.ToResponse(code.ErrorInvalidAuthToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\n\t\t// Determine resource ID and type currently requested\n\t\t// 确定当前请求想要访问的资源 ID 和类型\n\t\trid := c.Query(\"id\")\n\t\tif rid == \"\" {\n\t\t\trid = c.PostForm(\"id\")\n\t\t}\n\n\t\t// Simple resource type determination logic: distinguish by route path\n\t\t// 简单的资源类型判定逻辑：根据路由路径区分\n\t\trtp := \"note\"\n\t\tif strings.Contains(c.Request.URL.Path, \"/file\") {\n\t\t\trtp = \"file\"\n\t\t}\n\n\t\tif rid == \"\" {\n\t\t\tresponse.ToResponse(code.ErrorInvalidParams)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\n\t\t// Verify Token and its availability in database\n\t\t// 验证 Token 及其在数据库中的生效状态\n\t\tpassword := c.Query(\"password\")\n\t\tif password == \"\" {\n\t\t\tpassword = c.PostForm(\"password\")\n\t\t}\n\t\tentity, err := shareService.VerifyShare(c.Request.Context(), token, rid, rtp, password)\n\n\t\tif err != nil {\n\t\t\tswitch err {\n\t\t\tcase domain.ErrShareCancelled:\n\t\t\t\tresponse.ToResponse(code.ErrorShareRevoked)\n\t\t\tcase domain.ErrShareExpired:\n\t\t\t\tresponse.ToResponse(code.ErrorShareExpired)\n\t\t\tcase domain.ErrSharePasswordRequired:\n\t\t\t\tresponse.ToResponse(code.ErrorSharePasswordRequired)\n\t\t\tcase domain.ErrSharePasswordInvalid:\n\t\t\t\tresponse.ToResponse(code.ErrorSharePasswordInvalid)\n\t\t\tdefault:\n\t\t\t\tresponse.ToResponse(code.ErrorShareNotFound)\n\t\t\t}\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\n\t\tc.Set(\"share_entity\", entity)\n\t\tc.Set(\"share_token\", token)\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "internal/middleware/simple_auth_token.go",
    "content": "/**\n  @author: haierkeys\n  @since: 2022/9/14\n  @desc: Simple auth token middleware // 简单认证 Token 中间件\n**/\n\npackage middleware\n\nimport (\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// SimpleAuthTokenWithConfig simple Token authentication middleware (check if header/param matches secretKey)\n// SimpleAuthTokenWithConfig 简单 Token 认证中间件（检查 Header/参数是否匹配 secretKey）\n// Mainly used for private monitoring interfaces or simply protected interfaces\n// 主要用于私有监控接口或简单的保护接口\nfunc SimpleAuthTokenWithConfig(secretKey string) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\n\t\tif secretKey == \"\" {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\n\t\tresponse := app.NewResponse(c)\n\n\t\t// Check URL parameter token\n\t\t// 检查 URL 参数 token\n\t\tvar token string\n\n\t\tif s, exist := c.GetQuery(\"authorization\"); exist {\n\t\t\ttoken = s\n\t\t} else if s, exist = c.GetQuery(\"Authorization\"); exist {\n\t\t\ttoken = s\n\t\t} else if s = c.GetHeader(\"authorization\"); len(s) != 0 {\n\t\t\ttoken = s\n\t\t} else if s = c.GetHeader(\"Authorization\"); len(s) != 0 {\n\t\t\t// Check Authorization: Bearer <token>\n\t\t\t// 检查 Authorization: Bearer <token>\n\t\t\tif len(s) > 7 && s[:7] == \"Bearer \" {\n\t\t\t\ttoken = s[7:]\n\t\t\t} else {\n\t\t\t\ttoken = s\n\t\t\t}\n\t\t}\n\n\t\tif token != secretKey {\n\t\t\tresponse.ToResponse(code.ErrorInvalidAuthToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\t\tc.Next()\n\t}\n}\n\n// SimpleAuthToken simple Token authentication middleware (no secret key, always fails)\n// SimpleAuthToken 简单 Token 认证中间件（无密钥，始终失败）\n// Deprecated: Recommended to use SimpleAuthTokenWithConfig instead\n// Deprecated: 推荐使用 SimpleAuthTokenWithConfig\nfunc SimpleAuthToken() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tc.Next()\n\t}\n}\n"
  },
  {
    "path": "internal/middleware/static_compress.go",
    "content": "package middleware\n\nimport (\n\t\"embed\"\n\t\"net/http\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// StaticCompressMiddleware returns a middleware that supports pre-compressed files (.br, .gz).\n// StaticCompressMiddleware 返回一个支持预压缩文件（.br, .gz）的中间件。\n// It only handles requests for the frontend directory.\n// 它仅处理对 frontend 目录的请求。\nfunc StaticCompressMiddleware(frontendFiles embed.FS) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tpath := c.Request.URL.Path\n\n\t\t// Only handle /assets/ and /static/ paths which are served from frontendFiles\n\t\t// 仅处理从 frontendFiles 提供的 /assets/ 和 /static/ 路径\n\t\tvar internalPath string\n\t\tif strings.HasPrefix(path, \"/assets/\") {\n\t\t\tinternalPath = \"frontend/assets\" + strings.TrimPrefix(path, \"/assets\")\n\t\t} else if strings.HasPrefix(path, \"/static/\") {\n\t\t\tinternalPath = \"frontend/static\" + strings.TrimPrefix(path, \"/static\")\n\t\t} else {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\n\t\tacceptEncoding := c.GetHeader(\"Accept-Encoding\")\n\n\t\t// Priority: Brotli (.br) > Gzip (.gz)\n\t\t// 优先级：Brotli (.br) > Gzip (.gz)\n\n\t\tif strings.Contains(acceptEncoding, \"br\") {\n\t\t\tbrPath := internalPath + \".br\"\n\t\t\tif _, err := frontendFiles.Open(brPath); err == nil {\n\t\t\t\tc.Header(\"Content-Encoding\", \"br\")\n\t\t\t\tc.Header(\"Vary\", \"Accept-Encoding\")\n\t\t\t\t// Set Content-Type based on the original file extension\n\t\t\t\t// 根据原始文件后缀设置 Content-Type\n\t\t\t\tsetContentType(c, internalPath)\n\t\t\t\tc.FileFromFS(brPath, http.FS(frontendFiles))\n\t\t\t\tc.Abort()\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif strings.Contains(acceptEncoding, \"gzip\") {\n\t\t\tgzPath := internalPath + \".gz\"\n\t\t\tif _, err := frontendFiles.Open(gzPath); err == nil {\n\t\t\t\tc.Header(\"Content-Encoding\", \"gzip\")\n\t\t\t\tc.Header(\"Vary\", \"Accept-Encoding\")\n\t\t\t\tsetContentType(c, internalPath)\n\t\t\t\tc.FileFromFS(gzPath, http.FS(frontendFiles))\n\t\t\t\tc.Abort()\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tc.Next()\n\t}\n}\n\nfunc setContentType(c *gin.Context, path string) {\n\text := filepath.Ext(path)\n\tswitch ext {\n\tcase \".js\":\n\t\tc.Header(\"Content-Type\", \"application/javascript\")\n\tcase \".css\":\n\t\tc.Header(\"Content-Type\", \"text/css\")\n\tcase \".html\":\n\t\tc.Header(\"Content-Type\", \"text/html; charset=utf-8\")\n\tcase \".json\":\n\t\tc.Header(\"Content-Type\", \"application/json; charset=utf-8\")\n\tcase \".svg\":\n\t\tc.Header(\"Content-Type\", \"image/svg+xml\")\n\tcase \".png\":\n\t\tc.Header(\"Content-Type\", \"image/png\")\n\tcase \".jpg\", \".jpeg\":\n\t\tc.Header(\"Content-Type\", \"image/jpeg\")\n\tcase \".gif\":\n\t\tc.Header(\"Content-Type\", \"image/gif\")\n\tcase \".woff\":\n\t\tc.Header(\"Content-Type\", \"font/woff\")\n\tcase \".woff2\":\n\t\tc.Header(\"Content-Type\", \"font/woff2\")\n\tcase \".ttf\":\n\t\tc.Header(\"Content-Type\", \"font/ttf\")\n\t}\n}\n"
  },
  {
    "path": "internal/middleware/tracer.go",
    "content": "package middleware\n\nimport (\n\t\"context\"\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\nconst (\n\t// DefaultTraceIDHeader default Trace ID request header name // 默认的 Trace ID 请求头名称\n\tDefaultTraceIDHeader = \"X-Trace-ID\"\n\t// TraceIDKey Context 中存储 Trace ID 的键\n\t// TraceIDKey key for storing Trace ID in Context\n\tTraceIDKey = \"trace_id\"\n)\n\n// TraceMiddlewareWithConfig creates a request tracing middleware (with injected configuration)\n// TraceMiddlewareWithConfig 创建请求追踪中间件（使用注入的配置）\n// Functionality:\n// 功能：\n// 1. Get or generate a unique Trace ID from the request header\n// 1. 从请求头获取或生成唯一的 Trace ID\n// 2. Inject Trace ID into gin.Context and request.Context\n// 2. 将 Trace ID 注入到 gin.Context 和 request.Context\n// 3. Return Trace ID in the response header\n// 3. 在响应头中返回 Trace ID\nfunc TraceMiddlewareWithConfig(enabled bool, headerName string) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\t// Check if tracing is enabled\n\t\t// 检查是否启用追踪\n\t\tif !enabled {\n\t\t\tc.Next()\n\t\t\treturn\n\t\t}\n\n\t\t// Get configured request header name\n\t\t// 获取配置的请求头名称\n\t\tif headerName == \"\" {\n\t\t\theaderName = DefaultTraceIDHeader\n\t\t}\n\n\t\t// Try to get Trace ID from request header\n\t\t// 尝试从请求头获取 Trace ID\n\t\ttraceID := c.GetHeader(headerName)\n\t\tif traceID == \"\" {\n\t\t\t// Generate new Trace ID\n\t\t\t// 生成新的 Trace ID\n\t\t\ttraceID = generateTraceID()\n\t\t}\n\n\t\t// Store into gin.Context\n\t\t// 存储到 gin.Context\n\t\tc.Set(TraceIDKey, traceID)\n\n\t\t// Inject into request.Context\n\t\t// 注入到 request.Context\n\t\tctx := context.WithValue(c.Request.Context(), TraceIDKey, traceID)\n\t\tc.Request = c.Request.WithContext(ctx)\n\n\t\t// Add to response header\n\t\t// 添加到响应头\n\t\tc.Header(headerName, traceID)\n\n\t\tc.Next()\n\t}\n}\n\n// TraceMiddleware creates request tracing middleware (enabled by default)\n// TraceMiddleware 创建请求追踪中间件（默认启用）\n// Deprecated: Recommended to use TraceMiddlewareWithConfig\n// Deprecated: 推荐使用 TraceMiddlewareWithConfig\nfunc TraceMiddleware() gin.HandlerFunc {\n\treturn TraceMiddlewareWithConfig(true, DefaultTraceIDHeader)\n}\n\n// generateTraceID generates unique Trace ID\n// Format: {timestamp_nano}-{random_hex}\n// generateTraceID 生成唯一的 Trace ID\n// 格式: {timestamp_nano}-{random_hex}\nfunc generateTraceID() string {\n\t// Generate 8-byte random number\n\t// 生成 8 字节随机数\n\trandomBytes := make([]byte, 8)\n\tif _, err := rand.Read(randomBytes); err != nil {\n\t\t// If random number generation fails, use timestamp as backup\n\t\t// 如果随机数生成失败，使用时间戳作为后备\n\t\treturn fmt.Sprintf(\"%d\", time.Now().UnixNano())\n\t}\n\n\treturn fmt.Sprintf(\"%d-%s\",\n\t\ttime.Now().UnixNano(),\n\t\thex.EncodeToString(randomBytes)[:8])\n}\n\n// GetTraceID retrieves Trace ID from context.Context\n// GetTraceID 从 context.Context 获取 Trace ID\nfunc GetTraceID(ctx context.Context) string {\n\tif ctx == nil {\n\t\treturn \"\"\n\t}\n\tif id, ok := ctx.Value(TraceIDKey).(string); ok {\n\t\treturn id\n\t}\n\treturn \"\"\n}\n\n// GetTraceIDFromGin retrieves Trace ID from gin.Context\n// GetTraceIDFromGin 从 gin.Context 获取 Trace ID\nfunc GetTraceIDFromGin(c *gin.Context) string {\n\tif c == nil {\n\t\treturn \"\"\n\t}\n\tif id, exists := c.Get(TraceIDKey); exists {\n\t\tif traceID, ok := id.(string); ok {\n\t\t\treturn traceID\n\t\t}\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "internal/middleware/user_auth_token.go",
    "content": "package middleware\n\nimport (\n\t\"strings\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// UserAuthTokenWithConfig user Token authentication middleware (using injected secret key)\n// UserAuthTokenWithConfig 用户 Token 认证中间件（使用注入的密钥）\n// Supports Authorization: Bearer <token> format and URL parameter token (for image and other resource requests)\n// 支持 Authorization: Bearer <token> 格式和 URL 参数 token（用于图片等资源请求）\nfunc UserAuthTokenWithConfig(secretKey string) gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\tresponse := app.NewResponse(c)\n\t\tvar token string\n\n\t\t// Prioritize getting from Authorization header (standard Bearer format)\n\t\t// 优先从 Authorization header 获取（标准 Bearer 格式）\n\t\tauthHeader := c.GetHeader(\"Authorization\")\n\t\tif authHeader != \"\" && strings.HasPrefix(authHeader, \"Bearer \") {\n\t\t\ttoken = strings.TrimPrefix(authHeader, \"Bearer \")\n\t\t}\n\n\t\tif token == \"\" {\n\t\t\tauthHeader := c.GetHeader(\"Token\")\n\t\t\tif authHeader != \"\" {\n\t\t\t\ttoken = authHeader\n\t\t\t}\n\t\t\tauthHeader = c.GetHeader(\"token\")\n\t\t\tif authHeader != \"\" {\n\t\t\t\ttoken = authHeader\n\t\t\t}\n\t\t}\n\n\t\t// If not in header, try getting from URL parameter (for image resource requests etc.)\n\t\t// 如果 header 中没有，尝试从 URL 参数获取（用于图片等资源请求）\n\t\tif token == \"\" {\n\t\t\ttoken = c.Query(\"token\")\n\t\t}\n\n\t\tif token == \"\" {\n\t\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\n\t\tuser, err := app.ParseTokenWithKey(token, secretKey)\n\t\tif err != nil {\n\t\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\t\tc.Abort()\n\t\t\treturn\n\t\t}\n\n\t\tc.Set(\"user_token\", user)\n\t\tc.Next()\n\t}\n}\n\n// UserAuthToken user Token authentication middleware (no secret key, always fails)\n// UserAuthToken 用户 Token 认证中间件（无密钥，始终失败）\n// Deprecated: Use UserAuthTokenWithConfig instead\n// Deprecated: 推荐使用 UserAuthTokenWithConfig\nfunc UserAuthToken() gin.HandlerFunc {\n\treturn UserAuthTokenWithConfig(\"\")\n}\n"
  },
  {
    "path": "internal/model/backup_config.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage model\n\nimport (\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n)\n\nconst TableNameBackupConfig = \"backup_config\"\n\n// BackupConfig mapped from table <backup_config>\ntype BackupConfig struct {\n\tID               int64      `gorm:\"column:id;primaryKey\" json:\"id\" form:\"id\"`\n\tUID              int64      `gorm:\"column:uid;not null;index:idx_backup_config_uid,priority:1;default:0\" json:\"uid\" form:\"uid\"`\n\tVaultID          int64      `gorm:\"column:vault_id;not null;default:0\" json:\"vaultId\" form:\"vaultId\"`\n\tType             string     `gorm:\"column:type;default:''\" json:\"type\" form:\"type\"`\n\tStorageIds       string     `gorm:\"column:storage_ids;type:TEXT;default:''\" json:\"storageIds\" form:\"storageIds\"`\n\tIsEnabled        int64      `gorm:\"column:is_enabled;default:0\" json:\"isEnabled\" form:\"isEnabled\"`\n\tCronStrategy     string     `gorm:\"column:cron_strategy;default:''\" json:\"cronStrategy\" form:\"cronStrategy\"`\n\tCronExpression   string     `gorm:\"column:cron_expression;type:TEXT;default:''\" json:\"cronExpression\" form:\"cronExpression\"`\n\tIncludeVaultName int64      `gorm:\"column:include_vault_name;default:0\" json:\"includeVaultName\" form:\"includeVaultName\"`\n\tRetentionDays    int64      `gorm:\"column:retention_days;default:0\" json:\"retentionDays\" form:\"retentionDays\"`\n\tLastRunTime      time.Time  `gorm:\"column:last_run_time\" json:\"lastRunTime\" form:\"lastRunTime\"`\n\tNextRunTime      time.Time  `gorm:\"column:next_run_time;index:idx_backup_config_next_run_time,priority:1\" json:\"nextRunTime\" form:\"nextRunTime\"`\n\tLastStatus       int64      `gorm:\"column:last_status;default:0\" json:\"lastStatus\" form:\"lastStatus\"`\n\tLastMessage      string     `gorm:\"column:last_message;type:TEXT;default:''\" json:\"lastMessage\" form:\"lastMessage\"`\n\tCreatedAt        timex.Time `gorm:\"column:created_at;default:NULL;autoCreateTime:false\" json:\"createdAt\" form:\"createdAt\"`\n\tUpdatedAt        timex.Time `gorm:\"column:updated_at;default:NULL;autoUpdateTime:false\" json:\"updatedAt\" form:\"updatedAt\"`\n}\n\n// TableName BackupConfig's table name\nfunc (*BackupConfig) TableName() string {\n\treturn TableNameBackupConfig\n}\n"
  },
  {
    "path": "internal/model/backup_history.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage model\n\nimport (\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n)\n\nconst TableNameBackupHistory = \"backup_history\"\n\n// BackupHistory mapped from table <backup_history>\ntype BackupHistory struct {\n\tID        int64      `gorm:\"column:id;primaryKey\" json:\"id\" form:\"id\"`\n\tUID       int64      `gorm:\"column:uid;not null;index:idx_backup_history_uid,priority:1;default:0\" json:\"uid\" form:\"uid\"`\n\tConfigID  int64      `gorm:\"column:config_id;not null;index:idx_backup_history_config_id,priority:1;default:0\" json:\"configId\" form:\"configId\"`\n\tStorageID int64      `gorm:\"column:storage_id;not null;default:0\" json:\"storageId\" form:\"storageId\"`\n\tType      string     `gorm:\"column:type;default:''\" json:\"type\" form:\"type\"`\n\tStartTime time.Time  `gorm:\"column:start_time\" json:\"startTime\" form:\"startTime\"`\n\tEndTime   time.Time  `gorm:\"column:end_time\" json:\"endTime\" form:\"endTime\"`\n\tStatus    int64      `gorm:\"column:status;default:0\" json:\"status\" form:\"status\"`\n\tFileSize  int64      `gorm:\"column:file_size;default:0\" json:\"fileSize\" form:\"fileSize\"`\n\tFileCount int64      `gorm:\"column:file_count;default:0\" json:\"fileCount\" form:\"fileCount\"`\n\tMessage   string     `gorm:\"column:message;default:''\" json:\"message\" form:\"message\"`\n\tFilePath  string     `gorm:\"column:file_path;default:''\" json:\"filePath\" form:\"filePath\"`\n\tCreatedAt timex.Time `gorm:\"column:created_at;index:idx_backup_history_uid,priority:2;default:NULL;autoCreateTime:false\" json:\"createdAt\" form:\"createdAt\"`\n\tUpdatedAt timex.Time `gorm:\"column:updated_at;default:NULL;autoUpdateTime:false\" json:\"updatedAt\" form:\"updatedAt\"`\n}\n\n// TableName BackupHistory's table name\nfunc (*BackupHistory) TableName() string {\n\treturn TableNameBackupHistory\n}\n"
  },
  {
    "path": "internal/model/file.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage model\n\nimport \"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\nconst TableNameFile = \"file\"\n\n// File mapped from table <file>\ntype File struct {\n\tID               int64      `gorm:\"column:id;primaryKey\" json:\"id\" form:\"id\"`\n\tVaultID          int64      `gorm:\"column:vault_id;not null;index:idx_file_vault_id_path,priority:1;index:idx_file_vault_id_updated_timestamp,priority:1;index:idx_file_vault_id_updated_at,priority:1;index:idx_file_vault_id_rename,priority:1;index:idx_file_vault_id_action_rename,priority:1;index:idx_file_vault_id_path_hash,priority:1;index:idx_file_vault_id_action_fid,priority:1;default:0\" json:\"vaultId\" form:\"vaultId\"`\n\tAction           string     `gorm:\"column:action;type:varchar(255);index:idx_file_vault_id_action_rename,priority:2;index:idx_file_vault_id_action_fid,priority:2;default:''\" json:\"action\" form:\"action\"`\n\tFID              int64      `gorm:\"column:fid;index:idx_file_vault_id_action_fid,priority:3;default:0\" json:\"fid\" form:\"fid\"`\n\tPath             string     `gorm:\"column:path;type:varchar(255);index:idx_file_vault_id_path,priority:2;default:''\" json:\"path\" form:\"path\"`\n\tPathHash         string     `gorm:\"column:path_hash;type:varchar(255);index:idx_file_vault_id_path_hash,priority:2;default:''\" json:\"pathHash\" form:\"pathHash\"`\n\tContentHash      string     `gorm:\"column:content_hash;default:''\" json:\"contentHash\" form:\"contentHash\"`\n\tSavePath         string     `gorm:\"column:save_path;default:''\" json:\"savePath\" form:\"savePath\"`\n\tRename           int64      `gorm:\"column:rename;index:idx_file_vault_id_rename,priority:2;index:idx_file_vault_id_action_rename,priority:3;default:0\" json:\"rename\" form:\"rename\"`\n\tSize             int64      `gorm:\"column:size;not null;default:0\" json:\"size\" form:\"size\"`\n\tCtime            int64      `gorm:\"column:ctime;not null;default:0\" json:\"ctime\" form:\"ctime\"`\n\tMtime            int64      `gorm:\"column:mtime;not null;default:0\" json:\"mtime\" form:\"mtime\"`\n\tUpdatedTimestamp int64      `gorm:\"column:updated_timestamp;not null;index:idx_file_vault_id_updated_timestamp,priority:2;default:0\" json:\"updatedTimestamp\" form:\"updatedTimestamp\"`\n\tCreatedAt        timex.Time `gorm:\"column:created_at;default:NULL;autoCreateTime:false\" json:\"createdAt\" form:\"createdAt\"`\n\tUpdatedAt        timex.Time `gorm:\"column:updated_at;index:idx_file_vault_id_updated_at,priority:2;default:NULL;autoUpdateTime:false\" json:\"updatedAt\" form:\"updatedAt\"`\n}\n\n// TableName File's table name\nfunc (*File) TableName() string {\n\treturn TableNameFile\n}\n"
  },
  {
    "path": "internal/model/folder.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage model\n\nimport \"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\nconst TableNameFolder = \"folder\"\n\n// Folder mapped from table <folder>\ntype Folder struct {\n\tID               int64      `gorm:\"column:id;primaryKey\" json:\"id\" form:\"id\"`\n\tVaultID          int64      `gorm:\"column:vault_id;not null;index:idx_folder_vault_id_updated_timestamp,priority:1;index:idx_folder_vault_id_level_path,priority:1;index:idx_folder_vault_id_fid_path,priority:1;index:idx_folder_vault_id_path,priority:1;index:idx_folder_vault_id_path_hash,priority:1;default:0\" json:\"vaultId\" form:\"vaultId\"`\n\tAction           string     `gorm:\"column:action;default:''\" json:\"action\" form:\"action\"`\n\tPath             string     `gorm:\"column:path;type:varchar(255);index:idx_folder_vault_id_level_path,priority:3;index:idx_folder_vault_id_fid_path,priority:3;index:idx_folder_vault_id_path,priority:2;default:''\" json:\"path\" form:\"path\"`\n\tPathHash         string     `gorm:\"column:path_hash;type:varchar(255);index:idx_folder_vault_id_path_hash,priority:2;default:''\" json:\"pathHash\" form:\"pathHash\"`\n\tLevel            int64      `gorm:\"column:level;index:idx_folder_vault_id_level_path,priority:2;default:0\" json:\"level\" form:\"level\"`\n\tFID              int64      `gorm:\"column:fid;index:idx_folder_vault_id_fid_path,priority:2;default:0\" json:\"fid\" form:\"fid\"`\n\tCtime            int64      `gorm:\"column:ctime;default:0\" json:\"ctime\" form:\"ctime\"`\n\tMtime            int64      `gorm:\"column:mtime;not null;default:0\" json:\"mtime\" form:\"mtime\"`\n\tUpdatedTimestamp int64      `gorm:\"column:updated_timestamp;not null;index:idx_folder_vault_id_updated_timestamp,priority:2;default:0\" json:\"updatedTimestamp\" form:\"updatedTimestamp\"`\n\tCreatedAt        timex.Time `gorm:\"column:created_at;default:NULL;autoCreateTime:false\" json:\"createdAt\" form:\"createdAt\"`\n\tUpdatedAt        timex.Time `gorm:\"column:updated_at;default:NULL;autoUpdateTime:false\" json:\"updatedAt\" form:\"updatedAt\"`\n}\n\n// TableName Folder's table name\nfunc (*Folder) TableName() string {\n\treturn TableNameFolder\n}\n"
  },
  {
    "path": "internal/model/git_sync_config.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage model\n\nimport (\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n)\n\nconst TableNameGitSyncConfig = \"git_sync_config\"\n\n// GitSyncConfig mapped from table <git_sync_config>\ntype GitSyncConfig struct {\n\tID            int64      `gorm:\"column:id;primaryKey\" json:\"id\" form:\"id\"`\n\tUID           int64      `gorm:\"column:uid;not null;index:idx_git_sync_config_uid,priority:1;default:0\" json:\"uid\" form:\"uid\"`\n\tVaultID       int64      `gorm:\"column:vault_id;not null;default:0\" json:\"vaultId\" form:\"vaultId\"`\n\tRepoURL       string     `gorm:\"column:repo_url;default:''\" json:\"repoUrl\" form:\"repoUrl\"`\n\tUsername      string     `gorm:\"column:username;type:TEXT;default:''\" json:\"username\" form:\"username\"`\n\tPassword      string     `gorm:\"column:password;type:TEXT;default:''\" json:\"password\" form:\"password\"`\n\tBranch        string     `gorm:\"column:branch;type:TEXT;default:''\" json:\"branch\" form:\"branch\"`\n\tIsEnabled     int64      `gorm:\"column:is_enabled;default:0\" json:\"isEnabled\" form:\"isEnabled\"`\n\tDelay         int64      `gorm:\"column:delay;default:0\" json:\"delay\" form:\"delay\"`\n\tRetentionDays int64      `gorm:\"column:retention_days;default:0\" json:\"retentionDays\" form:\"retentionDays\"`\n\tLastSyncTime  time.Time  `gorm:\"column:last_sync_time\" json:\"lastSyncTime\" form:\"lastSyncTime\"`\n\tLastStatus    int64      `gorm:\"column:last_status;default:0\" json:\"lastStatus\" form:\"lastStatus\"`\n\tLastMessage   string     `gorm:\"column:last_message;type:TEXT;default:''\" json:\"lastMessage\" form:\"lastMessage\"`\n\tCreatedAt     timex.Time `gorm:\"column:created_at;default:NULL;autoCreateTime:false\" json:\"createdAt\" form:\"createdAt\"`\n\tUpdatedAt     timex.Time `gorm:\"column:updated_at;default:NULL;autoUpdateTime:false\" json:\"updatedAt\" form:\"updatedAt\"`\n}\n\n// TableName GitSyncConfig's table name\nfunc (*GitSyncConfig) TableName() string {\n\treturn TableNameGitSyncConfig\n}\n"
  },
  {
    "path": "internal/model/git_sync_history.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage model\n\nimport (\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n)\n\nconst TableNameGitSyncHistory = \"git_sync_history\"\n\n// GitSyncHistory mapped from table <git_sync_history>\ntype GitSyncHistory struct {\n\tID        int64      `gorm:\"column:id;primaryKey\" json:\"id\" form:\"id\"`\n\tUID       int64      `gorm:\"column:uid;not null;index:idx_git_sync_history_uid,priority:1;default:0\" json:\"uid\" form:\"uid\"`\n\tConfigID  int64      `gorm:\"column:config_id;not null;index:idx_git_sync_history_config_id,priority:1;default:0\" json:\"configId\" form:\"configId\"`\n\tStartTime time.Time  `gorm:\"column:start_time\" json:\"startTime\" form:\"startTime\"`\n\tEndTime   time.Time  `gorm:\"column:end_time\" json:\"endTime\" form:\"endTime\"`\n\tStatus    int64      `gorm:\"column:status;default:0\" json:\"status\" form:\"status\"`\n\tMessage   string     `gorm:\"column:message;type:TEXT;default:''\" json:\"message\" form:\"message\"`\n\tCreatedAt timex.Time `gorm:\"column:created_at;index:idx_git_sync_history_uid,priority:2;default:NULL;autoCreateTime:false\" json:\"createdAt\" form:\"createdAt\"`\n\tUpdatedAt timex.Time `gorm:\"column:updated_at;default:NULL;autoUpdateTime:false\" json:\"updatedAt\" form:\"updatedAt\"`\n}\n\n// TableName GitSyncHistory's table name\nfunc (*GitSyncHistory) TableName() string {\n\treturn TableNameGitSyncHistory\n}\n"
  },
  {
    "path": "internal/model/model.go",
    "content": "package model\n\nimport (\n\t\"gorm.io/gorm\"\n)\n\nfunc AutoMigrate(db *gorm.DB, key string) error {\n\tif db == nil {\n\t\treturn nil\n\t}\n\tswitch key {\n\n\tcase \"BackupConfig\":\n\t\treturn db.AutoMigrate(BackupConfig{})\n\n\tcase \"BackupHistory\":\n\t\treturn db.AutoMigrate(BackupHistory{})\n\n\tcase \"File\":\n\t\treturn db.AutoMigrate(File{})\n\n\tcase \"Folder\":\n\t\treturn db.AutoMigrate(Folder{})\n\n\tcase \"GitSyncConfig\":\n\t\treturn db.AutoMigrate(GitSyncConfig{})\n\n\tcase \"GitSyncHistory\":\n\t\treturn db.AutoMigrate(GitSyncHistory{})\n\n\tcase \"Note\":\n\t\treturn db.AutoMigrate(Note{})\n\n\tcase \"NoteHistory\":\n\t\treturn db.AutoMigrate(NoteHistory{})\n\n\tcase \"NoteLink\":\n\t\treturn db.AutoMigrate(NoteLink{})\n\n\tcase \"Setting\":\n\t\treturn db.AutoMigrate(Setting{})\n\n\tcase \"Storage\":\n\t\treturn db.AutoMigrate(Storage{})\n\n\tcase \"SyncLog\":\n\t\treturn db.AutoMigrate(SyncLog{})\n\n\tcase \"User\":\n\t\treturn db.AutoMigrate(User{})\n\n\tcase \"UserShare\":\n\t\treturn db.AutoMigrate(UserShare{})\n\n\tcase \"Vault\":\n\t\treturn db.AutoMigrate(Vault{})\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "internal/model/note.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage model\n\nimport \"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\nconst TableNameNote = \"note\"\n\n// Note mapped from table <note>\ntype Note struct {\n\tID                      int64      `gorm:\"column:id;primaryKey\" json:\"id\" form:\"id\"`\n\tVaultID                 int64      `gorm:\"column:vault_id;not null;index:idx_vault_id_path,priority:1;index:idx_vault_id_updated_timestamp,priority:1;index:idx_vault_id_updated_at,priority:1;index:idx_vault_id_rename,priority:1;index:idx_vault_id_action_rename,priority:1;index:idx_vault_id_action_fid,priority:1;default:0\" json:\"vaultId\" form:\"vaultId\"`\n\tAction                  string     `gorm:\"column:action;type:varchar(255);index:idx_vault_id_action_rename,priority:2;index:idx_vault_id_action_fid,priority:2;default:''\" json:\"action\" form:\"action\"`\n\tRename                  int64      `gorm:\"column:rename;index:idx_vault_id_rename,priority:2;index:idx_vault_id_action_rename,priority:3;default:0\" json:\"rename\" form:\"rename\"`\n\tFID                     int64      `gorm:\"column:fid;index:idx_vault_id_action_fid,priority:3;default:0\" json:\"fid\" form:\"fid\"`\n\tPath                    string     `gorm:\"column:path;type:varchar(255);index:idx_vault_id_path,priority:2;default:''\" json:\"path\" form:\"path\"`\n\tPathHash                string     `gorm:\"column:path_hash;default:''\" json:\"pathHash\" form:\"pathHash\"`\n\tContent                 string     `gorm:\"column:content;default:''\" json:\"content\" form:\"content\"`\n\tContentHash             string     `gorm:\"column:content_hash;default:''\" json:\"contentHash\" form:\"contentHash\"`\n\tContentLastSnapshot     string     `gorm:\"column:content_last_snapshot;not null;default:''\" json:\"contentLastSnapshot\" form:\"contentLastSnapshot\"`\n\tContentLastSnapshotHash string     `gorm:\"column:content_last_snapshot_hash;not null;default:''\" json:\"contentLastSnapshotHash\" form:\"contentLastSnapshotHash\"`\n\tVersion                 int64      `gorm:\"column:version;default:0\" json:\"version\" form:\"version\"`\n\tClientName              string     `gorm:\"column:client_name;not null;default:''\" json:\"clientName\" form:\"clientName\"`\n\tClientType              string     `gorm:\"column:client_type;not null;default:''\" json:\"clientType\" form:\"clientType\"`\n\tClientVersion           string     `gorm:\"column:client_version;not null;default:''\" json:\"clientVersion\" form:\"clientVersion\"`\n\tSize                    int64      `gorm:\"column:size;default:0\" json:\"size\" form:\"size\"`\n\tCtime                   int64      `gorm:\"column:ctime;default:0\" json:\"ctime\" form:\"ctime\"`\n\tMtime                   int64      `gorm:\"column:mtime;default:0\" json:\"mtime\" form:\"mtime\"`\n\tUpdatedTimestamp        int64      `gorm:\"column:updated_timestamp;index:idx_vault_id_updated_timestamp,priority:2;default:0\" json:\"updatedTimestamp\" form:\"updatedTimestamp\"`\n\tCreatedAt               timex.Time `gorm:\"column:created_at;default:NULL;autoCreateTime:false\" json:\"createdAt\" form:\"createdAt\"`\n\tUpdatedAt               timex.Time `gorm:\"column:updated_at;index:idx_vault_id_updated_at,priority:2;default:NULL;autoUpdateTime:false\" json:\"updatedAt\" form:\"updatedAt\"`\n}\n\n// TableName Note's table name\nfunc (*Note) TableName() string {\n\treturn TableNameNote\n}\n"
  },
  {
    "path": "internal/model/note_fts.go",
    "content": "// Package model 定义数据模型\npackage model\n\nimport (\n\t\"gorm.io/gorm\"\n)\n\n// FTS 表版本号，修改此值会触发重建索引\nconst NoteFTSVersion = 3\n\n// NoteFTS 存储笔记全文搜索的快照数据\ntype NoteFTS struct {\n\tNoteID  int64  `gorm:\"column:note_id;primaryKey\" json:\"noteId\"`\n\tPath    string `gorm:\"column:path\" json:\"path\"`\n\tContent string `gorm:\"column:content\" json:\"content\"`\n}\n\n// TableName 返回表名\nfunc (*NoteFTS) TableName() string {\n\treturn \"note_fts\"\n}\n\n// NoteFTSToken 倒排索引表，支持多数据库全文搜索\ntype NoteFTSToken struct {\n\tID     int64  `gorm:\"column:id;primaryKey;autoIncrement\"`\n\tNoteID int64  `gorm:\"column:note_id;index:idx_fts_note_id;index:idx_fts_token_note_id,priority:2\"`\n\tToken  string `gorm:\"column:token;type:varchar(255);index:idx_fts_token;index:idx_fts_token_note_id,priority:1\"`\n}\n\nfunc (*NoteFTSToken) TableName() string {\n\treturn \"note_fts_token\"\n}\n\n// NoteFTSMeta FTS 元数据表，用于存储版本信息\ntype NoteFTSMeta struct {\n\tKey   string `gorm:\"column:key;primaryKey\"`\n\tValue string `gorm:\"column:value\"`\n}\n\nfunc (*NoteFTSMeta) TableName() string {\n\treturn \"note_fts_meta\"\n}\n\n// CreateNoteFTSTable 创建搜索相关的标准数据库表\nfunc CreateNoteFTSTable(db *gorm.DB) error {\n\t// 创建元数据表\n\tif err := db.AutoMigrate(&NoteFTSMeta{}); err != nil {\n\t\treturn err\n\t}\n\n\t// 检查版本\n\tvar meta NoteFTSMeta\n\tdb.Where(\"key = ?\", \"version\").First(&meta)\n\tcurrentVersion := meta.Value\n\n\t// 如果版本不匹配，删除旧表重建\n\tif currentVersion != \"\" && currentVersion != string(rune(NoteFTSVersion+'0')) {\n\t\t_ = DropNoteFTSTable(db)\n\t}\n\n\t// 执行自动迁移\n\tif err := db.AutoMigrate(&NoteFTS{}, &NoteFTSToken{}); err != nil {\n\t\treturn err\n\t}\n\n\t// 更新版本号\n\tdb.Save(&NoteFTSMeta{Key: \"version\", Value: string(rune(NoteFTSVersion + '0'))})\n\n\treturn nil\n}\n\n// DropNoteFTSTable 删除全文搜索相关的表\nfunc DropNoteFTSTable(db *gorm.DB) error {\n\t_ = db.Migrator().DropTable(&NoteFTS{})\n\t_ = db.Migrator().DropTable(&NoteFTSToken{})\n\treturn nil\n}\n"
  },
  {
    "path": "internal/model/note_history.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage model\n\nimport \"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\nconst TableNameNoteHistory = \"note_history\"\n\n// NoteHistory mapped from table <note_history>\ntype NoteHistory struct {\n\tID          int64      `gorm:\"column:id;primaryKey\" json:\"id\" form:\"id\"`\n\tNoteID      int64      `gorm:\"column:note_id;not null;index:idx_note_history_content_hash,priority:1;index:idx_note_history_version,priority:1;index:idx_note_history_note_id,priority:1;default:0\" json:\"noteId\" form:\"noteId\"`\n\tVaultID     int64      `gorm:\"column:vault_id;not null;default:0\" json:\"vaultId\" form:\"vaultId\"`\n\tPath        string     `gorm:\"column:path;default:''\" json:\"path\" form:\"path\"`\n\tContent     string     `gorm:\"column:content;default:''\" json:\"content\" form:\"content\"`\n\tContentHash string     `gorm:\"column:content_hash;type:varchar(255);not null;index:idx_note_history_content_hash,priority:2;default:''\" json:\"contentHash\" form:\"contentHash\"`\n\tDiffPatch   string     `gorm:\"column:diff_patch;default:''\" json:\"diffPatch\" form:\"diffPatch\"`\n\tClientName  string     `gorm:\"column:client_name;default:''\" json:\"clientName\" form:\"clientName\"`\n\tClientType  string     `gorm:\"column:client_type;default:''\" json:\"clientType\" form:\"clientType\"`\n\tClientVersion string   `gorm:\"column:client_version;default:''\" json:\"clientVersion\" form:\"clientVersion\"`\n\tVersion     int64      `gorm:\"column:version;index:idx_note_history_version,priority:2;default:0\" json:\"version\" form:\"version\"`\n\tCreatedAt   timex.Time `gorm:\"column:created_at;default:NULL;autoCreateTime:false\" json:\"createdAt\" form:\"createdAt\"`\n\tUpdatedAt   timex.Time `gorm:\"column:updated_at;default:NULL;autoUpdateTime:false\" json:\"updatedAt\" form:\"updatedAt\"`\n}\n\n// TableName NoteHistory's table name\nfunc (*NoteHistory) TableName() string {\n\treturn TableNameNoteHistory\n}\n"
  },
  {
    "path": "internal/model/note_link.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage model\n\nimport \"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\nconst TableNameNoteLink = \"note_link\"\n\n// NoteLink mapped from table <note_link>\ntype NoteLink struct {\n\tID             int64      `gorm:\"column:id;primaryKey\" json:\"id\" form:\"id\"`\n\tSourceNoteID   int64      `gorm:\"column:source_note_id;not null;index:idx_source_note,priority:1;default:0\" json:\"sourceNoteId\" form:\"sourceNoteId\"`\n\tTargetPath     string     `gorm:\"column:target_path;not null;default:''\" json:\"targetPath\" form:\"targetPath\"`\n\tTargetPathHash string     `gorm:\"column:target_path_hash;type:varchar(255);not null;index:idx_target_path_hash,priority:1;default:''\" json:\"targetPathHash\" form:\"targetPathHash\"`\n\tLinkText       string     `gorm:\"column:link_text;default:''\" json:\"linkText\" form:\"linkText\"`\n\tIsEmbed        int64      `gorm:\"column:is_embed;default:0\" json:\"isEmbed\" form:\"isEmbed\"`\n\tVaultID        int64      `gorm:\"column:vault_id;not null;index:idx_target_path_hash,priority:2;default:0\" json:\"vaultId\" form:\"vaultId\"`\n\tUID            int64      `gorm:\"column:uid;not null;index:idx_target_path_hash,priority:3;default:0\" json:\"uid\" form:\"uid\"`\n\tCreatedAt      timex.Time `gorm:\"column:created_at;default:NULL;autoCreateTime:false\" json:\"createdAt\" form:\"createdAt\"`\n}\n\n// TableName NoteLink's table name\nfunc (*NoteLink) TableName() string {\n\treturn TableNameNoteLink\n}\n"
  },
  {
    "path": "internal/model/schema_version.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage model\n\nimport \"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\nconst TableNameSchemaVersion = \"schema_version\"\n\n// SchemaVersion mapped from table <schema_version>\ntype SchemaVersion struct {\n\tID          int64      `gorm:\"column:id;primaryKey;default:0\" json:\"id\"`\n\tVersion     string     `gorm:\"column:version;not null;uniqueIndex:idx_schema_version_version,priority:1;default:''\" json:\"version\"`\n\tDescription string     `gorm:\"column:description;default:''\" json:\"description\"`\n\tAppliedAt   timex.Time `gorm:\"column:applied_at;not null\" json:\"applied_at\"`\n}\n\n// TableName SchemaVersion's table name\nfunc (*SchemaVersion) TableName() string {\n\treturn TableNameSchemaVersion\n}\n"
  },
  {
    "path": "internal/model/setting.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage model\n\nimport \"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\nconst TableNameSetting = \"setting\"\n\n// Setting mapped from table <setting>\ntype Setting struct {\n\tID               int64      `gorm:\"column:id;primaryKey;index:idx_setting_id_path,priority:1;index:idx_setting_id_updated_timestamp,priority:1;index:idx_setting_id_updated_at,priority:1;index:idx_setting_id_path_hash,priority:1\" json:\"id\" form:\"id\"`\n\tVaultID          int64      `gorm:\"column:vault_id;not null;default:0\" json:\"vaultId\" form:\"vaultId\"`\n\tAction           string     `gorm:\"column:action;default:''\" json:\"action\" form:\"action\"`\n\tPath             string     `gorm:\"column:path;type:varchar(255);index:idx_setting_id_path,priority:2;default:''\" json:\"path\" form:\"path\"`\n\tPathHash         string     `gorm:\"column:path_hash;type:varchar(255);index:idx_setting_id_path_hash,priority:2;default:''\" json:\"pathHash\" form:\"pathHash\"`\n\tContent          string     `gorm:\"column:content;default:''\" json:\"content\" form:\"content\"`\n\tContentHash      string     `gorm:\"column:content_hash;default:''\" json:\"contentHash\" form:\"contentHash\"`\n\tSize             int64      `gorm:\"column:size;default:0\" json:\"size\" form:\"size\"`\n\tRename           int64      `gorm:\"column:rename;default:0\" json:\"rename\" form:\"rename\"`\n\tCtime            int64      `gorm:\"column:ctime;default:0\" json:\"ctime\" form:\"ctime\"`\n\tMtime            int64      `gorm:\"column:mtime;default:0\" json:\"mtime\" form:\"mtime\"`\n\tUpdatedTimestamp int64      `gorm:\"column:updated_timestamp;index:idx_setting_id_updated_timestamp,priority:2;default:0\" json:\"updatedTimestamp\" form:\"updatedTimestamp\"`\n\tCreatedAt        timex.Time `gorm:\"column:created_at;default:NULL;autoCreateTime:false\" json:\"createdAt\" form:\"createdAt\"`\n\tUpdatedAt        timex.Time `gorm:\"column:updated_at;index:idx_setting_id_updated_at,priority:2;default:NULL;autoUpdateTime:false\" json:\"updatedAt\" form:\"updatedAt\"`\n}\n\n// TableName Setting's table name\nfunc (*Setting) TableName() string {\n\treturn TableNameSetting\n}\n"
  },
  {
    "path": "internal/model/sqlite_sequence.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage model\n\nconst TableNameSqliteSequence = \"sqlite_sequence\"\n\n// SqliteSequence mapped from table <sqlite_sequence>\ntype SqliteSequence struct {\n\tName string `gorm:\"column:name;type\" json:\"name\"`\n\tSeq  string `gorm:\"column:seq;type\" json:\"seq\"`\n}\n\n// TableName SqliteSequence's table name\nfunc (*SqliteSequence) TableName() string {\n\treturn TableNameSqliteSequence\n}\n"
  },
  {
    "path": "internal/model/storage.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage model\n\nimport \"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\nconst TableNameStorage = \"storage\"\n\n// Storage mapped from table <storage>\ntype Storage struct {\n\tID              int64      `gorm:\"column:id;primaryKey\" json:\"id\" form:\"id\"`\n\tUID             int64      `gorm:\"column:uid;not null;index:idx_storage_uid,priority:1;default:0\" json:\"uid\" form:\"uid\"`\n\tType            string     `gorm:\"column:type;default:''\" json:\"type\" form:\"type\"`\n\tEndpoint        string     `gorm:\"column:endpoint;default:''\" json:\"endpoint\" form:\"endpoint\"`\n\tRegion          string     `gorm:\"column:region;default:''\" json:\"region\" form:\"region\"`\n\tAccountID       string     `gorm:\"column:account_id;default:''\" json:\"accountId\" form:\"accountId\"`\n\tBucketName      string     `gorm:\"column:bucket_name;default:''\" json:\"bucketName\" form:\"bucketName\"`\n\tAccessKeyID     string     `gorm:\"column:access_key_id;default:''\" json:\"accessKeyId\" form:\"accessKeyId\"`\n\tAccessKeySecret string     `gorm:\"column:access_key_secret;default:''\" json:\"accessKeySecret\" form:\"accessKeySecret\"`\n\tCustomPath      string     `gorm:\"column:custom_path;default:''\" json:\"customPath\" form:\"customPath\"`\n\tAccessURLPrefix string     `gorm:\"column:access_url_prefix;default:''\" json:\"accessUrlPrefix\" form:\"accessUrlPrefix\"`\n\tUser            string     `gorm:\"column:user;default:''\" json:\"user\" form:\"user\"`\n\tPassword        string     `gorm:\"column:password;default:''\" json:\"password\" form:\"password\"`\n\tIsEnabled       int64      `gorm:\"column:is_enabled;not null;default:0\" json:\"isEnabled\" form:\"isEnabled\"`\n\tIsDeleted       int64      `gorm:\"column:is_deleted;not null;default:0\" json:\"isDeleted\" form:\"isDeleted\"`\n\tCreatedAt       timex.Time `gorm:\"column:created_at;default:NULL;autoCreateTime:false\" json:\"createdAt\" form:\"createdAt\"`\n\tUpdatedAt       timex.Time `gorm:\"column:updated_at;default:NULL;autoUpdateTime:false\" json:\"updatedAt\" form:\"updatedAt\"`\n\tDeletedAt       timex.Time `gorm:\"column:deleted_at;default:NULL\" json:\"deletedAt\" form:\"deletedAt\"`\n}\n\n// TableName Storage's table name\nfunc (*Storage) TableName() string {\n\treturn TableNameStorage\n}\n"
  },
  {
    "path": "internal/model/sync_log.go",
    "content": "// Code generated manually. DO NOT EDIT by code generator.\n// 手动编写，请勿由代码生成器覆盖。\n\npackage model\n\nimport \"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\nconst TableNameSyncLog = \"sync_log\"\n\n// SyncLog mapped from table <sync_log>\n// SyncLog 对应数据库表 sync_log\ntype SyncLog struct {\n\tID            int64      `gorm:\"column:id;primaryKey\" json:\"id\" form:\"id\"`\n\tUID           int64      `gorm:\"column:uid;not null;default:0\" json:\"uid\" form:\"uid\"`\n\tVaultID       int64      `gorm:\"column:vault_id;not null;default:0\" json:\"vaultId\" form:\"vaultId\"`\n\tType          string     `gorm:\"column:type;not null;default:''\" json:\"type\" form:\"type\"`\n\tAction        string     `gorm:\"column:action;not null;default:''\" json:\"action\" form:\"action\"`\n\tChangedFields string     `gorm:\"column:changed_fields;not null;default:''\" json:\"changedFields\" form:\"changedFields\"`\n\tPath          string     `gorm:\"column:path;default:''\" json:\"path\" form:\"path\"`\n\tPathHash      string     `gorm:\"column:path_hash;default:''\" json:\"pathHash\" form:\"pathHash\"`\n\tSize          int64      `gorm:\"column:size;default:0\" json:\"size\" form:\"size\"`\n\tClientName    string     `gorm:\"column:client_name;default:''\" json:\"clientName\" form:\"clientName\"`\n\tClientType    string     `gorm:\"column:client_type;default:''\" json:\"clientType\" form:\"clientType\"`\n\tClientVersion string     `gorm:\"column:client_version;default:''\" json:\"clientVersion\" form:\"clientVersion\"`\n\tStatus        int        `gorm:\"column:status;default:1\" json:\"status\" form:\"status\"`\n\tMessage       string     `gorm:\"column:message;default:''\" json:\"message\" form:\"message\"`\n\tCreatedAt     timex.Time `gorm:\"column:created_at;default:NULL;autoCreateTime:false\" json:\"createdAt\" form:\"createdAt\"`\n}\n\n// TableName SyncLog's table name\n// TableName 返回 SyncLog 的表名\nfunc (*SyncLog) TableName() string {\n\treturn TableNameSyncLog\n}\n"
  },
  {
    "path": "internal/model/user.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage model\n\nimport \"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\nconst TableNameUser = \"user\"\n\n// User mapped from table <user>\ntype User struct {\n\tUID       int64      `gorm:\"column:uid;primaryKey\" json:\"uid\" form:\"uid\"`\n\tEmail     string     `gorm:\"column:email;type:varchar(255);index:idx_pre_user_email,priority:1;default:''\" json:\"email\" form:\"email\"`\n\tUsername  string     `gorm:\"column:username;default:''\" json:\"username\" form:\"username\"`\n\tPassword  string     `gorm:\"column:password;default:''\" json:\"password\" form:\"password\"`\n\tSalt      string     `gorm:\"column:salt;default:''\" json:\"salt\" form:\"salt\"`\n\tToken     string     `gorm:\"column:token;default:''\" json:\"token\" form:\"token\"`\n\tAvatar    string     `gorm:\"column:avatar;default:''\" json:\"avatar\" form:\"avatar\"`\n\tIsDeleted int64      `gorm:\"column:is_deleted;default:0\" json:\"isDeleted\" form:\"isDeleted\"`\n\tUpdatedAt timex.Time `gorm:\"column:updated_at;default:NULL;autoUpdateTime:false\" json:\"updatedAt\" form:\"updatedAt\"`\n\tCreatedAt timex.Time `gorm:\"column:created_at;default:NULL;autoCreateTime:false\" json:\"createdAt\" form:\"createdAt\"`\n\tDeletedAt timex.Time `gorm:\"column:deleted_at;default:NULL\" json:\"deletedAt\" form:\"deletedAt\"`\n}\n\n// TableName User's table name\nfunc (*User) TableName() string {\n\treturn TableNameUser\n}\n"
  },
  {
    "path": "internal/model/user_share.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage model\n\nimport (\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n)\n\nconst TableNameUserShare = \"user_share\"\n\n// UserShare mapped from table <user_share>\ntype UserShare struct {\n\tID           int64      `gorm:\"column:id;primaryKey\" json:\"id\" form:\"id\"`\n\tUID          int64      `gorm:\"column:uid;not null;index:idx_user_share_uid,priority:1;default:0\" json:\"uid\" form:\"uid\"`\n\tResType      string     `gorm:\"column:res_type;type:varchar(255);not null;index:idx_user_share_res_type_id,priority:1;default:''\" json:\"resType\" form:\"resType\"`\n\tResID        int64      `gorm:\"column:res_id;index:idx_user_share_res_type_id,priority:2;default:0\" json:\"resId\" form:\"resId\"`\n\tRes          string     `gorm:\"column:res;type:TEXT;default:''\" json:\"res\" form:\"res\"`\n\tStatus       int64      `gorm:\"column:status;default:0\" json:\"status\" form:\"status\"`\n\tViewCount    int64      `gorm:\"column:view_count;default:0\" json:\"viewCount\" form:\"viewCount\"`\n\tLastViewedAt time.Time  `gorm:\"column:last_viewed_at\" json:\"lastViewedAt\" form:\"lastViewedAt\"`\n\tExpiresAt    time.Time  `gorm:\"column:expires_at\" json:\"expiresAt\" form:\"expiresAt\"`\n\tPassword     string     `gorm:\"column:password;default:''\" json:\"password\" form:\"password\"`\n\tShortLink    string     `gorm:\"column:short_link;default:''\" json:\"shortLink\" form:\"shortLink\"`\n\tCreatedAt    timex.Time `gorm:\"column:created_at;default:NULL;autoCreateTime:false\" json:\"createdAt\" form:\"createdAt\"`\n\tUpdatedAt    timex.Time `gorm:\"column:updated_at;default:NULL;autoUpdateTime:false\" json:\"updatedAt\" form:\"updatedAt\"`\n}\n\n// TableName UserShare's table name\nfunc (*UserShare) TableName() string {\n\treturn TableNameUserShare\n}\n"
  },
  {
    "path": "internal/model/vault.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage model\n\nimport \"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\nconst TableNameVault = \"vault\"\n\n// Vault mapped from table <vault>\ntype Vault struct {\n\tID        int64      `gorm:\"column:id;primaryKey\" json:\"id\" form:\"id\"`\n\tVault     string     `gorm:\"column:vault;type:varchar(255);index:idx_vault_uid,priority:1;default:''\" json:\"vault\" form:\"vault\"`\n\tNoteCount int64      `gorm:\"column:note_count;default:0\" json:\"noteCount\" form:\"noteCount\"`\n\tNoteSize  int64      `gorm:\"column:note_size;default:0\" json:\"noteSize\" form:\"noteSize\"`\n\tFileCount int64      `gorm:\"column:file_count;default:0\" json:\"fileCount\" form:\"fileCount\"`\n\tFileSize  int64      `gorm:\"column:file_size;default:0\" json:\"fileSize\" form:\"fileSize\"`\n\tIsDeleted int64      `gorm:\"column:is_deleted;default:0\" json:\"isDeleted\" form:\"isDeleted\"`\n\tCreatedAt timex.Time `gorm:\"column:created_at;default:NULL;autoCreateTime:false\" json:\"createdAt\" form:\"createdAt\"`\n\tUpdatedAt timex.Time `gorm:\"column:updated_at;default:NULL;autoUpdateTime:false\" json:\"updatedAt\" form:\"updatedAt\"`\n}\n\n// TableName Vault's table name\nfunc (*Vault) TableName() string {\n\treturn TableNameVault\n}\n"
  },
  {
    "path": "internal/query/backup_config.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage query\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\n\t\"gorm.io/gen\"\n\t\"gorm.io/gen/field\"\n\n\t\"gorm.io/plugin/dbresolver\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n)\n\nfunc newBackupConfig(db *gorm.DB, opts ...gen.DOOption) backupConfig {\n\t_backupConfig := backupConfig{}\n\n\t_backupConfig.backupConfigDo.UseDB(db, opts...)\n\t_backupConfig.backupConfigDo.UseModel(&model.BackupConfig{})\n\n\ttableName := _backupConfig.backupConfigDo.TableName()\n\t_backupConfig.ALL = field.NewAsterisk(tableName)\n\t_backupConfig.ID = field.NewInt64(tableName, \"id\")\n\t_backupConfig.UID = field.NewInt64(tableName, \"uid\")\n\t_backupConfig.VaultID = field.NewInt64(tableName, \"vault_id\")\n\t_backupConfig.Type = field.NewString(tableName, \"type\")\n\t_backupConfig.StorageIds = field.NewString(tableName, \"storage_ids\")\n\t_backupConfig.IsEnabled = field.NewInt64(tableName, \"is_enabled\")\n\t_backupConfig.CronStrategy = field.NewString(tableName, \"cron_strategy\")\n\t_backupConfig.CronExpression = field.NewString(tableName, \"cron_expression\")\n\t_backupConfig.IncludeVaultName = field.NewInt64(tableName, \"include_vault_name\")\n\t_backupConfig.RetentionDays = field.NewInt64(tableName, \"retention_days\")\n\t_backupConfig.LastRunTime = field.NewTime(tableName, \"last_run_time\")\n\t_backupConfig.NextRunTime = field.NewTime(tableName, \"next_run_time\")\n\t_backupConfig.LastStatus = field.NewInt64(tableName, \"last_status\")\n\t_backupConfig.LastMessage = field.NewString(tableName, \"last_message\")\n\t_backupConfig.CreatedAt = field.NewField(tableName, \"created_at\")\n\t_backupConfig.UpdatedAt = field.NewField(tableName, \"updated_at\")\n\n\t_backupConfig.fillFieldMap()\n\n\treturn _backupConfig\n}\n\ntype backupConfig struct {\n\tbackupConfigDo backupConfigDo\n\n\tALL              field.Asterisk\n\tID               field.Int64\n\tUID              field.Int64\n\tVaultID          field.Int64\n\tType             field.String\n\tStorageIds       field.String\n\tIsEnabled        field.Int64\n\tCronStrategy     field.String\n\tCronExpression   field.String\n\tIncludeVaultName field.Int64\n\tRetentionDays    field.Int64\n\tLastRunTime      field.Time\n\tNextRunTime      field.Time\n\tLastStatus       field.Int64\n\tLastMessage      field.String\n\tCreatedAt        field.Field\n\tUpdatedAt        field.Field\n\n\tfieldMap map[string]field.Expr\n}\n\nfunc (b backupConfig) Table(newTableName string) *backupConfig {\n\tb.backupConfigDo.UseTable(newTableName)\n\treturn b.updateTableName(newTableName)\n}\n\nfunc (b backupConfig) As(alias string) *backupConfig {\n\tb.backupConfigDo.DO = *(b.backupConfigDo.As(alias).(*gen.DO))\n\treturn b.updateTableName(alias)\n}\n\nfunc (b *backupConfig) updateTableName(table string) *backupConfig {\n\tb.ALL = field.NewAsterisk(table)\n\tb.ID = field.NewInt64(table, \"id\")\n\tb.UID = field.NewInt64(table, \"uid\")\n\tb.VaultID = field.NewInt64(table, \"vault_id\")\n\tb.Type = field.NewString(table, \"type\")\n\tb.StorageIds = field.NewString(table, \"storage_ids\")\n\tb.IsEnabled = field.NewInt64(table, \"is_enabled\")\n\tb.CronStrategy = field.NewString(table, \"cron_strategy\")\n\tb.CronExpression = field.NewString(table, \"cron_expression\")\n\tb.IncludeVaultName = field.NewInt64(table, \"include_vault_name\")\n\tb.RetentionDays = field.NewInt64(table, \"retention_days\")\n\tb.LastRunTime = field.NewTime(table, \"last_run_time\")\n\tb.NextRunTime = field.NewTime(table, \"next_run_time\")\n\tb.LastStatus = field.NewInt64(table, \"last_status\")\n\tb.LastMessage = field.NewString(table, \"last_message\")\n\tb.CreatedAt = field.NewField(table, \"created_at\")\n\tb.UpdatedAt = field.NewField(table, \"updated_at\")\n\n\tb.fillFieldMap()\n\n\treturn b\n}\n\nfunc (b *backupConfig) WithContext(ctx context.Context) IBackupConfigDo {\n\treturn b.backupConfigDo.WithContext(ctx)\n}\n\nfunc (b backupConfig) TableName() string { return b.backupConfigDo.TableName() }\n\nfunc (b backupConfig) Alias() string { return b.backupConfigDo.Alias() }\n\nfunc (b backupConfig) Columns(cols ...field.Expr) gen.Columns {\n\treturn b.backupConfigDo.Columns(cols...)\n}\n\nfunc (b *backupConfig) GetFieldByName(fieldName string) (field.OrderExpr, bool) {\n\t_f, ok := b.fieldMap[fieldName]\n\tif !ok || _f == nil {\n\t\treturn nil, false\n\t}\n\t_oe, ok := _f.(field.OrderExpr)\n\treturn _oe, ok\n}\n\nfunc (b *backupConfig) fillFieldMap() {\n\tb.fieldMap = make(map[string]field.Expr, 16)\n\tb.fieldMap[\"id\"] = b.ID\n\tb.fieldMap[\"uid\"] = b.UID\n\tb.fieldMap[\"vault_id\"] = b.VaultID\n\tb.fieldMap[\"type\"] = b.Type\n\tb.fieldMap[\"storage_ids\"] = b.StorageIds\n\tb.fieldMap[\"is_enabled\"] = b.IsEnabled\n\tb.fieldMap[\"cron_strategy\"] = b.CronStrategy\n\tb.fieldMap[\"cron_expression\"] = b.CronExpression\n\tb.fieldMap[\"include_vault_name\"] = b.IncludeVaultName\n\tb.fieldMap[\"retention_days\"] = b.RetentionDays\n\tb.fieldMap[\"last_run_time\"] = b.LastRunTime\n\tb.fieldMap[\"next_run_time\"] = b.NextRunTime\n\tb.fieldMap[\"last_status\"] = b.LastStatus\n\tb.fieldMap[\"last_message\"] = b.LastMessage\n\tb.fieldMap[\"created_at\"] = b.CreatedAt\n\tb.fieldMap[\"updated_at\"] = b.UpdatedAt\n}\n\nfunc (b backupConfig) clone(db *gorm.DB) backupConfig {\n\tb.backupConfigDo.ReplaceConnPool(db.Statement.ConnPool)\n\treturn b\n}\n\nfunc (b backupConfig) replaceDB(db *gorm.DB) backupConfig {\n\tb.backupConfigDo.ReplaceDB(db)\n\treturn b\n}\n\ntype backupConfigDo struct{ gen.DO }\n\ntype IBackupConfigDo interface {\n\tgen.SubQuery\n\tDebug() IBackupConfigDo\n\tWithContext(ctx context.Context) IBackupConfigDo\n\tWithResult(fc func(tx gen.Dao)) gen.ResultInfo\n\tReplaceDB(db *gorm.DB)\n\tReadDB() IBackupConfigDo\n\tWriteDB() IBackupConfigDo\n\tAs(alias string) gen.Dao\n\tSession(config *gorm.Session) IBackupConfigDo\n\tColumns(cols ...field.Expr) gen.Columns\n\tClauses(conds ...clause.Expression) IBackupConfigDo\n\tNot(conds ...gen.Condition) IBackupConfigDo\n\tOr(conds ...gen.Condition) IBackupConfigDo\n\tSelect(conds ...field.Expr) IBackupConfigDo\n\tWhere(conds ...gen.Condition) IBackupConfigDo\n\tOrder(conds ...field.Expr) IBackupConfigDo\n\tDistinct(cols ...field.Expr) IBackupConfigDo\n\tOmit(cols ...field.Expr) IBackupConfigDo\n\tJoin(table schema.Tabler, on ...field.Expr) IBackupConfigDo\n\tLeftJoin(table schema.Tabler, on ...field.Expr) IBackupConfigDo\n\tRightJoin(table schema.Tabler, on ...field.Expr) IBackupConfigDo\n\tGroup(cols ...field.Expr) IBackupConfigDo\n\tHaving(conds ...gen.Condition) IBackupConfigDo\n\tLimit(limit int) IBackupConfigDo\n\tOffset(offset int) IBackupConfigDo\n\tCount() (count int64, err error)\n\tScopes(funcs ...func(gen.Dao) gen.Dao) IBackupConfigDo\n\tUnscoped() IBackupConfigDo\n\tCreate(values ...*model.BackupConfig) error\n\tCreateInBatches(values []*model.BackupConfig, batchSize int) error\n\tSave(values ...*model.BackupConfig) error\n\tFirst() (*model.BackupConfig, error)\n\tTake() (*model.BackupConfig, error)\n\tLast() (*model.BackupConfig, error)\n\tFind() ([]*model.BackupConfig, error)\n\tFindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.BackupConfig, err error)\n\tFindInBatches(result *[]*model.BackupConfig, batchSize int, fc func(tx gen.Dao, batch int) error) error\n\tPluck(column field.Expr, dest interface{}) error\n\tDelete(...*model.BackupConfig) (info gen.ResultInfo, err error)\n\tUpdate(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdates(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdateColumns(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateFrom(q gen.SubQuery) gen.Dao\n\tAttrs(attrs ...field.AssignExpr) IBackupConfigDo\n\tAssign(attrs ...field.AssignExpr) IBackupConfigDo\n\tJoins(fields ...field.RelationField) IBackupConfigDo\n\tPreload(fields ...field.RelationField) IBackupConfigDo\n\tFirstOrInit() (*model.BackupConfig, error)\n\tFirstOrCreate() (*model.BackupConfig, error)\n\tFindByPage(offset int, limit int) (result []*model.BackupConfig, count int64, err error)\n\tScanByPage(result interface{}, offset int, limit int) (count int64, err error)\n\tRows() (*sql.Rows, error)\n\tRow() *sql.Row\n\tScan(result interface{}) (err error)\n\tReturning(value interface{}, columns ...string) IBackupConfigDo\n\tUnderlyingDB() *gorm.DB\n\tschema.Tabler\n}\n\nfunc (b backupConfigDo) Debug() IBackupConfigDo {\n\treturn b.withDO(b.DO.Debug())\n}\n\nfunc (b backupConfigDo) WithContext(ctx context.Context) IBackupConfigDo {\n\treturn b.withDO(b.DO.WithContext(ctx))\n}\n\nfunc (b backupConfigDo) ReadDB() IBackupConfigDo {\n\treturn b.Clauses(dbresolver.Read)\n}\n\nfunc (b backupConfigDo) WriteDB() IBackupConfigDo {\n\treturn b.Clauses(dbresolver.Write)\n}\n\nfunc (b backupConfigDo) Session(config *gorm.Session) IBackupConfigDo {\n\treturn b.withDO(b.DO.Session(config))\n}\n\nfunc (b backupConfigDo) Clauses(conds ...clause.Expression) IBackupConfigDo {\n\treturn b.withDO(b.DO.Clauses(conds...))\n}\n\nfunc (b backupConfigDo) Returning(value interface{}, columns ...string) IBackupConfigDo {\n\treturn b.withDO(b.DO.Returning(value, columns...))\n}\n\nfunc (b backupConfigDo) Not(conds ...gen.Condition) IBackupConfigDo {\n\treturn b.withDO(b.DO.Not(conds...))\n}\n\nfunc (b backupConfigDo) Or(conds ...gen.Condition) IBackupConfigDo {\n\treturn b.withDO(b.DO.Or(conds...))\n}\n\nfunc (b backupConfigDo) Select(conds ...field.Expr) IBackupConfigDo {\n\treturn b.withDO(b.DO.Select(conds...))\n}\n\nfunc (b backupConfigDo) Where(conds ...gen.Condition) IBackupConfigDo {\n\treturn b.withDO(b.DO.Where(conds...))\n}\n\nfunc (b backupConfigDo) Order(conds ...field.Expr) IBackupConfigDo {\n\treturn b.withDO(b.DO.Order(conds...))\n}\n\nfunc (b backupConfigDo) Distinct(cols ...field.Expr) IBackupConfigDo {\n\treturn b.withDO(b.DO.Distinct(cols...))\n}\n\nfunc (b backupConfigDo) Omit(cols ...field.Expr) IBackupConfigDo {\n\treturn b.withDO(b.DO.Omit(cols...))\n}\n\nfunc (b backupConfigDo) Join(table schema.Tabler, on ...field.Expr) IBackupConfigDo {\n\treturn b.withDO(b.DO.Join(table, on...))\n}\n\nfunc (b backupConfigDo) LeftJoin(table schema.Tabler, on ...field.Expr) IBackupConfigDo {\n\treturn b.withDO(b.DO.LeftJoin(table, on...))\n}\n\nfunc (b backupConfigDo) RightJoin(table schema.Tabler, on ...field.Expr) IBackupConfigDo {\n\treturn b.withDO(b.DO.RightJoin(table, on...))\n}\n\nfunc (b backupConfigDo) Group(cols ...field.Expr) IBackupConfigDo {\n\treturn b.withDO(b.DO.Group(cols...))\n}\n\nfunc (b backupConfigDo) Having(conds ...gen.Condition) IBackupConfigDo {\n\treturn b.withDO(b.DO.Having(conds...))\n}\n\nfunc (b backupConfigDo) Limit(limit int) IBackupConfigDo {\n\treturn b.withDO(b.DO.Limit(limit))\n}\n\nfunc (b backupConfigDo) Offset(offset int) IBackupConfigDo {\n\treturn b.withDO(b.DO.Offset(offset))\n}\n\nfunc (b backupConfigDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IBackupConfigDo {\n\treturn b.withDO(b.DO.Scopes(funcs...))\n}\n\nfunc (b backupConfigDo) Unscoped() IBackupConfigDo {\n\treturn b.withDO(b.DO.Unscoped())\n}\n\nfunc (b backupConfigDo) Create(values ...*model.BackupConfig) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn b.DO.Create(values)\n}\n\nfunc (b backupConfigDo) CreateInBatches(values []*model.BackupConfig, batchSize int) error {\n\treturn b.DO.CreateInBatches(values, batchSize)\n}\n\n// Save : !!! underlying implementation is different with GORM\n// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)\nfunc (b backupConfigDo) Save(values ...*model.BackupConfig) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn b.DO.Save(values)\n}\n\nfunc (b backupConfigDo) First() (*model.BackupConfig, error) {\n\tif result, err := b.DO.First(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.BackupConfig), nil\n\t}\n}\n\nfunc (b backupConfigDo) Take() (*model.BackupConfig, error) {\n\tif result, err := b.DO.Take(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.BackupConfig), nil\n\t}\n}\n\nfunc (b backupConfigDo) Last() (*model.BackupConfig, error) {\n\tif result, err := b.DO.Last(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.BackupConfig), nil\n\t}\n}\n\nfunc (b backupConfigDo) Find() ([]*model.BackupConfig, error) {\n\tresult, err := b.DO.Find()\n\treturn result.([]*model.BackupConfig), err\n}\n\nfunc (b backupConfigDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.BackupConfig, err error) {\n\tbuf := make([]*model.BackupConfig, 0, batchSize)\n\terr = b.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {\n\t\tdefer func() { results = append(results, buf...) }()\n\t\treturn fc(tx, batch)\n\t})\n\treturn results, err\n}\n\nfunc (b backupConfigDo) FindInBatches(result *[]*model.BackupConfig, batchSize int, fc func(tx gen.Dao, batch int) error) error {\n\treturn b.DO.FindInBatches(result, batchSize, fc)\n}\n\nfunc (b backupConfigDo) Attrs(attrs ...field.AssignExpr) IBackupConfigDo {\n\treturn b.withDO(b.DO.Attrs(attrs...))\n}\n\nfunc (b backupConfigDo) Assign(attrs ...field.AssignExpr) IBackupConfigDo {\n\treturn b.withDO(b.DO.Assign(attrs...))\n}\n\nfunc (b backupConfigDo) Joins(fields ...field.RelationField) IBackupConfigDo {\n\tfor _, _f := range fields {\n\t\tb = *b.withDO(b.DO.Joins(_f))\n\t}\n\treturn &b\n}\n\nfunc (b backupConfigDo) Preload(fields ...field.RelationField) IBackupConfigDo {\n\tfor _, _f := range fields {\n\t\tb = *b.withDO(b.DO.Preload(_f))\n\t}\n\treturn &b\n}\n\nfunc (b backupConfigDo) FirstOrInit() (*model.BackupConfig, error) {\n\tif result, err := b.DO.FirstOrInit(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.BackupConfig), nil\n\t}\n}\n\nfunc (b backupConfigDo) FirstOrCreate() (*model.BackupConfig, error) {\n\tif result, err := b.DO.FirstOrCreate(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.BackupConfig), nil\n\t}\n}\n\nfunc (b backupConfigDo) FindByPage(offset int, limit int) (result []*model.BackupConfig, count int64, err error) {\n\tresult, err = b.Offset(offset).Limit(limit).Find()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif size := len(result); 0 < limit && 0 < size && size < limit {\n\t\tcount = int64(size + offset)\n\t\treturn\n\t}\n\n\tcount, err = b.Offset(-1).Limit(-1).Count()\n\treturn\n}\n\nfunc (b backupConfigDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {\n\tcount, err = b.Count()\n\tif err != nil {\n\t\treturn\n\t}\n\n\terr = b.Offset(offset).Limit(limit).Scan(result)\n\treturn\n}\n\nfunc (b backupConfigDo) Scan(result interface{}) (err error) {\n\treturn b.DO.Scan(result)\n}\n\nfunc (b backupConfigDo) Delete(models ...*model.BackupConfig) (result gen.ResultInfo, err error) {\n\treturn b.DO.Delete(models)\n}\n\nfunc (b *backupConfigDo) withDO(do gen.Dao) *backupConfigDo {\n\tb.DO = *do.(*gen.DO)\n\treturn b\n}\n"
  },
  {
    "path": "internal/query/backup_history.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage query\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\n\t\"gorm.io/gen\"\n\t\"gorm.io/gen/field\"\n\n\t\"gorm.io/plugin/dbresolver\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n)\n\nfunc newBackupHistory(db *gorm.DB, opts ...gen.DOOption) backupHistory {\n\t_backupHistory := backupHistory{}\n\n\t_backupHistory.backupHistoryDo.UseDB(db, opts...)\n\t_backupHistory.backupHistoryDo.UseModel(&model.BackupHistory{})\n\n\ttableName := _backupHistory.backupHistoryDo.TableName()\n\t_backupHistory.ALL = field.NewAsterisk(tableName)\n\t_backupHistory.ID = field.NewInt64(tableName, \"id\")\n\t_backupHistory.UID = field.NewInt64(tableName, \"uid\")\n\t_backupHistory.ConfigID = field.NewInt64(tableName, \"config_id\")\n\t_backupHistory.StorageID = field.NewInt64(tableName, \"storage_id\")\n\t_backupHistory.Type = field.NewString(tableName, \"type\")\n\t_backupHistory.StartTime = field.NewTime(tableName, \"start_time\")\n\t_backupHistory.EndTime = field.NewTime(tableName, \"end_time\")\n\t_backupHistory.Status = field.NewInt64(tableName, \"status\")\n\t_backupHistory.FileSize = field.NewInt64(tableName, \"file_size\")\n\t_backupHistory.FileCount = field.NewInt64(tableName, \"file_count\")\n\t_backupHistory.Message = field.NewString(tableName, \"message\")\n\t_backupHistory.FilePath = field.NewString(tableName, \"file_path\")\n\t_backupHistory.CreatedAt = field.NewField(tableName, \"created_at\")\n\t_backupHistory.UpdatedAt = field.NewField(tableName, \"updated_at\")\n\n\t_backupHistory.fillFieldMap()\n\n\treturn _backupHistory\n}\n\ntype backupHistory struct {\n\tbackupHistoryDo backupHistoryDo\n\n\tALL       field.Asterisk\n\tID        field.Int64\n\tUID       field.Int64\n\tConfigID  field.Int64\n\tStorageID field.Int64\n\tType      field.String\n\tStartTime field.Time\n\tEndTime   field.Time\n\tStatus    field.Int64\n\tFileSize  field.Int64\n\tFileCount field.Int64\n\tMessage   field.String\n\tFilePath  field.String\n\tCreatedAt field.Field\n\tUpdatedAt field.Field\n\n\tfieldMap map[string]field.Expr\n}\n\nfunc (b backupHistory) Table(newTableName string) *backupHistory {\n\tb.backupHistoryDo.UseTable(newTableName)\n\treturn b.updateTableName(newTableName)\n}\n\nfunc (b backupHistory) As(alias string) *backupHistory {\n\tb.backupHistoryDo.DO = *(b.backupHistoryDo.As(alias).(*gen.DO))\n\treturn b.updateTableName(alias)\n}\n\nfunc (b *backupHistory) updateTableName(table string) *backupHistory {\n\tb.ALL = field.NewAsterisk(table)\n\tb.ID = field.NewInt64(table, \"id\")\n\tb.UID = field.NewInt64(table, \"uid\")\n\tb.ConfigID = field.NewInt64(table, \"config_id\")\n\tb.StorageID = field.NewInt64(table, \"storage_id\")\n\tb.Type = field.NewString(table, \"type\")\n\tb.StartTime = field.NewTime(table, \"start_time\")\n\tb.EndTime = field.NewTime(table, \"end_time\")\n\tb.Status = field.NewInt64(table, \"status\")\n\tb.FileSize = field.NewInt64(table, \"file_size\")\n\tb.FileCount = field.NewInt64(table, \"file_count\")\n\tb.Message = field.NewString(table, \"message\")\n\tb.FilePath = field.NewString(table, \"file_path\")\n\tb.CreatedAt = field.NewField(table, \"created_at\")\n\tb.UpdatedAt = field.NewField(table, \"updated_at\")\n\n\tb.fillFieldMap()\n\n\treturn b\n}\n\nfunc (b *backupHistory) WithContext(ctx context.Context) IBackupHistoryDo {\n\treturn b.backupHistoryDo.WithContext(ctx)\n}\n\nfunc (b backupHistory) TableName() string { return b.backupHistoryDo.TableName() }\n\nfunc (b backupHistory) Alias() string { return b.backupHistoryDo.Alias() }\n\nfunc (b backupHistory) Columns(cols ...field.Expr) gen.Columns {\n\treturn b.backupHistoryDo.Columns(cols...)\n}\n\nfunc (b *backupHistory) GetFieldByName(fieldName string) (field.OrderExpr, bool) {\n\t_f, ok := b.fieldMap[fieldName]\n\tif !ok || _f == nil {\n\t\treturn nil, false\n\t}\n\t_oe, ok := _f.(field.OrderExpr)\n\treturn _oe, ok\n}\n\nfunc (b *backupHistory) fillFieldMap() {\n\tb.fieldMap = make(map[string]field.Expr, 14)\n\tb.fieldMap[\"id\"] = b.ID\n\tb.fieldMap[\"uid\"] = b.UID\n\tb.fieldMap[\"config_id\"] = b.ConfigID\n\tb.fieldMap[\"storage_id\"] = b.StorageID\n\tb.fieldMap[\"type\"] = b.Type\n\tb.fieldMap[\"start_time\"] = b.StartTime\n\tb.fieldMap[\"end_time\"] = b.EndTime\n\tb.fieldMap[\"status\"] = b.Status\n\tb.fieldMap[\"file_size\"] = b.FileSize\n\tb.fieldMap[\"file_count\"] = b.FileCount\n\tb.fieldMap[\"message\"] = b.Message\n\tb.fieldMap[\"file_path\"] = b.FilePath\n\tb.fieldMap[\"created_at\"] = b.CreatedAt\n\tb.fieldMap[\"updated_at\"] = b.UpdatedAt\n}\n\nfunc (b backupHistory) clone(db *gorm.DB) backupHistory {\n\tb.backupHistoryDo.ReplaceConnPool(db.Statement.ConnPool)\n\treturn b\n}\n\nfunc (b backupHistory) replaceDB(db *gorm.DB) backupHistory {\n\tb.backupHistoryDo.ReplaceDB(db)\n\treturn b\n}\n\ntype backupHistoryDo struct{ gen.DO }\n\ntype IBackupHistoryDo interface {\n\tgen.SubQuery\n\tDebug() IBackupHistoryDo\n\tWithContext(ctx context.Context) IBackupHistoryDo\n\tWithResult(fc func(tx gen.Dao)) gen.ResultInfo\n\tReplaceDB(db *gorm.DB)\n\tReadDB() IBackupHistoryDo\n\tWriteDB() IBackupHistoryDo\n\tAs(alias string) gen.Dao\n\tSession(config *gorm.Session) IBackupHistoryDo\n\tColumns(cols ...field.Expr) gen.Columns\n\tClauses(conds ...clause.Expression) IBackupHistoryDo\n\tNot(conds ...gen.Condition) IBackupHistoryDo\n\tOr(conds ...gen.Condition) IBackupHistoryDo\n\tSelect(conds ...field.Expr) IBackupHistoryDo\n\tWhere(conds ...gen.Condition) IBackupHistoryDo\n\tOrder(conds ...field.Expr) IBackupHistoryDo\n\tDistinct(cols ...field.Expr) IBackupHistoryDo\n\tOmit(cols ...field.Expr) IBackupHistoryDo\n\tJoin(table schema.Tabler, on ...field.Expr) IBackupHistoryDo\n\tLeftJoin(table schema.Tabler, on ...field.Expr) IBackupHistoryDo\n\tRightJoin(table schema.Tabler, on ...field.Expr) IBackupHistoryDo\n\tGroup(cols ...field.Expr) IBackupHistoryDo\n\tHaving(conds ...gen.Condition) IBackupHistoryDo\n\tLimit(limit int) IBackupHistoryDo\n\tOffset(offset int) IBackupHistoryDo\n\tCount() (count int64, err error)\n\tScopes(funcs ...func(gen.Dao) gen.Dao) IBackupHistoryDo\n\tUnscoped() IBackupHistoryDo\n\tCreate(values ...*model.BackupHistory) error\n\tCreateInBatches(values []*model.BackupHistory, batchSize int) error\n\tSave(values ...*model.BackupHistory) error\n\tFirst() (*model.BackupHistory, error)\n\tTake() (*model.BackupHistory, error)\n\tLast() (*model.BackupHistory, error)\n\tFind() ([]*model.BackupHistory, error)\n\tFindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.BackupHistory, err error)\n\tFindInBatches(result *[]*model.BackupHistory, batchSize int, fc func(tx gen.Dao, batch int) error) error\n\tPluck(column field.Expr, dest interface{}) error\n\tDelete(...*model.BackupHistory) (info gen.ResultInfo, err error)\n\tUpdate(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdates(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdateColumns(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateFrom(q gen.SubQuery) gen.Dao\n\tAttrs(attrs ...field.AssignExpr) IBackupHistoryDo\n\tAssign(attrs ...field.AssignExpr) IBackupHistoryDo\n\tJoins(fields ...field.RelationField) IBackupHistoryDo\n\tPreload(fields ...field.RelationField) IBackupHistoryDo\n\tFirstOrInit() (*model.BackupHistory, error)\n\tFirstOrCreate() (*model.BackupHistory, error)\n\tFindByPage(offset int, limit int) (result []*model.BackupHistory, count int64, err error)\n\tScanByPage(result interface{}, offset int, limit int) (count int64, err error)\n\tRows() (*sql.Rows, error)\n\tRow() *sql.Row\n\tScan(result interface{}) (err error)\n\tReturning(value interface{}, columns ...string) IBackupHistoryDo\n\tUnderlyingDB() *gorm.DB\n\tschema.Tabler\n}\n\nfunc (b backupHistoryDo) Debug() IBackupHistoryDo {\n\treturn b.withDO(b.DO.Debug())\n}\n\nfunc (b backupHistoryDo) WithContext(ctx context.Context) IBackupHistoryDo {\n\treturn b.withDO(b.DO.WithContext(ctx))\n}\n\nfunc (b backupHistoryDo) ReadDB() IBackupHistoryDo {\n\treturn b.Clauses(dbresolver.Read)\n}\n\nfunc (b backupHistoryDo) WriteDB() IBackupHistoryDo {\n\treturn b.Clauses(dbresolver.Write)\n}\n\nfunc (b backupHistoryDo) Session(config *gorm.Session) IBackupHistoryDo {\n\treturn b.withDO(b.DO.Session(config))\n}\n\nfunc (b backupHistoryDo) Clauses(conds ...clause.Expression) IBackupHistoryDo {\n\treturn b.withDO(b.DO.Clauses(conds...))\n}\n\nfunc (b backupHistoryDo) Returning(value interface{}, columns ...string) IBackupHistoryDo {\n\treturn b.withDO(b.DO.Returning(value, columns...))\n}\n\nfunc (b backupHistoryDo) Not(conds ...gen.Condition) IBackupHistoryDo {\n\treturn b.withDO(b.DO.Not(conds...))\n}\n\nfunc (b backupHistoryDo) Or(conds ...gen.Condition) IBackupHistoryDo {\n\treturn b.withDO(b.DO.Or(conds...))\n}\n\nfunc (b backupHistoryDo) Select(conds ...field.Expr) IBackupHistoryDo {\n\treturn b.withDO(b.DO.Select(conds...))\n}\n\nfunc (b backupHistoryDo) Where(conds ...gen.Condition) IBackupHistoryDo {\n\treturn b.withDO(b.DO.Where(conds...))\n}\n\nfunc (b backupHistoryDo) Order(conds ...field.Expr) IBackupHistoryDo {\n\treturn b.withDO(b.DO.Order(conds...))\n}\n\nfunc (b backupHistoryDo) Distinct(cols ...field.Expr) IBackupHistoryDo {\n\treturn b.withDO(b.DO.Distinct(cols...))\n}\n\nfunc (b backupHistoryDo) Omit(cols ...field.Expr) IBackupHistoryDo {\n\treturn b.withDO(b.DO.Omit(cols...))\n}\n\nfunc (b backupHistoryDo) Join(table schema.Tabler, on ...field.Expr) IBackupHistoryDo {\n\treturn b.withDO(b.DO.Join(table, on...))\n}\n\nfunc (b backupHistoryDo) LeftJoin(table schema.Tabler, on ...field.Expr) IBackupHistoryDo {\n\treturn b.withDO(b.DO.LeftJoin(table, on...))\n}\n\nfunc (b backupHistoryDo) RightJoin(table schema.Tabler, on ...field.Expr) IBackupHistoryDo {\n\treturn b.withDO(b.DO.RightJoin(table, on...))\n}\n\nfunc (b backupHistoryDo) Group(cols ...field.Expr) IBackupHistoryDo {\n\treturn b.withDO(b.DO.Group(cols...))\n}\n\nfunc (b backupHistoryDo) Having(conds ...gen.Condition) IBackupHistoryDo {\n\treturn b.withDO(b.DO.Having(conds...))\n}\n\nfunc (b backupHistoryDo) Limit(limit int) IBackupHistoryDo {\n\treturn b.withDO(b.DO.Limit(limit))\n}\n\nfunc (b backupHistoryDo) Offset(offset int) IBackupHistoryDo {\n\treturn b.withDO(b.DO.Offset(offset))\n}\n\nfunc (b backupHistoryDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IBackupHistoryDo {\n\treturn b.withDO(b.DO.Scopes(funcs...))\n}\n\nfunc (b backupHistoryDo) Unscoped() IBackupHistoryDo {\n\treturn b.withDO(b.DO.Unscoped())\n}\n\nfunc (b backupHistoryDo) Create(values ...*model.BackupHistory) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn b.DO.Create(values)\n}\n\nfunc (b backupHistoryDo) CreateInBatches(values []*model.BackupHistory, batchSize int) error {\n\treturn b.DO.CreateInBatches(values, batchSize)\n}\n\n// Save : !!! underlying implementation is different with GORM\n// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)\nfunc (b backupHistoryDo) Save(values ...*model.BackupHistory) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn b.DO.Save(values)\n}\n\nfunc (b backupHistoryDo) First() (*model.BackupHistory, error) {\n\tif result, err := b.DO.First(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.BackupHistory), nil\n\t}\n}\n\nfunc (b backupHistoryDo) Take() (*model.BackupHistory, error) {\n\tif result, err := b.DO.Take(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.BackupHistory), nil\n\t}\n}\n\nfunc (b backupHistoryDo) Last() (*model.BackupHistory, error) {\n\tif result, err := b.DO.Last(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.BackupHistory), nil\n\t}\n}\n\nfunc (b backupHistoryDo) Find() ([]*model.BackupHistory, error) {\n\tresult, err := b.DO.Find()\n\treturn result.([]*model.BackupHistory), err\n}\n\nfunc (b backupHistoryDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.BackupHistory, err error) {\n\tbuf := make([]*model.BackupHistory, 0, batchSize)\n\terr = b.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {\n\t\tdefer func() { results = append(results, buf...) }()\n\t\treturn fc(tx, batch)\n\t})\n\treturn results, err\n}\n\nfunc (b backupHistoryDo) FindInBatches(result *[]*model.BackupHistory, batchSize int, fc func(tx gen.Dao, batch int) error) error {\n\treturn b.DO.FindInBatches(result, batchSize, fc)\n}\n\nfunc (b backupHistoryDo) Attrs(attrs ...field.AssignExpr) IBackupHistoryDo {\n\treturn b.withDO(b.DO.Attrs(attrs...))\n}\n\nfunc (b backupHistoryDo) Assign(attrs ...field.AssignExpr) IBackupHistoryDo {\n\treturn b.withDO(b.DO.Assign(attrs...))\n}\n\nfunc (b backupHistoryDo) Joins(fields ...field.RelationField) IBackupHistoryDo {\n\tfor _, _f := range fields {\n\t\tb = *b.withDO(b.DO.Joins(_f))\n\t}\n\treturn &b\n}\n\nfunc (b backupHistoryDo) Preload(fields ...field.RelationField) IBackupHistoryDo {\n\tfor _, _f := range fields {\n\t\tb = *b.withDO(b.DO.Preload(_f))\n\t}\n\treturn &b\n}\n\nfunc (b backupHistoryDo) FirstOrInit() (*model.BackupHistory, error) {\n\tif result, err := b.DO.FirstOrInit(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.BackupHistory), nil\n\t}\n}\n\nfunc (b backupHistoryDo) FirstOrCreate() (*model.BackupHistory, error) {\n\tif result, err := b.DO.FirstOrCreate(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.BackupHistory), nil\n\t}\n}\n\nfunc (b backupHistoryDo) FindByPage(offset int, limit int) (result []*model.BackupHistory, count int64, err error) {\n\tresult, err = b.Offset(offset).Limit(limit).Find()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif size := len(result); 0 < limit && 0 < size && size < limit {\n\t\tcount = int64(size + offset)\n\t\treturn\n\t}\n\n\tcount, err = b.Offset(-1).Limit(-1).Count()\n\treturn\n}\n\nfunc (b backupHistoryDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {\n\tcount, err = b.Count()\n\tif err != nil {\n\t\treturn\n\t}\n\n\terr = b.Offset(offset).Limit(limit).Scan(result)\n\treturn\n}\n\nfunc (b backupHistoryDo) Scan(result interface{}) (err error) {\n\treturn b.DO.Scan(result)\n}\n\nfunc (b backupHistoryDo) Delete(models ...*model.BackupHistory) (result gen.ResultInfo, err error) {\n\treturn b.DO.Delete(models)\n}\n\nfunc (b *backupHistoryDo) withDO(do gen.Dao) *backupHistoryDo {\n\tb.DO = *do.(*gen.DO)\n\treturn b\n}\n"
  },
  {
    "path": "internal/query/file.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage query\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\n\t\"gorm.io/gen\"\n\t\"gorm.io/gen/field\"\n\n\t\"gorm.io/plugin/dbresolver\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n)\n\nfunc newFile(db *gorm.DB, opts ...gen.DOOption) file {\n\t_file := file{}\n\n\t_file.fileDo.UseDB(db, opts...)\n\t_file.fileDo.UseModel(&model.File{})\n\n\ttableName := _file.fileDo.TableName()\n\t_file.ALL = field.NewAsterisk(tableName)\n\t_file.ID = field.NewInt64(tableName, \"id\")\n\t_file.VaultID = field.NewInt64(tableName, \"vault_id\")\n\t_file.Action = field.NewString(tableName, \"action\")\n\t_file.FID = field.NewInt64(tableName, \"fid\")\n\t_file.Path = field.NewString(tableName, \"path\")\n\t_file.PathHash = field.NewString(tableName, \"path_hash\")\n\t_file.ContentHash = field.NewString(tableName, \"content_hash\")\n\t_file.SavePath = field.NewString(tableName, \"save_path\")\n\t_file.Rename = field.NewInt64(tableName, \"rename\")\n\t_file.Size = field.NewInt64(tableName, \"size\")\n\t_file.Ctime = field.NewInt64(tableName, \"ctime\")\n\t_file.Mtime = field.NewInt64(tableName, \"mtime\")\n\t_file.UpdatedTimestamp = field.NewInt64(tableName, \"updated_timestamp\")\n\t_file.CreatedAt = field.NewField(tableName, \"created_at\")\n\t_file.UpdatedAt = field.NewField(tableName, \"updated_at\")\n\n\t_file.fillFieldMap()\n\n\treturn _file\n}\n\ntype file struct {\n\tfileDo fileDo\n\n\tALL              field.Asterisk\n\tID               field.Int64\n\tVaultID          field.Int64\n\tAction           field.String\n\tFID              field.Int64\n\tPath             field.String\n\tPathHash         field.String\n\tContentHash      field.String\n\tSavePath         field.String\n\tRename           field.Int64\n\tSize             field.Int64\n\tCtime            field.Int64\n\tMtime            field.Int64\n\tUpdatedTimestamp field.Int64\n\tCreatedAt        field.Field\n\tUpdatedAt        field.Field\n\n\tfieldMap map[string]field.Expr\n}\n\nfunc (f file) Table(newTableName string) *file {\n\tf.fileDo.UseTable(newTableName)\n\treturn f.updateTableName(newTableName)\n}\n\nfunc (f file) As(alias string) *file {\n\tf.fileDo.DO = *(f.fileDo.As(alias).(*gen.DO))\n\treturn f.updateTableName(alias)\n}\n\nfunc (f *file) updateTableName(table string) *file {\n\tf.ALL = field.NewAsterisk(table)\n\tf.ID = field.NewInt64(table, \"id\")\n\tf.VaultID = field.NewInt64(table, \"vault_id\")\n\tf.Action = field.NewString(table, \"action\")\n\tf.FID = field.NewInt64(table, \"fid\")\n\tf.Path = field.NewString(table, \"path\")\n\tf.PathHash = field.NewString(table, \"path_hash\")\n\tf.ContentHash = field.NewString(table, \"content_hash\")\n\tf.SavePath = field.NewString(table, \"save_path\")\n\tf.Rename = field.NewInt64(table, \"rename\")\n\tf.Size = field.NewInt64(table, \"size\")\n\tf.Ctime = field.NewInt64(table, \"ctime\")\n\tf.Mtime = field.NewInt64(table, \"mtime\")\n\tf.UpdatedTimestamp = field.NewInt64(table, \"updated_timestamp\")\n\tf.CreatedAt = field.NewField(table, \"created_at\")\n\tf.UpdatedAt = field.NewField(table, \"updated_at\")\n\n\tf.fillFieldMap()\n\n\treturn f\n}\n\nfunc (f *file) WithContext(ctx context.Context) IFileDo { return f.fileDo.WithContext(ctx) }\n\nfunc (f file) TableName() string { return f.fileDo.TableName() }\n\nfunc (f file) Alias() string { return f.fileDo.Alias() }\n\nfunc (f file) Columns(cols ...field.Expr) gen.Columns { return f.fileDo.Columns(cols...) }\n\nfunc (f *file) GetFieldByName(fieldName string) (field.OrderExpr, bool) {\n\t_f, ok := f.fieldMap[fieldName]\n\tif !ok || _f == nil {\n\t\treturn nil, false\n\t}\n\t_oe, ok := _f.(field.OrderExpr)\n\treturn _oe, ok\n}\n\nfunc (f *file) fillFieldMap() {\n\tf.fieldMap = make(map[string]field.Expr, 15)\n\tf.fieldMap[\"id\"] = f.ID\n\tf.fieldMap[\"vault_id\"] = f.VaultID\n\tf.fieldMap[\"action\"] = f.Action\n\tf.fieldMap[\"fid\"] = f.FID\n\tf.fieldMap[\"path\"] = f.Path\n\tf.fieldMap[\"path_hash\"] = f.PathHash\n\tf.fieldMap[\"content_hash\"] = f.ContentHash\n\tf.fieldMap[\"save_path\"] = f.SavePath\n\tf.fieldMap[\"rename\"] = f.Rename\n\tf.fieldMap[\"size\"] = f.Size\n\tf.fieldMap[\"ctime\"] = f.Ctime\n\tf.fieldMap[\"mtime\"] = f.Mtime\n\tf.fieldMap[\"updated_timestamp\"] = f.UpdatedTimestamp\n\tf.fieldMap[\"created_at\"] = f.CreatedAt\n\tf.fieldMap[\"updated_at\"] = f.UpdatedAt\n}\n\nfunc (f file) clone(db *gorm.DB) file {\n\tf.fileDo.ReplaceConnPool(db.Statement.ConnPool)\n\treturn f\n}\n\nfunc (f file) replaceDB(db *gorm.DB) file {\n\tf.fileDo.ReplaceDB(db)\n\treturn f\n}\n\ntype fileDo struct{ gen.DO }\n\ntype IFileDo interface {\n\tgen.SubQuery\n\tDebug() IFileDo\n\tWithContext(ctx context.Context) IFileDo\n\tWithResult(fc func(tx gen.Dao)) gen.ResultInfo\n\tReplaceDB(db *gorm.DB)\n\tReadDB() IFileDo\n\tWriteDB() IFileDo\n\tAs(alias string) gen.Dao\n\tSession(config *gorm.Session) IFileDo\n\tColumns(cols ...field.Expr) gen.Columns\n\tClauses(conds ...clause.Expression) IFileDo\n\tNot(conds ...gen.Condition) IFileDo\n\tOr(conds ...gen.Condition) IFileDo\n\tSelect(conds ...field.Expr) IFileDo\n\tWhere(conds ...gen.Condition) IFileDo\n\tOrder(conds ...field.Expr) IFileDo\n\tDistinct(cols ...field.Expr) IFileDo\n\tOmit(cols ...field.Expr) IFileDo\n\tJoin(table schema.Tabler, on ...field.Expr) IFileDo\n\tLeftJoin(table schema.Tabler, on ...field.Expr) IFileDo\n\tRightJoin(table schema.Tabler, on ...field.Expr) IFileDo\n\tGroup(cols ...field.Expr) IFileDo\n\tHaving(conds ...gen.Condition) IFileDo\n\tLimit(limit int) IFileDo\n\tOffset(offset int) IFileDo\n\tCount() (count int64, err error)\n\tScopes(funcs ...func(gen.Dao) gen.Dao) IFileDo\n\tUnscoped() IFileDo\n\tCreate(values ...*model.File) error\n\tCreateInBatches(values []*model.File, batchSize int) error\n\tSave(values ...*model.File) error\n\tFirst() (*model.File, error)\n\tTake() (*model.File, error)\n\tLast() (*model.File, error)\n\tFind() ([]*model.File, error)\n\tFindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.File, err error)\n\tFindInBatches(result *[]*model.File, batchSize int, fc func(tx gen.Dao, batch int) error) error\n\tPluck(column field.Expr, dest interface{}) error\n\tDelete(...*model.File) (info gen.ResultInfo, err error)\n\tUpdate(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdates(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdateColumns(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateFrom(q gen.SubQuery) gen.Dao\n\tAttrs(attrs ...field.AssignExpr) IFileDo\n\tAssign(attrs ...field.AssignExpr) IFileDo\n\tJoins(fields ...field.RelationField) IFileDo\n\tPreload(fields ...field.RelationField) IFileDo\n\tFirstOrInit() (*model.File, error)\n\tFirstOrCreate() (*model.File, error)\n\tFindByPage(offset int, limit int) (result []*model.File, count int64, err error)\n\tScanByPage(result interface{}, offset int, limit int) (count int64, err error)\n\tRows() (*sql.Rows, error)\n\tRow() *sql.Row\n\tScan(result interface{}) (err error)\n\tReturning(value interface{}, columns ...string) IFileDo\n\tUnderlyingDB() *gorm.DB\n\tschema.Tabler\n}\n\nfunc (f fileDo) Debug() IFileDo {\n\treturn f.withDO(f.DO.Debug())\n}\n\nfunc (f fileDo) WithContext(ctx context.Context) IFileDo {\n\treturn f.withDO(f.DO.WithContext(ctx))\n}\n\nfunc (f fileDo) ReadDB() IFileDo {\n\treturn f.Clauses(dbresolver.Read)\n}\n\nfunc (f fileDo) WriteDB() IFileDo {\n\treturn f.Clauses(dbresolver.Write)\n}\n\nfunc (f fileDo) Session(config *gorm.Session) IFileDo {\n\treturn f.withDO(f.DO.Session(config))\n}\n\nfunc (f fileDo) Clauses(conds ...clause.Expression) IFileDo {\n\treturn f.withDO(f.DO.Clauses(conds...))\n}\n\nfunc (f fileDo) Returning(value interface{}, columns ...string) IFileDo {\n\treturn f.withDO(f.DO.Returning(value, columns...))\n}\n\nfunc (f fileDo) Not(conds ...gen.Condition) IFileDo {\n\treturn f.withDO(f.DO.Not(conds...))\n}\n\nfunc (f fileDo) Or(conds ...gen.Condition) IFileDo {\n\treturn f.withDO(f.DO.Or(conds...))\n}\n\nfunc (f fileDo) Select(conds ...field.Expr) IFileDo {\n\treturn f.withDO(f.DO.Select(conds...))\n}\n\nfunc (f fileDo) Where(conds ...gen.Condition) IFileDo {\n\treturn f.withDO(f.DO.Where(conds...))\n}\n\nfunc (f fileDo) Order(conds ...field.Expr) IFileDo {\n\treturn f.withDO(f.DO.Order(conds...))\n}\n\nfunc (f fileDo) Distinct(cols ...field.Expr) IFileDo {\n\treturn f.withDO(f.DO.Distinct(cols...))\n}\n\nfunc (f fileDo) Omit(cols ...field.Expr) IFileDo {\n\treturn f.withDO(f.DO.Omit(cols...))\n}\n\nfunc (f fileDo) Join(table schema.Tabler, on ...field.Expr) IFileDo {\n\treturn f.withDO(f.DO.Join(table, on...))\n}\n\nfunc (f fileDo) LeftJoin(table schema.Tabler, on ...field.Expr) IFileDo {\n\treturn f.withDO(f.DO.LeftJoin(table, on...))\n}\n\nfunc (f fileDo) RightJoin(table schema.Tabler, on ...field.Expr) IFileDo {\n\treturn f.withDO(f.DO.RightJoin(table, on...))\n}\n\nfunc (f fileDo) Group(cols ...field.Expr) IFileDo {\n\treturn f.withDO(f.DO.Group(cols...))\n}\n\nfunc (f fileDo) Having(conds ...gen.Condition) IFileDo {\n\treturn f.withDO(f.DO.Having(conds...))\n}\n\nfunc (f fileDo) Limit(limit int) IFileDo {\n\treturn f.withDO(f.DO.Limit(limit))\n}\n\nfunc (f fileDo) Offset(offset int) IFileDo {\n\treturn f.withDO(f.DO.Offset(offset))\n}\n\nfunc (f fileDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IFileDo {\n\treturn f.withDO(f.DO.Scopes(funcs...))\n}\n\nfunc (f fileDo) Unscoped() IFileDo {\n\treturn f.withDO(f.DO.Unscoped())\n}\n\nfunc (f fileDo) Create(values ...*model.File) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn f.DO.Create(values)\n}\n\nfunc (f fileDo) CreateInBatches(values []*model.File, batchSize int) error {\n\treturn f.DO.CreateInBatches(values, batchSize)\n}\n\n// Save : !!! underlying implementation is different with GORM\n// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)\nfunc (f fileDo) Save(values ...*model.File) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn f.DO.Save(values)\n}\n\nfunc (f fileDo) First() (*model.File, error) {\n\tif result, err := f.DO.First(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.File), nil\n\t}\n}\n\nfunc (f fileDo) Take() (*model.File, error) {\n\tif result, err := f.DO.Take(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.File), nil\n\t}\n}\n\nfunc (f fileDo) Last() (*model.File, error) {\n\tif result, err := f.DO.Last(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.File), nil\n\t}\n}\n\nfunc (f fileDo) Find() ([]*model.File, error) {\n\tresult, err := f.DO.Find()\n\treturn result.([]*model.File), err\n}\n\nfunc (f fileDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.File, err error) {\n\tbuf := make([]*model.File, 0, batchSize)\n\terr = f.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {\n\t\tdefer func() { results = append(results, buf...) }()\n\t\treturn fc(tx, batch)\n\t})\n\treturn results, err\n}\n\nfunc (f fileDo) FindInBatches(result *[]*model.File, batchSize int, fc func(tx gen.Dao, batch int) error) error {\n\treturn f.DO.FindInBatches(result, batchSize, fc)\n}\n\nfunc (f fileDo) Attrs(attrs ...field.AssignExpr) IFileDo {\n\treturn f.withDO(f.DO.Attrs(attrs...))\n}\n\nfunc (f fileDo) Assign(attrs ...field.AssignExpr) IFileDo {\n\treturn f.withDO(f.DO.Assign(attrs...))\n}\n\nfunc (f fileDo) Joins(fields ...field.RelationField) IFileDo {\n\tfor _, _f := range fields {\n\t\tf = *f.withDO(f.DO.Joins(_f))\n\t}\n\treturn &f\n}\n\nfunc (f fileDo) Preload(fields ...field.RelationField) IFileDo {\n\tfor _, _f := range fields {\n\t\tf = *f.withDO(f.DO.Preload(_f))\n\t}\n\treturn &f\n}\n\nfunc (f fileDo) FirstOrInit() (*model.File, error) {\n\tif result, err := f.DO.FirstOrInit(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.File), nil\n\t}\n}\n\nfunc (f fileDo) FirstOrCreate() (*model.File, error) {\n\tif result, err := f.DO.FirstOrCreate(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.File), nil\n\t}\n}\n\nfunc (f fileDo) FindByPage(offset int, limit int) (result []*model.File, count int64, err error) {\n\tresult, err = f.Offset(offset).Limit(limit).Find()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif size := len(result); 0 < limit && 0 < size && size < limit {\n\t\tcount = int64(size + offset)\n\t\treturn\n\t}\n\n\tcount, err = f.Offset(-1).Limit(-1).Count()\n\treturn\n}\n\nfunc (f fileDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {\n\tcount, err = f.Count()\n\tif err != nil {\n\t\treturn\n\t}\n\n\terr = f.Offset(offset).Limit(limit).Scan(result)\n\treturn\n}\n\nfunc (f fileDo) Scan(result interface{}) (err error) {\n\treturn f.DO.Scan(result)\n}\n\nfunc (f fileDo) Delete(models ...*model.File) (result gen.ResultInfo, err error) {\n\treturn f.DO.Delete(models)\n}\n\nfunc (f *fileDo) withDO(do gen.Dao) *fileDo {\n\tf.DO = *do.(*gen.DO)\n\treturn f\n}\n"
  },
  {
    "path": "internal/query/folder.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage query\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\n\t\"gorm.io/gen\"\n\t\"gorm.io/gen/field\"\n\n\t\"gorm.io/plugin/dbresolver\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n)\n\nfunc newFolder(db *gorm.DB, opts ...gen.DOOption) folder {\n\t_folder := folder{}\n\n\t_folder.folderDo.UseDB(db, opts...)\n\t_folder.folderDo.UseModel(&model.Folder{})\n\n\ttableName := _folder.folderDo.TableName()\n\t_folder.ALL = field.NewAsterisk(tableName)\n\t_folder.ID = field.NewInt64(tableName, \"id\")\n\t_folder.VaultID = field.NewInt64(tableName, \"vault_id\")\n\t_folder.Action = field.NewString(tableName, \"action\")\n\t_folder.Path = field.NewString(tableName, \"path\")\n\t_folder.PathHash = field.NewString(tableName, \"path_hash\")\n\t_folder.Level = field.NewInt64(tableName, \"level\")\n\t_folder.FID = field.NewInt64(tableName, \"fid\")\n\t_folder.Ctime = field.NewInt64(tableName, \"ctime\")\n\t_folder.Mtime = field.NewInt64(tableName, \"mtime\")\n\t_folder.UpdatedTimestamp = field.NewInt64(tableName, \"updated_timestamp\")\n\t_folder.CreatedAt = field.NewField(tableName, \"created_at\")\n\t_folder.UpdatedAt = field.NewField(tableName, \"updated_at\")\n\n\t_folder.fillFieldMap()\n\n\treturn _folder\n}\n\ntype folder struct {\n\tfolderDo folderDo\n\n\tALL              field.Asterisk\n\tID               field.Int64\n\tVaultID          field.Int64\n\tAction           field.String\n\tPath             field.String\n\tPathHash         field.String\n\tLevel            field.Int64\n\tFID              field.Int64\n\tCtime            field.Int64\n\tMtime            field.Int64\n\tUpdatedTimestamp field.Int64\n\tCreatedAt        field.Field\n\tUpdatedAt        field.Field\n\n\tfieldMap map[string]field.Expr\n}\n\nfunc (f folder) Table(newTableName string) *folder {\n\tf.folderDo.UseTable(newTableName)\n\treturn f.updateTableName(newTableName)\n}\n\nfunc (f folder) As(alias string) *folder {\n\tf.folderDo.DO = *(f.folderDo.As(alias).(*gen.DO))\n\treturn f.updateTableName(alias)\n}\n\nfunc (f *folder) updateTableName(table string) *folder {\n\tf.ALL = field.NewAsterisk(table)\n\tf.ID = field.NewInt64(table, \"id\")\n\tf.VaultID = field.NewInt64(table, \"vault_id\")\n\tf.Action = field.NewString(table, \"action\")\n\tf.Path = field.NewString(table, \"path\")\n\tf.PathHash = field.NewString(table, \"path_hash\")\n\tf.Level = field.NewInt64(table, \"level\")\n\tf.FID = field.NewInt64(table, \"fid\")\n\tf.Ctime = field.NewInt64(table, \"ctime\")\n\tf.Mtime = field.NewInt64(table, \"mtime\")\n\tf.UpdatedTimestamp = field.NewInt64(table, \"updated_timestamp\")\n\tf.CreatedAt = field.NewField(table, \"created_at\")\n\tf.UpdatedAt = field.NewField(table, \"updated_at\")\n\n\tf.fillFieldMap()\n\n\treturn f\n}\n\nfunc (f *folder) WithContext(ctx context.Context) IFolderDo { return f.folderDo.WithContext(ctx) }\n\nfunc (f folder) TableName() string { return f.folderDo.TableName() }\n\nfunc (f folder) Alias() string { return f.folderDo.Alias() }\n\nfunc (f folder) Columns(cols ...field.Expr) gen.Columns { return f.folderDo.Columns(cols...) }\n\nfunc (f *folder) GetFieldByName(fieldName string) (field.OrderExpr, bool) {\n\t_f, ok := f.fieldMap[fieldName]\n\tif !ok || _f == nil {\n\t\treturn nil, false\n\t}\n\t_oe, ok := _f.(field.OrderExpr)\n\treturn _oe, ok\n}\n\nfunc (f *folder) fillFieldMap() {\n\tf.fieldMap = make(map[string]field.Expr, 12)\n\tf.fieldMap[\"id\"] = f.ID\n\tf.fieldMap[\"vault_id\"] = f.VaultID\n\tf.fieldMap[\"action\"] = f.Action\n\tf.fieldMap[\"path\"] = f.Path\n\tf.fieldMap[\"path_hash\"] = f.PathHash\n\tf.fieldMap[\"level\"] = f.Level\n\tf.fieldMap[\"fid\"] = f.FID\n\tf.fieldMap[\"ctime\"] = f.Ctime\n\tf.fieldMap[\"mtime\"] = f.Mtime\n\tf.fieldMap[\"updated_timestamp\"] = f.UpdatedTimestamp\n\tf.fieldMap[\"created_at\"] = f.CreatedAt\n\tf.fieldMap[\"updated_at\"] = f.UpdatedAt\n}\n\nfunc (f folder) clone(db *gorm.DB) folder {\n\tf.folderDo.ReplaceConnPool(db.Statement.ConnPool)\n\treturn f\n}\n\nfunc (f folder) replaceDB(db *gorm.DB) folder {\n\tf.folderDo.ReplaceDB(db)\n\treturn f\n}\n\ntype folderDo struct{ gen.DO }\n\ntype IFolderDo interface {\n\tgen.SubQuery\n\tDebug() IFolderDo\n\tWithContext(ctx context.Context) IFolderDo\n\tWithResult(fc func(tx gen.Dao)) gen.ResultInfo\n\tReplaceDB(db *gorm.DB)\n\tReadDB() IFolderDo\n\tWriteDB() IFolderDo\n\tAs(alias string) gen.Dao\n\tSession(config *gorm.Session) IFolderDo\n\tColumns(cols ...field.Expr) gen.Columns\n\tClauses(conds ...clause.Expression) IFolderDo\n\tNot(conds ...gen.Condition) IFolderDo\n\tOr(conds ...gen.Condition) IFolderDo\n\tSelect(conds ...field.Expr) IFolderDo\n\tWhere(conds ...gen.Condition) IFolderDo\n\tOrder(conds ...field.Expr) IFolderDo\n\tDistinct(cols ...field.Expr) IFolderDo\n\tOmit(cols ...field.Expr) IFolderDo\n\tJoin(table schema.Tabler, on ...field.Expr) IFolderDo\n\tLeftJoin(table schema.Tabler, on ...field.Expr) IFolderDo\n\tRightJoin(table schema.Tabler, on ...field.Expr) IFolderDo\n\tGroup(cols ...field.Expr) IFolderDo\n\tHaving(conds ...gen.Condition) IFolderDo\n\tLimit(limit int) IFolderDo\n\tOffset(offset int) IFolderDo\n\tCount() (count int64, err error)\n\tScopes(funcs ...func(gen.Dao) gen.Dao) IFolderDo\n\tUnscoped() IFolderDo\n\tCreate(values ...*model.Folder) error\n\tCreateInBatches(values []*model.Folder, batchSize int) error\n\tSave(values ...*model.Folder) error\n\tFirst() (*model.Folder, error)\n\tTake() (*model.Folder, error)\n\tLast() (*model.Folder, error)\n\tFind() ([]*model.Folder, error)\n\tFindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Folder, err error)\n\tFindInBatches(result *[]*model.Folder, batchSize int, fc func(tx gen.Dao, batch int) error) error\n\tPluck(column field.Expr, dest interface{}) error\n\tDelete(...*model.Folder) (info gen.ResultInfo, err error)\n\tUpdate(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdates(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdateColumns(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateFrom(q gen.SubQuery) gen.Dao\n\tAttrs(attrs ...field.AssignExpr) IFolderDo\n\tAssign(attrs ...field.AssignExpr) IFolderDo\n\tJoins(fields ...field.RelationField) IFolderDo\n\tPreload(fields ...field.RelationField) IFolderDo\n\tFirstOrInit() (*model.Folder, error)\n\tFirstOrCreate() (*model.Folder, error)\n\tFindByPage(offset int, limit int) (result []*model.Folder, count int64, err error)\n\tScanByPage(result interface{}, offset int, limit int) (count int64, err error)\n\tRows() (*sql.Rows, error)\n\tRow() *sql.Row\n\tScan(result interface{}) (err error)\n\tReturning(value interface{}, columns ...string) IFolderDo\n\tUnderlyingDB() *gorm.DB\n\tschema.Tabler\n}\n\nfunc (f folderDo) Debug() IFolderDo {\n\treturn f.withDO(f.DO.Debug())\n}\n\nfunc (f folderDo) WithContext(ctx context.Context) IFolderDo {\n\treturn f.withDO(f.DO.WithContext(ctx))\n}\n\nfunc (f folderDo) ReadDB() IFolderDo {\n\treturn f.Clauses(dbresolver.Read)\n}\n\nfunc (f folderDo) WriteDB() IFolderDo {\n\treturn f.Clauses(dbresolver.Write)\n}\n\nfunc (f folderDo) Session(config *gorm.Session) IFolderDo {\n\treturn f.withDO(f.DO.Session(config))\n}\n\nfunc (f folderDo) Clauses(conds ...clause.Expression) IFolderDo {\n\treturn f.withDO(f.DO.Clauses(conds...))\n}\n\nfunc (f folderDo) Returning(value interface{}, columns ...string) IFolderDo {\n\treturn f.withDO(f.DO.Returning(value, columns...))\n}\n\nfunc (f folderDo) Not(conds ...gen.Condition) IFolderDo {\n\treturn f.withDO(f.DO.Not(conds...))\n}\n\nfunc (f folderDo) Or(conds ...gen.Condition) IFolderDo {\n\treturn f.withDO(f.DO.Or(conds...))\n}\n\nfunc (f folderDo) Select(conds ...field.Expr) IFolderDo {\n\treturn f.withDO(f.DO.Select(conds...))\n}\n\nfunc (f folderDo) Where(conds ...gen.Condition) IFolderDo {\n\treturn f.withDO(f.DO.Where(conds...))\n}\n\nfunc (f folderDo) Order(conds ...field.Expr) IFolderDo {\n\treturn f.withDO(f.DO.Order(conds...))\n}\n\nfunc (f folderDo) Distinct(cols ...field.Expr) IFolderDo {\n\treturn f.withDO(f.DO.Distinct(cols...))\n}\n\nfunc (f folderDo) Omit(cols ...field.Expr) IFolderDo {\n\treturn f.withDO(f.DO.Omit(cols...))\n}\n\nfunc (f folderDo) Join(table schema.Tabler, on ...field.Expr) IFolderDo {\n\treturn f.withDO(f.DO.Join(table, on...))\n}\n\nfunc (f folderDo) LeftJoin(table schema.Tabler, on ...field.Expr) IFolderDo {\n\treturn f.withDO(f.DO.LeftJoin(table, on...))\n}\n\nfunc (f folderDo) RightJoin(table schema.Tabler, on ...field.Expr) IFolderDo {\n\treturn f.withDO(f.DO.RightJoin(table, on...))\n}\n\nfunc (f folderDo) Group(cols ...field.Expr) IFolderDo {\n\treturn f.withDO(f.DO.Group(cols...))\n}\n\nfunc (f folderDo) Having(conds ...gen.Condition) IFolderDo {\n\treturn f.withDO(f.DO.Having(conds...))\n}\n\nfunc (f folderDo) Limit(limit int) IFolderDo {\n\treturn f.withDO(f.DO.Limit(limit))\n}\n\nfunc (f folderDo) Offset(offset int) IFolderDo {\n\treturn f.withDO(f.DO.Offset(offset))\n}\n\nfunc (f folderDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IFolderDo {\n\treturn f.withDO(f.DO.Scopes(funcs...))\n}\n\nfunc (f folderDo) Unscoped() IFolderDo {\n\treturn f.withDO(f.DO.Unscoped())\n}\n\nfunc (f folderDo) Create(values ...*model.Folder) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn f.DO.Create(values)\n}\n\nfunc (f folderDo) CreateInBatches(values []*model.Folder, batchSize int) error {\n\treturn f.DO.CreateInBatches(values, batchSize)\n}\n\n// Save : !!! underlying implementation is different with GORM\n// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)\nfunc (f folderDo) Save(values ...*model.Folder) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn f.DO.Save(values)\n}\n\nfunc (f folderDo) First() (*model.Folder, error) {\n\tif result, err := f.DO.First(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Folder), nil\n\t}\n}\n\nfunc (f folderDo) Take() (*model.Folder, error) {\n\tif result, err := f.DO.Take(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Folder), nil\n\t}\n}\n\nfunc (f folderDo) Last() (*model.Folder, error) {\n\tif result, err := f.DO.Last(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Folder), nil\n\t}\n}\n\nfunc (f folderDo) Find() ([]*model.Folder, error) {\n\tresult, err := f.DO.Find()\n\treturn result.([]*model.Folder), err\n}\n\nfunc (f folderDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Folder, err error) {\n\tbuf := make([]*model.Folder, 0, batchSize)\n\terr = f.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {\n\t\tdefer func() { results = append(results, buf...) }()\n\t\treturn fc(tx, batch)\n\t})\n\treturn results, err\n}\n\nfunc (f folderDo) FindInBatches(result *[]*model.Folder, batchSize int, fc func(tx gen.Dao, batch int) error) error {\n\treturn f.DO.FindInBatches(result, batchSize, fc)\n}\n\nfunc (f folderDo) Attrs(attrs ...field.AssignExpr) IFolderDo {\n\treturn f.withDO(f.DO.Attrs(attrs...))\n}\n\nfunc (f folderDo) Assign(attrs ...field.AssignExpr) IFolderDo {\n\treturn f.withDO(f.DO.Assign(attrs...))\n}\n\nfunc (f folderDo) Joins(fields ...field.RelationField) IFolderDo {\n\tfor _, _f := range fields {\n\t\tf = *f.withDO(f.DO.Joins(_f))\n\t}\n\treturn &f\n}\n\nfunc (f folderDo) Preload(fields ...field.RelationField) IFolderDo {\n\tfor _, _f := range fields {\n\t\tf = *f.withDO(f.DO.Preload(_f))\n\t}\n\treturn &f\n}\n\nfunc (f folderDo) FirstOrInit() (*model.Folder, error) {\n\tif result, err := f.DO.FirstOrInit(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Folder), nil\n\t}\n}\n\nfunc (f folderDo) FirstOrCreate() (*model.Folder, error) {\n\tif result, err := f.DO.FirstOrCreate(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Folder), nil\n\t}\n}\n\nfunc (f folderDo) FindByPage(offset int, limit int) (result []*model.Folder, count int64, err error) {\n\tresult, err = f.Offset(offset).Limit(limit).Find()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif size := len(result); 0 < limit && 0 < size && size < limit {\n\t\tcount = int64(size + offset)\n\t\treturn\n\t}\n\n\tcount, err = f.Offset(-1).Limit(-1).Count()\n\treturn\n}\n\nfunc (f folderDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {\n\tcount, err = f.Count()\n\tif err != nil {\n\t\treturn\n\t}\n\n\terr = f.Offset(offset).Limit(limit).Scan(result)\n\treturn\n}\n\nfunc (f folderDo) Scan(result interface{}) (err error) {\n\treturn f.DO.Scan(result)\n}\n\nfunc (f folderDo) Delete(models ...*model.Folder) (result gen.ResultInfo, err error) {\n\treturn f.DO.Delete(models)\n}\n\nfunc (f *folderDo) withDO(do gen.Dao) *folderDo {\n\tf.DO = *do.(*gen.DO)\n\treturn f\n}\n"
  },
  {
    "path": "internal/query/gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage query\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\n\t\"gorm.io/gorm\"\n\n\t\"gorm.io/gen\"\n\n\t\"gorm.io/plugin/dbresolver\"\n)\n\nfunc Use(db *gorm.DB, opts ...gen.DOOption) *Query {\n\treturn &Query{\n\t\tdb:             db,\n\t\tBackupConfig:   newBackupConfig(db, opts...),\n\t\tBackupHistory:  newBackupHistory(db, opts...),\n\t\tFile:           newFile(db, opts...),\n\t\tFolder:         newFolder(db, opts...),\n\t\tGitSyncConfig:  newGitSyncConfig(db, opts...),\n\t\tGitSyncHistory: newGitSyncHistory(db, opts...),\n\t\tNote:           newNote(db, opts...),\n\t\tNoteHistory:    newNoteHistory(db, opts...),\n\t\tNoteLink:       newNoteLink(db, opts...),\n\t\tSetting:        newSetting(db, opts...),\n\t\tStorage:        newStorage(db, opts...),\n\t\tUser:           newUser(db, opts...),\n\t\tUserShare:      newUserShare(db, opts...),\n\t\tVault:          newVault(db, opts...),\n\t}\n}\n\ntype Query struct {\n\tdb *gorm.DB\n\n\tBackupConfig   backupConfig\n\tBackupHistory  backupHistory\n\tFile           file\n\tFolder         folder\n\tGitSyncConfig  gitSyncConfig\n\tGitSyncHistory gitSyncHistory\n\tNote           note\n\tNoteHistory    noteHistory\n\tNoteLink       noteLink\n\tSetting        setting\n\tStorage        storage\n\tUser           user\n\tUserShare      userShare\n\tVault          vault\n}\n\nfunc (q *Query) Available() bool { return q.db != nil }\n\nfunc (q *Query) clone(db *gorm.DB) *Query {\n\treturn &Query{\n\t\tdb:             db,\n\t\tBackupConfig:   q.BackupConfig.clone(db),\n\t\tBackupHistory:  q.BackupHistory.clone(db),\n\t\tFile:           q.File.clone(db),\n\t\tFolder:         q.Folder.clone(db),\n\t\tGitSyncConfig:  q.GitSyncConfig.clone(db),\n\t\tGitSyncHistory: q.GitSyncHistory.clone(db),\n\t\tNote:           q.Note.clone(db),\n\t\tNoteHistory:    q.NoteHistory.clone(db),\n\t\tNoteLink:       q.NoteLink.clone(db),\n\t\tSetting:        q.Setting.clone(db),\n\t\tStorage:        q.Storage.clone(db),\n\t\tUser:           q.User.clone(db),\n\t\tUserShare:      q.UserShare.clone(db),\n\t\tVault:          q.Vault.clone(db),\n\t}\n}\n\nfunc (q *Query) ReadDB() *Query {\n\treturn q.ReplaceDB(q.db.Clauses(dbresolver.Read))\n}\n\nfunc (q *Query) WriteDB() *Query {\n\treturn q.ReplaceDB(q.db.Clauses(dbresolver.Write))\n}\n\nfunc (q *Query) ReplaceDB(db *gorm.DB) *Query {\n\treturn &Query{\n\t\tdb:             db,\n\t\tBackupConfig:   q.BackupConfig.replaceDB(db),\n\t\tBackupHistory:  q.BackupHistory.replaceDB(db),\n\t\tFile:           q.File.replaceDB(db),\n\t\tFolder:         q.Folder.replaceDB(db),\n\t\tGitSyncConfig:  q.GitSyncConfig.replaceDB(db),\n\t\tGitSyncHistory: q.GitSyncHistory.replaceDB(db),\n\t\tNote:           q.Note.replaceDB(db),\n\t\tNoteHistory:    q.NoteHistory.replaceDB(db),\n\t\tNoteLink:       q.NoteLink.replaceDB(db),\n\t\tSetting:        q.Setting.replaceDB(db),\n\t\tStorage:        q.Storage.replaceDB(db),\n\t\tUser:           q.User.replaceDB(db),\n\t\tUserShare:      q.UserShare.replaceDB(db),\n\t\tVault:          q.Vault.replaceDB(db),\n\t}\n}\n\ntype queryCtx struct {\n\tBackupConfig   IBackupConfigDo\n\tBackupHistory  IBackupHistoryDo\n\tFile           IFileDo\n\tFolder         IFolderDo\n\tGitSyncConfig  IGitSyncConfigDo\n\tGitSyncHistory IGitSyncHistoryDo\n\tNote           INoteDo\n\tNoteHistory    INoteHistoryDo\n\tNoteLink       INoteLinkDo\n\tSetting        ISettingDo\n\tStorage        IStorageDo\n\tUser           IUserDo\n\tUserShare      IUserShareDo\n\tVault          IVaultDo\n}\n\nfunc (q *Query) WithContext(ctx context.Context) *queryCtx {\n\treturn &queryCtx{\n\t\tBackupConfig:   q.BackupConfig.WithContext(ctx),\n\t\tBackupHistory:  q.BackupHistory.WithContext(ctx),\n\t\tFile:           q.File.WithContext(ctx),\n\t\tFolder:         q.Folder.WithContext(ctx),\n\t\tGitSyncConfig:  q.GitSyncConfig.WithContext(ctx),\n\t\tGitSyncHistory: q.GitSyncHistory.WithContext(ctx),\n\t\tNote:           q.Note.WithContext(ctx),\n\t\tNoteHistory:    q.NoteHistory.WithContext(ctx),\n\t\tNoteLink:       q.NoteLink.WithContext(ctx),\n\t\tSetting:        q.Setting.WithContext(ctx),\n\t\tStorage:        q.Storage.WithContext(ctx),\n\t\tUser:           q.User.WithContext(ctx),\n\t\tUserShare:      q.UserShare.WithContext(ctx),\n\t\tVault:          q.Vault.WithContext(ctx),\n\t}\n}\n\nfunc (q *Query) Transaction(fc func(tx *Query) error, opts ...*sql.TxOptions) error {\n\treturn q.db.Transaction(func(tx *gorm.DB) error { return fc(q.clone(tx)) }, opts...)\n}\n\nfunc (q *Query) Begin(opts ...*sql.TxOptions) *QueryTx {\n\ttx := q.db.Begin(opts...)\n\treturn &QueryTx{Query: q.clone(tx), Error: tx.Error}\n}\n\ntype QueryTx struct {\n\t*Query\n\tError error\n}\n\nfunc (q *QueryTx) Commit() error {\n\treturn q.db.Commit().Error\n}\n\nfunc (q *QueryTx) Rollback() error {\n\treturn q.db.Rollback().Error\n}\n\nfunc (q *QueryTx) SavePoint(name string) error {\n\treturn q.db.SavePoint(name).Error\n}\n\nfunc (q *QueryTx) RollbackTo(name string) error {\n\treturn q.db.RollbackTo(name).Error\n}\n"
  },
  {
    "path": "internal/query/git_sync_config.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage query\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\n\t\"gorm.io/gen\"\n\t\"gorm.io/gen/field\"\n\n\t\"gorm.io/plugin/dbresolver\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n)\n\nfunc newGitSyncConfig(db *gorm.DB, opts ...gen.DOOption) gitSyncConfig {\n\t_gitSyncConfig := gitSyncConfig{}\n\n\t_gitSyncConfig.gitSyncConfigDo.UseDB(db, opts...)\n\t_gitSyncConfig.gitSyncConfigDo.UseModel(&model.GitSyncConfig{})\n\n\ttableName := _gitSyncConfig.gitSyncConfigDo.TableName()\n\t_gitSyncConfig.ALL = field.NewAsterisk(tableName)\n\t_gitSyncConfig.ID = field.NewInt64(tableName, \"id\")\n\t_gitSyncConfig.UID = field.NewInt64(tableName, \"uid\")\n\t_gitSyncConfig.VaultID = field.NewInt64(tableName, \"vault_id\")\n\t_gitSyncConfig.RepoURL = field.NewString(tableName, \"repo_url\")\n\t_gitSyncConfig.Username = field.NewString(tableName, \"username\")\n\t_gitSyncConfig.Password = field.NewString(tableName, \"password\")\n\t_gitSyncConfig.Branch = field.NewString(tableName, \"branch\")\n\t_gitSyncConfig.IsEnabled = field.NewInt64(tableName, \"is_enabled\")\n\t_gitSyncConfig.Delay = field.NewInt64(tableName, \"delay\")\n\t_gitSyncConfig.RetentionDays = field.NewInt64(tableName, \"retention_days\")\n\t_gitSyncConfig.LastSyncTime = field.NewTime(tableName, \"last_sync_time\")\n\t_gitSyncConfig.LastStatus = field.NewInt64(tableName, \"last_status\")\n\t_gitSyncConfig.LastMessage = field.NewString(tableName, \"last_message\")\n\t_gitSyncConfig.CreatedAt = field.NewField(tableName, \"created_at\")\n\t_gitSyncConfig.UpdatedAt = field.NewField(tableName, \"updated_at\")\n\n\t_gitSyncConfig.fillFieldMap()\n\n\treturn _gitSyncConfig\n}\n\ntype gitSyncConfig struct {\n\tgitSyncConfigDo gitSyncConfigDo\n\n\tALL           field.Asterisk\n\tID            field.Int64\n\tUID           field.Int64\n\tVaultID       field.Int64\n\tRepoURL       field.String\n\tUsername      field.String\n\tPassword      field.String\n\tBranch        field.String\n\tIsEnabled     field.Int64\n\tDelay         field.Int64\n\tRetentionDays field.Int64\n\tLastSyncTime  field.Time\n\tLastStatus    field.Int64\n\tLastMessage   field.String\n\tCreatedAt     field.Field\n\tUpdatedAt     field.Field\n\n\tfieldMap map[string]field.Expr\n}\n\nfunc (g gitSyncConfig) Table(newTableName string) *gitSyncConfig {\n\tg.gitSyncConfigDo.UseTable(newTableName)\n\treturn g.updateTableName(newTableName)\n}\n\nfunc (g gitSyncConfig) As(alias string) *gitSyncConfig {\n\tg.gitSyncConfigDo.DO = *(g.gitSyncConfigDo.As(alias).(*gen.DO))\n\treturn g.updateTableName(alias)\n}\n\nfunc (g *gitSyncConfig) updateTableName(table string) *gitSyncConfig {\n\tg.ALL = field.NewAsterisk(table)\n\tg.ID = field.NewInt64(table, \"id\")\n\tg.UID = field.NewInt64(table, \"uid\")\n\tg.VaultID = field.NewInt64(table, \"vault_id\")\n\tg.RepoURL = field.NewString(table, \"repo_url\")\n\tg.Username = field.NewString(table, \"username\")\n\tg.Password = field.NewString(table, \"password\")\n\tg.Branch = field.NewString(table, \"branch\")\n\tg.IsEnabled = field.NewInt64(table, \"is_enabled\")\n\tg.Delay = field.NewInt64(table, \"delay\")\n\tg.RetentionDays = field.NewInt64(table, \"retention_days\")\n\tg.LastSyncTime = field.NewTime(table, \"last_sync_time\")\n\tg.LastStatus = field.NewInt64(table, \"last_status\")\n\tg.LastMessage = field.NewString(table, \"last_message\")\n\tg.CreatedAt = field.NewField(table, \"created_at\")\n\tg.UpdatedAt = field.NewField(table, \"updated_at\")\n\n\tg.fillFieldMap()\n\n\treturn g\n}\n\nfunc (g *gitSyncConfig) WithContext(ctx context.Context) IGitSyncConfigDo {\n\treturn g.gitSyncConfigDo.WithContext(ctx)\n}\n\nfunc (g gitSyncConfig) TableName() string { return g.gitSyncConfigDo.TableName() }\n\nfunc (g gitSyncConfig) Alias() string { return g.gitSyncConfigDo.Alias() }\n\nfunc (g gitSyncConfig) Columns(cols ...field.Expr) gen.Columns {\n\treturn g.gitSyncConfigDo.Columns(cols...)\n}\n\nfunc (g *gitSyncConfig) GetFieldByName(fieldName string) (field.OrderExpr, bool) {\n\t_f, ok := g.fieldMap[fieldName]\n\tif !ok || _f == nil {\n\t\treturn nil, false\n\t}\n\t_oe, ok := _f.(field.OrderExpr)\n\treturn _oe, ok\n}\n\nfunc (g *gitSyncConfig) fillFieldMap() {\n\tg.fieldMap = make(map[string]field.Expr, 15)\n\tg.fieldMap[\"id\"] = g.ID\n\tg.fieldMap[\"uid\"] = g.UID\n\tg.fieldMap[\"vault_id\"] = g.VaultID\n\tg.fieldMap[\"repo_url\"] = g.RepoURL\n\tg.fieldMap[\"username\"] = g.Username\n\tg.fieldMap[\"password\"] = g.Password\n\tg.fieldMap[\"branch\"] = g.Branch\n\tg.fieldMap[\"is_enabled\"] = g.IsEnabled\n\tg.fieldMap[\"delay\"] = g.Delay\n\tg.fieldMap[\"retention_days\"] = g.RetentionDays\n\tg.fieldMap[\"last_sync_time\"] = g.LastSyncTime\n\tg.fieldMap[\"last_status\"] = g.LastStatus\n\tg.fieldMap[\"last_message\"] = g.LastMessage\n\tg.fieldMap[\"created_at\"] = g.CreatedAt\n\tg.fieldMap[\"updated_at\"] = g.UpdatedAt\n}\n\nfunc (g gitSyncConfig) clone(db *gorm.DB) gitSyncConfig {\n\tg.gitSyncConfigDo.ReplaceConnPool(db.Statement.ConnPool)\n\treturn g\n}\n\nfunc (g gitSyncConfig) replaceDB(db *gorm.DB) gitSyncConfig {\n\tg.gitSyncConfigDo.ReplaceDB(db)\n\treturn g\n}\n\ntype gitSyncConfigDo struct{ gen.DO }\n\ntype IGitSyncConfigDo interface {\n\tgen.SubQuery\n\tDebug() IGitSyncConfigDo\n\tWithContext(ctx context.Context) IGitSyncConfigDo\n\tWithResult(fc func(tx gen.Dao)) gen.ResultInfo\n\tReplaceDB(db *gorm.DB)\n\tReadDB() IGitSyncConfigDo\n\tWriteDB() IGitSyncConfigDo\n\tAs(alias string) gen.Dao\n\tSession(config *gorm.Session) IGitSyncConfigDo\n\tColumns(cols ...field.Expr) gen.Columns\n\tClauses(conds ...clause.Expression) IGitSyncConfigDo\n\tNot(conds ...gen.Condition) IGitSyncConfigDo\n\tOr(conds ...gen.Condition) IGitSyncConfigDo\n\tSelect(conds ...field.Expr) IGitSyncConfigDo\n\tWhere(conds ...gen.Condition) IGitSyncConfigDo\n\tOrder(conds ...field.Expr) IGitSyncConfigDo\n\tDistinct(cols ...field.Expr) IGitSyncConfigDo\n\tOmit(cols ...field.Expr) IGitSyncConfigDo\n\tJoin(table schema.Tabler, on ...field.Expr) IGitSyncConfigDo\n\tLeftJoin(table schema.Tabler, on ...field.Expr) IGitSyncConfigDo\n\tRightJoin(table schema.Tabler, on ...field.Expr) IGitSyncConfigDo\n\tGroup(cols ...field.Expr) IGitSyncConfigDo\n\tHaving(conds ...gen.Condition) IGitSyncConfigDo\n\tLimit(limit int) IGitSyncConfigDo\n\tOffset(offset int) IGitSyncConfigDo\n\tCount() (count int64, err error)\n\tScopes(funcs ...func(gen.Dao) gen.Dao) IGitSyncConfigDo\n\tUnscoped() IGitSyncConfigDo\n\tCreate(values ...*model.GitSyncConfig) error\n\tCreateInBatches(values []*model.GitSyncConfig, batchSize int) error\n\tSave(values ...*model.GitSyncConfig) error\n\tFirst() (*model.GitSyncConfig, error)\n\tTake() (*model.GitSyncConfig, error)\n\tLast() (*model.GitSyncConfig, error)\n\tFind() ([]*model.GitSyncConfig, error)\n\tFindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.GitSyncConfig, err error)\n\tFindInBatches(result *[]*model.GitSyncConfig, batchSize int, fc func(tx gen.Dao, batch int) error) error\n\tPluck(column field.Expr, dest interface{}) error\n\tDelete(...*model.GitSyncConfig) (info gen.ResultInfo, err error)\n\tUpdate(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdates(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdateColumns(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateFrom(q gen.SubQuery) gen.Dao\n\tAttrs(attrs ...field.AssignExpr) IGitSyncConfigDo\n\tAssign(attrs ...field.AssignExpr) IGitSyncConfigDo\n\tJoins(fields ...field.RelationField) IGitSyncConfigDo\n\tPreload(fields ...field.RelationField) IGitSyncConfigDo\n\tFirstOrInit() (*model.GitSyncConfig, error)\n\tFirstOrCreate() (*model.GitSyncConfig, error)\n\tFindByPage(offset int, limit int) (result []*model.GitSyncConfig, count int64, err error)\n\tScanByPage(result interface{}, offset int, limit int) (count int64, err error)\n\tRows() (*sql.Rows, error)\n\tRow() *sql.Row\n\tScan(result interface{}) (err error)\n\tReturning(value interface{}, columns ...string) IGitSyncConfigDo\n\tUnderlyingDB() *gorm.DB\n\tschema.Tabler\n}\n\nfunc (g gitSyncConfigDo) Debug() IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Debug())\n}\n\nfunc (g gitSyncConfigDo) WithContext(ctx context.Context) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.WithContext(ctx))\n}\n\nfunc (g gitSyncConfigDo) ReadDB() IGitSyncConfigDo {\n\treturn g.Clauses(dbresolver.Read)\n}\n\nfunc (g gitSyncConfigDo) WriteDB() IGitSyncConfigDo {\n\treturn g.Clauses(dbresolver.Write)\n}\n\nfunc (g gitSyncConfigDo) Session(config *gorm.Session) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Session(config))\n}\n\nfunc (g gitSyncConfigDo) Clauses(conds ...clause.Expression) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Clauses(conds...))\n}\n\nfunc (g gitSyncConfigDo) Returning(value interface{}, columns ...string) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Returning(value, columns...))\n}\n\nfunc (g gitSyncConfigDo) Not(conds ...gen.Condition) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Not(conds...))\n}\n\nfunc (g gitSyncConfigDo) Or(conds ...gen.Condition) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Or(conds...))\n}\n\nfunc (g gitSyncConfigDo) Select(conds ...field.Expr) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Select(conds...))\n}\n\nfunc (g gitSyncConfigDo) Where(conds ...gen.Condition) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Where(conds...))\n}\n\nfunc (g gitSyncConfigDo) Order(conds ...field.Expr) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Order(conds...))\n}\n\nfunc (g gitSyncConfigDo) Distinct(cols ...field.Expr) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Distinct(cols...))\n}\n\nfunc (g gitSyncConfigDo) Omit(cols ...field.Expr) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Omit(cols...))\n}\n\nfunc (g gitSyncConfigDo) Join(table schema.Tabler, on ...field.Expr) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Join(table, on...))\n}\n\nfunc (g gitSyncConfigDo) LeftJoin(table schema.Tabler, on ...field.Expr) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.LeftJoin(table, on...))\n}\n\nfunc (g gitSyncConfigDo) RightJoin(table schema.Tabler, on ...field.Expr) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.RightJoin(table, on...))\n}\n\nfunc (g gitSyncConfigDo) Group(cols ...field.Expr) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Group(cols...))\n}\n\nfunc (g gitSyncConfigDo) Having(conds ...gen.Condition) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Having(conds...))\n}\n\nfunc (g gitSyncConfigDo) Limit(limit int) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Limit(limit))\n}\n\nfunc (g gitSyncConfigDo) Offset(offset int) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Offset(offset))\n}\n\nfunc (g gitSyncConfigDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Scopes(funcs...))\n}\n\nfunc (g gitSyncConfigDo) Unscoped() IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Unscoped())\n}\n\nfunc (g gitSyncConfigDo) Create(values ...*model.GitSyncConfig) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn g.DO.Create(values)\n}\n\nfunc (g gitSyncConfigDo) CreateInBatches(values []*model.GitSyncConfig, batchSize int) error {\n\treturn g.DO.CreateInBatches(values, batchSize)\n}\n\n// Save : !!! underlying implementation is different with GORM\n// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)\nfunc (g gitSyncConfigDo) Save(values ...*model.GitSyncConfig) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn g.DO.Save(values)\n}\n\nfunc (g gitSyncConfigDo) First() (*model.GitSyncConfig, error) {\n\tif result, err := g.DO.First(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.GitSyncConfig), nil\n\t}\n}\n\nfunc (g gitSyncConfigDo) Take() (*model.GitSyncConfig, error) {\n\tif result, err := g.DO.Take(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.GitSyncConfig), nil\n\t}\n}\n\nfunc (g gitSyncConfigDo) Last() (*model.GitSyncConfig, error) {\n\tif result, err := g.DO.Last(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.GitSyncConfig), nil\n\t}\n}\n\nfunc (g gitSyncConfigDo) Find() ([]*model.GitSyncConfig, error) {\n\tresult, err := g.DO.Find()\n\treturn result.([]*model.GitSyncConfig), err\n}\n\nfunc (g gitSyncConfigDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.GitSyncConfig, err error) {\n\tbuf := make([]*model.GitSyncConfig, 0, batchSize)\n\terr = g.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {\n\t\tdefer func() { results = append(results, buf...) }()\n\t\treturn fc(tx, batch)\n\t})\n\treturn results, err\n}\n\nfunc (g gitSyncConfigDo) FindInBatches(result *[]*model.GitSyncConfig, batchSize int, fc func(tx gen.Dao, batch int) error) error {\n\treturn g.DO.FindInBatches(result, batchSize, fc)\n}\n\nfunc (g gitSyncConfigDo) Attrs(attrs ...field.AssignExpr) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Attrs(attrs...))\n}\n\nfunc (g gitSyncConfigDo) Assign(attrs ...field.AssignExpr) IGitSyncConfigDo {\n\treturn g.withDO(g.DO.Assign(attrs...))\n}\n\nfunc (g gitSyncConfigDo) Joins(fields ...field.RelationField) IGitSyncConfigDo {\n\tfor _, _f := range fields {\n\t\tg = *g.withDO(g.DO.Joins(_f))\n\t}\n\treturn &g\n}\n\nfunc (g gitSyncConfigDo) Preload(fields ...field.RelationField) IGitSyncConfigDo {\n\tfor _, _f := range fields {\n\t\tg = *g.withDO(g.DO.Preload(_f))\n\t}\n\treturn &g\n}\n\nfunc (g gitSyncConfigDo) FirstOrInit() (*model.GitSyncConfig, error) {\n\tif result, err := g.DO.FirstOrInit(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.GitSyncConfig), nil\n\t}\n}\n\nfunc (g gitSyncConfigDo) FirstOrCreate() (*model.GitSyncConfig, error) {\n\tif result, err := g.DO.FirstOrCreate(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.GitSyncConfig), nil\n\t}\n}\n\nfunc (g gitSyncConfigDo) FindByPage(offset int, limit int) (result []*model.GitSyncConfig, count int64, err error) {\n\tresult, err = g.Offset(offset).Limit(limit).Find()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif size := len(result); 0 < limit && 0 < size && size < limit {\n\t\tcount = int64(size + offset)\n\t\treturn\n\t}\n\n\tcount, err = g.Offset(-1).Limit(-1).Count()\n\treturn\n}\n\nfunc (g gitSyncConfigDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {\n\tcount, err = g.Count()\n\tif err != nil {\n\t\treturn\n\t}\n\n\terr = g.Offset(offset).Limit(limit).Scan(result)\n\treturn\n}\n\nfunc (g gitSyncConfigDo) Scan(result interface{}) (err error) {\n\treturn g.DO.Scan(result)\n}\n\nfunc (g gitSyncConfigDo) Delete(models ...*model.GitSyncConfig) (result gen.ResultInfo, err error) {\n\treturn g.DO.Delete(models)\n}\n\nfunc (g *gitSyncConfigDo) withDO(do gen.Dao) *gitSyncConfigDo {\n\tg.DO = *do.(*gen.DO)\n\treturn g\n}\n"
  },
  {
    "path": "internal/query/git_sync_history.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage query\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\n\t\"gorm.io/gen\"\n\t\"gorm.io/gen/field\"\n\n\t\"gorm.io/plugin/dbresolver\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n)\n\nfunc newGitSyncHistory(db *gorm.DB, opts ...gen.DOOption) gitSyncHistory {\n\t_gitSyncHistory := gitSyncHistory{}\n\n\t_gitSyncHistory.gitSyncHistoryDo.UseDB(db, opts...)\n\t_gitSyncHistory.gitSyncHistoryDo.UseModel(&model.GitSyncHistory{})\n\n\ttableName := _gitSyncHistory.gitSyncHistoryDo.TableName()\n\t_gitSyncHistory.ALL = field.NewAsterisk(tableName)\n\t_gitSyncHistory.ID = field.NewInt64(tableName, \"id\")\n\t_gitSyncHistory.UID = field.NewInt64(tableName, \"uid\")\n\t_gitSyncHistory.ConfigID = field.NewInt64(tableName, \"config_id\")\n\t_gitSyncHistory.StartTime = field.NewTime(tableName, \"start_time\")\n\t_gitSyncHistory.EndTime = field.NewTime(tableName, \"end_time\")\n\t_gitSyncHistory.Status = field.NewInt64(tableName, \"status\")\n\t_gitSyncHistory.Message = field.NewString(tableName, \"message\")\n\t_gitSyncHistory.CreatedAt = field.NewField(tableName, \"created_at\")\n\t_gitSyncHistory.UpdatedAt = field.NewField(tableName, \"updated_at\")\n\n\t_gitSyncHistory.fillFieldMap()\n\n\treturn _gitSyncHistory\n}\n\ntype gitSyncHistory struct {\n\tgitSyncHistoryDo gitSyncHistoryDo\n\n\tALL       field.Asterisk\n\tID        field.Int64\n\tUID       field.Int64\n\tConfigID  field.Int64\n\tStartTime field.Time\n\tEndTime   field.Time\n\tStatus    field.Int64\n\tMessage   field.String\n\tCreatedAt field.Field\n\tUpdatedAt field.Field\n\n\tfieldMap map[string]field.Expr\n}\n\nfunc (g gitSyncHistory) Table(newTableName string) *gitSyncHistory {\n\tg.gitSyncHistoryDo.UseTable(newTableName)\n\treturn g.updateTableName(newTableName)\n}\n\nfunc (g gitSyncHistory) As(alias string) *gitSyncHistory {\n\tg.gitSyncHistoryDo.DO = *(g.gitSyncHistoryDo.As(alias).(*gen.DO))\n\treturn g.updateTableName(alias)\n}\n\nfunc (g *gitSyncHistory) updateTableName(table string) *gitSyncHistory {\n\tg.ALL = field.NewAsterisk(table)\n\tg.ID = field.NewInt64(table, \"id\")\n\tg.UID = field.NewInt64(table, \"uid\")\n\tg.ConfigID = field.NewInt64(table, \"config_id\")\n\tg.StartTime = field.NewTime(table, \"start_time\")\n\tg.EndTime = field.NewTime(table, \"end_time\")\n\tg.Status = field.NewInt64(table, \"status\")\n\tg.Message = field.NewString(table, \"message\")\n\tg.CreatedAt = field.NewField(table, \"created_at\")\n\tg.UpdatedAt = field.NewField(table, \"updated_at\")\n\n\tg.fillFieldMap()\n\n\treturn g\n}\n\nfunc (g *gitSyncHistory) WithContext(ctx context.Context) IGitSyncHistoryDo {\n\treturn g.gitSyncHistoryDo.WithContext(ctx)\n}\n\nfunc (g gitSyncHistory) TableName() string { return g.gitSyncHistoryDo.TableName() }\n\nfunc (g gitSyncHistory) Alias() string { return g.gitSyncHistoryDo.Alias() }\n\nfunc (g gitSyncHistory) Columns(cols ...field.Expr) gen.Columns {\n\treturn g.gitSyncHistoryDo.Columns(cols...)\n}\n\nfunc (g *gitSyncHistory) GetFieldByName(fieldName string) (field.OrderExpr, bool) {\n\t_f, ok := g.fieldMap[fieldName]\n\tif !ok || _f == nil {\n\t\treturn nil, false\n\t}\n\t_oe, ok := _f.(field.OrderExpr)\n\treturn _oe, ok\n}\n\nfunc (g *gitSyncHistory) fillFieldMap() {\n\tg.fieldMap = make(map[string]field.Expr, 9)\n\tg.fieldMap[\"id\"] = g.ID\n\tg.fieldMap[\"uid\"] = g.UID\n\tg.fieldMap[\"config_id\"] = g.ConfigID\n\tg.fieldMap[\"start_time\"] = g.StartTime\n\tg.fieldMap[\"end_time\"] = g.EndTime\n\tg.fieldMap[\"status\"] = g.Status\n\tg.fieldMap[\"message\"] = g.Message\n\tg.fieldMap[\"created_at\"] = g.CreatedAt\n\tg.fieldMap[\"updated_at\"] = g.UpdatedAt\n}\n\nfunc (g gitSyncHistory) clone(db *gorm.DB) gitSyncHistory {\n\tg.gitSyncHistoryDo.ReplaceConnPool(db.Statement.ConnPool)\n\treturn g\n}\n\nfunc (g gitSyncHistory) replaceDB(db *gorm.DB) gitSyncHistory {\n\tg.gitSyncHistoryDo.ReplaceDB(db)\n\treturn g\n}\n\ntype gitSyncHistoryDo struct{ gen.DO }\n\ntype IGitSyncHistoryDo interface {\n\tgen.SubQuery\n\tDebug() IGitSyncHistoryDo\n\tWithContext(ctx context.Context) IGitSyncHistoryDo\n\tWithResult(fc func(tx gen.Dao)) gen.ResultInfo\n\tReplaceDB(db *gorm.DB)\n\tReadDB() IGitSyncHistoryDo\n\tWriteDB() IGitSyncHistoryDo\n\tAs(alias string) gen.Dao\n\tSession(config *gorm.Session) IGitSyncHistoryDo\n\tColumns(cols ...field.Expr) gen.Columns\n\tClauses(conds ...clause.Expression) IGitSyncHistoryDo\n\tNot(conds ...gen.Condition) IGitSyncHistoryDo\n\tOr(conds ...gen.Condition) IGitSyncHistoryDo\n\tSelect(conds ...field.Expr) IGitSyncHistoryDo\n\tWhere(conds ...gen.Condition) IGitSyncHistoryDo\n\tOrder(conds ...field.Expr) IGitSyncHistoryDo\n\tDistinct(cols ...field.Expr) IGitSyncHistoryDo\n\tOmit(cols ...field.Expr) IGitSyncHistoryDo\n\tJoin(table schema.Tabler, on ...field.Expr) IGitSyncHistoryDo\n\tLeftJoin(table schema.Tabler, on ...field.Expr) IGitSyncHistoryDo\n\tRightJoin(table schema.Tabler, on ...field.Expr) IGitSyncHistoryDo\n\tGroup(cols ...field.Expr) IGitSyncHistoryDo\n\tHaving(conds ...gen.Condition) IGitSyncHistoryDo\n\tLimit(limit int) IGitSyncHistoryDo\n\tOffset(offset int) IGitSyncHistoryDo\n\tCount() (count int64, err error)\n\tScopes(funcs ...func(gen.Dao) gen.Dao) IGitSyncHistoryDo\n\tUnscoped() IGitSyncHistoryDo\n\tCreate(values ...*model.GitSyncHistory) error\n\tCreateInBatches(values []*model.GitSyncHistory, batchSize int) error\n\tSave(values ...*model.GitSyncHistory) error\n\tFirst() (*model.GitSyncHistory, error)\n\tTake() (*model.GitSyncHistory, error)\n\tLast() (*model.GitSyncHistory, error)\n\tFind() ([]*model.GitSyncHistory, error)\n\tFindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.GitSyncHistory, err error)\n\tFindInBatches(result *[]*model.GitSyncHistory, batchSize int, fc func(tx gen.Dao, batch int) error) error\n\tPluck(column field.Expr, dest interface{}) error\n\tDelete(...*model.GitSyncHistory) (info gen.ResultInfo, err error)\n\tUpdate(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdates(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdateColumns(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateFrom(q gen.SubQuery) gen.Dao\n\tAttrs(attrs ...field.AssignExpr) IGitSyncHistoryDo\n\tAssign(attrs ...field.AssignExpr) IGitSyncHistoryDo\n\tJoins(fields ...field.RelationField) IGitSyncHistoryDo\n\tPreload(fields ...field.RelationField) IGitSyncHistoryDo\n\tFirstOrInit() (*model.GitSyncHistory, error)\n\tFirstOrCreate() (*model.GitSyncHistory, error)\n\tFindByPage(offset int, limit int) (result []*model.GitSyncHistory, count int64, err error)\n\tScanByPage(result interface{}, offset int, limit int) (count int64, err error)\n\tRows() (*sql.Rows, error)\n\tRow() *sql.Row\n\tScan(result interface{}) (err error)\n\tReturning(value interface{}, columns ...string) IGitSyncHistoryDo\n\tUnderlyingDB() *gorm.DB\n\tschema.Tabler\n}\n\nfunc (g gitSyncHistoryDo) Debug() IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Debug())\n}\n\nfunc (g gitSyncHistoryDo) WithContext(ctx context.Context) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.WithContext(ctx))\n}\n\nfunc (g gitSyncHistoryDo) ReadDB() IGitSyncHistoryDo {\n\treturn g.Clauses(dbresolver.Read)\n}\n\nfunc (g gitSyncHistoryDo) WriteDB() IGitSyncHistoryDo {\n\treturn g.Clauses(dbresolver.Write)\n}\n\nfunc (g gitSyncHistoryDo) Session(config *gorm.Session) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Session(config))\n}\n\nfunc (g gitSyncHistoryDo) Clauses(conds ...clause.Expression) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Clauses(conds...))\n}\n\nfunc (g gitSyncHistoryDo) Returning(value interface{}, columns ...string) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Returning(value, columns...))\n}\n\nfunc (g gitSyncHistoryDo) Not(conds ...gen.Condition) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Not(conds...))\n}\n\nfunc (g gitSyncHistoryDo) Or(conds ...gen.Condition) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Or(conds...))\n}\n\nfunc (g gitSyncHistoryDo) Select(conds ...field.Expr) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Select(conds...))\n}\n\nfunc (g gitSyncHistoryDo) Where(conds ...gen.Condition) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Where(conds...))\n}\n\nfunc (g gitSyncHistoryDo) Order(conds ...field.Expr) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Order(conds...))\n}\n\nfunc (g gitSyncHistoryDo) Distinct(cols ...field.Expr) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Distinct(cols...))\n}\n\nfunc (g gitSyncHistoryDo) Omit(cols ...field.Expr) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Omit(cols...))\n}\n\nfunc (g gitSyncHistoryDo) Join(table schema.Tabler, on ...field.Expr) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Join(table, on...))\n}\n\nfunc (g gitSyncHistoryDo) LeftJoin(table schema.Tabler, on ...field.Expr) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.LeftJoin(table, on...))\n}\n\nfunc (g gitSyncHistoryDo) RightJoin(table schema.Tabler, on ...field.Expr) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.RightJoin(table, on...))\n}\n\nfunc (g gitSyncHistoryDo) Group(cols ...field.Expr) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Group(cols...))\n}\n\nfunc (g gitSyncHistoryDo) Having(conds ...gen.Condition) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Having(conds...))\n}\n\nfunc (g gitSyncHistoryDo) Limit(limit int) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Limit(limit))\n}\n\nfunc (g gitSyncHistoryDo) Offset(offset int) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Offset(offset))\n}\n\nfunc (g gitSyncHistoryDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Scopes(funcs...))\n}\n\nfunc (g gitSyncHistoryDo) Unscoped() IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Unscoped())\n}\n\nfunc (g gitSyncHistoryDo) Create(values ...*model.GitSyncHistory) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn g.DO.Create(values)\n}\n\nfunc (g gitSyncHistoryDo) CreateInBatches(values []*model.GitSyncHistory, batchSize int) error {\n\treturn g.DO.CreateInBatches(values, batchSize)\n}\n\n// Save : !!! underlying implementation is different with GORM\n// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)\nfunc (g gitSyncHistoryDo) Save(values ...*model.GitSyncHistory) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn g.DO.Save(values)\n}\n\nfunc (g gitSyncHistoryDo) First() (*model.GitSyncHistory, error) {\n\tif result, err := g.DO.First(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.GitSyncHistory), nil\n\t}\n}\n\nfunc (g gitSyncHistoryDo) Take() (*model.GitSyncHistory, error) {\n\tif result, err := g.DO.Take(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.GitSyncHistory), nil\n\t}\n}\n\nfunc (g gitSyncHistoryDo) Last() (*model.GitSyncHistory, error) {\n\tif result, err := g.DO.Last(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.GitSyncHistory), nil\n\t}\n}\n\nfunc (g gitSyncHistoryDo) Find() ([]*model.GitSyncHistory, error) {\n\tresult, err := g.DO.Find()\n\treturn result.([]*model.GitSyncHistory), err\n}\n\nfunc (g gitSyncHistoryDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.GitSyncHistory, err error) {\n\tbuf := make([]*model.GitSyncHistory, 0, batchSize)\n\terr = g.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {\n\t\tdefer func() { results = append(results, buf...) }()\n\t\treturn fc(tx, batch)\n\t})\n\treturn results, err\n}\n\nfunc (g gitSyncHistoryDo) FindInBatches(result *[]*model.GitSyncHistory, batchSize int, fc func(tx gen.Dao, batch int) error) error {\n\treturn g.DO.FindInBatches(result, batchSize, fc)\n}\n\nfunc (g gitSyncHistoryDo) Attrs(attrs ...field.AssignExpr) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Attrs(attrs...))\n}\n\nfunc (g gitSyncHistoryDo) Assign(attrs ...field.AssignExpr) IGitSyncHistoryDo {\n\treturn g.withDO(g.DO.Assign(attrs...))\n}\n\nfunc (g gitSyncHistoryDo) Joins(fields ...field.RelationField) IGitSyncHistoryDo {\n\tfor _, _f := range fields {\n\t\tg = *g.withDO(g.DO.Joins(_f))\n\t}\n\treturn &g\n}\n\nfunc (g gitSyncHistoryDo) Preload(fields ...field.RelationField) IGitSyncHistoryDo {\n\tfor _, _f := range fields {\n\t\tg = *g.withDO(g.DO.Preload(_f))\n\t}\n\treturn &g\n}\n\nfunc (g gitSyncHistoryDo) FirstOrInit() (*model.GitSyncHistory, error) {\n\tif result, err := g.DO.FirstOrInit(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.GitSyncHistory), nil\n\t}\n}\n\nfunc (g gitSyncHistoryDo) FirstOrCreate() (*model.GitSyncHistory, error) {\n\tif result, err := g.DO.FirstOrCreate(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.GitSyncHistory), nil\n\t}\n}\n\nfunc (g gitSyncHistoryDo) FindByPage(offset int, limit int) (result []*model.GitSyncHistory, count int64, err error) {\n\tresult, err = g.Offset(offset).Limit(limit).Find()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif size := len(result); 0 < limit && 0 < size && size < limit {\n\t\tcount = int64(size + offset)\n\t\treturn\n\t}\n\n\tcount, err = g.Offset(-1).Limit(-1).Count()\n\treturn\n}\n\nfunc (g gitSyncHistoryDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {\n\tcount, err = g.Count()\n\tif err != nil {\n\t\treturn\n\t}\n\n\terr = g.Offset(offset).Limit(limit).Scan(result)\n\treturn\n}\n\nfunc (g gitSyncHistoryDo) Scan(result interface{}) (err error) {\n\treturn g.DO.Scan(result)\n}\n\nfunc (g gitSyncHistoryDo) Delete(models ...*model.GitSyncHistory) (result gen.ResultInfo, err error) {\n\treturn g.DO.Delete(models)\n}\n\nfunc (g *gitSyncHistoryDo) withDO(do gen.Dao) *gitSyncHistoryDo {\n\tg.DO = *do.(*gen.DO)\n\treturn g\n}\n"
  },
  {
    "path": "internal/query/note.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage query\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\n\t\"gorm.io/gen\"\n\t\"gorm.io/gen/field\"\n\n\t\"gorm.io/plugin/dbresolver\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n)\n\nfunc newNote(db *gorm.DB, opts ...gen.DOOption) note {\n\t_note := note{}\n\n\t_note.noteDo.UseDB(db, opts...)\n\t_note.noteDo.UseModel(&model.Note{})\n\n\ttableName := _note.noteDo.TableName()\n\t_note.ALL = field.NewAsterisk(tableName)\n\t_note.ID = field.NewInt64(tableName, \"id\")\n\t_note.VaultID = field.NewInt64(tableName, \"vault_id\")\n\t_note.Action = field.NewString(tableName, \"action\")\n\t_note.Rename = field.NewInt64(tableName, \"rename\")\n\t_note.FID = field.NewInt64(tableName, \"fid\")\n\t_note.Path = field.NewString(tableName, \"path\")\n\t_note.PathHash = field.NewString(tableName, \"path_hash\")\n\t_note.Content = field.NewString(tableName, \"content\")\n\t_note.ContentHash = field.NewString(tableName, \"content_hash\")\n\t_note.ContentLastSnapshot = field.NewString(tableName, \"content_last_snapshot\")\n\t_note.ContentLastSnapshotHash = field.NewString(tableName, \"content_last_snapshot_hash\")\n\t_note.Version = field.NewInt64(tableName, \"version\")\n\t_note.ClientName = field.NewString(tableName, \"client_name\")\n\t_note.ClientType = field.NewString(tableName, \"client_type\")\n\t_note.ClientVersion = field.NewString(tableName, \"client_version\")\n\t_note.Size = field.NewInt64(tableName, \"size\")\n\t_note.Ctime = field.NewInt64(tableName, \"ctime\")\n\t_note.Mtime = field.NewInt64(tableName, \"mtime\")\n\t_note.UpdatedTimestamp = field.NewInt64(tableName, \"updated_timestamp\")\n\t_note.CreatedAt = field.NewField(tableName, \"created_at\")\n\t_note.UpdatedAt = field.NewField(tableName, \"updated_at\")\n\n\t_note.fillFieldMap()\n\n\treturn _note\n}\n\ntype note struct {\n\tnoteDo noteDo\n\n\tALL                     field.Asterisk\n\tID                      field.Int64\n\tVaultID                 field.Int64\n\tAction                  field.String\n\tRename                  field.Int64\n\tFID                     field.Int64\n\tPath                    field.String\n\tPathHash                field.String\n\tContent                 field.String\n\tContentHash             field.String\n\tContentLastSnapshot     field.String\n\tContentLastSnapshotHash field.String\n\tVersion                 field.Int64\n\tClientName              field.String\n\tClientType              field.String\n\tClientVersion           field.String\n\tSize                    field.Int64\n\tCtime                   field.Int64\n\tMtime                   field.Int64\n\tUpdatedTimestamp        field.Int64\n\tCreatedAt               field.Field\n\tUpdatedAt               field.Field\n\n\tfieldMap map[string]field.Expr\n}\n\nfunc (n note) Table(newTableName string) *note {\n\tn.noteDo.UseTable(newTableName)\n\treturn n.updateTableName(newTableName)\n}\n\nfunc (n note) As(alias string) *note {\n\tn.noteDo.DO = *(n.noteDo.As(alias).(*gen.DO))\n\treturn n.updateTableName(alias)\n}\n\nfunc (n *note) updateTableName(table string) *note {\n\tn.ALL = field.NewAsterisk(table)\n\tn.ID = field.NewInt64(table, \"id\")\n\tn.VaultID = field.NewInt64(table, \"vault_id\")\n\tn.Action = field.NewString(table, \"action\")\n\tn.Rename = field.NewInt64(table, \"rename\")\n\tn.FID = field.NewInt64(table, \"fid\")\n\tn.Path = field.NewString(table, \"path\")\n\tn.PathHash = field.NewString(table, \"path_hash\")\n\tn.Content = field.NewString(table, \"content\")\n\tn.ContentHash = field.NewString(table, \"content_hash\")\n\tn.ContentLastSnapshot = field.NewString(table, \"content_last_snapshot\")\n\tn.ContentLastSnapshotHash = field.NewString(table, \"content_last_snapshot_hash\")\n\tn.Version = field.NewInt64(table, \"version\")\n\tn.ClientName = field.NewString(table, \"client_name\")\n\tn.ClientType = field.NewString(table, \"client_type\")\n\tn.ClientVersion = field.NewString(table, \"client_version\")\n\tn.Size = field.NewInt64(table, \"size\")\n\tn.Ctime = field.NewInt64(table, \"ctime\")\n\tn.Mtime = field.NewInt64(table, \"mtime\")\n\tn.UpdatedTimestamp = field.NewInt64(table, \"updated_timestamp\")\n\tn.CreatedAt = field.NewField(table, \"created_at\")\n\tn.UpdatedAt = field.NewField(table, \"updated_at\")\n\n\tn.fillFieldMap()\n\n\treturn n\n}\n\nfunc (n *note) WithContext(ctx context.Context) INoteDo { return n.noteDo.WithContext(ctx) }\n\nfunc (n note) TableName() string { return n.noteDo.TableName() }\n\nfunc (n note) Alias() string { return n.noteDo.Alias() }\n\nfunc (n note) Columns(cols ...field.Expr) gen.Columns { return n.noteDo.Columns(cols...) }\n\nfunc (n *note) GetFieldByName(fieldName string) (field.OrderExpr, bool) {\n\t_f, ok := n.fieldMap[fieldName]\n\tif !ok || _f == nil {\n\t\treturn nil, false\n\t}\n\t_oe, ok := _f.(field.OrderExpr)\n\treturn _oe, ok\n}\n\nfunc (n *note) fillFieldMap() {\n\tn.fieldMap = make(map[string]field.Expr, 21)\n\tn.fieldMap[\"id\"] = n.ID\n\tn.fieldMap[\"vault_id\"] = n.VaultID\n\tn.fieldMap[\"action\"] = n.Action\n\tn.fieldMap[\"rename\"] = n.Rename\n\tn.fieldMap[\"fid\"] = n.FID\n\tn.fieldMap[\"path\"] = n.Path\n\tn.fieldMap[\"path_hash\"] = n.PathHash\n\tn.fieldMap[\"content\"] = n.Content\n\tn.fieldMap[\"content_hash\"] = n.ContentHash\n\tn.fieldMap[\"content_last_snapshot\"] = n.ContentLastSnapshot\n\tn.fieldMap[\"content_last_snapshot_hash\"] = n.ContentLastSnapshotHash\n\tn.fieldMap[\"version\"] = n.Version\n\tn.fieldMap[\"client_name\"] = n.ClientName\n\tn.fieldMap[\"client_type\"] = n.ClientType\n\tn.fieldMap[\"client_version\"] = n.ClientVersion\n\tn.fieldMap[\"size\"] = n.Size\n\tn.fieldMap[\"ctime\"] = n.Ctime\n\tn.fieldMap[\"mtime\"] = n.Mtime\n\tn.fieldMap[\"updated_timestamp\"] = n.UpdatedTimestamp\n\tn.fieldMap[\"created_at\"] = n.CreatedAt\n\tn.fieldMap[\"updated_at\"] = n.UpdatedAt\n}\n\nfunc (n note) clone(db *gorm.DB) note {\n\tn.noteDo.ReplaceConnPool(db.Statement.ConnPool)\n\treturn n\n}\n\nfunc (n note) replaceDB(db *gorm.DB) note {\n\tn.noteDo.ReplaceDB(db)\n\treturn n\n}\n\ntype noteDo struct{ gen.DO }\n\ntype INoteDo interface {\n\tgen.SubQuery\n\tDebug() INoteDo\n\tWithContext(ctx context.Context) INoteDo\n\tWithResult(fc func(tx gen.Dao)) gen.ResultInfo\n\tReplaceDB(db *gorm.DB)\n\tReadDB() INoteDo\n\tWriteDB() INoteDo\n\tAs(alias string) gen.Dao\n\tSession(config *gorm.Session) INoteDo\n\tColumns(cols ...field.Expr) gen.Columns\n\tClauses(conds ...clause.Expression) INoteDo\n\tNot(conds ...gen.Condition) INoteDo\n\tOr(conds ...gen.Condition) INoteDo\n\tSelect(conds ...field.Expr) INoteDo\n\tWhere(conds ...gen.Condition) INoteDo\n\tOrder(conds ...field.Expr) INoteDo\n\tDistinct(cols ...field.Expr) INoteDo\n\tOmit(cols ...field.Expr) INoteDo\n\tJoin(table schema.Tabler, on ...field.Expr) INoteDo\n\tLeftJoin(table schema.Tabler, on ...field.Expr) INoteDo\n\tRightJoin(table schema.Tabler, on ...field.Expr) INoteDo\n\tGroup(cols ...field.Expr) INoteDo\n\tHaving(conds ...gen.Condition) INoteDo\n\tLimit(limit int) INoteDo\n\tOffset(offset int) INoteDo\n\tCount() (count int64, err error)\n\tScopes(funcs ...func(gen.Dao) gen.Dao) INoteDo\n\tUnscoped() INoteDo\n\tCreate(values ...*model.Note) error\n\tCreateInBatches(values []*model.Note, batchSize int) error\n\tSave(values ...*model.Note) error\n\tFirst() (*model.Note, error)\n\tTake() (*model.Note, error)\n\tLast() (*model.Note, error)\n\tFind() ([]*model.Note, error)\n\tFindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Note, err error)\n\tFindInBatches(result *[]*model.Note, batchSize int, fc func(tx gen.Dao, batch int) error) error\n\tPluck(column field.Expr, dest interface{}) error\n\tDelete(...*model.Note) (info gen.ResultInfo, err error)\n\tUpdate(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdates(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdateColumns(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateFrom(q gen.SubQuery) gen.Dao\n\tAttrs(attrs ...field.AssignExpr) INoteDo\n\tAssign(attrs ...field.AssignExpr) INoteDo\n\tJoins(fields ...field.RelationField) INoteDo\n\tPreload(fields ...field.RelationField) INoteDo\n\tFirstOrInit() (*model.Note, error)\n\tFirstOrCreate() (*model.Note, error)\n\tFindByPage(offset int, limit int) (result []*model.Note, count int64, err error)\n\tScanByPage(result interface{}, offset int, limit int) (count int64, err error)\n\tRows() (*sql.Rows, error)\n\tRow() *sql.Row\n\tScan(result interface{}) (err error)\n\tReturning(value interface{}, columns ...string) INoteDo\n\tUnderlyingDB() *gorm.DB\n\tschema.Tabler\n}\n\nfunc (n noteDo) Debug() INoteDo {\n\treturn n.withDO(n.DO.Debug())\n}\n\nfunc (n noteDo) WithContext(ctx context.Context) INoteDo {\n\treturn n.withDO(n.DO.WithContext(ctx))\n}\n\nfunc (n noteDo) ReadDB() INoteDo {\n\treturn n.Clauses(dbresolver.Read)\n}\n\nfunc (n noteDo) WriteDB() INoteDo {\n\treturn n.Clauses(dbresolver.Write)\n}\n\nfunc (n noteDo) Session(config *gorm.Session) INoteDo {\n\treturn n.withDO(n.DO.Session(config))\n}\n\nfunc (n noteDo) Clauses(conds ...clause.Expression) INoteDo {\n\treturn n.withDO(n.DO.Clauses(conds...))\n}\n\nfunc (n noteDo) Returning(value interface{}, columns ...string) INoteDo {\n\treturn n.withDO(n.DO.Returning(value, columns...))\n}\n\nfunc (n noteDo) Not(conds ...gen.Condition) INoteDo {\n\treturn n.withDO(n.DO.Not(conds...))\n}\n\nfunc (n noteDo) Or(conds ...gen.Condition) INoteDo {\n\treturn n.withDO(n.DO.Or(conds...))\n}\n\nfunc (n noteDo) Select(conds ...field.Expr) INoteDo {\n\treturn n.withDO(n.DO.Select(conds...))\n}\n\nfunc (n noteDo) Where(conds ...gen.Condition) INoteDo {\n\treturn n.withDO(n.DO.Where(conds...))\n}\n\nfunc (n noteDo) Order(conds ...field.Expr) INoteDo {\n\treturn n.withDO(n.DO.Order(conds...))\n}\n\nfunc (n noteDo) Distinct(cols ...field.Expr) INoteDo {\n\treturn n.withDO(n.DO.Distinct(cols...))\n}\n\nfunc (n noteDo) Omit(cols ...field.Expr) INoteDo {\n\treturn n.withDO(n.DO.Omit(cols...))\n}\n\nfunc (n noteDo) Join(table schema.Tabler, on ...field.Expr) INoteDo {\n\treturn n.withDO(n.DO.Join(table, on...))\n}\n\nfunc (n noteDo) LeftJoin(table schema.Tabler, on ...field.Expr) INoteDo {\n\treturn n.withDO(n.DO.LeftJoin(table, on...))\n}\n\nfunc (n noteDo) RightJoin(table schema.Tabler, on ...field.Expr) INoteDo {\n\treturn n.withDO(n.DO.RightJoin(table, on...))\n}\n\nfunc (n noteDo) Group(cols ...field.Expr) INoteDo {\n\treturn n.withDO(n.DO.Group(cols...))\n}\n\nfunc (n noteDo) Having(conds ...gen.Condition) INoteDo {\n\treturn n.withDO(n.DO.Having(conds...))\n}\n\nfunc (n noteDo) Limit(limit int) INoteDo {\n\treturn n.withDO(n.DO.Limit(limit))\n}\n\nfunc (n noteDo) Offset(offset int) INoteDo {\n\treturn n.withDO(n.DO.Offset(offset))\n}\n\nfunc (n noteDo) Scopes(funcs ...func(gen.Dao) gen.Dao) INoteDo {\n\treturn n.withDO(n.DO.Scopes(funcs...))\n}\n\nfunc (n noteDo) Unscoped() INoteDo {\n\treturn n.withDO(n.DO.Unscoped())\n}\n\nfunc (n noteDo) Create(values ...*model.Note) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn n.DO.Create(values)\n}\n\nfunc (n noteDo) CreateInBatches(values []*model.Note, batchSize int) error {\n\treturn n.DO.CreateInBatches(values, batchSize)\n}\n\n// Save : !!! underlying implementation is different with GORM\n// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)\nfunc (n noteDo) Save(values ...*model.Note) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn n.DO.Save(values)\n}\n\nfunc (n noteDo) First() (*model.Note, error) {\n\tif result, err := n.DO.First(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Note), nil\n\t}\n}\n\nfunc (n noteDo) Take() (*model.Note, error) {\n\tif result, err := n.DO.Take(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Note), nil\n\t}\n}\n\nfunc (n noteDo) Last() (*model.Note, error) {\n\tif result, err := n.DO.Last(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Note), nil\n\t}\n}\n\nfunc (n noteDo) Find() ([]*model.Note, error) {\n\tresult, err := n.DO.Find()\n\treturn result.([]*model.Note), err\n}\n\nfunc (n noteDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Note, err error) {\n\tbuf := make([]*model.Note, 0, batchSize)\n\terr = n.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {\n\t\tdefer func() { results = append(results, buf...) }()\n\t\treturn fc(tx, batch)\n\t})\n\treturn results, err\n}\n\nfunc (n noteDo) FindInBatches(result *[]*model.Note, batchSize int, fc func(tx gen.Dao, batch int) error) error {\n\treturn n.DO.FindInBatches(result, batchSize, fc)\n}\n\nfunc (n noteDo) Attrs(attrs ...field.AssignExpr) INoteDo {\n\treturn n.withDO(n.DO.Attrs(attrs...))\n}\n\nfunc (n noteDo) Assign(attrs ...field.AssignExpr) INoteDo {\n\treturn n.withDO(n.DO.Assign(attrs...))\n}\n\nfunc (n noteDo) Joins(fields ...field.RelationField) INoteDo {\n\tfor _, _f := range fields {\n\t\tn = *n.withDO(n.DO.Joins(_f))\n\t}\n\treturn &n\n}\n\nfunc (n noteDo) Preload(fields ...field.RelationField) INoteDo {\n\tfor _, _f := range fields {\n\t\tn = *n.withDO(n.DO.Preload(_f))\n\t}\n\treturn &n\n}\n\nfunc (n noteDo) FirstOrInit() (*model.Note, error) {\n\tif result, err := n.DO.FirstOrInit(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Note), nil\n\t}\n}\n\nfunc (n noteDo) FirstOrCreate() (*model.Note, error) {\n\tif result, err := n.DO.FirstOrCreate(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Note), nil\n\t}\n}\n\nfunc (n noteDo) FindByPage(offset int, limit int) (result []*model.Note, count int64, err error) {\n\tresult, err = n.Offset(offset).Limit(limit).Find()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif size := len(result); 0 < limit && 0 < size && size < limit {\n\t\tcount = int64(size + offset)\n\t\treturn\n\t}\n\n\tcount, err = n.Offset(-1).Limit(-1).Count()\n\treturn\n}\n\nfunc (n noteDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {\n\tcount, err = n.Count()\n\tif err != nil {\n\t\treturn\n\t}\n\n\terr = n.Offset(offset).Limit(limit).Scan(result)\n\treturn\n}\n\nfunc (n noteDo) Scan(result interface{}) (err error) {\n\treturn n.DO.Scan(result)\n}\n\nfunc (n noteDo) Delete(models ...*model.Note) (result gen.ResultInfo, err error) {\n\treturn n.DO.Delete(models)\n}\n\nfunc (n *noteDo) withDO(do gen.Dao) *noteDo {\n\tn.DO = *do.(*gen.DO)\n\treturn n\n}\n"
  },
  {
    "path": "internal/query/note_history.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage query\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\n\t\"gorm.io/gen\"\n\t\"gorm.io/gen/field\"\n\n\t\"gorm.io/plugin/dbresolver\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n)\n\nfunc newNoteHistory(db *gorm.DB, opts ...gen.DOOption) noteHistory {\n\t_noteHistory := noteHistory{}\n\n\t_noteHistory.noteHistoryDo.UseDB(db, opts...)\n\t_noteHistory.noteHistoryDo.UseModel(&model.NoteHistory{})\n\n\ttableName := _noteHistory.noteHistoryDo.TableName()\n\t_noteHistory.ALL = field.NewAsterisk(tableName)\n\t_noteHistory.ID = field.NewInt64(tableName, \"id\")\n\t_noteHistory.NoteID = field.NewInt64(tableName, \"note_id\")\n\t_noteHistory.VaultID = field.NewInt64(tableName, \"vault_id\")\n\t_noteHistory.Path = field.NewString(tableName, \"path\")\n\t_noteHistory.Content = field.NewString(tableName, \"content\")\n\t_noteHistory.ContentHash = field.NewString(tableName, \"content_hash\")\n\t_noteHistory.DiffPatch = field.NewString(tableName, \"diff_patch\")\n\t_noteHistory.ClientName = field.NewString(tableName, \"client_name\")\n\t_noteHistory.ClientType = field.NewString(tableName, \"client_type\")\n\t_noteHistory.ClientVersion = field.NewString(tableName, \"client_version\")\n\t_noteHistory.Version = field.NewInt64(tableName, \"version\")\n\t_noteHistory.CreatedAt = field.NewField(tableName, \"created_at\")\n\t_noteHistory.UpdatedAt = field.NewField(tableName, \"updated_at\")\n\n\t_noteHistory.fillFieldMap()\n\n\treturn _noteHistory\n}\n\ntype noteHistory struct {\n\tnoteHistoryDo noteHistoryDo\n\n\tALL         field.Asterisk\n\tID          field.Int64\n\tNoteID      field.Int64\n\tVaultID     field.Int64\n\tPath        field.String\n\tContent     field.String\n\tContentHash field.String\n\tDiffPatch   field.String\n\tClientName  field.String\n\tClientType  field.String\n\tClientVersion field.String\n\tVersion     field.Int64\n\tCreatedAt   field.Field\n\tUpdatedAt   field.Field\n\n\tfieldMap map[string]field.Expr\n}\n\nfunc (n noteHistory) Table(newTableName string) *noteHistory {\n\tn.noteHistoryDo.UseTable(newTableName)\n\treturn n.updateTableName(newTableName)\n}\n\nfunc (n noteHistory) As(alias string) *noteHistory {\n\tn.noteHistoryDo.DO = *(n.noteHistoryDo.As(alias).(*gen.DO))\n\treturn n.updateTableName(alias)\n}\n\nfunc (n *noteHistory) updateTableName(table string) *noteHistory {\n\tn.ALL = field.NewAsterisk(table)\n\tn.ID = field.NewInt64(table, \"id\")\n\tn.NoteID = field.NewInt64(table, \"note_id\")\n\tn.VaultID = field.NewInt64(table, \"vault_id\")\n\tn.Path = field.NewString(table, \"path\")\n\tn.Content = field.NewString(table, \"content\")\n\tn.ContentHash = field.NewString(table, \"content_hash\")\n\tn.DiffPatch = field.NewString(table, \"diff_patch\")\n\tn.ClientName = field.NewString(table, \"client_name\")\n\tn.ClientType = field.NewString(table, \"client_type\")\n\tn.ClientVersion = field.NewString(table, \"client_version\")\n\tn.Version = field.NewInt64(table, \"version\")\n\tn.CreatedAt = field.NewField(table, \"created_at\")\n\tn.UpdatedAt = field.NewField(table, \"updated_at\")\n\n\tn.fillFieldMap()\n\n\treturn n\n}\n\nfunc (n *noteHistory) WithContext(ctx context.Context) INoteHistoryDo {\n\treturn n.noteHistoryDo.WithContext(ctx)\n}\n\nfunc (n noteHistory) TableName() string { return n.noteHistoryDo.TableName() }\n\nfunc (n noteHistory) Alias() string { return n.noteHistoryDo.Alias() }\n\nfunc (n noteHistory) Columns(cols ...field.Expr) gen.Columns { return n.noteHistoryDo.Columns(cols...) }\n\nfunc (n *noteHistory) GetFieldByName(fieldName string) (field.OrderExpr, bool) {\n\t_f, ok := n.fieldMap[fieldName]\n\tif !ok || _f == nil {\n\t\treturn nil, false\n\t}\n\t_oe, ok := _f.(field.OrderExpr)\n\treturn _oe, ok\n}\n\nfunc (n *noteHistory) fillFieldMap() {\n\tn.fieldMap = make(map[string]field.Expr, 13)\n\tn.fieldMap[\"id\"] = n.ID\n\tn.fieldMap[\"note_id\"] = n.NoteID\n\tn.fieldMap[\"vault_id\"] = n.VaultID\n\tn.fieldMap[\"path\"] = n.Path\n\tn.fieldMap[\"content\"] = n.Content\n\tn.fieldMap[\"content_hash\"] = n.ContentHash\n\tn.fieldMap[\"diff_patch\"] = n.DiffPatch\n\tn.fieldMap[\"client_name\"] = n.ClientName\n\tn.fieldMap[\"client_type\"] = n.ClientType\n\tn.fieldMap[\"client_version\"] = n.ClientVersion\n\tn.fieldMap[\"version\"] = n.Version\n\tn.fieldMap[\"created_at\"] = n.CreatedAt\n\tn.fieldMap[\"updated_at\"] = n.UpdatedAt\n}\n\nfunc (n noteHistory) clone(db *gorm.DB) noteHistory {\n\tn.noteHistoryDo.ReplaceConnPool(db.Statement.ConnPool)\n\treturn n\n}\n\nfunc (n noteHistory) replaceDB(db *gorm.DB) noteHistory {\n\tn.noteHistoryDo.ReplaceDB(db)\n\treturn n\n}\n\ntype noteHistoryDo struct{ gen.DO }\n\ntype INoteHistoryDo interface {\n\tgen.SubQuery\n\tDebug() INoteHistoryDo\n\tWithContext(ctx context.Context) INoteHistoryDo\n\tWithResult(fc func(tx gen.Dao)) gen.ResultInfo\n\tReplaceDB(db *gorm.DB)\n\tReadDB() INoteHistoryDo\n\tWriteDB() INoteHistoryDo\n\tAs(alias string) gen.Dao\n\tSession(config *gorm.Session) INoteHistoryDo\n\tColumns(cols ...field.Expr) gen.Columns\n\tClauses(conds ...clause.Expression) INoteHistoryDo\n\tNot(conds ...gen.Condition) INoteHistoryDo\n\tOr(conds ...gen.Condition) INoteHistoryDo\n\tSelect(conds ...field.Expr) INoteHistoryDo\n\tWhere(conds ...gen.Condition) INoteHistoryDo\n\tOrder(conds ...field.Expr) INoteHistoryDo\n\tDistinct(cols ...field.Expr) INoteHistoryDo\n\tOmit(cols ...field.Expr) INoteHistoryDo\n\tJoin(table schema.Tabler, on ...field.Expr) INoteHistoryDo\n\tLeftJoin(table schema.Tabler, on ...field.Expr) INoteHistoryDo\n\tRightJoin(table schema.Tabler, on ...field.Expr) INoteHistoryDo\n\tGroup(cols ...field.Expr) INoteHistoryDo\n\tHaving(conds ...gen.Condition) INoteHistoryDo\n\tLimit(limit int) INoteHistoryDo\n\tOffset(offset int) INoteHistoryDo\n\tCount() (count int64, err error)\n\tScopes(funcs ...func(gen.Dao) gen.Dao) INoteHistoryDo\n\tUnscoped() INoteHistoryDo\n\tCreate(values ...*model.NoteHistory) error\n\tCreateInBatches(values []*model.NoteHistory, batchSize int) error\n\tSave(values ...*model.NoteHistory) error\n\tFirst() (*model.NoteHistory, error)\n\tTake() (*model.NoteHistory, error)\n\tLast() (*model.NoteHistory, error)\n\tFind() ([]*model.NoteHistory, error)\n\tFindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.NoteHistory, err error)\n\tFindInBatches(result *[]*model.NoteHistory, batchSize int, fc func(tx gen.Dao, batch int) error) error\n\tPluck(column field.Expr, dest interface{}) error\n\tDelete(...*model.NoteHistory) (info gen.ResultInfo, err error)\n\tUpdate(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdates(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdateColumns(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateFrom(q gen.SubQuery) gen.Dao\n\tAttrs(attrs ...field.AssignExpr) INoteHistoryDo\n\tAssign(attrs ...field.AssignExpr) INoteHistoryDo\n\tJoins(fields ...field.RelationField) INoteHistoryDo\n\tPreload(fields ...field.RelationField) INoteHistoryDo\n\tFirstOrInit() (*model.NoteHistory, error)\n\tFirstOrCreate() (*model.NoteHistory, error)\n\tFindByPage(offset int, limit int) (result []*model.NoteHistory, count int64, err error)\n\tScanByPage(result interface{}, offset int, limit int) (count int64, err error)\n\tRows() (*sql.Rows, error)\n\tRow() *sql.Row\n\tScan(result interface{}) (err error)\n\tReturning(value interface{}, columns ...string) INoteHistoryDo\n\tUnderlyingDB() *gorm.DB\n\tschema.Tabler\n}\n\nfunc (n noteHistoryDo) Debug() INoteHistoryDo {\n\treturn n.withDO(n.DO.Debug())\n}\n\nfunc (n noteHistoryDo) WithContext(ctx context.Context) INoteHistoryDo {\n\treturn n.withDO(n.DO.WithContext(ctx))\n}\n\nfunc (n noteHistoryDo) ReadDB() INoteHistoryDo {\n\treturn n.Clauses(dbresolver.Read)\n}\n\nfunc (n noteHistoryDo) WriteDB() INoteHistoryDo {\n\treturn n.Clauses(dbresolver.Write)\n}\n\nfunc (n noteHistoryDo) Session(config *gorm.Session) INoteHistoryDo {\n\treturn n.withDO(n.DO.Session(config))\n}\n\nfunc (n noteHistoryDo) Clauses(conds ...clause.Expression) INoteHistoryDo {\n\treturn n.withDO(n.DO.Clauses(conds...))\n}\n\nfunc (n noteHistoryDo) Returning(value interface{}, columns ...string) INoteHistoryDo {\n\treturn n.withDO(n.DO.Returning(value, columns...))\n}\n\nfunc (n noteHistoryDo) Not(conds ...gen.Condition) INoteHistoryDo {\n\treturn n.withDO(n.DO.Not(conds...))\n}\n\nfunc (n noteHistoryDo) Or(conds ...gen.Condition) INoteHistoryDo {\n\treturn n.withDO(n.DO.Or(conds...))\n}\n\nfunc (n noteHistoryDo) Select(conds ...field.Expr) INoteHistoryDo {\n\treturn n.withDO(n.DO.Select(conds...))\n}\n\nfunc (n noteHistoryDo) Where(conds ...gen.Condition) INoteHistoryDo {\n\treturn n.withDO(n.DO.Where(conds...))\n}\n\nfunc (n noteHistoryDo) Order(conds ...field.Expr) INoteHistoryDo {\n\treturn n.withDO(n.DO.Order(conds...))\n}\n\nfunc (n noteHistoryDo) Distinct(cols ...field.Expr) INoteHistoryDo {\n\treturn n.withDO(n.DO.Distinct(cols...))\n}\n\nfunc (n noteHistoryDo) Omit(cols ...field.Expr) INoteHistoryDo {\n\treturn n.withDO(n.DO.Omit(cols...))\n}\n\nfunc (n noteHistoryDo) Join(table schema.Tabler, on ...field.Expr) INoteHistoryDo {\n\treturn n.withDO(n.DO.Join(table, on...))\n}\n\nfunc (n noteHistoryDo) LeftJoin(table schema.Tabler, on ...field.Expr) INoteHistoryDo {\n\treturn n.withDO(n.DO.LeftJoin(table, on...))\n}\n\nfunc (n noteHistoryDo) RightJoin(table schema.Tabler, on ...field.Expr) INoteHistoryDo {\n\treturn n.withDO(n.DO.RightJoin(table, on...))\n}\n\nfunc (n noteHistoryDo) Group(cols ...field.Expr) INoteHistoryDo {\n\treturn n.withDO(n.DO.Group(cols...))\n}\n\nfunc (n noteHistoryDo) Having(conds ...gen.Condition) INoteHistoryDo {\n\treturn n.withDO(n.DO.Having(conds...))\n}\n\nfunc (n noteHistoryDo) Limit(limit int) INoteHistoryDo {\n\treturn n.withDO(n.DO.Limit(limit))\n}\n\nfunc (n noteHistoryDo) Offset(offset int) INoteHistoryDo {\n\treturn n.withDO(n.DO.Offset(offset))\n}\n\nfunc (n noteHistoryDo) Scopes(funcs ...func(gen.Dao) gen.Dao) INoteHistoryDo {\n\treturn n.withDO(n.DO.Scopes(funcs...))\n}\n\nfunc (n noteHistoryDo) Unscoped() INoteHistoryDo {\n\treturn n.withDO(n.DO.Unscoped())\n}\n\nfunc (n noteHistoryDo) Create(values ...*model.NoteHistory) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn n.DO.Create(values)\n}\n\nfunc (n noteHistoryDo) CreateInBatches(values []*model.NoteHistory, batchSize int) error {\n\treturn n.DO.CreateInBatches(values, batchSize)\n}\n\n// Save : !!! underlying implementation is different with GORM\n// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)\nfunc (n noteHistoryDo) Save(values ...*model.NoteHistory) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn n.DO.Save(values)\n}\n\nfunc (n noteHistoryDo) First() (*model.NoteHistory, error) {\n\tif result, err := n.DO.First(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.NoteHistory), nil\n\t}\n}\n\nfunc (n noteHistoryDo) Take() (*model.NoteHistory, error) {\n\tif result, err := n.DO.Take(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.NoteHistory), nil\n\t}\n}\n\nfunc (n noteHistoryDo) Last() (*model.NoteHistory, error) {\n\tif result, err := n.DO.Last(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.NoteHistory), nil\n\t}\n}\n\nfunc (n noteHistoryDo) Find() ([]*model.NoteHistory, error) {\n\tresult, err := n.DO.Find()\n\treturn result.([]*model.NoteHistory), err\n}\n\nfunc (n noteHistoryDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.NoteHistory, err error) {\n\tbuf := make([]*model.NoteHistory, 0, batchSize)\n\terr = n.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {\n\t\tdefer func() { results = append(results, buf...) }()\n\t\treturn fc(tx, batch)\n\t})\n\treturn results, err\n}\n\nfunc (n noteHistoryDo) FindInBatches(result *[]*model.NoteHistory, batchSize int, fc func(tx gen.Dao, batch int) error) error {\n\treturn n.DO.FindInBatches(result, batchSize, fc)\n}\n\nfunc (n noteHistoryDo) Attrs(attrs ...field.AssignExpr) INoteHistoryDo {\n\treturn n.withDO(n.DO.Attrs(attrs...))\n}\n\nfunc (n noteHistoryDo) Assign(attrs ...field.AssignExpr) INoteHistoryDo {\n\treturn n.withDO(n.DO.Assign(attrs...))\n}\n\nfunc (n noteHistoryDo) Joins(fields ...field.RelationField) INoteHistoryDo {\n\tfor _, _f := range fields {\n\t\tn = *n.withDO(n.DO.Joins(_f))\n\t}\n\treturn &n\n}\n\nfunc (n noteHistoryDo) Preload(fields ...field.RelationField) INoteHistoryDo {\n\tfor _, _f := range fields {\n\t\tn = *n.withDO(n.DO.Preload(_f))\n\t}\n\treturn &n\n}\n\nfunc (n noteHistoryDo) FirstOrInit() (*model.NoteHistory, error) {\n\tif result, err := n.DO.FirstOrInit(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.NoteHistory), nil\n\t}\n}\n\nfunc (n noteHistoryDo) FirstOrCreate() (*model.NoteHistory, error) {\n\tif result, err := n.DO.FirstOrCreate(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.NoteHistory), nil\n\t}\n}\n\nfunc (n noteHistoryDo) FindByPage(offset int, limit int) (result []*model.NoteHistory, count int64, err error) {\n\tresult, err = n.Offset(offset).Limit(limit).Find()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif size := len(result); 0 < limit && 0 < size && size < limit {\n\t\tcount = int64(size + offset)\n\t\treturn\n\t}\n\n\tcount, err = n.Offset(-1).Limit(-1).Count()\n\treturn\n}\n\nfunc (n noteHistoryDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {\n\tcount, err = n.Count()\n\tif err != nil {\n\t\treturn\n\t}\n\n\terr = n.Offset(offset).Limit(limit).Scan(result)\n\treturn\n}\n\nfunc (n noteHistoryDo) Scan(result interface{}) (err error) {\n\treturn n.DO.Scan(result)\n}\n\nfunc (n noteHistoryDo) Delete(models ...*model.NoteHistory) (result gen.ResultInfo, err error) {\n\treturn n.DO.Delete(models)\n}\n\nfunc (n *noteHistoryDo) withDO(do gen.Dao) *noteHistoryDo {\n\tn.DO = *do.(*gen.DO)\n\treturn n\n}\n"
  },
  {
    "path": "internal/query/note_link.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage query\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\n\t\"gorm.io/gen\"\n\t\"gorm.io/gen/field\"\n\n\t\"gorm.io/plugin/dbresolver\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n)\n\nfunc newNoteLink(db *gorm.DB, opts ...gen.DOOption) noteLink {\n\t_noteLink := noteLink{}\n\n\t_noteLink.noteLinkDo.UseDB(db, opts...)\n\t_noteLink.noteLinkDo.UseModel(&model.NoteLink{})\n\n\ttableName := _noteLink.noteLinkDo.TableName()\n\t_noteLink.ALL = field.NewAsterisk(tableName)\n\t_noteLink.ID = field.NewInt64(tableName, \"id\")\n\t_noteLink.SourceNoteID = field.NewInt64(tableName, \"source_note_id\")\n\t_noteLink.TargetPath = field.NewString(tableName, \"target_path\")\n\t_noteLink.TargetPathHash = field.NewString(tableName, \"target_path_hash\")\n\t_noteLink.LinkText = field.NewString(tableName, \"link_text\")\n\t_noteLink.IsEmbed = field.NewInt64(tableName, \"is_embed\")\n\t_noteLink.VaultID = field.NewInt64(tableName, \"vault_id\")\n\t_noteLink.UID = field.NewInt64(tableName, \"uid\")\n\t_noteLink.CreatedAt = field.NewField(tableName, \"created_at\")\n\n\t_noteLink.fillFieldMap()\n\n\treturn _noteLink\n}\n\ntype noteLink struct {\n\tnoteLinkDo noteLinkDo\n\n\tALL            field.Asterisk\n\tID             field.Int64\n\tSourceNoteID   field.Int64\n\tTargetPath     field.String\n\tTargetPathHash field.String\n\tLinkText       field.String\n\tIsEmbed        field.Int64\n\tVaultID        field.Int64\n\tUID            field.Int64\n\tCreatedAt      field.Field\n\n\tfieldMap map[string]field.Expr\n}\n\nfunc (n noteLink) Table(newTableName string) *noteLink {\n\tn.noteLinkDo.UseTable(newTableName)\n\treturn n.updateTableName(newTableName)\n}\n\nfunc (n noteLink) As(alias string) *noteLink {\n\tn.noteLinkDo.DO = *(n.noteLinkDo.As(alias).(*gen.DO))\n\treturn n.updateTableName(alias)\n}\n\nfunc (n *noteLink) updateTableName(table string) *noteLink {\n\tn.ALL = field.NewAsterisk(table)\n\tn.ID = field.NewInt64(table, \"id\")\n\tn.SourceNoteID = field.NewInt64(table, \"source_note_id\")\n\tn.TargetPath = field.NewString(table, \"target_path\")\n\tn.TargetPathHash = field.NewString(table, \"target_path_hash\")\n\tn.LinkText = field.NewString(table, \"link_text\")\n\tn.IsEmbed = field.NewInt64(table, \"is_embed\")\n\tn.VaultID = field.NewInt64(table, \"vault_id\")\n\tn.UID = field.NewInt64(table, \"uid\")\n\tn.CreatedAt = field.NewField(table, \"created_at\")\n\n\tn.fillFieldMap()\n\n\treturn n\n}\n\nfunc (n *noteLink) WithContext(ctx context.Context) INoteLinkDo { return n.noteLinkDo.WithContext(ctx) }\n\nfunc (n noteLink) TableName() string { return n.noteLinkDo.TableName() }\n\nfunc (n noteLink) Alias() string { return n.noteLinkDo.Alias() }\n\nfunc (n noteLink) Columns(cols ...field.Expr) gen.Columns { return n.noteLinkDo.Columns(cols...) }\n\nfunc (n *noteLink) GetFieldByName(fieldName string) (field.OrderExpr, bool) {\n\t_f, ok := n.fieldMap[fieldName]\n\tif !ok || _f == nil {\n\t\treturn nil, false\n\t}\n\t_oe, ok := _f.(field.OrderExpr)\n\treturn _oe, ok\n}\n\nfunc (n *noteLink) fillFieldMap() {\n\tn.fieldMap = make(map[string]field.Expr, 9)\n\tn.fieldMap[\"id\"] = n.ID\n\tn.fieldMap[\"source_note_id\"] = n.SourceNoteID\n\tn.fieldMap[\"target_path\"] = n.TargetPath\n\tn.fieldMap[\"target_path_hash\"] = n.TargetPathHash\n\tn.fieldMap[\"link_text\"] = n.LinkText\n\tn.fieldMap[\"is_embed\"] = n.IsEmbed\n\tn.fieldMap[\"vault_id\"] = n.VaultID\n\tn.fieldMap[\"uid\"] = n.UID\n\tn.fieldMap[\"created_at\"] = n.CreatedAt\n}\n\nfunc (n noteLink) clone(db *gorm.DB) noteLink {\n\tn.noteLinkDo.ReplaceConnPool(db.Statement.ConnPool)\n\treturn n\n}\n\nfunc (n noteLink) replaceDB(db *gorm.DB) noteLink {\n\tn.noteLinkDo.ReplaceDB(db)\n\treturn n\n}\n\ntype noteLinkDo struct{ gen.DO }\n\ntype INoteLinkDo interface {\n\tgen.SubQuery\n\tDebug() INoteLinkDo\n\tWithContext(ctx context.Context) INoteLinkDo\n\tWithResult(fc func(tx gen.Dao)) gen.ResultInfo\n\tReplaceDB(db *gorm.DB)\n\tReadDB() INoteLinkDo\n\tWriteDB() INoteLinkDo\n\tAs(alias string) gen.Dao\n\tSession(config *gorm.Session) INoteLinkDo\n\tColumns(cols ...field.Expr) gen.Columns\n\tClauses(conds ...clause.Expression) INoteLinkDo\n\tNot(conds ...gen.Condition) INoteLinkDo\n\tOr(conds ...gen.Condition) INoteLinkDo\n\tSelect(conds ...field.Expr) INoteLinkDo\n\tWhere(conds ...gen.Condition) INoteLinkDo\n\tOrder(conds ...field.Expr) INoteLinkDo\n\tDistinct(cols ...field.Expr) INoteLinkDo\n\tOmit(cols ...field.Expr) INoteLinkDo\n\tJoin(table schema.Tabler, on ...field.Expr) INoteLinkDo\n\tLeftJoin(table schema.Tabler, on ...field.Expr) INoteLinkDo\n\tRightJoin(table schema.Tabler, on ...field.Expr) INoteLinkDo\n\tGroup(cols ...field.Expr) INoteLinkDo\n\tHaving(conds ...gen.Condition) INoteLinkDo\n\tLimit(limit int) INoteLinkDo\n\tOffset(offset int) INoteLinkDo\n\tCount() (count int64, err error)\n\tScopes(funcs ...func(gen.Dao) gen.Dao) INoteLinkDo\n\tUnscoped() INoteLinkDo\n\tCreate(values ...*model.NoteLink) error\n\tCreateInBatches(values []*model.NoteLink, batchSize int) error\n\tSave(values ...*model.NoteLink) error\n\tFirst() (*model.NoteLink, error)\n\tTake() (*model.NoteLink, error)\n\tLast() (*model.NoteLink, error)\n\tFind() ([]*model.NoteLink, error)\n\tFindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.NoteLink, err error)\n\tFindInBatches(result *[]*model.NoteLink, batchSize int, fc func(tx gen.Dao, batch int) error) error\n\tPluck(column field.Expr, dest interface{}) error\n\tDelete(...*model.NoteLink) (info gen.ResultInfo, err error)\n\tUpdate(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdates(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdateColumns(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateFrom(q gen.SubQuery) gen.Dao\n\tAttrs(attrs ...field.AssignExpr) INoteLinkDo\n\tAssign(attrs ...field.AssignExpr) INoteLinkDo\n\tJoins(fields ...field.RelationField) INoteLinkDo\n\tPreload(fields ...field.RelationField) INoteLinkDo\n\tFirstOrInit() (*model.NoteLink, error)\n\tFirstOrCreate() (*model.NoteLink, error)\n\tFindByPage(offset int, limit int) (result []*model.NoteLink, count int64, err error)\n\tScanByPage(result interface{}, offset int, limit int) (count int64, err error)\n\tRows() (*sql.Rows, error)\n\tRow() *sql.Row\n\tScan(result interface{}) (err error)\n\tReturning(value interface{}, columns ...string) INoteLinkDo\n\tUnderlyingDB() *gorm.DB\n\tschema.Tabler\n}\n\nfunc (n noteLinkDo) Debug() INoteLinkDo {\n\treturn n.withDO(n.DO.Debug())\n}\n\nfunc (n noteLinkDo) WithContext(ctx context.Context) INoteLinkDo {\n\treturn n.withDO(n.DO.WithContext(ctx))\n}\n\nfunc (n noteLinkDo) ReadDB() INoteLinkDo {\n\treturn n.Clauses(dbresolver.Read)\n}\n\nfunc (n noteLinkDo) WriteDB() INoteLinkDo {\n\treturn n.Clauses(dbresolver.Write)\n}\n\nfunc (n noteLinkDo) Session(config *gorm.Session) INoteLinkDo {\n\treturn n.withDO(n.DO.Session(config))\n}\n\nfunc (n noteLinkDo) Clauses(conds ...clause.Expression) INoteLinkDo {\n\treturn n.withDO(n.DO.Clauses(conds...))\n}\n\nfunc (n noteLinkDo) Returning(value interface{}, columns ...string) INoteLinkDo {\n\treturn n.withDO(n.DO.Returning(value, columns...))\n}\n\nfunc (n noteLinkDo) Not(conds ...gen.Condition) INoteLinkDo {\n\treturn n.withDO(n.DO.Not(conds...))\n}\n\nfunc (n noteLinkDo) Or(conds ...gen.Condition) INoteLinkDo {\n\treturn n.withDO(n.DO.Or(conds...))\n}\n\nfunc (n noteLinkDo) Select(conds ...field.Expr) INoteLinkDo {\n\treturn n.withDO(n.DO.Select(conds...))\n}\n\nfunc (n noteLinkDo) Where(conds ...gen.Condition) INoteLinkDo {\n\treturn n.withDO(n.DO.Where(conds...))\n}\n\nfunc (n noteLinkDo) Order(conds ...field.Expr) INoteLinkDo {\n\treturn n.withDO(n.DO.Order(conds...))\n}\n\nfunc (n noteLinkDo) Distinct(cols ...field.Expr) INoteLinkDo {\n\treturn n.withDO(n.DO.Distinct(cols...))\n}\n\nfunc (n noteLinkDo) Omit(cols ...field.Expr) INoteLinkDo {\n\treturn n.withDO(n.DO.Omit(cols...))\n}\n\nfunc (n noteLinkDo) Join(table schema.Tabler, on ...field.Expr) INoteLinkDo {\n\treturn n.withDO(n.DO.Join(table, on...))\n}\n\nfunc (n noteLinkDo) LeftJoin(table schema.Tabler, on ...field.Expr) INoteLinkDo {\n\treturn n.withDO(n.DO.LeftJoin(table, on...))\n}\n\nfunc (n noteLinkDo) RightJoin(table schema.Tabler, on ...field.Expr) INoteLinkDo {\n\treturn n.withDO(n.DO.RightJoin(table, on...))\n}\n\nfunc (n noteLinkDo) Group(cols ...field.Expr) INoteLinkDo {\n\treturn n.withDO(n.DO.Group(cols...))\n}\n\nfunc (n noteLinkDo) Having(conds ...gen.Condition) INoteLinkDo {\n\treturn n.withDO(n.DO.Having(conds...))\n}\n\nfunc (n noteLinkDo) Limit(limit int) INoteLinkDo {\n\treturn n.withDO(n.DO.Limit(limit))\n}\n\nfunc (n noteLinkDo) Offset(offset int) INoteLinkDo {\n\treturn n.withDO(n.DO.Offset(offset))\n}\n\nfunc (n noteLinkDo) Scopes(funcs ...func(gen.Dao) gen.Dao) INoteLinkDo {\n\treturn n.withDO(n.DO.Scopes(funcs...))\n}\n\nfunc (n noteLinkDo) Unscoped() INoteLinkDo {\n\treturn n.withDO(n.DO.Unscoped())\n}\n\nfunc (n noteLinkDo) Create(values ...*model.NoteLink) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn n.DO.Create(values)\n}\n\nfunc (n noteLinkDo) CreateInBatches(values []*model.NoteLink, batchSize int) error {\n\treturn n.DO.CreateInBatches(values, batchSize)\n}\n\n// Save : !!! underlying implementation is different with GORM\n// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)\nfunc (n noteLinkDo) Save(values ...*model.NoteLink) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn n.DO.Save(values)\n}\n\nfunc (n noteLinkDo) First() (*model.NoteLink, error) {\n\tif result, err := n.DO.First(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.NoteLink), nil\n\t}\n}\n\nfunc (n noteLinkDo) Take() (*model.NoteLink, error) {\n\tif result, err := n.DO.Take(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.NoteLink), nil\n\t}\n}\n\nfunc (n noteLinkDo) Last() (*model.NoteLink, error) {\n\tif result, err := n.DO.Last(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.NoteLink), nil\n\t}\n}\n\nfunc (n noteLinkDo) Find() ([]*model.NoteLink, error) {\n\tresult, err := n.DO.Find()\n\treturn result.([]*model.NoteLink), err\n}\n\nfunc (n noteLinkDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.NoteLink, err error) {\n\tbuf := make([]*model.NoteLink, 0, batchSize)\n\terr = n.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {\n\t\tdefer func() { results = append(results, buf...) }()\n\t\treturn fc(tx, batch)\n\t})\n\treturn results, err\n}\n\nfunc (n noteLinkDo) FindInBatches(result *[]*model.NoteLink, batchSize int, fc func(tx gen.Dao, batch int) error) error {\n\treturn n.DO.FindInBatches(result, batchSize, fc)\n}\n\nfunc (n noteLinkDo) Attrs(attrs ...field.AssignExpr) INoteLinkDo {\n\treturn n.withDO(n.DO.Attrs(attrs...))\n}\n\nfunc (n noteLinkDo) Assign(attrs ...field.AssignExpr) INoteLinkDo {\n\treturn n.withDO(n.DO.Assign(attrs...))\n}\n\nfunc (n noteLinkDo) Joins(fields ...field.RelationField) INoteLinkDo {\n\tfor _, _f := range fields {\n\t\tn = *n.withDO(n.DO.Joins(_f))\n\t}\n\treturn &n\n}\n\nfunc (n noteLinkDo) Preload(fields ...field.RelationField) INoteLinkDo {\n\tfor _, _f := range fields {\n\t\tn = *n.withDO(n.DO.Preload(_f))\n\t}\n\treturn &n\n}\n\nfunc (n noteLinkDo) FirstOrInit() (*model.NoteLink, error) {\n\tif result, err := n.DO.FirstOrInit(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.NoteLink), nil\n\t}\n}\n\nfunc (n noteLinkDo) FirstOrCreate() (*model.NoteLink, error) {\n\tif result, err := n.DO.FirstOrCreate(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.NoteLink), nil\n\t}\n}\n\nfunc (n noteLinkDo) FindByPage(offset int, limit int) (result []*model.NoteLink, count int64, err error) {\n\tresult, err = n.Offset(offset).Limit(limit).Find()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif size := len(result); 0 < limit && 0 < size && size < limit {\n\t\tcount = int64(size + offset)\n\t\treturn\n\t}\n\n\tcount, err = n.Offset(-1).Limit(-1).Count()\n\treturn\n}\n\nfunc (n noteLinkDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {\n\tcount, err = n.Count()\n\tif err != nil {\n\t\treturn\n\t}\n\n\terr = n.Offset(offset).Limit(limit).Scan(result)\n\treturn\n}\n\nfunc (n noteLinkDo) Scan(result interface{}) (err error) {\n\treturn n.DO.Scan(result)\n}\n\nfunc (n noteLinkDo) Delete(models ...*model.NoteLink) (result gen.ResultInfo, err error) {\n\treturn n.DO.Delete(models)\n}\n\nfunc (n *noteLinkDo) withDO(do gen.Dao) *noteLinkDo {\n\tn.DO = *do.(*gen.DO)\n\treturn n\n}\n"
  },
  {
    "path": "internal/query/setting.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage query\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\n\t\"gorm.io/gen\"\n\t\"gorm.io/gen/field\"\n\n\t\"gorm.io/plugin/dbresolver\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n)\n\nfunc newSetting(db *gorm.DB, opts ...gen.DOOption) setting {\n\t_setting := setting{}\n\n\t_setting.settingDo.UseDB(db, opts...)\n\t_setting.settingDo.UseModel(&model.Setting{})\n\n\ttableName := _setting.settingDo.TableName()\n\t_setting.ALL = field.NewAsterisk(tableName)\n\t_setting.ID = field.NewInt64(tableName, \"id\")\n\t_setting.VaultID = field.NewInt64(tableName, \"vault_id\")\n\t_setting.Action = field.NewString(tableName, \"action\")\n\t_setting.Path = field.NewString(tableName, \"path\")\n\t_setting.PathHash = field.NewString(tableName, \"path_hash\")\n\t_setting.Content = field.NewString(tableName, \"content\")\n\t_setting.ContentHash = field.NewString(tableName, \"content_hash\")\n\t_setting.Size = field.NewInt64(tableName, \"size\")\n\t_setting.Ctime = field.NewInt64(tableName, \"ctime\")\n\t_setting.Mtime = field.NewInt64(tableName, \"mtime\")\n\t_setting.UpdatedTimestamp = field.NewInt64(tableName, \"updated_timestamp\")\n\t_setting.CreatedAt = field.NewField(tableName, \"created_at\")\n\t_setting.UpdatedAt = field.NewField(tableName, \"updated_at\")\n\n\t_setting.fillFieldMap()\n\n\treturn _setting\n}\n\ntype setting struct {\n\tsettingDo settingDo\n\n\tALL              field.Asterisk\n\tID               field.Int64\n\tVaultID          field.Int64\n\tAction           field.String\n\tPath             field.String\n\tPathHash         field.String\n\tContent          field.String\n\tContentHash      field.String\n\tSize             field.Int64\n\tCtime            field.Int64\n\tMtime            field.Int64\n\tUpdatedTimestamp field.Int64\n\tCreatedAt        field.Field\n\tUpdatedAt        field.Field\n\n\tfieldMap map[string]field.Expr\n}\n\nfunc (s setting) Table(newTableName string) *setting {\n\ts.settingDo.UseTable(newTableName)\n\treturn s.updateTableName(newTableName)\n}\n\nfunc (s setting) As(alias string) *setting {\n\ts.settingDo.DO = *(s.settingDo.As(alias).(*gen.DO))\n\treturn s.updateTableName(alias)\n}\n\nfunc (s *setting) updateTableName(table string) *setting {\n\ts.ALL = field.NewAsterisk(table)\n\ts.ID = field.NewInt64(table, \"id\")\n\ts.VaultID = field.NewInt64(table, \"vault_id\")\n\ts.Action = field.NewString(table, \"action\")\n\ts.Path = field.NewString(table, \"path\")\n\ts.PathHash = field.NewString(table, \"path_hash\")\n\ts.Content = field.NewString(table, \"content\")\n\ts.ContentHash = field.NewString(table, \"content_hash\")\n\ts.Size = field.NewInt64(table, \"size\")\n\ts.Ctime = field.NewInt64(table, \"ctime\")\n\ts.Mtime = field.NewInt64(table, \"mtime\")\n\ts.UpdatedTimestamp = field.NewInt64(table, \"updated_timestamp\")\n\ts.CreatedAt = field.NewField(table, \"created_at\")\n\ts.UpdatedAt = field.NewField(table, \"updated_at\")\n\n\ts.fillFieldMap()\n\n\treturn s\n}\n\nfunc (s *setting) WithContext(ctx context.Context) ISettingDo { return s.settingDo.WithContext(ctx) }\n\nfunc (s setting) TableName() string { return s.settingDo.TableName() }\n\nfunc (s setting) Alias() string { return s.settingDo.Alias() }\n\nfunc (s setting) Columns(cols ...field.Expr) gen.Columns { return s.settingDo.Columns(cols...) }\n\nfunc (s *setting) GetFieldByName(fieldName string) (field.OrderExpr, bool) {\n\t_f, ok := s.fieldMap[fieldName]\n\tif !ok || _f == nil {\n\t\treturn nil, false\n\t}\n\t_oe, ok := _f.(field.OrderExpr)\n\treturn _oe, ok\n}\n\nfunc (s *setting) fillFieldMap() {\n\ts.fieldMap = make(map[string]field.Expr, 13)\n\ts.fieldMap[\"id\"] = s.ID\n\ts.fieldMap[\"vault_id\"] = s.VaultID\n\ts.fieldMap[\"action\"] = s.Action\n\ts.fieldMap[\"path\"] = s.Path\n\ts.fieldMap[\"path_hash\"] = s.PathHash\n\ts.fieldMap[\"content\"] = s.Content\n\ts.fieldMap[\"content_hash\"] = s.ContentHash\n\ts.fieldMap[\"size\"] = s.Size\n\ts.fieldMap[\"ctime\"] = s.Ctime\n\ts.fieldMap[\"mtime\"] = s.Mtime\n\ts.fieldMap[\"updated_timestamp\"] = s.UpdatedTimestamp\n\ts.fieldMap[\"created_at\"] = s.CreatedAt\n\ts.fieldMap[\"updated_at\"] = s.UpdatedAt\n}\n\nfunc (s setting) clone(db *gorm.DB) setting {\n\ts.settingDo.ReplaceConnPool(db.Statement.ConnPool)\n\treturn s\n}\n\nfunc (s setting) replaceDB(db *gorm.DB) setting {\n\ts.settingDo.ReplaceDB(db)\n\treturn s\n}\n\ntype settingDo struct{ gen.DO }\n\ntype ISettingDo interface {\n\tgen.SubQuery\n\tDebug() ISettingDo\n\tWithContext(ctx context.Context) ISettingDo\n\tWithResult(fc func(tx gen.Dao)) gen.ResultInfo\n\tReplaceDB(db *gorm.DB)\n\tReadDB() ISettingDo\n\tWriteDB() ISettingDo\n\tAs(alias string) gen.Dao\n\tSession(config *gorm.Session) ISettingDo\n\tColumns(cols ...field.Expr) gen.Columns\n\tClauses(conds ...clause.Expression) ISettingDo\n\tNot(conds ...gen.Condition) ISettingDo\n\tOr(conds ...gen.Condition) ISettingDo\n\tSelect(conds ...field.Expr) ISettingDo\n\tWhere(conds ...gen.Condition) ISettingDo\n\tOrder(conds ...field.Expr) ISettingDo\n\tDistinct(cols ...field.Expr) ISettingDo\n\tOmit(cols ...field.Expr) ISettingDo\n\tJoin(table schema.Tabler, on ...field.Expr) ISettingDo\n\tLeftJoin(table schema.Tabler, on ...field.Expr) ISettingDo\n\tRightJoin(table schema.Tabler, on ...field.Expr) ISettingDo\n\tGroup(cols ...field.Expr) ISettingDo\n\tHaving(conds ...gen.Condition) ISettingDo\n\tLimit(limit int) ISettingDo\n\tOffset(offset int) ISettingDo\n\tCount() (count int64, err error)\n\tScopes(funcs ...func(gen.Dao) gen.Dao) ISettingDo\n\tUnscoped() ISettingDo\n\tCreate(values ...*model.Setting) error\n\tCreateInBatches(values []*model.Setting, batchSize int) error\n\tSave(values ...*model.Setting) error\n\tFirst() (*model.Setting, error)\n\tTake() (*model.Setting, error)\n\tLast() (*model.Setting, error)\n\tFind() ([]*model.Setting, error)\n\tFindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Setting, err error)\n\tFindInBatches(result *[]*model.Setting, batchSize int, fc func(tx gen.Dao, batch int) error) error\n\tPluck(column field.Expr, dest interface{}) error\n\tDelete(...*model.Setting) (info gen.ResultInfo, err error)\n\tUpdate(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdates(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdateColumns(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateFrom(q gen.SubQuery) gen.Dao\n\tAttrs(attrs ...field.AssignExpr) ISettingDo\n\tAssign(attrs ...field.AssignExpr) ISettingDo\n\tJoins(fields ...field.RelationField) ISettingDo\n\tPreload(fields ...field.RelationField) ISettingDo\n\tFirstOrInit() (*model.Setting, error)\n\tFirstOrCreate() (*model.Setting, error)\n\tFindByPage(offset int, limit int) (result []*model.Setting, count int64, err error)\n\tScanByPage(result interface{}, offset int, limit int) (count int64, err error)\n\tRows() (*sql.Rows, error)\n\tRow() *sql.Row\n\tScan(result interface{}) (err error)\n\tReturning(value interface{}, columns ...string) ISettingDo\n\tUnderlyingDB() *gorm.DB\n\tschema.Tabler\n}\n\nfunc (s settingDo) Debug() ISettingDo {\n\treturn s.withDO(s.DO.Debug())\n}\n\nfunc (s settingDo) WithContext(ctx context.Context) ISettingDo {\n\treturn s.withDO(s.DO.WithContext(ctx))\n}\n\nfunc (s settingDo) ReadDB() ISettingDo {\n\treturn s.Clauses(dbresolver.Read)\n}\n\nfunc (s settingDo) WriteDB() ISettingDo {\n\treturn s.Clauses(dbresolver.Write)\n}\n\nfunc (s settingDo) Session(config *gorm.Session) ISettingDo {\n\treturn s.withDO(s.DO.Session(config))\n}\n\nfunc (s settingDo) Clauses(conds ...clause.Expression) ISettingDo {\n\treturn s.withDO(s.DO.Clauses(conds...))\n}\n\nfunc (s settingDo) Returning(value interface{}, columns ...string) ISettingDo {\n\treturn s.withDO(s.DO.Returning(value, columns...))\n}\n\nfunc (s settingDo) Not(conds ...gen.Condition) ISettingDo {\n\treturn s.withDO(s.DO.Not(conds...))\n}\n\nfunc (s settingDo) Or(conds ...gen.Condition) ISettingDo {\n\treturn s.withDO(s.DO.Or(conds...))\n}\n\nfunc (s settingDo) Select(conds ...field.Expr) ISettingDo {\n\treturn s.withDO(s.DO.Select(conds...))\n}\n\nfunc (s settingDo) Where(conds ...gen.Condition) ISettingDo {\n\treturn s.withDO(s.DO.Where(conds...))\n}\n\nfunc (s settingDo) Order(conds ...field.Expr) ISettingDo {\n\treturn s.withDO(s.DO.Order(conds...))\n}\n\nfunc (s settingDo) Distinct(cols ...field.Expr) ISettingDo {\n\treturn s.withDO(s.DO.Distinct(cols...))\n}\n\nfunc (s settingDo) Omit(cols ...field.Expr) ISettingDo {\n\treturn s.withDO(s.DO.Omit(cols...))\n}\n\nfunc (s settingDo) Join(table schema.Tabler, on ...field.Expr) ISettingDo {\n\treturn s.withDO(s.DO.Join(table, on...))\n}\n\nfunc (s settingDo) LeftJoin(table schema.Tabler, on ...field.Expr) ISettingDo {\n\treturn s.withDO(s.DO.LeftJoin(table, on...))\n}\n\nfunc (s settingDo) RightJoin(table schema.Tabler, on ...field.Expr) ISettingDo {\n\treturn s.withDO(s.DO.RightJoin(table, on...))\n}\n\nfunc (s settingDo) Group(cols ...field.Expr) ISettingDo {\n\treturn s.withDO(s.DO.Group(cols...))\n}\n\nfunc (s settingDo) Having(conds ...gen.Condition) ISettingDo {\n\treturn s.withDO(s.DO.Having(conds...))\n}\n\nfunc (s settingDo) Limit(limit int) ISettingDo {\n\treturn s.withDO(s.DO.Limit(limit))\n}\n\nfunc (s settingDo) Offset(offset int) ISettingDo {\n\treturn s.withDO(s.DO.Offset(offset))\n}\n\nfunc (s settingDo) Scopes(funcs ...func(gen.Dao) gen.Dao) ISettingDo {\n\treturn s.withDO(s.DO.Scopes(funcs...))\n}\n\nfunc (s settingDo) Unscoped() ISettingDo {\n\treturn s.withDO(s.DO.Unscoped())\n}\n\nfunc (s settingDo) Create(values ...*model.Setting) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn s.DO.Create(values)\n}\n\nfunc (s settingDo) CreateInBatches(values []*model.Setting, batchSize int) error {\n\treturn s.DO.CreateInBatches(values, batchSize)\n}\n\n// Save : !!! underlying implementation is different with GORM\n// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)\nfunc (s settingDo) Save(values ...*model.Setting) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn s.DO.Save(values)\n}\n\nfunc (s settingDo) First() (*model.Setting, error) {\n\tif result, err := s.DO.First(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Setting), nil\n\t}\n}\n\nfunc (s settingDo) Take() (*model.Setting, error) {\n\tif result, err := s.DO.Take(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Setting), nil\n\t}\n}\n\nfunc (s settingDo) Last() (*model.Setting, error) {\n\tif result, err := s.DO.Last(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Setting), nil\n\t}\n}\n\nfunc (s settingDo) Find() ([]*model.Setting, error) {\n\tresult, err := s.DO.Find()\n\treturn result.([]*model.Setting), err\n}\n\nfunc (s settingDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Setting, err error) {\n\tbuf := make([]*model.Setting, 0, batchSize)\n\terr = s.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {\n\t\tdefer func() { results = append(results, buf...) }()\n\t\treturn fc(tx, batch)\n\t})\n\treturn results, err\n}\n\nfunc (s settingDo) FindInBatches(result *[]*model.Setting, batchSize int, fc func(tx gen.Dao, batch int) error) error {\n\treturn s.DO.FindInBatches(result, batchSize, fc)\n}\n\nfunc (s settingDo) Attrs(attrs ...field.AssignExpr) ISettingDo {\n\treturn s.withDO(s.DO.Attrs(attrs...))\n}\n\nfunc (s settingDo) Assign(attrs ...field.AssignExpr) ISettingDo {\n\treturn s.withDO(s.DO.Assign(attrs...))\n}\n\nfunc (s settingDo) Joins(fields ...field.RelationField) ISettingDo {\n\tfor _, _f := range fields {\n\t\ts = *s.withDO(s.DO.Joins(_f))\n\t}\n\treturn &s\n}\n\nfunc (s settingDo) Preload(fields ...field.RelationField) ISettingDo {\n\tfor _, _f := range fields {\n\t\ts = *s.withDO(s.DO.Preload(_f))\n\t}\n\treturn &s\n}\n\nfunc (s settingDo) FirstOrInit() (*model.Setting, error) {\n\tif result, err := s.DO.FirstOrInit(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Setting), nil\n\t}\n}\n\nfunc (s settingDo) FirstOrCreate() (*model.Setting, error) {\n\tif result, err := s.DO.FirstOrCreate(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Setting), nil\n\t}\n}\n\nfunc (s settingDo) FindByPage(offset int, limit int) (result []*model.Setting, count int64, err error) {\n\tresult, err = s.Offset(offset).Limit(limit).Find()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif size := len(result); 0 < limit && 0 < size && size < limit {\n\t\tcount = int64(size + offset)\n\t\treturn\n\t}\n\n\tcount, err = s.Offset(-1).Limit(-1).Count()\n\treturn\n}\n\nfunc (s settingDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {\n\tcount, err = s.Count()\n\tif err != nil {\n\t\treturn\n\t}\n\n\terr = s.Offset(offset).Limit(limit).Scan(result)\n\treturn\n}\n\nfunc (s settingDo) Scan(result interface{}) (err error) {\n\treturn s.DO.Scan(result)\n}\n\nfunc (s settingDo) Delete(models ...*model.Setting) (result gen.ResultInfo, err error) {\n\treturn s.DO.Delete(models)\n}\n\nfunc (s *settingDo) withDO(do gen.Dao) *settingDo {\n\ts.DO = *do.(*gen.DO)\n\treturn s\n}\n"
  },
  {
    "path": "internal/query/storage.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage query\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\n\t\"gorm.io/gen\"\n\t\"gorm.io/gen/field\"\n\n\t\"gorm.io/plugin/dbresolver\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n)\n\nfunc newStorage(db *gorm.DB, opts ...gen.DOOption) storage {\n\t_storage := storage{}\n\n\t_storage.storageDo.UseDB(db, opts...)\n\t_storage.storageDo.UseModel(&model.Storage{})\n\n\ttableName := _storage.storageDo.TableName()\n\t_storage.ALL = field.NewAsterisk(tableName)\n\t_storage.ID = field.NewInt64(tableName, \"id\")\n\t_storage.UID = field.NewInt64(tableName, \"uid\")\n\t_storage.Type = field.NewString(tableName, \"type\")\n\t_storage.Endpoint = field.NewString(tableName, \"endpoint\")\n\t_storage.Region = field.NewString(tableName, \"region\")\n\t_storage.AccountID = field.NewString(tableName, \"account_id\")\n\t_storage.BucketName = field.NewString(tableName, \"bucket_name\")\n\t_storage.AccessKeyID = field.NewString(tableName, \"access_key_id\")\n\t_storage.AccessKeySecret = field.NewString(tableName, \"access_key_secret\")\n\t_storage.CustomPath = field.NewString(tableName, \"custom_path\")\n\t_storage.AccessURLPrefix = field.NewString(tableName, \"access_url_prefix\")\n\t_storage.User = field.NewString(tableName, \"user\")\n\t_storage.Password = field.NewString(tableName, \"password\")\n\t_storage.IsEnabled = field.NewInt64(tableName, \"is_enabled\")\n\t_storage.IsDeleted = field.NewInt64(tableName, \"is_deleted\")\n\t_storage.CreatedAt = field.NewField(tableName, \"created_at\")\n\t_storage.UpdatedAt = field.NewField(tableName, \"updated_at\")\n\t_storage.DeletedAt = field.NewField(tableName, \"deleted_at\")\n\n\t_storage.fillFieldMap()\n\n\treturn _storage\n}\n\ntype storage struct {\n\tstorageDo storageDo\n\n\tALL             field.Asterisk\n\tID              field.Int64\n\tUID             field.Int64\n\tType            field.String\n\tEndpoint        field.String\n\tRegion          field.String\n\tAccountID       field.String\n\tBucketName      field.String\n\tAccessKeyID     field.String\n\tAccessKeySecret field.String\n\tCustomPath      field.String\n\tAccessURLPrefix field.String\n\tUser            field.String\n\tPassword        field.String\n\tIsEnabled       field.Int64\n\tIsDeleted       field.Int64\n\tCreatedAt       field.Field\n\tUpdatedAt       field.Field\n\tDeletedAt       field.Field\n\n\tfieldMap map[string]field.Expr\n}\n\nfunc (s storage) Table(newTableName string) *storage {\n\ts.storageDo.UseTable(newTableName)\n\treturn s.updateTableName(newTableName)\n}\n\nfunc (s storage) As(alias string) *storage {\n\ts.storageDo.DO = *(s.storageDo.As(alias).(*gen.DO))\n\treturn s.updateTableName(alias)\n}\n\nfunc (s *storage) updateTableName(table string) *storage {\n\ts.ALL = field.NewAsterisk(table)\n\ts.ID = field.NewInt64(table, \"id\")\n\ts.UID = field.NewInt64(table, \"uid\")\n\ts.Type = field.NewString(table, \"type\")\n\ts.Endpoint = field.NewString(table, \"endpoint\")\n\ts.Region = field.NewString(table, \"region\")\n\ts.AccountID = field.NewString(table, \"account_id\")\n\ts.BucketName = field.NewString(table, \"bucket_name\")\n\ts.AccessKeyID = field.NewString(table, \"access_key_id\")\n\ts.AccessKeySecret = field.NewString(table, \"access_key_secret\")\n\ts.CustomPath = field.NewString(table, \"custom_path\")\n\ts.AccessURLPrefix = field.NewString(table, \"access_url_prefix\")\n\ts.User = field.NewString(table, \"user\")\n\ts.Password = field.NewString(table, \"password\")\n\ts.IsEnabled = field.NewInt64(table, \"is_enabled\")\n\ts.IsDeleted = field.NewInt64(table, \"is_deleted\")\n\ts.CreatedAt = field.NewField(table, \"created_at\")\n\ts.UpdatedAt = field.NewField(table, \"updated_at\")\n\ts.DeletedAt = field.NewField(table, \"deleted_at\")\n\n\ts.fillFieldMap()\n\n\treturn s\n}\n\nfunc (s *storage) WithContext(ctx context.Context) IStorageDo { return s.storageDo.WithContext(ctx) }\n\nfunc (s storage) TableName() string { return s.storageDo.TableName() }\n\nfunc (s storage) Alias() string { return s.storageDo.Alias() }\n\nfunc (s storage) Columns(cols ...field.Expr) gen.Columns { return s.storageDo.Columns(cols...) }\n\nfunc (s *storage) GetFieldByName(fieldName string) (field.OrderExpr, bool) {\n\t_f, ok := s.fieldMap[fieldName]\n\tif !ok || _f == nil {\n\t\treturn nil, false\n\t}\n\t_oe, ok := _f.(field.OrderExpr)\n\treturn _oe, ok\n}\n\nfunc (s *storage) fillFieldMap() {\n\ts.fieldMap = make(map[string]field.Expr, 18)\n\ts.fieldMap[\"id\"] = s.ID\n\ts.fieldMap[\"uid\"] = s.UID\n\ts.fieldMap[\"type\"] = s.Type\n\ts.fieldMap[\"endpoint\"] = s.Endpoint\n\ts.fieldMap[\"region\"] = s.Region\n\ts.fieldMap[\"account_id\"] = s.AccountID\n\ts.fieldMap[\"bucket_name\"] = s.BucketName\n\ts.fieldMap[\"access_key_id\"] = s.AccessKeyID\n\ts.fieldMap[\"access_key_secret\"] = s.AccessKeySecret\n\ts.fieldMap[\"custom_path\"] = s.CustomPath\n\ts.fieldMap[\"access_url_prefix\"] = s.AccessURLPrefix\n\ts.fieldMap[\"user\"] = s.User\n\ts.fieldMap[\"password\"] = s.Password\n\ts.fieldMap[\"is_enabled\"] = s.IsEnabled\n\ts.fieldMap[\"is_deleted\"] = s.IsDeleted\n\ts.fieldMap[\"created_at\"] = s.CreatedAt\n\ts.fieldMap[\"updated_at\"] = s.UpdatedAt\n\ts.fieldMap[\"deleted_at\"] = s.DeletedAt\n}\n\nfunc (s storage) clone(db *gorm.DB) storage {\n\ts.storageDo.ReplaceConnPool(db.Statement.ConnPool)\n\treturn s\n}\n\nfunc (s storage) replaceDB(db *gorm.DB) storage {\n\ts.storageDo.ReplaceDB(db)\n\treturn s\n}\n\ntype storageDo struct{ gen.DO }\n\ntype IStorageDo interface {\n\tgen.SubQuery\n\tDebug() IStorageDo\n\tWithContext(ctx context.Context) IStorageDo\n\tWithResult(fc func(tx gen.Dao)) gen.ResultInfo\n\tReplaceDB(db *gorm.DB)\n\tReadDB() IStorageDo\n\tWriteDB() IStorageDo\n\tAs(alias string) gen.Dao\n\tSession(config *gorm.Session) IStorageDo\n\tColumns(cols ...field.Expr) gen.Columns\n\tClauses(conds ...clause.Expression) IStorageDo\n\tNot(conds ...gen.Condition) IStorageDo\n\tOr(conds ...gen.Condition) IStorageDo\n\tSelect(conds ...field.Expr) IStorageDo\n\tWhere(conds ...gen.Condition) IStorageDo\n\tOrder(conds ...field.Expr) IStorageDo\n\tDistinct(cols ...field.Expr) IStorageDo\n\tOmit(cols ...field.Expr) IStorageDo\n\tJoin(table schema.Tabler, on ...field.Expr) IStorageDo\n\tLeftJoin(table schema.Tabler, on ...field.Expr) IStorageDo\n\tRightJoin(table schema.Tabler, on ...field.Expr) IStorageDo\n\tGroup(cols ...field.Expr) IStorageDo\n\tHaving(conds ...gen.Condition) IStorageDo\n\tLimit(limit int) IStorageDo\n\tOffset(offset int) IStorageDo\n\tCount() (count int64, err error)\n\tScopes(funcs ...func(gen.Dao) gen.Dao) IStorageDo\n\tUnscoped() IStorageDo\n\tCreate(values ...*model.Storage) error\n\tCreateInBatches(values []*model.Storage, batchSize int) error\n\tSave(values ...*model.Storage) error\n\tFirst() (*model.Storage, error)\n\tTake() (*model.Storage, error)\n\tLast() (*model.Storage, error)\n\tFind() ([]*model.Storage, error)\n\tFindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Storage, err error)\n\tFindInBatches(result *[]*model.Storage, batchSize int, fc func(tx gen.Dao, batch int) error) error\n\tPluck(column field.Expr, dest interface{}) error\n\tDelete(...*model.Storage) (info gen.ResultInfo, err error)\n\tUpdate(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdates(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdateColumns(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateFrom(q gen.SubQuery) gen.Dao\n\tAttrs(attrs ...field.AssignExpr) IStorageDo\n\tAssign(attrs ...field.AssignExpr) IStorageDo\n\tJoins(fields ...field.RelationField) IStorageDo\n\tPreload(fields ...field.RelationField) IStorageDo\n\tFirstOrInit() (*model.Storage, error)\n\tFirstOrCreate() (*model.Storage, error)\n\tFindByPage(offset int, limit int) (result []*model.Storage, count int64, err error)\n\tScanByPage(result interface{}, offset int, limit int) (count int64, err error)\n\tRows() (*sql.Rows, error)\n\tRow() *sql.Row\n\tScan(result interface{}) (err error)\n\tReturning(value interface{}, columns ...string) IStorageDo\n\tUnderlyingDB() *gorm.DB\n\tschema.Tabler\n}\n\nfunc (s storageDo) Debug() IStorageDo {\n\treturn s.withDO(s.DO.Debug())\n}\n\nfunc (s storageDo) WithContext(ctx context.Context) IStorageDo {\n\treturn s.withDO(s.DO.WithContext(ctx))\n}\n\nfunc (s storageDo) ReadDB() IStorageDo {\n\treturn s.Clauses(dbresolver.Read)\n}\n\nfunc (s storageDo) WriteDB() IStorageDo {\n\treturn s.Clauses(dbresolver.Write)\n}\n\nfunc (s storageDo) Session(config *gorm.Session) IStorageDo {\n\treturn s.withDO(s.DO.Session(config))\n}\n\nfunc (s storageDo) Clauses(conds ...clause.Expression) IStorageDo {\n\treturn s.withDO(s.DO.Clauses(conds...))\n}\n\nfunc (s storageDo) Returning(value interface{}, columns ...string) IStorageDo {\n\treturn s.withDO(s.DO.Returning(value, columns...))\n}\n\nfunc (s storageDo) Not(conds ...gen.Condition) IStorageDo {\n\treturn s.withDO(s.DO.Not(conds...))\n}\n\nfunc (s storageDo) Or(conds ...gen.Condition) IStorageDo {\n\treturn s.withDO(s.DO.Or(conds...))\n}\n\nfunc (s storageDo) Select(conds ...field.Expr) IStorageDo {\n\treturn s.withDO(s.DO.Select(conds...))\n}\n\nfunc (s storageDo) Where(conds ...gen.Condition) IStorageDo {\n\treturn s.withDO(s.DO.Where(conds...))\n}\n\nfunc (s storageDo) Order(conds ...field.Expr) IStorageDo {\n\treturn s.withDO(s.DO.Order(conds...))\n}\n\nfunc (s storageDo) Distinct(cols ...field.Expr) IStorageDo {\n\treturn s.withDO(s.DO.Distinct(cols...))\n}\n\nfunc (s storageDo) Omit(cols ...field.Expr) IStorageDo {\n\treturn s.withDO(s.DO.Omit(cols...))\n}\n\nfunc (s storageDo) Join(table schema.Tabler, on ...field.Expr) IStorageDo {\n\treturn s.withDO(s.DO.Join(table, on...))\n}\n\nfunc (s storageDo) LeftJoin(table schema.Tabler, on ...field.Expr) IStorageDo {\n\treturn s.withDO(s.DO.LeftJoin(table, on...))\n}\n\nfunc (s storageDo) RightJoin(table schema.Tabler, on ...field.Expr) IStorageDo {\n\treturn s.withDO(s.DO.RightJoin(table, on...))\n}\n\nfunc (s storageDo) Group(cols ...field.Expr) IStorageDo {\n\treturn s.withDO(s.DO.Group(cols...))\n}\n\nfunc (s storageDo) Having(conds ...gen.Condition) IStorageDo {\n\treturn s.withDO(s.DO.Having(conds...))\n}\n\nfunc (s storageDo) Limit(limit int) IStorageDo {\n\treturn s.withDO(s.DO.Limit(limit))\n}\n\nfunc (s storageDo) Offset(offset int) IStorageDo {\n\treturn s.withDO(s.DO.Offset(offset))\n}\n\nfunc (s storageDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IStorageDo {\n\treturn s.withDO(s.DO.Scopes(funcs...))\n}\n\nfunc (s storageDo) Unscoped() IStorageDo {\n\treturn s.withDO(s.DO.Unscoped())\n}\n\nfunc (s storageDo) Create(values ...*model.Storage) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn s.DO.Create(values)\n}\n\nfunc (s storageDo) CreateInBatches(values []*model.Storage, batchSize int) error {\n\treturn s.DO.CreateInBatches(values, batchSize)\n}\n\n// Save : !!! underlying implementation is different with GORM\n// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)\nfunc (s storageDo) Save(values ...*model.Storage) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn s.DO.Save(values)\n}\n\nfunc (s storageDo) First() (*model.Storage, error) {\n\tif result, err := s.DO.First(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Storage), nil\n\t}\n}\n\nfunc (s storageDo) Take() (*model.Storage, error) {\n\tif result, err := s.DO.Take(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Storage), nil\n\t}\n}\n\nfunc (s storageDo) Last() (*model.Storage, error) {\n\tif result, err := s.DO.Last(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Storage), nil\n\t}\n}\n\nfunc (s storageDo) Find() ([]*model.Storage, error) {\n\tresult, err := s.DO.Find()\n\treturn result.([]*model.Storage), err\n}\n\nfunc (s storageDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Storage, err error) {\n\tbuf := make([]*model.Storage, 0, batchSize)\n\terr = s.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {\n\t\tdefer func() { results = append(results, buf...) }()\n\t\treturn fc(tx, batch)\n\t})\n\treturn results, err\n}\n\nfunc (s storageDo) FindInBatches(result *[]*model.Storage, batchSize int, fc func(tx gen.Dao, batch int) error) error {\n\treturn s.DO.FindInBatches(result, batchSize, fc)\n}\n\nfunc (s storageDo) Attrs(attrs ...field.AssignExpr) IStorageDo {\n\treturn s.withDO(s.DO.Attrs(attrs...))\n}\n\nfunc (s storageDo) Assign(attrs ...field.AssignExpr) IStorageDo {\n\treturn s.withDO(s.DO.Assign(attrs...))\n}\n\nfunc (s storageDo) Joins(fields ...field.RelationField) IStorageDo {\n\tfor _, _f := range fields {\n\t\ts = *s.withDO(s.DO.Joins(_f))\n\t}\n\treturn &s\n}\n\nfunc (s storageDo) Preload(fields ...field.RelationField) IStorageDo {\n\tfor _, _f := range fields {\n\t\ts = *s.withDO(s.DO.Preload(_f))\n\t}\n\treturn &s\n}\n\nfunc (s storageDo) FirstOrInit() (*model.Storage, error) {\n\tif result, err := s.DO.FirstOrInit(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Storage), nil\n\t}\n}\n\nfunc (s storageDo) FirstOrCreate() (*model.Storage, error) {\n\tif result, err := s.DO.FirstOrCreate(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Storage), nil\n\t}\n}\n\nfunc (s storageDo) FindByPage(offset int, limit int) (result []*model.Storage, count int64, err error) {\n\tresult, err = s.Offset(offset).Limit(limit).Find()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif size := len(result); 0 < limit && 0 < size && size < limit {\n\t\tcount = int64(size + offset)\n\t\treturn\n\t}\n\n\tcount, err = s.Offset(-1).Limit(-1).Count()\n\treturn\n}\n\nfunc (s storageDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {\n\tcount, err = s.Count()\n\tif err != nil {\n\t\treturn\n\t}\n\n\terr = s.Offset(offset).Limit(limit).Scan(result)\n\treturn\n}\n\nfunc (s storageDo) Scan(result interface{}) (err error) {\n\treturn s.DO.Scan(result)\n}\n\nfunc (s storageDo) Delete(models ...*model.Storage) (result gen.ResultInfo, err error) {\n\treturn s.DO.Delete(models)\n}\n\nfunc (s *storageDo) withDO(do gen.Dao) *storageDo {\n\ts.DO = *do.(*gen.DO)\n\treturn s\n}\n"
  },
  {
    "path": "internal/query/user.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage query\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\n\t\"gorm.io/gen\"\n\t\"gorm.io/gen/field\"\n\n\t\"gorm.io/plugin/dbresolver\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n)\n\nfunc newUser(db *gorm.DB, opts ...gen.DOOption) user {\n\t_user := user{}\n\n\t_user.userDo.UseDB(db, opts...)\n\t_user.userDo.UseModel(&model.User{})\n\n\ttableName := _user.userDo.TableName()\n\t_user.ALL = field.NewAsterisk(tableName)\n\t_user.UID = field.NewInt64(tableName, \"uid\")\n\t_user.Email = field.NewString(tableName, \"email\")\n\t_user.Username = field.NewString(tableName, \"username\")\n\t_user.Password = field.NewString(tableName, \"password\")\n\t_user.Salt = field.NewString(tableName, \"salt\")\n\t_user.Token = field.NewString(tableName, \"token\")\n\t_user.Avatar = field.NewString(tableName, \"avatar\")\n\t_user.IsDeleted = field.NewInt64(tableName, \"is_deleted\")\n\t_user.UpdatedAt = field.NewField(tableName, \"updated_at\")\n\t_user.CreatedAt = field.NewField(tableName, \"created_at\")\n\t_user.DeletedAt = field.NewField(tableName, \"deleted_at\")\n\n\t_user.fillFieldMap()\n\n\treturn _user\n}\n\ntype user struct {\n\tuserDo userDo\n\n\tALL       field.Asterisk\n\tUID       field.Int64\n\tEmail     field.String\n\tUsername  field.String\n\tPassword  field.String\n\tSalt      field.String\n\tToken     field.String\n\tAvatar    field.String\n\tIsDeleted field.Int64\n\tUpdatedAt field.Field\n\tCreatedAt field.Field\n\tDeletedAt field.Field\n\n\tfieldMap map[string]field.Expr\n}\n\nfunc (u user) Table(newTableName string) *user {\n\tu.userDo.UseTable(newTableName)\n\treturn u.updateTableName(newTableName)\n}\n\nfunc (u user) As(alias string) *user {\n\tu.userDo.DO = *(u.userDo.As(alias).(*gen.DO))\n\treturn u.updateTableName(alias)\n}\n\nfunc (u *user) updateTableName(table string) *user {\n\tu.ALL = field.NewAsterisk(table)\n\tu.UID = field.NewInt64(table, \"uid\")\n\tu.Email = field.NewString(table, \"email\")\n\tu.Username = field.NewString(table, \"username\")\n\tu.Password = field.NewString(table, \"password\")\n\tu.Salt = field.NewString(table, \"salt\")\n\tu.Token = field.NewString(table, \"token\")\n\tu.Avatar = field.NewString(table, \"avatar\")\n\tu.IsDeleted = field.NewInt64(table, \"is_deleted\")\n\tu.UpdatedAt = field.NewField(table, \"updated_at\")\n\tu.CreatedAt = field.NewField(table, \"created_at\")\n\tu.DeletedAt = field.NewField(table, \"deleted_at\")\n\n\tu.fillFieldMap()\n\n\treturn u\n}\n\nfunc (u *user) WithContext(ctx context.Context) IUserDo { return u.userDo.WithContext(ctx) }\n\nfunc (u user) TableName() string { return u.userDo.TableName() }\n\nfunc (u user) Alias() string { return u.userDo.Alias() }\n\nfunc (u user) Columns(cols ...field.Expr) gen.Columns { return u.userDo.Columns(cols...) }\n\nfunc (u *user) GetFieldByName(fieldName string) (field.OrderExpr, bool) {\n\t_f, ok := u.fieldMap[fieldName]\n\tif !ok || _f == nil {\n\t\treturn nil, false\n\t}\n\t_oe, ok := _f.(field.OrderExpr)\n\treturn _oe, ok\n}\n\nfunc (u *user) fillFieldMap() {\n\tu.fieldMap = make(map[string]field.Expr, 11)\n\tu.fieldMap[\"uid\"] = u.UID\n\tu.fieldMap[\"email\"] = u.Email\n\tu.fieldMap[\"username\"] = u.Username\n\tu.fieldMap[\"password\"] = u.Password\n\tu.fieldMap[\"salt\"] = u.Salt\n\tu.fieldMap[\"token\"] = u.Token\n\tu.fieldMap[\"avatar\"] = u.Avatar\n\tu.fieldMap[\"is_deleted\"] = u.IsDeleted\n\tu.fieldMap[\"updated_at\"] = u.UpdatedAt\n\tu.fieldMap[\"created_at\"] = u.CreatedAt\n\tu.fieldMap[\"deleted_at\"] = u.DeletedAt\n}\n\nfunc (u user) clone(db *gorm.DB) user {\n\tu.userDo.ReplaceConnPool(db.Statement.ConnPool)\n\treturn u\n}\n\nfunc (u user) replaceDB(db *gorm.DB) user {\n\tu.userDo.ReplaceDB(db)\n\treturn u\n}\n\ntype userDo struct{ gen.DO }\n\ntype IUserDo interface {\n\tgen.SubQuery\n\tDebug() IUserDo\n\tWithContext(ctx context.Context) IUserDo\n\tWithResult(fc func(tx gen.Dao)) gen.ResultInfo\n\tReplaceDB(db *gorm.DB)\n\tReadDB() IUserDo\n\tWriteDB() IUserDo\n\tAs(alias string) gen.Dao\n\tSession(config *gorm.Session) IUserDo\n\tColumns(cols ...field.Expr) gen.Columns\n\tClauses(conds ...clause.Expression) IUserDo\n\tNot(conds ...gen.Condition) IUserDo\n\tOr(conds ...gen.Condition) IUserDo\n\tSelect(conds ...field.Expr) IUserDo\n\tWhere(conds ...gen.Condition) IUserDo\n\tOrder(conds ...field.Expr) IUserDo\n\tDistinct(cols ...field.Expr) IUserDo\n\tOmit(cols ...field.Expr) IUserDo\n\tJoin(table schema.Tabler, on ...field.Expr) IUserDo\n\tLeftJoin(table schema.Tabler, on ...field.Expr) IUserDo\n\tRightJoin(table schema.Tabler, on ...field.Expr) IUserDo\n\tGroup(cols ...field.Expr) IUserDo\n\tHaving(conds ...gen.Condition) IUserDo\n\tLimit(limit int) IUserDo\n\tOffset(offset int) IUserDo\n\tCount() (count int64, err error)\n\tScopes(funcs ...func(gen.Dao) gen.Dao) IUserDo\n\tUnscoped() IUserDo\n\tCreate(values ...*model.User) error\n\tCreateInBatches(values []*model.User, batchSize int) error\n\tSave(values ...*model.User) error\n\tFirst() (*model.User, error)\n\tTake() (*model.User, error)\n\tLast() (*model.User, error)\n\tFind() ([]*model.User, error)\n\tFindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.User, err error)\n\tFindInBatches(result *[]*model.User, batchSize int, fc func(tx gen.Dao, batch int) error) error\n\tPluck(column field.Expr, dest interface{}) error\n\tDelete(...*model.User) (info gen.ResultInfo, err error)\n\tUpdate(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdates(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdateColumns(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateFrom(q gen.SubQuery) gen.Dao\n\tAttrs(attrs ...field.AssignExpr) IUserDo\n\tAssign(attrs ...field.AssignExpr) IUserDo\n\tJoins(fields ...field.RelationField) IUserDo\n\tPreload(fields ...field.RelationField) IUserDo\n\tFirstOrInit() (*model.User, error)\n\tFirstOrCreate() (*model.User, error)\n\tFindByPage(offset int, limit int) (result []*model.User, count int64, err error)\n\tScanByPage(result interface{}, offset int, limit int) (count int64, err error)\n\tRows() (*sql.Rows, error)\n\tRow() *sql.Row\n\tScan(result interface{}) (err error)\n\tReturning(value interface{}, columns ...string) IUserDo\n\tUnderlyingDB() *gorm.DB\n\tschema.Tabler\n}\n\nfunc (u userDo) Debug() IUserDo {\n\treturn u.withDO(u.DO.Debug())\n}\n\nfunc (u userDo) WithContext(ctx context.Context) IUserDo {\n\treturn u.withDO(u.DO.WithContext(ctx))\n}\n\nfunc (u userDo) ReadDB() IUserDo {\n\treturn u.Clauses(dbresolver.Read)\n}\n\nfunc (u userDo) WriteDB() IUserDo {\n\treturn u.Clauses(dbresolver.Write)\n}\n\nfunc (u userDo) Session(config *gorm.Session) IUserDo {\n\treturn u.withDO(u.DO.Session(config))\n}\n\nfunc (u userDo) Clauses(conds ...clause.Expression) IUserDo {\n\treturn u.withDO(u.DO.Clauses(conds...))\n}\n\nfunc (u userDo) Returning(value interface{}, columns ...string) IUserDo {\n\treturn u.withDO(u.DO.Returning(value, columns...))\n}\n\nfunc (u userDo) Not(conds ...gen.Condition) IUserDo {\n\treturn u.withDO(u.DO.Not(conds...))\n}\n\nfunc (u userDo) Or(conds ...gen.Condition) IUserDo {\n\treturn u.withDO(u.DO.Or(conds...))\n}\n\nfunc (u userDo) Select(conds ...field.Expr) IUserDo {\n\treturn u.withDO(u.DO.Select(conds...))\n}\n\nfunc (u userDo) Where(conds ...gen.Condition) IUserDo {\n\treturn u.withDO(u.DO.Where(conds...))\n}\n\nfunc (u userDo) Order(conds ...field.Expr) IUserDo {\n\treturn u.withDO(u.DO.Order(conds...))\n}\n\nfunc (u userDo) Distinct(cols ...field.Expr) IUserDo {\n\treturn u.withDO(u.DO.Distinct(cols...))\n}\n\nfunc (u userDo) Omit(cols ...field.Expr) IUserDo {\n\treturn u.withDO(u.DO.Omit(cols...))\n}\n\nfunc (u userDo) Join(table schema.Tabler, on ...field.Expr) IUserDo {\n\treturn u.withDO(u.DO.Join(table, on...))\n}\n\nfunc (u userDo) LeftJoin(table schema.Tabler, on ...field.Expr) IUserDo {\n\treturn u.withDO(u.DO.LeftJoin(table, on...))\n}\n\nfunc (u userDo) RightJoin(table schema.Tabler, on ...field.Expr) IUserDo {\n\treturn u.withDO(u.DO.RightJoin(table, on...))\n}\n\nfunc (u userDo) Group(cols ...field.Expr) IUserDo {\n\treturn u.withDO(u.DO.Group(cols...))\n}\n\nfunc (u userDo) Having(conds ...gen.Condition) IUserDo {\n\treturn u.withDO(u.DO.Having(conds...))\n}\n\nfunc (u userDo) Limit(limit int) IUserDo {\n\treturn u.withDO(u.DO.Limit(limit))\n}\n\nfunc (u userDo) Offset(offset int) IUserDo {\n\treturn u.withDO(u.DO.Offset(offset))\n}\n\nfunc (u userDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IUserDo {\n\treturn u.withDO(u.DO.Scopes(funcs...))\n}\n\nfunc (u userDo) Unscoped() IUserDo {\n\treturn u.withDO(u.DO.Unscoped())\n}\n\nfunc (u userDo) Create(values ...*model.User) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn u.DO.Create(values)\n}\n\nfunc (u userDo) CreateInBatches(values []*model.User, batchSize int) error {\n\treturn u.DO.CreateInBatches(values, batchSize)\n}\n\n// Save : !!! underlying implementation is different with GORM\n// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)\nfunc (u userDo) Save(values ...*model.User) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn u.DO.Save(values)\n}\n\nfunc (u userDo) First() (*model.User, error) {\n\tif result, err := u.DO.First(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.User), nil\n\t}\n}\n\nfunc (u userDo) Take() (*model.User, error) {\n\tif result, err := u.DO.Take(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.User), nil\n\t}\n}\n\nfunc (u userDo) Last() (*model.User, error) {\n\tif result, err := u.DO.Last(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.User), nil\n\t}\n}\n\nfunc (u userDo) Find() ([]*model.User, error) {\n\tresult, err := u.DO.Find()\n\treturn result.([]*model.User), err\n}\n\nfunc (u userDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.User, err error) {\n\tbuf := make([]*model.User, 0, batchSize)\n\terr = u.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {\n\t\tdefer func() { results = append(results, buf...) }()\n\t\treturn fc(tx, batch)\n\t})\n\treturn results, err\n}\n\nfunc (u userDo) FindInBatches(result *[]*model.User, batchSize int, fc func(tx gen.Dao, batch int) error) error {\n\treturn u.DO.FindInBatches(result, batchSize, fc)\n}\n\nfunc (u userDo) Attrs(attrs ...field.AssignExpr) IUserDo {\n\treturn u.withDO(u.DO.Attrs(attrs...))\n}\n\nfunc (u userDo) Assign(attrs ...field.AssignExpr) IUserDo {\n\treturn u.withDO(u.DO.Assign(attrs...))\n}\n\nfunc (u userDo) Joins(fields ...field.RelationField) IUserDo {\n\tfor _, _f := range fields {\n\t\tu = *u.withDO(u.DO.Joins(_f))\n\t}\n\treturn &u\n}\n\nfunc (u userDo) Preload(fields ...field.RelationField) IUserDo {\n\tfor _, _f := range fields {\n\t\tu = *u.withDO(u.DO.Preload(_f))\n\t}\n\treturn &u\n}\n\nfunc (u userDo) FirstOrInit() (*model.User, error) {\n\tif result, err := u.DO.FirstOrInit(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.User), nil\n\t}\n}\n\nfunc (u userDo) FirstOrCreate() (*model.User, error) {\n\tif result, err := u.DO.FirstOrCreate(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.User), nil\n\t}\n}\n\nfunc (u userDo) FindByPage(offset int, limit int) (result []*model.User, count int64, err error) {\n\tresult, err = u.Offset(offset).Limit(limit).Find()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif size := len(result); 0 < limit && 0 < size && size < limit {\n\t\tcount = int64(size + offset)\n\t\treturn\n\t}\n\n\tcount, err = u.Offset(-1).Limit(-1).Count()\n\treturn\n}\n\nfunc (u userDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {\n\tcount, err = u.Count()\n\tif err != nil {\n\t\treturn\n\t}\n\n\terr = u.Offset(offset).Limit(limit).Scan(result)\n\treturn\n}\n\nfunc (u userDo) Scan(result interface{}) (err error) {\n\treturn u.DO.Scan(result)\n}\n\nfunc (u userDo) Delete(models ...*model.User) (result gen.ResultInfo, err error) {\n\treturn u.DO.Delete(models)\n}\n\nfunc (u *userDo) withDO(do gen.Dao) *userDo {\n\tu.DO = *do.(*gen.DO)\n\treturn u\n}\n"
  },
  {
    "path": "internal/query/user_share.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage query\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\n\t\"gorm.io/gen\"\n\t\"gorm.io/gen/field\"\n\n\t\"gorm.io/plugin/dbresolver\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n)\n\nfunc newUserShare(db *gorm.DB, opts ...gen.DOOption) userShare {\n\t_userShare := userShare{}\n\n\t_userShare.userShareDo.UseDB(db, opts...)\n\t_userShare.userShareDo.UseModel(&model.UserShare{})\n\n\ttableName := _userShare.userShareDo.TableName()\n\t_userShare.ALL = field.NewAsterisk(tableName)\n\t_userShare.ID = field.NewInt64(tableName, \"id\")\n\t_userShare.UID = field.NewInt64(tableName, \"uid\")\n\t_userShare.ResType = field.NewString(tableName, \"res_type\")\n\t_userShare.ResID = field.NewInt64(tableName, \"res_id\")\n\t_userShare.Res = field.NewString(tableName, \"res\")\n\t_userShare.Status = field.NewInt64(tableName, \"status\")\n\t_userShare.ViewCount = field.NewInt64(tableName, \"view_count\")\n\t_userShare.LastViewedAt = field.NewTime(tableName, \"last_viewed_at\")\n\t_userShare.ExpiresAt = field.NewTime(tableName, \"expires_at\")\n\t_userShare.Password = field.NewString(tableName, \"password\")\n\t_userShare.ShortLink = field.NewString(tableName, \"short_link\")\n\t_userShare.CreatedAt = field.NewField(tableName, \"created_at\")\n\t_userShare.UpdatedAt = field.NewField(tableName, \"updated_at\")\n\n\t_userShare.fillFieldMap()\n\n\treturn _userShare\n}\n\ntype userShare struct {\n\tuserShareDo userShareDo\n\n\tALL          field.Asterisk\n\tID           field.Int64\n\tUID          field.Int64\n\tResType      field.String\n\tResID        field.Int64\n\tRes          field.String\n\tStatus       field.Int64\n\tViewCount    field.Int64\n\tLastViewedAt field.Time\n\tExpiresAt    field.Time\n\tPassword     field.String\n\tShortLink    field.String\n\tCreatedAt    field.Field\n\tUpdatedAt    field.Field\n\n\tfieldMap map[string]field.Expr\n}\n\nfunc (u userShare) Table(newTableName string) *userShare {\n\tu.userShareDo.UseTable(newTableName)\n\treturn u.updateTableName(newTableName)\n}\n\nfunc (u userShare) As(alias string) *userShare {\n\tu.userShareDo.DO = *(u.userShareDo.As(alias).(*gen.DO))\n\treturn u.updateTableName(alias)\n}\n\nfunc (u *userShare) updateTableName(table string) *userShare {\n\tu.ALL = field.NewAsterisk(table)\n\tu.ID = field.NewInt64(table, \"id\")\n\tu.UID = field.NewInt64(table, \"uid\")\n\tu.ResType = field.NewString(table, \"res_type\")\n\tu.ResID = field.NewInt64(table, \"res_id\")\n\tu.Res = field.NewString(table, \"res\")\n\tu.Status = field.NewInt64(table, \"status\")\n\tu.ViewCount = field.NewInt64(table, \"view_count\")\n\tu.LastViewedAt = field.NewTime(table, \"last_viewed_at\")\n\tu.ExpiresAt = field.NewTime(table, \"expires_at\")\n\tu.Password = field.NewString(table, \"password\")\n\tu.ShortLink = field.NewString(table, \"short_link\")\n\tu.CreatedAt = field.NewField(table, \"created_at\")\n\tu.UpdatedAt = field.NewField(table, \"updated_at\")\n\n\tu.fillFieldMap()\n\n\treturn u\n}\n\nfunc (u *userShare) WithContext(ctx context.Context) IUserShareDo {\n\treturn u.userShareDo.WithContext(ctx)\n}\n\nfunc (u userShare) TableName() string { return u.userShareDo.TableName() }\n\nfunc (u userShare) Alias() string { return u.userShareDo.Alias() }\n\nfunc (u userShare) Columns(cols ...field.Expr) gen.Columns { return u.userShareDo.Columns(cols...) }\n\nfunc (u *userShare) GetFieldByName(fieldName string) (field.OrderExpr, bool) {\n\t_f, ok := u.fieldMap[fieldName]\n\tif !ok || _f == nil {\n\t\treturn nil, false\n\t}\n\t_oe, ok := _f.(field.OrderExpr)\n\treturn _oe, ok\n}\n\nfunc (u *userShare) fillFieldMap() {\n\tu.fieldMap = make(map[string]field.Expr, 13)\n\tu.fieldMap[\"id\"] = u.ID\n\tu.fieldMap[\"uid\"] = u.UID\n\tu.fieldMap[\"res_type\"] = u.ResType\n\tu.fieldMap[\"res_id\"] = u.ResID\n\tu.fieldMap[\"res\"] = u.Res\n\tu.fieldMap[\"status\"] = u.Status\n\tu.fieldMap[\"view_count\"] = u.ViewCount\n\tu.fieldMap[\"last_viewed_at\"] = u.LastViewedAt\n\tu.fieldMap[\"expires_at\"] = u.ExpiresAt\n\tu.fieldMap[\"password\"] = u.Password\n\tu.fieldMap[\"short_link\"] = u.ShortLink\n\tu.fieldMap[\"created_at\"] = u.CreatedAt\n\tu.fieldMap[\"updated_at\"] = u.UpdatedAt\n}\n\nfunc (u userShare) clone(db *gorm.DB) userShare {\n\tu.userShareDo.ReplaceConnPool(db.Statement.ConnPool)\n\treturn u\n}\n\nfunc (u userShare) replaceDB(db *gorm.DB) userShare {\n\tu.userShareDo.ReplaceDB(db)\n\treturn u\n}\n\ntype userShareDo struct{ gen.DO }\n\ntype IUserShareDo interface {\n\tgen.SubQuery\n\tDebug() IUserShareDo\n\tWithContext(ctx context.Context) IUserShareDo\n\tWithResult(fc func(tx gen.Dao)) gen.ResultInfo\n\tReplaceDB(db *gorm.DB)\n\tReadDB() IUserShareDo\n\tWriteDB() IUserShareDo\n\tAs(alias string) gen.Dao\n\tSession(config *gorm.Session) IUserShareDo\n\tColumns(cols ...field.Expr) gen.Columns\n\tClauses(conds ...clause.Expression) IUserShareDo\n\tNot(conds ...gen.Condition) IUserShareDo\n\tOr(conds ...gen.Condition) IUserShareDo\n\tSelect(conds ...field.Expr) IUserShareDo\n\tWhere(conds ...gen.Condition) IUserShareDo\n\tOrder(conds ...field.Expr) IUserShareDo\n\tDistinct(cols ...field.Expr) IUserShareDo\n\tOmit(cols ...field.Expr) IUserShareDo\n\tJoin(table schema.Tabler, on ...field.Expr) IUserShareDo\n\tLeftJoin(table schema.Tabler, on ...field.Expr) IUserShareDo\n\tRightJoin(table schema.Tabler, on ...field.Expr) IUserShareDo\n\tGroup(cols ...field.Expr) IUserShareDo\n\tHaving(conds ...gen.Condition) IUserShareDo\n\tLimit(limit int) IUserShareDo\n\tOffset(offset int) IUserShareDo\n\tCount() (count int64, err error)\n\tScopes(funcs ...func(gen.Dao) gen.Dao) IUserShareDo\n\tUnscoped() IUserShareDo\n\tCreate(values ...*model.UserShare) error\n\tCreateInBatches(values []*model.UserShare, batchSize int) error\n\tSave(values ...*model.UserShare) error\n\tFirst() (*model.UserShare, error)\n\tTake() (*model.UserShare, error)\n\tLast() (*model.UserShare, error)\n\tFind() ([]*model.UserShare, error)\n\tFindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.UserShare, err error)\n\tFindInBatches(result *[]*model.UserShare, batchSize int, fc func(tx gen.Dao, batch int) error) error\n\tPluck(column field.Expr, dest interface{}) error\n\tDelete(...*model.UserShare) (info gen.ResultInfo, err error)\n\tUpdate(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdates(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdateColumns(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateFrom(q gen.SubQuery) gen.Dao\n\tAttrs(attrs ...field.AssignExpr) IUserShareDo\n\tAssign(attrs ...field.AssignExpr) IUserShareDo\n\tJoins(fields ...field.RelationField) IUserShareDo\n\tPreload(fields ...field.RelationField) IUserShareDo\n\tFirstOrInit() (*model.UserShare, error)\n\tFirstOrCreate() (*model.UserShare, error)\n\tFindByPage(offset int, limit int) (result []*model.UserShare, count int64, err error)\n\tScanByPage(result interface{}, offset int, limit int) (count int64, err error)\n\tRows() (*sql.Rows, error)\n\tRow() *sql.Row\n\tScan(result interface{}) (err error)\n\tReturning(value interface{}, columns ...string) IUserShareDo\n\tUnderlyingDB() *gorm.DB\n\tschema.Tabler\n}\n\nfunc (u userShareDo) Debug() IUserShareDo {\n\treturn u.withDO(u.DO.Debug())\n}\n\nfunc (u userShareDo) WithContext(ctx context.Context) IUserShareDo {\n\treturn u.withDO(u.DO.WithContext(ctx))\n}\n\nfunc (u userShareDo) ReadDB() IUserShareDo {\n\treturn u.Clauses(dbresolver.Read)\n}\n\nfunc (u userShareDo) WriteDB() IUserShareDo {\n\treturn u.Clauses(dbresolver.Write)\n}\n\nfunc (u userShareDo) Session(config *gorm.Session) IUserShareDo {\n\treturn u.withDO(u.DO.Session(config))\n}\n\nfunc (u userShareDo) Clauses(conds ...clause.Expression) IUserShareDo {\n\treturn u.withDO(u.DO.Clauses(conds...))\n}\n\nfunc (u userShareDo) Returning(value interface{}, columns ...string) IUserShareDo {\n\treturn u.withDO(u.DO.Returning(value, columns...))\n}\n\nfunc (u userShareDo) Not(conds ...gen.Condition) IUserShareDo {\n\treturn u.withDO(u.DO.Not(conds...))\n}\n\nfunc (u userShareDo) Or(conds ...gen.Condition) IUserShareDo {\n\treturn u.withDO(u.DO.Or(conds...))\n}\n\nfunc (u userShareDo) Select(conds ...field.Expr) IUserShareDo {\n\treturn u.withDO(u.DO.Select(conds...))\n}\n\nfunc (u userShareDo) Where(conds ...gen.Condition) IUserShareDo {\n\treturn u.withDO(u.DO.Where(conds...))\n}\n\nfunc (u userShareDo) Order(conds ...field.Expr) IUserShareDo {\n\treturn u.withDO(u.DO.Order(conds...))\n}\n\nfunc (u userShareDo) Distinct(cols ...field.Expr) IUserShareDo {\n\treturn u.withDO(u.DO.Distinct(cols...))\n}\n\nfunc (u userShareDo) Omit(cols ...field.Expr) IUserShareDo {\n\treturn u.withDO(u.DO.Omit(cols...))\n}\n\nfunc (u userShareDo) Join(table schema.Tabler, on ...field.Expr) IUserShareDo {\n\treturn u.withDO(u.DO.Join(table, on...))\n}\n\nfunc (u userShareDo) LeftJoin(table schema.Tabler, on ...field.Expr) IUserShareDo {\n\treturn u.withDO(u.DO.LeftJoin(table, on...))\n}\n\nfunc (u userShareDo) RightJoin(table schema.Tabler, on ...field.Expr) IUserShareDo {\n\treturn u.withDO(u.DO.RightJoin(table, on...))\n}\n\nfunc (u userShareDo) Group(cols ...field.Expr) IUserShareDo {\n\treturn u.withDO(u.DO.Group(cols...))\n}\n\nfunc (u userShareDo) Having(conds ...gen.Condition) IUserShareDo {\n\treturn u.withDO(u.DO.Having(conds...))\n}\n\nfunc (u userShareDo) Limit(limit int) IUserShareDo {\n\treturn u.withDO(u.DO.Limit(limit))\n}\n\nfunc (u userShareDo) Offset(offset int) IUserShareDo {\n\treturn u.withDO(u.DO.Offset(offset))\n}\n\nfunc (u userShareDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IUserShareDo {\n\treturn u.withDO(u.DO.Scopes(funcs...))\n}\n\nfunc (u userShareDo) Unscoped() IUserShareDo {\n\treturn u.withDO(u.DO.Unscoped())\n}\n\nfunc (u userShareDo) Create(values ...*model.UserShare) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn u.DO.Create(values)\n}\n\nfunc (u userShareDo) CreateInBatches(values []*model.UserShare, batchSize int) error {\n\treturn u.DO.CreateInBatches(values, batchSize)\n}\n\n// Save : !!! underlying implementation is different with GORM\n// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)\nfunc (u userShareDo) Save(values ...*model.UserShare) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn u.DO.Save(values)\n}\n\nfunc (u userShareDo) First() (*model.UserShare, error) {\n\tif result, err := u.DO.First(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.UserShare), nil\n\t}\n}\n\nfunc (u userShareDo) Take() (*model.UserShare, error) {\n\tif result, err := u.DO.Take(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.UserShare), nil\n\t}\n}\n\nfunc (u userShareDo) Last() (*model.UserShare, error) {\n\tif result, err := u.DO.Last(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.UserShare), nil\n\t}\n}\n\nfunc (u userShareDo) Find() ([]*model.UserShare, error) {\n\tresult, err := u.DO.Find()\n\treturn result.([]*model.UserShare), err\n}\n\nfunc (u userShareDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.UserShare, err error) {\n\tbuf := make([]*model.UserShare, 0, batchSize)\n\terr = u.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {\n\t\tdefer func() { results = append(results, buf...) }()\n\t\treturn fc(tx, batch)\n\t})\n\treturn results, err\n}\n\nfunc (u userShareDo) FindInBatches(result *[]*model.UserShare, batchSize int, fc func(tx gen.Dao, batch int) error) error {\n\treturn u.DO.FindInBatches(result, batchSize, fc)\n}\n\nfunc (u userShareDo) Attrs(attrs ...field.AssignExpr) IUserShareDo {\n\treturn u.withDO(u.DO.Attrs(attrs...))\n}\n\nfunc (u userShareDo) Assign(attrs ...field.AssignExpr) IUserShareDo {\n\treturn u.withDO(u.DO.Assign(attrs...))\n}\n\nfunc (u userShareDo) Joins(fields ...field.RelationField) IUserShareDo {\n\tfor _, _f := range fields {\n\t\tu = *u.withDO(u.DO.Joins(_f))\n\t}\n\treturn &u\n}\n\nfunc (u userShareDo) Preload(fields ...field.RelationField) IUserShareDo {\n\tfor _, _f := range fields {\n\t\tu = *u.withDO(u.DO.Preload(_f))\n\t}\n\treturn &u\n}\n\nfunc (u userShareDo) FirstOrInit() (*model.UserShare, error) {\n\tif result, err := u.DO.FirstOrInit(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.UserShare), nil\n\t}\n}\n\nfunc (u userShareDo) FirstOrCreate() (*model.UserShare, error) {\n\tif result, err := u.DO.FirstOrCreate(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.UserShare), nil\n\t}\n}\n\nfunc (u userShareDo) FindByPage(offset int, limit int) (result []*model.UserShare, count int64, err error) {\n\tresult, err = u.Offset(offset).Limit(limit).Find()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif size := len(result); 0 < limit && 0 < size && size < limit {\n\t\tcount = int64(size + offset)\n\t\treturn\n\t}\n\n\tcount, err = u.Offset(-1).Limit(-1).Count()\n\treturn\n}\n\nfunc (u userShareDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {\n\tcount, err = u.Count()\n\tif err != nil {\n\t\treturn\n\t}\n\n\terr = u.Offset(offset).Limit(limit).Scan(result)\n\treturn\n}\n\nfunc (u userShareDo) Scan(result interface{}) (err error) {\n\treturn u.DO.Scan(result)\n}\n\nfunc (u userShareDo) Delete(models ...*model.UserShare) (result gen.ResultInfo, err error) {\n\treturn u.DO.Delete(models)\n}\n\nfunc (u *userShareDo) withDO(do gen.Dao) *userShareDo {\n\tu.DO = *do.(*gen.DO)\n\treturn u\n}\n"
  },
  {
    "path": "internal/query/vault.gen.go",
    "content": "// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n// Code generated by gorm.io/gen. DO NOT EDIT.\n\npackage query\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n\t\"gorm.io/gorm/schema\"\n\n\t\"gorm.io/gen\"\n\t\"gorm.io/gen/field\"\n\n\t\"gorm.io/plugin/dbresolver\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/model\"\n)\n\nfunc newVault(db *gorm.DB, opts ...gen.DOOption) vault {\n\t_vault := vault{}\n\n\t_vault.vaultDo.UseDB(db, opts...)\n\t_vault.vaultDo.UseModel(&model.Vault{})\n\n\ttableName := _vault.vaultDo.TableName()\n\t_vault.ALL = field.NewAsterisk(tableName)\n\t_vault.ID = field.NewInt64(tableName, \"id\")\n\t_vault.Vault = field.NewString(tableName, \"vault\")\n\t_vault.NoteCount = field.NewInt64(tableName, \"note_count\")\n\t_vault.NoteSize = field.NewInt64(tableName, \"note_size\")\n\t_vault.FileCount = field.NewInt64(tableName, \"file_count\")\n\t_vault.FileSize = field.NewInt64(tableName, \"file_size\")\n\t_vault.IsDeleted = field.NewInt64(tableName, \"is_deleted\")\n\t_vault.CreatedAt = field.NewField(tableName, \"created_at\")\n\t_vault.UpdatedAt = field.NewField(tableName, \"updated_at\")\n\n\t_vault.fillFieldMap()\n\n\treturn _vault\n}\n\ntype vault struct {\n\tvaultDo vaultDo\n\n\tALL       field.Asterisk\n\tID        field.Int64\n\tVault     field.String\n\tNoteCount field.Int64\n\tNoteSize  field.Int64\n\tFileCount field.Int64\n\tFileSize  field.Int64\n\tIsDeleted field.Int64\n\tCreatedAt field.Field\n\tUpdatedAt field.Field\n\n\tfieldMap map[string]field.Expr\n}\n\nfunc (v vault) Table(newTableName string) *vault {\n\tv.vaultDo.UseTable(newTableName)\n\treturn v.updateTableName(newTableName)\n}\n\nfunc (v vault) As(alias string) *vault {\n\tv.vaultDo.DO = *(v.vaultDo.As(alias).(*gen.DO))\n\treturn v.updateTableName(alias)\n}\n\nfunc (v *vault) updateTableName(table string) *vault {\n\tv.ALL = field.NewAsterisk(table)\n\tv.ID = field.NewInt64(table, \"id\")\n\tv.Vault = field.NewString(table, \"vault\")\n\tv.NoteCount = field.NewInt64(table, \"note_count\")\n\tv.NoteSize = field.NewInt64(table, \"note_size\")\n\tv.FileCount = field.NewInt64(table, \"file_count\")\n\tv.FileSize = field.NewInt64(table, \"file_size\")\n\tv.IsDeleted = field.NewInt64(table, \"is_deleted\")\n\tv.CreatedAt = field.NewField(table, \"created_at\")\n\tv.UpdatedAt = field.NewField(table, \"updated_at\")\n\n\tv.fillFieldMap()\n\n\treturn v\n}\n\nfunc (v *vault) WithContext(ctx context.Context) IVaultDo { return v.vaultDo.WithContext(ctx) }\n\nfunc (v vault) TableName() string { return v.vaultDo.TableName() }\n\nfunc (v vault) Alias() string { return v.vaultDo.Alias() }\n\nfunc (v vault) Columns(cols ...field.Expr) gen.Columns { return v.vaultDo.Columns(cols...) }\n\nfunc (v *vault) GetFieldByName(fieldName string) (field.OrderExpr, bool) {\n\t_f, ok := v.fieldMap[fieldName]\n\tif !ok || _f == nil {\n\t\treturn nil, false\n\t}\n\t_oe, ok := _f.(field.OrderExpr)\n\treturn _oe, ok\n}\n\nfunc (v *vault) fillFieldMap() {\n\tv.fieldMap = make(map[string]field.Expr, 9)\n\tv.fieldMap[\"id\"] = v.ID\n\tv.fieldMap[\"vault\"] = v.Vault\n\tv.fieldMap[\"note_count\"] = v.NoteCount\n\tv.fieldMap[\"note_size\"] = v.NoteSize\n\tv.fieldMap[\"file_count\"] = v.FileCount\n\tv.fieldMap[\"file_size\"] = v.FileSize\n\tv.fieldMap[\"is_deleted\"] = v.IsDeleted\n\tv.fieldMap[\"created_at\"] = v.CreatedAt\n\tv.fieldMap[\"updated_at\"] = v.UpdatedAt\n}\n\nfunc (v vault) clone(db *gorm.DB) vault {\n\tv.vaultDo.ReplaceConnPool(db.Statement.ConnPool)\n\treturn v\n}\n\nfunc (v vault) replaceDB(db *gorm.DB) vault {\n\tv.vaultDo.ReplaceDB(db)\n\treturn v\n}\n\ntype vaultDo struct{ gen.DO }\n\ntype IVaultDo interface {\n\tgen.SubQuery\n\tDebug() IVaultDo\n\tWithContext(ctx context.Context) IVaultDo\n\tWithResult(fc func(tx gen.Dao)) gen.ResultInfo\n\tReplaceDB(db *gorm.DB)\n\tReadDB() IVaultDo\n\tWriteDB() IVaultDo\n\tAs(alias string) gen.Dao\n\tSession(config *gorm.Session) IVaultDo\n\tColumns(cols ...field.Expr) gen.Columns\n\tClauses(conds ...clause.Expression) IVaultDo\n\tNot(conds ...gen.Condition) IVaultDo\n\tOr(conds ...gen.Condition) IVaultDo\n\tSelect(conds ...field.Expr) IVaultDo\n\tWhere(conds ...gen.Condition) IVaultDo\n\tOrder(conds ...field.Expr) IVaultDo\n\tDistinct(cols ...field.Expr) IVaultDo\n\tOmit(cols ...field.Expr) IVaultDo\n\tJoin(table schema.Tabler, on ...field.Expr) IVaultDo\n\tLeftJoin(table schema.Tabler, on ...field.Expr) IVaultDo\n\tRightJoin(table schema.Tabler, on ...field.Expr) IVaultDo\n\tGroup(cols ...field.Expr) IVaultDo\n\tHaving(conds ...gen.Condition) IVaultDo\n\tLimit(limit int) IVaultDo\n\tOffset(offset int) IVaultDo\n\tCount() (count int64, err error)\n\tScopes(funcs ...func(gen.Dao) gen.Dao) IVaultDo\n\tUnscoped() IVaultDo\n\tCreate(values ...*model.Vault) error\n\tCreateInBatches(values []*model.Vault, batchSize int) error\n\tSave(values ...*model.Vault) error\n\tFirst() (*model.Vault, error)\n\tTake() (*model.Vault, error)\n\tLast() (*model.Vault, error)\n\tFind() ([]*model.Vault, error)\n\tFindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Vault, err error)\n\tFindInBatches(result *[]*model.Vault, batchSize int, fc func(tx gen.Dao, batch int) error) error\n\tPluck(column field.Expr, dest interface{}) error\n\tDelete(...*model.Vault) (info gen.ResultInfo, err error)\n\tUpdate(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdates(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)\n\tUpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)\n\tUpdateColumns(value interface{}) (info gen.ResultInfo, err error)\n\tUpdateFrom(q gen.SubQuery) gen.Dao\n\tAttrs(attrs ...field.AssignExpr) IVaultDo\n\tAssign(attrs ...field.AssignExpr) IVaultDo\n\tJoins(fields ...field.RelationField) IVaultDo\n\tPreload(fields ...field.RelationField) IVaultDo\n\tFirstOrInit() (*model.Vault, error)\n\tFirstOrCreate() (*model.Vault, error)\n\tFindByPage(offset int, limit int) (result []*model.Vault, count int64, err error)\n\tScanByPage(result interface{}, offset int, limit int) (count int64, err error)\n\tRows() (*sql.Rows, error)\n\tRow() *sql.Row\n\tScan(result interface{}) (err error)\n\tReturning(value interface{}, columns ...string) IVaultDo\n\tUnderlyingDB() *gorm.DB\n\tschema.Tabler\n}\n\nfunc (v vaultDo) Debug() IVaultDo {\n\treturn v.withDO(v.DO.Debug())\n}\n\nfunc (v vaultDo) WithContext(ctx context.Context) IVaultDo {\n\treturn v.withDO(v.DO.WithContext(ctx))\n}\n\nfunc (v vaultDo) ReadDB() IVaultDo {\n\treturn v.Clauses(dbresolver.Read)\n}\n\nfunc (v vaultDo) WriteDB() IVaultDo {\n\treturn v.Clauses(dbresolver.Write)\n}\n\nfunc (v vaultDo) Session(config *gorm.Session) IVaultDo {\n\treturn v.withDO(v.DO.Session(config))\n}\n\nfunc (v vaultDo) Clauses(conds ...clause.Expression) IVaultDo {\n\treturn v.withDO(v.DO.Clauses(conds...))\n}\n\nfunc (v vaultDo) Returning(value interface{}, columns ...string) IVaultDo {\n\treturn v.withDO(v.DO.Returning(value, columns...))\n}\n\nfunc (v vaultDo) Not(conds ...gen.Condition) IVaultDo {\n\treturn v.withDO(v.DO.Not(conds...))\n}\n\nfunc (v vaultDo) Or(conds ...gen.Condition) IVaultDo {\n\treturn v.withDO(v.DO.Or(conds...))\n}\n\nfunc (v vaultDo) Select(conds ...field.Expr) IVaultDo {\n\treturn v.withDO(v.DO.Select(conds...))\n}\n\nfunc (v vaultDo) Where(conds ...gen.Condition) IVaultDo {\n\treturn v.withDO(v.DO.Where(conds...))\n}\n\nfunc (v vaultDo) Order(conds ...field.Expr) IVaultDo {\n\treturn v.withDO(v.DO.Order(conds...))\n}\n\nfunc (v vaultDo) Distinct(cols ...field.Expr) IVaultDo {\n\treturn v.withDO(v.DO.Distinct(cols...))\n}\n\nfunc (v vaultDo) Omit(cols ...field.Expr) IVaultDo {\n\treturn v.withDO(v.DO.Omit(cols...))\n}\n\nfunc (v vaultDo) Join(table schema.Tabler, on ...field.Expr) IVaultDo {\n\treturn v.withDO(v.DO.Join(table, on...))\n}\n\nfunc (v vaultDo) LeftJoin(table schema.Tabler, on ...field.Expr) IVaultDo {\n\treturn v.withDO(v.DO.LeftJoin(table, on...))\n}\n\nfunc (v vaultDo) RightJoin(table schema.Tabler, on ...field.Expr) IVaultDo {\n\treturn v.withDO(v.DO.RightJoin(table, on...))\n}\n\nfunc (v vaultDo) Group(cols ...field.Expr) IVaultDo {\n\treturn v.withDO(v.DO.Group(cols...))\n}\n\nfunc (v vaultDo) Having(conds ...gen.Condition) IVaultDo {\n\treturn v.withDO(v.DO.Having(conds...))\n}\n\nfunc (v vaultDo) Limit(limit int) IVaultDo {\n\treturn v.withDO(v.DO.Limit(limit))\n}\n\nfunc (v vaultDo) Offset(offset int) IVaultDo {\n\treturn v.withDO(v.DO.Offset(offset))\n}\n\nfunc (v vaultDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IVaultDo {\n\treturn v.withDO(v.DO.Scopes(funcs...))\n}\n\nfunc (v vaultDo) Unscoped() IVaultDo {\n\treturn v.withDO(v.DO.Unscoped())\n}\n\nfunc (v vaultDo) Create(values ...*model.Vault) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn v.DO.Create(values)\n}\n\nfunc (v vaultDo) CreateInBatches(values []*model.Vault, batchSize int) error {\n\treturn v.DO.CreateInBatches(values, batchSize)\n}\n\n// Save : !!! underlying implementation is different with GORM\n// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)\nfunc (v vaultDo) Save(values ...*model.Vault) error {\n\tif len(values) == 0 {\n\t\treturn nil\n\t}\n\treturn v.DO.Save(values)\n}\n\nfunc (v vaultDo) First() (*model.Vault, error) {\n\tif result, err := v.DO.First(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Vault), nil\n\t}\n}\n\nfunc (v vaultDo) Take() (*model.Vault, error) {\n\tif result, err := v.DO.Take(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Vault), nil\n\t}\n}\n\nfunc (v vaultDo) Last() (*model.Vault, error) {\n\tif result, err := v.DO.Last(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Vault), nil\n\t}\n}\n\nfunc (v vaultDo) Find() ([]*model.Vault, error) {\n\tresult, err := v.DO.Find()\n\treturn result.([]*model.Vault), err\n}\n\nfunc (v vaultDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Vault, err error) {\n\tbuf := make([]*model.Vault, 0, batchSize)\n\terr = v.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {\n\t\tdefer func() { results = append(results, buf...) }()\n\t\treturn fc(tx, batch)\n\t})\n\treturn results, err\n}\n\nfunc (v vaultDo) FindInBatches(result *[]*model.Vault, batchSize int, fc func(tx gen.Dao, batch int) error) error {\n\treturn v.DO.FindInBatches(result, batchSize, fc)\n}\n\nfunc (v vaultDo) Attrs(attrs ...field.AssignExpr) IVaultDo {\n\treturn v.withDO(v.DO.Attrs(attrs...))\n}\n\nfunc (v vaultDo) Assign(attrs ...field.AssignExpr) IVaultDo {\n\treturn v.withDO(v.DO.Assign(attrs...))\n}\n\nfunc (v vaultDo) Joins(fields ...field.RelationField) IVaultDo {\n\tfor _, _f := range fields {\n\t\tv = *v.withDO(v.DO.Joins(_f))\n\t}\n\treturn &v\n}\n\nfunc (v vaultDo) Preload(fields ...field.RelationField) IVaultDo {\n\tfor _, _f := range fields {\n\t\tv = *v.withDO(v.DO.Preload(_f))\n\t}\n\treturn &v\n}\n\nfunc (v vaultDo) FirstOrInit() (*model.Vault, error) {\n\tif result, err := v.DO.FirstOrInit(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Vault), nil\n\t}\n}\n\nfunc (v vaultDo) FirstOrCreate() (*model.Vault, error) {\n\tif result, err := v.DO.FirstOrCreate(); err != nil {\n\t\treturn nil, err\n\t} else {\n\t\treturn result.(*model.Vault), nil\n\t}\n}\n\nfunc (v vaultDo) FindByPage(offset int, limit int) (result []*model.Vault, count int64, err error) {\n\tresult, err = v.Offset(offset).Limit(limit).Find()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif size := len(result); 0 < limit && 0 < size && size < limit {\n\t\tcount = int64(size + offset)\n\t\treturn\n\t}\n\n\tcount, err = v.Offset(-1).Limit(-1).Count()\n\treturn\n}\n\nfunc (v vaultDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {\n\tcount, err = v.Count()\n\tif err != nil {\n\t\treturn\n\t}\n\n\terr = v.Offset(offset).Limit(limit).Scan(result)\n\treturn\n}\n\nfunc (v vaultDo) Scan(result interface{}) (err error) {\n\treturn v.DO.Scan(result)\n}\n\nfunc (v vaultDo) Delete(models ...*model.Vault) (result gen.ResultInfo, err error) {\n\treturn v.DO.Delete(models)\n}\n\nfunc (v *vaultDo) withDO(do gen.Dao) *vaultDo {\n\tv.DO = *do.(*gen.DO)\n\treturn v\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler.go",
    "content": "// Package api_router provides HTTP API router handlers\n// Package api_router 提供 HTTP API 路由处理器\npackage api_router\n\nimport (\n\t\"net/url\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n)\n\n// Handler basic Handler struct, encapsulates App Container\n// All API Handlers should embed this struct to gain dependency injection capability\n// Handler 基础 Handler 结构体，封装 App Container\n// 所有 API Handler 都应该嵌入此结构体以获得依赖注入能力\ntype Handler struct {\n\tApp *app.App\n\tWSS *pkgapp.WebsocketServer\n}\n\n// NewHandler creates a new base Handler instance\n// NewHandler 创建新的基础 Handler 实例\nfunc NewHandler(a *app.App) *Handler {\n\treturn &Handler{App: a}\n}\n\n// NewHandlerWithWSS creates Handler instance with WebSocket service\n// NewHandlerWithWSS 创建带 WebSocket 服务的 Handler 实例\nfunc NewHandlerWithWSS(a *app.App, wss *pkgapp.WebsocketServer) *Handler {\n\treturn &Handler{App: a, WSS: wss}\n}\n\n// getClientInfo extracts client type, name and version from request headers\n// getClientInfo 从请求头中提取客户端类型、名称和版本\nfunc (h *Handler) getClientInfo(c *gin.Context) (string, string, string) {\n\tclientType := c.GetHeader(\"X-Client\")\n\tclientName := c.GetHeader(\"X-Client-Name\")\n\tclientVersion := c.GetHeader(\"X-Client-Version\")\n\n\t// Decode clientName if it's URL-encoded\n\t// 如果 clientName 是 URL 编码的，则进行解码\n\tif clientName != \"\" {\n\t\tif decoded, err := url.QueryUnescape(clientName); err == nil {\n\t\t\tclientName = decoded\n\t\t}\n\t}\n\n\treturn clientType, clientName, clientVersion\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_admin_control.go",
    "content": "package api_router\n\nimport (\n\t\"archive/tar\"\n\t\"compress/gzip\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/config\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dao\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"github.com/shirou/gopsutil/v4/cpu\"\n\t\"github.com/shirou/gopsutil/v4/host\"\n\t\"github.com/shirou/gopsutil/v4/load\"\n\t\"github.com/shirou/gopsutil/v4/mem\"\n\t\"github.com/shirou/gopsutil/v4/process\"\n\t\"go.uber.org/zap\"\n)\n\n// AdminControlHandler Admin control configuration API router handler\n// AdminControlHandler 管理控制配置 API 路由处理器\n// Uses App Container to inject dependencies\n// 使用 App Container 注入依赖\ntype AdminControlHandler struct {\n\t*Handler\n\twss *pkgapp.WebsocketServer\n}\n\n// NewAdminControlHandler creates AdminControlHandler instance\n// NewAdminControlHandler 创建 AdminControlHandler 实例\nfunc NewAdminControlHandler(a *app.App, wss *pkgapp.WebsocketServer) *AdminControlHandler {\n\treturn &AdminControlHandler{\n\t\tHandler: NewHandler(a),\n\t\twss:     wss,\n\t}\n}\n\n// Config retrieves WebGUI configuration (public interface)\n// @Summary Get WebGUI basic config\n// @Description Get non-sensitive configuration required for frontend display, such as font settings, registration status, etc.\n// @Tags Config\n// @Produce json\n// @Success 200 {object} pkgapp.Res{data=dto.AdminWebGUIConfig} \"Success\"\n// @Router /api/webgui/config [get]\nfunc (h *AdminControlHandler) Config(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tcfg := h.App.Config()\n\tdata := dto.AdminWebGUIConfig{\n\t\tFontSet:          cfg.WebGUI.FontSet,\n\t\tRegisterIsEnable: cfg.User.RegisterIsEnable,\n\t\tAdminUID:         cfg.User.AdminUID,\n\t}\n\tresponse.ToResponse(code.Success.WithData(data))\n}\n\n// CheckAdmin checks if current user has admin privileges\n// @Summary Check admin permission\n// @Description Check if the current logged-in user has system admin privileges\n// @Tags Config\n// @Security UserAuthToken\n// @Produce json\n// @Success 200 {object} pkgapp.Res{data=dto.AdminCheckResponse} \"Success\"\n// @Router /api/admin/check [get]\nfunc (h *AdminControlHandler) CheckAdmin(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tcfg := h.App.Config()\n\tuid := pkgapp.GetUID(c)\n\n\tisAdmin := false\n\tif uid != 0 {\n\t\t// AdminUID 0 means no restriction, otherwise must match AdminUID\n\t\t// AdminUID 为 0 表示不限制，否则必须匹配 AdminUID\n\t\tif cfg.User.AdminUID == 0 || uid == int64(cfg.User.AdminUID) {\n\t\t\tisAdmin = true\n\t\t}\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(dto.AdminCheckResponse{\n\t\tIsAdmin: isAdmin,\n\t}))\n}\n\n// GetConfig retrieves admin configuration (requires admin privileges)\n// @Summary Get full admin config\n// @Description Get full system configuration information, requires admin privileges\n// @Tags Config\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Success 200 {object} pkgapp.Res{data=dto.AdminConfig} \"Success\"\n// @Failure 403 {object} pkgapp.Res \"Insufficient privileges\"\n// @Router /api/admin/config [get]\nfunc (h *AdminControlHandler) GetConfig(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tcfg := h.App.Config()\n\tlogger := h.App.Logger()\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tlogger.Error(\"apiRouter.WebGUI.GetConfig err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Deny access if AdminUID is configured and current user is not an admin\n\t// 当配置了管理员 UID 且当前用户不是管理员时，拒绝访问\n\tif cfg.User.AdminUID != 0 && uid != int64(cfg.User.AdminUID) {\n\t\tresponse.ToResponse(code.ErrorUserIsNotAdmin)\n\t\treturn\n\t}\n\n\tdata := &dto.AdminConfig{\n\t\tFontSet:                 cfg.WebGUI.FontSet,\n\t\tRegisterIsEnable:        cfg.User.RegisterIsEnable,\n\t\tFileChunkSize:           cfg.App.FileChunkSize,\n\t\tSoftDeleteRetentionTime: cfg.App.SoftDeleteRetentionTime,\n\t\tUploadSessionTimeout:    cfg.App.UploadSessionTimeout,\n\t\tHistoryKeepVersions:     cfg.App.HistoryKeepVersions,\n\t\tHistorySaveDelay:        cfg.App.HistorySaveDelay,\n\t\t// DefaultAPIFolder:        cfg.App.DefaultAPIFolder,\n\t\tAdminUID:         cfg.User.AdminUID,\n\t\tAuthTokenKey:     cfg.Security.AuthTokenKey,\n\t\tTokenExpiry:      cfg.Security.TokenExpiry,\n\t\tShareTokenKey:    cfg.Security.ShareTokenKey,\n\t\tShareTokenExpiry: cfg.Security.ShareTokenExpiry,\n\t\tPullSource:       cfg.App.PullSource,\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(data))\n}\n\n// UpdateConfig updates admin configuration (requires admin privileges)\n// @Summary Update admin config\n// @Description Modify full system configuration information, requires admin privileges\n// @Tags Config\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.AdminConfig true \"Config Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.AdminConfig} \"Success\"\n// @Failure 403 {object} pkgapp.Res \"Insufficient privileges\"\n// @Router /api/admin/config [post]\nfunc (h *AdminControlHandler) UpdateConfig(c *gin.Context) {\n\tparams := &dto.AdminConfig{}\n\tresponse := pkgapp.NewResponse(c)\n\tcfg := h.App.Config()\n\tlogger := h.App.Logger()\n\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\tlogger.Error(\"apiRouter.WebGUI.UpdateConfig.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tlogger.Error(\"apiRouter.WebGUI.UpdateConfig err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Deny access if AdminUID is configured and current user is not an admin\n\t// 当配置了管理员 UID 且当前用户不是管理员时，拒绝访问\n\tif cfg.User.AdminUID != 0 && uid != int64(cfg.User.AdminUID) {\n\t\tresponse.ToResponse(code.ErrorUserIsNotAdmin)\n\t\treturn\n\t}\n\n\t// Validate historyKeepVersions cannot be less than 100\n\t// 验证 historyKeepVersions 不能小于 100\n\tif params.HistoryKeepVersions > 0 && params.HistoryKeepVersions < 100 {\n\t\tlogger.Warn(\"apiRouter.WebGUI.UpdateConfig invalid historyKeepVersions\",\n\t\t\tzap.Int(\"value\", params.HistoryKeepVersions))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(\"historyKeepVersions must be at least 100\"))\n\t\treturn\n\t}\n\n\t// Validate historySaveDelay cannot be less than 10 seconds\n\t// 验证 historySaveDelay 不能小于 10 秒\n\tif params.HistorySaveDelay != \"\" {\n\t\tdelay, err := util.ParseDuration(params.HistorySaveDelay)\n\t\tif err != nil {\n\t\t\tlogger.Warn(\"apiRouter.WebGUI.UpdateConfig invalid historySaveDelay format\",\n\t\t\t\tzap.String(\"value\", params.HistorySaveDelay))\n\t\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(\"historySaveDelay format invalid, e.g. 10s, 1m\"))\n\t\t\treturn\n\t\t}\n\t\tif delay < 10*time.Second {\n\t\t\tlogger.Warn(\"apiRouter.WebGUI.UpdateConfig historySaveDelay too small\",\n\t\t\t\tzap.String(\"value\", params.HistorySaveDelay))\n\t\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(\"historySaveDelay must be at least 10s\"))\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Update configuration\n\t// 更新配置\n\tcfg.WebGUI.FontSet = params.FontSet\n\tcfg.User.RegisterIsEnable = params.RegisterIsEnable\n\tcfg.App.FileChunkSize = params.FileChunkSize\n\tcfg.App.SoftDeleteRetentionTime = params.SoftDeleteRetentionTime\n\tcfg.App.UploadSessionTimeout = params.UploadSessionTimeout\n\tcfg.App.HistoryKeepVersions = params.HistoryKeepVersions\n\tcfg.App.HistorySaveDelay = params.HistorySaveDelay\n\t//cfg.App.DefaultAPIFolder = params.DefaultAPIFolder\n\tcfg.User.AdminUID = params.AdminUID\n\tcfg.Security.AuthTokenKey = params.AuthTokenKey\n\tcfg.Security.TokenExpiry = params.TokenExpiry\n\tcfg.Security.ShareTokenKey = params.ShareTokenKey\n\tcfg.Security.ShareTokenExpiry = params.ShareTokenExpiry\n\tcfg.App.PullSource = params.PullSource\n\n\t// Save configuration to file\n\t// 保存配置到文件\n\tif err := cfg.Save(); err != nil {\n\t\tlogger.Error(\"apiRouter.WebGUI.UpdateConfig.Save err\", zap.Error(err))\n\t\tresponse.ToResponse(code.ErrorConfigSaveFailed)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(params))\n}\n\n// GetUserDatabaseConfig retrieves user database configuration (requires admin privileges)\n// @Summary Get user database config\n// @Description Get user database configuration information, requires admin privileges\n// @Tags Config\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Success 200 {object} pkgapp.Res{data=dto.AdminUserDatabaseConfig} \"Success\"\n// @Failure 403 {object} pkgapp.Res \"Insufficient privileges\"\n// @Router /api/admin/config/user_database [get]\nfunc (h *AdminControlHandler) GetUserDatabaseConfig(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tcfg := h.App.Config()\n\tlogger := h.App.Logger()\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tlogger.Error(\"apiRouter.AdminControl.GetUserDatabaseConfig err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Deny access if AdminUID is configured and current user is not an admin\n\t// 当配置了管理员 UID 且当前用户不是管理员时，拒绝访问\n\tif cfg.User.AdminUID != 0 && uid != int64(cfg.User.AdminUID) {\n\t\tresponse.ToResponse(code.ErrorUserIsNotAdmin)\n\t\treturn\n\t}\n\n\tdbCfg := cfg.UserDatabase\n\tdata := &dto.AdminUserDatabaseConfig{\n\t\tType:                dbCfg.Type,\n\t\tPath:                dbCfg.Path,\n\t\tUserName:            dbCfg.UserName,\n\t\tPassword:            dbCfg.Password,\n\t\tHost:                dbCfg.Host,\n\t\tPort:                dbCfg.Port,\n\t\tName:                dbCfg.Name,\n\t\tSSLMode:             dbCfg.SSLMode,\n\t\tSchema:              dbCfg.Schema,\n\t\tMaxIdleConns:        dbCfg.MaxIdleConns,\n\t\tMaxOpenConns:        dbCfg.MaxOpenConns,\n\t\tConnMaxLifetime:     dbCfg.ConnMaxLifetime,\n\t\tConnMaxIdleTime:     dbCfg.ConnMaxIdleTime,\n\t\tMaxWriteConcurrency: dbCfg.MaxWriteConcurrency,\n\t\tCharset:             dbCfg.Charset,\n\t\tParseTime:           dbCfg.ParseTime,\n\t}\n\tresponse.ToResponse(code.Success.WithData(data))\n}\n\n// UpdateUserDatabaseConfig updates user database configuration (requires admin privileges)\n// @Summary Update user database config\n// @Description Modify user database configuration information, requires admin privileges\n// @Tags Config\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.AdminUserDatabaseConfig true \"Config Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.AdminUserDatabaseConfig} \"Success\"\n// @Failure 403 {object} pkgapp.Res \"Insufficient privileges\"\n// @Router /api/admin/config/user_database [post]\nfunc (h *AdminControlHandler) UpdateUserDatabaseConfig(c *gin.Context) {\n\tparams := &dto.AdminUserDatabaseConfig{}\n\tresponse := pkgapp.NewResponse(c)\n\tcfg := h.App.Config()\n\tlogger := h.App.Logger()\n\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\tlogger.Error(\"apiRouter.AdminControl.UpdateUserDatabaseConfig.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tlogger.Error(\"apiRouter.AdminControl.UpdateUserDatabaseConfig err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Deny access if AdminUID is configured and current user is not an admin\n\t// 当配置了管理员 UID 且当前用户不是管理员时，拒绝访问\n\tif cfg.User.AdminUID != 0 && uid != int64(cfg.User.AdminUID) {\n\t\tresponse.ToResponse(code.ErrorUserIsNotAdmin)\n\t\treturn\n\t}\n\n\t// Update configuration\n\t// 更新配置\n\tcfg.UserDatabase.Type = params.Type\n\tcfg.UserDatabase.Path = params.Path\n\tcfg.UserDatabase.UserName = params.UserName\n\tcfg.UserDatabase.Password = params.Password\n\tcfg.UserDatabase.Host = params.Host\n\tcfg.UserDatabase.Port = params.Port\n\tcfg.UserDatabase.Name = params.Name\n\tcfg.UserDatabase.SSLMode = params.SSLMode\n\tcfg.UserDatabase.Schema = params.Schema\n\tcfg.UserDatabase.MaxIdleConns = params.MaxIdleConns\n\tcfg.UserDatabase.MaxOpenConns = params.MaxOpenConns\n\tcfg.UserDatabase.ConnMaxLifetime = params.ConnMaxLifetime\n\tcfg.UserDatabase.ConnMaxIdleTime = params.ConnMaxIdleTime\n\tcfg.UserDatabase.MaxWriteConcurrency = params.MaxWriteConcurrency\n\tcfg.UserDatabase.Charset = params.Charset\n\tcfg.UserDatabase.ParseTime = params.ParseTime\n\n\t// MySQL specific hardcoded defaults\n\t// MySQL 的硬编码默认逻辑\n\tif params.Type == \"mysql\" {\n\t\tcfg.UserDatabase.Charset = \"utf8mb4\"\n\t\tcfg.UserDatabase.ParseTime = true\n\t}\n\n\tif params.Type == \"sqlite\" {\n\t\tenableQueue := true\n\t\tcfg.UserDatabase.EnableWriteQueue = &enableQueue\n\t} else if params.Type == \"mysql\" || params.Type == \"postgres\" {\n\t\tenableQueue := false\n\t\tcfg.UserDatabase.EnableWriteQueue = &enableQueue\n\t}\n\n\t// Save configuration to file\n\t// 保存配置到文件\n\tif err := cfg.Save(); err != nil {\n\t\tlogger.Error(\"apiRouter.AdminControl.UpdateUserDatabaseConfig.Save err\", zap.Error(err))\n\t\tresponse.ToResponse(code.ErrorConfigSaveFailed)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(params))\n}\n\n// ValidateUserDatabaseConfig tests user database connection (requires admin privileges)\n// @Summary Test user database connection\n// ValidateUserDatabaseConfig tests user database connection (requires admin privileges)\n// @Summary Test user database connection\n// @Description Test if the provided database configuration can connect successfully, requires admin privileges\n// @Tags Config\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.AdminUserDatabaseConfig true \"Config Parameters\"\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Failure 400 {object} pkgapp.Res \"Connection failed\"\n// @Router /api/admin/config/user_database/test [post]\nfunc (h *AdminControlHandler) ValidateUserDatabaseConfig(c *gin.Context) {\n\tparams := &dto.AdminUserDatabaseConfig{}\n\tresponse := pkgapp.NewResponse(c)\n\tlogger := h.App.Logger()\n\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\tlogger.Error(\"apiRouter.AdminControl.ValidateUserDatabaseConfig.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\tcfg := h.App.Config()\n\t// Deny access if AdminUID is configured and current user is not an admin\n\t// 当配置了管理员 UID 且当前用户不是管理员时，拒绝访问\n\tif cfg.User.AdminUID != 0 && uid != int64(cfg.User.AdminUID) {\n\t\tresponse.ToResponse(code.ErrorUserIsNotAdmin)\n\t\treturn\n\t}\n\n\t// Map DTO to DatabaseConfig\n\t// 将 DTO 映射到 DatabaseConfig\n\tenableQueue := false\n\tif params.Type == \"sqlite\" {\n\t\tenableQueue = true\n\t}\n\tdbCfg := config.DatabaseConfig{\n\t\tType:                params.Type,\n\t\tPath:                params.Path,\n\t\tUserName:            params.UserName,\n\t\tPassword:            params.Password,\n\t\tHost:                params.Host,\n\t\tPort:                params.Port,\n\t\tName:                params.Name,\n\t\tSSLMode:             params.SSLMode,\n\t\tSchema:              params.Schema,\n\t\tAutoMigrate:         true,\n\t\tMaxIdleConns:        params.MaxIdleConns,\n\t\tMaxOpenConns:        params.MaxOpenConns,\n\t\tConnMaxLifetime:     params.ConnMaxLifetime,\n\t\tConnMaxIdleTime:     params.ConnMaxIdleTime,\n\t\tEnableWriteQueue:    &enableQueue,\n\t\tMaxWriteConcurrency: params.MaxWriteConcurrency,\n\t\tCharset:             params.Charset,\n\t\tParseTime:           params.ParseTime,\n\t}\n\n\t// Apply hardcoded default rules for MySQL during validation\n\t// 在测试连接时也应用 MySQL 的硬编码默认规则\n\tif params.Type == \"mysql\" {\n\t\tdbCfg.Charset = \"utf8mb4\"\n\t\tdbCfg.ParseTime = true\n\t}\n\n\t// Use dao.NewEngine to test connection\n\t// 使用 dao.NewEngine 测试连接\n\tdb, err := dao.NewEngine(dbCfg, h.App.Logger())\n\tif err != nil {\n\t\tlogger.Warn(\"Database connection test failed\", zap.Error(err))\n\t\tresponse.ToResponse(code.Failed.WithDetails(\"Connection failed: \" + err.Error()))\n\t\treturn\n\t}\n\n\tsqlDB, err := db.DB()\n\tif err != nil {\n\t\tresponse.ToResponse(code.Failed.WithDetails(\"Failed to get DB instance: \" + err.Error()))\n\t\treturn\n\t}\n\tdefer sqlDB.Close()\n\n\tif err := sqlDB.Ping(); err != nil {\n\t\tlogger.Warn(\"Database ping failed\", zap.Error(err))\n\t\tresponse.ToResponse(code.Failed.WithDetails(\"Ping failed: \" + err.Error()))\n\t\treturn\n\t}\n\n\t// For MySQL, verify CREATE DATABASE privilege\n\t// 针对 MySQL，验证是否具有创建数据库的权限\n\tif params.Type == \"mysql\" {\n\t\ttempDBName := fmt.Sprintf(\"fn_perm_check_%d\", time.Now().Unix())\n\t\tif err := db.Exec(fmt.Sprintf(\"CREATE DATABASE %s\", tempDBName)).Error; err != nil {\n\t\t\tlogger.Warn(\"MySQL CREATE DATABASE permission test failed\", zap.Error(err))\n\t\t\tresponse.ToResponse(code.Failed.WithDetails(\"Missing CREATE DATABASE permission: \" + err.Error()))\n\t\t\treturn\n\t\t}\n\t\t// Clean up immediately after successful verification\n\t\t// 验证成功后立即清理\n\t\t_ = db.Exec(fmt.Sprintf(\"DROP DATABASE %s\", tempDBName))\n\t}\n\n\tresponse.ToResponse(code.Success.WithDetails(\"Database connection and permission verification successful\"))\n}\n\n// GetNgrokConfig retrieves Ngrok tunnel configuration (requires admin privileges)\n// @Summary Get Ngrok config\n// @Description Get Ngrok tunnel configuration, requires admin privileges\n// @Tags Config\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Success 200 {object} pkgapp.Res{data=dto.AdminNgrokConfig} \"Success\"\n// @Failure 403 {object} pkgapp.Res \"Insufficient privileges\"\n// @Router /api/admin/config/ngrok [get]\nfunc (h *AdminControlHandler) GetNgrokConfig(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tcfg := h.App.Config()\n\tlogger := h.App.Logger()\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tlogger.Error(\"apiRouter.AdminControl.GetNgrokConfig err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\tif cfg.User.AdminUID != 0 && uid != int64(cfg.User.AdminUID) {\n\t\tresponse.ToResponse(code.ErrorUserIsNotAdmin)\n\t\treturn\n\t}\n\n\tdata := &dto.AdminNgrokConfig{\n\t\tEnabled:   cfg.Ngrok.Enabled,\n\t\tAuthToken: cfg.Ngrok.AuthToken,\n\t\tDomain:    cfg.Ngrok.Domain,\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(data))\n}\n\n// UpdateNgrokConfig updates Ngrok tunnel configuration (requires admin privileges)\n// @Summary Update Ngrok config\n// @Description Modify Ngrok tunnel configuration, requires admin privileges\n// @Tags Config\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.AdminNgrokConfig true \"Config Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.AdminNgrokConfig} \"Success\"\n// @Failure 403 {object} pkgapp.Res \"Insufficient privileges\"\n// @Router /api/admin/config/ngrok [post]\nfunc (h *AdminControlHandler) UpdateNgrokConfig(c *gin.Context) {\n\tparams := &dto.AdminNgrokConfig{}\n\tresponse := pkgapp.NewResponse(c)\n\tcfg := h.App.Config()\n\tlogger := h.App.Logger()\n\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\tlogger.Error(\"apiRouter.AdminControl.UpdateNgrokConfig.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tlogger.Error(\"apiRouter.AdminControl.UpdateNgrokConfig err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\tif cfg.User.AdminUID != 0 && uid != int64(cfg.User.AdminUID) {\n\t\tresponse.ToResponse(code.ErrorUserIsNotAdmin)\n\t\treturn\n\t}\n\n\tcfg.Ngrok.Enabled = params.Enabled\n\tcfg.Ngrok.AuthToken = params.AuthToken\n\tcfg.Ngrok.Domain = params.Domain\n\n\tif err := cfg.Save(); err != nil {\n\t\tlogger.Error(\"apiRouter.AdminControl.UpdateNgrokConfig.Save err\", zap.Error(err))\n\t\tresponse.ToResponse(code.ErrorConfigSaveFailed)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(params))\n}\n\n// GetCloudflareConfig retrieves Cloudflare tunnel configuration (requires admin privileges)\n// @Summary Get Cloudflare config\n// @Description Get Cloudflare tunnel configuration, requires admin privileges\n// @Tags Config\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Success 200 {object} pkgapp.Res{data=dto.AdminCloudflareConfig} \"Success\"\n// @Failure 403 {object} pkgapp.Res \"Insufficient privileges\"\n// @Router /api/admin/config/cloudflare [get]\nfunc (h *AdminControlHandler) GetCloudflareConfig(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tcfg := h.App.Config()\n\tlogger := h.App.Logger()\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tlogger.Error(\"apiRouter.AdminControl.GetCloudflareConfig err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\tif cfg.User.AdminUID != 0 && uid != int64(cfg.User.AdminUID) {\n\t\tresponse.ToResponse(code.ErrorUserIsNotAdmin)\n\t\treturn\n\t}\n\n\tdata := &dto.AdminCloudflareConfig{\n\t\tEnabled:    cfg.Cloudflare.Enabled,\n\t\tToken:      cfg.Cloudflare.Token,\n\t\tLogEnabled: cfg.Cloudflare.LogEnabled,\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(data))\n}\n\n// UpdateCloudflareConfig updates Cloudflare tunnel configuration (requires admin privileges)\n// @Summary Update Cloudflare config\n// @Description Modify Cloudflare tunnel configuration, requires admin privileges\n// @Tags Config\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.AdminCloudflareConfig true \"Config Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.AdminCloudflareConfig} \"Success\"\n// @Failure 403 {object} pkgapp.Res \"Insufficient privileges\"\n// @Router /api/admin/config/cloudflare [post]\nfunc (h *AdminControlHandler) UpdateCloudflareConfig(c *gin.Context) {\n\tparams := &dto.AdminCloudflareConfig{}\n\tresponse := pkgapp.NewResponse(c)\n\tcfg := h.App.Config()\n\tlogger := h.App.Logger()\n\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\tlogger.Error(\"apiRouter.AdminControl.UpdateCloudflareConfig.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tlogger.Error(\"apiRouter.AdminControl.UpdateCloudflareConfig err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\tif cfg.User.AdminUID != 0 && uid != int64(cfg.User.AdminUID) {\n\t\tresponse.ToResponse(code.ErrorUserIsNotAdmin)\n\t\treturn\n\t}\n\n\tcfg.Cloudflare.Enabled = params.Enabled\n\tcfg.Cloudflare.Token = params.Token\n\tcfg.Cloudflare.LogEnabled = params.LogEnabled\n\n\tif err := cfg.Save(); err != nil {\n\t\tlogger.Error(\"apiRouter.AdminControl.UpdateCloudflareConfig.Save err\", zap.Error(err))\n\t\tresponse.ToResponse(code.ErrorConfigSaveFailed)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(params))\n}\n\n// GetSystemInfo retrieves system monitoring information (requires admin privileges)\n// @Summary Get system stats\n// @Description Get server runtime, CPU, memory, host and process info, requires admin privileges\n// @Tags System\n// @Produce json\n// @Security UserAuthToken\n// @Success 200 {object} pkgapp.Res{data=dto.AdminSystemInfo} \"Success\"\n// @Router /api/admin/system/info [get]\nfunc (h *AdminControlHandler) GetSystemInfo(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tcfg := h.App.Config()\n\tlogger := h.App.Logger()\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tlogger.Error(\"apiRouter.WebGUI.GetSystemInfo err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\tif cfg.User.AdminUID != 0 && uid != int64(cfg.User.AdminUID) {\n\t\tresponse.ToResponse(code.ErrorUserIsNotAdmin)\n\t\treturn\n\t}\n\n\t// Go Runtime\n\tvar m runtime.MemStats\n\truntime.ReadMemStats(&m)\n\n\t// CPU\n\tcpuInfoList, _ := cpu.Info()\n\tcpuModel := \"\"\n\tif len(cpuInfoList) > 0 {\n\t\tcpuModel = cpuInfoList[0].ModelName\n\t}\n\tphysCores, _ := cpu.Counts(false)\n\tlogicCores, _ := cpu.Counts(true)\n\tcpuPercents, _ := cpu.Percent(time.Second, true)\n\tloadStat, _ := load.Avg()\n\n\t// Memory\n\tvMem, _ := mem.VirtualMemory()\n\tswapMem, _ := mem.SwapMemory()\n\n\t// Host\n\thInfo, _ := host.Info()\n\n\t// Process\n\tp, _ := process.NewProcess(int32(os.Getpid()))\n\tpName, _ := p.Name()\n\tpPPid, _ := p.Ppid()\n\tpCPU, _ := p.CPUPercent()\n\tpMem, _ := p.MemoryPercent()\n\n\tdata := dto.AdminSystemInfo{\n\t\tStartTime: h.App.StartTime,\n\t\tUptime:    time.Since(h.App.StartTime).Seconds(),\n\t\tRuntimeStatus: dto.AdminRuntimeInfo{\n\t\t\tNumGoroutine: runtime.NumGoroutine(),\n\t\t\tMemAlloc:     m.Alloc,\n\t\t\tMemTotal:     m.TotalAlloc,\n\t\t\tMemSys:       m.Sys,\n\t\t\tHeapSys:      m.HeapSys,\n\t\t\tHeapIdle:     m.HeapIdle,\n\t\t\tHeapInuse:    m.HeapInuse,\n\t\t\tHeapReleased: m.HeapReleased,\n\t\t\tStackSys:     m.StackSys,\n\t\t\tMSpanSys:     m.MSpanSys,\n\t\t\tMCacheSys:    m.MCacheSys,\n\t\t\tBuckHashSys:  m.BuckHashSys,\n\t\t\tGCSys:        m.GCSys,\n\t\t\tOtherSys:     m.OtherSys,\n\t\t\tNextGC:       m.NextGC,\n\t\t\tNumGC:        m.NumGC,\n\t\t},\n\t\tCPU: dto.AdminCPUInfo{\n\t\t\tModelName:     cpuModel,\n\t\t\tPhysicalCores: physCores,\n\t\t\tLogicalCores:  logicCores,\n\t\t\tPercent:       cpuPercents,\n\t\t\tLoadAvg: &dto.AdminLoadInfo{\n\t\t\t\tLoad1:  loadStat.Load1,\n\t\t\t\tLoad5:  loadStat.Load5,\n\t\t\t\tLoad15: loadStat.Load15,\n\t\t\t},\n\t\t},\n\t\tMemory: dto.AdminMemoryInfo{\n\t\t\tTotal:           vMem.Total,\n\t\t\tAvailable:       vMem.Available,\n\t\t\tUsed:            vMem.Used,\n\t\t\tUsedPercent:     vMem.UsedPercent,\n\t\t\tSwapTotal:       swapMem.Total,\n\t\t\tSwapUsed:        swapMem.Used,\n\t\t\tSwapUsedPercent: swapMem.UsedPercent,\n\t\t},\n\t\tHost: dto.AdminHostInfo{\n\t\t\tHostname:      hInfo.Hostname,\n\t\t\tOS:            hInfo.OS,\n\t\t\tOSPretty:      util.GetOSPrettyName(),\n\t\t\tPlatform:      hInfo.Platform,\n\t\t\tArch:          hInfo.KernelArch,\n\t\t\tKernelVersion: hInfo.KernelVersion,\n\t\t\tUptime:        hInfo.Uptime,\n\t\t\tCurrentTime:   time.Now(),\n\t\t\tTimeZone:      time.Now().Location().String(),\n\t\t\tTimeZoneOffset: func() int {\n\t\t\t\t_, offset := time.Now().Zone()\n\t\t\t\treturn offset\n\t\t\t}(),\n\t\t},\n\t\tProcess: dto.AdminProcessInfo{\n\t\t\tPID:           p.Pid,\n\t\t\tPPID:          pPPid,\n\t\t\tName:          pName,\n\t\t\tCPUPercent:    pCPU,\n\t\t\tMemoryPercent: pMem,\n\t\t},\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(data))\n}\n\n// Upgrade triggers server automatic upgrade\n// @Summary Trigger server upgrade\n// @Description Download latest version and restart server\n// @Tags System\n// @Produce json\n// @Security UserAuthToken\n// @Param version query string true \"Version to upgrade (e.g. 2.0.10 or latest)\"\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Router /api/admin/upgrade [get]\nfunc (h *AdminControlHandler) Upgrade(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tcfg := h.App.Config()\n\tuid := pkgapp.GetUID(c)\n\n\tif cfg.User.AdminUID != 0 && uid != int64(cfg.User.AdminUID) {\n\t\tresponse.ToResponse(code.ErrorUserIsNotAdmin)\n\t\treturn\n\t}\n\n\tvar upgradeReq dto.UpgradeRequest\n\tif ok, validErrs := pkgapp.BindAndValid(c, &upgradeReq); !ok {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(validErrs.Errors()...))\n\t\treturn\n\t}\n\n\tcheckInfo := h.App.CheckVersion(\"\")\n\tversion := \"\"\n\n\tif upgradeReq.Version == \"latest\" {\n\t\tif !checkInfo.VersionIsNew {\n\t\t\tresponse.ToResponse(code.Success.WithDetails(\"Current version is already up to date\"))\n\t\t\treturn\n\t\t}\n\t\tversion = checkInfo.VersionNewName\n\t} else {\n\t\tversion = upgradeReq.Version\n\t}\n\n\tversionRaw := strings.TrimPrefix(version, \"v\")\n\n\t// Determine download URL\n\t// 确定下载地址\n\tgoos := runtime.GOOS\n\tgoarch := runtime.GOARCH\n\n\t// Example: fast-note-sync-service-2.0.10-linux-amd64.tar.gz\n\tfileName := fmt.Sprintf(\"fast-note-sync-service-%s-%s-%s.tar.gz\", versionRaw, goos, goarch)\n\tdownloadURL := \"\"\n\tif checkInfo.GithubAvailable {\n\t\t// GitHub releases/download/[tag]/[filename]\n\t\t// Based on user feedback: URL should NOT have 'v' in the tag part if the tag itself doesn't have it\n\t\tdownloadURL = fmt.Sprintf(\"https://github.com/haierkeys/fast-note-sync-service/releases/download/%s/%s\", versionRaw, fileName)\n\t} else {\n\t\t// CNB download URL format\n\t\tdownloadURL = fmt.Sprintf(\"https://cnb.cool/haierkeys/fast-note-sync-service/-/releases/download/%s/%s\", versionRaw, fileName)\n\t}\n\n\th.App.Logger().Info(\"Starting upgrade download\", zap.String(\"url\", downloadURL), zap.String(\"version\", versionRaw))\n\n\t// Prepare temp directory\n\t// 使用 storage/temp/upgrade 作为临时目录\n\ttempDir := filepath.Join(\"storage\", \"temp\", \"upgrade\")\n\t_ = os.RemoveAll(tempDir)\n\tif err := os.MkdirAll(tempDir, 0755); err != nil {\n\t\tresponse.ToResponse(code.Failed.WithDetails(\"Failed to create temp directory: \" + err.Error()))\n\t\treturn\n\t}\n\n\t// Download\n\ttarPath := filepath.Join(tempDir, fileName)\n\tif err := h.downloadFile(downloadURL, tarPath); err != nil {\n\t\tresponse.ToResponse(code.Failed.WithDetails(\"Download failed: \" + err.Error()))\n\t\treturn\n\t}\n\n\t// Extract\n\tbinaryName := \"fast-note-sync-service\"\n\tif goos == \"windows\" {\n\t\tbinaryName += \".exe\"\n\t}\n\textractedBinaryPath := filepath.Join(tempDir, binaryName)\n\n\tif err := h.extractBinary(tarPath, tempDir, binaryName); err != nil {\n\t\tresponse.ToResponse(code.Failed.WithDetails(\"Extract failed: \" + err.Error()))\n\t\treturn\n\t}\n\n\t// Trigger upgrade in App\n\th.App.TriggerUpgrade(extractedBinaryPath)\n\n\tresponse.ToResponse(code.Success.WithDetails(\"Upgrade triggered, server is restarting...\"))\n}\n\n// Restart triggers server automatic restart\n// @Summary Trigger server restart\n// @Description Gracefully restart the server\n// @Tags System\n// @Produce json\n// @Security UserAuthToken\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Router /api/admin/restart [get]\nfunc (h *AdminControlHandler) Restart(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tcfg := h.App.Config()\n\tuid := pkgapp.GetUID(c)\n\n\tif cfg.User.AdminUID != 0 && uid != int64(cfg.User.AdminUID) {\n\t\tresponse.ToResponse(code.ErrorUserIsNotAdmin)\n\t\treturn\n\t}\n\n\tcurrentBinary, err := os.Executable()\n\tif err != nil {\n\t\tresponse.ToResponse(code.Failed.WithDetails(\"Failed to get current executable path: \" + err.Error()))\n\t\treturn\n\t}\n\n\th.App.TriggerUpgrade(currentBinary)\n\n\tresponse.ToResponse(code.Success.WithDetails(\"Restart triggered, server is restarting...\"))\n}\n\n// GC triggers manual garbage collection and releases memory to OS (requires admin privileges)\n// GC 手动触发垃圾回收并释放内存给操作系统（需要管理员权限）\n// @Summary Trigger manual GC\n// @Description Manually run Go runtime GC and release memory to OS, requires admin privileges\n// @Tags System\n// @Produce json\n// @Security UserAuthToken\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Failure 403 {object} pkgapp.Res \"Insufficient privileges\"\n// @Router /api/admin/gc [get]\nfunc (h *AdminControlHandler) GC(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tcfg := h.App.Config()\n\tlogger := h.App.Logger()\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\tif cfg.User.AdminUID != 0 && uid != int64(cfg.User.AdminUID) {\n\t\tresponse.ToResponse(code.ErrorUserIsNotAdmin)\n\t\treturn\n\t}\n\n\tvar mBefore, mAfter runtime.MemStats\n\truntime.ReadMemStats(&mBefore)\n\n\tstartTime := time.Now()\n\t// Trigger GC // 触发 GC\n\truntime.GC()\n\t// Release memory to OS // 释放内存给操作系统\n\tdebug.FreeOSMemory()\n\tduration := time.Since(startTime)\n\n\truntime.ReadMemStats(&mAfter)\n\n\tmemReleased := int64(mBefore.Alloc) - int64(mAfter.Alloc)\n\tlogger.Info(\"Manual GC completed\",\n\t\tzap.Duration(\"duration\", duration),\n\t\tzap.Uint64(\"allocBefore\", mBefore.Alloc),\n\t\tzap.Uint64(\"allocAfter\", mAfter.Alloc),\n\t\tzap.Int64(\"released\", memReleased),\n\t)\n\n\tdata := gin.H{\n\t\t\"duration\":    duration.String(),\n\t\t\"allocBefore\": mBefore.Alloc,\n\t\t\"allocAfter\":  mAfter.Alloc,\n\t\t\"released\":    memReleased,\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(data).WithDetails(\"Manual GC completed successfully\"))\n}\n\n// GetWSClients retrieves all currently connected WebSocket clients (requires admin privileges)\n// @Summary Get connected WebSocket clients\n// @Description Get a list of all current WebSocket connections, requires admin privileges\n// @Tags System\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Success 200 {object} pkgapp.Res{data=[]pkgapp.WSClientInfo} \"Success\"\n// @Failure 403 {object} pkgapp.Res \"Insufficient privileges\"\n// @Router /api/admin/ws_clients [get]\nfunc (h *AdminControlHandler) GetWSClients(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tcfg := h.App.Config()\n\tuid := pkgapp.GetUID(c)\n\n\tif uid == 0 {\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\tif cfg.User.AdminUID != 0 && uid != int64(cfg.User.AdminUID) {\n\t\tresponse.ToResponse(code.ErrorUserIsNotAdmin)\n\t\treturn\n\t}\n\n\tclients := h.wss.GetClients()\n\tresponse.ToResponse(code.Success.WithData(clients))\n}\n\nfunc (h *AdminControlHandler) downloadFile(url string, dest string) error {\n\tresp, err := http.Get(url)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"bad status: %s\", resp.Status)\n\t}\n\n\tout, err := os.Create(dest)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer out.Close()\n\n\t_, err = io.Copy(out, resp.Body)\n\treturn err\n}\n\nfunc (h *AdminControlHandler) extractBinary(tarPath string, destDir string, binaryName string) error {\n\tf, err := os.Open(tarPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\n\tgzr, err := gzip.NewReader(f)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer gzr.Close()\n\n\ttr := tar.NewReader(gzr)\n\n\tfor {\n\t\theader, err := tr.Next()\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Check if it's the binary we're looking for\n\t\t// Often files in tar.gz are in a subdirectory or have different names\n\t\t// In alpha-release.yml: tar -czvf ... . (contents of build/platform dir)\n\t\tif filepath.Base(header.Name) == binaryName {\n\t\t\ttarget := filepath.Join(destDir, binaryName)\n\t\t\tf, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif _, err := io.Copy(f, tr); err != nil {\n\t\t\t\tf.Close()\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tf.Close()\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"binary %s not found in archive\", binaryName)\n}\n\n// CloudflaredTunnelDownload triggers cloudflared binary download (requires admin privileges)\n// @Summary Download cloudflared binary\n// @Description Trigger the download of cloudflared binary for the current platform\n// @Tags System\n// @Security UserAuthToken\n// @Produce json\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Router /api/admin/cloudflared_tunnel_download [get]\nfunc (h *AdminControlHandler) CloudflaredTunnelDownload(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tcfg := h.App.Config()\n\tuid := pkgapp.GetUID(c)\n\n\tif cfg.User.AdminUID != 0 && uid != int64(cfg.User.AdminUID) {\n\t\tresponse.ToResponse(code.ErrorUserIsNotAdmin)\n\t\treturn\n\t}\n\n\th.App.Logger().Info(\"Starting manual cloudflared binary download via API\")\n\n\tpath, err := h.App.CloudflareService.DownloadBinary()\n\tif err != nil {\n\t\th.App.Logger().Error(\"Manual cloudflared download failed\", zap.Error(err))\n\t\t// 返回详细的错误提示（包含 DownloadBinary 中构造的建议）\n\t\tresponse.ToResponse(code.ErrorCloudflaredDownloadFailed.WithDetails(err.Error()))\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(gin.H{\"path\": path}).WithDetails(\"Cloudflared binary is ready\"))\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_admin_control_test.go",
    "content": "package api_router\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc newAdminTestContext(method, url, body string, uid int64) (*gin.Context, *httptest.ResponseRecorder) {\n\tw := httptest.NewRecorder()\n\tvar req *http.Request\n\tif body != \"\" {\n\t\treq = httptest.NewRequest(method, url, strings.NewReader(body))\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t} else {\n\t\treq = httptest.NewRequest(method, url, nil)\n\t}\n\n\tc, _ := gin.CreateTestContext(w)\n\tc.Request = req\n\tif uid > 0 {\n\t\tc.Set(\"user_token\", &pkgapp.UserEntity{UID: uid})\n\t}\n\treturn c, w\n}\n\nfunc newTestAdminHandler() (*AdminControlHandler, *app.App) {\n\ttestApp := app.NewTestApp(nil)\n\t// Set mock config values\n\tcfg := testApp.Config()\n\tcfg.User.AdminUID = 1\n\tcfg.WebGUI.FontSet = \"Inter\"\n\t\n\twss := pkgapp.NewWebsocketServer(pkgapp.WSConfig{}, testApp)\n\treturn NewAdminControlHandler(testApp, wss), testApp\n}\n\nfunc TestAdminControlHandler_Config_Success(t *testing.T) {\n\thandler, _ := newTestAdminHandler()\n\tc, w := newAdminTestContext(\"GET\", \"/api/webgui/config\", \"\", 0)\n\n\thandler.Config(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\t\n\tvar resp struct {\n\t\tData dto.AdminWebGUIConfig `json:\"data\"`\n\t}\n\tjson.Unmarshal(w.Body.Bytes(), &resp)\n\tassert.Equal(t, \"Inter\", resp.Data.FontSet)\n}\n\nfunc TestAdminControlHandler_GetConfig_Success(t *testing.T) {\n\thandler, _ := newTestAdminHandler()\n\tc, w := newAdminTestContext(\"GET\", \"/api/admin/config\", \"\", 1) // UID 1 is admin\n\n\thandler.GetConfig(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n}\n\nfunc TestAdminControlHandler_GetConfig_Forbidden(t *testing.T) {\n\thandler, _ := newTestAdminHandler()\n\tc, w := newAdminTestContext(\"GET\", \"/api/admin/config\", \"\", 2) // UID 2 is not admin\n\n\thandler.GetConfig(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.ErrorUserIsNotAdmin.Code())\n}\n\nfunc TestAdminControlHandler_GetSystemInfo_Success(t *testing.T) {\n\thandler, _ := newTestAdminHandler()\n\tc, w := newAdminTestContext(\"GET\", \"/api/admin/system/info\", \"\", 1)\n\n\thandler.GetSystemInfo(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tassert.Contains(t, w.Body.String(), `\"uptime\"`)\n}\n\nfunc TestAdminControlHandler_GC_Success(t *testing.T) {\n\thandler, _ := newTestAdminHandler()\n\tc, w := newAdminTestContext(\"GET\", \"/api/admin/gc\", \"\", 1)\n\n\thandler.GC(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tassert.Contains(t, w.Body.String(), \"Manual GC completed\")\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_backup.go",
    "content": "package api_router\n\nimport (\n\t\"context\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/middleware\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\tapperrors \"github.com/haierkeys/fast-note-sync-service/pkg/errors\"\n\t\"go.uber.org/zap\"\n)\n\n// BackupHandler backup API router handler\ntype BackupHandler struct {\n\t*Handler\n}\n\n// NewBackupHandler creates BackupHandler instance\nfunc NewBackupHandler(a *app.App) *BackupHandler {\n\treturn &BackupHandler{\n\t\tHandler: NewHandler(a),\n\t}\n}\n\n// GetConfigs gets backup configurations\n// @Summary Get backup configurations\n// @Tags Backup\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Success 200 {object} pkgapp.Res{data=[]dto.BackupConfigDTO} \"Success\"\n// @Failure 401 {object} pkgapp.Res \"Token Required\"\n// @Failure 500 {object} pkgapp.Res \"Internal Server Error\"\n// @Router /api/backup/configs [get]\nfunc (h *BackupHandler) GetConfigs(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\tconfigs, err := h.App.BackupService.GetConfigs(c.Request.Context(), uid)\n\tif err != nil {\n\t\th.logError(c.Request.Context(), \"BackupHandler.GetConfigs\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(configs))\n}\n\n// UpdateConfig updates backup configuration\n// @Summary Update backup configuration\n// @Tags Backup\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.BackupConfigRequest true \"Backup Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.BackupConfigDTO} \"Success\"\n// @Failure 400 {object} pkgapp.Res \"Invalid Params\"\n// @Failure 401 {object} pkgapp.Res \"Token Required\"\n// @Failure 500 {object} pkgapp.Res \"Internal Server Error\"\n// @Router /api/backup/config [post]\nfunc (h *BackupHandler) UpdateConfig(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.BackupConfigRequest{}\n\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\tconfig, err := h.App.BackupService.UpdateConfig(c.Request.Context(), uid, params)\n\tif err != nil {\n\t\th.logError(c.Request.Context(), \"BackupHandler.UpdateConfig\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.SuccessUpdate.WithData(config))\n}\n\n// DeleteConfig deletes backup configuration\n// @Summary Delete backup configuration\n// @Tags Backup\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.BackupExecuteRequest true \"Config ID\"\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Failure 400 {object} pkgapp.Res \"Invalid Params\"\n// @Failure 401 {object} pkgapp.Res \"Token Required\"\n// @Failure 500 {object} pkgapp.Res \"Internal Server Error\"\n// @Router /api/backup/config [delete]\nfunc (h *BackupHandler) DeleteConfig(c *gin.Context) {\n\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.BackupExecuteRequest{} // Reusing ID struct\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\terr := h.App.BackupService.DeleteConfig(c.Request.Context(), uid, params.ID)\n\tif err != nil {\n\t\th.logError(c.Request.Context(), \"BackupHandler.DeleteConfig\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success)\n}\n\n// ListHistory gets backup history list\n// @Summary Get backup history list\n// @Tags Backup\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.BackupHistoryListRequest true \"Backup History List Parameters\"\n// @Success 200 {object} pkgapp.Res{data=pkgapp.ListRes{list=[]dto.BackupHistoryDTO}} \"Success\"\n// @Failure 400 {object} pkgapp.Res \"Invalid Params\"\n// @Failure 401 {object} pkgapp.Res \"Token Required\"\n// @Failure 500 {object} pkgapp.Res \"Internal Server Error\"\n// @Router /api/backup/historys [get]\nfunc (h *BackupHandler) ListHistory(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.BackupHistoryListRequest{}\n\tpager := pkgapp.NewPager(c)\n\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Override pager with params if provided (though NewPager usually handles page/pageSize from query)\n\t// But BindAndValid parses them into params.\n\t// Let's sync them or just rely on params.\n\t// Actually pkgapp.NewPager extracts page and page_size from context query params.\n\t// Since we bind them to struct as well, we can just ensure consistency.\n\t// The service uses pager.Page and pager.PageSize.\n\t// Let's just use pager as is, because BindAndValid also reads from query.\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\tlist, total, err := h.App.BackupService.ListHistory(c.Request.Context(), uid, params.ConfigID, pager)\n\tif err != nil {\n\t\th.logError(c.Request.Context(), \"BackupHandler.ListHistory\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponseList(code.Success, list, int(total))\n}\n\n// Execute triggers a backup manually\n// @Summary Trigger a backup manually\n// @Tags Backup\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params body dto.BackupExecuteRequest true \"Backup Execute Parameters\"\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Failure 400 {object} pkgapp.Res \"Invalid Params\"\n// @Failure 401 {object} pkgapp.Res \"Token Required\"\n// @Failure 500 {object} pkgapp.Res \"Internal Server Error\"\n// @Router /api/backup/execute [post]\nfunc (h *BackupHandler) Execute(c *gin.Context) {\n\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.BackupExecuteRequest{}\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\terr := h.App.BackupService.ExecuteUserBackup(c.Request.Context(), uid, params.ID)\n\tif err != nil {\n\t\th.logError(c.Request.Context(), \"BackupHandler.Execute\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithDetails(\"Backup task completed, check history for details\"))\n}\n\nfunc (h *BackupHandler) logError(ctx context.Context, method string, err error) {\n\ttraceID := middleware.GetTraceID(ctx)\n\th.App.Logger().Error(method,\n\t\tzap.Error(err),\n\t\tzap.String(\"traceId\", traceID),\n\t)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_backup_test.go",
    "content": "package api_router\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tsvcmocks \"github.com/haierkeys/fast-note-sync-service/internal/service/mocks\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\nfunc newBackupTestContext(method, url, body string, uid int64) (*gin.Context, *httptest.ResponseRecorder) {\n\tw := httptest.NewRecorder()\n\tvar req *http.Request\n\tif body != \"\" {\n\t\treq = httptest.NewRequest(method, url, strings.NewReader(body))\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t} else {\n\t\treq = httptest.NewRequest(method, url, nil)\n\t}\n\n\tc, _ := gin.CreateTestContext(w)\n\tc.Request = req\n\tif uid > 0 {\n\t\tc.Set(\"user_token\", &pkgapp.UserEntity{UID: uid})\n\t}\n\treturn c, w\n}\n\nfunc newTestBackupHandler(backupSvc *svcmocks.MockBackupService) *BackupHandler {\n\ttestApp := app.NewTestApp(&app.Services{\n\t\tBackupService: backupSvc,\n\t})\n\treturn NewBackupHandler(testApp)\n}\n\n// TestBackupHandler_GetConfigs_Success verifies successful retrieval of backup configs\nfunc TestBackupHandler_GetConfigs_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockBackupService)\n\n\tmockData := []*dto.BackupConfigDTO{\n\t\t{ID: 1, Vault: \"main\"},\n\t}\n\n\tmockSvc.On(\"GetConfigs\", mock.Anything, int64(1)).Return(mockData, nil)\n\n\thandler := newTestBackupHandler(mockSvc)\n\tc, w := newBackupTestContext(\"GET\", \"/api/backup/configs\", \"\", 1)\n\n\thandler.GetConfigs(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestBackupHandler_UpdateConfig_Success verifies successful backup config update\nfunc TestBackupHandler_UpdateConfig_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockBackupService)\n\n\tmockData := &dto.BackupConfigDTO{\n\t\tID:    1,\n\t\tVault: \"main\",\n\t}\n\n\tmockSvc.On(\"UpdateConfig\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.BackupConfigRequest\")).\n\t\tReturn(mockData, nil)\n\n\thandler := newTestBackupHandler(mockSvc)\n\tbody := `{\"id\":1, \"vault\":\"main\", \"type\":\"sync\", \"storageIds\":\"[1]\", \"cronStrategy\":\"daily\"}`\n\tc, w := newBackupTestContext(\"POST\", \"/api/backup/config\", body, 1)\n\n\thandler.UpdateConfig(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.SuccessUpdate.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestBackupHandler_DeleteConfig_Success verifies successful backup config deletion\nfunc TestBackupHandler_DeleteConfig_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockBackupService)\n\n\tmockSvc.On(\"DeleteConfig\", mock.Anything, int64(1), int64(1)).Return(nil)\n\n\thandler := newTestBackupHandler(mockSvc)\n\t// Delete request with body since c.ShouldBind parses body instead of query\n\tbody := `{\"id\":1}`\n\tc, w := newBackupTestContext(\"DELETE\", \"/api/backup/config\", body, 1)\n\n\thandler.DeleteConfig(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestBackupHandler_ListHistory_Success verifies successful history fetch\nfunc TestBackupHandler_ListHistory_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockBackupService)\n\n\tmockData := []*dto.BackupHistoryDTO{\n\t\t{ID: 10, ConfigID: 1},\n\t}\n\n\tmockSvc.On(\"ListHistory\", mock.Anything, int64(1), int64(1), mock.AnythingOfType(\"*app.Pager\")).\n\t\tReturn(mockData, int64(1), nil)\n\n\thandler := newTestBackupHandler(mockSvc)\n\tc, w := newBackupTestContext(\"GET\", \"/api/backup/histories?configId=1\", \"\", 1)\n\n\thandler.ListHistory(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestBackupHandler_Execute_Success verifies manual trigger backup execution\nfunc TestBackupHandler_Execute_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockBackupService)\n\n\tmockSvc.On(\"ExecuteUserBackup\", mock.Anything, int64(1), int64(1)).Return(nil)\n\n\thandler := newTestBackupHandler(mockSvc)\n\tbody := `{\"id\":1}`\n\tc, w := newBackupTestContext(\"POST\", \"/api/backup/execute\", body, 1)\n\n\thandler.Execute(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_file.go",
    "content": "package api_router\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/middleware\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\tapperrors \"github.com/haierkeys/fast-note-sync-service/pkg/errors\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"go.uber.org/zap\"\n)\n\n// FileHandler file API router handler\n// FileHandler 文件 API 路由处理器\ntype FileHandler struct {\n\t*Handler\n}\n\n// NewFileHandler creates FileHandler instance\n// NewFileHandler 创建 FileHandler 实例\nfunc NewFileHandler(a *app.App, wss *pkgapp.WebsocketServer) *FileHandler {\n\treturn &FileHandler{\n\t\tHandler: NewHandlerWithWSS(a, wss),\n\t}\n}\n\n// List retrieves file list\n// @Summary Get file list\n// @Description Get attachment list for current user with pagination, search, filter, and sort support\n// @Tags File\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.FileListRequest true \"Query Parameters\"\n// @Param pagination query pkgapp.PaginationRequest true \"Pagination Parameters\"\n// @Success 200 {object} pkgapp.Res{data=pkgapp.ListRes{list=[]dto.FileDTO}} \"Success\"\n// @Router /api/files [get]\nfunc (h *FileHandler) List(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.FileListRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"FileHandler.List.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"FileHandler.List err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tpager := pkgapp.NewPager(c)\n\tfileSvc := h.App.GetFileService(h.getClientInfo(c))\n\tfiles, count, err := fileSvc.List(ctx, uid, params, pager)\n\tif err != nil {\n\t\th.logError(ctx, \"FileHandler.List\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponseList(code.Success, files, count)\n}\n\n// GetInfo retrieves raw content of file or note\n// @Summary Get attachment content\n// @Description Get raw binary data of an attachment by path, supports strong cache control\n// @Tags File\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce octet-stream\n// @Param params query dto.FileGetRequest true \"Get Parameters\"\n// @Success 200 {file} binary \"Success\"\n// @Router /api/file [get]\nfunc (h *FileHandler) GetInfo(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.FileGetRequest{}\n\n\t// Parameter binding and validation\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"FileHandler.GetContent.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"FileHandler.GetContent err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Calculate PathHash\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// Get request context\n\tctx := c.Request.Context()\n\n\tfileSvc := h.App.GetFileService(h.getClientInfo(c))\n\tsavePath, contentType, mtime, etag, fileName, err := fileSvc.GetContentInfo(ctx, uid, params)\n\tif err != nil {\n\t\th.logError(ctx, \"FileHandler.GetContent\", err)\n\t\tresponse.ToResponse(code.Failed.WithDetails(err.Error()))\n\t\treturn\n\t}\n\n\t// Open file for zero-copy serving\n\tfile, err := os.Open(savePath)\n\tif err != nil {\n\t\th.logError(ctx, \"FileHandler.GetContent.Open\", err)\n\t\tc.AbortWithStatus(http.StatusNotFound)\n\t\treturn\n\t}\n\tdefer file.Close()\n\n\t// Set response headers\n\tif contentType != \"\" {\n\t\tc.Header(\"Content-Type\", contentType)\n\t}\n\tc.Header(\"Cache-Control\", \"public, s-maxage=31536000, max-age=31536000, must-revalidate\")\n\tif etag != \"\" {\n\t\tc.Header(\"ETag\", etag)\n\t}\n\n\thttp.ServeContent(c.Writer, c.Request, fileName, time.UnixMilli(mtime), file)\n}\n\n// GetSharedContent retrieves shared file content\n// @Summary Get shared attachment content\n// @Description Get raw binary data of a specific attachment via share token\n// @Tags File\n// @Produce octet-stream\n// @Success 200 {file} binary \"Success\"\n// @Router /api/share/file [get]\n\n// Delete deletes a file\n// @Summary Delete attachment\n// @Description Permanently delete a specific attachment record and its physical file\n// @Tags File\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.FileDeleteRequest true \"Delete Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.FileDTO} \"Success\"\n// @Router /api/file [delete]\nfunc (h *FileHandler) Delete(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.FileDeleteRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\n\tif !valid {\n\t\th.App.Logger().Error(\"FileHandler.Delete.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"FileHandler.Delete err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Calculate PathHash\n\t// 计算 PathHash\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tfileSvc := h.App.GetFileService(h.getClientInfo(c))\n\t// Execute deletion\n\t// 执行删除\n\tfile, err := fileSvc.Delete(ctx, uid, params)\n\tif err != nil {\n\t\th.logError(ctx, \"FileHandler.Delete\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(file))\n\n\th.WSS.BroadcastToUser(uid, code.Success.WithData(\n\t\tdto.FileSyncDeleteMessage{\n\t\t\tPath:     file.Path,\n\t\t\tPathHash: file.PathHash,\n\t\t\tCtime:    file.Ctime,\n\t\t\tMtime:    file.Mtime,\n\t\t\tSize:     file.Size,\n\t\t},\n\t).WithVault(params.Vault), \"FileSyncDelete\")\n}\n\n// Get retrieves file metadata\n// @Summary Get attachment info\n// @Description Get attachment metadata (FileDTO) by path\n// @Tags File\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.FileGetRequest true \"Get Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.FileDTO} \"Success\"\n// @Router /api/file/info [get]\nfunc (h *FileHandler) Get(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.FileGetRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"FileHandler.Get.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"FileHandler.Get err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Calculate PathHash\n\t// 计算 PathHash\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tfileSvc := h.App.GetFileService(h.getClientInfo(c))\n\tfile, err := fileSvc.Get(ctx, uid, params)\n\tif err != nil {\n\t\th.logError(ctx, \"FileHandler.Get\", err)\n\t\tresponse.ToResponse(code.Failed.WithDetails(err.Error()))\n\t\treturn\n\t}\n\n\tif file == nil {\n\t\tresponse.ToResponse(code.ErrorNoteNotFound)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(file))\n}\n\n// Restore restores a file from trash\n// @Summary Restore attachment\n// @Description Restore deleted attachment from trash\n// @Tags File\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params body dto.FileRestoreRequest true \"Restore Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.FileDTO} \"Success\"\n// @Router /api/file/restore [put]\nfunc (h *FileHandler) Restore(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.FileRestoreRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"FileHandler.Restore.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"FileHandler.Restore err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Calculate PathHash\n\t// 计算 PathHash\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tfileSvc := h.App.GetFileService(h.getClientInfo(c))\n\n\t// Execute restore\n\t// 执行恢复\n\tfile, err := fileSvc.Restore(ctx, uid, params)\n\tif err != nil {\n\t\th.logError(ctx, \"FileHandler.Restore.FileRestore\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(file))\n\th.WSS.BroadcastToUser(uid, code.Success.WithData(file).WithVault(params.Vault), \"FileSyncUpdate\")\n}\n\n// logError records error log, including Trace ID\n// logError 记录错误日志，包含 Trace ID\nfunc (h *FileHandler) logError(ctx context.Context, method string, err error) {\n\ttraceID := middleware.GetTraceID(ctx)\n\th.App.Logger().Error(method,\n\t\tzap.Error(err),\n\t\tzap.String(\"traceId\", traceID),\n\t)\n}\n\n// RecycleClear clears files from recycle bin\n// @Summary Clear recycle bin\n// @Description Permanently clear selected files from recycle bin\n// @Tags File\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.FileRecycleClearRequest true \"Clear Parameters\"\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Router /api/file/recycle-clear [delete]\nfunc (h *FileHandler) RecycleClear(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.FileRecycleClearRequest{}\n\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\th.App.Logger().Error(\"FileHandler.RecycleClear.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"FileHandler.RecycleClear err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\tctx := c.Request.Context()\n\tfileSvc := h.App.GetFileService(h.getClientInfo(c))\n\tif err := fileSvc.RecycleClear(ctx, uid, params); err != nil {\n\t\th.logError(ctx, \"FileHandler.RecycleClear\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success)\n}\n\n// Rename renames a file\n// @Summary Rename attachment\n// @Description Rename an attachment to a new path\n// @Tags File\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.FileRenameRequest true \"Rename Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.FileDTO} \"Success\"\n// @Router /api/file/rename [post]\nfunc (h *FileHandler) Rename(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.FileRenameRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"FileHandler.Rename.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"FileHandler.Rename err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Validate paths\n\tif !util.ValidatePath(params.Path) || !util.ValidatePath(params.OldPath) {\n\t\tresponse.ToResponse(code.ErrorInvalidPath)\n\t\treturn\n\t}\n\n\t// Calculate PathHash\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\tif params.OldPathHash == \"\" {\n\t\tparams.OldPathHash = util.EncodeHash32(params.OldPath)\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tfileSvc := h.App.GetFileService(h.getClientInfo(c))\n\n\toldFile, newFile, err := fileSvc.Rename(ctx, uid, params)\n\tif err != nil {\n\t\th.logError(ctx, \"FileHandler.Rename\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(newFile))\n\n\t// Broadcast WebSocket event: FileSyncRename\n\t// 广播 WebSocket 事件: 文件同步重命名\n\th.WSS.BroadcastToUser(uid, code.Success.WithData(dto.FileSyncRenameMessage{\n\t\tPath:             newFile.Path,\n\t\tPathHash:         newFile.PathHash,\n\t\tContentHash:      newFile.ContentHash,\n\t\tCtime:            newFile.Ctime,\n\t\tMtime:            newFile.Mtime,\n\t\tSize:             newFile.Size,\n\t\tUpdatedTimestamp: newFile.UpdatedTimestamp,\n\t\tOldPath:          oldFile.Path,\n\t\tOldPathHash:      oldFile.PathHash,\n\t}).WithVault(params.Vault), \"FileSyncRename\")\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_file_test.go",
    "content": "package api_router\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tsvcmocks \"github.com/haierkeys/fast-note-sync-service/internal/service/mocks\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// newFileTestContext creates a gin.Context suitable for FileHandler tests.\nfunc newFileTestContext(method, url, body string, uid int64) (*gin.Context, *httptest.ResponseRecorder) {\n\tw := httptest.NewRecorder()\n\tvar req *http.Request\n\tif body != \"\" {\n\t\treq = httptest.NewRequest(method, url, strings.NewReader(body))\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t} else {\n\t\treq = httptest.NewRequest(method, url, nil)\n\t}\n\n\tc, _ := gin.CreateTestContext(w)\n\tc.Request = req\n\tif uid > 0 {\n\t\tc.Set(\"user_token\", &pkgapp.UserEntity{UID: uid})\n\t}\n\treturn c, w\n}\n\nfunc newTestFileHandler(fileSvc *svcmocks.MockFileService) *FileHandler {\n\ttestApp := app.NewTestApp(&app.Services{\n\t\tFileService: fileSvc,\n\t})\n\twss := pkgapp.NewWebsocketServer(pkgapp.WSConfig{}, testApp)\n\treturn NewFileHandler(testApp, wss)\n}\n\n// TestFileHandler_Get_Success verifies successful file fetch\nfunc TestFileHandler_Get_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockFileService)\n\tmockSvc.On(\"WithClient\", \"Web\", \"\").Return(mockSvc)\n\t\n\tmockData := &dto.FileDTO{\n\t\tID:       1,\n\t\tPath:     \"file1.jpg\",\n\t\tPathHash: \"h1\",\n\t}\n\n\tmockSvc.On(\"Get\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.FileGetRequest\")).\n\t\tReturn(mockData, nil)\n\n\thandler := newTestFileHandler(mockSvc)\n\tc, w := newFileTestContext(\"GET\", \"/api/file/info?vault=main&path=file1.jpg\", \"\", 1)\n\t\n\thandler.Get(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestFileHandler_List_Success verifies successful file list fetch\nfunc TestFileHandler_List_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockFileService)\n\tmockSvc.On(\"WithClient\", \"Web\", \"\").Return(mockSvc)\n\t\n\tlistData := []*dto.FileDTO{\n\t\t{ID: 1, Path: \"f1.jpg\"},\n\t\t{ID: 2, Path: \"f2.png\"},\n\t}\n\n\tmockSvc.On(\"List\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.FileListRequest\"), mock.AnythingOfType(\"*app.Pager\")).\n\t\tReturn(listData, 2, nil)\n\n\thandler := newTestFileHandler(mockSvc)\n\tc, w := newFileTestContext(\"GET\", \"/api/files?vault=main&page=1\", \"\", 1)\n\t\n\thandler.List(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestFileHandler_Delete_Success verifies successful file deletion\nfunc TestFileHandler_Delete_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockFileService)\n\tmockSvc.On(\"WithClient\", \"Web\", \"\").Return(mockSvc)\n\t\n\tdeletedFile := &dto.FileDTO{ID: 3, Path: \"f3.zip\"}\n\tmockSvc.On(\"Delete\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.FileDeleteRequest\")).\n\t\tReturn(deletedFile, nil)\n\n\thandler := newTestFileHandler(mockSvc)\n\tbody := `{\"vault\":\"main\", \"path\":\"f3.zip\", \"pathHash\":\"f3_hash\"}`\n\tc, w := newFileTestContext(\"DELETE\", \"/api/file\", body, 1)\n\n\thandler.Delete(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_folder.go",
    "content": "package api_router\n\nimport (\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\tapperrors \"github.com/haierkeys/fast-note-sync-service/pkg/errors\"\n\t\"go.uber.org/zap\"\n)\n\ntype FolderHandler struct {\n\t*Handler\n}\n\nfunc NewFolderHandler(appContainer *app.App) *FolderHandler {\n\treturn &FolderHandler{Handler: NewHandler(appContainer)}\n}\n\n// Get retrieves a folder\n// @Summary Get folder info\n// @Description Get folder info for current user by path or pathHash\n// @Tags Folder\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.FolderGetRequest true \"Query Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.FolderDTO} \"Success\"\n// @Router /api/folder [get]\nfunc (h *FolderHandler) Get(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.FolderGetRequest{}\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\th.App.Logger().Error(\"FolderHandler.Get.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tres, err := h.App.GetFolderService(h.getClientInfo(c)).Get(c.Request.Context(), uid, params)\n\tif err != nil {\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(res))\n}\n\n// List retrieves folder list\n// @Summary Get folder list\n// @Description Get folder list for current user by parent path or pathHash\n// @Tags Folder\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.FolderListRequest true \"Query Parameters\"\n// @Success 200 {object} pkgapp.Res{data=[]dto.FolderDTO} \"Success\"\n// @Router /api/folders [get]\nfunc (h *FolderHandler) List(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.FolderListRequest{}\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\th.App.Logger().Error(\"FolderHandler.List.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tres, err := h.App.GetFolderService(h.getClientInfo(c)).List(c.Request.Context(), uid, params)\n\tif err != nil {\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(res))\n}\n\n// Create creates a folder\n// @Summary Create folder\n// @Description Create a new folder or restore a deleted one by path\n// @Tags Folder\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.FolderCreateRequest true \"Create Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.FolderDTO} \"Success\"\n// @Router /api/folder [post]\nfunc (h *FolderHandler) Create(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.FolderCreateRequest{}\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\th.App.Logger().Error(\"FolderHandler.Create.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tres, err := h.App.GetFolderService(h.getClientInfo(c)).UpdateOrCreate(c.Request.Context(), uid, params)\n\tif err != nil {\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(res))\n}\n\n// Delete deletes a folder\n// @Summary Delete folder\n// @Description Soft delete a folder by path or pathHash\n// @Tags Folder\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.FolderDeleteRequest true \"Delete Parameters\"\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Router /api/folder [delete]\nfunc (h *FolderHandler) Delete(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.FolderDeleteRequest{}\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\th.App.Logger().Error(\"FolderHandler.Delete.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\t_, err := h.App.GetFolderService(h.getClientInfo(c)).Delete(c.Request.Context(), uid, params)\n\tif err != nil {\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success)\n}\n\n// ListNotes retrieves notes in a folder\n// @Summary List notes in folder\n// @Description List non-deleted notes in a specific folder with pagination and sorting\n// @Tags Folder\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.FolderContentRequest true \"Query Parameters\"\n// @Param pagination query pkgapp.PaginationRequest true \"Pagination Parameters\"\n// @Success 200 {object} pkgapp.Res{data=pkgapp.ListRes{list=[]dto.NoteDTO}} \"Success\"\n// @Router /api/folder/notes [get]\nfunc (h *FolderHandler) ListNotes(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.FolderContentRequest{}\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\th.App.Logger().Error(\"FolderHandler.ListNotes.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tpager := pkgapp.NewPager(c)\n\n\tres, count, err := h.App.GetFolderService(h.getClientInfo(c)).ListNotes(c.Request.Context(), uid, params, pager)\n\tif err != nil {\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponseList(code.Success, res, count)\n}\n\n// ListFiles retrieves files in a folder\n// @Summary List files in folder\n// @Description List non-deleted files in a specific folder with pagination and sorting\n// @Tags Folder\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.FolderContentRequest true \"Query Parameters\"\n// @Param params query pkgapp.PaginationRequest true \"Query Parameters\"\n// @Success 200 {object} pkgapp.Res{data=pkgapp.ListRes{list=[]dto.FileDTO}} \"Success\"\n// @Router /api/folder/files [get]\nfunc (h *FolderHandler) ListFiles(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.FolderContentRequest{}\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\th.App.Logger().Error(\"FolderHandler.ListFiles.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tpager := pkgapp.NewPager(c)\n\tres, count, err := h.App.GetFolderService(h.getClientInfo(c)).ListFiles(c.Request.Context(), uid, params, pager)\n\tif err != nil {\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponseList(code.Success, res, count)\n}\n\n// Tree returns the complete folder tree structure\n// @Summary Get folder tree\n// @Description Get the complete folder tree structure for a vault\n// @Tags Folder\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.FolderTreeRequest true \"Query Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.FolderTreeResponse} \"Success\"\n// @Router /api/folder/tree [get]\nfunc (h *FolderHandler) Tree(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.FolderTreeRequest{}\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\th.App.Logger().Error(\"FolderHandler.Tree.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tres, err := h.App.GetFolderService(h.getClientInfo(c)).GetTree(c.Request.Context(), uid, params)\n\tif err != nil {\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(res))\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_folder_test.go",
    "content": "package api_router\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tsvcmocks \"github.com/haierkeys/fast-note-sync-service/internal/service/mocks\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// newFolderTestContext creates a gin.Context suitable for FolderHandler tests.\nfunc newFolderTestContext(method, url, body string, uid int64) (*gin.Context, *httptest.ResponseRecorder) {\n\tw := httptest.NewRecorder()\n\tvar req *http.Request\n\tif body != \"\" {\n\t\treq = httptest.NewRequest(method, url, strings.NewReader(body))\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t} else {\n\t\treq = httptest.NewRequest(method, url, nil)\n\t}\n\n\tc, _ := gin.CreateTestContext(w)\n\tc.Request = req\n\tif uid > 0 {\n\t\tc.Set(\"user_token\", &pkgapp.UserEntity{UID: uid})\n\t}\n\treturn c, w\n}\n\nfunc newTestFolderHandler(folderSvc *svcmocks.MockFolderService) *FolderHandler {\n\ttestApp := app.NewTestApp(&app.Services{\n\t\tFolderService: folderSvc,\n\t})\n\treturn NewFolderHandler(testApp)\n}\n\n// TestFolderHandler_Get_Success verifies successful folder fetch\nfunc TestFolderHandler_Get_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockFolderService)\n\t\n\tmockData := &dto.FolderDTO{\n\t\tID:       1,\n\t\tPath:     \"folder1\",\n\t\tPathHash: \"h1\",\n\t}\n\n\tmockSvc.On(\"Get\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.FolderGetRequest\")).\n\t\tReturn(mockData, nil)\n\n\thandler := newTestFolderHandler(mockSvc)\n\tc, w := newFolderTestContext(\"GET\", \"/api/folder\", \"\", 1)\n\tc.Request.URL.RawQuery = \"vault=main&path=folder1\"\n\t\n\thandler.Get(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestFolderHandler_List_Success verifies successful folder list fetch\nfunc TestFolderHandler_List_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockFolderService)\n\t\n\tlistData := []*dto.FolderDTO{\n\t\t{ID: 1, Path: \"f1\"},\n\t\t{ID: 2, Path: \"f2\"},\n\t}\n\n\tmockSvc.On(\"List\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.FolderListRequest\")).\n\t\tReturn(listData, nil)\n\n\thandler := newTestFolderHandler(mockSvc)\n\tc, w := newFolderTestContext(\"GET\", \"/api/folders\", \"\", 1)\n\tc.Request.URL.RawQuery = \"vault=main\"\n\t\n\thandler.List(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestFolderHandler_Create_Success verifies successful folder creation\nfunc TestFolderHandler_Create_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockFolderService)\n\t\n\tcreatedFolder := &dto.FolderDTO{ID: 3, Path: \"f3\"}\n\tmockSvc.On(\"UpdateOrCreate\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.FolderCreateRequest\")).\n\t\tReturn(createdFolder, nil)\n\n\thandler := newTestFolderHandler(mockSvc)\n\tbody := `{\"vault\":\"main\", \"path\":\"f3\"}`\n\tc, w := newFolderTestContext(\"POST\", \"/api/folder\", body, 1)\n\n\thandler.Create(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestFolderHandler_Delete_Success verifies successful folder deletion\nfunc TestFolderHandler_Delete_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockFolderService)\n\t\n\tmockSvc.On(\"Delete\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.FolderDeleteRequest\")).\n\t\tReturn((*dto.FolderDTO)(nil), nil)\n\n\thandler := newTestFolderHandler(mockSvc)\n\tbody := `{\"vault\":\"main\", \"path\":\"f3\"}`\n\tc, w := newFolderTestContext(\"DELETE\", \"/api/folder\", body, 1)\n\n\thandler.Delete(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_git_sync.go",
    "content": "package api_router\n\nimport (\n\t\"context\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/middleware\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\tapperrors \"github.com/haierkeys/fast-note-sync-service/pkg/errors\"\n\t\"go.uber.org/zap\"\n)\n\n// GitSyncHandler git sync API router handler\ntype GitSyncHandler struct {\n\t*Handler\n}\n\n// NewGitSyncHandler creates GitSyncHandler instance\nfunc NewGitSyncHandler(a *app.App) *GitSyncHandler {\n\treturn &GitSyncHandler{\n\t\tHandler: NewHandler(a),\n\t}\n}\n\n// GetConfigs gets git sync configurations\n// @Summary Get git sync configurations\n// @Tags GitSync\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Success 200 {object} pkgapp.Res{data=[]dto.GitSyncConfigDTO} \"Success\"\n// @Failure 401 {object} pkgapp.Res \"Token Required\"\n// @Failure 500 {object} pkgapp.Res \"Internal Server Error\"\n// @Router /api/git-sync/configs [get]\nfunc (h *GitSyncHandler) GetConfigs(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\tconfigs, err := h.App.GitSyncService.GetConfigs(c.Request.Context(), uid)\n\tif err != nil {\n\t\th.logError(c.Request.Context(), \"GitSyncHandler.GetConfigs\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(configs))\n}\n\n// UpdateConfig updates or creates git sync configuration\n// @Summary Update git sync configuration\n// @Tags GitSync\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.GitSyncConfigRequest true \"Git Sync Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.GitSyncConfigDTO} \"Success\"\n// @Failure 400 {object} pkgapp.Res \"Invalid Params\"\n// @Failure 401 {object} pkgapp.Res \"Token Required\"\n// @Failure 500 {object} pkgapp.Res \"Internal Server Error\"\n// @Router /api/git-sync/config [post]\nfunc (h *GitSyncHandler) UpdateConfig(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.GitSyncConfigRequest{}\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\tconfig, err := h.App.GitSyncService.UpdateConfig(c.Request.Context(), uid, params)\n\tif err != nil {\n\t\th.logError(c.Request.Context(), \"GitSyncHandler.UpdateConfig\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.SuccessUpdate.WithData(config))\n}\n\n// DeleteConfig deletes git sync configuration\n// @Summary Delete git sync configuration\n// @Tags GitSync\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params body dto.GitSyncDeleteRequest true \"Git Sync ID\"\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Failure 400 {object} pkgapp.Res \"Invalid Params\"\n// @Failure 401 {object} pkgapp.Res \"Token Required\"\n// @Failure 500 {object} pkgapp.Res \"Internal Server Error\"\n// @Router /api/git-sync/config [delete]\nfunc (h *GitSyncHandler) DeleteConfig(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.GitSyncDeleteRequest{}\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\terr := h.App.GitSyncService.DeleteConfig(c.Request.Context(), uid, params.ID)\n\tif err != nil {\n\t\th.logError(c.Request.Context(), \"GitSyncHandler.DeleteConfig\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success)\n}\n\n// Validate validates git sync configuration parameters\n// @Summary Validate git sync parameters\n// @Tags GitSync\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.GitSyncValidateRequest true \"Validation Parameters\"\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Failure 400 {object} pkgapp.Res \"Invalid Params\"\n// @Failure 401 {object} pkgapp.Res \"Token Required\"\n// @Failure 500 {object} pkgapp.Res \"Internal Server Error\"\n// @Router /api/git-sync/validate [post]\nfunc (h *GitSyncHandler) Validate(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.GitSyncValidateRequest{}\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\terr := h.App.GitSyncService.Validate(c.Request.Context(), params)\n\tif err != nil {\n\t\th.logError(c.Request.Context(), \"GitSyncHandler.Validate\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithDetails(\"Validation successful\"))\n}\n\n// Execute manual sync task\n// @Summary Trigger a manual git sync\n// @Tags GitSync\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.GitSyncExecuteRequest true \"Execute Parameters\"\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Failure 400 {object} pkgapp.Res \"Invalid Params\"\n// @Failure 401 {object} pkgapp.Res \"Token Required\"\n// @Failure 500 {object} pkgapp.Res \"Internal Server Error\"\n// @Router /api/git-sync/config/execute [post]\nfunc (h *GitSyncHandler) Execute(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.GitSyncExecuteRequest{}\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\terr := h.App.GitSyncService.ExecuteSync(c.Request.Context(), uid, params.ID)\n\tif err != nil {\n\t\th.logError(c.Request.Context(), \"GitSyncHandler.Execute\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithDetails(\"Sync started in background\"))\n}\n\n// CleanWorkspace cleans local git workspace\n// @Summary Clean local git workspace\n// @Tags GitSync\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.GitSyncCleanRequest true \"Clean Parameters\"\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Failure 400 {object} pkgapp.Res \"Invalid Params\"\n// @Failure 401 {object} pkgapp.Res \"Token Required\"\n// @Failure 500 {object} pkgapp.Res \"Internal Server Error\"\n// @Router /api/git-sync/config/clean [delete]\nfunc (h *GitSyncHandler) CleanWorkspace(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.GitSyncCleanRequest{}\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\terr := h.App.GitSyncService.CleanWorkspace(c.Request.Context(), uid, params.ConfigID)\n\tif err != nil {\n\t\th.logError(c.Request.Context(), \"GitSyncHandler.CleanWorkspace\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithDetails(\"Workspace cleaned\"))\n}\n\n// GetHistories gets git sync histories\n// @Summary Get git sync histories\n// @Tags GitSync\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.GitSyncHistoryRequest true \"Parameters\"\n// @Success 200 {object} pkgapp.Res{data=pkgapp.ListRes{list=[]dto.GitSyncHistoryDTO}} \"Success\"\n// @Failure 401 {object} pkgapp.Res \"Token Required\"\n// @Failure 500 {object} pkgapp.Res \"Internal Server Error\"\n// @Router /api/git-sync/histories [get]\nfunc (h *GitSyncHandler) GetHistories(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.GitSyncHistoryRequest{}\n\tpager := pkgapp.NewPager(c)\n\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\tlist, total, err := h.App.GitSyncService.ListHistory(c.Request.Context(), uid, params.ConfigID, pager)\n\tif err != nil {\n\t\th.logError(c.Request.Context(), \"GitSyncHandler.GetHistories\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponseList(code.Success, list, int(total))\n}\n\nfunc (h *GitSyncHandler) logError(ctx context.Context, method string, err error) {\n\ttraceID := middleware.GetTraceID(ctx)\n\th.App.Logger().Error(method,\n\t\tzap.Error(err),\n\t\tzap.String(\"traceId\", traceID),\n\t)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_git_sync_test.go",
    "content": "package api_router\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tsvcmocks \"github.com/haierkeys/fast-note-sync-service/internal/service/mocks\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\nfunc newGitSyncTestContext(method, url, body string, uid int64) (*gin.Context, *httptest.ResponseRecorder) {\n\tw := httptest.NewRecorder()\n\tvar req *http.Request\n\tif body != \"\" {\n\t\treq = httptest.NewRequest(method, url, strings.NewReader(body))\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t} else {\n\t\treq = httptest.NewRequest(method, url, nil)\n\t}\n\n\tc, _ := gin.CreateTestContext(w)\n\tc.Request = req\n\tif uid > 0 {\n\t\tc.Set(\"user_token\", &pkgapp.UserEntity{UID: uid})\n\t}\n\treturn c, w\n}\n\nfunc newTestGitSyncHandler(gitSvc *svcmocks.MockGitSyncService) *GitSyncHandler {\n\ttestApp := app.NewTestApp(&app.Services{\n\t\tGitSyncService: gitSvc,\n\t})\n\treturn NewGitSyncHandler(testApp)\n}\n\n// TestGitSyncHandler_GetConfigs_Success verifies successful retrieval of git configs\nfunc TestGitSyncHandler_GetConfigs_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockGitSyncService)\n\n\tmockData := []*dto.GitSyncConfigDTO{\n\t\t{ID: 1, RepoURL: \"https://github.com/user/repo.git\"},\n\t}\n\n\tmockSvc.On(\"GetConfigs\", mock.Anything, int64(1)).Return(mockData, nil)\n\n\thandler := newTestGitSyncHandler(mockSvc)\n\tc, w := newGitSyncTestContext(\"GET\", \"/api/git-sync/configs\", \"\", 1)\n\n\thandler.GetConfigs(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestGitSyncHandler_UpdateConfig_Success verifies successful git sync update\nfunc TestGitSyncHandler_UpdateConfig_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockGitSyncService)\n\n\tmockData := &dto.GitSyncConfigDTO{\n\t\tID:      1,\n\t\tRepoURL: \"https://github.com/user/repo.git\",\n\t}\n\n\tmockSvc.On(\"UpdateConfig\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.GitSyncConfigRequest\")).\n\t\tReturn(mockData, nil)\n\n\thandler := newTestGitSyncHandler(mockSvc)\n\tbody := `{\"id\":1, \"repoUrl\":\"https://github.com/user/repo.git\"}`\n\tc, w := newGitSyncTestContext(\"POST\", \"/api/git-sync/config\", body, 1)\n\n\thandler.UpdateConfig(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.SuccessUpdate.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestGitSyncHandler_DeleteConfig_Success verifies successful deletion\nfunc TestGitSyncHandler_DeleteConfig_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockGitSyncService)\n\n\tmockSvc.On(\"DeleteConfig\", mock.Anything, int64(1), int64(1)).Return(nil)\n\n\thandler := newTestGitSyncHandler(mockSvc)\n\tbody := `{\"id\":1}`\n\tc, w := newGitSyncTestContext(\"DELETE\", \"/api/git-sync/config\", body, 1)\n\n\thandler.DeleteConfig(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestGitSyncHandler_Validate_Success verifies git sync param validation\nfunc TestGitSyncHandler_Validate_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockGitSyncService)\n\n\tmockSvc.On(\"Validate\", mock.Anything, mock.AnythingOfType(\"*dto.GitSyncValidateRequest\")).Return(nil)\n\n\thandler := newTestGitSyncHandler(mockSvc)\n\tbody := `{\"repoUrl\":\"https://github.com/user/repo.git\"}`\n\tc, w := newGitSyncTestContext(\"POST\", \"/api/git-sync/validate\", body, 1)\n\n\thandler.Validate(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestGitSyncHandler_Execute_Success verifies manual trigger\nfunc TestGitSyncHandler_Execute_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockGitSyncService)\n\n\tmockSvc.On(\"ExecuteSync\", mock.Anything, int64(1), int64(1)).Return(nil)\n\n\thandler := newTestGitSyncHandler(mockSvc)\n\tbody := `{\"id\":1}`\n\tc, w := newGitSyncTestContext(\"POST\", \"/api/git-sync/config/execute\", body, 1)\n\n\thandler.Execute(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestGitSyncHandler_CleanWorkspace_Success verifies workspace clean\nfunc TestGitSyncHandler_CleanWorkspace_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockGitSyncService)\n\n\tmockSvc.On(\"CleanWorkspace\", mock.Anything, int64(1), int64(1)).Return(nil)\n\n\thandler := newTestGitSyncHandler(mockSvc)\n\tbody := `{\"configId\":1}`\n\tc, w := newGitSyncTestContext(\"DELETE\", \"/api/git-sync/config/clean\", body, 1)\n\n\thandler.CleanWorkspace(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestGitSyncHandler_ListHistory_Success verifies getting history\nfunc TestGitSyncHandler_ListHistory_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockGitSyncService)\n\n\tmockData := []*dto.GitSyncHistoryDTO{\n\t\t{ID: 10, ConfigID: 1},\n\t}\n\n\tmockSvc.On(\"ListHistory\", mock.Anything, int64(1), int64(1), mock.AnythingOfType(\"*app.Pager\")).\n\t\tReturn(mockData, int64(1), nil)\n\n\thandler := newTestGitSyncHandler(mockSvc)\n\tc, w := newGitSyncTestContext(\"GET\", \"/api/git-sync/histories?configId=1\", \"\", 1)\n\n\thandler.GetHistories(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_health.go",
    "content": "// Package api_router provides HTTP API router handlers\n// Package api_router 提供 HTTP API 路由处理器\npackage api_router\n\nimport (\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// HealthHandler health check handler\n// HealthHandler 健康检查处理器\ntype HealthHandler struct {\n\t*Handler\n}\n\n// NewHealthHandler creates health check handler instance\n// NewHealthHandler 创建健康检查处理器实例\nfunc NewHealthHandler(a *app.App) *HealthHandler {\n\treturn &HealthHandler{Handler: NewHandler(a)}\n}\n\n// HealthResponse health check response\n// HealthResponse 健康检查响应\ntype HealthResponse struct {\n\tStatus   string  `json:\"status\"`   // \"healthy\" or \"unhealthy\" // \"healthy\" 或 \"unhealthy\"\n\tVersion  string  `json:\"version\"`  // Service version number // 服务版本号\n\tUptime   float64 `json:\"uptime\"`   // Uptime (seconds) // 运行时间（秒）\n\tDatabase string  `json:\"database\"` // \"connected\" or \"error\" // \"connected\" 或 \"error\"\n}\n\n// Check health check interface\n// @Summary Health check\n// @Description Check service health status, including database connection\n// @Tags System\n// @Produce json\n// @Success 200 {object} HealthResponse\n// @Router /api/health [get]\nfunc (h *HealthHandler) Check(c *gin.Context) {\n\tresponse := HealthResponse{\n\t\tStatus:   \"healthy\",\n\t\tVersion:  h.App.Version().Version,\n\t\tUptime:   time.Since(h.App.StartTime).Seconds(),\n\t\tDatabase: \"connected\",\n\t}\n\n\t// Check database connection\n\t// 检查数据库连接\n\tif err := h.App.DB.Raw(\"SELECT 1\").Error; err != nil {\n\t\tresponse.Status = \"unhealthy\"\n\t\tresponse.Database = \"error\"\n\t\tpkgapp.NewResponse(c).ToResponse(code.Failed.WithData(response))\n\t\treturn\n\t}\n\n\tpkgapp.NewResponse(c).ToResponse(code.Success.WithData(response))\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_health_test.go",
    "content": "package api_router\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"gorm.io/driver/sqlite\"\n\t\"gorm.io/gorm\"\n)\n\nfunc newHealthTestContext() (*gin.Context, *httptest.ResponseRecorder) {\n\tw := httptest.NewRecorder()\n\tc, _ := gin.CreateTestContext(w)\n\treq := httptest.NewRequest(\"GET\", \"/api/health\", nil)\n\tc.Request = req\n\treturn c, w\n}\n\nfunc TestHealthHandler_Check_Success(t *testing.T) {\n\t// Use in-memory SQLite for health check test\n\tdb, err := gorm.Open(sqlite.Open(\":memory:\"), &gorm.Config{})\n\tif err != nil {\n\t\tt.Fatalf(\"failed to connect database: %v\", err)\n\t}\n\n\ttestApp := app.NewTestApp(nil, db)\n\thandler := NewHealthHandler(testApp)\n\tc, w := newHealthTestContext()\n\n\thandler.Check(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\t\n\tassert.Contains(t, w.Body.String(), `\"status\":\"healthy\"`)\n\tassert.Contains(t, w.Body.String(), `\"database\":\"connected\"`)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_note.go",
    "content": "package api_router\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/middleware\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\tapperrors \"github.com/haierkeys/fast-note-sync-service/pkg/errors\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"go.uber.org/zap\"\n)\n\n// NoteHandler note API router handler\n// NoteHandler 笔记 API 路由处理器\n// Uses App Container to inject dependencies, supports unified error handling\n// 使用 App Container 注入依赖，支持统一错误处理\ntype NoteHandler struct {\n\t*Handler\n}\n\n// NewNoteHandler creates NoteHandler instance\n// NewNoteHandler 创建 NoteHandler 实例\nfunc NewNoteHandler(a *app.App, wss *pkgapp.WebsocketServer) *NoteHandler {\n\treturn &NoteHandler{\n\t\tHandler: NewHandlerWithWSS(a, wss),\n\t}\n}\n\n// Get retrieves note details\n// @Summary Get note details\n// @Description Get specific note content and metadata by path or path hash\n// @Tags Note\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.NoteGetRequest true \"Get Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.NoteWithFileLinksResponse} \"Success\"\n// @Router /api/note [get]\nfunc (h *NoteHandler) Get(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.NoteGetRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"NoteHandler.Get.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"NoteHandler.Get err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Calculate PathHash\n\t// 计算 PathHash\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tnoteSvc := h.App.GetNoteService(h.getClientInfo(c))\n\tnote, err := noteSvc.Get(ctx, uid, params)\n\tif err != nil {\n\t\th.logError(ctx, \"NoteHandler.Get\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\t// Parse ![[ ]] tags in content\n\t// 解析内容中的 ![[ ]] 标签\n\tfileLinks, err := h.App.FileService.ResolveEmbedLinks(ctx, uid, params.Vault, note.Path, note.Content)\n\tif err != nil {\n\t\th.App.Logger().Error(\"NoteHandler.Get FileResolveEmbedLinks err\", zap.Error(err))\n\t}\n\n\tnoteWithLinks := &dto.NoteWithFileLinksResponse{\n\t\tID:               note.ID,\n\t\tPath:             note.Path,\n\t\tPathHash:         note.PathHash,\n\t\tContent:          note.Content,\n\t\tContentHash:      note.ContentHash,\n\t\tFileLinks:        fileLinks,\n\t\tVersion:          note.Version,\n\t\tCtime:            note.Ctime,\n\t\tMtime:            note.Mtime,\n\t\tUpdatedTimestamp: note.UpdatedTimestamp,\n\t\tUpdatedAt:        note.UpdatedAt,\n\t\tCreatedAt:        note.CreatedAt,\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(noteWithLinks))\n}\n\n// List retrieves note list\n// @Summary Get note list\n// @Description Get note list for current user with pagination\n// @Tags Note\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.NoteListRequest true \"Query Parameters\"\n// @Param pagination query pkgapp.PaginationRequest true \"Pagination Parameters\"\n// @Success 200 {object} pkgapp.Res{data=pkgapp.ListRes{list=[]dto.NoteNoContentDTO}} \"Success\"\n// @Router /api/notes [get]\nfunc (h *NoteHandler) List(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.NoteListRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"NoteHandler.List.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"NoteHandler.List err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tnoteSvc := h.App.GetNoteService(h.getClientInfo(c))\n\tpager := pkgapp.NewPager(c)\n\n\tnotes, count, err := noteSvc.List(ctx, uid, params, pager)\n\tif err != nil {\n\t\th.logError(ctx, \"NoteHandler.List\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponseList(code.Success, notes, count)\n}\n\n// CreateOrUpdate creates or updates a note\n// @Summary Create or update note\n// @Description Handle note creation, modification, or renaming (identified by path change)\n// @Tags Note\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.NoteModifyOrCreateRequest true \"Note Content\"\n// @Success 200 {object} pkgapp.Res{data=dto.NoteDTO} \"Success\"\n// @Router /api/note [post]\nfunc (h *NoteHandler) CreateOrUpdate(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.NoteModifyOrCreateRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"NoteHandler.CreateOrUpdate.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"NoteHandler.CreateOrUpdate err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Validate paths for directory traversal attacks\n\tif !util.ValidatePath(params.Path) {\n\t\tresponse.ToResponse(code.ErrorInvalidPath)\n\t\treturn\n\t}\n\n\t// Apply default folder if configured\n\t// if defaultFolder := h.App.Config().App.DefaultAPIFolder; defaultFolder != \"\" {\n\t// \tparams.Path = util.ApplyDefaultFolder(params.Path, defaultFolder)\n\t// }\n\n\t// Calculate hash values\n\t// 计算哈希值\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\tif params.ContentHash == \"\" {\n\t\tparams.ContentHash = util.EncodeHash32(params.Content)\n\t}\n\tif params.Mtime == 0 {\n\t\tparams.Mtime = time.Now().UnixMilli()\n\t}\n\tif params.Ctime == 0 {\n\t\tparams.Ctime = params.Mtime\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tnoteSvc := h.App.GetNoteService(h.getClientInfo(c))\n\n\t// Check update\n\t// 检查更新\n\tcheckParams := &dto.NoteUpdateCheckRequest{\n\t\tVault:       params.Vault,\n\t\tPath:        params.Path,\n\t\tPathHash:    params.PathHash,\n\t\tContentHash: params.ContentHash,\n\t\tCtime:       params.Ctime,\n\t\tMtime:       params.Mtime,\n\t}\n\t_, noteSelect, err := noteSvc.UpdateCheck(ctx, uid, checkParams)\n\tif err != nil {\n\t\th.logError(ctx, \"NoteHandler.CreateOrUpdate.NoteUpdateCheck\", err)\n\t\tresponse.ToResponse(code.Failed.WithDetails(err.Error()))\n\t\treturn\n\t}\n\n\tif noteSelect != nil {\n\t\tif params.ContentHash != noteSelect.ContentHash {\n\t\t\tparams.Mtime = time.Now().UnixMilli()\n\t\t}\n\t}\n\n\tvar noteNew *dto.NoteDTO\n\n\t_, noteNew, err = noteSvc.ModifyOrCreate(ctx, uid, params, false)\n\tif err != nil {\n\t\th.logError(ctx, \"NoteHandler.CreateOrUpdate.NoteModifyOrCreate\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(noteNew))\n\th.WSS.BroadcastToUser(uid, code.Success.WithData(noteNew).WithVault(params.Vault), \"NoteSyncModify\")\n}\n\n// Delete deletes a note\n// @Summary Delete note\n// @Description Move note to trash\n// @Tags Note\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.NoteDeleteRequest true \"Delete Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.NoteDTO} \"Success\"\n// @Router /api/note [delete]\nfunc (h *NoteHandler) Delete(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.NoteDeleteRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"NoteHandler.Delete.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"NoteHandler.Delete err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Calculate PathHash\n\t// 计算 PathHash\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tnoteSvc := h.App.GetNoteService(h.getClientInfo(c))\n\n\t// Check if note exists\n\t// 检查笔记是否存在\n\tnoteSrc, err := noteSvc.Get(ctx, uid, &dto.NoteGetRequest{\n\t\tVault:    params.Vault,\n\t\tPath:     params.Path,\n\t\tPathHash: params.PathHash,\n\t})\n\tif err != nil {\n\t\th.logError(ctx, \"NoteHandler.Delete.NoteGet\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\tif noteSrc == nil || noteSrc.Action == \"delete\" {\n\t\tresponse.ToResponse(code.ErrorNoteNotFound)\n\t\treturn\n\t}\n\n\t// Execute deletion\n\t// 执行删除\n\tnote, err := noteSvc.Delete(ctx, uid, params)\n\tif err != nil {\n\t\th.logError(ctx, \"NoteHandler.Delete.NoteDelete\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(note))\n\th.WSS.BroadcastToUser(uid, code.Success.WithData(note).WithVault(params.Vault), \"NoteSyncDelete\")\n}\n\n// Restore restores a note from trash\n// @Summary Restore note\n// @Description Restore deleted note from trash\n// @Tags Note\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params body dto.NoteRestoreRequest true \"Restore Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.NoteDTO} \"Success\"\n// @Router /api/note/restore [put]\nfunc (h *NoteHandler) Restore(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.NoteRestoreRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"NoteHandler.Restore.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"NoteHandler.Restore err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Calculate PathHash\n\t// 计算 PathHash\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tnoteSvc := h.App.GetNoteService(h.getClientInfo(c))\n\n\t// Check if note exists in trash\n\t// 检查笔记是否存在于回收站\n\tnoteSrc, err := noteSvc.Get(ctx, uid, &dto.NoteGetRequest{\n\t\tVault:     params.Vault,\n\t\tPath:      params.Path,\n\t\tPathHash:  params.PathHash,\n\t\tIsRecycle: true,\n\t})\n\tif err != nil {\n\t\th.logError(ctx, \"NoteHandler.Restore.NoteGet\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\tif noteSrc == nil || noteSrc.Action != \"delete\" {\n\t\tresponse.ToResponse(code.ErrorNoteNotFound)\n\t\treturn\n\t}\n\n\t// Execute restore\n\t// 执行恢复\n\tnote, err := noteSvc.Restore(ctx, uid, params)\n\tif err != nil {\n\t\th.logError(ctx, \"NoteHandler.Restore.NoteRestore\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(note))\n\th.WSS.BroadcastToUser(uid, code.Success.WithData(note).WithVault(params.Vault), \"NoteSyncModify\")\n}\n\n// PatchFrontmatter modifies note frontmatter\n// @Summary Modify note frontmatter\n// @Description Update or delete note frontmatter fields\n// @Tags Note\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.NotePatchFrontmatterRequest  true \"Frontmatter Modification Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.NoteDTO} \"Success\"\n// @Router /api/note/frontmatter [patch]\nfunc (h *NoteHandler) PatchFrontmatter(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.NotePatchFrontmatterRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"NoteHandler.PatchFrontmatter.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"NoteHandler.PatchFrontmatter err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Apply default folder if configured\n\t// if defaultFolder := h.App.Config().App.DefaultAPIFolder; defaultFolder != \"\" {\n\t// \tparams.Path = util.ApplyDefaultFolder(params.Path, defaultFolder)\n\t// }\n\n\t// Calculate PathHash\n\t// 计算 PathHash\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tnoteSvc := h.App.GetNoteService(h.getClientInfo(c))\n\tnote, err := noteSvc.PatchFrontmatter(ctx, uid, params)\n\tif err != nil {\n\t\th.logError(ctx, \"NoteHandler.PatchFrontmatter\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(note))\n\th.WSS.BroadcastToUser(uid, code.Success.WithData(note).WithVault(params.Vault), \"NoteSyncModify\")\n}\n\n// Append appends content to a note\n// @Summary Append content to note\n// @Description Append content to the end of a note\n// @Tags Note\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.NoteAppendRequest true \"Append Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.NoteDTO} \"Success\"\n// @Router /api/note/append [post]\nfunc (h *NoteHandler) Append(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.NoteAppendRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"NoteHandler.Append.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"NoteHandler.Append err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Apply default folder if configured\n\t// if defaultFolder := h.App.Config().App.DefaultAPIFolder; defaultFolder != \"\" {\n\t// \tparams.Path = util.ApplyDefaultFolder(params.Path, defaultFolder)\n\t// }\n\n\t// Calculate PathHash\n\t// 计算 PathHash\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tnoteSvc := h.App.GetNoteService(h.getClientInfo(c))\n\tnote, err := noteSvc.AppendContent(ctx, uid, params)\n\tif err != nil {\n\t\th.logError(ctx, \"NoteHandler.Append\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(note))\n\th.WSS.BroadcastToUser(uid, code.Success.WithData(note).WithVault(params.Vault), \"NoteSyncModify\")\n}\n\n// Prepend inserts content at the beginning of a note\n// @Summary Prepend content to note\n// @Description Insert content at the beginning of a note (after frontmatter)\n// @Tags Note\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.NotePrependRequest true \"Prepend Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.NoteDTO} \"Success\"\n// @Router /api/note/prepend [post]\nfunc (h *NoteHandler) Prepend(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.NotePrependRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"NoteHandler.Prepend.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"NoteHandler.Prepend err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Apply default folder if configured\n\t// if defaultFolder := h.App.Config().App.DefaultAPIFolder; defaultFolder != \"\" {\n\t// \tparams.Path = util.ApplyDefaultFolder(params.Path, defaultFolder)\n\t// }\n\n\t// Calculate PathHash\n\t// 计算 PathHash\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tnoteSvc := h.App.GetNoteService(h.getClientInfo(c))\n\tnote, err := noteSvc.PrependContent(ctx, uid, params)\n\tif err != nil {\n\t\th.logError(ctx, \"NoteHandler.Prepend\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(note))\n\th.WSS.BroadcastToUser(uid, code.Success.WithData(note).WithVault(params.Vault), \"NoteSyncModify\")\n}\n\n// Replace performs find and replace in a note\n// @Summary Find and replace in note\n// @Description Perform find and replace operation in a note, supporting regular expressions\n// @Tags Note\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.NoteReplaceRequest true \"Find and Replace Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.NoteReplaceResponse} \"Success\"\n// @Router /api/note/replace [post]\nfunc (h *NoteHandler) Replace(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.NoteReplaceRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"NoteHandler.Replace.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"NoteHandler.Replace err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Apply default folder if configured\n\t// if defaultFolder := h.App.Config().App.DefaultAPIFolder; defaultFolder != \"\" {\n\t// \tparams.Path = util.ApplyDefaultFolder(params.Path, defaultFolder)\n\t// }\n\n\t// Calculate PathHash\n\t// 计算 PathHash\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tnoteSvc := h.App.GetNoteService(h.getClientInfo(c))\n\tresult, err := noteSvc.ReplaceContent(ctx, uid, params)\n\tif err != nil {\n\t\th.logError(ctx, \"NoteHandler.Replace\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(result))\n\tif result.Note != nil {\n\t\th.WSS.BroadcastToUser(uid, code.Success.WithData(result.Note).WithVault(params.Vault), \"NoteSyncModify\")\n\t}\n}\n\n\n// Rename renames a note\n// @Summary Rename note\n// @Description Rename a note to a new path\n// @Tags Note\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.NoteRenameRequest true \"Rename Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.NoteDTO} \"Success\"\n// @Router /api/note/rename [post]\nfunc (h *NoteHandler) Rename(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.NoteRenameRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"NoteHandler.Rename.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"NoteHandler.Rename err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Validate paths\n\tif !util.ValidatePath(params.Path) || !util.ValidatePath(params.OldPath) {\n\t\tresponse.ToResponse(code.ErrorInvalidPath)\n\t\treturn\n\t}\n\n\t// Calculate PathHash\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\tif params.OldPathHash == \"\" {\n\t\tparams.OldPathHash = util.EncodeHash32(params.OldPath)\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tnoteSvc := h.App.GetNoteService(h.getClientInfo(c))\n\n\toldNote, newNote, err := noteSvc.Rename(ctx, uid, params)\n\tif err != nil {\n\t\th.logError(ctx, \"NoteHandler.Rename\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(newNote))\n\n\t// Broadcast WebSocket event: NoteSyncRename\n\t// 广播 WebSocket 事件: 笔记同步重命名\n\th.WSS.BroadcastToUser(uid, code.Success.WithData(dto.NoteSyncRenameMessage{\n\t\tPath:             newNote.Path,\n\t\tPathHash:         newNote.PathHash,\n\t\tContentHash:      newNote.ContentHash,\n\t\tCtime:            newNote.Ctime,\n\t\tMtime:            newNote.Mtime,\n\t\tSize:             newNote.Size,\n\t\tOldPath:          oldNote.Path,\n\t\tOldPathHash:      oldNote.PathHash,\n\t\tUpdatedTimestamp: newNote.UpdatedTimestamp,\n\t}).WithVault(params.Vault), \"NoteSyncRename\")\n}\n\n// GetBacklinks retrieves backlinks to a specific note\n// @Summary Get backlinks\n// @Description Get all other notes that link to the specified note\n// @Tags Note\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.NoteLinkQueryRequest true \"Query Parameters\"\n// @Success 200 {object} pkgapp.Res{data=[]dto.NoteLinkItem} \"Success\"\n// @Router /api/note/backlinks [get]\nfunc (h *NoteHandler) GetBacklinks(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.NoteLinkQueryRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"NoteHandler.GetBacklinks.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"NoteHandler.GetBacklinks err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Apply default folder if configured\n\t// if defaultFolder := h.App.Config().App.DefaultAPIFolder; defaultFolder != \"\" {\n\t// \tparams.Path = util.ApplyDefaultFolder(params.Path, defaultFolder)\n\t// }\n\n\t// Calculate PathHash\n\t// 计算 PathHash\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tlinks, err := h.App.NoteLinkService.GetBacklinks(ctx, uid, params)\n\tif err != nil {\n\t\th.logError(ctx, \"NoteHandler.GetBacklinks\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(links))\n}\n\n// GetOutlinks retrieves outgoing links from a specific note\n// @Summary Get outgoing links\n// @Description Get other notes that the specified note links to\n// @Tags Note\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.NoteLinkQueryRequest true \"Query Parameters\"\n// @Success 200 {object} pkgapp.Res{data=[]dto.NoteLinkItem} \"Success\"\n// @Router /api/note/outlinks [get]\nfunc (h *NoteHandler) GetOutlinks(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.NoteLinkQueryRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"NoteHandler.GetOutlinks.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"NoteHandler.GetOutlinks err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Apply default folder if configured\n\t// if defaultFolder := h.App.Config().App.DefaultAPIFolder; defaultFolder != \"\" {\n\t// \tparams.Path = util.ApplyDefaultFolder(params.Path, defaultFolder)\n\t// }\n\n\t// Calculate PathHash\n\t// 计算 PathHash\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tlinks, err := h.App.NoteLinkService.GetOutlinks(ctx, uid, params)\n\tif err != nil {\n\t\th.logError(ctx, \"NoteHandler.GetOutlinks\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(links))\n}\n\n// logError records error log, including Trace ID\n// logError 记录错误日志，包含 Trace ID\nfunc (h *NoteHandler) logError(ctx context.Context, method string, err error) {\n\ttraceID := middleware.GetTraceID(ctx)\n\th.App.Logger().Error(method,\n\t\tzap.Error(err),\n\t\tzap.String(\"traceId\", traceID),\n\t)\n}\n\n// RecycleClear clears notes from recycle bin\n// @Summary Clear recycle bin\n// @Description Permanently clear selected notes from recycle bin\n// @Tags Note\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.NoteRecycleClearRequest true \"Clear Parameters\"\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Router /api/note/recycle-clear [delete]\nfunc (h *NoteHandler) RecycleClear(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.NoteRecycleClearRequest{}\n\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\th.App.Logger().Error(\"NoteHandler.RecycleClear.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"NoteHandler.RecycleClear err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\tctx := c.Request.Context()\n\tnoteSvc := h.App.GetNoteService(h.getClientInfo(c))\n\tif err := noteSvc.RecycleClear(ctx, uid, params); err != nil {\n\t\th.logError(ctx, \"NoteHandler.RecycleClear\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_note_history.go",
    "content": "package api_router\n\nimport (\n\t\"context\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/middleware\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\tapperrors \"github.com/haierkeys/fast-note-sync-service/pkg/errors\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"go.uber.org/zap\"\n)\n\n// NoteHistoryHandler note history API router handler\n// NoteHistoryHandler 笔记历史 API 路由处理器\n// Uses App Container to inject dependencies, supports unified error handling\n// 使用 App Container 注入依赖，支持统一错误处理\ntype NoteHistoryHandler struct {\n\t*Handler\n}\n\n// NewNoteHistoryHandler creates NoteHistoryHandler instance\n// NewNoteHistoryHandler 创建 NoteHistoryHandler 实例\nfunc NewNoteHistoryHandler(a *app.App, wss *pkgapp.WebsocketServer) *NoteHistoryHandler {\n\treturn &NoteHistoryHandler{\n\t\tHandler: NewHandlerWithWSS(a, wss),\n\t}\n}\n\n// NoteHistoryGetRequestParams request parameters for getting note history details\n// NoteHistoryGetRequestParams 获取笔记历史详情请求参数\ntype NoteHistoryGetRequestParams struct {\n\tID int64 `form:\"id\" binding:\"required\"`\n}\n\n// Get retrieves specific note history details\n// @Summary Get note history details\n// @Description Get specific note history content by history record ID\n// @Tags Note History\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param id query int64 true \"History Record ID\"\n// @Success 200 {object} pkgapp.Res{data=dto.NoteHistoryDTO} \"Success\"\n// @Router /api/note/history [get]\nfunc (h *NoteHistoryHandler) Get(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &NoteHistoryGetRequestParams{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"NoteHistoryHandler.Get.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"NoteHistoryHandler.Get err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\thistory, err := h.App.NoteHistoryService.Get(ctx, uid, params.ID)\n\tif err != nil {\n\t\th.logError(ctx, \"NoteHistoryHandler.Get\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(history))\n}\n\n// List retrieves note history list\n// @Summary Get note history list\n// @Description Get all history records for a specific note with pagination\n// @Tags Note History\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.NoteHistoryListRequest true \"Query Parameters\"\n// @Param pagination query pkgapp.PaginationRequest true \"Pagination Parameters\"\n// @Success 200 {object} pkgapp.Res{data=pkgapp.ListRes{list=[]dto.NoteHistoryDTO}} \"Success\"\n// @Router /api/note/histories [get]\nfunc (h *NoteHistoryHandler) List(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.NoteHistoryListRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"NoteHistoryHandler.List.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"NoteHistoryHandler.List err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\tpager := pkgapp.NewPager(c)\n\n\tlist, count, err := h.App.NoteHistoryService.List(ctx, uid, params, pager)\n\tif err != nil {\n\t\th.logError(ctx, \"NoteHistoryHandler.List\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponseList(code.Success, list, int(count))\n}\n\n// logError records error log, including Trace ID\n// logError 记录错误日志，包含 Trace ID\nfunc (h *NoteHistoryHandler) logError(ctx context.Context, method string, err error) {\n\ttraceID := middleware.GetTraceID(ctx)\n\th.App.Logger().Error(method,\n\t\tzap.Error(err),\n\t\tzap.String(\"traceId\", traceID),\n\t)\n}\n\n// Restore restores note content from history\n// @Summary Restore note from history\n// @Description Restore note content to a specific history version\n// @Tags Note History\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.NoteHistoryRestoreRequest true \"Restore Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.NoteDTO} \"Success\"\n// @Router /api/note/history/restore [put]\nfunc (h *NoteHistoryHandler) Restore(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.NoteHistoryRestoreRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"NoteHistoryHandler.Restore.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"NoteHistoryHandler.Restore err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\t// Execute restore\n\t// 执行恢复\n\tnote, err := h.App.NoteHistoryService.RestoreFromHistory(ctx, uid, params.HistoryID)\n\tif err != nil {\n\t\th.logError(ctx, \"NoteHistoryHandler.Restore\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(note).WithVault(params.Vault))\n\th.WSS.BroadcastToUser(uid, code.Success.WithData(note).WithVault(params.Vault), \"NoteSyncModify\")\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_note_history_test.go",
    "content": "package api_router\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tsvcmocks \"github.com/haierkeys/fast-note-sync-service/internal/service/mocks\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// newNoteHistoryTestContext creates a gin.Context suitable for NoteHistoryHandler tests.\nfunc newNoteHistoryTestContext(method, url, body string, uid int64) (*gin.Context, *httptest.ResponseRecorder) {\n\tw := httptest.NewRecorder()\n\tvar req *http.Request\n\tif body != \"\" {\n\t\treq = httptest.NewRequest(method, url, strings.NewReader(body))\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t} else {\n\t\treq = httptest.NewRequest(method, url, nil)\n\t}\n\n\tc, _ := gin.CreateTestContext(w)\n\tc.Request = req\n\tif uid > 0 {\n\t\tc.Set(\"user_token\", &pkgapp.UserEntity{UID: uid})\n\t}\n\treturn c, w\n}\n\nfunc newTestNoteHistoryHandler(historySvc *svcmocks.MockNoteHistoryService) *NoteHistoryHandler {\n\ttestApp := app.NewTestApp(&app.Services{\n\t\tNoteHistoryService: historySvc,\n\t})\n\twss := pkgapp.NewWebsocketServer(pkgapp.WSConfig{}, testApp)\n\treturn NewNoteHistoryHandler(testApp, wss)\n}\n\n// TestNoteHistoryHandler_Get_Success verifies successful history fetch\nfunc TestNoteHistoryHandler_Get_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockNoteHistoryService)\n\t\n\tmockData := &dto.NoteHistoryDTO{\n\t\tID:       10,\n\t\tNoteID:   1,\n\t\tPath:     \"note1.md\",\n\t\tContent:  \"old content\",\n\t}\n\n\tmockSvc.On(\"Get\", mock.Anything, int64(1), int64(10)).Return(mockData, nil)\n\n\thandler := newTestNoteHistoryHandler(mockSvc)\n\tc, w := newNoteHistoryTestContext(\"GET\", \"/api/note/history?id=10\", \"\", 1)\n\t\n\thandler.Get(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestNoteHistoryHandler_List_Success verifies successful history list fetch\nfunc TestNoteHistoryHandler_List_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockNoteHistoryService)\n\t\n\tlistData := []*dto.NoteHistoryNoContentDTO{\n\t\t{ID: 10, NoteID: 1, Path: \"note1.md\"},\n\t\t{ID: 11, NoteID: 1, Path: \"note1.md\"},\n\t}\n\n\tmockSvc.On(\"List\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.NoteHistoryListRequest\"), mock.AnythingOfType(\"*app.Pager\")).\n\t\tReturn(listData, int64(2), nil)\n\n\thandler := newTestNoteHistoryHandler(mockSvc)\n\tc, w := newNoteHistoryTestContext(\"GET\", \"/api/note/histories?vault=main&path=note1.md\", \"\", 1)\n\t\n\thandler.List(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestNoteHistoryHandler_Restore_Success verifies successful restore from history\nfunc TestNoteHistoryHandler_Restore_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockNoteHistoryService)\n\t\n\trestoredNote := &dto.NoteDTO{ID: 1, Path: \"note1.md\", Action: \"\"}\n\tmockSvc.On(\"RestoreFromHistory\", mock.Anything, int64(1), int64(10)).\n\t\tReturn(restoredNote, nil)\n\n\thandler := newTestNoteHistoryHandler(mockSvc)\n\tbody := `{\"vault\":\"main\", \"historyId\":10}`\n\tc, w := newNoteHistoryTestContext(\"PUT\", \"/api/note/history/restore\", body, 1)\n\n\thandler.Restore(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_note_test.go",
    "content": "package api_router\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tsvcmocks \"github.com/haierkeys/fast-note-sync-service/internal/service/mocks\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// newNoteTestContext creates a gin.Context suitable for NoteHandler tests.\nfunc newNoteTestContext(method, url, body string, uid int64) (*gin.Context, *httptest.ResponseRecorder) {\n\tw := httptest.NewRecorder()\n\tvar req *http.Request\n\tif body != \"\" {\n\t\treq = httptest.NewRequest(method, url, strings.NewReader(body))\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t} else {\n\t\treq = httptest.NewRequest(method, url, nil)\n\t}\n\n\tc, _ := gin.CreateTestContext(w)\n\tc.Request = req\n\tif uid > 0 {\n\t\tc.Set(\"user_token\", &pkgapp.UserEntity{UID: uid})\n\t}\n\treturn c, w\n}\n\n// newTestNoteHandler creates a NoteHandler with mock services.\nfunc newTestNoteHandler(noteSvc *svcmocks.MockNoteService, fileSvc *svcmocks.MockFileService) *NoteHandler {\n\ttestApp := app.NewTestApp(&app.Services{\n\t\tNoteService: noteSvc,\n\t\tFileService: fileSvc,\n\t})\n\t// WSS is used in NoteHandler for broadcasting\n\twss := pkgapp.NewWebsocketServer(pkgapp.WSConfig{}, testApp)\n\treturn NewNoteHandler(testApp, wss)\n}\n\n// TestNoteHandler_Get_Success verifies successful note fetch\nfunc TestNoteHandler_Get_Success(t *testing.T) {\n\tmockNoteSvc := new(svcmocks.MockNoteService)\n\tmockNoteSvc.On(\"WithClient\", \"Web\", \"\").Return(mockNoteSvc)\n\tmockFileSvc := new(svcmocks.MockFileService)\n\tmockFileSvc.On(\"WithClient\", \"Web\", \"\").Return(mockFileSvc)\n\n\tmockData := &dto.NoteDTO{\n\t\tID:       1,\n\t\tPath:     \"test.md\",\n\t\tPathHash: \"hash123\",\n\t\tContent:  \"content\",\n\t}\n\n\tmockNoteSvc.On(\"Get\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.NoteGetRequest\")).\n\t\tReturn(mockData, nil)\n\n\tmockFileSvc.On(\"ResolveEmbedLinks\", mock.Anything, int64(1), \"main\", \"test.md\", \"content\").\n\t\tReturn(map[string]string{}, nil)\n\n\thandler := newTestNoteHandler(mockNoteSvc, mockFileSvc)\n\tc, w := newNoteTestContext(\"GET\", \"/api/note\", \"\", 1)\n\t\n\t// Simulate binding query params\n\tc.Request.URL.RawQuery = \"vault=main&path=test.md\"\n\thandler.Get(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockNoteSvc.AssertExpectations(t)\n}\n\n// TestNoteHandler_List_Success verifies successful note list fetch\nfunc TestNoteHandler_List_Success(t *testing.T) {\n\tmockNoteSvc := new(svcmocks.MockNoteService)\n\tmockNoteSvc.On(\"WithClient\", \"Web\", \"\").Return(mockNoteSvc)\n\tlistData := []*dto.NoteNoContentDTO{\n\t\t{ID: 1, Path: \"test1.md\"},\n\t\t{ID: 2, Path: \"test2.md\"},\n\t}\n\n\tmockNoteSvc.On(\"List\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.NoteListRequest\"), mock.AnythingOfType(\"*app.Pager\")).\n\t\tReturn(listData, 2, nil)\n\n\thandler := newTestNoteHandler(mockNoteSvc, nil)\n\tc, w := newNoteTestContext(\"GET\", \"/api/notes\", \"\", 1)\n\tc.Request.URL.RawQuery = \"vault=main&page=1&size=10\"\n\t\n\thandler.List(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockNoteSvc.AssertExpectations(t)\n}\n\n// TestNoteHandler_CreateOrUpdate_Success verifies successful note creation/update\nfunc TestNoteHandler_CreateOrUpdate_Success(t *testing.T) {\n\tmockNoteSvc := new(svcmocks.MockNoteService)\n\tmockNoteSvc.On(\"WithClient\", \"Web\", \"\").Return(mockNoteSvc)\n\n\tmockNoteSvc.On(\"UpdateCheck\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.NoteUpdateCheckRequest\")).\n\t\tReturn(\"\", (*dto.NoteDTO)(nil), nil)\n\n\tcreatedNote := &dto.NoteDTO{ID: 2, Path: \"create.md\"}\n\tmockNoteSvc.On(\"ModifyOrCreate\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.NoteModifyOrCreateRequest\"), false).\n\t\tReturn(true, createdNote, nil)\n\n\thandler := newTestNoteHandler(mockNoteSvc, nil)\n\tbody := `{\"vault\":\"main\", \"path\":\"create.md\", \"content\":\"hello\"}`\n\tc, w := newNoteTestContext(\"POST\", \"/api/note\", body, 1)\n\n\thandler.CreateOrUpdate(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockNoteSvc.AssertExpectations(t)\n}\n\n// TestNoteHandler_Delete_Success verifies successful note deletion\nfunc TestNoteHandler_Delete_Success(t *testing.T) {\n\tmockNoteSvc := new(svcmocks.MockNoteService)\n\tmockNoteSvc.On(\"WithClient\", \"Web\", \"\").Return(mockNoteSvc)\n\n\texistingNote := &dto.NoteDTO{ID: 3, Path: \"del.md\", Action: \"\"}\n\tmockNoteSvc.On(\"Get\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.NoteGetRequest\")).\n\t\tReturn(existingNote, nil)\n\n\tmockNoteSvc.On(\"Delete\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.NoteDeleteRequest\")).\n\t\tReturn(existingNote, nil)\n\n\thandler := newTestNoteHandler(mockNoteSvc, nil)\n\tc, w := newNoteTestContext(\"DELETE\", \"/api/note\", \"\", 1)\n\tc.Request.URL.RawQuery = \"vault=main&path=del.md\"\n\n\thandler.Delete(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockNoteSvc.AssertExpectations(t)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_setting.go",
    "content": "package api_router\n\nimport (\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\tapperrors \"github.com/haierkeys/fast-note-sync-service/pkg/errors\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"go.uber.org/zap\"\n)\n\ntype SettingHandler struct {\n\t*Handler\n}\n\nfunc NewSettingHandler(appContainer *app.App, wss *pkgapp.WebsocketServer) *SettingHandler {\n\treturn &SettingHandler{\n\t\tHandler: NewHandlerWithWSS(appContainer, wss),\n\t}\n}\n\n// Get retrieves a setting\n// @Summary Get setting info\n// @Description Get setting info for current user by path or pathHash\n// @Tags Setting\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.SettingGetRequest true \"Query Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.SettingDTO} \"Success\"\n// @Router /api/setting [get]\nfunc (h *SettingHandler) Get(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.SettingGetRequest{}\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\th.App.Logger().Error(\"SettingHandler.Get.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tres, err := h.App.GetSettingService(h.getClientInfo(c)).Get(c.Request.Context(), uid, params)\n\tif err != nil {\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(res))\n}\n\n// List retrieves setting list\n// @Summary Get setting list\n// @Description Get setting list for current user with pagination and keyword filtering\n// @Tags Setting\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.SettingListRequest true \"Query Parameters\"\n// @Param pagination query pkgapp.PaginationRequest true \"Pagination Parameters\"\n// @Success 200 {object} pkgapp.Res{data=pkgapp.ListRes{list=[]dto.SettingDTO}} \"Success\"\n// @Router /api/settings [get]\nfunc (h *SettingHandler) List(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.SettingListRequest{}\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\th.App.Logger().Error(\"SettingHandler.List.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tpager := pkgapp.NewPager(c)\n\n\tres, count, err := h.App.GetSettingService(h.getClientInfo(c)).List(c.Request.Context(), uid, params, pager)\n\tif err != nil {\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponseList(code.Success, res, int(count))\n}\n\n// CreateOrUpdate creates or updates a setting\n// @Summary Create or update setting\n// @Description Create a new setting or update an existing one\n// @Tags Setting\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.SettingModifyOrCreateRequest true \"Create/Update Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.SettingDTO} \"Success\"\n// @Router /api/setting [post]\nfunc (h *SettingHandler) CreateOrUpdate(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.SettingModifyOrCreateRequest{}\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\th.App.Logger().Error(\"SettingHandler.CreateOrUpdate.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tparams.Mtime = timex.Now().UnixMilli()\n\tparams.Ctime = timex.Now().UnixMilli()\n\n\tuid := pkgapp.GetUID(c)\n\t_, res, err := h.App.GetSettingService(h.getClientInfo(c)).ModifyOrCreate(c.Request.Context(), uid, params, false)\n\tif err != nil {\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(res))\n\th.WSS.BroadcastToUser(uid, code.Success.WithData(dto.SettingSyncModifyMessage{\n\t\tVault:            params.Vault,\n\t\tPath:             res.Path,\n\t\tPathHash:         res.PathHash,\n\t\tContent:          res.Content,\n\t\tContentHash:      res.ContentHash,\n\t\tCtime:            res.Ctime,\n\t\tMtime:            res.Mtime,\n\t\tUpdatedTimestamp: res.UpdatedTimestamp,\n\t}).WithVault(params.Vault), string(dto.SettingSyncModify))\n}\n\n// Delete deletes a setting\n// @Summary Delete setting\n// @Description Soft delete a setting by path or pathHash\n// @Tags Setting\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.SettingDeleteRequest true \"Delete Parameters\"\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Router /api/setting [delete]\nfunc (h *SettingHandler) Delete(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.SettingDeleteRequest{}\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\th.App.Logger().Error(\"SettingHandler.Delete.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tres, err := h.App.GetSettingService(h.getClientInfo(c)).Delete(c.Request.Context(), uid, params)\n\tif err != nil {\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success)\n\th.WSS.BroadcastToUser(uid, code.Success.WithData(dto.SettingSyncDeleteMessage{\n\t\tPath:             res.Path,\n\t\tPathHash:         res.PathHash,\n\t\tCtime:            res.Ctime,\n\t\tMtime:            res.Mtime,\n\t\tUpdatedTimestamp: res.UpdatedTimestamp,\n\t}).WithVault(params.Vault), string(dto.SettingSyncDelete))\n}\n\n// Rename renames a setting\n// @Summary Rename setting\n// @Description Rename a setting and update its path and pathHash\n// @Tags Setting\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.SettingRenameRequest true \"Rename Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.SettingDTO} \"Success\"\n// @Router /api/setting/rename [post]\nfunc (h *SettingHandler) Rename(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.SettingRenameRequest{}\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\th.App.Logger().Error(\"SettingHandler.Rename.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tres, err := h.App.GetSettingService(h.getClientInfo(c)).Rename(c.Request.Context(), uid, params)\n\tif err != nil {\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(res))\n\n\t// Broadcast old path deletion and new path modification\n\th.WSS.BroadcastToUser(uid, code.Success.WithData(dto.SettingSyncDeleteMessage{\n\t\tPath:             params.OldPath,\n\t\tPathHash:         params.OldPathHash,\n\t\tCtime:            res.Ctime,\n\t\tMtime:            res.Mtime,\n\t\tUpdatedTimestamp: res.UpdatedTimestamp,\n\t}).WithVault(params.Vault), string(dto.SettingSyncDelete))\n\n\th.WSS.BroadcastToUser(uid, code.Success.WithData(dto.SettingSyncModifyMessage{\n\t\tVault:            params.Vault,\n\t\tPath:             res.Path,\n\t\tPathHash:         res.PathHash,\n\t\tContent:          res.Content,\n\t\tContentHash:      res.ContentHash,\n\t\tCtime:            res.Ctime,\n\t\tMtime:            res.Mtime,\n\t\tUpdatedTimestamp: res.UpdatedTimestamp,\n\t}).WithVault(params.Vault), string(dto.SettingSyncModify))\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_setting_test.go",
    "content": "package api_router\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tsvcmocks \"github.com/haierkeys/fast-note-sync-service/internal/service/mocks\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\nfunc newSettingTestContext(method, url, body string, uid int64) (*gin.Context, *httptest.ResponseRecorder) {\n\tw := httptest.NewRecorder()\n\tvar req *http.Request\n\tif body != \"\" {\n\t\treq = httptest.NewRequest(method, url, strings.NewReader(body))\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t} else {\n\t\treq = httptest.NewRequest(method, url, nil)\n\t}\n\n\tc, _ := gin.CreateTestContext(w)\n\tc.Request = req\n\tif uid > 0 {\n\t\tc.Set(\"user_token\", &pkgapp.UserEntity{UID: uid})\n\t}\n\treturn c, w\n}\n\nfunc newTestSettingHandler(settingSvc *svcmocks.MockSettingService) *SettingHandler {\n\ttestApp := app.NewTestApp(&app.Services{\n\t\tSettingService: settingSvc,\n\t})\n\twss := pkgapp.NewWebsocketServer(pkgapp.WSConfig{}, testApp)\n\treturn NewSettingHandler(testApp, wss)\n}\n\nfunc TestSettingHandler_Get_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockSettingService)\n\tmockData := &dto.SettingDTO{Path: \"theme\", Content: \"dark\"}\n\n\tmockSvc.On(\"Get\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.SettingGetRequest\")).\n\t\tReturn(mockData, nil)\n\n\thandler := newTestSettingHandler(mockSvc)\n\tc, w := newSettingTestContext(\"GET\", \"/api/setting?path=theme&vault=main\", \"\", 1)\n\n\thandler.Get(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\nfunc TestSettingHandler_List_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockSettingService)\n\tmockData := []*dto.SettingDTO{\n\t\t{Path: \"theme\", Content: \"dark\"},\n\t}\n\n\tmockSvc.On(\"List\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.SettingListRequest\"), mock.AnythingOfType(\"*app.Pager\")).\n\t\tReturn(mockData, int64(1), nil)\n\n\thandler := newTestSettingHandler(mockSvc)\n\tc, w := newSettingTestContext(\"GET\", \"/api/settings?vault=main\", \"\", 1)\n\n\thandler.List(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\nfunc TestSettingHandler_CreateOrUpdate_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockSettingService)\n\tmockData := &dto.SettingDTO{Path: \"theme\", Content: \"light\"}\n\n\tmockSvc.On(\"ModifyOrCreate\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.SettingModifyOrCreateRequest\"), false).\n\t\tReturn(true, mockData, nil)\n\n\thandler := newTestSettingHandler(mockSvc)\n\tbody := `{\"vault\":\"main\", \"path\":\"theme\", \"content\":\"light\"}`\n\tc, w := newSettingTestContext(\"POST\", \"/api/setting\", body, 1)\n\n\thandler.CreateOrUpdate(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\nfunc TestSettingHandler_Delete_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockSettingService)\n\tmockData := &dto.SettingDTO{Path: \"theme\"}\n\n\tmockSvc.On(\"Delete\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.SettingDeleteRequest\")).\n\t\tReturn(mockData, nil)\n\n\thandler := newTestSettingHandler(mockSvc)\n\tbody := `{\"vault\":\"main\", \"path\":\"theme\"}`\n\tc, w := newSettingTestContext(\"DELETE\", \"/api/setting\", body, 1)\n\n\thandler.Delete(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\nfunc TestSettingHandler_Rename_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockSettingService)\n\tmockData := &dto.SettingDTO{Path: \"new_theme\", Content: \"dark\"}\n\n\tmockSvc.On(\"Rename\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.SettingRenameRequest\")).\n\t\tReturn(mockData, nil)\n\n\thandler := newTestSettingHandler(mockSvc)\n\tbody := `{\"vault\":\"main\", \"oldPath\":\"theme\", \"newPath\":\"new_theme\"}`\n\tc, w := newSettingTestContext(\"POST\", \"/api/setting/rename\", body, 1)\n\n\thandler.Rename(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_share.go",
    "content": "package api_router\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/middleware\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"go.uber.org/zap\"\n)\n\n// ShareHandler share API router handler\n// ShareHandler 分享 API 路由处理器\ntype ShareHandler struct {\n\t*Handler\n}\n\n// NewShareHandler creates ShareHandler instance with WebSocket server\n// NewShareHandler 创建带 WebSocket 服务的 ShareHandler 实例\nfunc NewShareHandler(app *app.App, wss *pkgapp.WebsocketServer) *ShareHandler {\n\treturn &ShareHandler{\n\t\tHandler: NewHandlerWithWSS(app, wss),\n\t}\n}\n\n// Create creates a share\n// @Summary Create resource share\n// @Description Create a share token for a specific note or attachment, automatically resolve attachment references and authorize\n// @Tags Share\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.ShareCreateRequest true \"Share Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.ShareCreateResponse} \"Success\"\n// @Router /api/share [post]\nfunc (h *ShareHandler) Create(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.ShareCreateRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tctx := c.Request.Context()\n\n\t// Call service layer to generate Token (automatically identify type and resolve associated resources)\n\t// 调用服务层生成 Token (自动识别类型及解析关联资源)\n\tshareRes, err := h.App.ShareService.ShareGenerate(ctx, uid, params.Vault, params.Path, params.PathHash, params.Password)\n\tif err != nil {\n\t\tif cObj, ok := err.(*code.Code); ok {\n\t\t\tresponse.ToResponse(cObj)\n\t\t} else {\n\t\t\tresponse.ToResponse(code.Failed.WithDetails(err.Error()))\n\t\t}\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(shareRes))\n\th.WSS.BroadcastToUser(uid, code.Success, dto.ShareSyncRefresh)\n}\n\n// @Summary Get shared note details\n// @Description Get specific note content (restricted read-only access) via share token\n// @Tags Share\n// @Security ShareAuthToken\n// @Param Share-Token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.ShareResourceRequest true \"Get Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.NoteDTO} \"Success\"\n// @Router /api/share/note [get]\nfunc (h *ShareHandler) NoteGet(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.ShareResourceRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get authorization Token\n\t// 获取授权 Token\n\ttoken, _ := c.Get(\"share_token\")\n\tshareToken, _ := token.(string)\n\tif shareToken == \"\" {\n\t\tresponse.ToResponse(code.ErrorInvalidAuthToken)\n\t\treturn\n\t}\n\n\tctx := c.Request.Context()\n\tnoteDTO, err := h.App.ShareService.GetSharedNote(ctx, shareToken, params.ID, params.Password)\n\tif err != nil {\n\t\tif cObj, ok := err.(*code.Code); ok {\n\t\t\tresponse.ToResponse(cObj)\n\t\t} else {\n\t\t\th.logError(ctx, \"ShareHandler.NoteGet\", err)\n\t\t\tresponse.ToResponse(code.Failed.WithDetails(err.Error()))\n\t\t}\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(noteDTO))\n}\n\n// GetSharedContent retrieves shared file content\n// @Summary Get shared attachment content\n// @Description Get raw binary data of a specific attachment via share token\n// @Tags Share\n// @Security ShareAuthToken\n// @Param Share-Token header string true \"Auth Token\"\n// @Produce octet-stream\n// @Param params query dto.ShareResourceRequest true \"Get Parameters\"\n// @Success 200 {file} binary \"Success\"\n// @Router /api/share/file [get]\nfunc (h *ShareHandler) FileGet(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.ShareResourceRequest{}\n\n\t// Parameter binding and validation\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get authorization Token\n\ttoken, _ := c.Get(\"share_token\")\n\tshareToken, _ := token.(string)\n\tif shareToken == \"\" {\n\t\tresponse.ToResponse(code.ErrorInvalidAuthToken)\n\t\treturn\n\t}\n\n\tctx := c.Request.Context()\n\tsavePath, contentType, mtime, etag, fileName, err := h.App.ShareService.GetSharedFileInfo(ctx, shareToken, params.ID, params.Password)\n\n\tif err != nil {\n\t\tif cObj, ok := err.(*code.Code); ok {\n\t\t\tresponse.ToResponse(cObj)\n\t\t} else {\n\t\t\th.logError(ctx, \"ShareHandler.FileGet\", err)\n\t\t\tresponse.ToResponse(code.Failed.WithDetails(err.Error()))\n\t\t}\n\t\treturn\n\t}\n\n\t// Open file for zero-copy serving\n\tfile, err := os.Open(savePath)\n\tif err != nil {\n\t\th.logError(ctx, \"ShareHandler.FileGet.Open\", err)\n\t\tc.AbortWithStatus(http.StatusNotFound)\n\t\treturn\n\t}\n\tdefer file.Close()\n\n\t// Set response headers and output content\n\tif contentType != \"\" {\n\t\tc.Header(\"Content-Type\", contentType)\n\t}\n\tc.Header(\"Cache-Control\", \"public, s-maxage=31536000, max-age=31536000, must-revalidate\")\n\tif etag != \"\" {\n\t\tc.Header(\"ETag\", etag)\n\t}\n\n\thttp.ServeContent(c.Writer, c.Request, fileName, time.UnixMilli(mtime), file)\n}\n\n// Query queries a share by path\n// @Summary Query share by path\n// @Description Get share token and info by vault and path\n// @Tags Share\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Param params query dto.ShareQueryRequest true \"Query Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.ShareCreateResponse} \"Success\"\n// @Router /api/share [get]\nfunc (h *ShareHandler) Query(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.ShareQueryRequest{}\n\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tctx := c.Request.Context()\n\n\tshare, err := h.App.ShareService.GetShareByPath(ctx, uid, params.Vault, params.PathHash)\n\tif err != nil {\n\t\tif cObj, ok := err.(*code.Code); ok {\n\t\t\tresponse.ToResponse(cObj)\n\t\t} else {\n\t\t\tresponse.ToResponse(code.Failed.WithDetails(err.Error()))\n\t\t}\n\t\treturn\n\t}\n\n\t// Generate Token again (since it's not stored in DB, we use the SID encryption scheme)\n\ttoken, err := h.App.TokenManager.ShareGenerate(share.ID, uid, share.Resources)\n\tif err != nil {\n\t\tresponse.ToResponse(code.Failed.WithDetails(err.Error()))\n\t\treturn\n\t}\n\n\t// Determine main ID and type for response\n\tvar mainID int64\n\tvar mainType string\n\tif ids, ok := share.Resources[\"note\"]; ok && len(ids) > 0 {\n\t\tmainID, _ = strconv.ParseInt(ids[0], 10, 64)\n\t\tmainType = \"note\"\n\t} else if ids, ok := share.Resources[\"file\"]; ok && len(ids) > 0 {\n\t\tmainID, _ = strconv.ParseInt(ids[0], 10, 64)\n\t\tmainType = \"file\"\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(&dto.ShareCreateResponse{\n\t\tID:         mainID,\n\t\tType:       mainType,\n\t\tToken:      token,\n\t\tExpiresAt:  share.ExpiresAt,\n\t\tShortLink:  share.ShortLink,\n\t\tIsPassword: share.Password != \"\",\n\t}))\n}\n\n// Cancel cancels a share by ID or path\n// @Summary Cancel share\n// @Description Cancel a share by ID or path parameters\n// @Tags Share\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.ShareCancelRequest true \"Cancel Parameters\"\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Router /api/share [delete]\nfunc (h *ShareHandler) Cancel(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.ShareCancelRequest{}\n\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tctx := c.Request.Context()\n\n\tvar err error\n\tif params.ID > 0 {\n\t\terr = h.App.ShareService.StopShare(ctx, uid, params.ID)\n\t} else if params.Vault != \"\" && params.PathHash != \"\" {\n\t\terr = h.App.ShareService.StopShareByPath(ctx, uid, params.Vault, params.PathHash)\n\t} else {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(\"Either ID or Vault + PathHash must be provided\"))\n\t\treturn\n\t}\n\n\tif err != nil {\n\t\tif cObj, ok := err.(*code.Code); ok {\n\t\t\tresponse.ToResponse(cObj)\n\t\t} else {\n\t\t\tresponse.ToResponse(code.Failed.WithDetails(err.Error()))\n\t\t}\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success)\n\th.WSS.BroadcastToUser(uid, code.Success, dto.ShareSyncRefresh)\n}\n\n// UpdatePassword updates share password\n// @Summary Update share password\n// @Description Set or update password for a share record\n// @Tags Share\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.SharePasswordUpdateRequest true \"Update Parameters\"\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Router /api/share/password [post]\nfunc (h *ShareHandler) UpdatePassword(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.SharePasswordUpdateRequest{}\n\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tctx := c.Request.Context()\n\n\terr := h.App.ShareService.UpdateSharePassword(ctx, uid, params.Vault, params.Path, params.PathHash, params.Password)\n\tif err != nil {\n\t\tif cObj, ok := err.(*code.Code); ok {\n\t\t\tresponse.ToResponse(cObj)\n\t\t} else {\n\t\t\tresponse.ToResponse(code.Failed.WithDetails(err.Error()))\n\t\t}\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success)\n}\n\n// CreateShortLink creates a short link for an existing share\n// @Summary Create short link for share\n// @Description Call sink.cool API to generate a short link for a given share record\n// @Tags Share\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.ShareShortLinkCreateRequest true \"Short Link Parameters\"\n// @Success 200 {object} pkgapp.Res{data=string} \"Success\"\n// @Router /api/share/short_link [post]\nfunc (h *ShareHandler) CreateShortLink(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.ShareShortLinkCreateRequest{}\n\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tctx := c.Request.Context()\n\n\t// Only compute baseURL when client did not provide the full share URL\n\tbaseURL := \"\"\n\tif params.URL == \"\" {\n\t\tbaseURL = fmt.Sprintf(\"%s://%s\", c.Request.URL.Scheme, c.Request.Host)\n\t}\n\n\tshortURL, err := h.App.ShareService.CreateShortLink(ctx, uid, params.Vault, params.Path, params.PathHash, baseURL, params.URL, params.IsForce)\n\tif err != nil {\n\t\tif cObj, ok := err.(*code.Code); ok {\n\t\t\tresponse.ToResponse(cObj)\n\t\t} else {\n\t\t\tresponse.ToResponse(code.Failed.WithDetails(err.Error()))\n\t\t}\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(shortURL))\n}\n\n// List lists all shares of a user\n// @Summary List shares\n// @Description Get all active and inactive shares of the user, supports sorting and pagination\n// @Tags Share\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Param sort_by query string false \"Sort field: created_at, updated_at, expires_at (default: created_at)\"\n// @Param sort_order query string false \"Sort direction: asc or desc (default: desc)\"\n// @Param page query int false \"Page number\"\n// @Param pageSize query int false \"Page size\"\n// @Produce json\n// @Success 200 {object} pkgapp.Res{data=pkgapp.ListRes{list=[]dto.ShareListItem}} \"Success\"\n// @Router /api/shares [get]\nfunc (h *ShareHandler) List(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.ShareListRequest{}\n\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tctx := c.Request.Context()\n\tpager := pkgapp.NewPager(c)\n\n\titems, count, err := h.App.ShareService.ListShares(ctx, uid, params.SortBy, params.SortOrder, pager)\n\tif err != nil {\n\t\tresponse.ToResponse(code.Failed.WithDetails(err.Error()))\n\t\treturn\n\t}\n\n\tresponse.ToResponseList(code.Success, items, count)\n}\n\n// NoteSharePaths returns active shared note paths for a vault\n// NoteSharePaths 返回指定 vault 下有效分享的笔记路径列表，供前端懒加载分享图标\n// @Summary Get active shared note paths\n// @Tags Share\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Param vault query string true \"Vault name\"\n// @Success 200 {object} pkgapp.Res{data=[]string} \"Success\"\n// @Router /api/notes/share-paths [get]\nfunc (h *ShareHandler) NoteSharePaths(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tvault := c.Query(\"vault\")\n\tif vault == \"\" {\n\t\tresponse.ToResponse(code.ErrorInvalidParams)\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tctx := c.Request.Context()\n\n\tpaths, err := h.App.ShareService.GetActiveNotePathsByVault(ctx, uid, vault)\n\tif err != nil {\n\t\tif cObj, ok := err.(*code.Code); ok {\n\t\t\tresponse.ToResponse(cObj)\n\t\t} else {\n\t\t\tresponse.ToResponse(code.Failed.WithDetails(err.Error()))\n\t\t}\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(paths))\n}\n\n// logError records error log, including Trace ID\n// logError 记录错误日志，包含 Trace ID\nfunc (h *ShareHandler) logError(ctx context.Context, method string, err error) {\n\ttraceID := middleware.GetTraceID(ctx)\n\th.App.Logger().Error(method,\n\t\tzap.Error(err),\n\t\tzap.String(\"traceId\", traceID),\n\t)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_share_test.go",
    "content": "package api_router\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tsvcmocks \"github.com/haierkeys/fast-note-sync-service/internal/service/mocks\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\nfunc newShareTestContext(method, url, body string, uid int64) (*gin.Context, *httptest.ResponseRecorder) {\n\tw := httptest.NewRecorder()\n\tvar req *http.Request\n\tif body != \"\" {\n\t\treq = httptest.NewRequest(method, url, strings.NewReader(body))\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t} else {\n\t\treq = httptest.NewRequest(method, url, nil)\n\t}\n\n\tc, _ := gin.CreateTestContext(w)\n\tc.Request = req\n\tif uid > 0 {\n\t\tc.Set(\"user_token\", &pkgapp.UserEntity{UID: uid})\n\t}\n\treturn c, w\n}\n\ntype mockTokenManager struct {\n\tmock.Mock\n}\n\nfunc (m *mockTokenManager) Generate(uid int64, nickname, ip string) (string, error) {\n\targs := m.Called(uid, nickname, ip)\n\treturn args.String(0), args.Error(1)\n}\nfunc (m *mockTokenManager) Parse(token string) (*pkgapp.UserEntity, error) {\n\targs := m.Called(token)\n\treturn args.Get(0).(*pkgapp.UserEntity), args.Error(1)\n}\nfunc (m *mockTokenManager) ShareGenerate(shareID int64, uid int64, resources map[string][]string) (string, error) {\n\targs := m.Called(shareID, uid, resources)\n\treturn args.String(0), args.Error(1)\n}\nfunc (m *mockTokenManager) ShareParse(token string) (*pkgapp.ShareEntity, error) {\n\targs := m.Called(token)\n\treturn args.Get(0).(*pkgapp.ShareEntity), args.Error(1)\n}\nfunc (m *mockTokenManager) Validate(token string) error {\n\treturn m.Called(token).Error(0)\n}\nfunc (m *mockTokenManager) GetSecretKey() string {\n\treturn m.Called().String(0)\n}\n\nfunc newTestShareHandler(shareSvc *svcmocks.MockShareService, tm *mockTokenManager) *ShareHandler {\n\ttestApp := app.NewTestApp(&app.Services{\n\t\tShareService: shareSvc,\n\t})\n\tif tm != nil {\n\t\ttestApp.TokenManager = tm\n\t}\n\twss := pkgapp.NewWebsocketServer(pkgapp.WSConfig{}, testApp)\n\treturn NewShareHandler(testApp, wss)\n}\n\nfunc TestShareHandler_Create_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockShareService)\n\n\tmockData := &dto.ShareCreateResponse{\n\t\tID:    1,\n\t\tToken: \"share_token\",\n\t}\n\n\tmockSvc.On(\"ShareGenerate\", mock.Anything, int64(1), \"main\", \"test.md\", \"hash_123\", \"\").\n\t\tReturn(mockData, nil)\n\n\thandler := newTestShareHandler(mockSvc, nil)\n\tbody := `{\"vault\":\"main\", \"path\":\"test.md\", \"pathHash\":\"hash_123\", \"password\":\"\"}`\n\tc, w := newShareTestContext(\"POST\", \"/api/share\", body, 1)\n\n\thandler.Create(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\nfunc TestShareHandler_NoteGet_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockShareService)\n\n\tmockData := &dto.NoteDTO{\n\t\tID:   1,\n\t\tPath: \"test.md\",\n\t}\n\n\tmockSvc.On(\"GetSharedNote\", mock.Anything, \"token_123\", int64(1), \"\").\n\t\tReturn(mockData, nil)\n\n\thandler := newTestShareHandler(mockSvc, nil)\n\tc, w := newShareTestContext(\"GET\", \"/api/share/note?id=1\", \"\", 1)\n\tc.Set(\"share_token\", \"token_123\")\n\n\thandler.NoteGet(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\nfunc TestShareHandler_Query_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockShareService)\n\tmockTM := new(mockTokenManager)\n\n\tshareData := &domain.UserShare{\n\t\tID: 1,\n\t\tResources: map[string][]string{\n\t\t\t\"note\": {\"1\"},\n\t\t},\n\t}\n\n\tmockSvc.On(\"GetShareByPath\", mock.Anything, int64(1), \"main\", \"hash_123\").\n\t\tReturn(shareData, nil)\n\n\tmockTM.On(\"ShareGenerate\", int64(1), int64(1), map[string][]string{\"note\": {\"1\"}}).\n\t\tReturn(\"generated_token_123\", nil)\n\n\thandler := newTestShareHandler(mockSvc, mockTM)\n\tc, w := newShareTestContext(\"GET\", \"/api/share?vault=main&path=test.md&pathHash=hash_123\", \"\", 1)\n\n\thandler.Query(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n\tmockTM.AssertExpectations(t)\n}\n\nfunc TestShareHandler_Cancel_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockShareService)\n\n\tmockSvc.On(\"StopShare\", mock.Anything, int64(1), int64(1)).Return(nil)\n\n\thandler := newTestShareHandler(mockSvc, nil)\n\tbody := `{\"id\":1, \"vault\":\"main\"}`\n\tc, w := newShareTestContext(\"DELETE\", \"/api/share\", body, 1)\n\n\thandler.Cancel(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\nfunc TestShareHandler_UpdatePassword_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockShareService)\n\n\tmockSvc.On(\"UpdateSharePassword\", mock.Anything, int64(1), \"main\", \"test.md\", \"hash_123\", \"new_pwd\").Return(nil)\n\n\thandler := newTestShareHandler(mockSvc, nil)\n\tbody := `{\"vault\":\"main\", \"path\":\"test.md\", \"pathHash\":\"hash_123\", \"password\":\"new_pwd\"}`\n\tc, w := newShareTestContext(\"POST\", \"/api/share/password\", body, 1)\n\n\thandler.UpdatePassword(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\nfunc TestShareHandler_List_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockShareService)\n\n\tmockData := []*dto.ShareListItem{\n\t\t{ID: 1, VaultName: \"main\", NotePath: \"test.md\"},\n\t}\n\n\tmockSvc.On(\"ListShares\", mock.Anything, int64(1), \"created_at\", \"desc\", mock.AnythingOfType(\"*app.Pager\")).\n\t\tReturn(mockData, 1, nil)\n\n\thandler := newTestShareHandler(mockSvc, nil)\n\tc, w := newShareTestContext(\"GET\", \"/api/shares?sort_by=created_at&sort_order=desc\", \"\", 1)\n\n\thandler.List(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_storage.go",
    "content": "package api_router\n\nimport (\n\t\"context\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/middleware\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\tapperrors \"github.com/haierkeys/fast-note-sync-service/pkg/errors\"\n\t\"go.uber.org/zap\"\n)\n\n// StorageHandler configuration API router handler\ntype StorageHandler struct {\n\t*Handler\n}\n\n// NewStorageHandler creates StorageHandler instance\nfunc NewStorageHandler(a *app.App) *StorageHandler {\n\treturn &StorageHandler{\n\t\tHandler: NewHandler(a),\n\t}\n}\n\n// CreateOrUpdate creates or updates storage configuration\n// @Summary Create or update storage configuration\n// @Tags Storage\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.StoragePostRequest true \"Storage Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.StorageDTO} \"Success\"\n// @Router /api/storage [post]\nfunc (h *StorageHandler) CreateOrUpdate(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.StoragePostRequest{}\n\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\th.App.Logger().Error(\"StorageHandler.CreateOrUpdate.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\tstorage, err := h.App.StorageService.CreateOrUpdate(c.Request.Context(), uid, params.ID, params)\n\tif err != nil {\n\t\th.logError(c.Request.Context(), \"StorageHandler.CreateOrUpdate\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tif params.ID > 0 {\n\t\tresponse.ToResponse(code.SuccessUpdate.WithData(storage))\n\t} else {\n\t\tresponse.ToResponse(code.SuccessCreate.WithData(storage))\n\t}\n}\n\n// List gets storage configuration list\n// @Summary Get storage configuration list\n// @Tags Storage\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Success 200 {object} pkgapp.Res{data=[]dto.StorageDTO} \"Success\"\n// @Router /api/storage [get]\nfunc (h *StorageHandler) List(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\tlist, err := h.App.StorageService.List(c.Request.Context(), uid)\n\tif err != nil {\n\t\th.logError(c.Request.Context(), \"StorageHandler.List\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(list))\n}\n\n// Delete deletes storage configuration\n// @Summary Delete storage configuration\n// @Tags Storage\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param id query int64 true \"Storage ID\"\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Router /api/storage [delete]\nfunc (h *StorageHandler) Delete(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.StorageGetRequest{}\n\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\terr := h.App.StorageService.Delete(c.Request.Context(), uid, params.ID)\n\tif err != nil {\n\t\th.logError(c.Request.Context(), \"StorageHandler.Delete\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.SuccessDelete)\n}\n\n// EnabledTypes gets enabled storage types\n// @Summary Get enabled storage types\n// @Description Get list of enabled storage types. Possible values: localfs, oss, s3, r2, minio, webdav\n// @Tags Storage\n// @Produce json\n// @Success 200 {object} pkgapp.Res{data=[]string} \"Success. Data contains: localfs, oss, s3, r2, minio, webdav\"\n// @Router /api/storage/enabled_types [get]\nfunc (h *StorageHandler) EnabledTypes(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\n\ttypes, err := h.App.StorageService.GetEnabledTypes()\n\tif err != nil {\n\t\th.logError(c.Request.Context(), \"StorageHandler.EnabledTypes\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(types))\n}\n\n// Validate tests storage connectivity\n// @Summary Validate storage connection\n// @Tags Storage\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.StoragePostRequest true \"Storage Parameters\"\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Failure 400 {object} pkgapp.Res \"Invalid Params\"\n// @Failure 401 {object} pkgapp.Res \"Token Required\"\n// @Failure 500 {object} pkgapp.Res \"Internal Server Error\"\n// @Router /api/storage/validate [post]\nfunc (h *StorageHandler) Validate(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.StoragePostRequest{}\n\n\tif valid, errs := pkgapp.BindAndValid(c, params); !valid {\n\t\th.App.Logger().Error(\"StorageHandler.Validate.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\terr := h.App.StorageService.Validate(c.Request.Context(), params)\n\tif err != nil {\n\t\th.logError(c.Request.Context(), \"StorageHandler.Validate\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithDetails(\"Validation successful\"))\n}\n\nfunc (h *StorageHandler) logError(ctx context.Context, method string, err error) {\n\ttraceID := middleware.GetTraceID(ctx)\n\th.App.Logger().Error(method,\n\t\tzap.Error(err),\n\t\tzap.String(\"traceId\", traceID),\n\t)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_storage_test.go",
    "content": "package api_router\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tsvcmocks \"github.com/haierkeys/fast-note-sync-service/internal/service/mocks\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\nfunc newStorageTestContext(method, url, body string, uid int64) (*gin.Context, *httptest.ResponseRecorder) {\n\tw := httptest.NewRecorder()\n\tvar req *http.Request\n\tif body != \"\" {\n\t\treq = httptest.NewRequest(method, url, strings.NewReader(body))\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t} else {\n\t\treq = httptest.NewRequest(method, url, nil)\n\t}\n\n\tc, _ := gin.CreateTestContext(w)\n\tc.Request = req\n\tif uid > 0 {\n\t\tc.Set(\"user_token\", &pkgapp.UserEntity{UID: uid})\n\t}\n\treturn c, w\n}\n\nfunc newTestStorageHandler(storageSvc *svcmocks.MockStorageService) *StorageHandler {\n\ttestApp := app.NewTestApp(&app.Services{\n\t\tStorageService: storageSvc,\n\t})\n\treturn NewStorageHandler(testApp)\n}\n\nfunc TestStorageHandler_CreateOrUpdate_Create_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockStorageService)\n\tmockData := &dto.StorageDTO{ID: 1, Type: \"localfs\"}\n\t\n\tmockSvc.On(\"CreateOrUpdate\", mock.Anything, int64(1), int64(0), mock.AnythingOfType(\"*dto.StoragePostRequest\")).\n\t\tReturn(mockData, nil)\n\n\thandler := newTestStorageHandler(mockSvc)\n\tbody := `{\"type\":\"localfs\", \"accessUrlPrefix\":\"http://cdn\"}`\n\tc, w := newStorageTestContext(\"POST\", \"/api/storage\", body, 1)\n\n\thandler.CreateOrUpdate(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.SuccessCreate.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\nfunc TestStorageHandler_CreateOrUpdate_Update_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockStorageService)\n\tmockData := &dto.StorageDTO{ID: 1, Type: \"localfs\"}\n\t\n\tmockSvc.On(\"CreateOrUpdate\", mock.Anything, int64(1), int64(1), mock.AnythingOfType(\"*dto.StoragePostRequest\")).\n\t\tReturn(mockData, nil)\n\n\thandler := newTestStorageHandler(mockSvc)\n\tbody := `{\"id\":1, \"type\":\"localfs\", \"accessUrlPrefix\":\"http://cdn\"}`\n\tc, w := newStorageTestContext(\"POST\", \"/api/storage\", body, 1)\n\n\thandler.CreateOrUpdate(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.SuccessUpdate.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\nfunc TestStorageHandler_List_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockStorageService)\n\tmockData := []*dto.StorageDTO{\n\t\t{ID: 1, Type: \"localfs\"},\n\t}\n\n\tmockSvc.On(\"List\", mock.Anything, int64(1)).Return(mockData, nil)\n\n\thandler := newTestStorageHandler(mockSvc)\n\tc, w := newStorageTestContext(\"GET\", \"/api/storage\", \"\", 1)\n\n\thandler.List(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\nfunc TestStorageHandler_Delete_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockStorageService)\n\tmockSvc.On(\"Delete\", mock.Anything, int64(1), int64(1)).Return(nil)\n\n\thandler := newTestStorageHandler(mockSvc)\n\tc, w := newStorageTestContext(\"DELETE\", \"/api/storage?id=1\", \"\", 1)\n\n\thandler.Delete(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.SuccessDelete.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\nfunc TestStorageHandler_EnabledTypes_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockStorageService)\n\tmockData := []string{\"localfs\", \"s3\"}\n\t\n\tmockSvc.On(\"GetEnabledTypes\").Return(mockData, nil)\n\n\thandler := newTestStorageHandler(mockSvc)\n\tc, w := newStorageTestContext(\"GET\", \"/api/storage/enabled_types\", \"\", 1)\n\n\thandler.EnabledTypes(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\nfunc TestStorageHandler_Validate_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockStorageService)\n\t\n\tmockSvc.On(\"Validate\", mock.Anything, mock.AnythingOfType(\"*dto.StoragePostRequest\")).Return(nil)\n\n\thandler := newTestStorageHandler(mockSvc)\n\tbody := `{\"type\":\"localfs\", \"accessUrlPrefix\":\"http://cdn\"}`\n\tc, w := newStorageTestContext(\"POST\", \"/api/storage/validate\", body, 1)\n\n\thandler.Validate(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_sync_log.go",
    "content": "package api_router\n\nimport (\n\t\"context\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/middleware\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\tapperrors \"github.com/haierkeys/fast-note-sync-service/pkg/errors\"\n\t\"go.uber.org/zap\"\n)\n\n// SyncLogHandler sync log API router handler\n// SyncLogHandler 同步日志 API 路由处理器\ntype SyncLogHandler struct {\n\t*Handler\n}\n\n// NewSyncLogHandler creates SyncLogHandler instance\n// NewSyncLogHandler 创建 SyncLogHandler 实例\nfunc NewSyncLogHandler(a *app.App) *SyncLogHandler {\n\treturn &SyncLogHandler{\n\t\tHandler: NewHandler(a),\n\t}\n}\n\n// List retrieves sync log list with pagination\n// @Summary Get sync log list\n// @Description Get sync log list for current user with optional type/action filters and pagination\n// @Tags Sync Log\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.SyncLogListRequest true \"Query Parameters\"\n// @Param pagination query pkgapp.PaginationRequest true \"Pagination Parameters\"\n// @Success 200 {object} pkgapp.Res{data=pkgapp.ListRes{list=[]dto.SyncLogDTO}} \"Success\"\n// @Router /api/sync-logs [get]\nfunc (h *SyncLogHandler) List(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.SyncLogListRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"SyncLogHandler.List.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"SyncLogHandler.List err uid=0\")\n\t\tresponse.ToResponse(code.ErrorInvalidUserAuthToken)\n\t\treturn\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\t// Get VaultID by vault name if provided (0 means no vault scope filter)\n\t// 如果传入 vault 名称则解析 VaultID（0 表示不限 vault）\n\tvar vaultID int64\n\tif params.Vault != \"\" {\n\t\tvar err2 error\n\t\tvaultID, err2 = h.App.VaultService.MustGetID(ctx, uid, params.Vault)\n\t\tif err2 != nil {\n\t\t\th.syncLogErr(ctx, \"SyncLogHandler.List.VaultService.MustGetID\", err2)\n\t\t\tapperrors.ErrorResponse(c, err2)\n\t\t\treturn\n\t\t}\n\t}\n\n\tpager := pkgapp.NewPager(c)\n\n\tlist, total, err := h.App.SyncLogService.List(ctx, uid, vaultID, params.Type, params.Action, pager.Page, pager.PageSize)\n\tif err != nil {\n\t\th.syncLogErr(ctx, \"SyncLogHandler.List\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponseList(code.Success, list, int(total))\n}\n\n// syncLogErr records error log\n// syncLogErr 记录错误日志\nfunc (h *SyncLogHandler) syncLogErr(ctx context.Context, method string, err error) {\n\ttraceID := middleware.GetTraceID(ctx)\n\th.App.Logger().Error(method,\n\t\tzap.Error(err),\n\t\tzap.String(\"traceId\", traceID),\n\t)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_user.go",
    "content": "package api_router\n\nimport (\n\t\"context\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/middleware\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\tapperrors \"github.com/haierkeys/fast-note-sync-service/pkg/errors\"\n\t\"go.uber.org/zap\"\n)\n\n// UserHandler user API router handler\n// UserHandler 用户 API 路由处理器\n// Uses App Container to inject dependencies, supports unified error handling\n// 使用 App Container 注入依赖，支持统一错误处理\ntype UserHandler struct {\n\t*Handler\n}\n\n// NewUserHandler creates UserHandler instance\n// NewUserHandler 创建 UserHandler 实例\nfunc NewUserHandler(a *app.App) *UserHandler {\n\treturn &UserHandler{\n\t\tHandler: NewHandler(a),\n\t}\n}\n\n// Register user registration\n// @Summary User registration\n// @Description Handle user registration HTTP request, validate parameters and call UserService. Registration may be disabled in server settings.\n// @Description 处理用户注册 HTTP 请求，验证参数并调用 UserService。注册功能可能在服务器设置中被禁用。\n// @Tags User\n// @Accept json\n// @Produce json\n// @Param params body dto.UserCreateRequest true \"Register Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.UserDTO} \"Success\"\n// @Failure 400 {object} pkgapp.Res \"Invalid Parameters / Registration Disabled / User Already Exists\"\n// @Router /api/user/register [post]\nfunc (h *UserHandler) Register(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.UserCreateRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"UserHandler.Register.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get request context (including Trace ID)\n\t// 获取请求上下文（包含 Trace ID）\n\tctx := c.Request.Context()\n\n\t// Call UserService to perform registration\n\t// 调用 UserService 执行注册\n\tuserDTO, err := h.App.UserService.Register(ctx, params)\n\tif err != nil {\n\t\th.logError(ctx, \"UserHandler.Register\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(userDTO))\n}\n\n// Login user login\n// @Summary User login\n// @Description Handle user login HTTP request, validate parameters and return auth token.\n// @Description 处理用户登录 HTTP 请求，验证参数并返回认证 Token。\n// @Tags User\n// @Accept json\n// @Produce json\n// @Param params body dto.UserLoginRequest true \"Login Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.UserDTO} \"Success\"\n// @Failure 400 {object} pkgapp.Res \"Invalid Parameters / Invalid Credentials\"\n// @Router /api/user/login [post]\nfunc (h *UserHandler) Login(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.UserLoginRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"UserHandler.Login.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get request context and client IP\n\t// 获取请求上下文和客户端 IP\n\tctx := c.Request.Context()\n\tclientIP := c.ClientIP()\n\n\t// Call UserService to perform login\n\t// 调用 UserService 执行登录\n\tuserDTO, err := h.App.UserService.Login(ctx, params, clientIP)\n\tif err != nil {\n\t\th.logError(ctx, \"UserHandler.Login\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(userDTO))\n}\n\n// UserChangePassword changes user password\n// @Summary Change user password\n// @Description Handle password change request for current user, validate old password and update new password.\n// @Description 处理当前用户的修改密码请求，验证旧密码并更新新密码。\n// @Tags User\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.UserChangePasswordRequest true \"Change Password Parameters\"\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Failure 400 {object} pkgapp.Res \"Invalid Parameters / Old Password Incorrect\"\n// @Failure 401 {object} pkgapp.Res \"Unauthorized\"\n// @Router /api/user/change_password [post]\nfunc (h *UserHandler) UserChangePassword(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.UserChangePasswordRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"UserHandler.UserChangePassword.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"UserHandler.UserChangePassword err uid=0\")\n\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\t// Call UserService to change password\n\t// 调用 UserService 修改密码\n\terr := h.App.UserService.ChangePassword(ctx, uid, params)\n\tif err != nil {\n\t\th.logError(ctx, \"UserHandler.UserChangePassword\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.SuccessPasswordUpdate)\n}\n\n// UserInfo retrieves user info\n// @Summary Get user info\n// @Description Handle request to get current user info.\n// @Description 处理获取当前用户信息的请求。\n// @Tags User\n// @Accept json\n// @Produce json\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Success 200 {object} pkgapp.Res{data=dto.UserDTO} \"Success\"\n// @Failure 401 {object} pkgapp.Res \"Unauthorized\"\n// @Router /api/user/info [get]\nfunc (h *UserHandler) UserInfo(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"UserHandler.UserInfo err uid=0\")\n\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\t// Call UserService to get user info\n\t// 调用 UserService 获取用户信息\n\tuserDTO, err := h.App.UserService.GetInfo(ctx, uid)\n\tif err != nil {\n\t\th.logError(ctx, \"UserHandler.UserInfo\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(userDTO))\n}\n\n// logError records error log, including Trace ID\n// logError 记录错误日志，包含 Trace ID\nfunc (h *UserHandler) logError(ctx context.Context, method string, err error) {\n\ttraceID := middleware.GetTraceID(ctx)\n\th.App.Logger().Error(method,\n\t\tzap.Error(err),\n\t\tzap.String(\"traceId\", traceID),\n\t)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_user_test.go",
    "content": "// Package api_router provides HTTP API router handlers\n// Package api_router 提供 HTTP API 路由处理器\npackage api_router\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tsvcmocks \"github.com/haierkeys/fast-note-sync-service/internal/service/mocks\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// newUserTestContext creates a gin.Context suitable for UserHandler tests.\n// newUserTestContext 创建适合 UserHandler 测试的 gin.Context。\nfunc newUserTestContext(method, url, body string, uid int64) (*gin.Context, *httptest.ResponseRecorder) {\n\tw := httptest.NewRecorder()\n\n\tvar req *http.Request\n\tif body != \"\" {\n\t\treq = httptest.NewRequest(method, url, strings.NewReader(body))\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t} else {\n\t\treq = httptest.NewRequest(method, url, nil)\n\t}\n\n\tc, _ := gin.CreateTestContext(w)\n\tc.Request = req\n\n\t// Inject authenticated user into context when UID is provided.\n\t// 当 UID 非零时，将认证用户注入 context。\n\tif uid > 0 {\n\t\tc.Set(\"user_token\", &pkgapp.UserEntity{UID: uid})\n\t}\n\n\treturn c, w\n}\n\n// newUserHandler creates a UserHandler with the given mock service.\n// newUserHandler 创建使用指定 mock service 的 UserHandler。\nfunc newUserHandler(mockSvc *svcmocks.MockUserService) *UserHandler {\n\ttestApp := app.NewTestApp(&app.Services{\n\t\tUserService: mockSvc,\n\t})\n\treturn NewUserHandler(testApp)\n}\n\n// --- Register ---\n\n// TestUserHandler_Register_Success verifies successful registration response.\n// TestUserHandler_Register_Success 验证注册成功时的响应。\nfunc TestUserHandler_Register_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockUserService)\n\tmockSvc.On(\"Register\", mock.Anything, mock.AnythingOfType(\"*dto.UserCreateRequest\")).\n\t\tReturn(&dto.UserDTO{UID: 1, Username: \"testuser\", Token: \"test-token\"}, nil)\n\n\thandler := newUserHandler(mockSvc)\n\tbody := `{\"email\":\"test@example.com\",\"username\":\"testuser\",\"password\":\"pass123\",\"confirmPassword\":\"pass123\"}`\n\tc, w := newUserTestContext(\"POST\", \"/api/user/register\", body, 0)\n\thandler.Register(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestUserHandler_Register_ServiceError verifies that service errors are propagated.\n// TestUserHandler_Register_ServiceError 验证 service 错误被正确传递到响应。\nfunc TestUserHandler_Register_ServiceError(t *testing.T) {\n\tmockSvc := new(svcmocks.MockUserService)\n\tmockSvc.On(\"Register\", mock.Anything, mock.AnythingOfType(\"*dto.UserCreateRequest\")).\n\t\tReturn(nil, code.ErrorUserRegisterIsDisable)\n\n\thandler := newUserHandler(mockSvc)\n\tbody := `{\"email\":\"test@example.com\",\"username\":\"testuser\",\"password\":\"pass123\",\"confirmPassword\":\"pass123\"}`\n\tc, w := newUserTestContext(\"POST\", \"/api/user/register\", body, 0)\n\thandler.Register(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\t// Service-returned error code should be reflected in response\n\t// service 返回的错误码应反映在响应中\n\tassertResponseCode(t, w, code.ErrorUserRegisterIsDisable.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// --- Login ---\n\n// TestUserHandler_Login_Success verifies successful login response.\n// TestUserHandler_Login_Success 验证登录成功时的响应。\nfunc TestUserHandler_Login_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockUserService)\n\tmockSvc.On(\"Login\", mock.Anything, mock.AnythingOfType(\"*dto.UserLoginRequest\"), mock.AnythingOfType(\"string\")).\n\t\tReturn(&dto.UserDTO{UID: 1, Token: \"test-token\"}, nil)\n\n\thandler := newUserHandler(mockSvc)\n\tbody := `{\"credentials\":\"test@example.com\",\"password\":\"pass123\"}`\n\tc, w := newUserTestContext(\"POST\", \"/api/user/login\", body, 0)\n\thandler.Login(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestUserHandler_Login_Failure verifies error response when credentials are wrong.\n// TestUserHandler_Login_Failure 验证凭证错误时返回错误响应。\nfunc TestUserHandler_Login_Failure(t *testing.T) {\n\tmockSvc := new(svcmocks.MockUserService)\n\tmockSvc.On(\"Login\", mock.Anything, mock.AnythingOfType(\"*dto.UserLoginRequest\"), mock.AnythingOfType(\"string\")).\n\t\tReturn(nil, code.ErrorUserLoginPasswordFailed)\n\n\thandler := newUserHandler(mockSvc)\n\tbody := `{\"credentials\":\"test@example.com\",\"password\":\"wrong\"}`\n\tc, w := newUserTestContext(\"POST\", \"/api/user/login\", body, 0)\n\thandler.Login(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.ErrorUserLoginPasswordFailed.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// --- UserInfo ---\n\n// TestUserHandler_UserInfo_Success verifies successful user info retrieval.\n// TestUserHandler_UserInfo_Success 验证正常获取用户信息的响应。\nfunc TestUserHandler_UserInfo_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockUserService)\n\tmockSvc.On(\"GetInfo\", mock.Anything, int64(1)).\n\t\tReturn(&dto.UserDTO{UID: 1, Email: \"a@b.com\", Username: \"user1\"}, nil)\n\n\thandler := newUserHandler(mockSvc)\n\tc, w := newUserTestContext(\"GET\", \"/api/user/info\", \"\", 1)\n\thandler.UserInfo(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestUserHandler_UserInfo_NoUID verifies auth error when UID is missing.\n// TestUserHandler_UserInfo_NoUID 验证缺少 UID 时返回认证错误。\nfunc TestUserHandler_UserInfo_NoUID(t *testing.T) {\n\tmockSvc := new(svcmocks.MockUserService)\n\n\thandler := newUserHandler(mockSvc)\n\tc, w := newUserTestContext(\"GET\", \"/api/user/info\", \"\", 0)\n\thandler.UserInfo(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.ErrorNotUserAuthToken.Code())\n\tmockSvc.AssertExpectations(t) // no service call expected // 期望没有 service 调用\n}\n\n// --- ChangePassword ---\n\n// TestUserHandler_ChangePassword_Success verifies successful password change response.\n// TestUserHandler_ChangePassword_Success 验证修改密码成功时的响应。\nfunc TestUserHandler_ChangePassword_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockUserService)\n\tmockSvc.On(\"ChangePassword\", mock.Anything, int64(1), mock.AnythingOfType(\"*dto.UserChangePasswordRequest\")).\n\t\tReturn(nil)\n\n\thandler := newUserHandler(mockSvc)\n\tbody := `{\"oldPassword\":\"old123\",\"password\":\"new123\",\"confirmPassword\":\"new123\"}`\n\tc, w := newUserTestContext(\"POST\", \"/api/user/change_password\", body, 1)\n\thandler.UserChangePassword(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.SuccessPasswordUpdate.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestUserHandler_ChangePassword_NoUID verifies auth error when UID is missing.\n// TestUserHandler_ChangePassword_NoUID 验证缺少 UID 时返回认证错误。\nfunc TestUserHandler_ChangePassword_NoUID(t *testing.T) {\n\tmockSvc := new(svcmocks.MockUserService)\n\n\thandler := newUserHandler(mockSvc)\n\tbody := `{\"oldPassword\":\"old123\",\"password\":\"new123\",\"confirmPassword\":\"new123\"}`\n\tc, w := newUserTestContext(\"POST\", \"/api/user/change_password\", body, 0)\n\thandler.UserChangePassword(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.ErrorNotUserAuthToken.Code())\n\tmockSvc.AssertExpectations(t)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_vault.go",
    "content": "package api_router\n\nimport (\n\t\"context\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/middleware\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\tapperrors \"github.com/haierkeys/fast-note-sync-service/pkg/errors\"\n\t\"go.uber.org/zap\"\n)\n\n// VaultHandler vault API router handler\n// VaultHandler 仓库 API 路由处理器\n// Uses App Container to inject dependencies, supports unified error handling\n// 使用 App Container 注入依赖，支持统一错误处理\ntype VaultHandler struct {\n\t*Handler\n}\n\n// NewVaultHandler creates VaultHandler instance\n// NewVaultHandler 创建 VaultHandler 实例\nfunc NewVaultHandler(a *app.App) *VaultHandler {\n\treturn &VaultHandler{\n\t\tHandler: NewHandler(a),\n\t}\n}\n\n// CreateOrUpdate creates or updates a vault\n// @Summary Create or update vault\n// @Description Be used to create a new vault or update an existing vault configuration based on the ID in the request parameters\n// @Tags Vault\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Accept json\n// @Produce json\n// @Param params body dto.VaultPostRequest true \"Vault Parameters\"\n// @Success 200 {object} pkgapp.Res{data=dto.VaultDTO} \"Success\"\n// @Router /api/vault [post]\nfunc (h *VaultHandler) CreateOrUpdate(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.VaultPostRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"VaultHandler.CreateOrUpdate.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"VaultHandler.CreateOrUpdate err uid=0\")\n\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tvar vault *dto.VaultDTO\n\tvar err error\n\n\tif params.ID > 0 {\n\t\t// Update logic\n\t\tvault, err = h.App.VaultService.Update(ctx, uid, params.ID, params.Vault)\n\t\tif err != nil {\n\t\t\th.logError(ctx, \"VaultHandler.CreateOrUpdate.Update\", err)\n\t\t\tapperrors.ErrorResponse(c, err)\n\t\t\treturn\n\t\t}\n\t\tresponse.ToResponse(code.SuccessUpdate.WithData(vault))\n\t} else {\n\t\t// Create logic\n\t\tvault, err = h.App.VaultService.Create(ctx, uid, params.Vault)\n\t\tif err != nil {\n\t\t\th.logError(ctx, \"VaultHandler.CreateOrUpdate.Create\", err)\n\t\t\tapperrors.ErrorResponse(c, err)\n\t\t\treturn\n\t\t}\n\t\tresponse.ToResponse(code.SuccessCreate.WithData(vault))\n\t}\n}\n\n// Get retrieves vault details\n// @Summary Get vault details\n// @Description Get specific vault configuration details by vault ID\n// @Tags Vault\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param id query int64 true \"Vault ID\"\n// @Success 200 {object} pkgapp.Res{data=dto.VaultDTO} \"Success\"\n// @Router /api/vault/get [get]\nfunc (h *VaultHandler) Get(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.VaultGetRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"VaultHandler.Get.BindAndValid errs\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tvault, err := h.App.VaultService.Get(ctx, uid, params.ID)\n\tif err != nil {\n\t\th.logError(ctx, \"VaultHandler.Get\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(vault))\n}\n\n// List retrieves vault list\n// @Summary Get vault list\n// @Description Get all note vaults for current user\n// @Tags Vault\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Success 200 {object} pkgapp.Res{data=[]dto.VaultDTO} \"Success\"\n// @Router /api/vault [get]\nfunc (h *VaultHandler) List(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"VaultHandler.List err uid=0\")\n\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\tvaults, err := h.App.VaultService.List(ctx, uid)\n\tif err != nil {\n\t\th.logError(ctx, \"VaultHandler.List\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.Success.WithData(vaults))\n}\n\n// Delete deletes a vault\n// @Summary Delete vault\n// @Description Permanently delete a specific note vault and all associated notes and attachments\n// @Tags Vault\n// @Security UserAuthToken\n// @Param token header string true \"Auth Token\"\n// @Produce json\n// @Param params query dto.VaultGetRequest true \"Delete Parameters\"\n// @Success 200 {object} pkgapp.Res \"Success\"\n// @Router /api/vault [delete]\nfunc (h *VaultHandler) Delete(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tparams := &dto.VaultGetRequest{}\n\n\t// Parameter binding and validation\n\t// 参数绑定和验证\n\tvalid, errs := pkgapp.BindAndValid(c, params)\n\tif !valid {\n\t\th.App.Logger().Error(\"VaultHandler.Delete.BindAndValid err\", zap.Error(errs))\n\t\tresponse.ToResponse(code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()).WithData(errs.MapsToString()))\n\t\treturn\n\t}\n\n\t// Get UID\n\t// 获取用户 ID\n\tuid := pkgapp.GetUID(c)\n\tif uid == 0 {\n\t\th.App.Logger().Error(\"VaultHandler.Delete err uid=0\")\n\t\tresponse.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\t// Get request context\n\t// 获取请求上下文\n\tctx := c.Request.Context()\n\n\terr := h.App.VaultService.Delete(ctx, uid, params.ID)\n\tif err != nil {\n\t\th.logError(ctx, \"VaultHandler.Delete\", err)\n\t\tapperrors.ErrorResponse(c, err)\n\t\treturn\n\t}\n\n\tresponse.ToResponse(code.SuccessDelete)\n}\n\n// logError records error log, including Trace ID\n// logError 记录错误日志，包含 Trace ID\nfunc (h *VaultHandler) logError(ctx context.Context, method string, err error) {\n\ttraceID := middleware.GetTraceID(ctx)\n\th.App.Logger().Error(method,\n\t\tzap.Error(err),\n\t\tzap.String(\"traceId\", traceID),\n\t)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_vault_test.go",
    "content": "// Package api_router provides HTTP API router handlers\n// Package api_router 提供 HTTP API 路由处理器\npackage api_router\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tsvcmocks \"github.com/haierkeys/fast-note-sync-service/internal/service/mocks\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\nfunc init() {\n\t// Switch gin to test mode to suppress debug output.\n\t// 切换到测试模式，抑制 gin 的调试输出。\n\tgin.SetMode(gin.TestMode)\n}\n\n// newVaultTestContext creates a gin.Context suitable for VaultHandler tests.\n// newVaultTestContext 创建适合 VaultHandler 测试的 gin.Context。\n//\n// method: HTTP method (GET, POST, DELETE)\n// url: request URL\n// body: request body JSON string (empty string for no body)\n// uid: authenticated user ID (0 means unauthenticated)\nfunc newVaultTestContext(method, url, body string, uid int64) (*gin.Context, *httptest.ResponseRecorder) {\n\tw := httptest.NewRecorder()\n\n\tvar req *http.Request\n\tif body != \"\" {\n\t\treq = httptest.NewRequest(method, url, strings.NewReader(body))\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\t} else {\n\t\treq = httptest.NewRequest(method, url, nil)\n\t}\n\n\tc, _ := gin.CreateTestContext(w)\n\tc.Request = req\n\n\t// Inject authenticated user ID into context (simulates JWT middleware).\n\t// 将认证用户 ID 注入 context（模拟 JWT 中间件的行为）。\n\tif uid > 0 {\n\t\tc.Set(\"user_token\", &pkgapp.UserEntity{UID: uid})\n\t}\n\n\treturn c, w\n}\n\n// decodeRes decodes the pkgapp.Res response body.\n// decodeRes 解码 pkgapp.Res 响应体。\nfunc decodeRes(t *testing.T, w *httptest.ResponseRecorder) map[string]interface{} {\n\tt.Helper()\n\tvar result map[string]interface{}\n\terr := json.Unmarshal(w.Body.Bytes(), &result)\n\tassert.NoError(t, err, \"response body should be valid JSON\")\n\treturn result\n}\n\n// assertResponseCode checks the business code in the JSON response.\n// assertResponseCode 检查 JSON 响应中的业务码。\nfunc assertResponseCode(t *testing.T, w *httptest.ResponseRecorder, wantCode int) {\n\tt.Helper()\n\tresult := decodeRes(t, w)\n\tgotCode, ok := result[\"code\"].(float64)\n\tassert.True(t, ok, \"response should have a code field\")\n\tassert.Equal(t, float64(wantCode), gotCode, \"unexpected business code\")\n}\n\n// newVaultHandler creates a VaultHandler with the given mock service.\n// newVaultHandler 创建使用指定 mock service 的 VaultHandler。\nfunc newVaultHandler(mockSvc *svcmocks.MockVaultService) *VaultHandler {\n\ttestApp := app.NewTestApp(&app.Services{\n\t\tVaultService: mockSvc,\n\t})\n\treturn NewVaultHandler(testApp)\n}\n\n// --- List ---\n\n// TestVaultHandler_List_Success verifies successful vault list response.\n// TestVaultHandler_List_Success 验证正常获取 Vault 列表的响应。\nfunc TestVaultHandler_List_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockVaultService)\n\tmockSvc.On(\"List\", mock.Anything, int64(1)).\n\t\tReturn([]*dto.VaultDTO{\n\t\t\t{ID: 1, Name: \"Vault-A\"},\n\t\t\t{ID: 2, Name: \"Vault-B\"},\n\t\t}, nil)\n\n\thandler := newVaultHandler(mockSvc)\n\tc, w := newVaultTestContext(\"GET\", \"/api/vault\", \"\", 1)\n\thandler.List(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestVaultHandler_List_NoUID verifies that missing UID returns auth error.\n// TestVaultHandler_List_NoUID 验证未携带 UID 时返回认证错误。\nfunc TestVaultHandler_List_NoUID(t *testing.T) {\n\tmockSvc := new(svcmocks.MockVaultService)\n\n\thandler := newVaultHandler(mockSvc)\n\t// uid=0 means no authentication injected\n\t// uid=0 表示未注入认证信息\n\tc, w := newVaultTestContext(\"GET\", \"/api/vault\", \"\", 0)\n\thandler.List(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.ErrorNotUserAuthToken.Code())\n\tmockSvc.AssertExpectations(t) // no service call expected // 期望没有 service 调用\n}\n\n// --- Create (CreateOrUpdate with ID=0) ---\n\n// TestVaultHandler_CreateOrUpdate_Create_Success verifies successful vault creation via POST.\n// TestVaultHandler_CreateOrUpdate_Create_Success 验证 POST 创建 Vault 成功。\nfunc TestVaultHandler_CreateOrUpdate_Create_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockVaultService)\n\tmockSvc.On(\"Create\", mock.Anything, int64(1), \"TestVault\").\n\t\tReturn(&dto.VaultDTO{ID: 10, Name: \"TestVault\"}, nil)\n\n\thandler := newVaultHandler(mockSvc)\n\tbody := `{\"vault\": \"TestVault\", \"id\": 0}`\n\tc, w := newVaultTestContext(\"POST\", \"/api/vault\", body, 1)\n\thandler.CreateOrUpdate(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.SuccessCreate.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestVaultHandler_CreateOrUpdate_Update_Success verifies successful vault update via POST.\n// TestVaultHandler_CreateOrUpdate_Update_Success 验证 POST 更新 Vault 成功。\nfunc TestVaultHandler_CreateOrUpdate_Update_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockVaultService)\n\tmockSvc.On(\"Update\", mock.Anything, int64(1), int64(5), \"UpdatedVault\").\n\t\tReturn(&dto.VaultDTO{ID: 5, Name: \"UpdatedVault\"}, nil)\n\n\thandler := newVaultHandler(mockSvc)\n\tbody := `{\"vault\": \"UpdatedVault\", \"id\": 5}`\n\tc, w := newVaultTestContext(\"POST\", \"/api/vault\", body, 1)\n\thandler.CreateOrUpdate(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.SuccessUpdate.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestVaultHandler_CreateOrUpdate_NoUID verifies auth error when UID is missing.\n// TestVaultHandler_CreateOrUpdate_NoUID 验证缺少 UID 时返回认证错误。\nfunc TestVaultHandler_CreateOrUpdate_NoUID(t *testing.T) {\n\tmockSvc := new(svcmocks.MockVaultService)\n\n\thandler := newVaultHandler(mockSvc)\n\tbody := `{\"vault\": \"TestVault\"}`\n\tc, w := newVaultTestContext(\"POST\", \"/api/vault\", body, 0)\n\thandler.CreateOrUpdate(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.ErrorNotUserAuthToken.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// --- Get ---\n\n// TestVaultHandler_Get_Success verifies successful vault retrieval.\n// TestVaultHandler_Get_Success 验证正常获取 Vault 详情。\nfunc TestVaultHandler_Get_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockVaultService)\n\tmockSvc.On(\"Get\", mock.Anything, int64(1), int64(3)).\n\t\tReturn(&dto.VaultDTO{ID: 3, Name: \"MyVault\"}, nil)\n\n\thandler := newVaultHandler(mockSvc)\n\tc, w := newVaultTestContext(\"GET\", \"/api/vault/get?id=3\", \"\", 1)\n\t// Manually set query param since gin test context doesn't parse URL\n\t// 手动设置查询参数（gin 测试 context 不自动解析 URL query）\n\tc.Request.URL.RawQuery = \"id=3\"\n\thandler.Get(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// --- Delete ---\n\n// TestVaultHandler_Delete_Success verifies successful vault deletion.\n// TestVaultHandler_Delete_Success 验证正常删除 Vault。\nfunc TestVaultHandler_Delete_Success(t *testing.T) {\n\tmockSvc := new(svcmocks.MockVaultService)\n\tmockSvc.On(\"Delete\", mock.Anything, int64(1), int64(4)).\n\t\tReturn(nil)\n\n\thandler := newVaultHandler(mockSvc)\n\tc, w := newVaultTestContext(\"DELETE\", \"/api/vault?id=4\", \"\", 1)\n\tc.Request.URL.RawQuery = \"id=4\"\n\thandler.Delete(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.SuccessDelete.Code())\n\tmockSvc.AssertExpectations(t)\n}\n\n// TestVaultHandler_Delete_NoUID verifies auth error when UID is missing.\n// TestVaultHandler_Delete_NoUID 验证缺少 UID 时返回认证错误。\nfunc TestVaultHandler_Delete_NoUID(t *testing.T) {\n\tmockSvc := new(svcmocks.MockVaultService)\n\n\thandler := newVaultHandler(mockSvc)\n\tc, w := newVaultTestContext(\"DELETE\", \"/api/vault?id=4\", \"\", 0)\n\tc.Request.URL.RawQuery = \"id=4\"\n\thandler.Delete(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.ErrorNotUserAuthToken.Code())\n\tmockSvc.AssertExpectations(t)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_version.go",
    "content": "package api_router\n\nimport (\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n)\n\n// VersionHandler version info API router handler\n// VersionHandler 版本信息 API 路由处理器\n// Uses App Container to inject dependencies\n// 使用 App Container 注入依赖\ntype VersionHandler struct {\n\t*Handler\n}\n\n// NewVersionHandler creates VersionHandler instance\n// NewVersionHandler 创建 VersionHandler 实例\nfunc NewVersionHandler(a *app.App) *VersionHandler {\n\treturn &VersionHandler{\n\t\tHandler: NewHandler(a),\n\t}\n}\n\n// ServerVersion retrieves server version information\n// @Summary Get server version info\n// @Description Get current server software version, Git tag, and build time\n// @Tags System\n// @Produce json\n// @Success 200 {object} pkgapp.Res{data=dto.VersionDTO} \"Success\"\n// @Router /api/version [get]\nfunc (h *VersionHandler) ServerVersion(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tversionInfo := h.App.Version()\n\tcheckInfo := h.App.CheckVersion(\"\")\n\tresponse.ToResponse(code.Success.WithData(dto.VersionDTO{\n\t\tVersion:                          versionInfo.Version,\n\t\tGitTag:                           versionInfo.GitTag,\n\t\tBuildTime:                        versionInfo.BuildTime,\n\t\tVersionIsNew:                     checkInfo.VersionIsNew,\n\t\tVersionNewName:                   checkInfo.VersionNewName,\n\t\tVersionNewLink:                   checkInfo.VersionNewLink,\n\t\tVersionNewChangelog:              checkInfo.VersionNewChangelog,\n\t\tVersionNewChangelogContent:       checkInfo.VersionNewChangelogContent,\n\t\tPluginVersionNewName:             checkInfo.PluginVersionNewName,\n\t\tPluginVersionNewLink:             checkInfo.PluginVersionNewLink,\n\t\tPluginVersionNewChangelog:        checkInfo.PluginVersionNewChangelog,\n\t\tPluginVersionNewChangelogContent: checkInfo.PluginVersionNewChangelogContent,\n\t}))\n}\n\n// Support retrieves support records by language with pagination and sorting\n// Support 分页并排序获取指定语言的打赏记录\n// @Summary Get support records\n// @Description Get support records for the specified language with pagination and sorting\n// @Tags System\n// @Produce json\n// @Param lang query string false \"Language code (default: en)\"\n// @Param sortBy query string false \"Sort by field (amount, time, name, item)\"\n// @Param sortOrder query string false \"Sort order (asc, desc)\"\n// @Param page query int false \"Page number\"\n// @Param pageSize query int false \"Page size\"\n// @Success 200 {object} pkgapp.Res{data=pkgapp.ListRes} \"Success\"\n// @Router /api/support [get]\nfunc (h *VersionHandler) Support(c *gin.Context) {\n\tresponse := pkgapp.NewResponse(c)\n\tlang := strings.ToLower(c.Query(\"lang\"))\n\tif lang == \"\" {\n\t\tlang = \"en\"\n\t}\n\n\tsortBy := c.Query(\"sortBy\")\n\tsortOrder := c.Query(\"sortOrder\")\n\tif sortOrder == \"\" {\n\t\tsortOrder = \"desc\"\n\t}\n\n\tpager := pkgapp.NewPager(c)\n\tdata, total := h.App.GetSupportRecordsPage(lang, sortBy, sortOrder, pager.Page, pager.PageSize)\n\n\tresponse.ToResponseList(code.Success, data, total)\n}\n"
  },
  {
    "path": "internal/routers/api_router/handler_version_test.go",
    "content": "package api_router\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc newVersionTestContext(url string) (*gin.Context, *httptest.ResponseRecorder) {\n\tw := httptest.NewRecorder()\n\tc, _ := gin.CreateTestContext(w)\n\treq := httptest.NewRequest(\"GET\", url, nil)\n\tc.Request = req\n\treturn c, w\n}\n\nfunc TestVersionHandler_ServerVersion_Success(t *testing.T) {\n\ttestApp := app.NewTestApp(nil)\n\thandler := NewVersionHandler(testApp)\n\tc, w := newVersionTestContext(\"/api/version\")\n\n\thandler.ServerVersion(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\t\n\t// Verify that the response contains the version from app.Version\n\tvar resp struct {\n\t\tData struct {\n\t\t\tVersion string `json:\"version\"`\n\t\t} `json:\"data\"`\n\t}\n\terr := json.Unmarshal(w.Body.Bytes(), &resp)\n\tassert.NoError(t, err)\n\tassert.Equal(t, app.Version, resp.Data.Version)\n}\n\nfunc TestVersionHandler_Support_Success(t *testing.T) {\n\ttestApp := app.NewTestApp(nil)\n\t\n\t// Inject some support records\n\trecords := []pkgapp.SupportRecord{\n\t\t{Name: \"User1\", Amount: \"10\", Time: \"2023-01-01\"},\n\t}\n\ttestApp.UpdateSupportRecords(\"en\", records)\n\n\thandler := NewVersionHandler(testApp)\n\tc, w := newVersionTestContext(\"/api/support?lang=en&page=1&pageSize=10\")\n\n\thandler.Support(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassertResponseCode(t, w, code.Success.Code())\n\t\n\tassert.Contains(t, w.Body.String(), \"User1\")\n}\n"
  },
  {
    "path": "internal/routers/api_router/metrics.go",
    "content": "package api_router\n\nimport (\n\t\"expvar\"\n\t\"fmt\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// Expvar exports system runtime metrics\n// Expvar 导出系统运行时指标\n// Function name: Expvar\n// 函数名: Expvar\n// usage: Handles HTTP requests for system runtime metrics (expvar). Writes JSON data exported by expvar to the response.\n// 函数使用说明: 处理获取系统运行时指标 (expvar) 的 HTTP 请求。将 expvar 导出的 JSON 数据写入响应。\n// Parameters:\n//   - c *gin.Context: Gin Context\n//\n// 参数说明:\n//   - c *gin.Context: Gin 上下文\n//\n// Return:\n//   - JSON: JSON data containing system metrics\n//\n// 返回值说明:\n//   - JSON: 包含系统指标的 JSON 数据\nfunc Expvar(c *gin.Context) {\n\tc.Writer.Header().Set(\"Content-Type\", \"application/json; charset=utf-8\")\n\tfirst := true\n\treport := func(key string, value interface{}) {\n\t\tif !first {\n\t\t\tfmt.Fprintf(c.Writer, \",\\n\")\n\t\t}\n\t\tfirst = false\n\t\tif str, ok := value.(string); ok {\n\t\t\tfmt.Fprintf(c.Writer, \"%q: %q\", key, str)\n\t\t} else {\n\t\t\tfmt.Fprintf(c.Writer, \"%q: %v\", key, value)\n\t\t}\n\t}\n\n\tfmt.Fprintf(c.Writer, \"{\\n\")\n\texpvar.Do(func(kv expvar.KeyValue) {\n\t\treport(kv.Key, kv.Value)\n\t})\n\tfmt.Fprintf(c.Writer, \"\\n}\\n\")\n}\n"
  },
  {
    "path": "internal/routers/api_router/metrics_test.go",
    "content": "package api_router\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestExpvar_Success(t *testing.T) {\n\tw := httptest.NewRecorder()\n\tc, _ := gin.CreateTestContext(w)\n\treq := httptest.NewRequest(\"GET\", \"/debug/vars\", nil)\n\tc.Request = req\n\n\tExpvar(c)\n\n\tassert.Equal(t, http.StatusOK, w.Code)\n\tassert.Contains(t, w.Header().Get(\"Content-Type\"), \"application/json\")\n\t\n\t// expvar should at least contain \"cmdline\" and \"memstats\" by default in Go\n\tassert.Contains(t, w.Body.String(), \"cmdline\")\n\tassert.Contains(t, w.Body.String(), \"memstats\")\n}\n"
  },
  {
    "path": "internal/routers/mcp_router/file_tools.go",
    "content": "package mcp_router\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"github.com/mark3labs/mcp-go/mcp\"\n\tmcpsrv \"github.com/mark3labs/mcp-go/server\"\n)\n\nfunc registerFileTools(srv *mcpsrv.MCPServer, appContainer *app.App, wss *pkgapp.WebsocketServer) {\n\tfileSvc := appContainer.FileService\n\n\t// 1. List Files\n\ttoolListFiles := mcp.NewTool(\"file_list\",\n\t\tmcp.WithDescription(\"List files in a vault\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"keyword\", mcp.Description(\"Search keyword\")),\n\t)\n\tsrv.AddTool(toolListFiles, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\tkeyword, _ := args[\"keyword\"].(string)\n\n\t\tpager := &pkgapp.Pager{\n\t\t\tPage:     pkgapp.GetPage(1),\n\t\t\tPageSize: pkgapp.GetPageSize(100),\n\t\t}\n\t\tfiles, _, err := fileSvc.WithClient(getClientInfoFromContext(ctx)).List(ctx, uid, &dto.FileListRequest{\n\t\t\tVault:   vault,\n\t\t\tKeyword: keyword,\n\t\t}, pager)\n\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\tresStr := fmt.Sprintf(\"Found %d files:\\n\", len(files))\n\t\tfor _, f := range files {\n\t\t\tresStr += fmt.Sprintf(\"- %s (Size: %d)\\n\", f.Path, f.Size)\n\t\t}\n\t\treturn mcp.NewToolResultText(resStr), nil\n\t})\n\n\t// 2. Get File Info\n\ttoolGetFileInfo := mcp.NewTool(\"file_get_info\",\n\t\tmcp.WithDescription(\"Get file metadata information\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"path\", mcp.Required(), mcp.Description(\"File path\")),\n\t)\n\tsrv.AddTool(toolGetFileInfo, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\tpath, _ := args[\"path\"].(string)\n\n\t\tfile, err := fileSvc.WithClient(getClientInfoFromContext(ctx)).Get(ctx, uid, &dto.FileGetRequest{\n\t\t\tVault:    vault,\n\t\t\tPath:     path,\n\t\t\tPathHash: util.EncodeHash32(path),\n\t\t})\n\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\tresStr := fmt.Sprintf(\"File path: %s\\nSize: %d bytes\\nMtime: %d\", file.Path, file.Size, file.Mtime)\n\t\treturn mcp.NewToolResultText(resStr), nil\n\t})\n\n\t// 3. Get File Content (Read File)\n\ttoolGetContent := mcp.NewTool(\"file_read\",\n\t\tmcp.WithDescription(\"Read file content and return. Returned as base64 string because it might be binary.\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"path\", mcp.Required(), mcp.Description(\"File path\")),\n\t)\n\tsrv.AddTool(toolGetContent, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\tpath, _ := args[\"path\"].(string)\n\n\t\treader, _, _, _, err := fileSvc.WithClient(getClientInfoFromContext(ctx)).GetContent(ctx, uid, &dto.FileGetRequest{\n\t\t\tVault:    vault,\n\t\t\tPath:     path,\n\t\t\tPathHash: util.EncodeHash32(path),\n\t\t})\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\t\tdefer reader.Close()\n\n\t\tdata, err := io.ReadAll(reader)\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\tb64 := base64.StdEncoding.EncodeToString(data)\n\t\treturn mcp.NewToolResultText(fmt.Sprintf(\"Base64 Content follows:\\n%s\", b64)), nil\n\t})\n\n\t// 4. Delete File\n\ttoolDeleteFile := mcp.NewTool(\"file_delete\",\n\t\tmcp.WithDescription(\"Delete a file\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"path\", mcp.Required(), mcp.Description(\"File path\")),\n\t)\n\tsrv.AddTool(toolDeleteFile, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\tpath, _ := args[\"path\"].(string)\n\n\t\tfile, err := fileSvc.WithClient(getClientInfoFromContext(ctx)).Delete(ctx, uid, &dto.FileDeleteRequest{\n\t\t\tVault:    vault,\n\t\t\tPath:     path,\n\t\t\tPathHash: util.EncodeHash32(path),\n\t\t})\n\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\twss.BroadcastToUser(uid, code.Success.WithData(file).WithVault(vault), \"FileSyncDelete\")\n\t\treturn mcp.NewToolResultText(fmt.Sprintf(\"Deleted file: %s\", file.Path)), nil\n\t})\n\n\t// 5. Rename File\n\ttoolRenameFile := mcp.NewTool(\"file_rename\",\n\t\tmcp.WithDescription(\"Rename a file\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"oldPath\", mcp.Required(), mcp.Description(\"Old file path\")),\n\t\tmcp.WithString(\"newPath\", mcp.Required(), mcp.Description(\"New file path\")),\n\t)\n\tsrv.AddTool(toolRenameFile, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\toldPath, _ := args[\"oldPath\"].(string)\n\t\tnewPath, _ := args[\"newPath\"].(string)\n\n\t\toldFile, newFile, err := fileSvc.WithClient(getClientInfoFromContext(ctx)).Rename(ctx, uid, &dto.FileRenameRequest{\n\t\t\tVault:       vault,\n\t\t\tOldPath:     oldPath,\n\t\t\tOldPathHash: util.EncodeHash32(oldPath),\n\t\t\tPath:        newPath,\n\t\t\tPathHash:    util.EncodeHash32(newPath),\n\t\t})\n\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\twss.BroadcastToUser(uid, code.Success.WithData(dto.FileSyncRenameMessage{\n\t\t\tPath:             newFile.Path,\n\t\t\tPathHash:         newFile.PathHash,\n\t\t\tContentHash:      newFile.ContentHash,\n\t\t\tCtime:            newFile.Ctime,\n\t\t\tMtime:            newFile.Mtime,\n\t\t\tSize:             newFile.Size,\n\t\t\tUpdatedTimestamp: newFile.UpdatedTimestamp,\n\t\t\tOldPath:          oldFile.Path,\n\t\t\tOldPathHash:      oldFile.PathHash,\n\t\t}).WithVault(vault), \"FileSyncRename\")\n\t\treturn mcp.NewToolResultText(fmt.Sprintf(\"Renamed file from %s to %s\", oldFile.Path, newFile.Path)), nil\n\t})\n\n\t// 1. Restore File\n\ttoolRestoreFile := mcp.NewTool(\"file_restore\",\n\t\tmcp.WithDescription(\"Restore a deleted file from recycle bin\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"path\", mcp.Required(), mcp.Description(\"File path\")),\n\t)\n\tsrv.AddTool(toolRestoreFile, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\tpath, _ := args[\"path\"].(string)\n\n\t\tfile, err := fileSvc.WithClient(getClientInfoFromContext(ctx)).Restore(ctx, uid, &dto.FileRestoreRequest{\n\t\t\tVault:    vault,\n\t\t\tPath:     path,\n\t\t\tPathHash: util.EncodeHash32(path),\n\t\t})\n\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\twss.BroadcastToUser(uid, code.Success.WithData(file).WithVault(vault), \"FileSyncUpdate\")\n\t\treturn mcp.NewToolResultText(fmt.Sprintf(\"Restored file: %s\", file.Path)), nil\n\t})\n\n\t// 2. Recycle Clear File\n\ttoolRecycleClearFile := mcp.NewTool(\"file_recycle_clear\",\n\t\tmcp.WithDescription(\"Permanently delete a file from recycle bin (or all if path is empty)\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"path\", mcp.Description(\"File path. If empty, potentially clear all\")),\n\t)\n\tsrv.AddTool(toolRecycleClearFile, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\tpath, _ := args[\"path\"].(string)\n\n\t\terr := fileSvc.WithClient(getClientInfoFromContext(ctx)).RecycleClear(ctx, uid, &dto.FileRecycleClearRequest{\n\t\t\tVault:    vault,\n\t\t\tPath:     path,\n\t\t\tPathHash: util.EncodeHash32(path),\n\t\t})\n\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\treturn mcp.NewToolResultText(\"Recycle clear successful\"), nil\n\t})\n}\n"
  },
  {
    "path": "internal/routers/mcp_router/mcp.go",
    "content": "package mcp_router\n\nimport (\n\t\"context\"\n\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t_ \"github.com/gookit/goutil/dump\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\tmcpserver \"github.com/mark3labs/mcp-go/server\"\n)\n\ntype MCPHandler struct {\n\tmcpServer       *mcpserver.MCPServer\n\tsseServer       *mcpserver.SSEServer\n\tssePingInterval time.Duration // SSE heartbeat interval / SSE 心跳间隔\n}\n\nfunc NewMCPHandler(appContainer *app.App, wss *pkgapp.WebsocketServer) *MCPHandler {\n\tcfg := appContainer.Config()\n\tpingInterval := time.Duration(cfg.Server.MCPSSEPingInterval) * time.Second\n\tif pingInterval <= 0 {\n\t\tpingInterval = 30 * time.Second // fallback default\n\t}\n\n\tsrv := NewMCPServer(appContainer, wss)\n\n\tsseSrv := mcpserver.NewSSEServer(srv,\n\t\tmcpserver.WithMessageEndpoint(\"/api/mcp/message\"),\n\t\tmcpserver.WithKeepAlive(true),\n\t\tmcpserver.WithKeepAliveInterval(pingInterval),\n\t\tmcpserver.WithSSEContextFunc(func(ctx context.Context, r *http.Request) context.Context {\n\t\t\tif val := r.Context().Value(\"uid\"); val != nil {\n\t\t\t\tctx = context.WithValue(ctx, \"uid\", val)\n\t\t\t}\n\t\t\tif vaultName := r.Header.Get(\"X-Default-Vault-Name\"); vaultName != \"\" {\n\t\t\t\tctx = context.WithValue(ctx, \"default_vault_name\", vaultName)\n\t\t\t}\n\n\t\t\t// Extract client info\n\t\t\tif clientType := r.Header.Get(\"X-Client\"); clientType != \"\" {\n\t\t\t\tctx = context.WithValue(ctx, \"client_type\", clientType)\n\t\t\t}\n\t\t\tclientName := r.Header.Get(\"X-Client-Name\")\n\t\t\tif clientName == \"\" {\n\t\t\t\tclientName = \"MCP\"\n\t\t\t} else {\n\t\t\t\tif decoded, err := url.QueryUnescape(clientName); err == nil {\n\t\t\t\t\tclientName = decoded\n\t\t\t\t}\n\t\t\t\tclientName = \"MCP \" + clientName\n\t\t\t}\n\t\t\tctx = context.WithValue(ctx, \"client_name\", clientName)\n\t\t\tif clientVersion := r.Header.Get(\"X-Client-Version\"); clientVersion != \"\" {\n\t\t\t\tctx = context.WithValue(ctx, \"client_version\", clientVersion)\n\t\t\t}\n\t\t\treturn ctx\n\t\t}))\n\n\treturn &MCPHandler{\n\t\tmcpServer:       srv,\n\t\tsseServer:       sseSrv,\n\t\tssePingInterval: pingInterval,\n\t}\n}\n\nfunc (h *MCPHandler) HandleSSE(c *gin.Context) {\n\tuid := pkgapp.GetUID(c)\n\tctx := context.WithValue(c.Request.Context(), \"uid\", uid)\n\tif vaultName := c.GetHeader(\"X-Default-Vault-Name\"); vaultName != \"\" {\n\t\tctx = context.WithValue(ctx, \"default_vault_name\", vaultName)\n\t}\n\n\t// Extract client info\n\tif clientType := c.GetHeader(\"X-Client\"); clientType != \"\" {\n\t\tctx = context.WithValue(ctx, \"client_type\", clientType)\n\t}\n\tif clientName := c.GetHeader(\"X-Client-Name\"); clientName != \"\" {\n\t\tif decoded, err := url.QueryUnescape(clientName); err == nil {\n\t\t\tclientName = decoded\n\t\t}\n\t\tctx = context.WithValue(ctx, \"client_name\", clientName)\n\t}\n\tif clientVersion := c.GetHeader(\"X-Client-Version\"); clientVersion != \"\" {\n\t\tctx = context.WithValue(ctx, \"client_version\", clientVersion)\n\t}\n\n\t// Set SSE headers\n\t// 设置 SSE 响应头\n\tc.Header(\"Content-Type\", \"text/event-stream\")\n\tc.Header(\"Cache-Control\", \"no-cache\")\n\tc.Header(\"Connection\", \"keep-alive\")\n\tc.Header(\"Proxy-Connection\", \"keep-alive\")\n\tc.Header(\"X-Accel-Buffering\", \"no\") // Disable proxy buffering / 禁用代理缓冲\n\n\t// Flush headers immediately\n\t// 立即发送响应头\n\tc.Writer.Flush()\n\n\t// If it's a HEAD request, we've sent the headers, so we can return\n\t// 如果是 HEAD 请求，我们已经发送了响应头，可以直接返回\n\tif c.Request.Method == http.MethodHead {\n\t\treturn\n\t}\n\n\t// Let SSEServer handle the SSE connection\n\th.sseServer.SSEHandler().ServeHTTP(c.Writer, c.Request.WithContext(ctx))\n}\n\nfunc (h *MCPHandler) HandleMessage(c *gin.Context) {\n\t// Let SSEServer handle the message\n\th.sseServer.MessageHandler().ServeHTTP(c.Writer, c.Request)\n}\n"
  },
  {
    "path": "internal/routers/mcp_router/mcp_test.go",
    "content": "package mcp_router\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestHandleSSE_HEAD(t *testing.T) {\n\tgin.SetMode(gin.TestMode)\n\t\n\t// Create a dummy app container or use a real one if possible\n\t// Here we just need a handler, but NewMCPHandler requires app and websocket server.\n\t// For simplicity, we can mock the behavior or just test if the handler sets the header.\n\t\n\tr := gin.New()\n\t\n\t// We'll manually create a handler state that doesn't crash\n\th := &MCPHandler{} \n\n\tr.Match([]string{http.MethodGet, http.MethodHead}, \"/sse\", h.HandleSSE)\n\n\tw := httptest.NewRecorder()\n\treq, _ := http.NewRequest(http.MethodHead, \"/sse\", nil)\n\tr.ServeHTTP(w, req)\n\n\tassert.Equal(t, \"text/event-stream\", w.Header().Get(\"Content-Type\"))\n\tassert.Equal(t, \"no-cache\", w.Header().Get(\"Cache-Control\"))\n\tassert.Equal(t, \"keep-alive\", w.Header().Get(\"Connection\"))\n}\n"
  },
  {
    "path": "internal/routers/mcp_router/note_tools.go",
    "content": "package mcp_router\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"github.com/mark3labs/mcp-go/mcp\"\n\tmcpsrv \"github.com/mark3labs/mcp-go/server\"\n)\n\nfunc registerNoteTools(srv *mcpsrv.MCPServer, appContainer *app.App, wss *pkgapp.WebsocketServer) {\n\tnoteSvc := appContainer.NoteService\n\n\t// 1. List Notes\n\ttoolListNotes := mcp.NewTool(\"note_list\",\n\t\tmcp.WithDescription(\"List notes in a vault\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"keyword\", mcp.Description(\"Search keyword\")),\n\t)\n\tsrv.AddTool(toolListNotes, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\tkeyword, _ := args[\"keyword\"].(string)\n\n\t\tpager := &pkgapp.Pager{\n\t\t\tPage:     pkgapp.GetPage(1),\n\t\t\tPageSize: pkgapp.GetPageSize(100),\n\t\t}\n\t\tnotes, _, err := noteSvc.WithClient(getClientInfoFromContext(ctx)).List(ctx, uid, &dto.NoteListRequest{\n\t\t\tVault:   vault,\n\t\t\tKeyword: keyword,\n\t\t}, pager)\n\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\tresStr := fmt.Sprintf(\"Found %d notes:\\n\", len(notes))\n\t\tfor _, n := range notes {\n\t\t\tresStr += fmt.Sprintf(\"- %s (ID: %d, Size: %d, Mtime: %d)\\n\", n.Path, n.ID, n.Size, n.Mtime)\n\t\t}\n\t\treturn mcp.NewToolResultText(resStr), nil\n\t})\n\n\t// 2. Get Note\n\ttoolGetNote := mcp.NewTool(\"note_get\",\n\t\tmcp.WithDescription(\"Get a single note by path\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"path\", mcp.Required(), mcp.Description(\"Note path\")),\n\t)\n\tsrv.AddTool(toolGetNote, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\tpath, _ := args[\"path\"].(string)\n\t\tpathHash := util.EncodeHash32(path)\n\n\t\tnote, err := noteSvc.WithClient(getClientInfoFromContext(ctx)).Get(ctx, uid, &dto.NoteGetRequest{\n\t\t\tVault:    vault,\n\t\t\tPath:     path,\n\t\t\tPathHash: pathHash,\n\t\t})\n\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\treturn mcp.NewToolResultText(note.Content), nil\n\t})\n\n\t// 3. Create or Update Note\n\ttoolCreateUpdateNote := mcp.NewTool(\"note_create_or_update\",\n\t\tmcp.WithDescription(\"Create or update a note\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"path\", mcp.Required(), mcp.Description(\"Note path\")),\n\t\tmcp.WithString(\"content\", mcp.Required(), mcp.Description(\"Note content\")),\n\t)\n\tsrv.AddTool(toolCreateUpdateNote, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\tpath, _ := args[\"path\"].(string)\n\t\tcontent, _ := args[\"content\"].(string)\n\t\tpathHash := util.EncodeHash32(path)\n\t\tcontentHash := util.EncodeHash32(content)\n\n\t\tnow := time.Now().UnixMilli()\n\t\t_, note, err := noteSvc.WithClient(getClientInfoFromContext(ctx)).ModifyOrCreate(ctx, uid, &dto.NoteModifyOrCreateRequest{\n\t\t\tVault:       vault,\n\t\t\tPath:        path,\n\t\t\tPathHash:    pathHash,\n\t\t\tContent:     content,\n\t\t\tContentHash: contentHash,\n\t\t\tMtime:       now,\n\t\t\tCtime:       now,\n\t\t}, false)\n\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\twss.BroadcastToUser(uid, code.Success.WithData(note).WithVault(vault), \"NoteSyncModify\")\n\t\treturn mcp.NewToolResultText(fmt.Sprintf(\"Successfully saved note: %s (Version: %d)\", note.Path, note.Version)), nil\n\t})\n\n\t// 4. Delete Note\n\ttoolDeleteNote := mcp.NewTool(\"note_delete\",\n\t\tmcp.WithDescription(\"Delete a note\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"path\", mcp.Required(), mcp.Description(\"Note path\")),\n\t)\n\tsrv.AddTool(toolDeleteNote, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\tpath, _ := args[\"path\"].(string)\n\t\tpathHash := util.EncodeHash32(path)\n\n\t\tnote, err := noteSvc.WithClient(getClientInfoFromContext(ctx)).Delete(ctx, uid, &dto.NoteDeleteRequest{\n\t\t\tVault:    vault,\n\t\t\tPath:     path,\n\t\t\tPathHash: pathHash,\n\t\t})\n\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\twss.BroadcastToUser(uid, code.Success.WithData(note).WithVault(vault), \"NoteSyncDelete\")\n\t\treturn mcp.NewToolResultText(fmt.Sprintf(\"Deleted note: %s\", note.Path)), nil\n\t})\n\n\t// 5. Rename Note\n\ttoolRenameNote := mcp.NewTool(\"note_rename\",\n\t\tmcp.WithDescription(\"Rename a note\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"oldPath\", mcp.Required(), mcp.Description(\"Old note path\")),\n\t\tmcp.WithString(\"newPath\", mcp.Required(), mcp.Description(\"New note path\")),\n\t)\n\tsrv.AddTool(toolRenameNote, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\toldPath, _ := args[\"oldPath\"].(string)\n\t\tnewPath, _ := args[\"newPath\"].(string)\n\n\t\toldNote, newNote, err := noteSvc.WithClient(getClientInfoFromContext(ctx)).Rename(ctx, uid, &dto.NoteRenameRequest{\n\t\t\tVault:       vault,\n\t\t\tOldPath:     oldPath,\n\t\t\tOldPathHash: util.EncodeHash32(oldPath),\n\t\t\tPath:        newPath,\n\t\t\tPathHash:    util.EncodeHash32(newPath),\n\t\t})\n\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\twss.BroadcastToUser(uid, code.Success.WithData(dto.NoteSyncRenameMessage{\n\t\t\tPath:             newNote.Path,\n\t\t\tPathHash:         newNote.PathHash,\n\t\t\tContentHash:      newNote.ContentHash,\n\t\t\tCtime:            newNote.Ctime,\n\t\t\tMtime:            newNote.Mtime,\n\t\t\tSize:             newNote.Size,\n\t\t\tOldPath:          oldNote.Path,\n\t\t\tOldPathHash:      oldNote.PathHash,\n\t\t\tUpdatedTimestamp: newNote.UpdatedTimestamp,\n\t\t}).WithVault(vault), \"NoteSyncRename\")\n\t\treturn mcp.NewToolResultText(fmt.Sprintf(\"Renamed note from %s to %s\", oldNote.Path, newNote.Path)), nil\n\t})\n\n\t// 1. Restore Note\n\ttoolRestoreNote := mcp.NewTool(\"note_restore\",\n\t\tmcp.WithDescription(\"Restore a deleted note from recycle bin\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"path\", mcp.Required(), mcp.Description(\"Note path\")),\n\t)\n\tsrv.AddTool(toolRestoreNote, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\tpath, _ := args[\"path\"].(string)\n\n\t\tnote, err := noteSvc.WithClient(getClientInfoFromContext(ctx)).Restore(ctx, uid, &dto.NoteRestoreRequest{\n\t\t\tVault:    vault,\n\t\t\tPath:     path,\n\t\t\tPathHash: util.EncodeHash32(path),\n\t\t})\n\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\twss.BroadcastToUser(uid, code.Success.WithData(note).WithVault(vault), \"NoteSyncModify\")\n\t\treturn mcp.NewToolResultText(fmt.Sprintf(\"Restored note: %s\", note.Path)), nil\n\t})\n\n\t// 2. Recycle Clear Note\n\ttoolRecycleClear := mcp.NewTool(\"note_recycle_clear\",\n\t\tmcp.WithDescription(\"Permanently delete a note from recycle bin (or all if path is empty)\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"path\", mcp.Description(\"Note path. If empty, potentially clear all (based on service logic)\")),\n\t)\n\tsrv.AddTool(toolRecycleClear, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\tpath, _ := args[\"path\"].(string)\n\n\t\terr := noteSvc.WithClient(getClientInfoFromContext(ctx)).RecycleClear(ctx, uid, &dto.NoteRecycleClearRequest{\n\t\t\tVault:    vault,\n\t\t\tPath:     path,\n\t\t\tPathHash: util.EncodeHash32(path),\n\t\t})\n\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\treturn mcp.NewToolResultText(\"Recycle clear successful\"), nil\n\t})\n\n\t// 3. Patch Frontmatter\n\ttoolPatchFrontmatter := mcp.NewTool(\"note_patch_frontmatter\",\n\t\tmcp.WithDescription(\"Patch (update or remove) frontmatter of a note\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"path\", mcp.Required(), mcp.Description(\"Note path\")),\n\t\tmcp.WithString(\"updates\", mcp.Description(\"JSON string for fields to update (e.g. {\\\"tags\\\":[\\\"t1\\\"]})\")),\n\t\tmcp.WithString(\"remove\", mcp.Description(\"JSON string array for fields to remove (e.g. [\\\"old_tag\\\"])\")),\n\t)\n\tsrv.AddTool(toolPatchFrontmatter, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\tpath, _ := args[\"path\"].(string)\n\t\tupdatesStr, _ := args[\"updates\"].(string)\n\t\tremoveStr, _ := args[\"remove\"].(string)\n\n\t\tvar updates map[string]interface{}\n\t\tif updatesStr != \"\" {\n\t\t\tif err := json.Unmarshal([]byte(updatesStr), &updates); err != nil {\n\t\t\t\treturn mcp.NewToolResultError(fmt.Sprintf(\"Invalid JSON for updates: %v\", err)), nil\n\t\t\t}\n\t\t}\n\n\t\tvar remove []string\n\t\tif removeStr != \"\" {\n\t\t\tif err := json.Unmarshal([]byte(removeStr), &remove); err != nil {\n\t\t\t\treturn mcp.NewToolResultError(fmt.Sprintf(\"Invalid JSON for remove: %v\", err)), nil\n\t\t\t}\n\t\t}\n\n\t\tnote, err := noteSvc.WithClient(getClientInfoFromContext(ctx)).PatchFrontmatter(ctx, uid, &dto.NotePatchFrontmatterRequest{\n\t\t\tVault:    vault,\n\t\t\tPath:     path,\n\t\t\tPathHash: util.EncodeHash32(path),\n\t\t\tUpdates:  updates,\n\t\t\tRemove:   remove,\n\t\t})\n\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\twss.BroadcastToUser(uid, code.Success.WithData(note).WithVault(vault), \"NoteSyncModify\")\n\t\treturn mcp.NewToolResultText(fmt.Sprintf(\"Frontmatter patched for %s\", note.Path)), nil\n\t})\n\n\t// 4. Append\n\ttoolAppend := mcp.NewTool(\"note_append\",\n\t\tmcp.WithDescription(\"Append content to the end of a note\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"path\", mcp.Required(), mcp.Description(\"Note path\")),\n\t\tmcp.WithString(\"content\", mcp.Required(), mcp.Description(\"Content to append\")),\n\t)\n\tsrv.AddTool(toolAppend, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\tpath, _ := args[\"path\"].(string)\n\t\tcontent, _ := args[\"content\"].(string)\n\n\t\tnote, err := noteSvc.WithClient(getClientInfoFromContext(ctx)).AppendContent(ctx, uid, &dto.NoteAppendRequest{\n\t\t\tVault:    vault,\n\t\t\tPath:     path,\n\t\t\tPathHash: util.EncodeHash32(path),\n\t\t\tContent:  content,\n\t\t})\n\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\twss.BroadcastToUser(uid, code.Success.WithData(note).WithVault(vault), \"NoteSyncModify\")\n\t\treturn mcp.NewToolResultText(fmt.Sprintf(\"Appended content to %s\", note.Path)), nil\n\t})\n\n\t// 5. Prepend\n\ttoolPrepend := mcp.NewTool(\"note_prepend\",\n\t\tmcp.WithDescription(\"Prepend content to the beginning of a note (after frontmatter)\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"path\", mcp.Required(), mcp.Description(\"Note path\")),\n\t\tmcp.WithString(\"content\", mcp.Required(), mcp.Description(\"Content to prepend\")),\n\t)\n\tsrv.AddTool(toolPrepend, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\tpath, _ := args[\"path\"].(string)\n\t\tcontent, _ := args[\"content\"].(string)\n\n\t\tnote, err := noteSvc.WithClient(getClientInfoFromContext(ctx)).PrependContent(ctx, uid, &dto.NotePrependRequest{\n\t\t\tVault:    vault,\n\t\t\tPath:     path,\n\t\t\tPathHash: util.EncodeHash32(path),\n\t\t\tContent:  content,\n\t\t})\n\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\twss.BroadcastToUser(uid, code.Success.WithData(note).WithVault(vault), \"NoteSyncModify\")\n\t\treturn mcp.NewToolResultText(fmt.Sprintf(\"Prepended content to %s\", note.Path)), nil\n\t})\n\n\t// 6. Replace\n\ttoolReplace := mcp.NewTool(\"note_replace\",\n\t\tmcp.WithDescription(\"Find and replace text in a note\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"path\", mcp.Required(), mcp.Description(\"Note path\")),\n\t\tmcp.WithString(\"find\", mcp.Required(), mcp.Description(\"Content to find\")),\n\t\tmcp.WithString(\"replace\", mcp.Required(), mcp.Description(\"Content to replace with\")),\n\t\tmcp.WithBoolean(\"regex\", mcp.Description(\"Use regex matching (default false)\")),\n\t\tmcp.WithBoolean(\"all\", mcp.Description(\"Replace all matches (default true)\")),\n\t\tmcp.WithBoolean(\"failIfNoMatch\", mcp.Description(\"Fail if no match (default true)\")),\n\t)\n\tsrv.AddTool(toolReplace, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\tpath, _ := args[\"path\"].(string)\n\t\tfind, _ := args[\"find\"].(string)\n\t\treplace, _ := args[\"replace\"].(string)\n\t\tregex, okRegex := args[\"regex\"].(bool)\n\t\tif !okRegex {\n\t\t\tregex = false\n\t\t}\n\t\tall, okAll := args[\"all\"].(bool)\n\t\tif !okAll {\n\t\t\tall = true\n\t\t}\n\t\tfailIfNoMatch, okFail := args[\"failIfNoMatch\"].(bool)\n\t\tif !okFail {\n\t\t\tfailIfNoMatch = true\n\t\t}\n\n\t\tres, err := noteSvc.WithClient(getClientInfoFromContext(ctx)).ReplaceContent(ctx, uid, &dto.NoteReplaceRequest{\n\t\t\tVault:         vault,\n\t\t\tPath:          path,\n\t\t\tPathHash:      util.EncodeHash32(path),\n\t\t\tFind:          find,\n\t\t\tReplace:       replace,\n\t\t\tRegex:         regex,\n\t\t\tAll:           all,\n\t\t\tFailIfNoMatch: failIfNoMatch,\n\t\t})\n\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\twss.BroadcastToUser(uid, code.Success.WithData(res.Note).WithVault(vault), \"NoteSyncModify\")\n\t\treturn mcp.NewToolResultText(fmt.Sprintf(\"Replaced %d occurrences\", res.MatchCount)), nil\n\t})\n\n\t// 7. Get Backlinks\n\ttoolGetBacklinks := mcp.NewTool(\"note_get_backlinks\",\n\t\tmcp.WithDescription(\"Get backlinks to a note\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"path\", mcp.Required(), mcp.Description(\"Note path\")),\n\t)\n\tsrv.AddTool(toolGetBacklinks, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\tpath, _ := args[\"path\"].(string)\n\n\t\tlinkSvc := appContainer.NoteLinkService\n\t\tlinks, err := linkSvc.GetBacklinks(ctx, uid, &dto.NoteLinkQueryRequest{\n\t\t\tVault:    vault,\n\t\t\tPath:     path,\n\t\t\tPathHash: util.EncodeHash32(path),\n\t\t})\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\tb, err := json.Marshal(links)\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\treturn mcp.NewToolResultText(string(b)), nil\n\t})\n\n\t// 8. Get Outlinks\n\ttoolGetOutlinks := mcp.NewTool(\"note_get_outlinks\",\n\t\tmcp.WithDescription(\"Get outlinks from a note\"),\n\t\tmcp.WithString(\"vault\", mcp.Description(\"Vault name. Omitting this or providing 'default' will use the client-configured default vault.\")),\n\t\tmcp.WithString(\"path\", mcp.Required(), mcp.Description(\"Note path\")),\n\t)\n\tsrv.AddTool(toolGetOutlinks, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvault, _ := args[\"vault\"].(string)\n\t\tif vault == \"\" || strings.EqualFold(vault, \"default\") {\n\t\t\tvault = getDefaultVaultName(ctx, appContainer)\n\t\t}\n\t\tpath, _ := args[\"path\"].(string)\n\n\t\tlinkSvc := appContainer.NoteLinkService\n\t\tlinks, err := linkSvc.GetOutlinks(ctx, uid, &dto.NoteLinkQueryRequest{\n\t\t\tVault:    vault,\n\t\t\tPath:     path,\n\t\t\tPathHash: util.EncodeHash32(path),\n\t\t})\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\tb, err := json.Marshal(links)\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\treturn mcp.NewToolResultText(string(b)), nil\n\t})\n}\n"
  },
  {
    "path": "internal/routers/mcp_router/server.go",
    "content": "package mcp_router\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/mark3labs/mcp-go/mcp\"\n\tmcpsrv \"github.com/mark3labs/mcp-go/server\"\n)\n\nfunc getUIDFromContext(ctx context.Context) int64 {\n\tif val := ctx.Value(\"uid\"); val != nil {\n\t\tif uid, ok := val.(int64); ok {\n\t\t\treturn uid\n\t\t}\n\t}\n\treturn 1\n}\n\nfunc getClientInfoFromContext(ctx context.Context) (string, string, string) {\n\tvar cType, cName, cVer string\n\tif val := ctx.Value(\"client_type\"); val != nil {\n\t\tcType, _ = val.(string)\n\t}\n\tif val := ctx.Value(\"client_name\"); val != nil {\n\t\tcName, _ = val.(string)\n\t}\n\tif val := ctx.Value(\"client_version\"); val != nil {\n\t\tcVer, _ = val.(string)\n\t}\n\treturn cType, cName, cVer\n}\n\nfunc getDefaultVaultName(ctx context.Context, appContainer *app.App) string {\n\t// 1. From context (Header X-Default-Vault-Name)\n\tif val := ctx.Value(\"default_vault_name\"); val != nil {\n\t\tif name, ok := val.(string); ok && name != \"\" {\n\t\t\treturn name\n\t\t}\n\t}\n\n\tuid := getUIDFromContext(ctx)\n\n\t// 2. From user settings (placeholder, assuming there might be a default vault setting)\n\t// We can try to list vaults and pick the first one as a fallback for now\n\tvaults, err := appContainer.VaultService.List(ctx, uid)\n\tif err == nil && len(vaults) > 0 {\n\t\treturn vaults[0].Name\n\t}\n\n\treturn \"Default\"\n}\n\nfunc getArgs(req mcp.CallToolRequest) map[string]interface{} {\n\tif req.Params.Arguments != nil {\n\t\tif args, ok := req.Params.Arguments.(map[string]interface{}); ok {\n\t\t\treturn args\n\t\t}\n\t}\n\treturn make(map[string]interface{})\n}\n\nfunc NewMCPServer(appContainer *app.App, wss *pkgapp.WebsocketServer) *mcpsrv.MCPServer {\n\t// Create MCP server\n\tsrv := mcpsrv.NewMCPServer(\n\t\t\"fast-note-sync-service\",\n\t\tappContainer.Version().Version,\n\t)\n\n\t// Note Tools\n\tregisterNoteTools(srv, appContainer, wss)\n\n\t// File Tools\n\tregisterFileTools(srv, appContainer, wss)\n\n\t// Vault Tools\n\tregisterVaultTools(srv, appContainer)\n\n\treturn srv\n}\n"
  },
  {
    "path": "internal/routers/mcp_router/vault_tools.go",
    "content": "package mcp_router\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/mark3labs/mcp-go/mcp\"\n\tmcpsrv \"github.com/mark3labs/mcp-go/server\"\n)\n\nfunc getInt64Arg(args map[string]interface{}, key string) int64 {\n\tif val, ok := args[key]; ok {\n\t\tif f, ok := val.(float64); ok {\n\t\t\treturn int64(f)\n\t\t}\n\t\tif i, ok := val.(int64); ok {\n\t\t\treturn i\n\t\t}\n\t\tif i, ok := val.(int); ok {\n\t\t\treturn int64(i)\n\t\t}\n\t}\n\treturn 0\n}\n\nfunc registerVaultTools(srv *mcpsrv.MCPServer, appContainer *app.App) {\n\tvaultSvc := appContainer.VaultService\n\n\t// 1. List Vaults\n\ttoolListVaults := mcp.NewTool(\"vault_list\",\n\t\tmcp.WithDescription(\"List all available note vaults\"),\n\t)\n\tsrv.AddTool(toolListVaults, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\n\t\tvaults, err := vaultSvc.List(ctx, uid)\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\tif len(vaults) == 0 {\n\t\t\treturn mcp.NewToolResultText(\"No vaults found.\"), nil\n\t\t}\n\n\t\tresStr := fmt.Sprintf(\"Found %d vaults:\\n\", len(vaults))\n\t\tfor _, v := range vaults {\n\t\t\tresStr += fmt.Sprintf(\"- %s (ID: %d) [Notes: %d, Files: %d]\\n\", v.Name, v.ID, v.NoteCount, v.FileCount)\n\t\t}\n\t\treturn mcp.NewToolResultText(resStr), nil\n\t})\n\n\t// 2. Get Vault\n\ttoolGetVault := mcp.NewTool(\"vault_get\",\n\t\tmcp.WithDescription(\"Get details of a specific vault by ID\"),\n\t\tmcp.WithNumber(\"id\", mcp.Required(), mcp.Description(\"Vault ID\")),\n\t)\n\tsrv.AddTool(toolGetVault, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tid := getInt64Arg(args, \"id\")\n\n\t\tvault, err := vaultSvc.Get(ctx, uid, id)\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\tresStr := fmt.Sprintf(\"Vault: %s\\nID: %d\\nNotes: %d\\nFiles: %d\\nTotal Size: %d\", vault.Name, vault.ID, vault.NoteCount, vault.FileCount, vault.Size)\n\t\treturn mcp.NewToolResultText(resStr), nil\n\t})\n\n\t// 3. Create or Update Vault\n\ttoolCreateUpdateVault := mcp.NewTool(\"vault_create_or_update\",\n\t\tmcp.WithDescription(\"Create a new vault or update an existing vault (by passing 'id')\"),\n\t\tmcp.WithString(\"vault\", mcp.Required(), mcp.Description(\"Vault name\")),\n\t\tmcp.WithNumber(\"id\", mcp.Description(\"Vault ID for update. Omit or 0 to create new vault.\")),\n\t)\n\tsrv.AddTool(toolCreateUpdateVault, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tvaultName, _ := args[\"vault\"].(string)\n\t\tid := getInt64Arg(args, \"id\")\n\n\t\tif id > 0 {\n\t\t\tvault, err := vaultSvc.Update(ctx, uid, id, vaultName)\n\t\t\tif err != nil {\n\t\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t\t}\n\t\t\treturn mcp.NewToolResultText(fmt.Sprintf(\"Vault updated: %s (ID: %d)\", vault.Name, vault.ID)), nil\n\t\t} else {\n\t\t\tvault, err := vaultSvc.Create(ctx, uid, vaultName)\n\t\t\tif err != nil {\n\t\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t\t}\n\t\t\treturn mcp.NewToolResultText(fmt.Sprintf(\"Vault created: %s (ID: %d)\", vault.Name, vault.ID)), nil\n\t\t}\n\t})\n\n\t// 4. Delete Vault\n\ttoolDeleteVault := mcp.NewTool(\"vault_delete\",\n\t\tmcp.WithDescription(\"Delete a vault by ID\"),\n\t\tmcp.WithNumber(\"id\", mcp.Required(), mcp.Description(\"Vault ID\")),\n\t)\n\tsrv.AddTool(toolDeleteVault, func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {\n\t\tuid := getUIDFromContext(ctx)\n\t\targs := getArgs(req)\n\t\tid := getInt64Arg(args, \"id\")\n\n\t\terr := vaultSvc.Delete(ctx, uid, id)\n\t\tif err != nil {\n\t\t\treturn mcp.NewToolResultError(err.Error()), nil\n\t\t}\n\n\t\treturn mcp.NewToolResultText(fmt.Sprintf(\"Deleted vault with ID: %d\", id)), nil\n\t})\n}\n"
  },
  {
    "path": "internal/routers/pprof.go",
    "content": "package routers\n\nimport (\n\t\"net/http\"\n\t\"net/http/pprof\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/middleware\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/routers/api_router\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/prometheus/client_golang/prometheus/promhttp\"\n\t\"go.uber.org/zap\"\n)\n\nconst (\n\t// DefaultPrefix url prefix of pprof\n\tDefaultPrefix = \"/debug/pprof\"\n)\n\n// NewPrivateRouterWithLogger creates private router (using injected logger)\n// NewPrivateRouterWithLogger 创建私有路由（使用注入的日志器）\nfunc NewPrivateRouterWithLogger(runMode string, logger *zap.Logger) *gin.Engine {\n\n\tr := gin.New()\n\n\tif runMode == \"debug\" {\n\t\tr.Use(gin.Recovery())\n\t} else {\n\t\tr.Use(middleware.RecoveryWithLogger(logger))\n\t}\n\n\t// prom monitoring\n\t// prom监控\n\tr.GET(\"/debug/vars\", api_router.Expvar)\n\tr.GET(\"metrics\", gin.WrapH(promhttp.Handler()))\n\n\tif runMode == \"debug\" {\n\t\tp := r.Group(\"pprof\")\n\t\t{\n\t\t\tp.GET(\"/\", pprofHandler(pprof.Index))\n\t\t\tp.GET(\"/cmdline\", pprofHandler(pprof.Cmdline))\n\t\t\tp.GET(\"/profile\", pprofHandler(pprof.Profile))\n\t\t\tp.POST(\"/symbol\", pprofHandler(pprof.Symbol))\n\t\t\tp.GET(\"/symbol\", pprofHandler(pprof.Symbol))\n\t\t\tp.GET(\"/trace\", pprofHandler(pprof.Trace))\n\t\t\tp.GET(\"/allocs\", pprofHandler(pprof.Handler(\"allocs\").ServeHTTP))\n\t\t\tp.GET(\"/block\", pprofHandler(pprof.Handler(\"block\").ServeHTTP))\n\t\t\tp.GET(\"/goroutine\", pprofHandler(pprof.Handler(\"goroutine\").ServeHTTP))\n\t\t\tp.GET(\"/heap\", pprofHandler(pprof.Handler(\"heap\").ServeHTTP))\n\t\t\tp.GET(\"/mutex\", pprofHandler(pprof.Handler(\"mutex\").ServeHTTP))\n\t\t\tp.GET(\"/threadcreate\", pprofHandler(pprof.Handler(\"threadcreate\").ServeHTTP))\n\t\t}\n\t}\n\n\treturn r\n}\n\n// NewPrivateRouterWithConfig creates private router (using injected config, using nop logger)\n// NewPrivateRouterWithConfig 创建私有路由（使用注入的配置，使用 nop logger）\n// Deprecated: Recommended to use NewPrivateRouterWithLogger\n// Deprecated: 推荐使用 NewPrivateRouterWithLogger\nfunc NewPrivateRouterWithConfig(runMode string) *gin.Engine {\n\treturn NewPrivateRouterWithLogger(runMode, zap.NewNop())\n}\n\n// NewPrivateRouter creates private router (using default release mode)\n// NewPrivateRouter 创建私有路由（使用默认 release 模式）\n// Deprecated: Recommended to use NewPrivateRouterWithLogger\n// Deprecated: 推荐使用 NewPrivateRouterWithLogger\nfunc NewPrivateRouter() *gin.Engine {\n\treturn NewPrivateRouterWithConfig(\"release\")\n}\n\nfunc pprofHandler(h http.HandlerFunc) gin.HandlerFunc {\n\thandler := h\n\treturn func(c *gin.Context) {\n\t\thandler.ServeHTTP(c.Writer, c.Request)\n\t}\n}\n"
  },
  {
    "path": "internal/routers/router.go",
    "content": "package routers\n\nimport (\n\t\"embed\"\n\t\"io/fs\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t_ \"github.com/haierkeys/fast-note-sync-service/docs\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/middleware\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/routers/api_router\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/routers/mcp_router\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/routers/websocket_router\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/limiter\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\n\t\"github.com/gin-gonic/gin\"\n\tut \"github.com/go-playground/universal-translator\"\n\t\"github.com/lxzan/gws\"\n\tswaggerFiles \"github.com/swaggo/files\"\n\tginSwagger \"github.com/swaggo/gin-swagger\"\n)\n\nvar methodLimiters = limiter.NewMethodLimiter().AddBuckets(\n\tlimiter.BucketRule{\n\t\tKey:          \"/auth\",\n\t\tFillInterval: time.Second,\n\t\tCapacity:     10,\n\t\tQuantum:      10,\n\t},\n\tlimiter.BucketRule{\n\t\tKey:          \"/api/user/login\",\n\t\tFillInterval: time.Second,\n\t\tCapacity:     5,\n\t\tQuantum:      1,\n\t},\n\tlimiter.BucketRule{\n\t\tKey:          \"/api/share/verify\",\n\t\tFillInterval: time.Second,\n\t\tCapacity:     10,\n\t\tQuantum:      1,\n\t},\n\tlimiter.BucketRule{\n\t\tKey:          \"/api/share/note\",\n\t\tFillInterval: time.Second,\n\t\tCapacity:     10,\n\t\tQuantum:      1,\n\t},\n)\n\nfunc NewRouter(frontendFiles embed.FS, appContainer *app.App, uni *ut.UniversalTranslator) *gin.Engine {\n\n\t// Get configuration\n\t// 获取配置\n\tcfg := appContainer.Config()\n\n\tvar wss = pkgapp.NewWebsocketServer(pkgapp.WSConfig{\n\t\tGWSOption: gws.ServerOption{\n\t\t\tCheckUtf8Enabled: cfg.App.WebSocketCheckUtf8Enabled,\n\t\t\tParallelEnabled:  cfg.App.WebSocketParallelEnabled, // Enable parallel message processing from config\n\t\t\t// 从配置开启并行消息处理\n\t\t\tRecovery: gws.Recovery, // Enable exception recovery\n\t\t\t// 开启异常恢复\n\t\t\tPermessageDeflate: gws.PermessageDeflate{\n\t\t\t\tEnabled:               cfg.App.WebSocketCompressionEnabled,\n\t\t\t\tLevel:                 cfg.App.WebSocketCompressionLevel,\n\t\t\t\tThreshold:             cfg.App.WebSocketCompressionThreshold,\n\t\t\t\tServerContextTakeover: true,\n\t\t\t\tClientContextTakeover: true,\n\t\t\t}, // Enable compression from config\n\t\t\t// 从配置开启压缩\n\t\t\tParallelGolimit:    cfg.App.WebSocketParallelGolimit,\n\t\t\tReadMaxPayloadSize: int(util.ParseSize(cfg.App.WebSocketReadMaxPayloadSize, 1024*1024*64)), // Load from config, default 64MB\n\t\t\t// 从配置读取，默认 64MB\n\t\t\tWriteMaxPayloadSize: int(util.ParseSize(cfg.App.WebSocketWriteMaxPayloadSize, 1024*1024*64)), // Load from config, default 64MB\n\t\t\t// 从配置读取，默认 64MB\n\t\t},\n\t}, appContainer)\n\tappContainer.SetWSS(wss)\n\n\t// Create WebSocket Handlers (injected App Container)\n\t// 创建 WebSocket Handlers（注入 App Container）\n\tnoteWSHandler := websocket_router.NewNoteWSHandler(appContainer)\n\tfolderWSHandler := websocket_router.NewFolderWSHandler(appContainer)\n\tfileWSHandler := websocket_router.NewFileWSHandler(appContainer)\n\tsettingWSHandler := websocket_router.NewSettingWSHandler(appContainer)\n\n\t// Note\n\twss.Use(dto.NoteReceiveModify, noteWSHandler.NoteModify)\n\twss.Use(dto.NoteReceiveDelete, noteWSHandler.NoteDelete)\n\twss.Use(dto.NoteReceiveRename, noteWSHandler.NoteRename)\n\twss.Use(dto.NoteReceiveRePush, noteWSHandler.NoteRePush)\n\twss.Use(dto.NoteReceiveCheck, noteWSHandler.NoteModifyCheck)\n\twss.Use(dto.NoteReceiveSync, noteWSHandler.NoteSync)\n\n\t// Folder\n\twss.Use(dto.FolderReceiveSync, folderWSHandler.FolderSync)\n\twss.Use(dto.FolderReceiveModify, folderWSHandler.FolderModify)\n\twss.Use(dto.FolderReceiveDelete, folderWSHandler.FolderDelete)\n\twss.Use(dto.FolderReceiveRename, folderWSHandler.FolderRename)\n\n\t// Setting\n\twss.Use(dto.SettingReceiveModify, settingWSHandler.SettingModify)\n\twss.Use(dto.SettingReceiveDelete, settingWSHandler.SettingDelete)\n\twss.Use(dto.SettingReceiveCheck, settingWSHandler.SettingModifyCheck)\n\twss.Use(dto.SettingReceiveSync, settingWSHandler.SettingSync)\n\twss.Use(dto.SettingReceiveClear, settingWSHandler.SettingClear)\n\n\t// Attachment\n\twss.Use(dto.FileReceiveSync, fileWSHandler.FileSync)\n\twss.Use(dto.FileReceiveUploadCheck, fileWSHandler.FileUploadCheck)\n\twss.Use(dto.FileReceiveRename, fileWSHandler.FileRename)\n\twss.Use(dto.FileReceiveDelete, fileWSHandler.FileDelete)\n\twss.Use(dto.FileReceiveChunkDownload, fileWSHandler.FileChunkDownload)\n\twss.Use(dto.FileReceiveRePush, fileWSHandler.FileRePush)\n\n\t// Attachment chunk upload\n\twss.UseBinary(dto.VaultFileMsgType, fileWSHandler.FileUploadChunkBinary)\n\n\twss.UseUserVerify(noteWSHandler.UserInfo)\n\n\tfrontendAssets, _ := fs.Sub(frontendFiles, \"frontend/assets\")\n\tfrontendStatic, _ := fs.Sub(frontendFiles, \"frontend/static\")\n\tfrontendIndexContent, _ := frontendFiles.ReadFile(\"frontend/index.html\")\n\tfrontendShareContent, _ := frontendFiles.ReadFile(\"frontend/share.html\")\n\n\tr := gin.New()\n\tr.Use(middleware.Proxy())\n\tr.Use(middleware.Cors())\n\n\tr.GET(\"/\", func(c *gin.Context) {\n\t\tc.Redirect(http.StatusFound, \"/webgui\")\n\t})\n\tr.GET(\"/webgui/\", func(c *gin.Context) {\n\t\tc.Data(http.StatusOK, \"text/html; charset=utf-8\", frontendIndexContent)\n\t})\n\n\tr.GET(\"/share/:side/:token\", func(c *gin.Context) {\n\t\tc.Data(http.StatusOK, \"text/html; charset=utf-8\", frontendShareContent)\n\t})\n\n\tuserStaticPath := \"storage/user_static\"\n\tif _, err := os.Stat(userStaticPath); os.IsNotExist(err) {\n\t\t_ = os.MkdirAll(userStaticPath, os.ModePerm)\n\t}\n\n\tcacheMiddleware := func(c *gin.Context) {\n\t\t// Set strong cache, cache for one year\n\t\t// 设置强缓存，缓存一年\n\t\tc.Header(\"Cache-Control\", \"public, s-maxage=31536000, max-age=31536000, must-revalidate\")\n\t\tc.Next()\n\t}\n\n\tr.Group(\"/assets\", cacheMiddleware, middleware.StaticCompressMiddleware(frontendFiles)).StaticFS(\"/\", http.FS(frontendAssets))\n\tr.Group(\"/static\", cacheMiddleware, middleware.StaticCompressMiddleware(frontendFiles)).StaticFS(\"/\", http.FS(frontendStatic))\n\tr.Group(\"/user_static\", cacheMiddleware).Static(\"/\", userStaticPath)\n\n\tapi := r.Group(\"/api\")\n\t{\n\t\tapi.Use(middleware.AppInfoWithConfig(app.Name, appContainer.Version().Version))\n\t\tapi.Use(gin.Logger())\n\t\tapi.Use(middleware.TraceMiddlewareWithConfig(cfg.Tracer.Enabled, cfg.Tracer.Header)) // Trace ID middleware\n\t\t// Trace ID 中间件\n\t\tapi.Use(middleware.RateLimiter(methodLimiters))\n\n\t\t// MCP routes (No Timeout)\n\t\t// MCP 路由 (无超时限制)\n\t\tmcpHandler := mcp_router.NewMCPHandler(appContainer, wss)\n\t\tmcpGroup := api.Group(\"/mcp\")\n\t\tmcpGroup.Use(middleware.UserAuthTokenWithConfig(cfg.Security.AuthTokenKey))\n\t\t{\n\t\t\tmcpGroup.Match([]string{http.MethodGet, http.MethodHead}, \"/sse\", mcpHandler.HandleSSE)\n\t\t\tmcpGroup.POST(\"/message\", mcpHandler.HandleMessage)\n\t\t}\n\n\t\tapi.Use(middleware.ContextTimeout(time.Duration(cfg.App.DefaultContextTimeout) * time.Second))\n\t\tapi.Use(middleware.LangWithTranslator(uni))\n\t\tapi.Use(middleware.AccessLogWithLogger(appContainer.Logger()))\n\t\tapi.Use(middleware.RecoveryWithLogger(appContainer.Logger()))\n\n\t\t// Create Handlers (injected App Container)\n\t\t// 创建 Handlers（注入 App Container）\n\t\tuserHandler := api_router.NewUserHandler(appContainer)\n\t\tvaultHandler := api_router.NewVaultHandler(appContainer)\n\t\tnoteHandler := api_router.NewNoteHandler(appContainer, wss)\n\t\tfolderHandler := api_router.NewFolderHandler(appContainer)\n\t\tfileHandler := api_router.NewFileHandler(appContainer, wss)\n\t\tnoteHistoryHandler := api_router.NewNoteHistoryHandler(appContainer, wss)\n\t\tversionHandler := api_router.NewVersionHandler(appContainer)\n\t\tadminControlHandler := api_router.NewAdminControlHandler(appContainer, wss)\n\t\tshareHandler := api_router.NewShareHandler(appContainer, wss)\n\t\tstorageHandler := api_router.NewStorageHandler(appContainer)\n\t\tbackupHandler := api_router.NewBackupHandler(appContainer)\n\t\tgitSyncHandler := api_router.NewGitSyncHandler(appContainer)\n\t\tsettingHandler := api_router.NewSettingHandler(appContainer, wss)\n\t\tsyncLogHandler := api_router.NewSyncLogHandler(appContainer)\n\n\t\tapi.POST(\"/user/register\", userHandler.Register)\n\t\tapi.POST(\"/user/login\", userHandler.Login)\n\t\tapi.GET(\"/user/sync\", wss.Run())\n\n\t\t// Add server version interface (no auth required)\n\t\t// 添加服务端版本号接口（无需认证）\n\t\tapi.GET(\"/version\", versionHandler.ServerVersion)\n\t\tapi.GET(\"/support\", versionHandler.Support)\n\t\tapi.GET(\"/webgui/config\", adminControlHandler.Config)\n\n\t\t// Health check interface (no auth required)\n\t\t// 健康检查接口（无需认证）\n\t\thealthHandler := api_router.NewHealthHandler(appContainer)\n\t\tapi.GET(\"/health\", healthHandler.Check)\n\n\t\t// Share routing group (controlled read-only access)\n\t\t// 分享路由组 (受控的只读访问)\n\t\tshare := api.Group(\"/share\")\n\t\tshare.Use(middleware.ShareAuthToken(appContainer.ShareService))\n\t\t{\n\t\t\tshare.GET(\"/note\", shareHandler.NoteGet) // Get shared note\n\t\t\t// 获取分享的笔记\n\t\t\tshare.GET(\"/file\", shareHandler.FileGet) // Get shared file content\n\t\t\t// 获取分享的文件内容\n\t\t}\n\n\t\t// Auth routing group (authentication required)\n\t\t// 需要认证的路由组\n\t\tauth := api.Group(\"/\")\n\t\tauth.Use(middleware.UserAuthTokenWithConfig(cfg.Security.AuthTokenKey))\n\t\t{\n\t\t\t// Create share\n\t\t\t// 创建分享\n\t\t\tauth.POST(\"/share\", shareHandler.Create)\n\t\t\tauth.POST(\"/share/password\", shareHandler.UpdatePassword)\n\t\t\tauth.GET(\"/share\", shareHandler.Query)\n\t\t\tauth.DELETE(\"/share\", shareHandler.Cancel)\n\t\t\tauth.POST(\"/share/short_link\", shareHandler.CreateShortLink)\n\t\t\tauth.GET(\"/shares\", shareHandler.List)\n\n\t\t\t// Admin config interface\n\t\t\t// 管理员配置接口\n\t\t\tauth.GET(\"/admin/check\", adminControlHandler.CheckAdmin)\n\t\t\tauth.GET(\"/admin/config\", adminControlHandler.GetConfig)\n\t\t\tauth.POST(\"/admin/config\", adminControlHandler.UpdateConfig)\n\t\t\tauth.GET(\"/admin/config/user_database\", adminControlHandler.GetUserDatabaseConfig)\n\t\t\tauth.POST(\"/admin/config/user_database\", adminControlHandler.UpdateUserDatabaseConfig)\n\t\t\tauth.POST(\"/admin/config/user_database/test\", adminControlHandler.ValidateUserDatabaseConfig)\n\t\t\tauth.GET(\"/admin/config/ngrok\", adminControlHandler.GetNgrokConfig)\n\t\t\tauth.POST(\"/admin/config/ngrok\", adminControlHandler.UpdateNgrokConfig)\n\t\t\tauth.GET(\"/admin/config/cloudflare\", adminControlHandler.GetCloudflareConfig)\n\t\t\tauth.POST(\"/admin/config/cloudflare\", adminControlHandler.UpdateCloudflareConfig)\n\t\t\tauth.GET(\"/admin/systeminfo\", adminControlHandler.GetSystemInfo)\n\t\t\tauth.GET(\"/admin/upgrade\", adminControlHandler.Upgrade)\n\t\t\tauth.GET(\"/admin/restart\", adminControlHandler.Restart)\n\t\t\tauth.GET(\"/admin/gc\", adminControlHandler.GC)\n\t\t\tauth.GET(\"/admin/ws_clients\", adminControlHandler.GetWSClients)\n\t\t\tauth.GET(\"/admin/cloudflared_tunnel_download\", adminControlHandler.CloudflaredTunnelDownload)\n\n\t\t\tauth.POST(\"/user/change_password\", userHandler.UserChangePassword)\n\t\t\tauth.GET(\"/user/info\", userHandler.UserInfo)\n\t\t\tauth.GET(\"/vault\", vaultHandler.List)\n\t\t\tauth.POST(\"/vault\", vaultHandler.CreateOrUpdate)\n\t\t\tauth.DELETE(\"/vault\", vaultHandler.Delete)\n\n\t\t\tauth.GET(\"/note\", noteHandler.Get)\n\t\t\tauth.POST(\"/note\", noteHandler.CreateOrUpdate)\n\t\t\tauth.DELETE(\"/note\", noteHandler.Delete)\n\t\t\tauth.PUT(\"/note/restore\", noteHandler.Restore)\n\t\t\tauth.POST(\"/note/rename\", noteHandler.Rename)\n\t\t\tauth.GET(\"/notes\", noteHandler.List)\n\t\t\tauth.DELETE(\"/note/recycle-clear\", noteHandler.RecycleClear)\n\t\t\tauth.GET(\"/notes/share-paths\", shareHandler.NoteSharePaths)\n\n\t\t\tauth.GET(\"/folder\", folderHandler.Get)\n\t\t\tauth.POST(\"/folder\", folderHandler.Create)\n\t\t\tauth.DELETE(\"/folder\", folderHandler.Delete)\n\t\t\tauth.GET(\"/folders\", folderHandler.List)\n\t\t\tauth.GET(\"/folder/notes\", folderHandler.ListNotes)\n\t\t\tauth.GET(\"/folder/files\", folderHandler.ListFiles)\n\t\t\tauth.GET(\"/folder/tree\", folderHandler.Tree)\n\n\t\t\t// Note edit operations\n\t\t\tauth.PATCH(\"/note/frontmatter\", noteHandler.PatchFrontmatter)\n\t\t\tauth.POST(\"/note/append\", noteHandler.Append)\n\t\t\tauth.POST(\"/note/prepend\", noteHandler.Prepend)\n\t\t\tauth.POST(\"/note/replace\", noteHandler.Replace)\n\n\t\t\t// Note link operations\n\t\t\tauth.GET(\"/note/backlinks\", noteHandler.GetBacklinks)\n\t\t\tauth.GET(\"/note/outlinks\", noteHandler.GetOutlinks)\n\n\t\t\tauth.GET(\"/file\", fileHandler.GetInfo)\n\t\t\tauth.OPTIONS(\"/file\", func(c *gin.Context) { c.Status(http.StatusNoContent) })\n\t\t\tauth.GET(\"/file/info\", fileHandler.Get)\n\t\t\tauth.OPTIONS(\"/file/info\", func(c *gin.Context) { c.Status(http.StatusNoContent) })\n\t\t\tauth.DELETE(\"/file\", fileHandler.Delete)\n\t\t\tauth.PUT(\"/file/restore\", fileHandler.Restore)\n\t\t\tauth.POST(\"/file/rename\", fileHandler.Rename)\n\t\t\tauth.GET(\"/files\", fileHandler.List)\n\t\t\tauth.DELETE(\"/file/recycle-clear\", fileHandler.RecycleClear)\n\t\t\tauth.OPTIONS(\"/files\", func(c *gin.Context) { c.Status(http.StatusNoContent) })\n\n\t\t\tauth.GET(\"/note/history\", noteHistoryHandler.Get)\n\t\t\tauth.GET(\"/note/histories\", noteHistoryHandler.List)\n\t\t\tauth.PUT(\"/note/history/restore\", noteHistoryHandler.Restore)\n\n\t\t\tauth.GET(\"/storage\", storageHandler.List)\n\t\t\tauth.POST(\"/storage\", storageHandler.CreateOrUpdate)\n\t\t\tauth.GET(\"/storage/enabled_types\", storageHandler.EnabledTypes)\n\t\t\tauth.POST(\"/storage/validate\", storageHandler.Validate)\n\t\t\tauth.DELETE(\"/storage\", storageHandler.Delete)\n\n\t\t\tauth.GET(\"/backup/configs\", backupHandler.GetConfigs)\n\t\t\tauth.POST(\"/backup/config\", backupHandler.UpdateConfig)\n\t\t\tauth.DELETE(\"/backup/config\", backupHandler.DeleteConfig)\n\t\t\tauth.GET(\"/backup/historys\", backupHandler.ListHistory)\n\t\t\tauth.POST(\"/backup/execute\", backupHandler.Execute)\n\n\t\t\tauth.GET(\"/git-sync/configs\", gitSyncHandler.GetConfigs)\n\t\t\tauth.POST(\"/git-sync/config\", gitSyncHandler.UpdateConfig)\n\t\t\tauth.DELETE(\"/git-sync/config\", gitSyncHandler.DeleteConfig)\n\t\t\tauth.POST(\"/git-sync/validate\", gitSyncHandler.Validate)\n\t\t\tauth.DELETE(\"/git-sync/config/clean\", gitSyncHandler.CleanWorkspace)\n\t\t\tauth.POST(\"/git-sync/config/execute\", gitSyncHandler.Execute)\n\t\t\tauth.GET(\"/git-sync/histories\", gitSyncHandler.GetHistories)\n\n\t\t\tauth.GET(\"/setting\", settingHandler.Get)\n\t\t\tauth.POST(\"/setting\", settingHandler.CreateOrUpdate)\n\t\t\tauth.DELETE(\"/setting\", settingHandler.Delete)\n\t\t\tauth.POST(\"/setting/rename\", settingHandler.Rename)\n\t\t\tauth.GET(\"/settings\", settingHandler.List)\n\n\t\t\t// Sync log routes\n\t\t\t// 同步日志路由\n\t\t\tauth.GET(\"/sync-logs\", syncLogHandler.List)\n\n\t\t}\n\n\t}\n\n\t// Swagger UI (outside auth group to ensure public access)\n\t// Swagger UI (放在 auth 组外，确保可以公开访问)\n\tr.GET(\"/docs/*any\", func(c *gin.Context) {\n\t\tp := c.Param(\"any\")\n\t\tif p == \"\" || p == \"/\" {\n\t\t\tc.Redirect(http.StatusMovedPermanently, \"/docs/index.html\")\n\t\t\treturn\n\t\t}\n\t\tginSwagger.WrapHandler(swaggerFiles.Handler)(c)\n\t})\n\n\t// Read debug page from embedded FS\n\tdebugPageContent, _ := frontendFiles.ReadFile(\"docs/test_ws_debug.html\")\n\tr.GET(\"/ws_debug\", func(c *gin.Context) {\n\t\tc.Data(http.StatusOK, \"text/html; charset=utf-8\", debugPageContent)\n\t})\n\n\t// Read swagger files from embedded FS\n\tswaggerJSON, _ := frontendFiles.ReadFile(\"docs/swagger.yaml\")\n\tr.GET(\"/openapi/\", func(c *gin.Context) {\n\t\tc.Redirect(http.StatusMovedPermanently, \"/openapi.json\")\n\t})\n\tr.GET(\"/openapi.json\", func(c *gin.Context) {\n\t\tc.Data(http.StatusOK, \"application/json; charset=utf-8\", swaggerJSON)\n\t})\n\tswaggerYAML, _ := frontendFiles.ReadFile(\"docs/swagger.yaml\")\n\tr.GET(\"/openapi.yaml\", func(c *gin.Context) {\n\t\tc.Data(http.StatusOK, \"application/x-yaml; charset=utf-8\", swaggerYAML)\n\t})\n\n\tif cfg.Storage.LocalFS.HttpfsIsEnable && cfg.Storage.LocalFS.IsEnabled {\n\t\tr.StaticFS(cfg.Storage.LocalFS.SavePath, http.Dir(cfg.Storage.LocalFS.SavePath))\n\t\tr.OPTIONS(cfg.Storage.LocalFS.SavePath+\"/*filepath\", func(c *gin.Context) {\n\t\t\tc.Status(http.StatusNoContent)\n\t\t})\n\t}\n\n\tr.NoRoute(middleware.NoFound())\n\n\treturn r\n}\n"
  },
  {
    "path": "internal/routers/websocket_router/handler.go",
    "content": "// Package websocket_router provides WebSocket router handlers\n// Package websocket_router 提供 WebSocket 路由处理器\npackage websocket_router\n\nimport (\n\t\"context\"\n\t\"strings\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"go.uber.org/zap\"\n)\n\n// WSHandler WebSocket base Handler struct, encapsulates App Container\n// All WebSocket Handlers should embed this struct to gain dependency injection capability\n// WSHandler WebSocket 基础 Handler 结构体，封装 App Container\n// 所有 WebSocket Handler 都应该嵌入此结构体以获得依赖注入能力\ntype WSHandler struct {\n\tApp *app.App\n}\n\n// NewWSHandler creates WebSocket base Handler instance\n// NewWSHandler 创建 WebSocket 基础 Handler 实例\nfunc NewWSHandler(a *app.App) *WSHandler {\n\treturn &WSHandler{App: a}\n}\n\n// logError records error log, including Trace ID\n// Directly use WebsocketClient.TraceID field, avoiding fetching from potentially invalid HTTP context\n// logError 记录错误日志，包含 Trace ID\n// 直接使用 WebsocketClient.TraceID 字段，避免从可能失效的 HTTP context 获取\nfunc (h *WSHandler) logError(c *pkgapp.WebsocketClient, method string, err error) {\n\ttraceID := \"\"\n\tif c != nil {\n\t\ttraceID = c.TraceID\n\t}\n\n\t// If connection closed error and context canceled, downgrade log level\n\t// 如果是连接关闭导致的错误且 context 已取消，降级日志级别\n\tif isNetworkClosedError(err) && c != nil && c.Context().Err() != nil {\n\t\th.logDebug(c, method, zap.Error(err))\n\t\treturn\n\t}\n\n\th.App.Logger().Error(method,\n\t\tzap.Error(err),\n\t\tzap.String(\"traceId\", traceID),\n\t)\n}\n\n// logDebug records debug log, including Trace ID\n// logDebug 记录调试日志，包含 Trace ID\nfunc (h *WSHandler) logDebug(c *pkgapp.WebsocketClient, method string, fields ...zap.Field) {\n\ttraceID := \"\"\n\tif c != nil {\n\t\ttraceID = c.TraceID\n\t}\n\tallFields := append([]zap.Field{zap.String(\"traceId\", traceID)}, fields...)\n\th.App.Logger().Debug(method, allFields...)\n}\n\n// logInfo records info log, including Trace ID\n// Directly use WebsocketClient.TraceID field, avoiding fetching from potentially invalid HTTP context\n// logInfo 记录信息日志，包含 Trace ID\n// 直接使用 WebsocketClient.TraceID 字段，避免从可能失效的 HTTP context 获取\nfunc (h *WSHandler) logInfo(c *pkgapp.WebsocketClient, method string, fields ...zap.Field) {\n\ttraceID := \"\"\n\tif c != nil {\n\t\ttraceID = c.TraceID\n\t}\n\tallFields := append([]zap.Field{zap.String(\"traceId\", traceID)}, fields...)\n\th.App.Logger().Info(method, allFields...)\n}\n\n// logWarn records warning log, including Trace ID\n// Directly use WebsocketClient.TraceID field, avoiding fetching from potentially invalid HTTP context\n// logWarn 记录警告日志，包含 Trace ID\n// 直接使用 WebsocketClient.TraceID 字段，避免从可能失效的 HTTP context 获取\nfunc (h *WSHandler) logWarn(c *pkgapp.WebsocketClient, method string, fields ...zap.Field) {\n\ttraceID := \"\"\n\tif c != nil {\n\t\ttraceID = c.TraceID\n\t}\n\tallFields := append([]zap.Field{zap.String(\"traceId\", traceID)}, fields...)\n\th.App.Logger().Warn(method, allFields...)\n}\n\n// respondError unified error response method\n// Records error log and sends error response with Details to client\n// respondError 统一错误响应方法\n// 记录错误日志并发送包含 Details 的错误响应给客户端\nfunc (h *WSHandler) respondError(c *pkgapp.WebsocketClient, codeErr *code.Code, err error, method string) {\n\th.logError(c, method, err)\n\t// If err is already a *code.Code, it might have more details than codeErr\n\tif cErr, ok := err.(*code.Code); ok {\n\t\tc.ToResponse(cErr)\n\t\treturn\n\t}\n\tc.ToResponse(codeErr.WithDetails(err.Error()))\n}\n\n// respondErrorWithData unified error response method with data\n// Records error log and sends error response with Details and Data to client\n// respondErrorWithData 带数据的统一错误响应方法\n// 记录错误日志并发送包含 Details 和 Data 的错误响应给客户端\nfunc (h *WSHandler) respondErrorWithData(c *pkgapp.WebsocketClient, codeErr *code.Code, err error, data interface{}, method string) {\n\th.logError(c, method, err)\n\tc.ToResponse(codeErr.WithDetails(err.Error()).WithData(data))\n}\n\n// GetTraceID retrieves Trace ID from WebSocket client\n// Directly use WebsocketClient.TraceID field, avoiding fetching from potentially invalid HTTP context\n// GetTraceID 从 WebSocket 客户端获取 Trace ID\n// 直接使用 WebsocketClient.TraceID 字段，避免从可能失效的 HTTP context 获取\nfunc GetTraceID(c *pkgapp.WebsocketClient) string {\n\tif c == nil {\n\t\treturn \"\"\n\t}\n\treturn c.TraceID\n}\n\n// LogErrorWithLogger records error log, including Trace ID (uses injected logger)\n// LogErrorWithLogger 记录错误日志，包含 Trace ID（使用注入的 logger）\nfunc LogErrorWithLogger(logger *zap.Logger, c *pkgapp.WebsocketClient, method string, err error) {\n\ttraceID := GetTraceID(c)\n\n\t// If connection closed error and context canceled, downgrade log level\n\t// 如果是连接关闭导致的错误且 context 已取消，降级日志级别\n\tif isNetworkClosedError(err) && c != nil && c.Context().Err() != nil {\n\t\tallFields := []zap.Field{zap.String(\"traceId\", traceID), zap.Error(err)}\n\t\tlogger.Debug(method, allFields...)\n\t\treturn\n\t}\n\n\tlogger.Error(method,\n\t\tzap.Error(err),\n\t\tzap.String(\"traceId\", traceID),\n\t)\n}\n\n// LogInfoWithLogger records info log, including Trace ID (uses injected logger)\n// LogInfoWithLogger 记录信息日志，包含 Trace ID（使用注入的 logger）\nfunc LogInfoWithLogger(logger *zap.Logger, c *pkgapp.WebsocketClient, method string, fields ...zap.Field) {\n\ttraceID := GetTraceID(c)\n\tallFields := append([]zap.Field{zap.String(\"traceId\", traceID)}, fields...)\n\tlogger.Info(method, allFields...)\n}\n\n// LogWarnWithLogger records warning log, including Trace ID (uses injected logger)\n// LogWarnWithLogger 记录警告日志，包含 Trace ID（使用注入的 logger）\nfunc LogWarnWithLogger(logger *zap.Logger, c *pkgapp.WebsocketClient, method string, fields ...zap.Field) {\n\ttraceID := GetTraceID(c)\n\tallFields := append([]zap.Field{zap.String(\"traceId\", traceID)}, fields...)\n\tlogger.Warn(method, allFields...)\n}\n\n// isNetworkClosedError checks if the error is related to network closure\n// isNetworkClosedError 检查是否为网络关闭相关的错误\nfunc isNetworkClosedError(err error) bool {\n\tif err == nil {\n\t\treturn false\n\t}\n\tmsg := err.Error()\n\treturn strings.Contains(msg, \"use of closed network connection\") ||\n\t\tstrings.Contains(msg, \"connection reset by peer\") ||\n\t\tstrings.Contains(msg, \"broken pipe\") ||\n\t\terr == context.Canceled\n}\n"
  },
  {
    "path": "internal/routers/websocket_router/ws_file.go",
    "content": "package websocket_router\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/logger\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\n\t\"go.uber.org/zap\"\n)\n\n// FileWSHandler WebSocket file handler\n// FileWSHandler WebSocket 文件处理器\n// Uses App Container to inject dependencies\n// 使用 App Container 注入依赖\ntype FileWSHandler struct {\n\t*WSHandler\n}\n\n// NewFileWSHandler creates FileWSHandler instance\n// NewFileWSHandler 创建 FileWSHandler 实例\nfunc NewFileWSHandler(a *app.App) *FileWSHandler {\n\treturn &FileWSHandler{\n\t\tWSHandler: NewWSHandler(a),\n\t}\n}\n\ntype FileUploadBinaryChunkSession struct {\n\tID             string              // Session ID // 会话 ID\n\tVault          string              // Vault Name // 仓库名称\n\tPath           string              // File Path // 文件路径\n\tPathHash       string              // File Path Hash // 文件路径哈希值\n\tContentHash    string              // File Content Hash // 文件内容哈希值\n\tCtime          int64               // Creation time // 创建时间\n\tMtime          int64               // Modification time // 修改时间\n\tSize           int64               // File size // 文件大小\n\tTotalChunks    int64               // Total chunks // 总分块数\n\tUploadedChunks int64               // Uploaded chunks // 已上传分块数\n\tUploadedBytes  int64               // Uploaded bytes // 已上传字节数\n\tChunkSize      int64               // Chunk size // 分块大小\n\tSavePath       string              // Temp save path // 临时保存路径\n\tFileHandle     *os.File            // File handle // 文件句柄\n\tmu             sync.Mutex          // Mutex to protect concurrent operations // 互斥锁，保护并发操作\n\tCreatedAt      time.Time           // Created time // 创建时间\n\tCancelFunc     context.CancelFunc  // Cancel function for timeout control // 取消函数，用于超时控制\n\tuploadedChunks map[uint32]struct{} // Record of uploaded chunk indices for idempotency // 已上传分块索引记录，用于幂等\n\tisCompleted    bool                // Whether upload is completely finished // 上传是否已彻底完成\n}\n\nfunc (s *FileUploadBinaryChunkSession) Cleanup() {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\n\t// Cancel timeout timer\n\t// 取消超时定时器\n\tif s.CancelFunc != nil {\n\t\ts.CancelFunc()\n\t\ts.CancelFunc = nil\n\t}\n\n\tif s.FileHandle != nil {\n\t\tif err := s.FileHandle.Close(); err != nil {\n\t\t\tzap.L().Warn(\"cleanup: failed to close file handle\",\n\t\t\t\tzap.String(logger.FieldSessionID, s.ID),\n\t\t\t\tzap.String(logger.FieldPath, s.Path),\n\t\t\t\tzap.String(logger.FieldMethod, \"FileUploadBinaryChunkSession.Cleanup\"),\n\t\t\t\tzap.Error(err),\n\t\t\t)\n\t\t}\n\t\ts.FileHandle = nil\n\t}\n\t// Check if SavePath exists before attempting to remove it\n\tif s.SavePath != \"\" {\n\t\tif _, err := os.Stat(s.SavePath); err == nil {\n\t\t\tif err := os.Remove(s.SavePath); err != nil {\n\t\t\t\tzap.L().Warn(\"cleanup: failed to remove temp file\",\n\t\t\t\t\tzap.String(logger.FieldSessionID, s.ID),\n\t\t\t\t\tzap.String(logger.FieldPath, s.SavePath),\n\t\t\t\t\tzap.String(logger.FieldMethod, \"FileUploadBinaryChunkSession.Cleanup\"),\n\t\t\t\t\tzap.Error(err),\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\ts.SavePath = \"\"\n\t}\n}\n\n// FileDownloadChunkSession defines the session state for file chunk download\n// Used to track progress and file info for large file chunk downloads\n// FileDownloadChunkSession 定义文件分块下载的会话状态。\n// 用于跟踪大文件分块下载的进度和文件信息。\ntype FileDownloadChunkSession struct {\n\tSessionID   string // Session ID // 会话 ID\n\tPath        string // File path (for logging) // 文件路径(用于日志)\n\tSize        int64  // File size // 文件大小\n\tTotalChunks int64  // Total chunks // 总分块数\n\tChunkSize   int64  // Chunk size // 分块大小\n\tSavePath    string // File actual save path // 文件实际保存路径\n}\n\n// FileUploadCheck checks file upload request, initializes upload session or confirms no upload needed\n// FileUploadCheck 检查文件上传请求，初始化上传会话或确认无需上传。\nfunc (h *FileWSHandler) FileUploadCheck(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.FileUpdateCheckRequest{}\n\n\t// Bind and validate parameters\n\t// 绑定并验证参数\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.file.FileUploadCheck.BindAndValid\")\n\t\treturn\n\t}\n\n\t// Required parameter validation\n\t// 必填参数校验\n\tif params.PathHash == \"\" {\n\t\tc.ToResponse(code.ErrorInvalidParams.WithDetails(\"pathHash is required\"))\n\t\treturn\n\t}\n\tif params.ContentHash == \"\" {\n\t\tc.ToResponse(code.ErrorInvalidParams.WithDetails(\"contentHash is required\"))\n\t\treturn\n\t}\n\tif params.Mtime == 0 {\n\t\tc.ToResponse(code.ErrorInvalidParams.WithDetails(\"mtime is required\"))\n\t\treturn\n\t}\n\tif params.Ctime == 0 {\n\t\tc.ToResponse(code.ErrorInvalidParams.WithDetails(\"ctime is required\"))\n\t\treturn\n\t}\n\n\tctx := c.Context()\n\n\tpkgapp.NoteModifyLog(c.TraceID, c.User.UID, \"FileUploadCheck\", params.Path, params.Vault)\n\n\t// Check and create vault, internally uses SF to merge concurrent requests, avoiding duplicate creation issues\n\t// 检查并创建仓库，内部使用SF合并并发请求, 避免重复创建问题\n\th.App.VaultService.GetOrCreate(ctx, c.User.UID, params.Vault)\n\n\t// 检查文件更新状态\n\tfileService := h.App.GetFileService(c.ClientType, c.ClientName, c.ClientVersion)\n\tupdateMode, fileSvc, err := fileService.UploadCheck(ctx, c.User.UID, params)\n\n\tif err != nil {\n\t\th.respondError(c, code.ErrorFileUploadCheckFailed, err, \"websocket_router.file.FileUploadCheck.UploadCheck\")\n\t\treturn\n\t}\n\n\t// UpdateContent 或 Create 模式，需要客户端上传文件\n\tswitch updateMode {\n\tcase \"UpdateContent\", \"Create\":\n\n\t\tsession, err := h.handleFileUploadSessionCreate(c, params.Vault, params.Path, params.PathHash, params.ContentHash, params.Size, params.Ctime, params.Mtime)\n\t\tif err != nil {\n\t\t\th.respondError(c, code.ErrorFileUploadCheckFailed, err, \"websocket_router.file.FileUploadCheck.handleFileUploadSessionCreate\")\n\t\t\treturn\n\t\t}\n\n\t\tc.ToResponse(code.Success.WithData(\n\t\t\tdto.FileSyncUploadMessage{\n\t\t\t\tPath:      session.Path,\n\t\t\t\tSessionID: session.ID,\n\t\t\t\tChunkSize: session.ChunkSize,\n\t\t\t},\n\t\t), dto.FileUpload)\n\t\treturn\n\n\tcase \"UpdateMtime\":\n\t\t// 当用户 mtime 小于服务端 mtime 时，通知用户更新mtime\n\t\tc.ToResponse(code.Success.WithData(\n\t\t\tdto.FileSyncMtimeMessage{\n\t\t\t\tPath:             fileSvc.Path,\n\t\t\t\tCtime:            fileSvc.Ctime,\n\t\t\t\tMtime:            fileSvc.Mtime,\n\t\t\t\tUpdatedTimestamp: fileSvc.UpdatedTimestamp,\n\t\t\t},\n\t\t), dto.FileSyncMtime)\n\t\treturn\n\tdefault:\n\t\t// 无需更新\n\t\tc.ToResponse(code.SuccessNoUpdate)\n\t}\n}\n\n// FileUploadChunkBinary handles binary data for file chunk upload.\n// FileUploadChunkBinary 处理文件分块上传的二进制数据。\nfunc (h *FileWSHandler) FileUploadChunkBinary(c *pkgapp.WebsocketClient, data []byte) {\n\t// Check if context is cancelled (connection might be closed)\n\t// 检查 context 是否已取消（连接可能已关闭）\n\tselect {\n\tcase <-c.Context().Done():\n\t\th.logInfo(c, \"FileUploadChunkBinary: context cancelled, skipping chunk processing\")\n\t\treturn\n\tdefault:\n\t}\n\n\tif len(data) < 40 {\n\t\th.logError(c, \"websocket_router.file.FileUploadChunkBinary\", fmt.Errorf(\"invalid data length: %d\", len(data)))\n\t\treturn\n\t}\n\n\t// Parse session ID and chunk index\n\t// 解析会话 ID 和分块索引\n\tsessionID := string(data[:36])\n\tchunkIndex := binary.BigEndian.Uint32(data[36:40])\n\tchunkData := data[40:]\n\n\t// Get session from global server (supports cross-connection)\n\t// 从全局服务器获取会话 (支持跨连接)\n\tbinarySession := c.Server.GetSession(c.User.ID, sessionID)\n\n\tif binarySession == nil {\n\t\th.logError(c, \"websocket_router.file.FileUploadChunkBinary\", fmt.Errorf(\"session not found: %s\", sessionID))\n\t\tc.ToResponse(code.ErrorFileUploadSessionNotFound.WithData(map[string]string{\n\t\t\t\"sessionID\": sessionID,\n\t\t}))\n\t\treturn\n\t}\n\n\tsession := binarySession.(*FileUploadBinaryChunkSession)\n\tif session == nil {\n\t\th.logError(c, \"websocket_router.file.FileUploadChunkBinary\", fmt.Errorf(\"session is nil: %s\", sessionID))\n\t\tc.ToResponse(code.ErrorFileUploadSessionNotFound.WithData(map[string]string{\n\t\t\t\"sessionID\": sessionID,\n\t\t}))\n\t\treturn\n\t}\n\n\tsession.mu.Lock()\n\t// 1. Check if completely finished (Idempotency for late chunks)\n\t// 1. 检查是否已彻底完成 (针对延迟到达分片的幂等性)\n\tif session.isCompleted {\n\t\tsession.mu.Unlock()\n\t\tc.ToResponse(code.Success)\n\t\treturn\n\t}\n\n\t// 2. Check if chunk index has been uploaded (Idempotency for duplicate chunks)\n\t// 2. 检查分块索引是否已上传 (针对重复分片的幂等性)\n\tif _, ok := session.uploadedChunks[chunkIndex]; ok {\n\t\tsession.mu.Unlock()\n\t\tc.ToResponse(code.Success)\n\t\treturn\n\t}\n\n\t// Calculate write offset and write data\n\t// 计算写入偏移量并写入数据\n\toffset := int64(chunkIndex) * int64(session.ChunkSize)\n\n\tif session.FileHandle == nil {\n\t\t// Lazy initialize file handle on first chunk\n\t\t// 在收到第一个分片时延迟初始化文件句柄\n\t\tf, err := os.Create(session.SavePath)\n\t\tif err != nil {\n\t\t\tsession.mu.Unlock()\n\t\t\th.handleFileUploadSessionCleanup(c, sessionID)\n\t\t\th.respondErrorWithData(c, code.ErrorFileUploadFailed, err, map[string]string{\"sessionID\": sessionID}, \"websocket_router.file.FileUploadChunkBinary.Create\")\n\t\t\treturn\n\t\t}\n\t\tsession.FileHandle = f\n\t}\n\tfileHandle := session.FileHandle\n\tsession.mu.Unlock()\n\n\tif _, err := fileHandle.WriteAt(chunkData, offset); err != nil {\n\t\t// Cleanup session on fatal error\n\t\t// 致命错误时清理会话\n\t\th.handleFileUploadSessionCleanup(c, sessionID)\n\t\th.respondErrorWithData(c, code.ErrorFileUploadFailed, err, map[string]string{\"sessionID\": sessionID}, \"websocket_router.file.FileUploadChunkBinary.WriteAt\")\n\t\treturn\n\t}\n\n\t// Update uploaded count and bytes\n\t// 更新已上传计数和字节数\n\tsession.mu.Lock()\n\tsession.uploadedChunks[chunkIndex] = struct{}{} // 记录已上传的分块索引\n\tsession.UploadedChunks++\n\tsession.UploadedBytes += int64(len(chunkData))\n\tuploadedBytes := session.UploadedBytes\n\tsession.mu.Unlock()\n\n\t// Check if all data has been uploaded (judged by bytes)\n\t// 检查是否所有数据都已上传(根据字节数判断)\n\tif uploadedBytes >= session.Size {\n\n\t\th.logInfo(c, \"FileUploadComplete: upload finished\",\n\t\t\tzap.String(\"sessionID\", sessionID),\n\t\t\tzap.String(\"path\", session.Path),\n\t\t\tzap.Int64(\"uploadedBytes\", uploadedBytes),\n\t\t\tzap.Int64(\"totalSize\", session.Size),\n\t\t\tzap.Int64(\"uploadedChunks\", session.UploadedChunks),\n\t\t\tzap.Int64(\"totalChunks\", session.TotalChunks))\n\n\t\t// NOTE: Session removal is delayed until UploadComplete is successful\n\t\t// 注意：Session 的移除延迟到 UploadComplete 成功之后\n\n\t\t// Cancel timeout timer\n\t\t// 取消超时定时器\n\t\tif session.CancelFunc != nil {\n\t\t\tsession.CancelFunc()\n\t\t}\n\n\t\t// Close temp file handle\n\t\t// 关闭临时文件句柄\n\t\tif err := session.FileHandle.Close(); err != nil {\n\t\t\th.respondErrorWithData(c, code.ErrorFileUploadFailed, err, map[string]string{\"sessionID\": sessionID}, \"websocket_router.file.FileUploadChunkBinary.Close\")\n\t\t\treturn\n\t\t}\n\t\tsession.FileHandle = nil // Avoid double closing during cleanup // 避免再次清理时重复关闭\n\n\t\tctx := c.Context()\n\n\t\t// Check and create vault, internally uses SF to merge concurrent requests, avoiding duplicate creation issues\n\t\t// 检查并创建仓库，内部使用SF合并并发请求, 避免重复创建问题\n\t\th.App.VaultService.GetOrCreate(ctx, c.User.UID, session.Vault)\n\n\t\tsvcParams := &dto.FileUpdateRequest{\n\t\t\tVault:       session.Vault,\n\t\t\tPath:        session.Path,\n\t\t\tPathHash:    session.PathHash,\n\t\t\tContentHash: session.ContentHash,\n\t\t\tSavePath:    session.SavePath, // 将临时文件的完整路径作为 SavePath 传入\n\t\t\tSize:        session.Size,\n\t\t\tCtime:       session.Ctime,\n\t\t\tMtime:       session.Mtime,\n\t\t}\n\n\t\t// Update or create file record (DAO layer will automatically move temp file from SavePath to f_{id} folder)\n\t\t// 更新或创建 file 记录 (DAO 层会自动将 SavePath 里的临时文件移动到 f_{id} 文件夹)\n\t\tfileService := h.App.GetFileService(c.ClientType, c.ClientName, c.ClientVersion)\n\t\t_, fileSvc, err := fileService.UploadComplete(ctx, c.User.UID, svcParams)\n\n\t\tif err != nil {\n\t\t\th.respondError(c, code.ErrorFileModifyOrCreateFailed, err, \"websocket_router.file.FileUploadChunkBinary.UploadComplete\")\n\t\t\treturn\n\t\t}\n\n\t\tif fileSvc == nil {\n\t\t\th.logInfo(c, \"FileUploadChunkBinary: fileSvc is nil, skipping broadcast\", zap.String(\"path\", session.Path))\n\t\t\tc.ToResponse(code.Success)\n\t\t\treturn\n\t\t}\n\n\t\t// Reply to sender with ack carrying server timestamp, so client can update lastFileSyncTime\n\t\t// 回复发送方携带服务端时间戳的 ack，让客户端可以更新 lastFileSyncTime\n\t\tc.ToResponse(code.Success.WithData(dto.FileUploadAckMessage{\n\t\t\tLastTime: fileSvc.UpdatedTimestamp,\n\t\t\tPath:     session.Path,\n\t\t\tPathHash: session.PathHash,\n\t\t}).WithVault(session.Vault), string(dto.FileUploadAck))\n\n\t\t// Mark as completed and remove from global server\n\t\t// 标记为已完成并从全局服务器移除\n\t\tsession.mu.Lock()\n\t\tsession.isCompleted = true\n\t\tsession.mu.Unlock()\n\t\tc.Server.RemoveSession(c.User.ID, session.ID)\n\n\t\t// Broadcast file update message\n\t\t// 广播文件更新消息\n\t\tc.BroadcastResponse(code.Success.WithData(\n\t\t\tdto.FileSyncModifyMessage{\n\t\t\t\tPath:             fileSvc.Path,\n\t\t\t\tPathHash:         fileSvc.PathHash,\n\t\t\t\tContentHash:      fileSvc.ContentHash,\n\t\t\t\tSize:             fileSvc.Size,\n\t\t\t\tCtime:            fileSvc.Ctime,\n\t\t\t\tMtime:            fileSvc.Mtime,\n\t\t\t\tUpdatedTimestamp: fileSvc.UpdatedTimestamp,\n\t\t\t},\n\t\t).WithVault(session.Vault), true, dto.FileSyncUpdate)\n\t}\n}\n\n// FileDelete handles file deletion request.\n// FileDelete 处理文件删除请求。\nfunc (h *FileWSHandler) FileDelete(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.FileDeleteRequest{}\n\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.file.FileDelete.BindAndValid\")\n\t\treturn\n\t}\n\n\tctx := c.Context()\n\n\tpkgapp.NoteModifyLog(c.TraceID, c.User.UID, \"FileDelete\", params.Path, params.Vault)\n\n\t// 获取或创建仓库\n\th.App.VaultService.GetOrCreate(ctx, c.User.UID, params.Vault)\n\n\t// Execute deletion logic\n\t// 执行删除逻辑\n\tfileService := h.App.GetFileService(c.ClientType, c.ClientName, c.ClientVersion)\n\tfileSvc, err := fileService.Delete(ctx, c.User.UID, params)\n\n\tif err != nil {\n\t\th.respondError(c, code.ErrorFileDeleteFailed, err, \"websocket_router.file.FileDelete.Delete\")\n\t\treturn\n\t}\n\n\tc.ToResponse(code.Success.WithData(dto.FileDeleteAckMessage{\n\t\tLastTime: fileSvc.UpdatedTimestamp,\n\t\tPath:     fileSvc.Path,\n\t\tPathHash: fileSvc.PathHash,\n\t}), string(dto.FileDeleteAck))\n\n\t// Broadcast file deletion message\n\t// 广播文件删除消息\n\tc.BroadcastResponse(code.Success.WithData(\n\t\tdto.FileSyncDeleteMessage{\n\t\t\tPath:             fileSvc.Path,\n\t\t\tPathHash:         fileSvc.PathHash,\n\t\t\tCtime:            fileSvc.Ctime,\n\t\t\tMtime:            fileSvc.Mtime,\n\t\t\tSize:             fileSvc.Size,\n\t\t\tUpdatedTimestamp: fileSvc.UpdatedTimestamp,\n\t\t},\n\t).WithVault(params.Vault), true, dto.FileSyncDelete)\n}\n\n// FileRename handles file rename request.\n// FileRename 处理文件重命名请求。\nfunc (h *FileWSHandler) FileRename(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.FileRenameRequest{}\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.file.FileRename.BindAndValid\")\n\t\treturn\n\t}\n\n\tuid := c.User.UID\n\tfileService := h.App.GetFileService(c.ClientType, c.ClientName, c.ClientVersion)\n\toldFile, newFile, err := fileService.Rename(c.Context(), uid, params)\n\tif err != nil {\n\t\th.respondError(c, code.ErrorFileRenameFailed, err, \"websocket_router.file.FileRename.Rename\")\n\t\treturn\n\t}\n\n\t// Reply to sender with ack carrying server timestamp, so client can update lastFileSyncTime\n\t// 回复发送方携带服务端时间戳的 ack，让客户端可以更新 lastFileSyncTime\n\tc.ToResponse(code.Success.WithData(dto.FileRenameAckMessage{\n\t\tLastTime: newFile.UpdatedTimestamp,\n\t\tPath:     newFile.Path,\n\t\tPathHash: newFile.PathHash,\n\t}).WithVault(params.Vault), string(dto.FileRenameAck))\n\n\tc.BroadcastResponse(code.Success.WithData(\n\t\tdto.FileSyncRenameMessage{\n\t\t\tPath:             newFile.Path,\n\t\t\tPathHash:         newFile.PathHash,\n\t\t\tContentHash:      newFile.ContentHash,\n\t\t\tCtime:            newFile.Ctime,\n\t\t\tMtime:            newFile.Mtime,\n\t\t\tSize:             newFile.Size,\n\t\t\tOldPath:          oldFile.Path,\n\t\t\tOldPathHash:      oldFile.PathHash,\n\t\t\tUpdatedTimestamp: newFile.UpdatedTimestamp,\n\t\t},\n\t).WithVault(params.Vault), true, dto.FileSyncRename)\n}\n\n// FileChunkDownload handles file chunk download request.\n// Client requests file download via this interface, server creates download session and starts sending chunks.\n// FileChunkDownload 处理文件分片下载请求。\n// 客户端通过此接口请求下载文件,服务端创建下载会话并开始发送分片。\nfunc (h *FileWSHandler) FileChunkDownload(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.FileGetRequest{}\n\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.file.FileChunkDownload.BindAndValid\")\n\t\treturn\n\t}\n\n\tctx := c.Context()\n\n\tpkgapp.NoteModifyLog(c.TraceID, c.User.UID, \"FileChunkDownload\", params.Path, params.Vault)\n\n\t// 获取或创建仓库\n\th.App.VaultService.GetOrCreate(ctx, c.User.UID, params.Vault)\n\n\t// Get file info\n\t// 获取文件信息\n\tfileService := h.App.GetFileService(c.ClientType, c.ClientName, c.ClientVersion)\n\tfileSvc, err := fileService.Get(ctx, c.User.UID, params)\n\n\tif err != nil {\n\t\th.respondError(c, code.ErrorFileGetFailed, err, \"websocket_router.file.FileChunkDownload.Get\")\n\t\treturn\n\t}\n\n\t// Check if file exists on disk\n\t// 检查文件是否存在于磁盘\n\tif _, err := os.Stat(fileSvc.SavePath); os.IsNotExist(err) {\n\t\th.respondError(c, code.ErrorFileGetFailed, fmt.Errorf(\"file not found on disk: %s (pathHash: %s)\", fileSvc.SavePath, fileSvc.PathHash), \"websocket_router.file.FileChunkDownload.Stat\")\n\t\treturn\n\t}\n\n\t// Create download session\n\t// 创建下载会话\n\tsessionID := uuid.New().String()\n\tchunkSize := getChunkSizeFromConfig(h.App.Config()) // 从注入的配置获取\n\n\t// Calculate total chunks\n\t// 计算总分块数\n\ttotalChunks := util.Ceil(fileSvc.Size, chunkSize)\n\n\t// Initialize download session\n\t// 初始化下载会话\n\tsession := &FileDownloadChunkSession{\n\t\tSessionID:   sessionID,\n\t\tPath:        fileSvc.Path,\n\t\tSize:        fileSvc.Size,\n\t\tTotalChunks: totalChunks,\n\t\tChunkSize:   chunkSize,\n\t\tSavePath:    fileSvc.SavePath,\n\t}\n\n\t// Send download ready message\n\t// 发送下载准备消息\n\tc.ToResponse(code.Success.WithData(\n\t\tdto.FileSyncDownloadMessage{\n\t\t\tPath:        fileSvc.Path,\n\t\t\tCtime:       fileSvc.Ctime,\n\t\t\tMtime:       fileSvc.Mtime,\n\t\t\tSessionID:   session.SessionID,\n\t\t\tChunkSize:   session.ChunkSize,\n\t\t\tTotalChunks: session.TotalChunks,\n\t\t\tSize:        session.Size,\n\t\t},\n\t), dto.FileSyncChunkDownload)\n\n\t// Start chunk sending, pass timeout and logger\n\t// 启动分片发送,传入超时时间和 logger\n\tgo h.handleFileChunkDownloadSendChunks(c, session)\n}\n\n// FileSync batch checks if user files need update.\n// Compares file list between client and server, deciding which files need upload, update or delete.\n// FileSync 批量检测用户文件是否需要更新。\n// 对比客户端和服务端的文件列表，决定哪些文件需要上传、更新或删除。\nfunc (h *FileWSHandler) FileSync(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.FileSyncRequest{}\n\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.file.FileSync.BindAndValid\")\n\t\treturn\n\t}\n\n\tctx := c.Context()\n\n\tpkgapp.NoteModifyLog(c.TraceID, c.User.UID, \"FileSync\", \"\", params.Vault)\n\n\t// 获取或创建仓库\n\th.App.VaultService.GetOrCreate(ctx, c.User.UID, params.Vault)\n\n\t// Get list of changed files after last sync\n\t// 获取最后一次同步后的变更文件列表\n\tfileService := h.App.GetFileService(c.ClientType, c.ClientName, c.ClientVersion)\n\tlist, err := fileService.ListByLastTime(ctx, c.User.UID, params)\n\n\tif err != nil {\n\t\th.respondError(c, code.ErrorFileListFailed, err, \"websocket_router.file.FileSync.ListByLastTime\")\n\t\treturn\n\t}\n\n\t// Build client file index\n\t// 构建客户端文件索引\n\tvar cFiles map[string]dto.FileSyncCheckRequest = make(map[string]dto.FileSyncCheckRequest, 0)\n\tvar cFilesKeys map[string]struct{} = make(map[string]struct{}, 0)\n\n\tif len(params.Files) > 0 {\n\t\tfor _, file := range params.Files {\n\t\t\tcFiles[file.PathHash] = file\n\t\t\tcFilesKeys[file.PathHash] = struct{}{}\n\t\t}\n\t}\n\n\t// Create message queue for collecting all messages to be sent\n\t// 创建消息队列，用于收集所有待发送的消息\n\tvar messageQueue []dto.WSQueuedMessage\n\n\tvar lastTime int64\n\tvar needUploadCount int64\n\tvar needModifyCount int64\n\tvar needSyncMtimeCount int64\n\tvar needDeleteCount int64\n\n\tvar cDelFilesKeys map[string]struct{} = make(map[string]struct{}, 0)\n\n\t// Handle files deleted by client\n\t// 处理客户端删除的文件\n\tif len(params.DelFiles) > 0 {\n\t\tfor _, delFile := range params.DelFiles {\n\t\t\t// Check if file exists before deleting\n\t\t\t// 删除前检查文件是否存在\n\t\t\tgetParams := &dto.FileGetRequest{\n\t\t\t\tVault:    params.Vault,\n\t\t\t\tPathHash: delFile.PathHash,\n\t\t\t}\n\t\t\tcheckFile, err := fileService.Get(ctx, c.User.UID, getParams)\n\n\t\t\t// If file exists, execute delete\n\t\t\t// 如果文件存在，执行删除\n\t\t\tif err == nil && checkFile != nil && checkFile.Action != \"delete\" {\n\t\t\t\tdelParams := &dto.FileDeleteRequest{\n\t\t\t\t\tVault:    params.Vault,\n\t\t\t\t\tPath:     delFile.Path,\n\t\t\t\t\tPathHash: delFile.PathHash,\n\t\t\t\t}\n\t\t\t\tfileSvc, err := fileService.Delete(ctx, c.User.UID, delParams)\n\t\t\t\tif err != nil {\n\t\t\t\t\th.App.Logger().Error(\"websocket_router.file.FileSync.FileService.Delete\",\n\t\t\t\t\t\tzap.String(logger.FieldTraceID, c.TraceID),\n\t\t\t\t\t\tzap.Int64(logger.FieldUID, c.User.UID),\n\t\t\t\t\t\tzap.String(logger.FieldPath, delFile.Path),\n\t\t\t\t\t\tzap.Error(err))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// Record PathHash deleted by client to avoid duplicate sending\n\t\t\t\t// 记录客户端已主动删除的 PathHash，避免重复下发\n\t\t\t\tcDelFilesKeys[delFile.PathHash] = struct{}{}\n\n\t\t\t\t// Broadcast deletion to other clients\n\t\t\t\t// 将删除消息广播给其他客户端\n\t\t\t\tc.BroadcastResponse(code.Success.WithData(\n\t\t\t\t\tdto.FileSyncDeleteMessage{\n\t\t\t\t\t\tPath:     fileSvc.Path,\n\t\t\t\t\t\tPathHash: fileSvc.PathHash,\n\t\t\t\t\t\tCtime:    fileSvc.Ctime,\n\t\t\t\t\t\tMtime:    fileSvc.Mtime,\n\t\t\t\t\t\tSize:     fileSvc.Size,\n\t\t\t\t\t},\n\t\t\t\t).WithVault(params.Vault), true, dto.FileSyncDelete)\n\n\t\t\t} else {\n\t\t\t\t// File does not exist, but we still need to record exclusion and broadcast delete message to ensure data consistency\n\t\t\t\t// 文件不存在，但仍需记录排除并广播删除消息，以确保数据一致性\n\n\t\t\t\th.App.Logger().Debug(\"websocket_router.file.FileSync.FileService.Get check failed (not found or already deleted), broadcasting delete anyway\",\n\t\t\t\t\tzap.String(logger.FieldTraceID, c.TraceID),\n\t\t\t\t\tzap.String(\"pathHash\", delFile.PathHash))\n\n\t\t\t\t// Record PathHash\n\t\t\t\t// 记录 PathHash\n\t\t\t\tcDelFilesKeys[delFile.PathHash] = struct{}{}\n\n\t\t\t\t// Broadcast deletion with available info (Path/PathHash)\n\t\t\t\t// 使用现有信息(Path/PathHash)广播删除\n\t\t\t\tc.BroadcastResponse(code.Success.WithData(\n\t\t\t\t\tdto.FileSyncDeleteMessage{\n\t\t\t\t\t\tPath:     delFile.Path,\n\t\t\t\t\t\tPathHash: delFile.PathHash,\n\t\t\t\t\t\tCtime:    0,\n\t\t\t\t\t\tMtime:    0,\n\t\t\t\t\t\tSize:     0,\n\t\t\t\t\t},\n\t\t\t\t).WithVault(params.Vault), true, dto.FileSyncDelete)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Handle files missing on client (only for incremental sync)\n\t// 处理客户端缺失的文件（仅限增量同步）\n\tif params.LastTime > 0 && len(params.MissingFiles) > 0 {\n\t\tfor _, missingFile := range params.MissingFiles {\n\t\t\tgetParams := &dto.FileGetRequest{\n\t\t\t\tVault:    params.Vault,\n\t\t\t\tPathHash: missingFile.PathHash,\n\t\t\t}\n\t\t\tfileSvc, err := fileService.Get(ctx, c.User.UID, getParams)\n\t\t\tif err != nil {\n\t\t\t\th.App.Logger().Warn(\"websocket_router.file.FileSync.FileService.Get\",\n\t\t\t\t\tzap.String(logger.FieldTraceID, c.TraceID),\n\t\t\t\t\tzap.String(\"pathHash\", missingFile.PathHash),\n\t\t\t\t\tzap.Error(err))\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif fileSvc != nil && fileSvc.Action != \"delete\" {\n\n\t\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\t\tAction: dto.FileSyncUpdate,\n\t\t\t\t\tData: dto.FileSyncModifyMessage{\n\t\t\t\t\t\tPath:             fileSvc.Path,\n\t\t\t\t\t\tPathHash:         fileSvc.PathHash,\n\t\t\t\t\t\tContentHash:      fileSvc.ContentHash,\n\t\t\t\t\t\tSize:             fileSvc.Size,\n\t\t\t\t\t\tCtime:            fileSvc.Ctime,\n\t\t\t\t\t\tMtime:            fileSvc.Mtime,\n\t\t\t\t\t\tUpdatedTimestamp: fileSvc.UpdatedTimestamp,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tneedModifyCount++\n\t\t\t\t// 加入排除索引\n\t\t\t\tcDelFilesKeys[fileSvc.PathHash] = struct{}{}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Iterate over server file list for processing\n\t// 遍历服务端文件列表进行处理\n\tfor _, file := range list {\n\t\t// 如果该文件是客户端刚才通过参数告知删除的，则跳过下发\n\t\tif _, ok := cDelFilesKeys[file.PathHash]; ok {\n\t\t\tcontinue\n\t\t}\n\n\t\t// lastTime is set after the loop via timex.Now(), do not update here\n\t\t// lastTime 在循环后统一由 timex.Now() 赋值，此处不更新\n\n\t\tif file.Action == \"delete\" {\n\t\t\t// Server already deleted, notify client to delete (regardless of whether client has it)\n\t\t\t// 服务端已删除，通知客户端删除（不再检查客户端是否存在）\n\t\t\tif _, ok := cFiles[file.PathHash]; ok {\n\t\t\t\tdelete(cFilesKeys, file.PathHash)\n\t\t\t}\n\t\t\t// 将消息添加到队列\n\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\tAction: dto.FileSyncDelete,\n\t\t\t\tData: dto.FileSyncDeleteMessage{\n\t\t\t\t\tPath:             file.Path,\n\t\t\t\t\tPathHash:         file.PathHash,\n\t\t\t\t\tCtime:            file.Ctime,\n\t\t\t\t\tMtime:            file.Mtime,\n\t\t\t\t\tSize:             file.Size,\n\t\t\t\t\tUpdatedTimestamp: file.UpdatedTimestamp,\n\t\t\t\t},\n\t\t\t})\n\t\t\tneedDeleteCount++\n\n\t\t} else {\n\n\t\t\tif cFile, ok := cFiles[file.PathHash]; ok {\n\t\t\t\t// Client has this file\n\t\t\t\t// 客户端存在该文件\n\t\t\t\tdelete(cFilesKeys, file.PathHash)\n\n\t\t\t\tif file.ContentHash == cFile.ContentHash && file.Mtime == cFile.Mtime {\n\t\t\t\t\t// Content and time match, no action\n\t\t\t\t\t// 内容与时间一致，无操作\n\t\t\t\t\tcontinue\n\t\t\t\t} else if file.ContentHash != cFile.ContentHash {\n\t\t\t\t\t// Content inconsistent\n\t\t\t\t\t// 内容不一致\n\t\t\t\t\tif file.Mtime > cFile.Mtime {\n\t\t\t\t\t\t// 服务端修改时间比客户端新, 通知客户端下载更新文件\n\t\t\t\t\t\tfileMessage := &dto.FileSyncModifyMessage{\n\t\t\t\t\t\t\tPath:             file.Path,\n\t\t\t\t\t\t\tPathHash:         file.PathHash,\n\t\t\t\t\t\t\tContentHash:      file.ContentHash,\n\t\t\t\t\t\t\tSize:             file.Size,\n\t\t\t\t\t\t\tCtime:            file.Ctime,\n\t\t\t\t\t\t\tMtime:            file.Mtime,\n\t\t\t\t\t\t\tUpdatedTimestamp: file.UpdatedTimestamp,\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// 将消息添加到队列而非立即发送\n\t\t\t\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\t\t\t\tAction: dto.FileSyncUpdate,\n\t\t\t\t\t\t\tData:   fileMessage,\n\t\t\t\t\t\t})\n\t\t\t\t\t\tneedModifyCount++\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// 服务端修改时间比客户端旧, 通知客户端上传文件\n\t\t\t\t\t\tsession, ferr := h.handleFileUploadSessionCreate(c, params.Vault, cFile.Path, cFile.PathHash, cFile.ContentHash, cFile.Size, file.Ctime, cFile.Mtime)\n\t\t\t\t\t\tif ferr != nil {\n\t\t\t\t\t\t\th.logError(c, \"websocket_router.file.FileSync handleFileUploadSession err\", ferr)\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// 将消息添加到队列而非立即发送\n\t\t\t\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\t\t\t\tAction: dto.FileUpload,\n\t\t\t\t\t\t\tData: dto.FileSyncUploadMessage{\n\t\t\t\t\t\t\t\tPath:      session.Path,\n\t\t\t\t\t\t\t\tSessionID: session.ID,\n\t\t\t\t\t\t\t\tChunkSize: session.ChunkSize,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t\tneedUploadCount++\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Content matches, but modification time differs, notify client to update file modification time\n\t\t\t\t\t// 内容一致, 但修改时间不一致, 通知客户端更新文件修改时间\n\t\t\t\t\t// Add message to queue instead of sending immediately\n\t\t\t\t\t// 将消息添加到队列而非立即发送\n\t\t\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\t\t\tAction: dto.FileSyncMtime,\n\t\t\t\t\t\tData: dto.FileSyncMtimeMessage{\n\t\t\t\t\t\t\tPath:             file.Path,\n\t\t\t\t\t\t\tCtime:            file.Ctime,\n\t\t\t\t\t\t\tMtime:            file.Mtime,\n\t\t\t\t\t\t\tUpdatedTimestamp: file.UpdatedTimestamp,\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\tneedSyncMtimeCount++\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// File client doesn't have, notify client to download file\n\t\t\t\t// 客户端没有的文件, 通知客户端下载文件\n\t\t\t\t// 将消息添加到队列而非立即发送\n\t\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\t\tAction: dto.FileSyncUpdate,\n\t\t\t\t\tData: dto.FileSyncModifyMessage{\n\t\t\t\t\t\tPath:             file.Path,\n\t\t\t\t\t\tPathHash:         file.PathHash,\n\t\t\t\t\t\tContentHash:      file.ContentHash,\n\t\t\t\t\t\tSize:             file.Size,\n\t\t\t\t\t\tCtime:            file.Ctime,\n\t\t\t\t\t\tMtime:            file.Mtime,\n\t\t\t\t\t\tUpdatedTimestamp: file.UpdatedTimestamp,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tneedModifyCount++\n\t\t\t}\n\t\t}\n\t}\n\n\t// Use current time as lastTime regardless of whether list is empty,\n\t// ensuring lastTime > all returned files' updated_timestamp (mirrors FolderSync design)\n\t// 无论 list 是否为空，均取当前时间作为 lastTime，\n\t// 确保 lastTime > 所有返回文件的 updated_timestamp（与 FolderSync 保持一致）\n\tlastTime = timex.Now().UnixMilli()\n\t// Handle files that exist on client but not synced on server (request client upload)\n\t// 处理客户端存在但服务端未同步的文件（请求客户端上传）\n\tif len(cFilesKeys) > 0 {\n\t\tfor pathHash := range cFilesKeys {\n\t\t\tfile := cFiles[pathHash]\n\t\t\t// Create upload session and return FileUpload message\n\t\t\t// 创建上传会话并返回 FileUpload 消息\n\t\t\tsession, ferr := h.handleFileUploadSessionCreate(c, params.Vault, file.Path, file.PathHash, file.ContentHash, file.Size, file.Ctime, file.Mtime)\n\t\t\tif ferr != nil {\n\t\t\t\th.logError(c, \"websocket_router.file.FileSync handleFileUploadSession err\", ferr)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// Add message to queue instead of sending immediately\n\t\t\t// 将消息添加到队列而非立即发送\n\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\tAction: dto.FileUpload,\n\t\t\t\tData: dto.FileSyncUploadMessage{\n\t\t\t\t\tPath:      session.Path,\n\t\t\t\t\tSessionID: session.ID,\n\t\t\t\t\tChunkSize: session.ChunkSize,\n\t\t\t\t},\n\t\t\t})\n\t\t\tneedUploadCount++\n\t\t}\n\t}\n\n\t// Send FileSyncEnd message\n\t// 发送 FileSyncEnd 消息\n\tc.ToResponse(code.Success.WithData(\n\t\tdto.FileSyncEndMessage{\n\t\t\tLastTime:           lastTime,\n\t\t\tNeedUploadCount:    needUploadCount,\n\t\t\tNeedModifyCount:    needModifyCount,\n\t\t\tNeedSyncMtimeCount: needSyncMtimeCount,\n\t\t\tNeedDeleteCount:    needDeleteCount,\n\t\t},\n\t).WithVault(params.Vault).WithContext(params.Context), dto.FileSyncEnd)\n\n\t// Send queued messages in batches to reduce CPU/memory pressure\n\t// 分批发送队列中的消息，以减轻 CPU 和内存压力\n\tbatchSize := 200\n\tfor i := 0; i < len(messageQueue); i += batchSize {\n\t\tend := i + batchSize\n\t\tif end > len(messageQueue) {\n\t\t\tend = len(messageQueue)\n\t\t}\n\t\tfor _, item := range messageQueue[i:end] {\n\t\t\tc.ToResponse(code.Success.WithData(item.Data).WithVault(params.Vault).WithContext(params.Context), item.Action)\n\t\t}\n\t\t// Optional: slight pause could be added here if network congestion is a concern,\n\t\t// but the primary goal is reducing serialization overhead per message block.\n\t}\n}\n\n// cleanupSession cleans up discarded upload sessions due to completion or timeout.\n// cleanupSession 清理因为完成或超时而废弃的上传会话。\nfunc (h *FileWSHandler) handleFileUploadSessionCleanup(c *pkgapp.WebsocketClient, sessionID string) {\n\tbinarySession := c.Server.GetSession(c.User.ID, sessionID)\n\tif binarySession == nil {\n\t\treturn\n\t}\n\tc.Server.RemoveSession(c.User.ID, sessionID)\n\n\tsession := binarySession.(pkgapp.SessionCleaner)\n\tsession.Cleanup()\n\n\th.logInfo(c, \"cleanupSession: session cleaned up\",\n\t\tzap.String(\"sessionID\", sessionID))\n}\n\nfunc (h *FileWSHandler) handleFileUploadSessionTimeout(c *pkgapp.WebsocketClient, sessionID string, timeout time.Duration) context.CancelFunc {\n\tif timeout <= 0 {\n\t\treturn nil\n\t}\n\n\t// Use context.Background() to ensure timeout timer is not affected by connection loss\n\t// This keeps the session valid during network fluctuation and reconnection\n\t// 使用 context.Background() 确保超时定时器不受连接断开影响\n\t// 这样在网络波动重连期间，会话依然有效\n\tctx, cancel := context.WithCancel(context.Background())\n\n\tgo func() {\n\t\ttimer := time.NewTimer(timeout)\n\t\tdefer timer.Stop()\n\n\t\tselect {\n\t\tcase <-timer.C:\n\t\t\t// Timeout triggered, clean up session\n\t\t\t// 超时触发，清理会话\n\t\t\th.logWarn(c, \"startSessionTimeout: session timeout, cleaning up\",\n\t\t\t\tzap.String(\"sessionID\", sessionID),\n\t\t\t\tzap.Duration(\"timeout\", timeout))\n\n\t\t\t// Execute cleanup on timeout\n\t\t\t// 超时执行清理\n\t\t\th.handleFileUploadSessionCleanup(c, sessionID)\n\t\tcase <-ctx.Done():\n\t\t\t// Cancel timer\n\t\t\t// 取消定时器\n\t\t\treturn\n\t\t}\n\t}()\n\n\treturn cancel\n}\n\n// handleFileUploadSession initializes a file upload session and returns upload message.\n// handleFileUploadSession 初始化一个文件上传会话并返回上传消息.\nfunc (h *FileWSHandler) handleFileUploadSessionCreate(c *pkgapp.WebsocketClient, vault, path, pathHash, contentHash string, size, ctime, mtime int64) (*FileUploadBinaryChunkSession, error) {\n\tsessionID := uuid.New().String()\n\tcfg := h.App.Config()\n\ttempDir := cfg.App.TempPath\n\tif tempDir == \"\" {\n\t\ttempDir = \"storage/temp\"\n\t}\n\n\t// Create temp directory\n\t// 创建临时目录\n\tif err := os.MkdirAll(tempDir, 0754); err != nil {\n\t\th.logError(c, \"websocket_router.file.handleFileUploadSession.MkdirAll\", err)\n\t\treturn nil, err\n\t}\n\n\tvar tempPath string\n\n\tfor i := 0; i < 10; i++ {\n\t\ttryFileName := uuid.New().String()\n\t\ttryPath := filepath.Join(tempDir, tryFileName)\n\n\t\tif _, err := os.Stat(tryPath); os.IsNotExist(err) {\n\t\t\ttempPath = tryPath\n\t\t\tbreak\n\t\t}\n\t}\n\n\t// Initialize chunk upload session\n\t// 初始化分块上传会话\n\tsession := &FileUploadBinaryChunkSession{\n\t\tID:             sessionID,\n\t\tVault:          vault,\n\t\tPath:           path,\n\t\tPathHash:       pathHash,\n\t\tContentHash:    contentHash,\n\t\tSize:           size,\n\t\tCtime:          ctime,\n\t\tMtime:          mtime,\n\t\tChunkSize:      getChunkSizeFromConfig(cfg), // 从注入的配置获取\n\t\tSavePath:       tempPath,\n\t\tFileHandle:     nil, // Lazy creation in FileUploadChunkBinary // 在 FileUploadChunkBinary 中延迟创建\n\t\tCreatedAt:      time.Now(),\n\t\tuploadedChunks: make(map[uint32]struct{}),\n\t}\n\t// Adjust chunk size based on file size\n\t// 根据文件大小调整分块大小\n\tsession.TotalChunks = util.Ceil(session.Size, session.ChunkSize)\n\n\t// Configure timeout\n\t// 配置超时时间\n\tvar timeout time.Duration\n\tif cfg.App.UploadSessionTimeout != \"\" && cfg.App.UploadSessionTimeout != \"0\" {\n\t\tvar err error\n\t\ttimeout, err = util.ParseDuration(cfg.App.UploadSessionTimeout)\n\t\tif err != nil {\n\t\t\th.logWarn(c, \"handleFileUploadSession: invalid upload-session-timeout config, using default 20m\",\n\t\t\t\tzap.String(\"config\", cfg.App.UploadSessionTimeout),\n\t\t\t\tzap.Error(err))\n\t\t\ttimeout = 20 * time.Minute\n\t\t}\n\t} else {\n\t\ttimeout = 20 * time.Minute\n\t}\n\n\t// Start timeout cleanup task\n\t// 启动超时清理任务\n\tsession.CancelFunc = h.handleFileUploadSessionTimeout(c, sessionID, timeout)\n\n\t// Register to global server\n\t// 注册到全局服务器\n\tc.Server.SetSession(c.User.ID, session.ID, session)\n\n\treturn session, nil\n}\n\n// handleFileChunkDownloadSendChunks executes file chunk sending.\n// Runs in independent goroutine, reads file and sends binary chunks via WebSocket.\n// handleFileChunkDownloadSendChunks 执行文件分片发送。\n// 在独立的 goroutine 中运行,读取文件并通过 WebSocket 发送二进制分片。\nfunc (h *FileWSHandler) handleFileChunkDownloadSendChunks(c *pkgapp.WebsocketClient, session *FileDownloadChunkSession) {\n\tlogger := h.App.Logger()\n\t// Get timeout from config, default 1 hour\n\t// 从配置获取超时时间，默认 1 小时\n\ttimeout := 1 * time.Hour\n\tcfg := h.App.Config()\n\tif cfg.App.DownloadSessionTimeout != \"\" && cfg.App.DownloadSessionTimeout != \"0\" {\n\t\tif t, err := util.ParseDuration(cfg.App.DownloadSessionTimeout); err == nil {\n\t\t\ttimeout = t\n\t\t} else {\n\t\t\tLogWarnWithLogger(logger, c, \"sendFileChunks: invalid download-session-timeout config, using default 1h\",\n\t\t\t\tzap.String(\"config\", cfg.App.DownloadSessionTimeout),\n\t\t\t\tzap.Error(err))\n\t\t}\n\t}\n\n\t// Create timeout context, based on WebSocket connection context\n\t// 创建超时上下文，基于 WebSocket 连接的 context\n\tctx, cancel := context.WithTimeout(c.Context(), timeout)\n\tdefer cancel()\n\n\t// Open file\n\t// 打开文件\n\tfile, err := os.Open(session.SavePath)\n\tif err != nil {\n\t\tLogErrorWithLogger(logger, c, \"sendFileChunks: failed to open file\", err)\n\t\tc.ToResponse(code.ErrorFileGetFailed.WithDetails(\"failed to open file\"))\n\t\treturn\n\t}\n\tdefer file.Close()\n\n\tLogInfoWithLogger(logger, c, \"sendFileChunks: starting file download\",\n\t\tzap.String(\"sessionID\", session.SessionID),\n\t\tzap.String(\"path\", session.Path),\n\t\tzap.Int64(\"size\", session.Size),\n\t\tzap.Int64(\"totalChunks\", session.TotalChunks))\n\n\t// Loop to send chunks\n\t// 循环发送分片\n\tfor chunkIndex := int64(0); chunkIndex < session.TotalChunks; chunkIndex++ {\n\t\t// Check timeout\n\t\t// 检查超时\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\tLogWarnWithLogger(logger, c, \"sendFileChunks: download timeout\",\n\t\t\t\tzap.String(\"sessionID\", session.SessionID),\n\t\t\t\tzap.Int64(\"sentChunks\", chunkIndex),\n\t\t\t\tzap.Int64(\"totalChunks\", session.TotalChunks))\n\t\t\treturn\n\t\tdefault:\n\t\t}\n\n\t\t// Calculate current chunk size\n\t\t// 计算当前分片的大小\n\t\tchunkStart := chunkIndex * session.ChunkSize\n\t\tchunkEnd := chunkStart + session.ChunkSize\n\t\tif chunkEnd > session.Size {\n\t\t\tchunkEnd = session.Size\n\t\t}\n\t\tcurrentChunkSize := chunkEnd - chunkStart\n\n\t\t// 读取分片数据\n\t\tchunkData := make([]byte, currentChunkSize)\n\t\tn, err := file.ReadAt(chunkData, chunkStart)\n\t\tif err != nil && err.Error() != \"EOF\" {\n\t\t\tLogErrorWithLogger(logger, c, \"sendFileChunks: failed to read chunk\", err)\n\t\t\tc.ToResponse(code.ErrorFileGetFailed.WithDetails(\"failed to read file chunk\"))\n\t\t\treturn\n\t\t}\n\n\t\t// 构造二进制消息\n\t\t// 格式: [36 bytes session_id][4 bytes chunk_index][chunk_data]\n\t\theaderSize := 40\n\t\tpacket := make([]byte, headerSize+n)\n\n\t\t// 1. Session ID (36 bytes)\n\t\tcopy(packet[0:36], []byte(session.SessionID))\n\n\t\t// 2. Chunk Index (4 bytes, Big Endian)\n\t\tbinary.BigEndian.PutUint32(packet[36:40], uint32(chunkIndex))\n\n\t\t// 3. Chunk Data\n\t\tcopy(packet[40:], chunkData[:n])\n\n\t\t// 发送二进制消息\n\t\terr = c.SendBinary(dto.VaultFileMsgType, packet)\n\t\tif err != nil {\n\t\t\tLogErrorWithLogger(logger, c, \"sendFileChunks: failed to send chunk\", err)\n\t\t\treturn\n\t\t}\n\n\t\t// 每发送 100 个分片记录一次日志\n\t\tif (chunkIndex+1)%100 == 0 || chunkIndex == session.TotalChunks-1 {\n\t\t\tLogInfoWithLogger(logger, c, \"sendFileChunks: progress\",\n\t\t\t\tzap.String(\"sessionID\", session.SessionID),\n\t\t\t\tzap.Int64(\"sent\", chunkIndex+1),\n\t\t\t\tzap.Int64(\"total\", session.TotalChunks))\n\t\t}\n\t}\n\n\tLogInfoWithLogger(logger, c, \"sendFileChunks: download completed\",\n\t\tzap.String(\"sessionID\", session.SessionID),\n\t\tzap.String(\"path\", session.Path),\n\t\tzap.Int64(\"totalChunks\", session.TotalChunks))\n}\n\nfunc (h *FileWSHandler) FileRePush(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.FileGetRequest{}\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.file.FileRePush.BindAndValid\")\n\t\treturn\n\t}\n\n\tpkgapp.NoteModifyLog(c.TraceID, c.User.UID, \"FileRePush\", params.Path, params.Vault)\n\n\tctx := c.Context()\n\t// 获取或创建仓库\n\th.App.VaultService.GetOrCreate(ctx, c.User.UID, params.Vault)\n\n\tfileSvc, err := h.App.GetFileService(c.ClientType, c.ClientName, c.ClientVersion).Get(ctx, c.User.UID, params)\n\tif err != nil {\n\t\th.respondError(c, code.ErrorFileGetFailed, err, \"websocket_router.file.FileRePush.Get\")\n\t\treturn\n\t}\n\n\tif fileSvc != nil && fileSvc.Action != \"delete\" {\n\t\tc.ToResponse(code.Success.WithData(\n\t\t\tdto.FileSyncModifyMessage{\n\t\t\t\tPath:             fileSvc.Path,\n\t\t\t\tPathHash:         fileSvc.PathHash,\n\t\t\t\tContentHash:      fileSvc.ContentHash,\n\t\t\t\tSize:             fileSvc.Size,\n\t\t\t\tCtime:            fileSvc.Ctime,\n\t\t\t\tMtime:            fileSvc.Mtime,\n\t\t\t\tUpdatedTimestamp: fileSvc.UpdatedTimestamp,\n\t\t\t},\n\t\t).WithVault(params.Vault), dto.FileSyncUpdate)\n\t} else {\n\t\tc.ToResponse(code.ErrorFileGetFailed)\n\t}\n}\n\n// getChunkSizeFromConfig 从注入的配置获取分片大小, 默认为 512KB\nfunc getChunkSizeFromConfig(cfg *app.AppConfig) int64 {\n\treturn util.ParseSize(cfg.App.FileChunkSize, 1024*512)\n}\n"
  },
  {
    "path": "internal/routers/websocket_router/ws_folder.go",
    "content": "package websocket_router\n\nimport (\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\tlogpkg \"github.com/haierkeys/fast-note-sync-service/pkg/logger\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"go.uber.org/zap\"\n)\n\ntype FolderWSHandler struct {\n\t*WSHandler\n}\n\nfunc NewFolderWSHandler(a *app.App) *FolderWSHandler {\n\treturn &FolderWSHandler{WSHandler: NewWSHandler(a)}\n}\n\n// FolderSync handles folder synchronization\nfunc (h *FolderWSHandler) FolderSync(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.FolderSyncRequest{}\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.folder.FolderSync.BindAndValid\")\n\t\treturn\n\t}\n\n\tctx := c.Context()\n\tuid := c.User.UID\n\n\t// Check and create vault\n\th.App.VaultService.GetOrCreate(ctx, uid, params.Vault)\n\n\tfolderSvc := h.App.GetFolderService(c.ClientType, c.ClientName, c.ClientVersion)\n\n\tvar cFolders map[string]dto.FolderSyncCheckRequest = make(map[string]dto.FolderSyncCheckRequest)\n\tvar cFoldersKeys map[string]struct{} = make(map[string]struct{}, 0)\n\tif len(params.Folders) > 0 {\n\t\tfor _, f := range params.Folders {\n\t\t\tcFolders[f.PathHash] = f\n\t\t}\n\t\tfor _, folder := range params.Folders {\n\t\t\tcFoldersKeys[folder.PathHash] = struct{}{}\n\t\t}\n\t}\n\n\tvar messageQueue []dto.WSQueuedMessage\n\tvar needModifyCount int64\n\tvar needDeleteCount int64\n\tvar cDelFoldersKeys map[string]struct{} = make(map[string]struct{})\n\n\t// Handle deleted folders from client\n\tif len(params.DelFolders) > 0 {\n\t\tfor _, delFolder := range params.DelFolders {\n\n\t\t\t// Check if folder exists before deleting\n\t\t\tcheckFolder, err := folderSvc.Get(ctx, uid, &dto.FolderGetRequest{\n\t\t\t\tVault:    params.Vault,\n\t\t\t\tPathHash: delFolder.PathHash,\n\t\t\t})\n\n\t\t\tif err == nil && checkFolder != nil && checkFolder.Action != \"delete\" {\n\t\t\t\tdelParams := &dto.FolderDeleteRequest{\n\t\t\t\t\tVault:    params.Vault,\n\t\t\t\t\tPath:     delFolder.Path,\n\t\t\t\t\tPathHash: delFolder.PathHash,\n\t\t\t\t}\n\t\t\t\tfolder, err := folderSvc.Delete(ctx, uid, delParams)\n\t\t\t\tif err != nil {\n\t\t\t\t\th.App.Logger().Error(\"websocket_router.folder.FolderSync.FolderService.Delete\",\n\t\t\t\t\t\tzap.String(logpkg.FieldTraceID, c.TraceID),\n\t\t\t\t\t\tzap.Int64(logpkg.FieldUID, uid),\n\t\t\t\t\t\tzap.String(logpkg.FieldPath, delFolder.Path),\n\t\t\t\t\t\tzap.Error(err))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tcDelFoldersKeys[delFolder.PathHash] = struct{}{}\n\t\t\t\t// Broadcast deletion to other clients\n\t\t\t\tc.BroadcastResponse(code.Success.WithData(\n\t\t\t\t\tdto.FolderSyncDeleteMessage{\n\t\t\t\t\t\tPath:     folder.Path,\n\t\t\t\t\t\tPathHash: folder.PathHash,\n\t\t\t\t\t\tCtime:    folder.Ctime,\n\t\t\t\t\t\tMtime:    folder.Mtime,\n\t\t\t\t\t},\n\t\t\t\t).WithVault(params.Vault).WithContext(params.Context), true, dto.FolderSyncDelete)\n\t\t\t} else {\n\t\t\t\th.App.Logger().Debug(\"websocket_router.folder.FolderSync.FolderService.Get check failed (not found or already deleted), broadcasting delete anyway\",\n\t\t\t\t\tzap.String(logpkg.FieldTraceID, c.TraceID),\n\t\t\t\t\tzap.String(\"pathHash\", delFolder.PathHash))\n\n\t\t\t\tcDelFoldersKeys[delFolder.PathHash] = struct{}{}\n\t\t\t\t// Broadcast deletion with available info\n\t\t\t\tc.BroadcastResponse(code.Success.WithData(\n\t\t\t\t\tdto.FolderSyncDeleteMessage{\n\t\t\t\t\t\tPath:     delFolder.Path,\n\t\t\t\t\t\tPathHash: delFolder.PathHash,\n\t\t\t\t\t\tCtime:    0,\n\t\t\t\t\t\tMtime:    0,\n\t\t\t\t\t},\n\t\t\t\t).WithVault(params.Vault).WithContext(params.Context), true, dto.FolderSyncDelete)\n\t\t\t}\n\n\t\t}\n\t}\n\n\t// Handle missing folders on client\n\tif params.LastTime > 0 && len(params.MissingFolders) > 0 {\n\t\tfor _, missingFolder := range params.MissingFolders {\n\t\t\tfolder, err := folderSvc.Get(ctx, uid, &dto.FolderGetRequest{\n\t\t\t\tVault:    params.Vault,\n\t\t\t\tPath:     missingFolder.Path,\n\t\t\t\tPathHash: missingFolder.PathHash,\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\th.App.Logger().Warn(\"websocket_router.folder.FolderSync.FolderService.Get\",\n\t\t\t\t\tzap.String(logpkg.FieldTraceID, c.TraceID),\n\t\t\t\t\tzap.String(\"pathHash\", missingFolder.PathHash),\n\t\t\t\t\tzap.Error(err))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif folder != nil && folder.Action != \"delete\" {\n\t\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\t\tAction: dto.FolderSyncModify,\n\t\t\t\t\tData: dto.FolderSyncModifyMessage{\n\t\t\t\t\t\tPath:             folder.Path,\n\t\t\t\t\t\tPathHash:         folder.PathHash,\n\t\t\t\t\t\tCtime:            folder.Ctime,\n\t\t\t\t\t\tMtime:            folder.Mtime,\n\t\t\t\t\t\tUpdatedTimestamp: folder.UpdatedTimestamp,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tneedModifyCount++\n\t\t\t\tcDelFoldersKeys[folder.PathHash] = struct{}{}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Get updated folders from server\n\tlist, err := folderSvc.ListByUpdatedTimestamp(ctx, uid, params.Vault, params.LastTime)\n\tif err != nil {\n\t\th.respondError(c, code.ErrorFolderListFailed, err, \"websocket_router.folder.FolderSync.ListByUpdatedTimestamp\")\n\t\treturn\n\t}\n\n\tfor _, folder := range list {\n\t\tif _, ok := cDelFoldersKeys[folder.PathHash]; ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tif folder.Action == \"delete\" {\n\t\t\tdelete(cFoldersKeys, folder.PathHash)\n\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\tAction: dto.FolderSyncDelete,\n\t\t\t\tData: dto.FolderSyncDeleteMessage{\n\t\t\t\t\tPath:     folder.Path,\n\t\t\t\t\tPathHash: folder.PathHash,\n\t\t\t\t\tCtime:    folder.Ctime,\n\t\t\t\t\tMtime:    folder.Mtime,\n\t\t\t\t},\n\t\t\t})\n\t\t\tneedDeleteCount++\n\t\t} else {\n\t\t\tdelete(cFoldersKeys, folder.PathHash)\n\t\t\t_, exists := cFolders[folder.PathHash]\n\t\t\tif !exists {\n\n\t\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\t\tAction: dto.FolderSyncModify,\n\t\t\t\t\tData: dto.FolderSyncModifyMessage{\n\t\t\t\t\t\tPath:             folder.Path,\n\t\t\t\t\t\tPathHash:         folder.PathHash,\n\t\t\t\t\t\tCtime:            folder.Ctime,\n\t\t\t\t\t\tMtime:            folder.Mtime,\n\t\t\t\t\t\tUpdatedTimestamp: folder.UpdatedTimestamp,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tneedModifyCount++\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(cFoldersKeys) > 0 {\n\t\tfor pathHash := range cFoldersKeys {\n\t\t\tfolder := cFolders[pathHash]\n\n\t\t\tnewFolder, err := folderSvc.UpdateOrCreate(ctx, uid, &dto.FolderCreateRequest{\n\t\t\t\tVault:    params.Vault,\n\t\t\t\tPath:     folder.Path,\n\t\t\t\tPathHash: folder.PathHash,\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\th.logError(c, \"websocket_router.folder.FolderSync.UpdateOrCreate\", err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tc.BroadcastResponse(code.Success.WithData(\n\t\t\t\tdto.FolderSyncModifyMessage{\n\t\t\t\t\tPath:             newFolder.Path,\n\t\t\t\t\tPathHash:         newFolder.PathHash,\n\t\t\t\t\tCtime:            newFolder.Ctime,\n\t\t\t\t\tMtime:            newFolder.Mtime,\n\t\t\t\t\tUpdatedTimestamp: newFolder.UpdatedTimestamp,\n\t\t\t\t},\n\t\t\t).WithVault(params.Vault).WithContext(params.Context), true, dto.FolderSyncModify)\n\t\t}\n\t}\n\n\t// Send FolderSyncEnd message\n\t// 发送 FolderSyncEnd 消息\n\tc.ToResponse(code.Success.WithData(&dto.FolderSyncEndMessage{\n\t\tLastTime:        timex.Now().UnixMilli(),\n\t\tNeedModifyCount: needModifyCount,\n\t\tNeedDeleteCount: needDeleteCount,\n\t}).WithContext(params.Context), dto.FolderSyncEnd)\n\n\t// Send queued messages individually\n\t// 逐条发送队列中的消息\n\tfor _, item := range messageQueue {\n\t\tc.ToResponse(code.Success.WithData(item.Data).WithContext(params.Context), item.Action)\n\t}\n}\n\n// FolderModify handles folder modification/creation\nfunc (h *FolderWSHandler) FolderModify(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.FolderCreateRequest{}\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.folder.FolderModify.BindAndValid\")\n\t\treturn\n\t}\n\n\tuid := c.User.UID\n\tfolder, err := h.App.GetFolderService(c.ClientType, c.ClientName, c.ClientVersion).UpdateOrCreate(c.Context(), uid, params)\n\tif err != nil {\n\t\th.respondError(c, code.ErrorFolderModifyOrCreateFailed, err, \"websocket_router.folder.FolderModify.UpdateOrCreate\")\n\t\treturn\n\t}\n\n\tc.ToResponse(code.Success.WithData(dto.FolderModifyAckMessage{\n\t\tLastTime: folder.UpdatedTimestamp,\n\t\tPath:     folder.Path,\n\t\tPathHash: folder.PathHash,\n\t}), string(dto.FolderModifyAck))\n\tc.BroadcastResponse(code.Success.WithData(\n\t\tdto.FolderSyncModifyMessage{\n\t\t\tPath:             folder.Path,\n\t\t\tPathHash:         folder.PathHash,\n\t\t\tCtime:            folder.Ctime,\n\t\t\tMtime:            folder.Mtime,\n\t\t\tUpdatedTimestamp: folder.UpdatedTimestamp,\n\t\t},\n\t).WithVault(params.Vault), true, dto.FolderSyncModify)\n}\n\n// FolderDelete handles folder deletion\n// 删除\nfunc (h *FolderWSHandler) FolderDelete(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.FolderDeleteRequest{}\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.folder.FolderDelete.BindAndValid\")\n\t\treturn\n\t}\n\n\tuid := c.User.UID\n\tfolder, err := h.App.GetFolderService(c.ClientType, c.ClientName, c.ClientVersion).Delete(c.Context(), uid, params)\n\tif err != nil {\n\t\th.respondError(c, code.ErrorFolderDeleteFailed, err, \"websocket_router.folder.FolderDelete.Delete\")\n\t\treturn\n\t}\n\n\tc.ToResponse(code.Success.WithData(dto.FolderDeleteAckMessage{\n\t\tLastTime: folder.UpdatedTimestamp,\n\t\tPath:     folder.Path,\n\t\tPathHash: folder.PathHash,\n\t}), string(dto.FolderDeleteAck))\n\tc.BroadcastResponse(code.Success.WithData(\n\t\tdto.FolderSyncDeleteMessage{\n\t\t\tPath:     folder.Path,\n\t\t\tPathHash: folder.PathHash,\n\t\t\tCtime:    folder.Ctime,\n\t\t\tMtime:    folder.Mtime,\n\t\t},\n\t).WithVault(params.Vault), true, dto.FolderSyncDelete)\n}\n\n// FolderRename handles folder renaming\n// 重命名文件夹\nfunc (h *FolderWSHandler) FolderRename(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.FolderRenameRequest{}\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.folder.FolderRename.BindAndValid\")\n\t\treturn\n\t}\n\n\tuid := c.User.UID\n\tfolderSvc := h.App.GetFolderService(c.ClientType, c.ClientName, c.ClientVersion)\n\toldFolder, newFolder, err := folderSvc.Rename(c.Context(), uid, params)\n\tif err != nil {\n\t\th.respondError(c, code.ErrorFolderRenameFailed, err, \"websocket_router.folder.FolderRename.Rename\")\n\t\treturn\n\t}\n\n\tc.ToResponse(code.Success.WithData(dto.FolderRenameAckMessage{\n\t\tLastTime: newFolder.UpdatedTimestamp,\n\t\tPath:     newFolder.Path,\n\t\tPathHash: newFolder.PathHash,\n\t}), string(dto.FolderRenameAck))\n\n\t// 如果 oldFolder 为空，说明是新增文件夹\n\tif oldFolder == nil {\n\t\tc.BroadcastResponse(code.Success.WithData(\n\t\t\tdto.FolderSyncModifyMessage{\n\t\t\t\tPath:             newFolder.Path,\n\t\t\t\tPathHash:         newFolder.PathHash,\n\t\t\t\tCtime:            newFolder.Ctime,\n\t\t\t\tMtime:            newFolder.Mtime,\n\t\t\t\tUpdatedTimestamp: newFolder.UpdatedTimestamp,\n\t\t\t},\n\t\t).WithVault(params.Vault), true, dto.FolderSyncModify)\n\t\treturn\n\t}\n\n\tc.BroadcastResponse(code.Success.WithData(dto.FolderSyncRenameMessage{\n\t\tPath:        newFolder.Path,\n\t\tPathHash:    newFolder.PathHash,\n\t\tCtime:       newFolder.Ctime,\n\t\tMtime:       newFolder.Mtime,\n\t\tOldPath:     oldFolder.Path,\n\t\tOldPathHash: oldFolder.PathHash,\n\t}).WithVault(params.Vault), true, dto.FolderSyncRename)\n\n}\n"
  },
  {
    "path": "internal/routers/websocket_router/ws_note.go",
    "content": "package websocket_router\n\nimport (\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/convert\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/diff\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/logger\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\n\t\"go.uber.org/zap\"\n)\n\n// NoteWSHandler WebSocket note handler\n// NoteWSHandler WebSocket 笔记处理器\n// Uses App Container to inject dependencies\n// 使用 App Container 注入依赖\ntype NoteWSHandler struct {\n\t*WSHandler\n}\n\n// NewNoteWSHandler creates NoteWSHandler instance\n// NewNoteWSHandler 创建 NoteWSHandler 实例\nfunc NewNoteWSHandler(a *app.App) *NoteWSHandler {\n\treturn &NoteWSHandler{\n\t\tWSHandler: NewWSHandler(a),\n\t}\n}\n\n// NoteModify handles WebSocket messages for file modification\n// 函数名: NoteModify\n// Function name: NoteModify\n// usage: Handles note modification or creation messages sent by clients, performs parameter validation, update checks, and writes back to the database or notifies other clients when necessary.\n// 函数使用说明: 处理客户端发送的笔记修改或创建消息，进行参数校验、更新检查并在需要时写回数据库或通知其他客户端。\n// Parameters:\n//   - c *pkgapp.WebsocketClient: Current WebSocket client connection, including context, user info, response sending capability, etc.\n//\n// 参数说明:\n//   - c *pkgapp.WebsocketClient: 当前 WebSocket 客户端连接，包含上下文、用户信息、发送响应等能力。\n//   - msg *pkgapp.WebSocketMessage: Received WebSocket message, containing message data and type.\n//\n// 参数说明:\n//   - msg *pkgapp.WebSocketMessage: 接收到的 WebSocket 消息，包含消息数据和类型.\n//\n// Return:\n//   - None\n//\n// 返回值说明:\n//   - 无\nfunc (h *NoteWSHandler) NoteModify(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.NoteModifyOrCreateRequest{}\n\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.note.NoteModify.BindAndValid\")\n\t\treturn\n\t}\n\tif params.PathHash == \"\" {\n\t\tc.ToResponse(code.ErrorInvalidParams.WithDetails(\"pathHash is required\"))\n\t\treturn\n\t}\n\tif params.ContentHash == \"\" {\n\t\tc.ToResponse(code.ErrorInvalidParams.WithDetails(\"contentHash is required\"))\n\t\treturn\n\t}\n\tif params.Mtime == 0 {\n\t\tc.ToResponse(code.ErrorInvalidParams.WithDetails(\"mtime is required\"))\n\t\treturn\n\t}\n\tif params.Ctime == 0 {\n\t\tc.ToResponse(code.ErrorInvalidParams.WithDetails(\"ctime is required\"))\n\t\treturn\n\t}\n\n\tpkgapp.NoteModifyLog(c.TraceID, c.User.UID, \"NoteModify\", params.Path, params.Vault)\n\n\tctx := c.Context()\n\n\tnoteSvc := h.App.GetNoteService(c.ClientType, c.ClientName, c.ClientVersion)\n\n\t// Check and create vault, internally uses SF to merge concurrent requests, avoiding duplicate creation issues\n\t// 检查并创建仓库，内部使用SF合并并发请求, 避免重复创建问题\n\th.App.VaultService.GetOrCreate(ctx, c.User.UID, params.Vault)\n\n\tcheckParams := convert.StructAssign(params, &dto.NoteUpdateCheckRequest{}).(*dto.NoteUpdateCheckRequest)\n\tupdateMode, nodeCheck, err := noteSvc.UpdateCheck(ctx, c.User.UID, checkParams)\n\n\tif err != nil {\n\t\th.respondError(c, code.ErrorNoteModifyOrCreateFailed, err, \"websocket_router.note.NoteModify.UpdateCheck\")\n\t\treturn\n\t}\n\n\tswitch updateMode {\n\tcase \"UpdateContent\", \"Create\":\n\n\t\tvar isExcludeSelf bool = true\n\n\t\t// Perform conflict detection when a note with the same name exists on the server\n\t\t// 当服务器存在同名笔记时，进行冲突检测\n\t\tif nodeCheck != nil {\n\t\t\tserverHash := nodeCheck.ContentHash\n\t\t\tbaseHash := params.BaseHash\n\t\t\tcontentHash := params.ContentHash\n\n\t\t\t// Skip update and return success (no update) to client when content hasn't changed\n\t\t\t// 当内容未变化时，跳过更新，给客户端返回成功(无更新)\n\t\t\tif serverHash == contentHash {\n\n\t\t\t\th.App.Logger().Debug(\"server content equals client content, skipping update\",\n\t\t\t\t\tzap.String(logger.FieldTraceID, c.TraceID),\n\t\t\t\t\tzap.Int64(logger.FieldUID, c.User.UID),\n\t\t\t\t\tzap.String(logger.FieldPath, params.Path),\n\t\t\t\t\tzap.String(\"contentHash\", contentHash))\n\t\t\t\t// 内容已存在，仍需发 NoteModifyAck 以便客户端消费 pendingNoteModifies，避免无限重传\n\t\t\t\t// Content already exists; still send NoteModifyAck so client can consume pendingNoteModifies and avoid infinite re-upload\n\t\t\t\tc.ToResponse(code.Success.WithData(dto.NoteModifyAckMessage{\n\t\t\t\t\tLastTime: nodeCheck.UpdatedTimestamp,\n\t\t\t\t\tPath:     params.Path,\n\t\t\t\t\tPathHash: params.PathHash,\n\t\t\t\t}), string(dto.NoteModifyAck))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tc.DiffMergePathsMu.RLock()\n\t\t\t_, mergeIsNeed := c.DiffMergePaths[params.Path]\n\t\t\tc.DiffMergePathsMu.RUnlock()\n\n\t\t\tif mergeIsNeed {\n\t\t\t\tc.DiffMergePathsMu.Lock()\n\t\t\t\tdelete(c.DiffMergePaths, params.Path)\n\t\t\t\tc.DiffMergePathsMu.Unlock()\n\n\t\t\t\t// Skip merge and use client to override server directly if no offline sync strategy is set\n\t\t\t\t// 没有设置离线同步策略时，跳过合并，直接使用客户端覆盖服务端\n\t\t\t\tif c.OfflineSyncStrategy == \"\" {\n\t\t\t\t\th.App.Logger().Debug(\"no offline sync strategy, skipping merge, using client to override server\",\n\t\t\t\t\t\tzap.String(logger.FieldTraceID, c.TraceID),\n\t\t\t\t\t\tzap.Int64(logger.FieldUID, c.User.UID),\n\t\t\t\t\t\tzap.String(logger.FieldPath, params.Path))\n\n\t\t\t\t\tc.DiffMergePathsMu.Lock()\n\t\t\t\t\tdelete(c.DiffMergePaths, params.Path)\n\t\t\t\t\tc.DiffMergePathsMu.Unlock()\n\n\t\t\t\t\t// Skip merge and use client to override server directly when server version is found to be an ancestor of client version\n\t\t\t\t\t// 当发现服务器版本是客户端版本的前身时，跳过合并，直接使用客户端覆盖服务端\n\t\t\t\t} else if serverHash == baseHash {\n\t\t\t\t\th.App.Logger().Debug(\"server version is client version's ancestor, skipping merge, using client to override server\",\n\t\t\t\t\t\tzap.String(logger.FieldTraceID, c.TraceID),\n\t\t\t\t\t\tzap.Int64(logger.FieldUID, c.User.UID),\n\t\t\t\t\t\tzap.String(logger.FieldPath, params.Path),\n\t\t\t\t\t\tzap.String(\"baseHash\", baseHash))\n\n\t\t\t\t\t// Perform merge operation\n\t\t\t\t\t// 执行合并操作\n\t\t\t\t\t// case 1: baseHash is empty, client side creates new note, note with same name exists on server\n\t\t\t\t\t// case 1: baseHash 为空时，插件端 新创建笔记, 服务端存在同名笔记\n\t\t\t\t\t// case 2: baseHash is not empty, client side note and server side note have same base source, server side modification time is later than client side\n\t\t\t\t\t// case 2: baseHash 不为空时，插件端 笔记 和 服务端笔记 同一base源 , 服务端笔记版修改时间大于插件端\n\t\t\t\t\t// case 3: baseHash is not empty, client side note and server side note have same base source, server side modification time is earlier than client side\n\t\t\t\t\t// case 3: baseHash 不为空时，插件端 笔记 和 服务端笔记 同一base源 , 服务端笔记版修改时间小于插件端\n\t\t\t\t\t// case 4: baseHash is not empty, client side note and server side note from different base source, server side modification time is later than client side\n\t\t\t\t\t// case 4: baseHash 不为空时，插件端 笔记 和 服务端笔记 不同base源, 服务端笔记版修改时间大于插件端\n\t\t\t\t\t// case 5: baseHash is not empty, client side note and server side note from different base source, server side modification time is earlier than client side\n\t\t\t\t\t// case 5: baseHash 不为空时，插件端 笔记 和 服务端笔记 不同base源, 服务端笔记版修改时间小于插件端\n\t\t\t\t\t// Question 1: Some edited content matches server note snapshot but time dimension differs, they should not be identified as the same version\n\t\t\t\t\t// 问题1. 某编辑内容和服务器笔记快照一致 但是时间维度不一致 不应该将他们识别为同一版本\n\t\t\t\t\t// Question 2: Because historical snapshots are only generated every 30s... client side basehash has a high probability of not finding basehash, and we can't generate a snapshot for every change... too wasteful.\n\t\t\t\t\t// 问题2. 因为历史快照是 30s 才生成一份.. 导致插件端的basehash 有很大概率找不到 basehash, 又不能每次变更都生成一个快照.. 太浪费了\n\t\t\t\t} else {\n\n\t\t\t\t\th.App.Logger().Info(\"potential merge conflict detected\",\n\t\t\t\t\t\tzap.String(logger.FieldTraceID, c.TraceID),\n\t\t\t\t\t\tzap.Int64(logger.FieldUID, c.User.UID),\n\t\t\t\t\t\tzap.String(logger.FieldPath, params.Path),\n\t\t\t\t\t\tzap.String(\"serverHash\", serverHash),\n\t\t\t\t\t\tzap.String(\"baseHash\", baseHash),\n\t\t\t\t\t\tzap.String(\"contentHash\", contentHash),\n\t\t\t\t\t\tzap.String(\"offlineSyncStrategy\", c.OfflineSyncStrategy))\n\n\t\t\t\t\t// If it's a diff merge, perform merge logic\n\t\t\t\t\t// Note: Logic to skip merge based on contentHash matching historical snapshot has been removed\n\t\t\t\t\t// Reason: This logic caused valid user modifications to be silently discarded (when content happened to be same as some historical snapshot)\n\t\t\t\t\t// 如果是 diff 合并，需要执行合并逻辑\n\t\t\t\t\t// 注意：已移除基于 contentHash 匹配历史快照跳过合并的逻辑\n\t\t\t\t\t// 原因：该逻辑会导致用户有效修改被静默丢弃（当内容恰好与某个历史快照相同时）\n\n\t\t\t\t\tvar baseContent string\n\t\t\t\t\tvar baseHashNotFound bool\n\n\t\t\t\t\t// Find merge base version\n\t\t\t\t\t// When baseHash is valid and different from contentHash, try to find it in history\n\t\t\t\t\t// 查找合并基准版本\n\t\t\t\t\t// 当 baseHash 有效且与 contentHash 不同时，尝试从历史记录中查找\n\t\t\t\t\tif !params.BaseHashMissing {\n\t\t\t\t\t\tnoteHistory, err := h.App.NoteHistoryService.GetByNoteIDAndHash(ctx, c.User.UID, nodeCheck.ID, baseHash)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\th.respondError(c, code.ErrorNoteModifyOrCreateFailed, err, \"websocket_router.note.NoteModify.GetByNoteIDAndHash\")\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif noteHistory != nil {\n\t\t\t\t\t\t\tbaseContent = noteHistory.Content\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// History record not found\n\t\t\t\t\t\t\t// 历史记录未找到\n\t\t\t\t\t\t\th.App.Logger().Warn(\"history record not found for baseHash\",\n\t\t\t\t\t\t\t\tzap.String(logger.FieldTraceID, c.TraceID),\n\t\t\t\t\t\t\t\tzap.Int64(logger.FieldUID, c.User.UID),\n\t\t\t\t\t\t\t\tzap.String(logger.FieldPath, params.Path),\n\t\t\t\t\t\t\t\tzap.String(\"baseHash\", baseHash))\n\t\t\t\t\t\t\tbaseHashNotFound = true\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// baseHash is empty or client marked as unavailable\n\t\t\t\t\t\t// baseHash 为空或客户端标记为不可用\n\t\t\t\t\t\tif baseHash == \"\" || params.BaseHashMissing {\n\t\t\t\t\t\t\th.App.Logger().Warn(\"baseHash is empty or missing\",\n\t\t\t\t\t\t\t\tzap.String(logger.FieldTraceID, c.TraceID),\n\t\t\t\t\t\t\t\tzap.Int64(logger.FieldUID, c.User.UID),\n\t\t\t\t\t\t\t\tzap.String(logger.FieldPath, params.Path),\n\t\t\t\t\t\t\t\tzap.Bool(\"baseHashMissing\", params.BaseHashMissing))\n\t\t\t\t\t\t\tbaseHashNotFound = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// When baseHash is not found, use server current content as base and continue merging\n\t\t\t\t\t// This usually happens when: another device goes online to sync during the delayed historical record creation (20s)\n\t\t\t\t\t// Using server content as base correctly merges in most scenarios\n\t\t\t\t\t// 当 baseHash 找不到时，使用服务端当前内容作为 base 继续合并\n\t\t\t\t\t// 这种情况通常发生在：历史记录延迟创建（20秒）期间另一设备上线同步\n\t\t\t\t\t// 使用服务端内容作为 base 在大多数场景下能正确合并\n\t\t\t\t\tif baseHashNotFound {\n\t\t\t\t\t\th.App.Logger().Warn(\"baseHash not found, using server content as merge base\",\n\t\t\t\t\t\t\tzap.String(logger.FieldTraceID, c.TraceID),\n\t\t\t\t\t\t\tzap.Int64(logger.FieldUID, c.User.UID),\n\t\t\t\t\t\t\tzap.String(logger.FieldPath, params.Path),\n\t\t\t\t\t\t\tzap.String(\"baseHash\", baseHash),\n\t\t\t\t\t\t\tzap.Bool(\"baseHashMissing\", params.BaseHashMissing))\n\t\t\t\t\t\tbaseContent = nodeCheck.Content\n\t\t\t\t\t}\n\n\t\t\t\t\tclientContent := params.Content\n\t\t\t\t\tserverContent := nodeCheck.Content\n\n\t\t\t\t\t// Determine patch application order\n\t\t\t\t\t// ignoreTimeMerge strategy: ignore timestamp, fixed use client priority\n\t\t\t\t\t// When both sides modify different areas, result is consistent (patch application order doesn't affect)\n\t\t\t\t\t// When both sides modify same area, hasConflict will detect conflict and create conflict file\n\t\t\t\t\t// 确定 patch 应用顺序\n\t\t\t\t\t// ignoreTimeMerge 策略：忽略时间戳，固定使用客户端优先\n\t\t\t\t\t// 当两边修改不同区域时，结果一致（patch 应用顺序不影响）\n\t\t\t\t\t// 当两边修改同一区域时，hasConflict 会检测到冲突并创建冲突文件\n\t\t\t\t\tvar pc1First bool\n\t\t\t\t\tif c.OfflineSyncStrategy == \"ignoreTimeMerge\" {\n\t\t\t\t\t\tpc1First = true\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Other strategies: use time to determine priority\n\t\t\t\t\t\t// 其他策略：使用时间决定优先级\n\t\t\t\t\t\tpc1First = params.Mtime <= nodeCheck.Mtime\n\t\t\t\t\t}\n\n\t\t\t\t\tvar mergeResult diff.MergeResult\n\t\t\t\t\tif !baseHashNotFound {\n\t\t\t\t\t\t// Use text merge with conflict detection\n\t\t\t\t\t\t// 使用带冲突检测的文本检测\n\t\t\t\t\t\tmergeResult, err = diff.MergeTexts(baseContent, clientContent, serverContent, pc1First)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\th.respondError(c, code.ErrorNoteModifyOrCreateFailed, err, \"websocket_router.note.NoteModify.MergeTexts\")\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\th.App.Logger().Info(\"merge completed\",\n\t\t\t\t\t\t\tzap.String(logger.FieldTraceID, c.TraceID),\n\t\t\t\t\t\t\tzap.Int64(logger.FieldUID, c.User.UID),\n\t\t\t\t\t\t\tzap.String(logger.FieldPath, params.Path),\n\t\t\t\t\t\t\tzap.Bool(\"hasConflict\", mergeResult.HasConflict),\n\t\t\t\t\t\t\tzap.Int(\"baseLen\", len(baseContent)),\n\t\t\t\t\t\t\tzap.Int(\"clientLen\", len(clientContent)),\n\t\t\t\t\t\t\tzap.Int(\"serverLen\", len(serverContent)),\n\t\t\t\t\t\t\tzap.Int(\"resultLen\", len(mergeResult.Content)),\n\t\t\t\t\t\t\tzap.Bool(\"pc1First\", pc1First))\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check if conflict exists, perform further merge operations\n\t\t\t\t\t// 检查是否存在冲突， 执行进一步合并操作\n\t\t\t\t\tif mergeResult.HasConflict || baseHashNotFound {\n\n\t\t\t\t\t\t// Notify user of merge conflict, need to handle redundant note content\n\t\t\t\t\t\t// 通知用户出现合并冲突, 需要处理笔记冗余内容\n\t\t\t\t\t\t// todo 先暂时不通知用户出现冲突\n\t\t\t\t\t\t// c.ToResponse(code.ErrorSyncConflict.WithData(dto.NoteSyncNeedPushMessage{\n\t\t\t\t\t\t// \tPath:     params.Path,\n\t\t\t\t\t\t// \tPathHash: params.PathHash,\n\t\t\t\t\t\t// }), dto.NoteSyncNeedPush)\n\n\t\t\t\t\t\t// Force merge to keep all text from PC1 and PC2\n\t\t\t\t\t\t// 强制合并 保留PC1 PC2全部文本\n\t\t\t\t\t\tmergeResult.Content, err = diff.MergeTextsIgnoreConflictIgnoreDelete(baseContent, clientContent, serverContent, pc1First)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\th.respondError(c, code.ErrorNoteModifyOrCreateFailed, err, \"websocket_router.note.NoteModify.MergeTextsIgnoreConflictIgnoreDelete\")\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// // 创建冲突文件保存客户端内容\n\t\t\t\t\t\t// conflictReq := &dto.ConflictFileRequest{\n\t\t\t\t\t\t// \tVault:             params.Vault,\n\t\t\t\t\t\t// \tOriginalPath:      params.Path,\n\t\t\t\t\t\t// \tClientContent:     params.Content,\n\t\t\t\t\t\t// \tClientContentHash: params.ContentHash,\n\t\t\t\t\t\t// \tCtime:             params.Ctime,\n\t\t\t\t\t\t// \tMtime:             params.Mtime,\n\t\t\t\t\t\t// }\n\n\t\t\t\t\t\t// conflictResp, err := h.App.ConflictService.CreateConflictFile(ctx, c.User.UID, conflictReq)\n\t\t\t\t\t\t// if err != nil {\n\t\t\t\t\t\t// \th.App.Logger().Error(\"failed to create conflict file\",\n\t\t\t\t\t\t// \t\tzap.String(logger.FieldTraceID, c.TraceID),\n\t\t\t\t\t\t// \t\tzap.Int64(logger.FieldUID, c.User.UID),\n\t\t\t\t\t\t// \t\tzap.String(logger.FieldPath, params.Path),\n\t\t\t\t\t\t// \t\tzap.Error(err))\n\t\t\t\t\t\t// \th.respondError(c, code.ErrorNoteModifyOrCreateFailed, err, \"websocket_router.note.NoteModify.CreateConflictFile\")\n\t\t\t\t\t\t// \treturn\n\t\t\t\t\t\t// }\n\n\t\t\t\t\t\t// h.App.Logger().Info(\"merge conflict detected, conflict file created\",\n\t\t\t\t\t\t// \tzap.String(logger.FieldTraceID, c.TraceID),\n\t\t\t\t\t\t// \tzap.Int64(logger.FieldUID, c.User.UID),\n\t\t\t\t\t\t// \tzap.String(logger.FieldPath, params.Path),\n\t\t\t\t\t\t// \tzap.String(\"conflictPath\", conflictResp.ConflictPath),\n\t\t\t\t\t\t// \tzap.String(\"conflictInfo\", mergeResult.ConflictInfo))\n\n\t\t\t\t\t\t// // 返回冲突文件创建成功的响应\n\t\t\t\t\t\t// c.ToResponse(code.ErrorConflictFileCreated.WithData(conflictResp))\n\t\t\t\t\t\t// return\n\t\t\t\t\t}\n\n\t\t\t\t\tparams.Content = mergeResult.Content\n\t\t\t\t\tparams.ContentHash = util.EncodeHash32(params.Content)\n\t\t\t\t\tparams.Mtime = timex.Now().UnixMilli()\n\n\t\t\t\t\tisExcludeSelf = false\n\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t\t_, note, err := noteSvc.ModifyOrCreate(ctx, c.User.UID, params, true)\n\t\tif err != nil {\n\t\t\th.respondError(c, code.ErrorNoteModifyOrCreateFailed, err, \"websocket_router.note.NoteModify.ModifyOrCreate\")\n\t\t\treturn\n\t\t}\n\n\t\t// 通知发送方上传已确认，携带 lastTime 和 path 供客户端更新 hashManager\n\t\t// Notify sender of successful write with lastTime and path for client hashManager update\n\t\tc.ToResponse(code.Success.WithData(dto.NoteModifyAckMessage{\n\t\t\tLastTime: note.UpdatedTimestamp,\n\t\t\tPath:     note.Path,\n\t\t\tPathHash: note.PathHash,\n\t\t}), string(dto.NoteModifyAck))\n\t\tc.BroadcastResponse(code.Success.WithData(\n\t\t\tdto.NoteSyncModifyMessage{\n\t\t\t\tPath:             note.Path,\n\t\t\t\tPathHash:         note.PathHash,\n\t\t\t\tContent:          note.Content,\n\t\t\t\tContentHash:      note.ContentHash,\n\t\t\t\tCtime:            note.Ctime,\n\t\t\t\tMtime:            note.Mtime,\n\t\t\t\tUpdatedTimestamp: note.UpdatedTimestamp,\n\t\t\t},\n\t\t).WithVault(params.Vault), isExcludeSelf, dto.NoteSyncModify)\n\t\treturn\n\n\tcase \"UpdateMtime\":\n\t\t// Notify client of note modification time update\n\t\t// 通知 客户端 Note 修改时间更新\n\t\tc.ToResponse(code.Success.WithData(\n\t\t\tdto.NoteSyncMtimeMessage{\n\t\t\t\tPath:             nodeCheck.Path,\n\t\t\t\tCtime:            nodeCheck.Ctime,\n\t\t\t\tMtime:            nodeCheck.Mtime,\n\t\t\t\tUpdatedTimestamp: nodeCheck.UpdatedTimestamp,\n\t\t\t},\n\t\t).WithVault(params.Vault), dto.NoteSyncMtime)\n\t\treturn\n\tdefault:\n\t\t// SuccessNoUpdate 场景也需发 NoteModifyAck，避免客户端 pendingNoteModifies 条目泄漏导致无限重传\n\t\t// SuccessNoUpdate also needs NoteModifyAck to prevent client pendingNoteModifies leak causing infinite re-upload\n\t\tif nodeCheck != nil {\n\t\t\tc.ToResponse(code.Success.WithData(dto.NoteModifyAckMessage{\n\t\t\t\tLastTime: nodeCheck.UpdatedTimestamp,\n\t\t\t\tPath:     params.Path,\n\t\t\t\tPathHash: params.PathHash,\n\t\t\t}), string(dto.NoteModifyAck))\n\t\t} else {\n\t\t\tc.ToResponse(code.SuccessNoUpdate)\n\t\t}\n\t\treturn\n\t}\n}\n\n// NoteModifyCheck checks the necessity of file modification\n// 函数名: NoteModifyCheck\n// Function name: NoteModifyCheck\n// usage: Only used to check difference between note status provided by client and server status, deciding if client needs to upload note or just sync mtime.\n// 函数使用说明: 仅用于检查客户端提供的笔记状态与服务器状态的差异，决定客户端是否需要上传笔记或只需同步 mtime。\n// Parameters:\n//   - c *pkgapp.WebsocketClient: Current WebSocket client connection, including context and user info.\n//\n// 参数说明:\n//   - c *pkgapp.WebsocketClient: 当前 WebSocket 客户端连接，包含上下文和用户信息。\n//   - msg *pkgapp.WebSocketMessage: Received message, containing note info needing check.\n//\n// 参数说明:\n//   - msg *pkgapp.WebSocketMessage: 接收到的消息，包含需要检查的笔记信息。\n//\n// Return:\n//   - None\n//\n// 返回值说明:\n//   - 无\nfunc (h *NoteWSHandler) NoteModifyCheck(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\n\tparams := &dto.NoteUpdateCheckRequest{}\n\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.note.NoteModifyCheck.BindAndValid\")\n\t\treturn\n\t}\n\n\tctx := c.Context()\n\n\tnoteSvc := h.App.GetNoteService(c.ClientType, c.ClientName, c.ClientVersion)\n\n\tpkgapp.NoteModifyLog(c.TraceID, c.User.UID, \"NoteModifyCheck\", params.Path, params.Vault)\n\n\t// Check and create vault, internally uses SF to merge concurrent requests, avoiding duplicate creation issues\n\t// 检查并创建仓库，内部使用SF合并并发请求, 避免重复创建问题\n\th.App.VaultService.GetOrCreate(ctx, c.User.UID, params.Vault)\n\n\tupdateMode, nodeCheck, err := noteSvc.UpdateCheck(ctx, c.User.UID, params)\n\n\tif err != nil {\n\t\th.respondError(c, code.ErrorNoteUpdateCheckFailed, err, \"websocket_router.note.NoteModifyCheck.UpdateCheck\")\n\t\treturn\n\t}\n\n\t// Notify client to upload note\n\t// 通知客户端上传笔记\n\tswitch updateMode {\n\tcase \"UpdateContent\", \"Create\":\n\t\tc.ToResponse(code.Success.WithData(\n\t\t\tdto.NoteSyncNeedPushMessage{\n\t\t\t\tPath:     nodeCheck.Path,\n\t\t\t\tPathHash: nodeCheck.PathHash,\n\t\t\t},\n\t\t), dto.NoteSyncNeedPush)\n\t\treturn\n\tcase \"UpdateMtime\":\n\t\t// Force client to update mtime without transferring note content\n\t\t// 强制客户端更新mtime 不传输笔记内容\n\t\tc.ToResponse(code.Success.WithData(\n\t\t\tdto.NoteSyncMtimeMessage{\n\t\t\t\tPath:             nodeCheck.Path,\n\t\t\t\tCtime:            nodeCheck.Ctime,\n\t\t\t\tMtime:            nodeCheck.Mtime,\n\t\t\t\tUpdatedTimestamp: nodeCheck.UpdatedTimestamp,\n\t\t\t},\n\t\t), dto.NoteSyncMtime)\n\t\treturn\n\tdefault:\n\t\tc.ToResponse(code.SuccessNoUpdate)\n\t\treturn\n\t}\n}\n\n// NoteDelete handles WebSocket messages for file deletion\n// 函数名: NoteDelete\n// Function name: NoteDelete\n// usage: Receives client note deletion request, performs deletion, and notifies other clients to sync deletion events.\n// 函数使用说明: 接收客户端的笔记删除请求，执行删除操作并通知其他客户端同步删除事件。\n// Parameters:\n//   - c *pkgapp.WebsocketClient: Current WebSocket client connection, including response sending and broadcasting capabilities.\n//\n// 参数说明:\n//   - c *pkgapp.WebsocketClient: 当前 WebSocket 客户端连接，包含发送响应与广播能力。\n//   - msg *pkgapp.WebSocketMessage: Received deletion request message, containing parameters like note identifier to delete.\n//\n// 参数说明:\n//   - msg *pkgapp.WebSocketMessage: 接收到的删除请求消息，包含要删除的笔记标识等参数。\n//\n// Return:\n//   - None\n//\n// 返回值说明:\n//   - 无\nfunc (h *NoteWSHandler) NoteDelete(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.NoteDeleteRequest{}\n\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.note.NoteDelete.BindAndValid\")\n\t\treturn\n\t}\n\n\tpkgapp.NoteModifyLog(c.TraceID, c.User.UID, \"NoteDelete\", params.Path, params.Vault)\n\n\tctx := c.Context()\n\n\tnoteSvc := h.App.GetNoteService(c.ClientType, c.ClientName, c.ClientVersion)\n\n\t// Check and create vault, internally uses SF to merge concurrent requests, avoiding duplicate creation issues\n\t// 检查并创建仓库，内部使用SF合并并发请求, 避免重复创建问题\n\th.App.VaultService.GetOrCreate(ctx, c.User.UID, params.Vault)\n\n\tnote, err := noteSvc.Delete(ctx, c.User.UID, params)\n\n\tif err != nil {\n\t\th.respondError(c, code.ErrorNoteDeleteFailed, err, \"websocket_router.note.handleNoteDelete.Delete\")\n\t\treturn\n\t}\n\n\tc.ToResponse(code.Success.WithData(dto.NoteDeleteAckMessage{\n\t\tLastTime: note.UpdatedTimestamp,\n\t\tPath:     note.Path,\n\t\tPathHash: note.PathHash,\n\t}), string(dto.NoteDeleteAck))\n\tc.BroadcastResponse(code.Success.WithData(\n\t\tdto.NoteSyncDeleteMessage{\n\t\t\tPath:             note.Path,\n\t\t\tPathHash:         note.PathHash,\n\t\t\tCtime:            note.Ctime,\n\t\t\tMtime:            note.Mtime,\n\t\t\tSize:             note.Size,\n\t\t\tUpdatedTimestamp: note.UpdatedTimestamp,\n\t\t},\n\t).WithVault(params.Vault), true, dto.NoteSyncDelete)\n}\n\n// NoteRename handles WebSocket messages for file renaming\n// 函数名: NoteRename\n// Function name: NoteRename\n// usage: Receives client note renaming request, performs renaming, and notifies all clients to sync old path deletion and new path creation.\n// 函数使用说明: 接收客户端的笔记重命名请求，执行重命名操作，并通知所有客户端同步删除旧路径和创建新路径。\n// Parameters:\n//   - c *pkgapp.WebsocketClient: Current WebSocket client connection.\n//\n// 参数说明:\n//   - c *pkgapp.WebsocketClient: 当前 WebSocket 客户端连接。\n//   - msg *pkgapp.WebSocketMessage: Received renaming request message.\n//\n// 参数说明:\n//   - msg *pkgapp.WebSocketMessage: 接收到的重命名请求消息。\n//\n// Return:\n//   - None\n//\n// 返回值说明:\n//   - 无\nfunc (h *NoteWSHandler) NoteRename(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.NoteRenameRequest{}\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.note.NoteRename.BindAndValid\")\n\t\treturn\n\t}\n\n\tpkgapp.NoteModifyLog(c.TraceID, c.User.UID, \"NoteRename\", params.Path, params.Vault)\n\n\tuid := c.User.UID\n\toldNote, newNote, err := h.App.GetNoteService(c.ClientType, c.ClientName, c.ClientVersion).Rename(c.Context(), uid, params)\n\tif err != nil {\n\t\th.respondError(c, code.ErrorRenameNoteTargetExist, err, \"websocket_router.note.NoteRename.Rename\")\n\t\treturn\n\t}\n\n\t// 通知发送方重命名已确认，携带 lastTime 供客户端 FIFO 队列更新 hashManager\n\t// Notify sender of successful rename with lastTime for client FIFO queue hashManager update\n\tc.ToResponse(code.Success.WithData(dto.NoteRenameAckMessage{\n\t\tLastTime: newNote.UpdatedTimestamp,\n\t\tPath:     newNote.Path,\n\t\tPathHash: newNote.PathHash,\n\t}), string(dto.NoteRenameAck))\n\tc.BroadcastResponse(code.Success.WithData(\n\t\tdto.NoteSyncRenameMessage{\n\t\t\tPath:             newNote.Path,\n\t\t\tPathHash:         newNote.PathHash,\n\t\t\tContentHash:      newNote.ContentHash,\n\t\t\tCtime:            newNote.Ctime,\n\t\t\tMtime:            newNote.Mtime,\n\t\t\tSize:             newNote.Size,\n\t\t\tUpdatedTimestamp: newNote.UpdatedTimestamp,\n\t\t\tOldPath:          oldNote.Path,\n\t\t\tOldPathHash:      oldNote.PathHash,\n\t\t},\n\t).WithVault(params.Vault), true, dto.NoteSyncRename)\n}\n\nfunc (h *NoteWSHandler) NoteRePush(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.NoteGetRequest{}\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.note.NoteReceiveMissing.BindAndValid\")\n\t\treturn\n\t}\n\n\tpkgapp.NoteModifyLog(c.TraceID, c.User.UID, \"NoteRePush\", params.Path, params.Vault)\n\n\tuid := c.User.UID\n\tnote, err := h.App.GetNoteService(c.ClientType, c.ClientName, c.ClientVersion).Get(c.Context(), uid, params)\n\tif err != nil {\n\t\th.respondError(c, code.ErrorNoteNotFound, err, \"websocket_router.note.NoteRePush.Get\")\n\t\treturn\n\t}\n\n\tif note != nil && note.Action != \"delete\" {\n\t\tc.ToResponse(code.Success.WithData(\n\t\t\tdto.NoteSyncModifyMessage{\n\t\t\t\tPath:             note.Path,\n\t\t\t\tPathHash:         note.PathHash,\n\t\t\t\tContent:          note.Content,\n\t\t\t\tContentHash:      note.ContentHash,\n\t\t\t\tCtime:            note.Ctime,\n\t\t\t\tMtime:            note.Mtime,\n\t\t\t\tUpdatedTimestamp: note.UpdatedTimestamp,\n\t\t\t},\n\t\t).WithVault(params.Vault), dto.NoteSyncModify)\n\t} else {\n\t\tc.ToResponse(code.ErrorNoteNotFound)\n\t}\n\n}\n\n// NoteSync handles full or incremental note sync\n// 函数名: NoteSync\n// Function name: NoteSync\n// usage: Compares local note list provided by client with server side recent update list, deciding which notes need uploading, mtime sync, deletion or update; finally returns sync end message.\n// 函数使用说明: 根据客户端提供的本地笔记列表与服务器端最近更新列表比较，决定返回哪些笔记需要上传、需要同步 mtime、需要删除或需要更新；最后返回同步结束消息。\n// Parameters:\n//   - c *pkgapp.WebsocketClient: Current WebSocket client connection, including context and response sending capability.\n//\n// 参数说明:\n//   - c *pkgapp.WebsocketClient: 当前 WebSocket 客户端连接，包含上下文与响应发送能力。\n//   - msg *pkgapp.WebSocketMessage: Received sync request, containing client note digest and sync start time, etc.\n//\n// 参数说明:\n//   - msg *pkgapp.WebSocketMessage: 接收到的同步请求，包含客户端的笔记摘要和同步起始时间等信息。\n//\n// Return:\n//   - None\n//\n// 返回值说明:\n//   - 无\nfunc (h *NoteWSHandler) NoteSync(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.NoteSyncRequest{}\n\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.note.NoteSync.BindAndValid\")\n\t\treturn\n\t}\n\n\tctx := c.Context()\n\n\tnoteSvc := h.App.GetNoteService(c.ClientType, c.ClientName, c.ClientVersion)\n\n\tpkgapp.NoteModifyLog(c.TraceID, c.User.UID, \"NoteSync\", \"\", params.Vault)\n\n\t// Check and create vault, internally uses SF to merge concurrent requests, avoiding duplicate creation issues\n\t// 检查并创建仓库，内部使用SF合并并发请求, 避免重复创建问题\n\th.App.VaultService.GetOrCreate(ctx, c.User.UID, params.Vault)\n\n\tlist, err := noteSvc.ListByLastTime(ctx, c.User.UID, params)\n\n\tif err != nil {\n\t\th.respondError(c, code.ErrorNoteListFailed, err, \"websocket_router.note.NoteSync.ListByLastTime\")\n\t\treturn\n\t}\n\n\tvar cNotes map[string]dto.NoteSyncCheckRequest = make(map[string]dto.NoteSyncCheckRequest, 0)\n\tvar cNotesKeys map[string]struct{} = make(map[string]struct{}, 0)\n\n\tif len(params.Notes) > 0 {\n\t\tfor _, note := range params.Notes {\n\t\t\tcNotes[note.PathHash] = note\n\t\t\tcNotesKeys[note.PathHash] = struct{}{}\n\t\t}\n\t}\n\n\t// Create message queue for collecting all messages to be sent\n\t// 创建消息队列，用于收集所有待发送的消息\n\tvar messageQueue []dto.WSQueuedMessage\n\n\tvar lastTime int64\n\tvar needUploadCount int64\n\tvar needModifyCount int64\n\tvar needSyncMtimeCount int64\n\tvar needDeleteCount int64\n\n\tvar cDelNotesKeys map[string]struct{} = make(map[string]struct{}, 0)\n\n\t// Handle notes deleted by client\n\t// 处理客户端删除的笔记\n\tif len(params.DelNotes) > 0 {\n\t\tfor _, delNote := range params.DelNotes {\n\t\t\t// Check if note exists before deleting\n\t\t\t// 删除前检查笔记是否存在\n\t\t\tgetCheckParams := &dto.NoteGetRequest{\n\t\t\t\tVault:    params.Vault,\n\t\t\t\tPathHash: delNote.PathHash,\n\t\t\t}\n\t\t\tcheckNote, err := noteSvc.Get(ctx, c.User.UID, getCheckParams)\n\n\t\t\t// If note exists, execute delete\n\t\t\t// 如果笔记存在，执行删除\n\t\t\tif err == nil && checkNote != nil && checkNote.Action != \"delete\" {\n\t\t\t\tdelParams := &dto.NoteDeleteRequest{\n\t\t\t\t\tVault:    params.Vault,\n\t\t\t\t\tPath:     delNote.Path,\n\t\t\t\t\tPathHash: delNote.PathHash,\n\t\t\t\t}\n\t\t\t\tnote, err := noteSvc.Delete(ctx, c.User.UID, delParams)\n\t\t\t\tif err != nil {\n\t\t\t\t\th.App.Logger().Error(\"websocket_router.note.NoteSync.noteSvc.Delete\",\n\t\t\t\t\t\tzap.String(logger.FieldTraceID, c.TraceID),\n\t\t\t\t\t\tzap.Int64(logger.FieldUID, c.User.UID),\n\t\t\t\t\t\tzap.String(logger.FieldPath, delNote.Path),\n\t\t\t\t\t\tzap.Error(err))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// Record PathHash deleted by client to avoid duplicate sending\n\t\t\t\t// 记录客户端已主动删除的 PathHash，避免重复下发\n\t\t\t\tcDelNotesKeys[delNote.PathHash] = struct{}{}\n\n\t\t\t\t// Broadcast deletion to other clients\n\t\t\t\t// 将删除消息广播给其他客户端\n\t\t\t\tc.BroadcastResponse(code.Success.WithData(\n\t\t\t\t\tdto.NoteSyncDeleteMessage{\n\t\t\t\t\t\tPath:     note.Path,\n\t\t\t\t\t\tPathHash: note.PathHash,\n\t\t\t\t\t\tCtime:    note.Ctime,\n\t\t\t\t\t\tMtime:    note.Mtime,\n\t\t\t\t\t\tSize:     note.Size,\n\t\t\t\t\t},\n\t\t\t\t).WithVault(params.Vault), true, dto.NoteSyncDelete)\n\n\t\t\t} else {\n\t\t\t\t// Note does not exist, but we still need to record exclusion and broadcast delete message to ensure data consistency\n\t\t\t\t// 笔记不存在，但仍需记录排除并广播删除消息，以确保数据一致性\n\n\t\t\t\th.App.Logger().Debug(\"websocket_router.note.NoteSync.noteSvc.Get check failed (not found or already deleted), broadcasting delete anyway\",\n\t\t\t\t\tzap.String(logger.FieldTraceID, c.TraceID),\n\t\t\t\t\tzap.String(\"pathHash\", delNote.PathHash))\n\n\t\t\t\t// Record PathHash\n\t\t\t\t// 记录 PathHash\n\t\t\t\tcDelNotesKeys[delNote.PathHash] = struct{}{}\n\n\t\t\t\t// Broadcast deletion with available info (Path/PathHash)\n\t\t\t\t// 使用现有信息(Path/PathHash)广播删除\n\t\t\t\tc.BroadcastResponse(code.Success.WithData(\n\t\t\t\t\tdto.NoteSyncDeleteMessage{\n\t\t\t\t\t\tPath:     delNote.Path,\n\t\t\t\t\t\tPathHash: delNote.PathHash,\n\t\t\t\t\t\tCtime:    0,\n\t\t\t\t\t\tMtime:    0,\n\t\t\t\t\t\tSize:     0,\n\t\t\t\t\t},\n\t\t\t\t).WithVault(params.Vault), true, dto.NoteSyncDelete)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Handle notes missing on client (only for incremental sync)\n\t// 处理客户端缺失的笔记（仅限增量同步）\n\tif params.LastTime > 0 && len(params.MissingNotes) > 0 {\n\t\tfor _, missingNote := range params.MissingNotes {\n\t\t\tgetParams := &dto.NoteGetRequest{\n\t\t\t\tVault:    params.Vault,\n\t\t\t\tPath:     missingNote.Path,\n\t\t\t\tPathHash: missingNote.PathHash,\n\t\t\t}\n\t\t\tnote, err := noteSvc.Get(ctx, c.User.UID, getParams)\n\t\t\tif err != nil {\n\t\t\t\th.App.Logger().Warn(\"websocket_router.note.NoteSync.noteSvc.Get\",\n\t\t\t\t\tzap.String(logger.FieldTraceID, c.TraceID),\n\t\t\t\t\tzap.Int64(logger.FieldUID, c.User.UID),\n\t\t\t\t\tzap.String(\"path\", missingNote.Path),\n\t\t\t\t\tzap.String(\"pathHash\", missingNote.PathHash),\n\t\t\t\t\tzap.Error(err))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif note != nil && note.Action != \"delete\" {\n\t\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\t\tAction: dto.NoteSyncModify,\n\t\t\t\t\tData: dto.NoteSyncModifyMessage{\n\t\t\t\t\t\tPath:             note.Path,\n\t\t\t\t\t\tPathHash:         note.PathHash,\n\t\t\t\t\t\tContent:          note.Content,\n\t\t\t\t\t\tContentHash:      note.ContentHash,\n\t\t\t\t\t\tCtime:            note.Ctime,\n\t\t\t\t\t\tMtime:            note.Mtime,\n\t\t\t\t\t\tUpdatedTimestamp: note.UpdatedTimestamp,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tneedModifyCount++\n\t\t\t\t// 加入排除索引\n\t\t\t\tcDelNotesKeys[note.PathHash] = struct{}{}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, note := range list {\n\t\t// 如果该笔记是客户端刚才通过参数告知删除的，则跳过下发\n\t\tif _, ok := cDelNotesKeys[note.PathHash]; ok {\n\t\t\tcontinue\n\t\t}\n\n\t\t// lastTime is set after the loop via timex.Now(), do not update here\n\t\t// lastTime 在循环后统一由 timex.Now() 赋值，此处不更新\n\t\tif note.Action == \"delete\" {\n\t\t\t// Server already deleted, notify client to delete (regardless of whether client has it)\n\t\t\t// 服务端已经删除, 通知客户端删除（不再检查客户端是否存在）\n\t\t\tif _, ok := cNotes[note.PathHash]; ok {\n\t\t\t\tdelete(cNotesKeys, note.PathHash)\n\t\t\t}\n\t\t\t// 将消息添加到队列\n\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\tAction: dto.NoteSyncDelete,\n\t\t\t\tData: dto.NoteSyncDeleteMessage{\n\t\t\t\t\tPath:             note.Path,\n\t\t\t\t\tPathHash:         note.PathHash,\n\t\t\t\t\tCtime:            note.Ctime,\n\t\t\t\t\tMtime:            note.Mtime,\n\t\t\t\t\tSize:             note.Size,\n\t\t\t\t\tUpdatedTimestamp: note.UpdatedTimestamp,\n\t\t\t\t},\n\t\t\t})\n\t\t\tneedDeleteCount++\n\t\t} else {\n\t\t\t// Check if client has it\n\t\t\t//检查客户端是否有\n\t\t\tif cNote, ok := cNotes[note.PathHash]; ok {\n\n\t\t\t\tdelete(cNotesKeys, note.PathHash)\n\n\t\t\t\tif note.ContentHash == cNote.ContentHash && note.Mtime == cNote.Mtime {\n\t\t\t\t\t// Content and modification time match, skip\n\t\t\t\t\t//内容和修改时间一致, 跳过\n\t\t\t\t\tcontinue\n\t\t\t\t} else if note.ContentHash != cNote.ContentHash {\n\t\t\t\t\t// Content inconsistent\n\t\t\t\t\t// 内容不一致\n\t\t\t\t\tif cNote.Mtime < note.Mtime {\n\n\t\t\t\t\t\tswitch c.OfflineSyncStrategy {\n\t\t\t\t\t\t// When ignore time and merge, register those needing merge, notify client to upload note\n\t\t\t\t\t\t//当忽略时间并合并时,登记需要合并的, 通知客户端上传笔记\n\t\t\t\t\t\tcase \"ignoreTimeMerge\":\n\n\t\t\t\t\t\t\tc.DiffMergePathsMu.Lock()\n\t\t\t\t\t\t\tc.DiffMergePaths[note.Path] = pkgapp.DiffMergeEntry{CreatedAt: time.Now()}\n\t\t\t\t\t\t\tc.DiffMergePathsMu.Unlock()\n\n\t\t\t\t\t\t\t// Add message to queue instead of sending immediately\n\t\t\t\t\t\t\t// 将消息添加到队列而非立即发送\n\t\t\t\t\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\t\t\t\t\tAction: dto.NoteSyncNeedPush,\n\t\t\t\t\t\t\t\tData: dto.NoteSyncNeedPushMessage{\n\t\t\t\t\t\t\t\t\tPath:     note.Path,\n\t\t\t\t\t\t\t\t\tPathHash: note.PathHash,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tneedUploadCount++\n\t\t\t\t\t\t// When only new notes are merged, since local note is older, server notifies client to override local with cloud note\n\t\t\t\t\t\t// Don't set, default override as well\n\t\t\t\t\t\t// 当设置新笔记才进行合并, 因为本地笔记比较老, 服务器通知客户端使用云端笔记覆盖本地\n\t\t\t\t\t\t// 不设置 默认也一样覆盖\n\t\t\t\t\t\tcase \"newTimeMerge\", \"\":\n\t\t\t\t\t\t\t// 将消息添加到队列而非立即发送\n\t\t\t\t\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\t\t\t\t\tAction: dto.NoteSyncModify,\n\t\t\t\t\t\t\t\tData: dto.NoteSyncModifyMessage{\n\t\t\t\t\t\t\t\t\tPath:             note.Path,\n\t\t\t\t\t\t\t\t\tPathHash:         note.PathHash,\n\t\t\t\t\t\t\t\t\tContent:          note.Content,\n\t\t\t\t\t\t\t\t\tContentHash:      note.ContentHash,\n\t\t\t\t\t\t\t\t\tCtime:            note.Ctime,\n\t\t\t\t\t\t\t\t\tMtime:            note.Mtime,\n\t\t\t\t\t\t\t\t\tUpdatedTimestamp: note.UpdatedTimestamp,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tneedModifyCount++\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Server modification time is newer than client, notify client to update note\n\t\t\t\t\t\t// 服务端修改时间比客户端新, 通知客户端更新笔记\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Client note is newer than server, notify client to upload note\n\t\t\t\t\t\t// Offline sync strategy description:\n\t\t\t\t\t\t// - ignoreTimeMerge: ignore timestamp, always execute three-way merge, need to register to DiffMergePaths\n\t\t\t\t\t\t// - newTimeMerge: new time priority, register DiffMergePaths\n\t\t\t\t\t\t// 客户端笔记 比服务端笔记新, 通知客户端上传笔记\n\t\t\t\t\t\t// 离线同步策略说明：\n\t\t\t\t\t\t// - ignoreTimeMerge: 忽略时间戳，始终执行三方合并，需要登记到 DiffMergePaths\n\t\t\t\t\t\t// - newTimeMerge: 新时间优先, 登记 DiffMergePaths\n\n\t\t\t\t\t\tif c.OfflineSyncStrategy == \"ignoreTimeMerge\" || c.OfflineSyncStrategy == \"newTimeMerge\" {\n\t\t\t\t\t\t\tc.DiffMergePathsMu.Lock()\n\t\t\t\t\t\t\tc.DiffMergePaths[note.Path] = pkgapp.DiffMergeEntry{CreatedAt: time.Now()}\n\t\t\t\t\t\t\tc.DiffMergePathsMu.Unlock()\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Add message to queue instead of sending immediately\n\t\t\t\t\t\t// 将消息添加到队列而非立即发送\n\t\t\t\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\t\t\t\tAction: dto.NoteSyncNeedPush,\n\t\t\t\t\t\t\tData: dto.NoteSyncNeedPushMessage{\n\t\t\t\t\t\t\t\tPath:     note.Path,\n\t\t\t\t\t\t\t\tPathHash: note.PathHash,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t\tneedUploadCount++\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Content matches, but modification time differs, notify client to update note mtime\n\t\t\t\t\t// 内容一致, 但修改时间不一致, 通知客户端更新笔记修改时间\n\t\t\t\t\t// Add message to queue instead of sending immediately\n\t\t\t\t\t// 将消息添加到队列而非立即发送\n\t\t\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\t\t\tAction: dto.NoteSyncMtime,\n\t\t\t\t\t\tData: dto.NoteSyncMtimeMessage{\n\t\t\t\t\t\t\tPath:             note.Path,\n\t\t\t\t\t\t\tCtime:            note.Ctime,\n\t\t\t\t\t\t\tMtime:            note.Mtime,\n\t\t\t\t\t\t\tUpdatedTimestamp: note.UpdatedTimestamp,\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\tneedSyncMtimeCount++\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// File client doesn't have, notify client to create file\n\t\t\t\t// 客户端没有的文件, 通知客户端创建文件\n\t\t\t\t// 将消息添加到队列而非立即发送\n\t\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\t\tAction: dto.NoteSyncModify,\n\t\t\t\t\tData: dto.NoteSyncModifyMessage{\n\t\t\t\t\t\tPath:             note.Path,\n\t\t\t\t\t\tPathHash:         note.PathHash,\n\t\t\t\t\t\tContent:          note.Content,\n\t\t\t\t\t\tContentHash:      note.ContentHash,\n\t\t\t\t\t\tCtime:            note.Ctime,\n\t\t\t\t\t\tMtime:            note.Mtime,\n\t\t\t\t\t\tUpdatedTimestamp: note.UpdatedTimestamp,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tneedModifyCount++\n\t\t\t}\n\t\t}\n\t}\n\n\t// Use current time as lastTime regardless of whether list is empty,\n\t// ensuring lastTime > all returned notes' updated_timestamp (mirrors FolderSync design)\n\t// 无论 list 是否为空，均取当前时间作为 lastTime，\n\t// 确保 lastTime > 所有返回笔记的 updated_timestamp（与 FolderSync 保持一致）\n\tlastTime = timex.Now().UnixMilli()\n\tif len(cNotesKeys) > 0 {\n\t\tfor pathHash := range cNotesKeys {\n\t\t\tnote := cNotes[pathHash]\n\n\t\t\t// Add message to queue instead of sending immediately\n\t\t\t// 将消息添加到队列而非立即发送\n\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\tAction: dto.NoteSyncNeedPush,\n\t\t\t\tData: dto.NoteSyncNeedPushMessage{\n\t\t\t\t\tPath:     note.Path,\n\t\t\t\t\tPathHash: note.PathHash,\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tneedUploadCount++\n\t\t}\n\t}\n\n\tc.IsFirstSync = true\n\n\t// Send NoteSyncEnd message, containing all counts\n\t// 发送 NoteSyncEnd 消息，包含所有统计计数\n\tc.ToResponse(code.Success.WithData(\n\t\tdto.NoteSyncEndMessage{\n\t\t\tLastTime:           lastTime,\n\t\t\tNeedUploadCount:    needUploadCount,\n\t\t\tNeedModifyCount:    needModifyCount,\n\t\t\tNeedSyncMtimeCount: needSyncMtimeCount,\n\t\t\tNeedDeleteCount:    needDeleteCount,\n\t\t},\n\t).WithVault(params.Vault).WithContext(params.Context), dto.NoteSyncEnd)\n\n\t// After End message, send all queued messages individually\n\t// 在 End 消息后，逐条发送队列中的消息\n\tfor _, item := range messageQueue {\n\t\tc.ToResponse(code.Success.WithData(item.Data).WithVault(params.Vault).WithContext(params.Context), item.Action)\n\t}\n}\n\n// UserInfo verifies and retrieves user info\n// 函数名: UserInfo\n// Function name: UserInfo\n// usage: Retrieves user info from service layer and converts to UserSelectEntity structure needed by WebSocket (for WebSocket user verification).\n// 函数使用说明: 从 service 层获取用户信息并转换成 WebSocket 需要的 UserSelectEntity 结构体（用于 WebSocket 用户验证）。\n// Parameters:\n//   - c *pkgapp.WebsocketClient: Current WebSocket client connection, including context and service factory (SF).\n//\n// 参数说明:\n//   - c *pkgapp.WebsocketClient: 当前 WebSocket 客户端连接，包含上下文与服务工厂（SF）。\n//   - uid int64: User ID to query.\n//\n// 参数说明:\n//   - uid int64: 要查询的用户 ID。\n//\n// Return:\n//   - *pkgapp.UserSelectEntity: If user found, returns converted user entity, otherwise nil.\n//\n// 返回值说明:\n//   - *pkgapp.UserSelectEntity: 如果查询到用户则返回转换后的用户实体，否则返回 nil。\n//   - error: Error during query (if any).\n//\n// 返回值说明:\n//   - error: 查询过程中的错误（若有）。\nfunc (h *NoteWSHandler) UserInfo(c *pkgapp.WebsocketClient, uid int64) (*pkgapp.UserSelectEntity, error) {\n\n\t// Use WebSocket connection's long-lived context\n\t// 使用 WebSocket 连接的长生命周期 context\n\tctx := c.Context()\n\tuser, err := h.App.UserService.GetInfo(ctx, uid)\n\n\tvar userEntity *pkgapp.UserSelectEntity\n\tif user != nil {\n\t\tuserEntity = convert.StructAssign(user, &pkgapp.UserSelectEntity{}).(*pkgapp.UserSelectEntity)\n\t}\n\n\treturn userEntity, err\n\n}\n"
  },
  {
    "path": "internal/routers/websocket_router/ws_setting.go",
    "content": "package websocket_router\n\nimport (\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/convert\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/logger\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"go.uber.org/zap\"\n)\n\n// SettingWSHandler WebSocket setting handler\n// SettingWSHandler WebSocket 配置处理器\n// Uses App Container to inject dependencies\n// 使用 App Container 注入依赖\ntype SettingWSHandler struct {\n\t*WSHandler\n}\n\n// NewSettingWSHandler creates SettingWSHandler instance\n// NewSettingWSHandler 创建 SettingWSHandler 实例\nfunc NewSettingWSHandler(a *app.App) *SettingWSHandler {\n\treturn &SettingWSHandler{\n\t\tWSHandler: NewWSHandler(a),\n\t}\n}\n\n// SettingModify handles setting modification messages\n// SettingModify 处理配置修改消息\nfunc (h *SettingWSHandler) SettingModify(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.SettingModifyOrCreateRequest{}\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.setting.SettingModify.BindAndValid\")\n\t\treturn\n\t}\n\n\tctx := c.Context()\n\n\tpkgapp.NoteModifyLog(c.TraceID, c.User.UID, \"SettingModify\", params.Path, params.Vault)\n\n\th.App.VaultService.GetOrCreate(ctx, c.User.UID, params.Vault)\n\n\tsettingSvc := h.App.GetSettingService(c.ClientType, c.ClientName, c.ClientVersion)\n\n\tcheckParams := convert.StructAssign(params, &dto.SettingUpdateCheckRequest{}).(*dto.SettingUpdateCheckRequest)\n\tupdateMode, settingCheck, err := settingSvc.UpdateCheck(ctx, c.User.UID, checkParams)\n\tif err != nil {\n\t\th.respondError(c, code.ErrorSettingModifyOrCreateFailed, err, \"websocket_router.setting.SettingModify.UpdateCheck\")\n\t\treturn\n\t}\n\n\tswitch updateMode {\n\tcase \"UpdateContent\", \"Create\":\n\t\t_, setting, err := settingSvc.ModifyOrCreate(ctx, c.User.UID, params, true)\n\t\tif err != nil {\n\t\t\th.respondError(c, code.ErrorSettingModifyOrCreateFailed, err, \"websocket_router.setting.SettingModify.ModifyOrCreate\")\n\t\t\treturn\n\t\t}\n\n\t\tc.ToResponse(code.Success.WithData(dto.SettingModifyAckMessage{\n\t\t\tLastTime: setting.UpdatedTimestamp,\n\t\t\tPath:     setting.Path,\n\t\t\tPathHash: setting.PathHash,\n\t\t}), string(dto.SettingModifyAck))\n\t\tc.BroadcastResponse(code.Success.WithData(\n\t\t\tdto.SettingSyncModifyMessage{\n\t\t\t\tVault:            params.Vault,\n\t\t\t\tPath:             setting.Path,\n\t\t\t\tPathHash:         setting.PathHash,\n\t\t\t\tContent:          setting.Content,\n\t\t\t\tContentHash:      setting.ContentHash,\n\t\t\t\tCtime:            setting.Ctime,\n\t\t\t\tMtime:            setting.Mtime,\n\t\t\t\tUpdatedTimestamp: setting.UpdatedTimestamp,\n\t\t\t},\n\t\t).WithVault(params.Vault), true, dto.SettingSyncModify)\n\t\treturn\n\n\tcase \"UpdateMtime\":\n\t\tc.ToResponse(code.Success.WithData(dto.SettingModifyAckMessage{\n\t\t\tLastTime: settingCheck.UpdatedTimestamp,\n\t\t\tPath:     settingCheck.Path,\n\t\t\tPathHash: settingCheck.PathHash,\n\t\t}), string(dto.SettingModifyAck))\n\t\treturn\n\tdefault:\n\t\tc.ToResponse(code.SuccessNoUpdate)\n\t\treturn\n\t}\n}\n\n// SettingModifyCheck checks the necessity of setting modification\n// SettingModifyCheck 检查配置修改必要性\nfunc (h *SettingWSHandler) SettingModifyCheck(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.SettingUpdateCheckRequest{}\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.setting.SettingModifyCheck.BindAndValid\")\n\t\treturn\n\t}\n\n\tctx := c.Context()\n\n\tpkgapp.NoteModifyLog(c.TraceID, c.User.UID, \"SettingModifyCheck\", params.Path, params.Vault)\n\n\th.App.VaultService.GetOrCreate(ctx, c.User.UID, params.Vault)\n\n\tsettingSvc := h.App.GetSettingService(c.ClientType, c.ClientName, c.ClientVersion)\n\n\tupdateMode, settingCheck, err := settingSvc.UpdateCheck(ctx, c.User.UID, params)\n\tif err != nil {\n\t\th.respondError(c, code.ErrorSettingUpdateCheckFailed, err, \"websocket_router.setting.SettingModifyCheck.UpdateCheck\")\n\t\treturn\n\t}\n\n\tswitch updateMode {\n\tcase \"UpdateContent\", \"Create\":\n\t\tc.ToResponse(code.Success.WithData(\n\t\t\tdto.SettingSyncNeedUploadMessage{\n\t\t\t\tPath: settingCheck.Path,\n\t\t\t},\n\t\t), dto.SettingSyncNeedUpload)\n\t\treturn\n\tcase \"UpdateMtime\":\n\t\tc.ToResponse(code.Success.WithData(\n\t\t\tdto.SettingSyncMtimeMessage{\n\t\t\t\tPath:             settingCheck.Path,\n\t\t\t\tCtime:            settingCheck.Ctime,\n\t\t\t\tMtime:            settingCheck.Mtime,\n\t\t\t\tUpdatedTimestamp: settingCheck.UpdatedTimestamp,\n\t\t\t},\n\t\t), dto.SettingSyncMtime)\n\t\treturn\n\tdefault:\n\t\tc.ToResponse(code.SuccessNoUpdate)\n\t\treturn\n\t}\n}\n\n// SettingDelete handles setting deletion messages\n// SettingDelete 处理配置删除消息\nfunc (h *SettingWSHandler) SettingDelete(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.SettingDeleteRequest{}\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.setting.SettingDelete.BindAndValid\")\n\t\treturn\n\t}\n\n\tctx := c.Context()\n\n\tpkgapp.NoteModifyLog(c.TraceID, c.User.UID, \"SettingDelete\", params.Path, params.Vault)\n\n\th.App.VaultService.GetOrCreate(ctx, c.User.UID, params.Vault)\n\n\tsettingSvc := h.App.GetSettingService(c.ClientType, c.ClientName, c.ClientVersion)\n\n\tsetting, err := settingSvc.Delete(ctx, c.User.UID, params)\n\tif err != nil {\n\t\th.respondError(c, code.ErrorSettingDeleteFailed, err, \"websocket_router.setting.SettingDelete.Delete\")\n\t\treturn\n\t}\n\n\tc.ToResponse(code.Success.WithData(dto.SettingDeleteAckMessage{\n\t\tLastTime: setting.UpdatedTimestamp,\n\t\tPath:     setting.Path,\n\t\tPathHash: setting.PathHash,\n\t}), string(dto.SettingDeleteAck))\n\tc.BroadcastResponse(code.Success.WithData(\n\t\tdto.SettingSyncDeleteMessage{\n\t\t\tPath:             setting.Path,\n\t\t\tPathHash:         setting.PathHash,\n\t\t\tCtime:            setting.Ctime,\n\t\t\tMtime:            setting.Mtime,\n\t\t\tUpdatedTimestamp: setting.UpdatedTimestamp,\n\t\t},\n\t).WithVault(params.Vault), true, dto.SettingSyncDelete)\n}\n\n// SettingSync handles setting synchronization messages\n// SettingSync 处理配置同步消息\nfunc (h *SettingWSHandler) SettingSync(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.SettingSyncRequest{}\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.setting.SettingSync.BindAndValid\")\n\t\treturn\n\t}\n\n\tctx := c.Context()\n\n\tpkgapp.NoteModifyLog(c.TraceID, c.User.UID, \"SettingSync\", \"\", params.Vault)\n\n\th.App.VaultService.GetOrCreate(ctx, c.User.UID, params.Vault)\n\n\tsettingSvc := h.App.GetSettingService(c.ClientType, c.ClientName, c.ClientVersion)\n\n\tlist, err := settingSvc.Sync(ctx, c.User.UID, params)\n\tif err != nil {\n\t\th.respondError(c, code.ErrorSettingListFailed, err, \"websocket_router.setting.SettingSync.Sync\")\n\t\treturn\n\t}\n\n\tcSettings := make(map[string]dto.SettingSyncCheckRequest)\n\tcSettingsKeys := make(map[string]struct{})\n\tfor _, s := range params.Settings {\n\t\tcSettings[s.PathHash] = s\n\t\tcSettingsKeys[s.PathHash] = struct{}{}\n\t}\n\n\t// Create message queue for collecting all messages to be sent\n\t// 创建消息队列，用于收集所有待发送的消息\n\t// Check and create vault, internally uses SF to merge concurrent requests, avoiding duplicate creation issues\n\t// 检查并创建仓库，内部使用SF合并并发请求, 避免重复创建问题\n\tvar messageQueue []dto.WSQueuedMessage\n\n\tvar lastTime int64\n\tvar needUploadCount int64\n\tvar needModifyCount int64\n\tvar needSyncMtimeCount int64\n\tvar needDeleteCount int64\n\n\tvar cDelSettingsKeys map[string]struct{} = make(map[string]struct{}, 0)\n\n\t// Handle settings deleted by client\n\t// 处理客户端删除的配置\n\tif len(params.DelSettings) > 0 {\n\t\tfor _, delSetting := range params.DelSettings {\n\n\t\t\t// Check if setting exists before deleting\n\t\t\tgetCheckParams := &dto.SettingGetRequest{\n\t\t\t\tVault:    params.Vault,\n\t\t\t\tPathHash: delSetting.PathHash,\n\t\t\t}\n\t\t\tcheckSetting, err := settingSvc.Get(ctx, c.User.UID, getCheckParams)\n\n\t\t\tif err == nil && checkSetting != nil && checkSetting.Action != \"delete\" {\n\t\t\t\tdelParams := &dto.SettingDeleteRequest{\n\t\t\t\t\tVault:    params.Vault,\n\t\t\t\t\tPath:     delSetting.Path,\n\t\t\t\t\tPathHash: delSetting.PathHash,\n\t\t\t\t}\n\t\t\t\tsetting, err := settingSvc.Delete(ctx, c.User.UID, delParams)\n\t\t\t\tif err != nil {\n\t\t\t\t\th.App.Logger().Error(\"websocket_router.setting.SettingSync.SettingService.Delete\",\n\t\t\t\t\t\tzap.String(logger.FieldTraceID, c.TraceID),\n\t\t\t\t\t\tzap.Int64(logger.FieldUID, c.User.UID),\n\t\t\t\t\t\tzap.String(logger.FieldPath, delSetting.Path),\n\t\t\t\t\t\tzap.Error(err))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// 记录客户端已主动删除的 PathHash,避免重复下发\n\t\t\t\tcDelSettingsKeys[delSetting.PathHash] = struct{}{}\n\t\t\t\t// Broadcast deletion to other clients\n\t\t\t\t// 将删除消息广播给其他客户端\n\t\t\t\tc.BroadcastResponse(code.Success.WithData(\n\t\t\t\t\tdto.SettingSyncDeleteMessage{\n\t\t\t\t\t\tPath: setting.Path,\n\t\t\t\t\t},\n\t\t\t\t).WithVault(params.Vault), true, dto.SettingSyncDelete)\n\t\t\t} else {\n\t\t\t\th.App.Logger().Debug(\"websocket_router.setting.SettingSync.SettingService.Get check failed (not found or already deleted), broadcasting delete anyway\",\n\t\t\t\t\tzap.String(logger.FieldTraceID, c.TraceID),\n\t\t\t\t\tzap.String(\"pathHash\", delSetting.PathHash))\n\n\t\t\t\t// Record PathHash\n\t\t\t\t// 记录 PathHash\n\t\t\t\tcDelSettingsKeys[delSetting.PathHash] = struct{}{}\n\n\t\t\t\t// Broadcast deletion with available info (Path)\n\t\t\t\t// 使用现有信息(Path)广播删除\n\t\t\t\tc.BroadcastResponse(code.Success.WithData(\n\t\t\t\t\tdto.SettingSyncDeleteMessage{\n\t\t\t\t\t\tPath: delSetting.Path,\n\t\t\t\t\t},\n\t\t\t\t).WithVault(params.Vault), true, dto.SettingSyncDelete)\n\t\t\t}\n\n\t\t}\n\t}\n\n\t// Handle settings missing on client (only for incremental sync)\n\t// 处理客户端缺失的配置（仅限增量同步）\n\tif params.LastTime > 0 && len(params.MissingSettings) > 0 {\n\t\tfor _, missingSetting := range params.MissingSettings {\n\t\t\tgetParams := &dto.SettingGetRequest{\n\t\t\t\tVault:    params.Vault,\n\t\t\t\tPathHash: missingSetting.PathHash,\n\t\t\t}\n\t\t\tsetting, err := settingSvc.Get(ctx, c.User.UID, getParams)\n\t\t\tif err != nil {\n\t\t\t\th.App.Logger().Warn(\"websocket_router.setting.SettingSync.SettingService.Get\",\n\t\t\t\t\tzap.String(logger.FieldTraceID, c.TraceID),\n\t\t\t\t\tzap.String(\"pathHash\", missingSetting.PathHash),\n\t\t\t\t\tzap.Error(err))\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif setting != nil && setting.Action != \"delete\" {\n\n\t\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\t\tAction: dto.SettingSyncModify,\n\t\t\t\t\tData: dto.SettingSyncModifyMessage{\n\t\t\t\t\t\tVault:            params.Vault,\n\t\t\t\t\t\tPath:             setting.Path,\n\t\t\t\t\t\tPathHash:         setting.PathHash,\n\t\t\t\t\t\tContent:          setting.Content,\n\t\t\t\t\t\tContentHash:      setting.ContentHash,\n\t\t\t\t\t\tCtime:            setting.Ctime,\n\t\t\t\t\t\tMtime:            setting.Mtime,\n\t\t\t\t\t\tUpdatedTimestamp: setting.UpdatedTimestamp,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tneedModifyCount++\n\t\t\t\t// 加入排除索引\n\t\t\t\tcDelSettingsKeys[setting.PathHash] = struct{}{}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, s := range list {\n\t\t// 如果该配置是客户端刚才通过参数告知删除的,则跳过下发\n\t\tif _, ok := cDelSettingsKeys[s.PathHash]; ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tif s.UpdatedTimestamp >= lastTime {\n\t\t\tlastTime = s.UpdatedTimestamp\n\t\t}\n\t\tif s.Action == \"delete\" {\n\t\t\t// Server already deleted, notify client to delete (regardless of whether client has it)\n\t\t\t// 服务端已经删除，通知客户端删除（不再检查客户端是否存在）\n\t\t\tif _, ok := cSettings[s.PathHash]; ok {\n\t\t\t\tdelete(cSettingsKeys, s.PathHash)\n\t\t\t}\n\t\t\t// 将消息添加到队列\n\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\tAction: dto.SettingSyncDelete,\n\t\t\t\tData: dto.SettingSyncDeleteMessage{\n\t\t\t\t\tPath:             s.Path,\n\t\t\t\t\tPathHash:         s.PathHash,\n\t\t\t\t\tCtime:            s.Ctime,\n\t\t\t\t\tMtime:            s.Mtime,\n\t\t\t\t\tUpdatedTimestamp: s.UpdatedTimestamp,\n\t\t\t\t},\n\t\t\t})\n\t\t\tneedDeleteCount++\n\t\t} else {\n\t\t\tif cSetting, ok := cSettings[s.PathHash]; ok {\n\t\t\t\tdelete(cSettingsKeys, s.PathHash)\n\t\t\t\tif s.ContentHash == cSetting.ContentHash && s.Mtime == cSetting.Mtime {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// 强制覆盖连接端\n\t\t\t\tif params.Cover {\n\t\t\t\t\t// 将消息添加到队列而非立即发送\n\t\t\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\t\t\tAction: dto.SettingSyncModify,\n\t\t\t\t\t\tData: dto.SettingSyncModifyMessage{\n\t\t\t\t\t\t\tVault:            params.Vault,\n\t\t\t\t\t\t\tPath:             s.Path,\n\t\t\t\t\t\t\tPathHash:         s.PathHash,\n\t\t\t\t\t\t\tContent:          s.Content,\n\t\t\t\t\t\t\tContentHash:      s.ContentHash,\n\t\t\t\t\t\t\tCtime:            s.Ctime,\n\t\t\t\t\t\t\tMtime:            s.Mtime,\n\t\t\t\t\t\t\tUpdatedTimestamp: s.UpdatedTimestamp,\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\tneedModifyCount++\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// 链接端和服务端， 文件内容相同\n\t\t\t\tif s.ContentHash != cSetting.ContentHash {\n\t\t\t\t\tif s.Mtime >= cSetting.Mtime {\n\t\t\t\t\t\t// Server file mtime is greater than client file mtime, notify client to update\n\t\t\t\t\t\t// 将消息添加到队列而非立即发送\n\t\t\t\t\t\t// 服务端文件 mtime 大于链接端文件 mtime，则通知连接端更新\n\t\t\t\t\t\t// 将消息添加到队列而非立即发送\n\t\t\t\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\t\t\t\tAction: dto.SettingSyncModify,\n\t\t\t\t\t\t\tData: dto.SettingSyncModifyMessage{\n\t\t\t\t\t\t\t\tVault:            params.Vault,\n\t\t\t\t\t\t\t\tPath:             s.Path,\n\t\t\t\t\t\t\t\tPathHash:         s.PathHash,\n\t\t\t\t\t\t\t\tContent:          s.Content,\n\t\t\t\t\t\t\t\tContentHash:      s.ContentHash,\n\t\t\t\t\t\t\t\tCtime:            s.Ctime,\n\t\t\t\t\t\t\t\tMtime:            s.Mtime,\n\t\t\t\t\t\t\t\tUpdatedTimestamp: s.UpdatedTimestamp,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t\tneedModifyCount++\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Server file mtime is less than client file mtime, notify client to update\n\t\t\t\t\t\t// 将消息添加到队列而非立即发送\n\t\t\t\t\t\t// 服务端文件 mtime 小于链接端文件 mtime，则通知连接端更新\n\t\t\t\t\t\t// 将消息添加到队列而非立即发送\n\t\t\t\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\t\t\t\tAction: dto.SettingSyncNeedUpload,\n\t\t\t\t\t\t\tData: dto.SettingSyncNeedUploadMessage{\n\t\t\t\t\t\t\t\tPath: s.Path,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t\tneedUploadCount++\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Client and server have same content, but different mtime\n\t\t\t\t\t// 将消息添加到队列而非立即发送\n\t\t\t\t\t// 链接端和服务端， 文件内容相同，文件 mtime 时间不同\n\t\t\t\t\t// 将消息添加到队列而非立即发送\n\t\t\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\t\t\tAction: dto.SettingSyncMtime,\n\t\t\t\t\t\tData: dto.SettingSyncMtimeMessage{\n\t\t\t\t\t\t\tPath:             s.Path,\n\t\t\t\t\t\t\tCtime:            s.Ctime,\n\t\t\t\t\t\t\tMtime:            s.Mtime,\n\t\t\t\t\t\t\tUpdatedTimestamp: s.UpdatedTimestamp,\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\tneedSyncMtimeCount++\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// 将消息添加到队列而非立即发送\n\t\t\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\t\t\tAction: dto.SettingSyncModify,\n\t\t\t\t\tData: dto.SettingSyncModifyMessage{\n\t\t\t\t\t\tVault:            params.Vault,\n\t\t\t\t\t\tPath:             s.Path,\n\t\t\t\t\t\tPathHash:         s.PathHash,\n\t\t\t\t\t\tContent:          s.Content,\n\t\t\t\t\t\tContentHash:      s.ContentHash,\n\t\t\t\t\t\tCtime:            s.Ctime,\n\t\t\t\t\t\tMtime:            s.Mtime,\n\t\t\t\t\t\tUpdatedTimestamp: s.UpdatedTimestamp,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tneedModifyCount++\n\t\t\t}\n\t\t}\n\t}\n\n\tif list == nil {\n\t\tlastTime = timex.Now().UnixMilli()\n\t}\n\tfor pathHash := range cSettingsKeys {\n\t\ts := cSettings[pathHash]\n\t\t// Add message to queue instead of sending immediately\n\t\t// 将消息添加到队列而非立即发送\n\t\tmessageQueue = append(messageQueue, dto.WSQueuedMessage{\n\t\t\tAction: dto.SettingSyncNeedUpload,\n\t\t\tData:   dto.SettingSyncNeedUploadMessage{Path: s.Path},\n\t\t})\n\t\tneedUploadCount++\n\t}\n\n\t// Send SettingSyncEnd message\n\t// 发送 SettingSyncEnd 消息\n\tc.ToResponse(code.Success.WithData(\n\t\tdto.SettingSyncEndMessage{\n\t\t\tLastTime:           lastTime,\n\t\t\tNeedUploadCount:    needUploadCount,\n\t\t\tNeedModifyCount:    needModifyCount,\n\t\t\tNeedSyncMtimeCount: needSyncMtimeCount,\n\t\t\tNeedDeleteCount:    needDeleteCount,\n\t\t},\n\t).WithContext(params.Context), dto.SettingSyncEnd)\n\n\t// Send queued messages individually\n\t// 逐条发送队列中的消息\n\tfor _, item := range messageQueue {\n\t\tc.ToResponse(code.Success.WithData(item.Data).WithContext(params.Context), item.Action)\n\t}\n}\n\n// SettingClear handles clear all settings messages\n// SettingClear 处理清理所有配置消息\nfunc (h *SettingWSHandler) SettingClear(c *pkgapp.WebsocketClient, msg *pkgapp.WebSocketMessage) {\n\tparams := &dto.SettingClearRequest{}\n\tvalid, errs := c.BindAndValid(msg.Data, params)\n\tif !valid {\n\t\th.respondErrorWithData(c, code.ErrorInvalidParams.WithDetails(errs.ErrorsToString()), errs, errs.MapsToString(), \"websocket_router.setting.SettingClear.BindAndValid\")\n\t\treturn\n\t}\n\n\tctx := c.Context()\n\n\tpkgapp.NoteModifyLog(c.TraceID, c.User.UID, \"SettingClear\", \"\", params.Vault)\n\n\terr := h.App.GetSettingService(c.ClientType, c.ClientName, c.ClientVersion).ClearByVault(ctx, c.User.UID, params.Vault)\n\tif err != nil {\n\t\th.respondError(c, code.ErrorSettingDeleteFailed, err, \"websocket_router.setting.SettingClear.ClearByVault\")\n\t\treturn\n\t}\n\n\t// Broadcast clearing to other clients with vault info\n\t// 将清除消息广播给其他客户端，带上笔记本信息\n\tc.BroadcastResponse(code.Success.WithData(nil).WithVault(params.Vault), false, dto.SettingSyncClear)\n}\n"
  },
  {
    "path": "internal/service/backup_service.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/config\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/storage\"\n\tpkgstorage \"github.com/haierkeys/fast-note-sync-service/pkg/storage\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"github.com/robfig/cron/v3\"\n\t\"go.uber.org/zap\"\n)\n\nvar errNoUpdates = errors.New(\"no updates found\")\n\n// BackupService defines the business service interface for Backup\n// 定义备份业务服务接口\ntype BackupService interface {\n\tGetConfigs(ctx context.Context, uid int64) ([]*dto.BackupConfigDTO, error)\n\tDeleteConfig(ctx context.Context, uid int64, configID int64) error\n\tUpdateConfig(ctx context.Context, uid int64, req *dto.BackupConfigRequest) (*dto.BackupConfigDTO, error)\n\tListHistory(ctx context.Context, uid int64, configID int64, pager *app.Pager) ([]*dto.BackupHistoryDTO, int64, error)\n\tExecuteUserBackup(ctx context.Context, uid int64, configID int64) error\n\tExecuteTaskBackups(ctx context.Context) error\n\tNotifyUpdated(uid int64)\n\tShutdown(ctx context.Context) error\n}\n\ntype backupService struct {\n\tbackupRepo     domain.BackupRepository\n\tnoteRepo       domain.NoteRepository\n\tfolderRepo     domain.FolderRepository\n\tfileRepo       domain.FileRepository\n\tvaultRepo      domain.VaultRepository\n\tstorageService StorageService\n\tstorageConfig  *config.StorageConfig\n\tlogger         *zap.Logger\n\tsyncTimers     map[int64]*time.Timer\n\ttimerMu        sync.Mutex\n\tctx            context.Context\n\tcancel         context.CancelFunc\n\twg             sync.WaitGroup\n\tpendingSyncs   sync.Map                     // key: uid (int64), value: bool\n\trunningTasks   map[int64]context.CancelFunc // key: configID\n\trunningMu      sync.Mutex\n}\n\n// NewBackupService creates BackupService instance\n// 创建 BackupService 实例\nfunc NewBackupService(\n\tbackupRepo domain.BackupRepository,\n\tnoteRepo domain.NoteRepository,\n\tfolderRepo domain.FolderRepository,\n\tfileRepo domain.FileRepository,\n\tvaultRepo domain.VaultRepository,\n\tstorageService StorageService,\n\tstorageConfig *config.StorageConfig,\n\tlogger *zap.Logger,\n) BackupService {\n\tctx, cancel := context.WithCancel(context.Background())\n\treturn &backupService{\n\t\tbackupRepo:     backupRepo,\n\t\tnoteRepo:       noteRepo,\n\t\tfolderRepo:     folderRepo,\n\t\tfileRepo:       fileRepo,\n\t\tvaultRepo:      vaultRepo,\n\t\tstorageService: storageService,\n\t\tstorageConfig:  storageConfig,\n\t\tlogger:         logger,\n\t\tsyncTimers:     make(map[int64]*time.Timer),\n\t\trunningTasks:   make(map[int64]context.CancelFunc),\n\t\tctx:            ctx,\n\t\tcancel:         cancel,\n\t}\n}\n\n// GetConfigs Get user's backup configurations\n// 获取用户的备份配置列表\nfunc (s *backupService) GetConfigs(ctx context.Context, uid int64) ([]*dto.BackupConfigDTO, error) {\n\tconfigs, err := s.backupRepo.ListConfigs(ctx, uid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar results []*dto.BackupConfigDTO\n\tfor _, c := range configs {\n\t\tresults = append(results, s.configToDTO(ctx, c))\n\t}\n\treturn results, nil\n}\n\n// UpdateConfig Update or create backup configuration\n// 更新或创建备份配置\nfunc (s *backupService) UpdateConfig(ctx context.Context, uid int64, req *dto.BackupConfigRequest) (*dto.BackupConfigDTO, error) {\n\tvar vaultID int64\n\tif req.Vault != \"\" && req.Vault != \"0\" && req.Vault != \"all\" {\n\t\tv, err := s.vaultRepo.GetByName(ctx, req.Vault, uid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif v == nil {\n\t\t\treturn nil, code.ErrorVaultNotFound\n\t\t}\n\t\tvaultID = v.ID\n\t}\n\n\t// Validate Storage IDs\n\tvar storageIds []int64\n\tif err := json.Unmarshal([]byte(req.StorageIds), &storageIds); err != nil {\n\t\treturn nil, code.ErrorBackupStorageIDInvalid\n\t}\n\tfor _, sid := range storageIds {\n\t\tif _, err := s.storageService.Get(ctx, uid, sid); err != nil {\n\t\t\treturn nil, code.ErrorStorageNotFound\n\t\t}\n\t}\n\n\tconfig := &domain.BackupConfig{\n\t\tID:               req.ID,\n\t\tUID:              uid,\n\t\tVaultID:          vaultID,\n\t\tType:             req.Type,\n\t\tStorageIds:       req.StorageIds,\n\t\tIsEnabled:        req.IsEnabled,\n\t\tCronStrategy:     req.CronStrategy,\n\t\tCronExpression:   req.CronExpression,\n\t\tIncludeVaultName: req.IncludeVaultName,\n\t\tRetentionDays:    req.RetentionDays,\n\t}\n\n\t// Preserve state fields if updating existing config\n\tif req.ID > 0 {\n\t\tif old, err := s.backupRepo.GetByID(ctx, req.ID, uid); err == nil && old != nil {\n\t\t\tconfig.LastRunTime = old.LastRunTime\n\t\t\tconfig.LastStatus = old.LastStatus\n\t\t\tconfig.LastMessage = old.LastMessage\n\t\t}\n\t}\n\n\t// Calculate NextRunTime based on Cron Strategy\n\ts.calculateNextRunTime(config)\n\n\tupdated, err := s.backupRepo.SaveConfig(ctx, config, uid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Trigger sync check immediately if enabled and type is sync\n\tif updated.IsEnabled && (updated.Type == \"sync\") {\n\n\t\tfmt.Println(\"Trigger sync check immediately if enabled and type is sync\")\n\t\ts.pendingSyncs.Store(uid, true)\n\t}\n\n\treturn s.configToDTO(ctx, updated), nil\n}\n\n// DeleteConfig Deletes a backup configuration\n// 删除备份配置\nfunc (s *backupService) DeleteConfig(ctx context.Context, uid int64, configID int64) error {\n\t// First check if config exists and belongs to user\n\tconfig, err := s.backupRepo.GetByID(ctx, configID, uid)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif config == nil {\n\t\treturn code.ErrorBackupConfigNotFound\n\t}\n\n\treturn s.backupRepo.DeleteConfig(ctx, configID, uid)\n}\n\n// ListHistory List backup history with pagination\n// 分页查询备份历史记录\nfunc (s *backupService) ListHistory(ctx context.Context, uid int64, configID int64, pager *app.Pager) ([]*dto.BackupHistoryDTO, int64, error) {\n\thistories, count, err := s.backupRepo.ListHistory(ctx, uid, configID, pager.Page, pager.PageSize)\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tvar results []*dto.BackupHistoryDTO\n\tfor _, h := range histories {\n\t\tresults = append(results, s.historyToDTO(h))\n\t}\n\treturn results, count, nil\n}\n\nfunc (s *backupService) configToDTO(ctx context.Context, d *domain.BackupConfig) *dto.BackupConfigDTO {\n\tif d == nil {\n\t\treturn nil\n\t}\n\tvaultName := \"all\"\n\tif d.VaultID > 0 {\n\t\tif v, err := s.vaultRepo.GetByID(ctx, d.VaultID, d.UID); err == nil && v != nil {\n\t\t\tvaultName = v.Name\n\t\t}\n\t}\n\treturn &dto.BackupConfigDTO{\n\t\tID:               d.ID,\n\t\tUID:              d.UID,\n\t\tVault:            vaultName,\n\t\tType:             d.Type,\n\t\tStorageIds:       d.StorageIds,\n\t\tIsEnabled:        d.IsEnabled,\n\t\tCronStrategy:     d.CronStrategy,\n\t\tCronExpression:   d.CronExpression,\n\t\tIncludeVaultName: d.IncludeVaultName,\n\t\tRetentionDays:    d.RetentionDays,\n\t\tLastRunTime:      timex.Time(d.LastRunTime),\n\t\tNextRunTime:      timex.Time(d.NextRunTime),\n\t\tLastStatus:       d.LastStatus,\n\t\tLastMessage:      d.LastMessage,\n\t\tCreatedAt:        timex.Time(d.CreatedAt),\n\t\tUpdatedAt:        timex.Time(d.UpdatedAt),\n\t}\n}\n\nfunc (s *backupService) historyToDTO(d *domain.BackupHistory) *dto.BackupHistoryDTO {\n\tif d == nil {\n\t\treturn nil\n\t}\n\treturn &dto.BackupHistoryDTO{\n\t\tID:        d.ID,\n\t\tUID:       d.UID,\n\t\tConfigID:  d.ConfigID,\n\t\tStorageID: d.StorageID,\n\t\tType:      d.Type,\n\t\tStartTime: timex.Time(d.StartTime),\n\t\tEndTime:   timex.Time(d.EndTime),\n\t\tStatus:    d.Status,\n\t\tFileSize:  d.FileSize,\n\t\tFileCount: d.FileCount,\n\t\tMessage:   d.Message,\n\t\tFilePath:  d.FilePath,\n\t\tCreatedAt: timex.Time(d.CreatedAt),\n\t\tUpdatedAt: timex.Time(d.UpdatedAt),\n\t}\n}\n\n// ExecuteUserBackup Manually execute user backup task\n// 手动执行用户备份任务\nfunc (s *backupService) ExecuteUserBackup(ctx context.Context, uid int64, configID int64) error {\n\t// If configID is specified, execute specific task\n\tif configID <= 0 {\n\t\treturn code.ErrorBackupExecuteIDReq\n\t}\n\n\tconfig, err := s.backupRepo.GetByID(ctx, configID, uid)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif config == nil {\n\t\treturn code.ErrorBackupConfigNotFound\n\t}\n\tif !config.IsEnabled {\n\t\treturn code.ErrorBackupConfigDisabled\n\t}\n\t// Record error\n\t// 记录错误\n\tif err := s.handleBackupSync(ctx, config, true); err != nil {\n\t\t// Service shutdown errors bypass finishTask and are not persisted to history\n\t\tif s.ctx.Err() != nil {\n\t\t\treturn err\n\t\t}\n\t\ts.logger.Warn(\"Manual backup completed with errors\",\n\t\t\tzap.Int64(\"uid\", uid),\n\t\t\tzap.Int64(\"configID\", configID),\n\t\t\tzap.Error(err),\n\t\t)\n\t}\n\treturn nil\n}\n\n// ExecuteTaskBackups Poll and process all scheduled backup tasks\n// 轮询处理所有待执行的定时备份任务\nfunc (s *backupService) ExecuteTaskBackups(ctx context.Context) error {\n\tconfigs, err := s.backupRepo.ListEnabledConfigs(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tnow := time.Now()\n\tfor _, config := range configs {\n\t\tif !config.IsEnabled {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Check if user has pending changes\n\t\t_, pending := s.pendingSyncs.LoadAndDelete(config.UID)\n\n\t\tisScheduled := config.NextRunTime.Before(now)\n\t\tshouldTrigger := false\n\n\t\tif isScheduled {\n\t\t\tshouldTrigger = true\n\t\t} else if pending && config.Type == \"sync\" {\n\t\t\t// Only \"sync\" type tasks are allowed to be triggered directly by changes (debounced)\n\t\t\tshouldTrigger = true\n\t\t}\n\n\t\tif shouldTrigger {\n\t\t\ts.logger.Info(\"Triggering backup task\",\n\t\t\t\tzap.Int64(\"uid\", config.UID),\n\t\t\t\tzap.String(\"type\", config.Type),\n\t\t\t\tzap.Bool(\"isScheduled\", isScheduled),\n\t\t\t\tzap.Bool(\"isPending\", pending),\n\t\t\t)\n\t\t\tgo func(cfg *domain.BackupConfig, p bool) {\n\t\t\t\t// Use service context to support graceful shutdown\n\t\t\t\tif err := s.handleBackupSync(s.ctx, cfg, p); err != nil {\n\t\t\t\t\ts.logger.Error(\"Backup execution failed\", zap.Int64(\"uid\", cfg.UID), zap.Error(err))\n\t\t\t\t}\n\t\t\t}(config, pending)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// calculateNextRunTime Calculate next run time based on Cron strategy\n// 根据 Cron 策略计算下次运行时间\nfunc (s *backupService) calculateNextRunTime(config *domain.BackupConfig) {\n\tif !config.IsEnabled {\n\t\treturn\n\t}\n\n\tif config.Type == \"sync\" {\n\t\tconfig.NextRunTime = time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC)\n\t\treturn\n\t}\n\n\texpr := \"\"\n\tswitch config.CronStrategy {\n\tcase \"daily\":\n\t\texpr = \"0 0 * * *\" // Midnight daily\n\tcase \"weekly\":\n\t\texpr = \"0 0 * * 0\" // Midnight Sunday\n\tcase \"monthly\":\n\t\texpr = \"0 0 1 * *\" // Midnight 1st of month\n\tcase \"custom\":\n\t\texpr = config.CronExpression\n\t}\n\n\tparser := cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow)\n\tschedule, err := parser.Parse(expr)\n\tif err != nil {\n\t\ts.logger.Error(\"Failed to parse cron expression\", zap.String(\"expr\", expr), zap.Error(err))\n\t\treturn\n\t}\n\n\tconfig.NextRunTime = schedule.Next(time.Now())\n}\n\n// handleBackupSync Core entry point for performing backup/sync\n// 执行备份/同步的核心入口\nfunc (s *backupService) handleBackupSync(ctx context.Context, config *domain.BackupConfig, isPending bool) error {\n\tuid := config.UID\n\tconfigID := config.ID\n\n\t// 1. Concurrency conflict handling strategy\n\t// 1. 并发冲突处理策略\n\ts.runningMu.Lock()\n\tif cancel, running := s.runningTasks[configID]; running {\n\t\tif config.Type == \"sync\" {\n\t\t\t// Sync task strategy: cancel old task, execute new one\n\t\t\t// 同步任务策略：取消旧任务，执行新任务\n\t\t\ts.logger.Info(\"Cancelling existing sync task to start a newer one\", zap.Int64(\"uid\", uid), zap.Int64(\"configID\", configID))\n\t\t\tcancel()\n\t\t\tdelete(s.runningTasks, configID)\n\t\t} else {\n\t\t\t// Full/Incremental backup strategy: keep old task, ignore new one\n\t\t\t// 全量/增量备份策略：保留旧任务，忽略新任务\n\t\t\ts.runningMu.Unlock()\n\t\t\ts.logger.Info(\"Backup task already running, skipping this trigger\", zap.Int64(\"uid\", uid), zap.Int64(\"configID\", configID), zap.String(\"type\", config.Type))\n\t\t\treturn nil\n\t\t}\n\t}\n\n\t// Create context with cancel function\n\t// 创建带取消功能的 context\n\ttaskCtx, taskCancel := context.WithCancel(ctx)\n\ts.runningTasks[configID] = taskCancel\n\ts.runningMu.Unlock()\n\n\t// Cleanup on task finish\n\t// 任务结束时的清理\n\tdefer func() {\n\t\ts.runningMu.Lock()\n\t\tif _, ok := s.runningTasks[configID]; ok {\n\t\t\t// Ensure current cancel record is cleaned up\n\t\t\t// 确保清理当前的 cancel 记录\n\t\t\tdelete(s.runningTasks, configID)\n\t\t}\n\t\ts.runningMu.Unlock()\n\t\ttaskCancel() // Release resources // 释放资源\n\t}()\n\n\ts.wg.Add(1)\n\tdefer s.wg.Done()\n\n\t// Check if context is already done\n\tselect {\n\tcase <-taskCtx.Done():\n\t\treturn taskCtx.Err()\n\tdefault:\n\t}\n\n\tstartTime := time.Now()\n\tprevRunTime := config.LastRunTime // Record last run time before this execution // 记录本次执行前的上一次执行时间\n\n\tshouldRun := false\n\tswitch config.Type {\n\tcase \"full\":\n\t\tshouldRun = true\n\tcase \"incremental\", \"sync\":\n\t\t// Exception: If it's the first run (prevRunTime is zero), we must execute to create a base backup.\n\t\tif isPending || prevRunTime.IsZero() {\n\t\t\tshouldRun = true\n\t\t}\n\tdefault:\n\t\treturn code.ErrorBackupTypeUnknown\n\t}\n\n\tif !shouldRun {\n\t\ts.logger.Info(\"Skipping backup: no pending changes\", zap.Int64(\"uid\", uid), zap.String(\"type\", config.Type))\n\t\ts.recordNoUpdateHistory(taskCtx, config, startTime)\n\t\treturn s.finishTask(taskCtx, config, errNoUpdates, 0, 0, startTime)\n\t}\n\n\ts.logger.Info(\"handleBackupSync start\", zap.Int64(\"uid\", uid), zap.String(\"type\", config.Type))\n\n\t// 2. Set running status (Running)\n\t// 2. 设置运行状态 (Running)\n\tconfig.LastStatus = domain.BackupStatusRunning\n\ts.backupRepo.SaveConfig(taskCtx, config, uid)\n\n\t// 3. Prepare temporary working directory\n\t// 3. 准备临时工作目录\n\ttempDir, err := os.MkdirTemp(\"\", fmt.Sprintf(\"backup_%d_\", uid))\n\tif err != nil {\n\t\treturn s.finishTask(taskCtx, config, err, 0, 0, startTime)\n\t}\n\tdefer os.RemoveAll(tempDir)\n\n\tvar fileCount, fileSize int64\n\tvar backupErr error\n\n\t// 4. Execute core logic\n\t// 4. 执行核心逻辑\n\tswitch config.Type {\n\tcase \"full\":\n\t\tfileCount, fileSize, backupErr = s.runArchive(taskCtx, config, tempDir, startTime, prevRunTime)\n\tcase \"incremental\":\n\t\tfileCount, fileSize, backupErr = s.runArchive(taskCtx, config, tempDir, startTime, prevRunTime)\n\tcase \"sync\":\n\t\tbackupErr = s.runSync(taskCtx, config, startTime, prevRunTime)\n\t}\n\n\t// 5. Update final status and cleanup\n\t// 5. 更新最终状态与清理\n\treturn s.finishTask(taskCtx, config, backupErr, fileCount, fileSize, startTime)\n}\n\n// getVaultName Get vault name by ID\n// 根据 ID 获取 Vault 名称\nfunc (s *backupService) getVaultName(ctx context.Context, vaultID, uid int64) string {\n\tif vaultID > 0 {\n\t\tif v, err := s.vaultRepo.GetByID(ctx, vaultID, uid); err == nil && v != nil {\n\t\t\treturn v.Name\n\t\t}\n\t}\n\treturn \"all\"\n}\n\n// runArchive Execute archive backup (full/incremental)\n// 1. Export notes and attachments to temp directory\n// 2. Archive to ZIP\n// 3. Upload to all configured storage targets\n// 执行压缩归档备份 (全量/增量)\n// 1. 导出笔记和附件到临时目录\n// 2. 打包为 ZIP\n// 3. 上传到配置的所有存储目标\nfunc (s *backupService) runArchive(ctx context.Context, config *domain.BackupConfig, tempDir string, startTime time.Time, lastRun time.Time) (int64, int64, error) {\n\tuid := config.UID\n\tvaultName := s.getVaultName(ctx, config.VaultID, uid)\n\tzipName := fmt.Sprintf(\"backup_%s_%d_%s_%s.zip\", config.Type, uid, vaultName, startTime.Format(\"20060102_150405\"))\n\tzipPath := filepath.Join(os.TempDir(), zipName)\n\n\tdefer os.Remove(zipPath)\n\n\t// 1. Collect resources (includes notes and attachments)\n\t// 1. 收集资源 (包含笔记和附件)\n\tcount, size, err := s.exportArchiveFiles(ctx, uid, config.VaultID, tempDir, config.Type == \"incremental\", lastRun)\n\tif err != nil {\n\t\treturn 0, 0, err\n\t}\n\n\tif count == 0 {\n\t\ts.recordNoUpdateHistory(ctx, config, startTime)\n\t\treturn 0, 0, errNoUpdates\n\t}\n\n\t// 2. Zip archive\n\t// 2. 压缩打包\n\tif err := util.Zip(tempDir, zipPath); err != nil {\n\t\treturn 0, 0, err\n\t}\n\n\t// 3. Upload to all storage targets\n\t// 3. 上传到所有存储目标\n\tvar storageIds []int64\n\tif err := json.Unmarshal([]byte(config.StorageIds), &storageIds); err != nil {\n\t\treturn count, size, code.ErrorBackupStorageIDInvalid\n\t}\n\n\tfor _, sid := range storageIds {\n\t\tst, err := s.storageService.Get(ctx, uid, sid)\n\t\tif err != nil {\n\t\t\ts.logger.Warn(\"Failed to get storage config, skipping\", zap.Int64(\"sid\", sid), zap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\tif !st.IsEnabled {\n\t\t\ts.logger.Info(\"Storage is disabled, skipping\", zap.Int64(\"sid\", sid))\n\t\t\tcontinue\n\t\t}\n\t\ts.uploadArchive(ctx, uid, config.ID, st, zipPath, zipName, config.Type, startTime, count, size)\n\t}\n\n\treturn count, size, nil\n}\n\n// runSync Execute real-time file sync\n// Iterate through file changes and mirror sync to all storage targets (no archiving)\n// 执行实时文件同步\n// 遍历文件变更，直接镜像同步到所有存储目标 (不打包)\nfunc (s *backupService) runSync(ctx context.Context, config *domain.BackupConfig, startTime time.Time, lastRun time.Time) error {\n\tvar storageIds []int64\n\tif err := json.Unmarshal([]byte(config.StorageIds), &storageIds); err != nil {\n\t\treturn code.ErrorBackupStorageIDInvalid\n\t}\n\n\t// First, check if there are any updates across all storages\n\t// Note: syncFiles will check all resources and return true if any changes found\n\thasUpdates, err := s.syncFiles(ctx, config.UID, config.VaultID, config.ID, nil, startTime, lastRun, config.IncludeVaultName)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif !hasUpdates {\n\t\ts.recordNoUpdateHistory(ctx, config, startTime)\n\t\treturn errNoUpdates\n\t}\n\n\tvar syncErrors []string\n\tfor _, sid := range storageIds {\n\t\tst, err := s.storageService.Get(ctx, config.UID, sid)\n\t\tif err != nil {\n\t\t\ts.logger.Warn(\"Failed to get storage config, skipping\", zap.Int64(\"sid\", sid), zap.Error(err))\n\t\t\tsyncErrors = append(syncErrors, fmt.Sprintf(\"storage %d: config error: %v\", sid, err))\n\t\t\tcontinue\n\t\t}\n\t\tif !st.IsEnabled {\n\t\t\ts.logger.Info(\"Storage is disabled, skipping\", zap.Int64(\"sid\", sid))\n\t\t\tcontinue\n\t\t}\n\t\tif st.Type == storage.LOCAL {\n\t\t\tst.CustomPath = filepath.Join(strconv.FormatInt(config.UID, 10), strconv.FormatInt(config.VaultID, 10), st.CustomPath)\n\t\t}\n\t\tif _, err := s.syncFiles(ctx, config.UID, config.VaultID, config.ID, st, startTime, lastRun, config.IncludeVaultName); err != nil {\n\t\t\ts.logger.Warn(\"Sync to storage failed\", zap.Int64(\"sid\", sid), zap.String(\"type\", st.Type), zap.Error(err))\n\t\t\tsyncErrors = append(syncErrors, fmt.Sprintf(\"storage %d (%s): %v\", sid, st.Type, err))\n\t\t}\n\t}\n\tif len(syncErrors) > 0 {\n\t\treturn fmt.Errorf(\"sync errors: %s\", strings.Join(syncErrors, \"; \"))\n\t}\n\treturn nil\n}\n\n// finishTask Update final status and cleanup after task completion\n// 任务完成后的状态更新与清理\nfunc (s *backupService) finishTask(ctx context.Context, config *domain.BackupConfig, err error, fileCount, fileSize int64, startTime time.Time) error {\n\tconfig.LastRunTime = startTime // Update last run time // 更新最后执行时间\n\n\tif s.ctx.Err() != nil {\n\t\t// Service shutdown or context cancelled\n\t\tconfig.LastStatus = domain.BackupStatusStopped // 4: Stopped // 4: 停止\n\t\tconfig.LastMessage = \"Backup stopped by system\"\n\t\tif err != nil {\n\t\t\tconfig.LastMessage += fmt.Sprintf(\": %v\", err)\n\t\t}\n\t} else if err == nil {\n\t\tconfig.LastStatus = domain.BackupStatusSuccess // 2: Success // 2: 成功\n\t\tconfig.LastMessage = \"Backup completed successfully\"\n\t} else if errors.Is(err, errNoUpdates) {\n\t\tconfig.LastStatus = domain.BackupStatusNoUpdate // 5: No update // 5: 无更新\n\t\tconfig.LastMessage = \"Backup success, no updates found\"\n\t\terr = nil // Clear error for return\n\t} else {\n\t\tconfig.LastStatus = domain.BackupStatusFailed // 3: Failed // 3: 失败\n\t\tconfig.LastMessage = fmt.Sprintf(\"Backup failed: %v\", err)\n\t}\n\n\t// Use a new context for status update to ensure it persists even if the task context is cancelled\n\tsaveCtx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) // Increased timeout for file deletion\n\tdefer cancel()\n\n\ts.calculateNextRunTime(config)\n\ts.backupRepo.SaveConfig(saveCtx, config, config.UID)\n\n\tif config.RetentionDays != 0 {\n\t\tvar cutoffTime time.Time\n\t\tif config.RetentionDays == -1 {\n\t\t\t// -1 means clean up all history except the current one\n\t\t\tcutoffTime = startTime\n\t\t} else if config.RetentionDays > 0 {\n\t\t\t// > 0 means clean up history older than RetentionDays\n\t\t\tcutoffTime = time.Now().AddDate(0, 0, -config.RetentionDays)\n\t\t}\n\n\t\tif !cutoffTime.IsZero() {\n\t\t\t// 1. Fetch old history before deleting from DB\n\t\t\t// 1. 在从 DB 删除前获取旧的历史记录\n\t\t\toldHistories, err := s.backupRepo.ListOldHistory(saveCtx, config.UID, config.ID, cutoffTime)\n\t\t\tif err != nil {\n\t\t\t\ts.logger.Error(\"Failed to list old backup history for cleanup\", zap.Error(err))\n\t\t\t} else {\n\t\t\t\t// 2. Delete corresponding files in storage for non-sync backups\n\t\t\t// 2. 对于非同步备份，删除存储中对应的文件\n\t\t\t\tfor _, history := range oldHistories {\n\t\t\t\t\tif history.Type != \"sync\" && history.FilePath != \"\" {\n\t\t\t\t\t\tst, err := s.storageService.Get(saveCtx, history.UID, history.StorageID)\n\t\t\t\t\t\tif err != nil || st == nil || !st.IsEnabled {\n\t\t\t\t\t\t\ts.logger.Warn(\"Could not get storage client for cleanup or storage disabled\", zap.Int64(\"sid\", history.StorageID), zap.Error(err))\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tclient, err := s.getStorageClient(saveCtx, history.UID, st)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\ts.logger.Warn(\"Failed to initialize storage client for cleanup\", zap.Error(err))\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif err := client.Delete(history.FilePath); err != nil {\n\t\t\t\t\t\t\ts.logger.Warn(\"Failed to delete old backup file\", zap.String(\"file\", history.FilePath), zap.Error(err))\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ts.logger.Info(\"Successfully deleted old backup file\", zap.String(\"file\", history.FilePath))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 3. Delete records from database\n\t\t\t// 3. 从数据库中删除记录\n\t\t\tif err := s.backupRepo.DeleteOldHistory(saveCtx, config.UID, config.ID, cutoffTime); err != nil {\n\t\t\t\ts.logger.Error(\"Failed to delete old backup history records from database\", zap.Error(err))\n\t\t\t}\n\t\t}\n\t}\n\n\treturn err\n}\n\n// exportArchiveFiles Export files to be backed up to temp directory for subsequent archiving\n// 将需要备份的文件导出到临时目录，用于后续打包\nfunc (s *backupService) exportArchiveFiles(ctx context.Context, uid, vaultID int64, targetDir string, incremental bool, lastRun time.Time) (int64, int64, error) {\n\tif vaultID <= 0 {\n\t\treturn 0, 0, code.ErrorBackupVaultRequired\n\t}\n\n\tvault, err := s.vaultRepo.GetByID(ctx, vaultID, uid)\n\tif err != nil {\n\t\treturn 0, 0, err\n\t}\n\tif vault == nil {\n\t\treturn 0, 0, code.ErrorVaultNotFound\n\t}\n\n\ttotalCount := int64(0)\n\ttotalSize := int64(0)\n\n\terr = s.forEachResource(ctx, uid, vault, incremental, lastRun, func(v *domain.Vault, path string, isNote bool, content []byte, localSize int64, localPath string, mtime time.Time, isDeleted bool) error {\n\t\tif isDeleted {\n\t\t\treturn nil\n\t\t}\n\n\t\tdestPath := filepath.Join(targetDir, path)\n\t\tif err := os.MkdirAll(filepath.Dir(destPath), 0755); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif isNote {\n\t\t\tif err := os.WriteFile(destPath, content, 0644); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tif err := util.CopyFile(localPath, destPath); err != nil {\n\t\t\t\t// Skip missing files instead of failing the entire backup.\n\t\t\t\t// This can happen when the DB record exists but the file\n\t\t\t\t// has been manually deleted or lost due to data inconsistency.\n\t\t\t\tif os.IsNotExist(err) {\n\t\t\t\t\ts.logger.Warn(\"Skipping backup of missing file\",\n\t\t\t\t\t\tzap.String(\"path\", path),\n\t\t\t\t\t\tzap.String(\"localPath\", localPath))\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\ttotalCount++\n\t\ttotalSize += localSize\n\t\treturn nil\n\t})\n\n\treturn totalCount, totalSize, err\n}\n\n// uploadArchive Upload the archived ZIP file to specified storage target\n// 将打包好的 ZIP 文件上传到指定的存储目标\nfunc (s *backupService) uploadArchive(ctx context.Context, uid, configId int64, stDTO *dto.StorageDTO, filePath, fileName, bType string, startTime time.Time, count, size int64) {\n\th := &domain.BackupHistory{\n\t\tUID:       uid,\n\t\tConfigID:  configId,\n\t\tStorageID: stDTO.ID,\n\t\tType:      bType,\n\t\tStartTime: startTime,\n\t\tStatus:    domain.BackupStatusRunning,\n\t\tFileCount: count,\n\t\tFileSize:  size,\n\t\tFilePath:  fileName,\n\t}\n\n\th, err := s.backupRepo.CreateHistory(ctx, h, uid)\n\tif err != nil {\n\t\ts.logger.Error(\"Failed to create backup history\", zap.Error(err))\n\t\treturn\n\t}\n\n\tclient, err := s.getStorageClient(ctx, uid, stDTO)\n\tif err != nil {\n\t\ts.updateHistory(ctx, h, domain.BackupStatusFailed, err.Error())\n\t\treturn\n\t}\n\n\tf, err := os.Open(filePath)\n\tif err != nil {\n\t\ts.updateHistory(ctx, h, domain.BackupStatusFailed, fmt.Sprintf(\"Failed to open backup file: %v\", err))\n\t\treturn\n\t}\n\tdefer f.Close()\n\n\t_, err = client.SendFile(fileName, f, \"application/zip\", startTime)\n\tif err != nil {\n\t\ts.updateHistory(ctx, h, domain.BackupStatusFailed, fmt.Sprintf(\"Upload failed: %v\", err))\n\t\treturn\n\t}\n\n\ts.updateHistory(ctx, h, domain.BackupStatusSuccess, \"Success\")\n}\n\n// syncFiles Sync file changes to specified storage target (supports add, modify, delete)\n// returns (hasChanges, error)\n// 将文件变更同步到指定的存储目标 (支持新增、修改和删除)\nfunc (s *backupService) syncFiles(ctx context.Context, uid, vaultID, configId int64, stDTO *dto.StorageDTO, startTime time.Time, lastRun time.Time, includeVaultName bool) (bool, error) {\n\tvar h *domain.BackupHistory\n\tvar client pkgstorage.Storager\n\n\tif stDTO != nil {\n\t\th = &domain.BackupHistory{\n\t\t\tUID:       uid,\n\t\t\tConfigID:  configId,\n\t\t\tStorageID: stDTO.ID,\n\t\t\tType:      \"sync\",\n\t\t\tStartTime: startTime,\n\t\t\tStatus:    domain.BackupStatusRunning,\n\t\t}\n\n\t\tvar err error\n\t\th, err = s.backupRepo.CreateHistory(ctx, h, uid)\n\t\tif err != nil {\n\t\t\ts.logger.Error(\"Failed to create sync history\", zap.Error(err))\n\t\t\treturn false, err\n\t\t}\n\n\t\tclient, err = s.getStorageClient(ctx, uid, stDTO)\n\t\tif err != nil {\n\t\t\ts.updateHistory(ctx, h, domain.BackupStatusFailed, err.Error())\n\t\t\treturn false, err\n\t\t}\n\t}\n\n\tif vaultID <= 0 {\n\t\tif h != nil {\n\t\t\ts.updateHistory(ctx, h, domain.BackupStatusFailed, code.ErrorBackupVaultRequired.Msg())\n\t\t}\n\t\treturn false, code.ErrorBackupVaultRequired\n\t}\n\n\tvault, err := s.vaultRepo.GetByID(ctx, vaultID, uid)\n\tif err != nil {\n\t\tif h != nil {\n\t\t\ts.updateHistory(ctx, h, domain.BackupStatusFailed, err.Error())\n\t\t}\n\t\treturn false, err\n\t}\n\tif vault == nil {\n\t\tif h != nil {\n\t\t\ts.updateHistory(ctx, h, domain.BackupStatusFailed, code.ErrorVaultNotFound.Msg())\n\t\t}\n\t\treturn false, code.ErrorVaultNotFound\n\t}\n\n\ttotalCount, totalSize := int64(0), int64(0)\n\tfailedCount := int64(0)\n\tvar lastSendErr error\n\thasChanges := false\n\terr = s.forEachResource(ctx, uid, vault, !lastRun.IsZero(), lastRun, func(v *domain.Vault, path string, isNote bool, content []byte, localSize int64, localPath string, mtime time.Time, isDeleted bool) error {\n\t\thasChanges = true\n\t\tif client == nil {\n\t\t\treturn nil // Just checking for changes // 仅检查变更\n\t\t}\n\n\t\tobjName := path\n\t\tif includeVaultName && v != nil {\n\t\t\tobjName = v.Name + \"/\" + path\n\t\t}\n\t\tif isDeleted {\n\t\t\tif delErr := client.Delete(objName); delErr != nil {\n\t\t\t\tfailedCount++\n\t\t\t\tlastSendErr = delErr\n\t\t\t\ts.logger.Warn(\"Sync delete failed\", zap.String(\"path\", objName), zap.Error(delErr))\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\n\t\tvar sendErr error\n\t\tif isNote {\n\t\t\t_, sendErr = client.SendContent(objName, content, mtime)\n\t\t} else {\n\t\t\tif f, err := os.Open(localPath); err == nil {\n\t\t\t\t_, sendErr = client.SendFile(objName, f, \"application/octet-stream\", mtime)\n\t\t\t\tf.Close()\n\t\t\t} else {\n\t\t\t\tsendErr = err\n\t\t\t}\n\t\t}\n\n\t\tif sendErr != nil {\n\t\t\tfailedCount++\n\t\t\tlastSendErr = sendErr\n\t\t\ts.logger.Warn(\"Sync upload failed\", zap.String(\"path\", objName), zap.Error(sendErr))\n\t\t} else {\n\t\t\ttotalCount++\n\t\t\ttotalSize += localSize\n\t\t}\n\t\treturn nil\n\t})\n\n\tif err != nil {\n\t\tif h != nil {\n\t\t\ts.updateHistory(ctx, h, domain.BackupStatusFailed, err.Error())\n\t\t}\n\t\treturn hasChanges, err\n\t}\n\n\tif h != nil {\n\t\th.FileCount = totalCount\n\t\th.FileSize = totalSize\n\t\tif !hasChanges {\n\t\t\ts.updateHistory(ctx, h, domain.BackupStatusNoUpdate, \"No updates\") // No updates // 无更新\n\t\t} else if failedCount > 0 {\n\t\t\tmsg := fmt.Sprintf(\"Partial failure: %d files synced, %d files failed. Last error: %v\", totalCount, failedCount, lastSendErr)\n\t\t\ts.updateHistory(ctx, h, domain.BackupStatusFailed, msg)\n\t\t} else {\n\t\t\ts.updateHistory(ctx, h, domain.BackupStatusSuccess, \"Success\") // Success // 成功\n\t\t}\n\t}\n\n\tif failedCount > 0 {\n\t\treturn hasChanges, fmt.Errorf(\"sync completed with %d failures, last error: %w\", failedCount, lastSendErr)\n\t}\n\treturn hasChanges, nil\n}\n\ntype resourceAction func(v *domain.Vault, path string, isNote bool, content []byte, localSize int64, localPath string, mtime time.Time, isDeleted bool) error // resourceAction 定义资源处理动作 // resourceAction defines resource processing action\n\n// forEachResource Iterate through all resources (notes and attachments) in the specified vault\n// 遍历指定 Vault 中的所有资源 (笔记和附件)\nfunc (s *backupService) forEachResource(ctx context.Context, uid int64, v *domain.Vault, incremental bool, lastRun time.Time, action resourceAction) error {\n\t// Check context before processing\n\tif ctx.Err() != nil {\n\t\treturn ctx.Err()\n\t}\n\n\t// 1. Handle notes\n\t// 1. 处理笔记\n\tvar notes []*domain.Note\n\tvar err error\n\tif incremental && !lastRun.IsZero() {\n\t\tnotes, err = s.noteRepo.ListByUpdatedTimestamp(ctx, lastRun.UnixMilli(), v.ID, uid)\n\t} else {\n\t\t// List notes // 列出笔记\n\t\t// List(ctx, vaultID, page, pageSize, uid, keyword, isDeleted, sort, isAsc, tag, folder)\n\t\tnotes, err = s.noteRepo.List(ctx, v.ID, 1, 1000000, uid, \"\", false, \"\", false, \"\", \"\", nil)\n\t}\n\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, n := range notes {\n\t\tif ctx.Err() != nil {\n\t\t\treturn ctx.Err()\n\t\t}\n\t\tpath := n.Path\n\t\tif filepath.Ext(path) != \".md\" {\n\t\t\tpath += \".md\"\n\t\t}\n\t\tif err := action(v, path, true, []byte(n.Content), int64(len(n.Content)), \"\", time.UnixMilli(n.Mtime), n.IsDeleted()); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t// 2. Handle attachments\n\t// 2. 处理附件\n\tvar files []*domain.File\n\tif incremental && !lastRun.IsZero() {\n\t\tfiles, err = s.fileRepo.ListByUpdatedTimestamp(ctx, lastRun.UnixMilli(), v.ID, uid)\n\t} else {\n\t\tfiles, err = s.fileRepo.List(ctx, v.ID, 1, 1000000, uid, \"\", false, \"\", \"\")\n\t}\n\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, f := range files {\n\t\tif ctx.Err() != nil {\n\t\t\treturn ctx.Err()\n\t\t}\n\t\tvar size int64\n\t\t// Check file existence/size if not deleted // 如果未删除，检查文件是否存在/大小\n\t\tif !f.IsDeleted() {\n\t\t\tif info, _ := os.Stat(f.SavePath); info != nil {\n\t\t\t\tsize = info.Size()\n\t\t\t}\n\t\t}\n\t\tif err := action(v, f.Path, false, nil, size, f.SavePath, time.UnixMilli(f.Mtime), f.IsDeleted()); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// getStorageClient Get and initialize storage client\n// 获取并初始化存储客户端\nfunc (s *backupService) getStorageClient(ctx context.Context, uid int64, stDTO *dto.StorageDTO) (pkgstorage.Storager, error) {\n\tsConfig := &pkgstorage.Config{\n\t\tType:            stDTO.Type,\n\t\tCustomPath:      stDTO.CustomPath,\n\t\tEndpoint:        stDTO.Endpoint,\n\t\tRegion:          stDTO.Region,\n\t\tBucketName:      stDTO.BucketName,\n\t\tAccessKeyID:     stDTO.AccessKeyID,\n\t\tAccessKeySecret: stDTO.AccessKeySecret,\n\t\tAccountID:       stDTO.AccountID,\n\t\tUser:            stDTO.User,\n\t\tPassword:        stDTO.Password,\n\t\tSavePath:        s.storageConfig.LocalFS.SavePath,\n\t}\n\n\treturn pkgstorage.NewClient(sConfig)\n}\n\nfunc (s *backupService) updateHistory(ctx context.Context, h *domain.BackupHistory, status int, message string) {\n\th.Status = status\n\th.Message = message\n\th.EndTime = time.Now()\n\n\t// Use a new context for history update to ensure it persists even if the task context is cancelled\n\tsaveCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\tdefer cancel()\n\n\ts.backupRepo.CreateHistory(saveCtx, h, h.UID)\n}\n\nfunc (s *backupService) recordNoUpdateHistory(ctx context.Context, config *domain.BackupConfig, startTime time.Time) {\n\tvar storageIds []int64\n\tif err := json.Unmarshal([]byte(config.StorageIds), &storageIds); err != nil {\n\t\treturn\n\t}\n\n\tfor _, sid := range storageIds {\n\t\th := &domain.BackupHistory{\n\t\t\tUID:       config.UID,\n\t\t\tConfigID:  config.ID,\n\t\t\tStorageID: sid,\n\t\t\tType:      config.Type,\n\t\t\tStartTime: startTime,\n\t\t\tStatus:    domain.BackupStatusNoUpdate,\n\t\t\tMessage:   \"No updates\",\n\t\t\tEndTime:   time.Now(),\n\t\t}\n\t\t// Use a new context for history update\n\t\tsaveCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\t\ts.backupRepo.CreateHistory(saveCtx, h, config.UID)\n\t\tcancel()\n\t}\n}\n\nconst syncDebounceDelay = 30 * time.Second\n\n// NotifyUpdated Trigger debounced incremental sync task\n// Called when note/file/folder changes, executes ExecuteUserBackup after syncDebounceDelay\n// 触发防抖的增量同步任务\n// 当笔记/文件/目录发生变更时调用，会在延迟 syncDebounceDelay 后执行 ExecuteUserBackup\nfunc (s *backupService) NotifyUpdated(uid int64) {\n\ts.timerMu.Lock()\n\tdefer s.timerMu.Unlock()\n\n\tif timer, ok := s.syncTimers[uid]; ok {\n\t\ttimer.Stop()\n\t}\n\n\ts.syncTimers[uid] = time.AfterFunc(syncDebounceDelay, func() {\n\t\ts.logger.Info(\"Triggering debounced sync (memory flag)\", zap.Int64(\"uid\", uid))\n\n\t\t// Set in-memory flag instead of DB write // 设置内存标志而非 DB 写入\n\t\ts.pendingSyncs.Store(uid, true)\n\n\t\ts.timerMu.Lock()\n\t\tdelete(s.syncTimers, uid)\n\t\ts.timerMu.Unlock()\n\t})\n}\n\n// Shutdown Clean up resources and handle state changes during shutdown\n// 停止服务，清理资源并处理关闭时的状态变更\nfunc (s *backupService) Shutdown(ctx context.Context) error {\n\t// 1. Signal all background tasks to stop\n\t// 1. 通知所有后台任务停止\n\ts.cancel()\n\n\ts.timerMu.Lock()\n\t// Stop all pending sync timers\n\t// 停止所有待执行的同步定时器\n\tfor uid, timer := range s.syncTimers {\n\t\tif timer.Stop() {\n\t\t\ts.logger.Info(\"Stopped pending sync timer during shutdown\", zap.Int64(\"uid\", uid))\n\t\t}\n\t}\n\t// Clear the map\n\t// 清空 map\n\ts.syncTimers = make(map[int64]*time.Timer)\n\ts.timerMu.Unlock()\n\n\t// 2. Wait for active backup/sync tasks to finish or abort\n\t// 2. 等待活跃的备份/同步任务完成或中止\n\t// We use a channel to support timeout if needed, though ctx passed to Shutdown usually handles timeout\n\t// 我们使用 channel 来支持必要的超时，尽管传给 Shutdown 的 ctx 通常会处理超时\n\tdone := make(chan struct{})\n\tgo func() {\n\t\ts.wg.Wait()\n\t\tclose(done)\n\t}()\n\n\tselect {\n\tcase <-done:\n\t\ts.logger.Info(\"All backup tasks finished successfully during shutdown\")\n\tcase <-ctx.Done():\n\t\ts.logger.Warn(\"Shutdown context expired before all backup tasks finished\")\n\t\treturn ctx.Err()\n\t}\n\n\treturn nil\n}\n\nvar _ BackupService = (*backupService)(nil)\n"
  },
  {
    "path": "internal/service/backup_service_test.go",
    "content": "// Package service implements the business logic layer.\n// Package service 实现业务逻辑层。\npackage service\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\tdomainmocks \"github.com/haierkeys/fast-note-sync-service/internal/domain/mocks\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/mock\"\n\t\"go.uber.org/zap\"\n)\n\n// backupStorageStub is a minimal StorageService stub for backup tests.\n// backupStorageStub 是用于备份测试的最小 StorageService stub，避免循环导入。\n// Note: service/mocks imports service, so it cannot be imported back in service package tests.\n// 注意: service/mocks 包导入了 service，因此在 service 包测试中不能反向导入。\ntype backupStorageStub struct {\n\tstorages map[int64]*dto.StorageDTO\n}\n\nfunc (s *backupStorageStub) Get(_ context.Context, _ int64, id int64) (*dto.StorageDTO, error) {\n\tif v, ok := s.storages[id]; ok {\n\t\treturn v, nil\n\t}\n\treturn nil, nil\n}\nfunc (s *backupStorageStub) CreateOrUpdate(_ context.Context, _ int64, _ int64, _ *dto.StoragePostRequest) (*dto.StorageDTO, error) {\n\treturn nil, nil\n}\nfunc (s *backupStorageStub) List(_ context.Context, _ int64) ([]*dto.StorageDTO, error) {\n\treturn nil, nil\n}\nfunc (s *backupStorageStub) Delete(_ context.Context, _ int64, _ int64) error { return nil }\nfunc (s *backupStorageStub) GetEnabledTypes() ([]string, error)               { return nil, nil }\nfunc (s *backupStorageStub) Validate(_ context.Context, _ *dto.StoragePostRequest) error {\n\treturn nil\n}\n\n// --- BackupService constructor helper ---\n\n// newBackupSvc builds a backupService with mock dependencies for testing.\n// newBackupSvc 使用 mock 依赖构建用于测试的 backupService。\nfunc newBackupSvc(\n\tbackupRepo *domainmocks.MockBackupRepository,\n\tvaultRepo *domainmocks.MockVaultRepository,\n\tstorageSvc *backupStorageStub,\n) *backupService {\n\treturn &backupService{\n\t\tbackupRepo:     backupRepo,\n\t\tnoteRepo:       new(domainmocks.MockNoteRepository),\n\t\tfolderRepo:     new(domainmocks.MockFolderRepository),\n\t\tfileRepo:       new(domainmocks.MockFileRepository),\n\t\tvaultRepo:      vaultRepo,\n\t\tstorageService: storageSvc,\n\t\tlogger:         zap.NewNop(),\n\t\tsyncTimers:     make(map[int64]*time.Timer),\n\t\trunningTasks:   make(map[int64]context.CancelFunc),\n\t}\n}\n\n// --- GetConfigs ---\n\n// TestBackupService_GetConfigs_Success verifies that GetConfigs returns mapped DTOs.\n// TestBackupService_GetConfigs_Success 验证 GetConfigs 正确返回映射后的 DTO 列表。\nfunc TestBackupService_GetConfigs_Success(t *testing.T) {\n\tbackupRepo := new(domainmocks.MockBackupRepository)\n\tvaultRepo := new(domainmocks.MockVaultRepository)\n\tstorageSvc := &backupStorageStub{}\n\n\tconfigs := []*domain.BackupConfig{\n\t\t{ID: 1, UID: 1, Type: \"full\", IsEnabled: true},\n\t\t{ID: 2, UID: 1, Type: \"incremental\", IsEnabled: false},\n\t}\n\tbackupRepo.On(\"ListConfigs\", mock.Anything, int64(1)).Return(configs, nil)\n\n\t// GetConfigs internally calls VaultRepo.GetByID for each config's VaultID\n\t// GetConfigs 内部会对每个 config 的 VaultID 调用 VaultRepo.GetByID\n\tvaultRepo.On(\"GetByID\", mock.Anything, int64(0), int64(1)).Return(nil, nil).Maybe()\n\n\tsvc := newBackupSvc(backupRepo, vaultRepo, storageSvc)\n\tresult, err := svc.GetConfigs(context.Background(), 1)\n\n\tassert.NoError(t, err)\n\tassert.Len(t, result, 2)\n\tassert.Equal(t, int64(1), result[0].ID)\n\tassert.Equal(t, int64(2), result[1].ID)\n\tbackupRepo.AssertExpectations(t)\n}\n\n// TestBackupService_GetConfigs_Empty verifies that GetConfigs returns empty slice when no configs exist.\n// TestBackupService_GetConfigs_Empty 验证没有备份配置时返回空列表。\nfunc TestBackupService_GetConfigs_Empty(t *testing.T) {\n\tbackupRepo := new(domainmocks.MockBackupRepository)\n\tvaultRepo := new(domainmocks.MockVaultRepository)\n\tstorageSvc := &backupStorageStub{}\n\n\tbackupRepo.On(\"ListConfigs\", mock.Anything, int64(1)).Return([]*domain.BackupConfig{}, nil)\n\n\tsvc := newBackupSvc(backupRepo, vaultRepo, storageSvc)\n\tresult, err := svc.GetConfigs(context.Background(), 1)\n\n\tassert.NoError(t, err)\n\tassert.Empty(t, result)\n\tbackupRepo.AssertExpectations(t)\n}\n\n// --- UpdateConfig ---\n\n// TestBackupService_UpdateConfig_Success verifies config is saved with resolved vault ID.\n// TestBackupService_UpdateConfig_Success 验证配置以解析后的 VaultID 保存。\nfunc TestBackupService_UpdateConfig_Success(t *testing.T) {\n\tbackupRepo := new(domainmocks.MockBackupRepository)\n\tvaultRepo := new(domainmocks.MockVaultRepository)\n\tstorageSvc := &backupStorageStub{storages: map[int64]*dto.StorageDTO{\n\t\t200: {ID: 200, IsEnabled: true},\n\t}}\n\n\tvault := &domain.Vault{ID: 100, Name: \"myvault\"}\n\tvaultRepo.On(\"GetByName\", mock.Anything, \"myvault\", int64(1)).Return(vault, nil)\n\n\t// storageSvc.Get is handled by backupStorageStub directly.\n\t// StorageService.Get 由 backupStorageStub 直接处理，无需 mock.On 配置。\n\n\tsavedConfig := &domain.BackupConfig{ID: 1, VaultID: 100, Type: \"full\", IsEnabled: true}\n\tbackupRepo.On(\"SaveConfig\", mock.Anything, mock.MatchedBy(func(c *domain.BackupConfig) bool {\n\t\treturn c.VaultID == 100 && c.Type == \"full\"\n\t}), int64(1)).Return(savedConfig, nil)\n\n\t// GetByID is called after save from configToDTO; the uid passed is config.UID (0 in this test fixture).\n\t// configToDTO 调用 GetByID 时传入的 uid 是 config.UID（本测试 fixture 为 0）。\n\tvaultRepo.On(\"GetByID\", mock.Anything, int64(100), int64(0)).Return(vault, nil)\n\n\tsvc := newBackupSvc(backupRepo, vaultRepo, storageSvc)\n\treq := &dto.BackupConfigRequest{\n\t\tVault:      \"myvault\",\n\t\tStorageIds: \"[200]\",\n\t\tType:       \"full\",\n\t\tIsEnabled:  true,\n\t}\n\tresult, err := svc.UpdateConfig(context.Background(), 1, req)\n\n\tassert.NoError(t, err)\n\tassert.NotNil(t, result)\n\tassert.Equal(t, \"myvault\", result.Vault)\n\tbackupRepo.AssertExpectations(t)\n\tvaultRepo.AssertExpectations(t)\n}\n\n// --- DeleteConfig ---\n\n// TestBackupService_DeleteConfig_Success verifies existing config is deleted.\n// TestBackupService_DeleteConfig_Success 验证成功删除已存在的备份配置。\nfunc TestBackupService_DeleteConfig_Success(t *testing.T) {\n\tbackupRepo := new(domainmocks.MockBackupRepository)\n\tvaultRepo := new(domainmocks.MockVaultRepository)\n\tstorageSvc := &backupStorageStub{}\n\n\tbackupRepo.On(\"GetByID\", mock.Anything, int64(1), int64(1)).Return(\n\t\t&domain.BackupConfig{ID: 1, UID: 1}, nil,\n\t)\n\tbackupRepo.On(\"DeleteConfig\", mock.Anything, int64(1), int64(1)).Return(nil)\n\n\tsvc := newBackupSvc(backupRepo, vaultRepo, storageSvc)\n\terr := svc.DeleteConfig(context.Background(), 1, 1)\n\n\tassert.NoError(t, err)\n\tbackupRepo.AssertExpectations(t)\n}\n\n// TestBackupService_DeleteConfig_NotFound verifies error when config does not exist.\n// TestBackupService_DeleteConfig_NotFound 验证删除不存在的配置时返回错误。\nfunc TestBackupService_DeleteConfig_NotFound(t *testing.T) {\n\tbackupRepo := new(domainmocks.MockBackupRepository)\n\tvaultRepo := new(domainmocks.MockVaultRepository)\n\tstorageSvc := &backupStorageStub{}\n\n\tbackupRepo.On(\"GetByID\", mock.Anything, int64(999), int64(1)).Return(nil, nil)\n\n\tsvc := newBackupSvc(backupRepo, vaultRepo, storageSvc)\n\terr := svc.DeleteConfig(context.Background(), 1, 999)\n\n\tassert.Error(t, err)\n\tbackupRepo.AssertExpectations(t)\n}\n"
  },
  {
    "path": "internal/service/cloudflare_service.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"go.uber.org/zap\"\n)\n\n// CloudflareService provides Cloudflare Tunnel service\n// CloudflareService 提供 Cloudflare Tunnel 隧道服务\ntype CloudflareService interface {\n\tStart(ctx context.Context, token string, logEnabled bool) error\n\tStop(ctx context.Context) error\n\tTunnelURL() string\n\t// DownloadBinary downloads the cloudflared binary and returns the path or a detailed error\n\t// DownloadBinary 下载 cloudflared 二进制文件，返回路径或包含手动下载建议的详细错误\n\tDownloadBinary() (string, error)\n}\n\ntype cloudflareService struct {\n\tlogger     zap.Logger\n\ttoken      string\n\tlogEnabled bool\n\turl        string\n\tctx        context.Context\n\tcancel     context.CancelFunc\n\twg         sync.WaitGroup\n\tcmd        *exec.Cmd\n}\n\n// NewCloudflareService creates a new Cloudflare service\n// NewCloudflareService 创建一个新的 Cloudflare 服务\nfunc NewCloudflareService(logger *zap.Logger) CloudflareService {\n\treturn &cloudflareService{\n\t\tlogger: *logger,\n\t}\n}\n\n// Start starts the Cloudflare tunnel\n// Start 启动 Cloudflare 隧道\nfunc (s *cloudflareService) Start(ctx context.Context, token string, logEnabled bool) error {\n\tif token == \"\" {\n\t\treturn fmt.Errorf(\"cloudflare tunnel token is required\")\n\t}\n\ts.token = token\n\ts.logEnabled = logEnabled\n\n\ts.ctx, s.cancel = context.WithCancel(ctx)\n\n\ts.logger.Info(\"Starting Cloudflare Tunnel service...\")\n\n\t// Ensure binary exists\n\t// 确保二进制文件存在\n\tbinPath, err := s.DownloadBinary()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ts.wg.Add(1)\n\tgo func() {\n\t\tdefer s.wg.Done()\n\t\tif err := s.runTunnelProcess(s.ctx, binPath, token); err != nil {\n\t\t\ts.logger.Error(\"Cloudflare Tunnel process failed\", zap.Error(err))\n\t\t}\n\t}()\n\n\treturn nil\n}\n\n// DownloadBinary implements active download logic\n// DownloadBinary 实现主动下载逻辑\nfunc (s *cloudflareService) DownloadBinary() (string, error) {\n\tstorageDir := \"storage/cloudflared_tunnel\"\n\tif err := os.MkdirAll(storageDir, 0755); err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to create storage directory: %w\", err)\n\t}\n\n\text := \"\"\n\tif runtime.GOOS == \"windows\" {\n\t\text = \".exe\"\n\t}\n\n\tgoos := runtime.GOOS\n\tgoarch := runtime.GOARCH\n\tfileName := fmt.Sprintf(\"cloudflared-%s-%s%s\", goos, goarch, ext)\n\tbinPath := filepath.Join(storageDir, fileName)\n\n\t// If file already exists and is executable\n\t// 如果文件已存在且可执行\n\tif _, err := os.Stat(binPath); err == nil {\n\t\treturn binPath, nil\n\t}\n\n\t// Construct download URL\n\t// 构造下载链接\n\tdownloadURL := \"https://github.com/cloudflare/cloudflared/releases/latest/download/\" + fileName\n\n\ts.logger.Info(\"Cloudflared binary not found, attempting to download...\", zap.String(\"url\", downloadURL))\n\n\t// Execute download\n\t// 执行下载\n\tclient := &http.Client{Timeout: 5 * time.Minute}\n\tresp, err := client.Get(downloadURL)\n\tif err != nil {\n\t\tif code.GetGlobalDefaultLang() == \"zh_cn\" {\n\t\t\treturn \"\", fmt.Errorf(\"下载失败:\\n%v。 \\n[💡 建议] 请手动下载: %s \\n并放置于: %s\", err, downloadURL, storageDir)\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"download failed:\\n%v. \\n[💡 Suggestion] Please manually download from: %s \\nAnd place it in: %s\", err, downloadURL, storageDir)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tif code.GetGlobalDefaultLang() == \"zh_cn\" {\n\t\t\treturn \"\", fmt.Errorf(\"下载服务器返回状态 %s。 \\n[💡 建议] 请手动下载: \\n %s \\n并放置于: %s\", resp.Status, downloadURL, storageDir)\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"download server returned %s. \\n[💡 Suggestion] Please manually download from:\\n %s \\nAnd place it in: %s\", resp.Status, downloadURL, storageDir)\n\t}\n\n\t// Save to file\n\t// 保存到文件\n\tout, err := os.Create(binPath)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to create binary file: %w\", err)\n\t}\n\tdefer out.Close()\n\n\tif _, err = io.Copy(out, resp.Body); err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to save binary: %w\", err)\n\t}\n\n\t// Grant execution permission (Unix)\n\t// 赋予执行权限 (Unix)\n\tif runtime.GOOS != \"windows\" {\n\t\tif err := os.Chmod(binPath, 0755); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\n\ts.logger.Info(\"Cloudflared binary downloaded successfully\", zap.String(\"path\", binPath))\n\treturn binPath, nil\n}\n\n// runTunnelProcess runs external process\n// runTunnelProcess 运行外部进程\nfunc (s *cloudflareService) runTunnelProcess(ctx context.Context, binPath, token string) error {\n\tvar writers []io.Writer\n\twriters = append(writers, os.Stdout)\n\n\tvar errWriters []io.Writer\n\terrWriters = append(errWriters, os.Stderr)\n\n\tif s.logEnabled {\n\t\t// Ensure system log directory exists\n\t\t// 确保统一日志目录存在\n\t\tlogDir := \"storage/logs\"\n\t\tif err := os.MkdirAll(logDir, 0755); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to create log directory: %w\", err)\n\t\t}\n\t\tlogPath := filepath.Join(logDir, \"cloudflared_tunnel.log\")\n\n\t\tlogFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to open cloudflared log file: %w\", err)\n\t\t}\n\t\tdefer logFile.Close()\n\n\t\twriters = append(writers, logFile)\n\t\terrWriters = append(errWriters, logFile)\n\t\ts.logger.Info(\"Cloudflare Tunnel logging enabled\", zap.String(\"logPath\", logPath))\n\t}\n\n\t// Using context.WithCancel ensures child processes are killed when the main context is cancelled\n\t// 使用 context.WithCancel 可以确保主 Context 取消时，子进程也被杀死\n\ts.cmd = exec.CommandContext(ctx, binPath, \"tunnel\", \"--no-autoupdate\", \"run\", \"--token\", token)\n\n\ts.cmd.Stdout = io.MultiWriter(writers...)\n\ts.cmd.Stderr = io.MultiWriter(errWriters...)\n\n\ts.logger.Info(\"Lauching cloudflared process...\")\n\tif err := s.cmd.Start(); err != nil {\n\t\treturn err\n\t}\n\n\t// Wait for process completion\n\t// 等待进程结束\n\tif err := s.cmd.Wait(); err != nil && ctx.Err() == nil {\n\t\treturn fmt.Errorf(\"cloudflared exited unexpectedly: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// Stop stops the Cloudflare tunnel\n// Stop 停止 Cloudflare 隧道\nfunc (s *cloudflareService) Stop(ctx context.Context) error {\n\ts.logger.Info(\"Shutting down Cloudflare Tunnel service...\")\n\tif s.cancel != nil {\n\t\ts.cancel()\n\t}\n\n\tdone := make(chan struct{})\n\tgo func() {\n\t\ts.wg.Wait()\n\t\tclose(done)\n\t}()\n\n\tselect {\n\tcase <-done:\n\t\ts.logger.Info(\"Cloudflare Tunnel process terminated\")\n\tcase <-ctx.Done():\n\t\ts.logger.Warn(\"Cloudflare Tunnel shutdown timed out\")\n\t}\n\n\treturn nil\n}\n\n// TunnelURL returns the current tunnel URL\n// TunnelURL 返回当前隧道 URL\nfunc (s *cloudflareService) TunnelURL() string {\n\treturn s.url\n}\n"
  },
  {
    "path": "internal/service/config.go",
    "content": "// Package service implements the business logic layer\n// Package service 实现业务逻辑层\npackage service\n\n// ServiceConfig service layer configuration\n// ServiceConfig 服务层配置\ntype ServiceConfig struct {\n\tUser UserServiceConfig // User related config // 用户相关配置\n\tApp  AppServiceConfig  // App related config // 应用相关配置\n}\n\n// UserServiceConfig user service configuration\n// UserServiceConfig 用户服务配置\ntype UserServiceConfig struct {\n\tRegisterIsEnable bool // Whether registration is enabled // 注册是否启用\n}\n\n// AppServiceConfig app service configuration\n// AppServiceConfig 应用服务配置\ntype AppServiceConfig struct {\n\tSoftDeleteRetentionTime string                 // Soft delete retention time (e.g., 7d, 24h, 30m, 0/empty for no cleanup) // 软删除保留时间（支持格式：7d、24h、30m、0 或空表示不自动清理）\n\tHistoryKeepVersions     int                    // History versions to keep // 历史记录保留版本数\n\tHistorySaveDelay        string                 // History save delay (e.g., 10s, 1m, default 10s) // 历史记录保存延迟时间（支持格式：10s、1m，默认 10s）\n\tShareTokenExpiry        string                 // Share token expiry // 分享 Token 过期时间\n\tShortLink               ShortLinkServiceConfig // Short link configuration // 短链配置\n}\n\n// ShortLinkServiceConfig short link service configuration\n// ShortLinkServiceConfig 短链服务配置\ntype ShortLinkServiceConfig struct {\n\tBaseURL  string // Base URL // 基础 URL\n\tAPIKey   string // API Key // API 密钥\n\tPassword string // Password // 密码\n\tCloaking bool   // Cloaking // 遮盖\n}\n"
  },
  {
    "path": "internal/service/conflict_service.go",
    "content": "// Package service implements the business logic layer\n// Package service 实现业务逻辑层\npackage service\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/logger\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"go.uber.org/zap\"\n)\n\n// ConflictService defines the conflict service interface\n// ConflictService 定义冲突文件服务接口\ntype ConflictService interface {\n\t// CreateConflictFile creates a conflict file\n\t// CreateConflictFile 创建冲突文件\n\t// When merging fails, save the client content as a conflict file\n\t// 当合并失败时，将客户端内容保存为冲突文件\n\tCreateConflictFile(ctx context.Context, uid int64, params *dto.ConflictFileRequest) (*dto.ConflictFileResponse, error)\n}\n\n// conflictService implements ConflictService interface\n// conflictService 实现 ConflictService 接口\ntype conflictService struct {\n\tnoteRepo     domain.NoteRepository\n\tvaultService VaultService\n\tlogger       *zap.Logger\n\tclientName   string\n}\n\n// NewConflictService creates a ConflictService instance\n// NewConflictService 创建 ConflictService 实例\nfunc NewConflictService(noteRepo domain.NoteRepository, vaultSvc VaultService, logger *zap.Logger) ConflictService {\n\treturn &conflictService{\n\t\tnoteRepo:     noteRepo,\n\t\tvaultService: vaultSvc,\n\t\tlogger:       logger,\n\t\tclientName:   \"conflict-service\",\n\t}\n}\n\n// CreateConflictFile creates a conflict file\n// CreateConflictFile 创建冲突文件\nfunc (s *conflictService) CreateConflictFile(ctx context.Context, uid int64, params *dto.ConflictFileRequest) (*dto.ConflictFileResponse, error) {\n\t// Get VaultID\n\t// 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Generate conflict file path\n\t// 生成冲突文件路径\n\tconflictPath := s.generateConflictPath(params.OriginalPath)\n\t\n\t// Generate conflict path hash\n\t// 生成冲突路径哈希\n\tconflictPathHash := util.EncodeHash32(conflictPath)\n\n\t// Create conflict file note\n\t// 创建冲突文件笔记\n\tconflictNote := &domain.Note{\n\t\tVaultID:     vaultID,\n\t\tPath:        conflictPath,\n\t\tPathHash:    conflictPathHash,\n\t\tContent:     params.ClientContent,\n\t\tContentHash: params.ClientContentHash,\n\t\tClientName:  s.clientName,\n\t\tSize:        int64(len(params.ClientContent)),\n\t\tMtime:       params.Mtime,\n\t\tCtime:       params.Ctime,\n\t\tAction:      domain.NoteActionCreate,\n\t}\n\n\t// Save conflict file\n\t// 保存冲突文件\n\tcreated, err := s.noteRepo.Create(ctx, conflictNote, uid)\n\tif err != nil {\n\t\ts.logger.Error(\"failed to create conflict file\",\n\t\t\tzap.String(\"originalPath\", params.OriginalPath),\n\t\t\tzap.String(\"conflictPath\", conflictPath),\n\t\t\tzap.Int64(logger.FieldUID, uid),\n\t\t\tzap.Error(err))\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\ts.logger.Info(\"conflict file created\",\n\t\tzap.String(\"originalPath\", params.OriginalPath),\n\t\tzap.String(\"conflictPath\", conflictPath),\n\t\tzap.Int64(logger.FieldUID, uid),\n\t\tzap.Int64(\"noteId\", created.ID))\n\n\treturn &dto.ConflictFileResponse{\n\t\tConflictPath: conflictPath,\n\t\tMessage:      \"合并失败，已保存冲突版本\",\n\t\tNoteID:       created.ID,\n\t}, nil\n}\n\n// generateConflictPath generates conflict file path\n// Format: {baseName}.conflict.{timestamp}{ext}\n// Example: notes/test.md -> notes/test.conflict.20060102150405.md\n// generateConflictPath 生成冲突文件路径\n// 格式: {baseName}.conflict.{timestamp}{ext}\n// 例如: notes/test.md -> notes/test.conflict.20060102150405.md\nfunc (s *conflictService) generateConflictPath(originalPath string) string {\n\ttimestamp := time.Now().Format(\"20060102150405\")\n\text := filepath.Ext(originalPath)\n\tbaseName := strings.TrimSuffix(originalPath, ext)\n\treturn fmt.Sprintf(\"%s.conflict.%s%s\", baseName, timestamp, ext)\n}\n\n// 确保 conflictService 实现了 ConflictService 接口\nvar _ ConflictService = (*conflictService)(nil)\n"
  },
  {
    "path": "internal/service/conflict_service_test.go",
    "content": "// Package service implements the business logic layer.\n// Package service 实现业务逻辑层。\npackage service\n\nimport (\n\t\"regexp\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/leanovate/gopter\"\n\t\"github.com/leanovate/gopter/gen\"\n\t\"github.com/leanovate/gopter/prop\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\n// TestProperty_ConflictFilePathFormat uses property-based testing to validate that\n// the conflict path format is always correct regardless of input.\n// TestProperty_ConflictFilePathFormat 使用基于属性的测试验证，无论输入如何，冲突路径格式始终正确。\nfunc TestProperty_ConflictFilePathFormat(t *testing.T) {\n\tparameters := gopter.DefaultTestParameters()\n\tparameters.MinSuccessfulTests = 100\n\n\tproperties := gopter.NewProperties(parameters)\n\n\t// Property: generated conflict path must match {base}.conflict.{14-digit-timestamp}{ext}\n\t// 属性：生成的冲突路径必须符合 {base}.conflict.{14位时间戳}{ext} 格式\n\tproperties.Property(\"conflict path matches expected format\", prop.ForAll(\n\t\tfunc(dir, filename, ext string) bool {\n\t\t\tvar originalPath string\n\t\t\tif dir != \"\" {\n\t\t\t\toriginalPath = dir + \"/\" + filename + ext\n\t\t\t} else {\n\t\t\t\toriginalPath = filename + ext\n\t\t\t}\n\n\t\t\tsvc := &conflictService{}\n\t\t\tconflictPath := svc.generateConflictPath(originalPath)\n\n\t\t\t// Pattern: {baseName}.conflict.{14-digit-timestamp}{ext}\n\t\t\t// 格式: {基础名}.conflict.{14位数字时间戳}{扩展名}\n\t\t\tpattern := regexp.MustCompile(`^(.+)\\.conflict\\.(\\d{14})(\\.[^.]+)?$`)\n\t\t\tmatches := pattern.FindStringSubmatch(conflictPath)\n\t\t\tif matches == nil {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Base name must be preserved.\n\t\t\t// 基础名称必须被保留。\n\t\t\tbaseName := matches[1]\n\t\t\texpectedBase := strings.TrimSuffix(originalPath, ext)\n\t\t\tif baseName != expectedBase {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Extension must be preserved.\n\t\t\t// 扩展名必须被保留。\n\t\t\tgotExt := matches[3]\n\t\t\treturn gotExt == ext\n\t\t},\n\t\tgen.AlphaString().SuchThat(func(s string) bool {\n\t\t\treturn !strings.Contains(s, \".\") && !strings.Contains(s, \"/\")\n\t\t}),\n\t\tgen.AlphaString().SuchThat(func(s string) bool {\n\t\t\treturn len(s) > 0 && !strings.Contains(s, \".\") && !strings.Contains(s, \"/\")\n\t\t}),\n\t\tgen.OneConstOf(\".md\", \".txt\", \".json\", \"\"),\n\t))\n\n\tproperties.TestingRun(t)\n}\n\n// TestGenerateConflictPath verifies that generateConflictPath produces correctly formatted paths.\n// TestGenerateConflictPath 验证 generateConflictPath 生成格式正确的路径。\nfunc TestGenerateConflictPath(t *testing.T) {\n\tsvc := &conflictService{}\n\n\ttests := []struct {\n\t\tname         string // test case name / 测试用例名称\n\t\toriginalPath string // input path / 输入路径\n\t\twantContains string // expected substring / 期望包含的子字符串\n\t\twantSuffix   string // expected suffix / 期望的后缀\n\t}{\n\t\t{\n\t\t\tname:         \"markdown file\",\n\t\t\toriginalPath: \"notes/test.md\",\n\t\t\twantContains: \"notes/test.conflict.\",\n\t\t\twantSuffix:   \".md\",\n\t\t},\n\t\t{\n\t\t\tname:         \"nested path\",\n\t\t\toriginalPath: \"folder/subfolder/note.md\",\n\t\t\twantContains: \"folder/subfolder/note.conflict.\",\n\t\t\twantSuffix:   \".md\",\n\t\t},\n\t\t{\n\t\t\tname:         \"no extension\",\n\t\t\toriginalPath: \"README\",\n\t\t\twantContains: \"README.conflict.\",\n\t\t\twantSuffix:   \"\",\n\t\t},\n\t\t{\n\t\t\tname:         \"txt file\",\n\t\t\toriginalPath: \"data.txt\",\n\t\t\twantContains: \"data.conflict.\",\n\t\t\twantSuffix:   \".txt\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := svc.generateConflictPath(tt.originalPath)\n\n\t\t\tassert.Contains(t, got, tt.wantContains, \"path should contain expected base\")\n\n\t\t\tif tt.wantSuffix != \"\" {\n\t\t\t\tassert.True(t, strings.HasSuffix(got, tt.wantSuffix),\n\t\t\t\t\t\"expected suffix %q but got %q\", tt.wantSuffix, got)\n\t\t\t}\n\n\t\t\t// Timestamp must be a 14-digit number.\n\t\t\t// 时间戳必须是 14 位数字。\n\t\t\tpattern := regexp.MustCompile(`\\.conflict\\.(\\d{14})`)\n\t\t\tassert.Regexp(t, pattern, got, \"conflict path should contain 14-digit timestamp\")\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/service/db_utils.go",
    "content": "// Package service implements the business logic layer\n// Package service 实现业务逻辑层\npackage service\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dao\"\n\n\t\"gorm.io/gorm\"\n)\n\n// DBUtils database utility service, providing database migration and SQL execution functions\n// Used for background tasks and upgrade scripts\n// DBUtils 数据库工具服务，提供数据库迁移和 SQL 执行功能\n// 用于后台任务和升级脚本\ntype DBUtils struct {\n\tdao *dao.Dao\n}\n\n// NewDBUtils creates DBUtils instance\n// db: Database connection (required)\n// ctx: Context\n// opts: Dao configuration options\n// NewDBUtils 创建 DBUtils 实例\n// db: 数据库连接（必须）\n// ctx: 上下文\n// opts: Dao 配置选项\nfunc NewDBUtils(db *gorm.DB, ctx context.Context, opts ...dao.DaoOption) *DBUtils {\n\treturn &DBUtils{\n\t\tdao: dao.New(db, ctx, opts...),\n\t}\n}\n\n// ExposeAutoMigrate exposes automatic migration interface\n// ExposeAutoMigrate 暴露自动迁移接口\nfunc (u *DBUtils) ExposeAutoMigrate() error {\n\t// Migrate user table first\n\t// 先迁移 User 表\n\terr := u.dao.AutoMigrate(0, \"User\")\n\tif err != nil {\n\t\treturn err\n\t}\n\t// Get all user UIDs\n\t// 获取所有用户 UID\n\tuids, err := u.dao.GetAllUserUIDs()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, uid := range uids {\n\t\terr = u.dao.AutoMigrate(uid, \"\")\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// ExecuteSQL executes SQL interface\n// ExecuteSQL 执行 SQL 接口\nfunc (u *DBUtils) ExecuteSQL(sql string) error {\n\tdb := u.dao.ResolveDB()\n\tif db != nil {\n\t\tdb.Exec(sql)\n\t}\n\treturn nil\n}\n\n// GetAllUserUIDs retrieves all user UIDs\n// GetAllUserUIDs 获取所有用户的 UID\nfunc (u *DBUtils) GetAllUserUIDs() ([]int64, error) {\n\treturn u.dao.GetAllUserUIDs()\n}\n"
  },
  {
    "path": "internal/service/file_service.go",
    "content": "// Package service implements the business logic layer\n// Package service 实现业务逻辑层\npackage service\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"mime\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/logger\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"go.uber.org/zap\"\n\t\"golang.org/x/sync/singleflight\"\n\t\"gorm.io/gorm\"\n)\n\n// FileService defines the file business service interface\n// FileService 定义文件业务服务接口\ntype FileService interface {\n\t// Get retrieves a single file\n\t// Get 获取单条文件\n\tGet(ctx context.Context, uid int64, params *dto.FileGetRequest) (*dto.FileDTO, error)\n\n\t// UpdateCheck checks if file needs updating\n\t// UpdateCheck 检查文件是否需要更新\n\tUpdateCheck(ctx context.Context, uid int64, params *dto.FileUpdateCheckRequest) (string, *dto.FileDTO, error)\n\n\t// UploadCheck checks file upload (alias for UpdateCheck, used for WebSocket upload check)\n\t// UploadCheck 检查文件上传（UpdateCheck 的别名，用于 WebSocket 上传检查）\n\tUploadCheck(ctx context.Context, uid int64, params *dto.FileUpdateCheckRequest) (string, *dto.FileDTO, error)\n\n\t// UpdateOrCreate creates or modifies a file\n\t// UpdateOrCreate 创建或修改文件\n\tUpdateOrCreate(ctx context.Context, uid int64, params *dto.FileUpdateRequest, mtimeCheck bool) (bool, *dto.FileDTO, error)\n\n\t// UploadComplete completes file upload (alias for UpdateOrCreate, used for WebSocket upload completion)\n\t// UploadComplete 完成文件上传（UpdateOrCreate 的别名，用于 WebSocket 上传完成）\n\tUploadComplete(ctx context.Context, uid int64, params *dto.FileUpdateRequest) (bool, *dto.FileDTO, error)\n\n\t// Delete deletes a file\n\t// Delete 删除文件\n\tDelete(ctx context.Context, uid int64, params *dto.FileDeleteRequest) (*dto.FileDTO, error)\n\n\t// List retrieves file list\n\t// List 获取文件列表\n\tList(ctx context.Context, uid int64, params *dto.FileListRequest, pager *app.Pager) ([]*dto.FileDTO, int, error)\n\n\t// ListByLastTime retrieves files updated after lastTime\n\t// ListByLastTime 获取在 lastTime 之后更新的文件\n\tListByLastTime(ctx context.Context, uid int64, params *dto.FileSyncRequest) ([]*dto.FileDTO, error)\n\n\t// CountSizeSum counts total number and total size of files in a vault\n\t// CountSizeSum 统计 vault 中文件总数与总大小\n\tCountSizeSum(ctx context.Context, vaultID int64, uid int64) error\n\n\t// Cleanup cleans up expired soft-deleted files\n\t// Cleanup 清理过期的软删除文件\n\tCleanup(ctx context.Context, uid int64) error\n\n\t// CleanupByTime cleans up expired soft-deleted files for all users by cutoff time\n\t// CleanupByTime 按截止时间清理所有用户的过期软删除文件\n\tCleanupByTime(ctx context.Context, cutoffTime int64) error\n\n\t// ResolveEmbedLinks resolves local file links in note content\n\t// ResolveEmbedLinks 解析笔记内容中的本地文件链接\n\tResolveEmbedLinks(ctx context.Context, uid int64, vaultName string, notePath string, content string) (map[string]string, error)\n\n\t// GetContent retrieves raw content of note or attachment file\n\t// GetContent 获取笔记或附件文件的原始内容\n\tGetContent(ctx context.Context, uid int64, params *dto.FileGetRequest) (io.ReadCloser, string, int64, string, error)\n\n\t// GetContentInfo retrieves file metadata and path for zero-copy download\n\t// GetContentInfo 获取文件的元数据和路径，用于零拷贝下载\n\tGetContentInfo(ctx context.Context, uid int64, params *dto.FileGetRequest) (savePath string, contentType string, mtime int64, etag string, fileName string, err error)\n\n\t// Restore restores a file (from recycle bin)\n\t// Restore 恢复文件（从回收站恢复）\n\tRestore(ctx context.Context, uid int64, params *dto.FileRestoreRequest) (*dto.FileDTO, error)\n\t// Rename renames a file\n\t// Rename 重命名文件\n\tRename(ctx context.Context, uid int64, params *dto.FileRenameRequest) (*dto.FileDTO, *dto.FileDTO, error)\n\t// WithClient sets client info\n\t// WithClient 设置客户端信息\n\tWithClient(clientType, name, version string) FileService\n\n\t// RecycleClear cleans up the recycle bin\n\t// RecycleClear 清理回收站\n\tRecycleClear(ctx context.Context, uid int64, params *dto.FileRecycleClearRequest) error\n\n\t// CleanDuplicateFiles cleans up duplicate file records\n\t// CleanDuplicateFiles 清理重复的文件记录\n\tCleanDuplicateFiles(ctx context.Context, uid int64, vaultID int64) error\n}\n\n// fileService implementation of FileService interface\n// fileService 实现 FileService 接口\ntype fileService struct {\n\tfileRepo       domain.FileRepository // File repository // 文件仓库\n\tnoteRepo       domain.NoteRepository // Note repository // 笔记仓库\n\tvaultService   VaultService          // Vault service // 仓库服务\n\tfolderService  FolderService         // Folder service // 文件夹服务\n\tsyncLogService SyncLogService        // Sync log service // 同步日志服务\n\tsf             *singleflight.Group   // Singleflight group // 并发请求合并组\n\tclientType     string                // Client type // 客户端类型\n\tclientName     string                // Client name // 客户端名称\n\tclientVer      string                // Client version // 客户端版本\n\tconfig         *ServiceConfig        // Service configuration // 服务配置\n\tbackupService  BackupService         // Backup service // 备份服务\n\tgitSyncService GitSyncService        // Git sync service // Git 同步服务\n\tcountTimers    *sync.Map             // Timers for CountSizeSum debounce // CountSizeSum 防抖计时器\n}\n\n// NewFileService creates FileService instance\n// NewFileService 创建 FileService 实例\nfunc NewFileService(fileRepo domain.FileRepository, noteRepo domain.NoteRepository, vaultSvc VaultService, folderSvc FolderService, backupSvc BackupService, gitSyncSvc GitSyncService, syncLogSvc SyncLogService, config *ServiceConfig) FileService {\n\treturn &fileService{\n\t\tfileRepo:       fileRepo,\n\t\tnoteRepo:       noteRepo,\n\t\tvaultService:   vaultSvc,\n\t\tfolderService:  folderSvc,\n\t\tbackupService:  backupSvc,\n\t\tgitSyncService: gitSyncSvc,\n\t\tsyncLogService: syncLogSvc,\n\t\tsf:             &singleflight.Group{},\n\t\tconfig:         config,\n\t\tcountTimers:    &sync.Map{},\n\t}\n}\n\n// domainToDTO converts domain model to DTO\n// domainToDTO 将领域模型转换为 DTO\nfunc (s *fileService) domainToDTO(file *domain.File) *dto.FileDTO {\n\tif file == nil {\n\t\treturn nil\n\t}\n\treturn &dto.FileDTO{\n\t\tID:               file.ID,\n\t\tAction:           string(file.Action),\n\t\tPath:             file.Path,\n\t\tPathHash:         file.PathHash,\n\t\tContentHash:      file.ContentHash,\n\t\tSavePath:         file.SavePath,\n\t\tRename:           file.Rename,\n\t\tSize:             file.Size,\n\t\tCtime:            file.Ctime,\n\t\tMtime:            file.Mtime,\n\t\tUpdatedTimestamp: file.UpdatedTimestamp,\n\t}\n}\n\n// Get retrieves a single file\n// Get 获取单条文件\nfunc (s *fileService) Get(ctx context.Context, uid int64, params *dto.FileGetRequest) (*dto.FileDTO, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfile, err := s.fileRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn s.domainToDTO(file), nil\n}\n\n// UpdateCheck checks if file needs updating\n// UpdateCheck 检查文件是否需要更新\nfunc (s *fileService) UpdateCheck(ctx context.Context, uid int64, params *dto.FileUpdateCheckRequest) (string, *dto.FileDTO, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn \"\", nil, err\n\t}\n\n\tfile, _ := s.fileRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\tif file != nil {\n\t\tfileDTO := s.domainToDTO(file)\n\n\t\t// Check if file is deleted\n\t\t// 检查文件是否已删除\n\t\tif file.Action == domain.FileActionDelete {\n\t\t\treturn \"Create\", nil, nil\n\t\t}\n\n\t\t// Check if content is consistent\n\t\t// 检查内容是否一致\n\t\tif file.ContentHash == params.ContentHash {\n\t\t\t// Notify user to update mtime when user mtime is less than server mtime\n\t\t\t// 当用户 mtime 小于服务端 mtime 时，通知用户更新 mtime\n\t\t\tif params.Mtime < file.Mtime {\n\t\t\t\treturn \"UpdateMtime\", fileDTO, nil\n\t\t\t} else if params.Mtime > file.Mtime {\n\t\t\t\tif err := s.fileRepo.UpdateMtime(ctx, params.Mtime, file.ID, uid); err != nil {\n\t\t\t\t\t// Non-critical update failed, log warning but do not block flow\n\t\t\t\t\t// 非关键更新失败，记录警告日志但不阻断流程\n\t\t\t\t\tzap.L().Warn(\"UpdateMtime failed for file\",\n\t\t\t\t\t\tzap.Int64(logger.FieldUID, uid),\n\t\t\t\t\t\tzap.Int64(\"fileId\", file.ID),\n\t\t\t\t\t\tzap.Int64(\"mtime\", params.Mtime),\n\t\t\t\t\t\tzap.String(logger.FieldMethod, \"FileService.UpdateCheck\"),\n\t\t\t\t\t\tzap.Error(err),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn \"\", fileDTO, nil\n\t\t}\n\t\treturn \"UpdateContent\", fileDTO, nil\n\t}\n\treturn \"Create\", nil, nil\n}\n\n// UpdateOrCreate creates or modifies a file\n// UpdateOrCreate 创建或修改文件\nfunc (s *fileService) UpdateOrCreate(ctx context.Context, uid int64, params *dto.FileUpdateRequest, mtimeCheck bool) (bool, *dto.FileDTO, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn false, nil, err\n\t}\n\n\tkey := fmt.Sprintf(\"update_or_create_%d_%d_%s\", uid, vaultID, params.PathHash)\n\ttype result struct {\n\t\tisNew bool\n\t\tdto   *dto.FileDTO\n\t}\n\n\tval, err, _ := s.sf.Do(key, func() (any, error) {\n\t\tvar isNew bool\n\t\tfile, _ := s.fileRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\n\t\tif file != nil {\n\t\t\tisNew = false\n\t\t\t// Check if content is consistent, excluding files marked as deleted\n\t\t\t// 检查内容是否一致,排除掉已被标记删除的文件\n\t\t\tif mtimeCheck && file.Action != domain.FileActionDelete && file.Mtime == params.Mtime && file.ContentHash == params.ContentHash {\n\t\t\t\treturn &result{isNew: isNew, dto: s.domainToDTO(file)}, nil\n\t\t\t}\n\n\t\t\t// If content is consistent but modification time is different, only update modification time\n\t\t\t// 检查内容是否一致但修改时间不同，则只更新修改时间\n\t\t\tif mtimeCheck && file.Mtime < params.Mtime && file.ContentHash == params.ContentHash {\n\t\t\t\terr := s.fileRepo.UpdateActionMtime(ctx, domain.FileActionModify, params.Mtime, file.ID, uid)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t\t\t}\n\t\t\t\tfile.Mtime = params.Mtime\n\t\t\t\t// Log mtime-only update // 记录仅 mtime 变更日志\n\t\t\t\tif s.syncLogService != nil {\n\t\t\t\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeFile, domain.SyncLogActionModify, \"mtime\", file.Path, file.PathHash, s.clientType, s.clientName, s.clientVer, file.Size)\n\t\t\t\t}\n\t\t\t\tif s.backupService != nil {\n\t\t\t\t\tgo s.backupService.NotifyUpdated(uid)\n\t\t\t\t}\n\t\t\t\tif s.gitSyncService != nil {\n\t\t\t\t\tgo s.gitSyncService.NotifyUpdated(uid, vaultID)\n\t\t\t\t}\n\t\t\t\treturn &result{isNew: isNew, dto: s.domainToDTO(file)}, nil\n\t\t\t}\n\n\t\t\t// Set action\n\t\t\t// Set action\n\t\t\t// 设置 action\n\t\t\tvar action domain.FileAction\n\t\t\tif file.Action == domain.FileActionDelete {\n\t\t\t\taction = domain.FileActionCreate\n\t\t\t} else {\n\t\t\t\taction = domain.FileActionModify\n\t\t\t}\n\n\t\t\t// Update file\n\t\t\t// Update file\n\t\t\t// 更新文件\n\t\t\tfile.VaultID = vaultID\n\t\t\tfile.Path = params.Path\n\t\t\tfile.PathHash = params.PathHash\n\t\t\tfile.ContentHash = params.ContentHash\n\t\t\tfile.SavePath = params.SavePath\n\t\t\tfile.Size = params.Size\n\t\t\tfile.Mtime = params.Mtime\n\t\t\tfile.Ctime = params.Ctime\n\t\t\tfile.Action = action\n\t\t\tfile.Rename = 0\n\n\t\t\tupdated, err := s.fileRepo.Update(ctx, file, uid)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t\t}\n\n\t\t\t// Log content modify // 记录内容变更日志\n\t\t\tif s.syncLogService != nil {\n\t\t\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeFile, domain.SyncLogActionModify, \"content,mtime\", updated.Path, updated.PathHash, s.clientType, s.clientName, s.clientVer, updated.Size)\n\t\t\t}\n\n\t\t\tgo s.CountSizeSum(context.Background(), vaultID, uid)\n\t\t\tgo s.folderService.SyncResourceFID(context.Background(), uid, vaultID, nil, []int64{updated.ID})\n\t\t\tif s.backupService != nil {\n\t\t\t\tgo s.backupService.NotifyUpdated(uid)\n\t\t\t}\n\t\t\tif s.gitSyncService != nil {\n\t\t\t\tgo s.gitSyncService.NotifyUpdated(uid, vaultID)\n\t\t\t}\n\t\t\treturn &result{isNew: isNew, dto: s.domainToDTO(updated)}, nil\n\t\t}\n\n\t\t// Create new file // 创建新文件\n\t\tisNew = true\n\t\tnewFile := &domain.File{\n\t\t\tVaultID:     vaultID,\n\t\t\tPath:        params.Path,\n\t\t\tPathHash:    params.PathHash,\n\t\t\tContentHash: params.ContentHash,\n\t\t\tSavePath:    params.SavePath,\n\t\t\tSize:        params.Size,\n\t\t\tMtime:       params.Mtime,\n\t\t\tCtime:       params.Ctime,\n\t\t\tAction:      domain.FileActionCreate,\n\t\t}\n\n\t\tcreated, err := s.fileRepo.Create(ctx, newFile, uid)\n\t\tif err != nil {\n\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t}\n\n\t\t// Log create // 记录新建日志\n\t\tif s.syncLogService != nil {\n\t\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeFile, domain.SyncLogActionCreate, \"\", created.Path, created.PathHash, s.clientType, s.clientName, s.clientVer, created.Size)\n\t\t}\n\n\t\tgo s.CountSizeSum(context.Background(), vaultID, uid)\n\t\tgo s.folderService.SyncResourceFID(context.Background(), uid, vaultID, nil, []int64{created.ID})\n\t\tif s.backupService != nil {\n\t\t\tgo s.backupService.NotifyUpdated(uid)\n\t\t}\n\t\tif s.gitSyncService != nil {\n\t\t\tgo s.gitSyncService.NotifyUpdated(uid, vaultID)\n\t\t}\n\t\treturn &result{isNew: isNew, dto: s.domainToDTO(created)}, nil\n\t})\n\n\tif err != nil {\n\t\treturn false, nil, err\n\t}\n\n\tres := val.(*result)\n\treturn res.isNew, res.dto, nil\n}\n\n// Delete deletes a file\n// Delete 删除文件\nfunc (s *fileService) Delete(ctx context.Context, uid int64, params *dto.FileDeleteRequest) (*dto.FileDTO, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID // 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfile, err := s.fileRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Update to deleted status // 更新为删除状态\n\tfile.Action = domain.FileActionDelete\n\tfile.Rename = 0\n\n\tupdated, err := s.fileRepo.Update(ctx, file, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Log soft delete // 记录软删除日志\n\tif s.syncLogService != nil {\n\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeFile, domain.SyncLogActionSoftDelete, \"\", file.Path, file.PathHash, s.clientType, s.clientName, s.clientVer, file.Size)\n\t}\n\n\tgo s.CountSizeSum(context.Background(), vaultID, uid)\n\tif s.backupService != nil {\n\t\tgo s.backupService.NotifyUpdated(uid)\n\t}\n\tif s.gitSyncService != nil {\n\t\tgo s.gitSyncService.NotifyUpdated(uid, vaultID)\n\t}\n\treturn s.domainToDTO(updated), nil\n}\n\n// Restore restores a file (from recycle bin)\n// Restore 恢复文件（从回收站恢复）\nfunc (s *fileService) Restore(ctx context.Context, uid int64, params *dto.FileRestoreRequest) (*dto.FileDTO, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID // 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Calculate PathHash if not provided // 如果未提供，则计算 PathHash\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// Get file from recycle bin\n\tfile, err := s.fileRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorNoteNotFound\n\t}\n\n\t// Check if file is deleted // 检查文件是否已删除\n\tif file.Action != domain.FileActionDelete {\n\t\treturn nil, code.ErrorNoteNotFound\n\t}\n\n\t// Update to modified status and update modification time\n\tfile.Action = domain.FileActionModify\n\tfile.Mtime = time.Now().UnixMilli()\n\tfile.Rename = 0\n\n\tupdated, err := s.fileRepo.Update(ctx, file, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Log restore // 记录恢复日志\n\tif s.syncLogService != nil {\n\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeFile, domain.SyncLogActionRestore, \"\", updated.Path, updated.PathHash, s.clientType, s.clientName, s.clientVer, updated.Size)\n\t}\n\n\tgo s.CountSizeSum(context.Background(), vaultID, uid)\n\tif s.backupService != nil {\n\t\tgo s.backupService.NotifyUpdated(uid)\n\t}\n\tif s.gitSyncService != nil {\n\t\tgo s.gitSyncService.NotifyUpdated(uid, vaultID)\n\t}\n\treturn s.domainToDTO(updated), nil\n}\n\n// List retrieves file list\n// List 获取文件列表\nfunc (s *fileService) List(ctx context.Context, uid int64, params *dto.FileListRequest, pager *app.Pager) ([]*dto.FileDTO, int, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tfiles, err := s.fileRepo.List(ctx, vaultID, pager.Page, pager.PageSize, uid, params.Keyword, params.IsRecycle, params.SortBy, params.SortOrder)\n\tif err != nil {\n\t\treturn nil, 0, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tcount, err := s.fileRepo.ListCount(ctx, vaultID, uid, params.Keyword, params.IsRecycle)\n\tif err != nil {\n\t\treturn nil, 0, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tvar result []*dto.FileDTO\n\tfor _, f := range files {\n\t\tresult = append(result, s.domainToDTO(f))\n\t}\n\n\treturn result, int(count), nil\n}\n\n// ListByLastTime retrieves files updated after lastTime\n// ListByLastTime 获取在 lastTime 之后更新的文件\nfunc (s *fileService) ListByLastTime(ctx context.Context, uid int64, params *dto.FileSyncRequest) ([]*dto.FileDTO, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfiles, err := s.fileRepo.ListByUpdatedTimestamp(ctx, params.LastTime, vaultID, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tvar results []*dto.FileDTO\n\tcacheList := make(map[string]bool)\n\tfor _, file := range files {\n\t\tif cacheList[file.PathHash] {\n\t\t\tcontinue\n\t\t}\n\t\tresults = append(results, s.domainToDTO(file))\n\t\tcacheList[file.PathHash] = true\n\t}\n\n\treturn results, nil\n}\n\n// CountSizeSum counts total number and total size of files in a vault\n// CountSizeSum 统计 vault 中文件总数与总大小\nfunc (s *fileService) CountSizeSum(ctx context.Context, vaultID int64, uid int64) error {\n\tkey := fmt.Sprintf(\"%d_%d\", uid, vaultID)\n\n\t// Debounce: 10 seconds delay. If a new request comes within 10s, reset the timer.\n\t// 防抖：10秒延迟。如果10秒内有新请求，重置计时器。\n\tif timerOld, ok := s.countTimers.Load(key); ok {\n\t\tif t, ok := timerOld.(*time.Timer); ok {\n\t\t\tt.Stop()\n\t\t}\n\t}\n\n\ttimer := time.AfterFunc(10*time.Second, func() {\n\t\tdefer s.countTimers.Delete(key)\n\n\t\t// Use singleflight to ensure only one actual DB query runs for same key even if debounce period ends simultaneously\n\t\t// 使用 singleflight 确保即使防抖期同时结束，同一 key 也只有一个真实的 DB 查询\n\t\ts.sf.Do(key, func() (any, error) {\n\t\t\tresult, err := s.fileRepo.CountSizeSum(context.Background(), vaultID, uid)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t\t}\n\t\t\t// Update vault stats, and removed the nested SyncResourceFID call\n\t\t\t// 更新仓库统计，并移除了嵌套的 SyncResourceFID 调用\n\t\t\treturn nil, s.vaultService.UpdateFileStats(context.Background(), result.Size, result.Count, vaultID, uid)\n\t\t})\n\t})\n\n\ts.countTimers.Store(key, timer)\n\treturn nil\n}\n\n// Cleanup cleans up expired soft-deleted files\n// Cleanup 清理过期的软删除文件\nfunc (s *fileService) Cleanup(ctx context.Context, uid int64) error {\n\tif s.config == nil {\n\t\treturn nil\n\t}\n\tretentionTimeStr := s.config.App.SoftDeleteRetentionTime\n\tif retentionTimeStr == \"\" || retentionTimeStr == \"0\" {\n\t\treturn nil\n\t}\n\n\tretentionDuration, err := util.ParseDuration(retentionTimeStr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif retentionDuration <= 0 {\n\t\treturn nil\n\t}\n\n\tcutoffTime := time.Now().Add(-retentionDuration).UnixMilli()\n\treturn s.fileRepo.DeletePhysicalByTime(ctx, cutoffTime, uid)\n}\n\n// CleanupByTime cleans up expired soft-deleted files for all users by cutoff time\n// CleanupByTime 按截止时间清理所有用户的过期软删除文件\nfunc (s *fileService) CleanupByTime(ctx context.Context, cutoffTime int64) error {\n\treturn s.fileRepo.DeletePhysicalByTimeAll(ctx, cutoffTime)\n}\n\n// GetContent retrieves raw content of note or attachment file\n// GetContent 获取笔记或附件文件的原始内容\n// Return value description:\n// 返回值说明:\n//   - []byte: Raw file data // 文件原始数据\n//   - string: MIME type (Content-Type) // MIME 类型 (Content-Type)\n//   - int64: mtime (Last-Modified) // mtime (Last-Modified)\n//   - string: etag (Content-Hash) // etag (Content-Hash)\n//   - error: Error on failure // 出错时返回错误\nfunc (s *fileService) GetContent(ctx context.Context, uid int64, params *dto.FileGetRequest) (io.ReadCloser, string, int64, string, error) {\n\t// 1. Get vault ID\n\t// 1. 获取仓库 ID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, \"\", 0, \"\", err\n\t}\n\n\t// 2. Confirm path hash\n\t// 2. 确认路径哈希\n\tpathHash := params.PathHash\n\tif pathHash == \"\" {\n\t\tpathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// 4. Attempt to get from File table (attachment/binary file)\n\t// 4. 尝试从 File 表获取 (附件/二进制文件)\n\tif s.fileRepo != nil {\n\t\tfile, err := s.fileRepo.GetByPathHash(ctx, pathHash, vaultID, uid)\n\t\tif err == nil && file != nil {\n\t\t\t// Identify file MIME type\n\t\t\t// 识别文件 MIME 类型\n\t\t\text := filepath.Ext(params.Path)\n\t\t\tcontentType := mime.TypeByExtension(ext)\n\t\t\tif contentType == \"\" {\n\t\t\t\tcontentType = \"application/octet-stream\"\n\t\t\t}\n\n\t\t\t// Use file's content hash as ETag from DB if available\n\t\t\t// 使用 DB 中的 ContentHash 作为 ETag\n\t\t\tetag := file.ContentHash\n\t\t\tif etag == \"\" {\n\t\t\t\tetag = file.PathHash\n\t\t\t}\n\n\t\t\t// Open file for streaming\n\t\t\t// 打开文件用于流式传输\n\t\t\tf, err := os.Open(file.SavePath)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, \"\", 0, \"\", code.ErrorFileReadFailed.WithDetails(err.Error())\n\t\t\t}\n\n\t\t\treturn f, contentType, file.Mtime, etag, nil\n\t\t}\n\t}\n\n\treturn nil, \"\", 0, \"\", code.ErrorNoteNotFound\n}\n\n// GetContentInfo retrieves file metadata and path for zero-copy download\n// GetContentInfo 获取文件的元数据和路径，用于零拷贝下载\nfunc (s *fileService) GetContentInfo(ctx context.Context, uid int64, params *dto.FileGetRequest) (string, string, int64, string, string, error) {\n\t// 1. Get vault ID\n\t// 1. 获取仓库 ID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn \"\", \"\", 0, \"\", \"\", err\n\t}\n\n\t// 2. Confirm path hash\n\t// 2. 确认路径哈希\n\tpathHash := params.PathHash\n\tif pathHash == \"\" {\n\t\tpathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// 3. Attempt to get from File table\n\t// 3. 尝试从 File 表获取\n\tif s.fileRepo != nil {\n\t\tfile, err := s.fileRepo.GetByPathHash(ctx, pathHash, vaultID, uid)\n\t\tif err == nil && file != nil {\n\t\t\t// Check IsRecycle support\n\t\t\t// 检查回收站标识支持\n\t\t\tif params.IsRecycle {\n\t\t\t\tif file.Action != domain.FileActionDelete {\n\t\t\t\t\treturn \"\", \"\", 0, \"\", \"\", code.ErrorFileNotFound\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif file.Action == domain.FileActionDelete {\n\t\t\t\t\treturn \"\", \"\", 0, \"\", \"\", code.ErrorFileNotFound\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Identify file MIME type\n\t\t\t// 识别文件 MIME 类型\n\t\t\text := filepath.Ext(file.Path)\n\t\t\tcontentType := mime.TypeByExtension(ext)\n\t\t\tif contentType == \"\" {\n\t\t\t\tcontentType = \"application/octet-stream\"\n\t\t\t}\n\n\t\t\t// Use file's content hash as ETag\n\t\t\tetag := file.ContentHash\n\t\t\tif etag == \"\" {\n\t\t\t\tetag = file.PathHash\n\t\t\t}\n\n\t\t\treturn file.SavePath, contentType, file.Mtime, etag, filepath.Base(file.Path), nil\n\t\t}\n\t}\n\n\treturn \"\", \"\", 0, \"\", \"\", code.ErrorNoteNotFound\n}\n\n// ResolveEmbedLinks resolves local file links in note content\n// ResolveEmbedLinks 解析笔记内容中的本地文件链接\nfunc (s *fileService) ResolveEmbedLinks(ctx context.Context, uid int64, vaultName string, notePath string, content string) (map[string]string, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, vaultName)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\trawRefs := extractSharedNoteFileRefs(content)\n\tresultMap := make(map[string]string, len(rawRefs))\n\tfor _, rawRef := range rawRefs {\n\t\tfile, err := s.resolveNoteFileReference(ctx, uid, vaultID, notePath, rawRef)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif file != nil {\n\t\t\tresultMap[rawRef] = file.Path\n\t\t}\n\t}\n\n\treturn resultMap, nil\n}\n\nfunc (s *fileService) resolveNoteFileReference(ctx context.Context, uid int64, vaultID int64, notePath string, rawRef string) (*domain.File, error) {\n\tref := strings.TrimSpace(rawRef)\n\tif !isLocalSharePath(ref) {\n\t\treturn nil, nil\n\t}\n\n\tfor _, candidate := range buildSharePathCandidates(notePath, ref) {\n\t\tfile, err := s.fileRepo.GetByPath(ctx, candidate, vaultID, uid)\n\t\tif err == nil && file != nil && file.Action != domain.FileActionDelete {\n\t\t\treturn file, nil\n\t\t}\n\t}\n\n\tnormalizedRef := normalizeShareVaultPath(ref)\n\tif normalizedRef != \"\" && !strings.Contains(normalizedRef, \"/\") {\n\t\tfile, err := s.fileRepo.GetByPathLike(ctx, normalizedRef, vaultID, uid)\n\t\tif err == nil && file != nil && file.Action != domain.FileActionDelete {\n\t\t\treturn file, nil\n\t\t}\n\t}\n\n\treturn nil, nil\n}\n\n// Sync syncs files (alias for ListByLastTime, used for WebSocket sync)\n// Sync 同步文件（ListByLastTime 的别名，用于 WebSocket 同步）\nfunc (s *fileService) Sync(ctx context.Context, uid int64, params *dto.FileSyncRequest) ([]*dto.FileDTO, error) {\n\treturn s.ListByLastTime(ctx, uid, params)\n}\n\n// UploadCheck checks file upload (alias for UpdateCheck, used for WebSocket upload check)\n// UploadCheck 检查文件上传（UpdateCheck 的别名，用于 WebSocket 上传检查）\nfunc (s *fileService) UploadCheck(ctx context.Context, uid int64, params *dto.FileUpdateCheckRequest) (string, *dto.FileDTO, error) {\n\treturn s.UpdateCheck(ctx, uid, params)\n}\n\n// UploadComplete completes file upload (alias for UpdateOrCreate, used for WebSocket upload completion)\n// UploadComplete 完成文件上传（UpdateOrCreate 的别名，用于 WebSocket 上传完成）\nfunc (s *fileService) UploadComplete(ctx context.Context, uid int64, params *dto.FileUpdateRequest) (bool, *dto.FileDTO, error) {\n\treturn s.UpdateOrCreate(ctx, uid, params, true)\n}\n\n// Rename renames a file\n// Rename 重命名文件\nfunc (s *fileService) Rename(ctx context.Context, uid int64, params *dto.FileRenameRequest) (*dto.FileDTO, *dto.FileDTO, error) {\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tnewPath := strings.Trim(params.Path, \"/\")\n\tnewPathHash := params.PathHash\n\tif newPathHash == \"\" {\n\t\tnewPathHash = util.EncodeHash32(newPath)\n\t}\n\n\toldPath := strings.Trim(params.OldPath, \"/\")\n\toldPathHash := params.OldPathHash\n\tif oldPathHash == \"\" {\n\t\toldPathHash = util.EncodeHash32(oldPath)\n\t}\n\n\tkey := fmt.Sprintf(\"rename_%d_%d_%s_%s\", uid, vaultID, oldPathHash, newPathHash)\n\ttype result struct {\n\t\toldFile *dto.FileDTO\n\t\tnewFile *dto.FileDTO\n\t}\n\n\tval, err, _ := s.sf.Do(key, func() (any, error) {\n\t\t// 1. Check if target path has valid file\n\t\t// 1. 判断目标路径是否存在有效文件\n\t\texistFile, _ := s.fileRepo.GetByPathHash(ctx, newPathHash, vaultID, uid)\n\t\tif existFile != nil && existFile.Action != domain.FileActionDelete {\n\t\t\treturn nil, code.ErrorFileExist\n\t\t}\n\n\t\t// 2. Get old file\n\t\t// 2. Get old file\n\t\t// 2. 获取旧文件\n\t\tf, err := s.fileRepo.GetByPathHash(ctx, oldPathHash, vaultID, uid)\n\t\tif err != nil {\n\t\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\t\treturn nil, code.ErrorFileNotFound\n\t\t\t}\n\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t}\n\n\t\t// 3. Mark old file as deleted\n\t\t// 3. Mark old file as deleted\n\t\t// 3. 标记旧文件删除\n\t\tf.Action = domain.FileActionDelete\n\t\tf.Rename = 1\n\t\tf.UpdatedTimestamp = timex.Now().UnixMilli()\n\t\toldFile, err := s.fileRepo.Update(ctx, f, uid)\n\t\tif err != nil {\n\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t}\n\n\t\t// 4. Create new or reuse file record\n\t\t// 4. 新建或复用文件记录\n\t\tvar newFileCreated *domain.File\n\t\tif existFile != nil {\n\t\t\t// 复用已删除的记录\n\t\t\texistFile.Action = domain.FileActionCreate\n\t\t\texistFile.Path = newPath\n\t\t\texistFile.PathHash = newPathHash\n\t\t\tnewPathDir := \"\"\n\t\t\tif idx := strings.LastIndex(newPath, \"/\"); idx >= 0 {\n\t\t\t\tnewPathDir = newPath[:idx]\n\t\t\t}\n\t\t\texistFile.FID, _ = s.folderService.EnsurePathFID(ctx, uid, vaultID, newPathDir)\n\t\t\texistFile.ContentHash = f.ContentHash\n\t\t\texistFile.SavePath = f.SavePath\n\t\t\texistFile.Size = f.Size\n\t\t\texistFile.Mtime = f.Mtime // Preserve original mtime // 保留原始修改时间\n\t\t\texistFile.UpdatedTimestamp = timex.Now().UnixMilli()\n\t\t\tnewFileCreated, err = s.fileRepo.Update(ctx, existFile, uid)\n\t\t} else {\n\t\t\t// 创建新记录\n\t\t\tnewFile := &domain.File{\n\t\t\t\tVaultID:          vaultID,\n\t\t\t\tAction:           domain.FileActionCreate,\n\t\t\t\tPath:             newPath,\n\t\t\t\tPathHash:         newPathHash,\n\t\t\t\tFID:              f.FID,\n\t\t\t\tCtime:            f.Ctime,\n\t\t\t\tMtime:            f.Mtime, // Preserve original mtime // 保留原始修改时间\n\t\t\t\tUpdatedTimestamp: timex.Now().UnixMilli(),\n\t\t\t\tContentHash:      f.ContentHash,\n\t\t\t\tSavePath:         f.SavePath,\n\t\t\t\tSize:             f.Size,\n\t\t\t}\n\t\t\tnewPathDir := \"\"\n\t\t\tif idx := strings.LastIndex(newPath, \"/\"); idx >= 0 {\n\t\t\t\tnewPathDir = newPath[:idx]\n\t\t\t}\n\t\t\t// 确保 FID 正确\n\t\t\tnewFile.FID, _ = s.folderService.EnsurePathFID(ctx, uid, vaultID, newPathDir)\n\t\t\tnewFileCreated, err = s.fileRepo.Create(ctx, newFile, uid)\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t}\n\n\t\t// Log rename // 记录重命名日志\n\t\tif s.syncLogService != nil {\n\t\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeFile, domain.SyncLogActionRename, \"path\", newFileCreated.Path, newFileCreated.PathHash, s.clientType, s.clientName, s.clientVer, newFileCreated.Size)\n\t\t}\n\n\t\t// 修正目录FID\n\t\tgo s.folderService.SyncResourceFID(context.Background(), uid, vaultID, nil, []int64{newFileCreated.ID})\n\n\t\tif s.backupService != nil {\n\t\t\tgo s.backupService.NotifyUpdated(uid)\n\t\t}\n\t\tif s.gitSyncService != nil {\n\t\t\tgo s.gitSyncService.NotifyUpdated(uid, vaultID)\n\t\t}\n\n\t\treturn &result{oldFile: s.domainToDTO(oldFile), newFile: s.domainToDTO(newFileCreated)}, nil\n\t})\n\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tres := val.(*result)\n\treturn res.oldFile, res.newFile, nil\n}\n\n// WithClient sets client info, returns new FileService instance\n// WithClient 设置客户端信息，返回新 FileService 实例\nfunc (s *fileService) WithClient(clientType, name, version string) FileService {\n\treturn &fileService{\n\t\tfileRepo:       s.fileRepo,\n\t\tnoteRepo:       s.noteRepo,\n\t\tvaultService:   s.vaultService,\n\t\tfolderService:  s.folderService,\n\t\tsyncLogService: s.syncLogService,\n\t\tsf:             s.sf,\n\t\tclientType:     clientType,\n\t\tclientName:     name,\n\t\tclientVer:      version,\n\t\tconfig:         s.config,\n\t\tbackupService:  s.backupService,\n\t\tgitSyncService: s.gitSyncService,\n\t\tcountTimers:    s.countTimers, // Share the same timer map // 共享同一个计时器 map\n\t}\n}\n\n// RecycleClear 清理回收站\nfunc (s *fileService) RecycleClear(ctx context.Context, uid int64, params *dto.FileRecycleClearRequest) error {\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif params.Path != \"\" && params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// Capture items to be deleted for detailed logging\n\t// 捕获待删除的项目以便进行详细日志记录\n\tvar filesToDelete []*domain.File\n\tif params.PathHash != \"\" {\n\t\tfile, _ := s.fileRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\t\tif file != nil {\n\t\t\tfilesToDelete = append(filesToDelete, file)\n\t\t\tif params.Path == \"\" {\n\t\t\t\tparams.Path = file.Path\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// Clear all: retrieve all files in recycle bin (using a large page size)\n\t\t// 清理全部：获取回收站中的所有文件（使用较大的分页限制）\n\t\tfilesToDelete, _ = s.fileRepo.List(ctx, vaultID, 1, 10000, uid, \"\", true, \"\", \"\")\n\t}\n\n\terr = s.fileRepo.RecycleClear(ctx, params.Path, params.PathHash, vaultID, uid)\n\tif err != nil {\n\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Log permanent delete for each item // 为每一项记录彻底删除日志\n\tif s.syncLogService != nil {\n\t\tfor _, f := range filesToDelete {\n\t\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeFile, domain.SyncLogActionDelete, \"\", f.Path, f.PathHash, s.clientType, s.clientName, s.clientVer, f.Size)\n\t\t}\n\t}\n\n\tgo s.CountSizeSum(context.Background(), vaultID, uid)\n\treturn nil\n}\n\n// CleanDuplicateFiles 清理重复的文件记录\nfunc (s *fileService) CleanDuplicateFiles(ctx context.Context, uid int64, vaultID int64) error {\n\t// 获取所有文件（包含已删除，以便全局去重）\n\tfiles, err := s.fileRepo.ListByUpdatedTimestamp(ctx, 0, vaultID, uid)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// 按 PathHash 分组\n\tgrouped := make(map[string][]*domain.File)\n\tfor _, f := range files {\n\t\tgrouped[f.PathHash] = append(grouped[f.PathHash], f)\n\t}\n\n\tfor pathHash, list := range grouped {\n\t\tif len(list) <= 1 {\n\t\t\tcontinue\n\t\t}\n\n\t\t//保留规则：\n\t\t// 1. 优先保留 Action != delete 的记录\n\t\t// 2. 如果有多个活跃记录，保留 UpdatedTimestamp 最大（最新）的一条\n\t\t// 3. 如果时间戳一致，保留 ID 最大的记录\n\n\t\tvar bestFile *domain.File\n\t\tfor _, f := range list {\n\t\t\tif bestFile == nil {\n\t\t\t\tbestFile = f\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// 比较逻辑\n\t\t\tisBetter := false\n\t\t\tif f.Action != domain.FileActionDelete && bestFile.Action == domain.FileActionDelete {\n\t\t\t\tisBetter = true\n\t\t\t} else if f.Action == bestFile.Action {\n\t\t\t\tif f.UpdatedTimestamp > bestFile.UpdatedTimestamp {\n\t\t\t\t\tisBetter = true\n\t\t\t\t} else if f.UpdatedTimestamp == bestFile.UpdatedTimestamp && f.ID > bestFile.ID {\n\t\t\t\t\tisBetter = true\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif isBetter {\n\t\t\t\tbestFile = f\n\t\t\t}\n\t\t}\n\n\t\t// 删除非 bestFile 的所有记录\n\t\tfor _, f := range list {\n\t\t\tif f.ID != bestFile.ID {\n\t\t\t\t// 清除 singleflight 缓存，防止残留\n\t\t\t\ts.sf.Forget(fmt.Sprintf(\"update_or_create_%d_%d_%s\", uid, vaultID, pathHash))\n\t\t\t\ts.sf.Forget(fmt.Sprintf(\"rename_%d_%d_%s\", uid, vaultID, pathHash))\n\n\t\t\t\t_ = s.fileRepo.Delete(ctx, f.ID, uid)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Verify fileService implements FileService interface\n// 确保 fileService 实现了 FileService interface\nvar _ FileService = (*fileService)(nil)\n"
  },
  {
    "path": "internal/service/folder_service.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/workerpool\"\n\t\"golang.org/x/sync/singleflight\"\n\t\"gorm.io/gorm\"\n)\n\n// FolderService 文件夹业务服务接口\ntype FolderService interface {\n\tGet(ctx context.Context, uid int64, params *dto.FolderGetRequest) (*dto.FolderDTO, error)\n\tList(ctx context.Context, uid int64, params *dto.FolderListRequest) ([]*dto.FolderDTO, error)\n\tListByUpdatedTimestamp(ctx context.Context, uid int64, vault string, lastTime int64) ([]*dto.FolderDTO, error)\n\tUpdateOrCreate(ctx context.Context, uid int64, params *dto.FolderCreateRequest) (*dto.FolderDTO, error)\n\tDelete(ctx context.Context, uid int64, params *dto.FolderDeleteRequest) (*dto.FolderDTO, error)\n\tRename(ctx context.Context, uid int64, params *dto.FolderRenameRequest) (*dto.FolderDTO, *dto.FolderDTO, error)\n\tListNotes(ctx context.Context, uid int64, params *dto.FolderContentRequest, pager *app.Pager) ([]*dto.NoteNoContentDTO, int, error)\n\tListFiles(ctx context.Context, uid int64, params *dto.FolderContentRequest, pager *app.Pager) ([]*dto.FileDTO, int, error)\n\tEnsurePathFID(ctx context.Context, uid int64, vaultID int64, path string) (int64, error)\n\tSyncResourceFID(ctx context.Context, uid int64, vaultID int64, noteIDs []int64, fileIDs []int64) error\n\tGetTree(ctx context.Context, uid int64, params *dto.FolderTreeRequest) (*dto.FolderTreeResponse, error)\n\tCleanDuplicateFolders(ctx context.Context, uid int64, vaultID int64) error\n\tWithClient(clientType, clientName, clientVersion string) FolderService\n}\n\ntype folderService struct {\n\tfolderRepo    domain.FolderRepository\n\tnoteRepo      domain.NoteRepository\n\tfileRepo      domain.FileRepository\n\tvaultService  VaultService\n\tsf            singleflight.Group\n\tbackupService  BackupService\n\tpool           *workerpool.Pool\n\tsyncLogService SyncLogService\n\tclientType     string\n\tclientName     string\n\tclientVersion  string\n}\n\nfunc NewFolderService(folderRepo domain.FolderRepository, noteRepo domain.NoteRepository, fileRepo domain.FileRepository, vaultSvc VaultService, backupSvc BackupService, syncLogSvc SyncLogService, pool *workerpool.Pool) FolderService {\n\treturn &folderService{\n\t\tfolderRepo:     folderRepo,\n\t\tnoteRepo:       noteRepo,\n\t\tfileRepo:       fileRepo,\n\t\tvaultService:   vaultSvc,\n\t\tbackupService:  backupSvc,\n\t\tsyncLogService: syncLogSvc,\n\t\tpool:           pool,\n\t\tsf:             singleflight.Group{},\n\t}\n}\n\nfunc (s *folderService) domainToDTO(f *domain.Folder) *dto.FolderDTO {\n\tif f == nil {\n\t\treturn nil\n\t}\n\treturn &dto.FolderDTO{\n\t\tID:               f.ID,\n\t\tAction:           string(f.Action),\n\t\tPath:             f.Path,\n\t\tPathHash:         f.PathHash,\n\t\tLevel:            f.Level,\n\t\tFID:              f.FID,\n\t\tCtime:            f.Ctime,\n\t\tMtime:            f.Mtime,\n\t\tUpdatedTimestamp: f.UpdatedTimestamp,\n\t\tUpdatedAt:        timex.Time(f.UpdatedAt),\n\t\tCreatedAt:        timex.Time(f.CreatedAt),\n\t}\n}\n\nfunc (s *folderService) WithClient(clientType, clientName, clientVersion string) FolderService {\n\tns := *s\n\tns.clientType = clientType\n\tns.clientName = clientName\n\tns.clientVersion = clientVersion\n\treturn &ns\n}\n\nfunc (s *folderService) List(ctx context.Context, uid int64, params *dto.FolderListRequest) ([]*dto.FolderDTO, error) {\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif params.Path != strings.Trim(params.Path, \"/\") && params.Path != \"\" {\n\t\treturn nil, code.ErrorInvalidParams.WithDetails(\"path cannot be empty\")\n\t}\n\n\tvar fid int64 = 0\n\tif params.Path != \"\" {\n\t\tif params.PathHash == \"\" {\n\t\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t\t}\n\t\tf, err := s.folderRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\t\tif err == nil {\n\t\t\tfid = f.ID\n\t\t}\n\t}\n\n\tfolders, err := s.folderRepo.GetByFID(ctx, fid, vaultID, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tvar res []*dto.FolderDTO\n\tfor _, f := range folders {\n\t\tres = append(res, s.domainToDTO(f))\n\t}\n\treturn res, nil\n}\n\nfunc (s *folderService) UpdateOrCreate(ctx context.Context, uid int64, params *dto.FolderCreateRequest) (*dto.FolderDTO, error) {\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif params.Path != strings.Trim(params.Path, \"/\") && params.Path != \"\" {\n\t\treturn nil, code.ErrorInvalidParams.WithDetails(\"path cannot be empty\")\n\t}\n\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// Unified call to EnsurePathFID\n\t// 统一调用 EnsurePathFID\n\tfid, err := s.EnsurePathFID(ctx, uid, vaultID, params.Path)\n\tif err != nil {\n\t\treturn nil, code.ErrorFolderModifyOrCreateFailed.WithDetails(err.Error())\n\t}\n\n\tf, err := s.folderRepo.GetByID(ctx, fid, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tif s.backupService != nil {\n\t\ts.backupService.NotifyUpdated(uid)\n\t}\n\n\tif s.syncLogService != nil {\n\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeFolder, domain.SyncLogActionCreate, \"\", f.Path, f.PathHash, s.clientType, s.clientName, s.clientVersion, 0)\n\t}\n\n\treturn s.domainToDTO(f), nil\n}\n\nfunc (s *folderService) Delete(ctx context.Context, uid int64, params *dto.FolderDeleteRequest) (*dto.FolderDTO, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID // 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif params.Path != strings.Trim(params.Path, \"/\") && params.Path != \"\" {\n\t\treturn nil, code.ErrorInvalidParams.WithDetails(\"path cannot be empty\")\n\t}\n\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\tf, err := s.folderRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorFolderNotFound\n\t\t}\n\t\treturn nil, code.ErrorFolderGetFailed.WithDetails(err.Error())\n\t}\n\n\t// Update to deleted status\n\t// 更新为删除状态\n\tf.Action = domain.FolderActionDelete\n\tf.UpdatedTimestamp = time.Now().UnixMilli()\n\t_, err = s.folderRepo.Update(ctx, f, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorFolderDeleteFailed.WithDetails(err.Error())\n\t}\n\n\tif s.backupService != nil {\n\t\ts.backupService.NotifyUpdated(uid)\n\t}\n\n\tif s.syncLogService != nil {\n\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeFolder, domain.SyncLogActionDelete, \"\", f.Path, f.PathHash, s.clientType, s.clientName, s.clientVersion, 0)\n\t}\n\n\treturn s.domainToDTO(f), nil\n}\n\nfunc (s *folderService) ListByUpdatedTimestamp(ctx context.Context, uid int64, vault string, lastTime int64) ([]*dto.FolderDTO, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID // 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfolders, err := s.folderRepo.ListByUpdatedTimestamp(ctx, lastTime, vaultID, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorFolderListFailed.WithDetails(err.Error())\n\t}\n\n\tvar res []*dto.FolderDTO\n\tcache := make(map[string]bool)\n\tfor _, f := range folders {\n\t\tif cache[f.PathHash] {\n\t\t\tcontinue\n\t\t}\n\t\tres = append(res, s.domainToDTO(f))\n\t\tcache[f.PathHash] = true\n\t}\n\n\treturn res, nil\n}\n\nfunc (s *folderService) Rename(ctx context.Context, uid int64, params *dto.FolderRenameRequest) (*dto.FolderDTO, *dto.FolderDTO, error) {\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tif params.Path != strings.Trim(params.Path, \"/\") && params.Path != \"\" {\n\t\treturn nil, nil, code.ErrorInvalidParams.WithDetails(\"path cannot be empty\")\n\t}\n\n\tif params.OldPath != strings.Trim(params.OldPath, \"/\") && params.OldPath != \"\" {\n\t\treturn nil, nil, code.ErrorInvalidParams.WithDetails(\"oldPath cannot be empty\")\n\t}\n\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\tif params.OldPathHash == \"\" {\n\t\tparams.OldPathHash = util.EncodeHash32(params.OldPath)\n\t}\n\n\t// Rename renames a folder\n\t// Rename 重命名文件夹\n\toldFolder, err := s.folderRepo.GetByPathHash(ctx, params.OldPathHash, vaultID, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\tnewFolder, err := s.UpdateOrCreate(ctx, uid, &dto.FolderCreateRequest{\n\t\t\t\tPath:     params.Path,\n\t\t\t\tPathHash: params.PathHash,\n\t\t\t\tVault:    params.Vault,\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, err\n\t\t\t}\n\t\t\treturn nil, newFolder, nil\n\t\t}\n\t\treturn nil, nil, code.ErrorFolderGetFailed.WithDetails(params.OldPathHash + \"->\" + params.PathHash + \":\" + err.Error())\n\t}\n\n\t// 1. Check if target path has valid folder\n\t// 1. 判断目标路径是否存在有效文件夹\n\texistFolder, _ := s.folderRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\tif existFolder != nil && existFolder.Action != domain.FolderActionDelete {\n\t\tnewFolder, err := s.Delete(ctx, uid, &dto.FolderDeleteRequest{\n\t\t\tPath:     params.OldPath,\n\t\t\tPathHash: params.OldPathHash,\n\t\t\tVault:    params.Vault,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\treturn s.domainToDTO(oldFolder), newFolder, nil\n\t}\n\n\t// 3. Mark old folder as deleted\n\t// 3. 标记旧文件夹删除\n\toldFolder.Action = domain.FolderActionDelete\n\toldFolder.UpdatedTimestamp = timex.Now().UnixMilli()\n\toldFolder, err = s.folderRepo.Update(ctx, oldFolder, uid)\n\tif err != nil {\n\t\treturn nil, nil, code.ErrorFolderRenameFailed.WithDetails(err.Error())\n\t}\n\n\t// 4. New or reuse folder record\n\t// 4. 新建或复用文件夹记录\n\t// 统一调用 EnsurePathFID\n\tfid, err := s.EnsurePathFID(ctx, uid, vaultID, params.Path)\n\tif err != nil {\n\t\treturn nil, nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tnewFolderCreated, err := s.folderRepo.GetByID(ctx, fid, uid)\n\tif err != nil {\n\t\treturn nil, nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tif s.backupService != nil {\n\t\ts.backupService.NotifyUpdated(uid)\n\t}\n\n\tif s.syncLogService != nil {\n\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeFolder, domain.SyncLogActionRename, \"path\", newFolderCreated.Path, newFolderCreated.PathHash, s.clientType, s.clientName, s.clientVersion, 0)\n\t}\n\n\treturn s.domainToDTO(oldFolder), s.domainToDTO(newFolderCreated), nil\n}\n\nfunc (s *folderService) Get(ctx context.Context, uid int64, params *dto.FolderGetRequest) (*dto.FolderDTO, error) {\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif params.Path != strings.Trim(params.Path, \"/\") && params.Path != \"\" {\n\t\treturn nil, code.ErrorInvalidParams.WithDetails(\"path cannot be empty\")\n\t}\n\n\tif params.Path != \"\" && params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\tif params.PathHash == \"\" {\n\t\treturn nil, code.ErrorInvalidParams.WithDetails(\"path or pathHash is required\")\n\t}\n\n\tf, err := s.folderRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorFolderNotFound\n\t\t}\n\t\treturn nil, code.ErrorFolderGetFailed.WithDetails(err.Error())\n\t}\n\n\treturn s.domainToDTO(f), nil\n}\n\nfunc (s *folderService) ListNotes(ctx context.Context, uid int64, params *dto.FolderContentRequest, pager *app.Pager) ([]*dto.NoteNoContentDTO, int, error) {\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tif params.Path != strings.Trim(params.Path, \"/\") && params.Path != \"\" {\n\t\treturn nil, 0, code.ErrorInvalidParams.WithDetails(\"path cannot be empty\")\n\t}\n\n\tvar fid int64 = 0\n\tif params.Path != \"\" {\n\t\tif params.PathHash == \"\" {\n\t\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t\t}\n\t\tf, err := s.folderRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\t\tif err == nil {\n\t\t\tfid = f.ID\n\t\t}\n\t}\n\n\tnotes, err := s.noteRepo.ListByFID(ctx, fid, vaultID, uid, pager.Page, pager.PageSize, params.SortBy, params.SortOrder)\n\tif err != nil {\n\t\treturn nil, 0, code.ErrorNoteListFailed.WithDetails(err.Error())\n\t}\n\n\tcount, err := s.noteRepo.ListByFIDCount(ctx, fid, vaultID, uid)\n\tif err != nil {\n\t\treturn nil, 0, code.ErrorNoteListFailed.WithDetails(err.Error())\n\t}\n\n\tvar res []*dto.NoteNoContentDTO\n\tfor _, n := range notes {\n\t\tres = append(res, &dto.NoteNoContentDTO{\n\t\t\tID:               n.ID,\n\t\t\tAction:           string(n.Action),\n\t\t\tPath:             n.Path,\n\t\t\tPathHash:         n.PathHash,\n\t\t\tVersion:          n.Version,\n\t\t\tCtime:            n.Ctime,\n\t\t\tMtime:            n.Mtime,\n\t\t\tUpdatedTimestamp: n.UpdatedTimestamp,\n\t\t\tUpdatedAt:        timex.Time(n.UpdatedAt),\n\t\t\tCreatedAt:        timex.Time(n.CreatedAt),\n\t\t})\n\t}\n\treturn res, int(count), nil\n}\n\nfunc (s *folderService) ListFiles(ctx context.Context, uid int64, params *dto.FolderContentRequest, pager *app.Pager) ([]*dto.FileDTO, int, error) {\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tif params.Path != strings.Trim(params.Path, \"/\") && params.Path != \"\" {\n\t\treturn nil, 0, code.ErrorInvalidParams.WithDetails(\"path cannot be empty\")\n\t}\n\n\tvar fid int64 = 0\n\tif params.Path != \"\" {\n\t\tif params.PathHash == \"\" {\n\t\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t\t}\n\t\tf, err := s.folderRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\t\tif err == nil {\n\t\t\tfid = f.ID\n\t\t}\n\t}\n\n\tfiles, err := s.fileRepo.ListByFID(ctx, fid, vaultID, uid, pager.Page, pager.PageSize, params.SortBy, params.SortOrder)\n\tif err != nil {\n\t\treturn nil, 0, code.ErrorFileListFailed.WithDetails(err.Error())\n\t}\n\n\tcount, err := s.fileRepo.ListByFIDCount(ctx, fid, vaultID, uid)\n\tif err != nil {\n\t\treturn nil, 0, code.ErrorFileListFailed.WithDetails(err.Error())\n\t}\n\n\tvar res []*dto.FileDTO\n\tfor _, f := range files {\n\t\tres = append(res, &dto.FileDTO{\n\t\t\tID:               f.ID,\n\t\t\tAction:           string(f.Action),\n\t\t\tPath:             f.Path,\n\t\t\tPathHash:         f.PathHash,\n\t\t\tContentHash:      f.ContentHash,\n\t\t\tSavePath:         f.SavePath,\n\t\t\tSize:             f.Size,\n\t\t\tCtime:            f.Ctime,\n\t\t\tMtime:            f.Mtime,\n\t\t\tUpdatedTimestamp: f.UpdatedTimestamp,\n\t\t\tUpdatedAt:        timex.Time(f.UpdatedAt),\n\t\t\tCreatedAt:        timex.Time(f.CreatedAt),\n\t\t})\n\t}\n\treturn res, int(count), nil\n}\n\n// EnsurePathFID ensures all folders in path exist and returns the ID of the deepest folder\n// EnsurePathFID 确保路径中的所有文件夹都存在并返回最深文件夹的 ID\n//\n// NOTE: Race condition — when multiple notes sync concurrently (even from a\n// single device), each goroutine calls EnsurePathFID independently. The\n// check-then-create below (GetByPathHash → Create) is not atomic, so two\n// goroutines can both see \"not found\" and both insert a folder record for the\n// same path. This produces duplicate rows in the folder table (confirmed via\n// direct DB inspection — e.g. 3 rows for \"projects\" with IDs 4,5,6).\n//\n// The query-side methods (List, ListNotes, ListFiles, GetTree) handle this by\n// resolving all folder IDs per path (GetAllByPathHash + FID IN queries).\n//\n// A proper fix would be to either:\n//   - Use singleflight keyed by (vaultID, path) to coalesce concurrent creates\n//   - Add a UNIQUE constraint on (vault_id, path_hash) and handle conflict\nfunc (s *folderService) EnsurePathFID(ctx context.Context, uid int64, vaultID int64, path string) (int64, error) {\n\tpath = strings.Trim(path, \"/\")\n\tif path == \"\" {\n\t\treturn 0, nil\n\t}\n\n\t// Decompose directory\n\t// 分解目录\n\tparts := strings.Split(path, \"/\")\n\tvar currentFID int64 = 0\n\n\tfor i := range parts {\n\t\tcurrentPath := strings.Join(parts[:i+1], \"/\")\n\t\tpathHash := util.EncodeHash32(currentPath)\n\n\t\tkey := fmt.Sprintf(\"ensure_folder_%d_%d_%s\", uid, vaultID, pathHash)\n\t\tval, err, _ := s.sf.Do(key, func() (any, error) {\n\t\t\tf, err := s.folderRepo.GetByPathHash(ctx, pathHash, vaultID, uid)\n\t\t\tif err != nil {\n\t\t\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\t\t\tnewFolder := &domain.Folder{\n\t\t\t\t\t\tVaultID:  vaultID,\n\t\t\t\t\t\tAction:   domain.FolderActionCreate,\n\t\t\t\t\t\tPath:     currentPath,\n\t\t\t\t\t\tPathHash: pathHash,\n\t\t\t\t\t\tLevel:    int64(i + 1),\n\t\t\t\t\t\tFID:      currentFID,\n\t\t\t\t\t\tCtime:    timex.Now().UnixMilli(),\n\t\t\t\t\t\tMtime:    timex.Now().UnixMilli(),\n\t\t\t\t\t}\n\t\t\t\t\tf, err = s.folderRepo.Create(ctx, newFolder, uid)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn 0, err\n\t\t\t\t\t}\n\n\t\t\t\t\tif s.syncLogService != nil {\n\t\t\t\t\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeFolder, domain.SyncLogActionCreate, \"\", f.Path, f.PathHash, s.clientType, s.clientName, s.clientVersion, 0)\n\t\t\t\t\t}\n\t\t\t\t\treturn f.ID, nil\n\t\t\t\t}\n\t\t\t\treturn nil, err\n\t\t\t} else if f.Action == domain.FolderActionDelete {\n\t\t\t\tf.Action = domain.FolderActionCreate\n\t\t\t\tf.Ctime = timex.Now().UnixMilli()\n\t\t\t\tf.Mtime = timex.Now().UnixMilli()\n\t\t\t\tf, err = s.folderRepo.Update(ctx, f, uid)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\t\tif s.syncLogService != nil {\n\t\t\t\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeFolder, domain.SyncLogActionRestore, \"\", f.Path, f.PathHash, s.clientType, s.clientName, s.clientVersion, 0)\n\t\t\t\t}\n\t\t\t\treturn f.ID, nil\n\t\t\t}\n\t\t\treturn f.ID, nil\n\t\t})\n\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\tcurrentFID = val.(int64)\n\t}\n\treturn currentFID, nil\n}\n\n// SyncResourceFID syncs FID of resources (called after folder rename/move)\n// SyncResourceFID 同步资源的 FID（在文件夹重命名/移动后调用）\nfunc (s *folderService) SyncResourceFID(ctx context.Context, uid int64, vaultID int64, noteIDs []int64, fileIDs []int64) error {\n\t// Use pool for async execution to avoid CPU spike // 使用协程池异步执行，避免 CPU 飙升\n\tif s.pool != nil {\n\t\t// Create a background context to avoid being cancelled by request context\n\t\t// 使用背景 context 避免被请求 context 取消\n\t\tbgCtx := context.Background()\n\t\terr := s.pool.SubmitAsync(bgCtx, func(c context.Context) error {\n\t\t\treturn s.doSyncResourceFID(c, uid, vaultID, noteIDs, fileIDs)\n\t\t})\n\t\tif err != nil {\n\t\t\t// Fallback to direct goroutine if pool is full/closed (better than losing consistency)\n\t\t\t// 如果池满或关闭，则回退到直接协程执行（保底一致性）\n\t\t\tgo s.doSyncResourceFID(context.Background(), uid, vaultID, noteIDs, fileIDs)\n\t\t}\n\t\treturn nil\n\t}\n\n\t// Legacy behavior for safety if pool is not initialized\n\t// 如果池未初始化，则保留原逻辑\n\tgo s.doSyncResourceFID(context.Background(), uid, vaultID, noteIDs, fileIDs)\n\treturn nil\n}\n\nfunc (s *folderService) doSyncResourceFID(ctx context.Context, uid int64, vaultID int64, noteIDs []int64, fileIDs []int64) error {\n\t// Sync notes\n\t// 同步笔记\n\tvar notes []*domain.Note\n\tvar err error\n\tif len(noteIDs) > 0 {\n\t\tnotes, err = s.noteRepo.ListByIDs(ctx, noteIDs, uid)\n\t} else if len(noteIDs) == 0 && len(fileIDs) == 0 {\n\t\t// 全量同步\n\t\tnotes, err = s.noteRepo.ListByUpdatedTimestamp(ctx, 0, vaultID, uid)\n\t}\n\n\tif err == nil {\n\t\tfor _, n := range notes {\n\t\t\t// Set action\n\t\t\t// 设置 action\n\t\t\tif n.Action == domain.NoteActionDelete {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tpath := strings.Trim(n.Path, \"/\")\n\t\t\tif !strings.Contains(path, \"/\") {\n\t\t\t\tif n.FID != 0 {\n\t\t\t\t\t// 仅更新 FID，不更新 updated_timestamp，避免污染增量同步时间戳\n\t\t\t\t\t// Only update FID without touching updated_timestamp to avoid polluting incremental sync timestamps\n\t\t\t\t\t_ = s.noteRepo.UpdateFID(ctx, n.ID, 0, uid)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tparentPath := path[:strings.LastIndex(path, \"/\")]\n\t\t\tfid, err := s.EnsurePathFID(ctx, uid, vaultID, parentPath)\n\t\t\tif err == nil && n.FID != fid {\n\t\t\t\t// 仅更新 FID，不更新 updated_timestamp，避免污染增量同步时间戳\n\t\t\t\t// Only update FID without touching updated_timestamp to avoid polluting incremental sync timestamps\n\t\t\t\t_ = s.noteRepo.UpdateFID(ctx, n.ID, fid, uid)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Sync files\n\t// 同步文件\n\tvar files []*domain.File\n\tif len(fileIDs) > 0 {\n\t\tfiles, err = s.fileRepo.ListByIDs(ctx, fileIDs, uid)\n\t} else if len(noteIDs) == 0 && len(fileIDs) == 0 {\n\t\t// 全量同步\n\t\tfiles, err = s.fileRepo.ListByUpdatedTimestamp(ctx, 0, vaultID, uid)\n\t}\n\n\tif err == nil {\n\t\tfor _, f := range files {\n\t\t\tif f.Action == domain.FileActionDelete {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tpath := strings.Trim(f.Path, \"/\")\n\t\t\tif !strings.Contains(path, \"/\") {\n\t\t\t\tif f.FID != 0 {\n\t\t\t\t\t// 仅更新 FID，不更新 updated_timestamp，避免污染增量同步时间戳\n\t\t\t\t\t// Only update FID without touching updated_timestamp to avoid polluting incremental sync timestamps\n\t\t\t\t\t_ = s.fileRepo.UpdateFID(ctx, f.ID, 0, uid)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tparentPath := f.Path[:strings.LastIndex(f.Path, \"/\")]\n\t\t\tfid, err := s.EnsurePathFID(ctx, uid, vaultID, parentPath)\n\t\t\tif err == nil && f.FID != fid {\n\t\t\t\t// 仅更新 FID，不更新 updated_timestamp，避免污染增量同步时间戳\n\t\t\t\t// Only update FID without touching updated_timestamp to avoid polluting incremental sync timestamps\n\t\t\t\t_ = s.fileRepo.UpdateFID(ctx, f.ID, fid, uid)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// GetTree returns the complete folder tree structure for a vault\nfunc (s *folderService) GetTree(ctx context.Context, uid int64, params *dto.FolderTreeRequest) (*dto.FolderTreeResponse, error) {\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Get all non-deleted folders // 获取所有未删除的文件夹\n\tfolders, err := s.folderRepo.ListByUpdatedTimestamp(ctx, 0, vaultID, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorFolderListFailed.WithDetails(err.Error())\n\t}\n\n\t// Deduplicate by path, collect all IDs per path for counting\n\ttype folderInfo struct {\n\t\tpath   string\n\t\tids    []int64 // all DB IDs for this path (for counting notes/files)\n\t\tparent string  // parent path (\"\" for root)\n\t}\n\tinfoByPath := make(map[string]*folderInfo)\n\n\tfor _, f := range folders {\n\t\tif f.Action == domain.FolderActionDelete {\n\t\t\tcontinue\n\t\t}\n\t\tinfo, exists := infoByPath[f.Path]\n\t\tif !exists {\n\t\t\tparent := \"\"\n\t\t\tif idx := strings.LastIndex(f.Path, \"/\"); idx >= 0 {\n\t\t\t\tparent = f.Path[:idx]\n\t\t\t}\n\t\t\tinfo = &folderInfo{path: f.Path, parent: parent}\n\t\t\tinfoByPath[f.Path] = info\n\t\t}\n\t\tinfo.ids = append(info.ids, f.ID)\n\t}\n\n\t// Count notes and files per folder (sum across all duplicate IDs)\n\tnoteCountByPath := make(map[string]int)\n\tfileCountByPath := make(map[string]int)\n\tfor path, info := range infoByPath {\n\t\tfor _, id := range info.ids {\n\t\t\tnc, err := s.noteRepo.ListByFIDCount(ctx, id, vaultID, uid)\n\t\t\tif err == nil {\n\t\t\t\tnoteCountByPath[path] += int(nc)\n\t\t\t}\n\t\t\tfc, err := s.fileRepo.ListByFIDCount(ctx, id, vaultID, uid)\n\t\t\tif err == nil {\n\t\t\t\tfileCountByPath[path] += int(fc)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Root counts (FID = 0)\n\trootNoteCount := 0\n\trootFileCount := 0\n\tcount, err := s.noteRepo.ListByFIDCount(ctx, 0, vaultID, uid)\n\tif err == nil {\n\t\trootNoteCount = int(count)\n\t}\n\tcount, err = s.fileRepo.ListByFIDCount(ctx, 0, vaultID, uid)\n\tif err == nil {\n\t\trootFileCount = int(count)\n\t}\n\n\t// Build parent→children map by path\n\tchildrenByParent := make(map[string][]string)\n\tfor path, info := range infoByPath {\n\t\tchildrenByParent[info.parent] = append(childrenByParent[info.parent], path)\n\t}\n\n\t// Build tree recursively\n\tvar buildNode func(path string, currentDepth int) *dto.FolderTreeNode\n\tbuildNode = func(path string, currentDepth int) *dto.FolderTreeNode {\n\t\tname := path\n\t\tif idx := strings.LastIndex(path, \"/\"); idx >= 0 {\n\t\t\tname = path[idx+1:]\n\t\t}\n\n\t\tnode := &dto.FolderTreeNode{\n\t\t\tPath:      path,\n\t\t\tName:      name,\n\t\t\tNoteCount: noteCountByPath[path],\n\t\t\tFileCount: fileCountByPath[path],\n\t\t}\n\n\t\tif params.Depth > 0 && currentDepth >= params.Depth {\n\t\t\treturn node\n\t\t}\n\n\t\tfor _, childPath := range childrenByParent[path] {\n\t\t\tnode.Children = append(node.Children, buildNode(childPath, currentDepth+1))\n\t\t}\n\n\t\treturn node\n\t}\n\n\t// Build root level folders (parent = \"\")\n\tvar rootFolders []*dto.FolderTreeNode\n\tfor _, path := range childrenByParent[\"\"] {\n\t\trootFolders = append(rootFolders, buildNode(path, 1))\n\t}\n\n\treturn &dto.FolderTreeResponse{\n\t\tFolders:       rootFolders,\n\t\tRootNoteCount: rootNoteCount,\n\t\tRootFileCount: rootFileCount,\n\t}, nil\n}\n\nfunc (s *folderService) CleanDuplicateFolders(ctx context.Context, uid int64, vaultID int64) error {\n\t// 1. Get all folder records (including deleted ones for logical cleanup)\n\t// 1. 获取所有文件夹记录（包含已删除的，以便按逻辑清理）\n\tfolders, err := s.folderRepo.ListByUpdatedTimestamp(ctx, 0, vaultID, uid)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// 2. Group by PathHash\n\t// 2. 按 PathHash 分组\n\tgrouped := make(map[string][]*domain.Folder)\n\tfor _, f := range folders {\n\t\tgrouped[f.PathHash] = append(grouped[f.PathHash], f)\n\t}\n\n\t// 3. Traverse groups and identify duplicates\n\t// 3. 遍历分组，识别重复项\n\tfor pathHash, list := range grouped {\n\t\tif len(list) <= 1 {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Check if any deleted records exist in this group to resolve issues where deleted folders are recreated by EnsurePathFID\n\t\t// 检查这组重复记录中是否有被标记为删除的（处理已删除但仍被 EnsurePathFID 误创出的情况）\n\t\tvar hasDeleted bool\n\t\tfor _, f := range list {\n\t\t\tif f.Action == domain.FolderActionDelete {\n\t\t\t\thasDeleted = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif hasDeleted {\n\t\t\t// If deleted records exist, remove all active records for this path to resolve consistency\n\t\t\t// 如果存在已删除记录，则删除所有未标记删除的记录（解决已删除但仍被 EnsurePathFID 误创出的活跃记录）\n\t\t\tfor _, f := range list {\n\t\t\t\tif f.Action != domain.FolderActionDelete {\n\t\t\t\t\ts.sf.Forget(fmt.Sprintf(\"ensure_folder_%d_%d_%s\", uid, vaultID, pathHash))\n\t\t\t\t\t_ = s.folderRepo.Delete(ctx, f.ID, uid)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// If all records are active, keep the one with the largest ID (assumed to be the latest)\n\t\t\t// 如果全部都是活跃记录，保留 ID 最大的一条（假设是最后创建的）\n\t\t\tvar maxID int64\n\t\t\tfor _, f := range list {\n\t\t\t\tif f.ID > maxID {\n\t\t\t\t\tmaxID = f.ID\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor _, f := range list {\n\t\t\t\tif f.ID != maxID {\n\t\t\t\t\ts.sf.Forget(fmt.Sprintf(\"ensure_folder_%d_%d_%s\", uid, vaultID, pathHash))\n\t\t\t\t\t_ = s.folderRepo.Delete(ctx, f.ID, uid)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "internal/service/folder_service_test.go",
    "content": "// Package service implements the business logic layer.\n// Package service 实现业务逻辑层。\npackage service\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\tdomainmocks \"github.com/haierkeys/fast-note-sync-service/internal/domain/mocks\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// TestCleanDuplicateFolders uses table-driven tests to verify dedup logic.\n// TestCleanDuplicateFolders 使用表驱动测试验证重复文件夹清理逻辑。\nfunc TestCleanDuplicateFolders(t *testing.T) {\n\tctx := context.Background()\n\tuid := int64(1)\n\tvaultID := int64(1)\n\n\ttests := []struct {\n\t\tname           string                // test case name / 测试用例名称\n\t\tfolders        []*domain.Folder      // input folders / 输入的文件夹列表\n\t\twantDeletedIDs []int64               // expected deleted IDs (order-insensitive) / 期望被删除的 ID（顺序无关）\n\t}{\n\t\t{\n\t\t\t// When a hash has both delete and create records, the active ones should be deleted.\n\t\t\t// 当同一 hash 既有 delete 也有 create 记录时，应删除 active 记录。\n\t\t\tname: \"mixed deleted and active - delete active\",\n\t\t\tfolders: []*domain.Folder{\n\t\t\t\t{ID: 1, PathHash: \"h1\", Action: domain.FolderActionDelete},\n\t\t\t\t{ID: 2, PathHash: \"h1\", Action: domain.FolderActionCreate},\n\t\t\t},\n\t\t\twantDeletedIDs: []int64{2},\n\t\t},\n\t\t{\n\t\t\t// When all records are active, keep the highest ID and delete the rest.\n\t\t\t// 当所有记录都是 active 时，保留最大 ID，删除其余记录。\n\t\t\tname: \"all active - keep max ID\",\n\t\t\tfolders: []*domain.Folder{\n\t\t\t\t{ID: 3, PathHash: \"h2\", Action: domain.FolderActionCreate},\n\t\t\t\t{ID: 4, PathHash: \"h2\", Action: domain.FolderActionCreate},\n\t\t\t\t{ID: 5, PathHash: \"h2\", Action: domain.FolderActionCreate},\n\t\t\t},\n\t\t\twantDeletedIDs: []int64{3, 4},\n\t\t},\n\t\t{\n\t\t\t// When each hash has only one record, nothing should be deleted.\n\t\t\t// 当每个 hash 只有一条记录时，不应删除任何内容。\n\t\t\tname: \"no duplicates - delete nothing\",\n\t\t\tfolders: []*domain.Folder{\n\t\t\t\t{ID: 6, PathHash: \"h3\", Action: domain.FolderActionCreate},\n\t\t\t\t{ID: 7, PathHash: \"h4\", Action: domain.FolderActionCreate},\n\t\t\t},\n\t\t\twantDeletedIDs: nil,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tmockRepo := new(domainmocks.MockFolderRepository)\n\n\t\t\t// Stub ListByUpdatedTimestamp to return the test fixture folders.\n\t\t\t// Stub ListByUpdatedTimestamp 返回测试固定数据的文件夹列表。\n\t\t\tmockRepo.On(\"ListByUpdatedTimestamp\", mock.Anything, int64(0), vaultID, uid).\n\t\t\t\tReturn(tt.folders, nil)\n\n\t\t\t// Stub Delete for each expected deleted ID.\n\t\t\t// 为每个期望删除的 ID Stub Delete。\n\t\t\tfor _, id := range tt.wantDeletedIDs {\n\t\t\t\tmockRepo.On(\"Delete\", mock.Anything, id, uid).Return(nil)\n\t\t\t}\n\n\t\t\tsvc := &folderService{folderRepo: mockRepo}\n\t\t\terr := svc.CleanDuplicateFolders(ctx, uid, vaultID)\n\n\t\t\tassert.NoError(t, err)\n\t\t\tmockRepo.AssertExpectations(t)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/service/git_sync_service.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"runtime/debug\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-git/go-git/v5\"\n\t\"github.com/go-git/go-git/v5/config\"\n\t\"github.com/go-git/go-git/v5/plumbing\"\n\t\"github.com/go-git/go-git/v5/plumbing/object\"\n\t\"github.com/go-git/go-git/v5/plumbing/transport\"\n\tgithttp \"github.com/go-git/go-git/v5/plumbing/transport/http\"\n\tappconfig \"github.com/haierkeys/fast-note-sync-service/internal/config\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"go.uber.org/zap\"\n\t\"gorm.io/gorm\"\n)\n\n// errNoChanges indicates that no changes were found after the Git sync check\n// errNoChanges 表示 Git 同步检查后没有发现任何需要提交的变更\nvar errNoChanges = errors.New(\"no changes found\")\n\nconst gitSyncBatchSize = 100\n\n// GitSyncService defines the Git synchronization business service interface\n// GitSyncService 定义 Git 同步业务服务接口\ntype GitSyncService interface {\n\tGetConfigs(ctx context.Context, uid int64) ([]*dto.GitSyncConfigDTO, error)\n\tGetConfig(ctx context.Context, uid int64, vaultID int64) (*dto.GitSyncConfigDTO, error)\n\tUpdateConfig(ctx context.Context, uid int64, params *dto.GitSyncConfigRequest) (*dto.GitSyncConfigDTO, error)\n\tDeleteConfig(ctx context.Context, uid int64, id int64) error\n\tValidate(ctx context.Context, params *dto.GitSyncValidateRequest) error\n\tExecuteSync(ctx context.Context, uid int64, id int64) error\n\tCleanWorkspace(ctx context.Context, uid int64, configID int64) error\n\tListHistory(ctx context.Context, uid int64, configID int64, pager *pkgapp.Pager) ([]*dto.GitSyncHistoryDTO, int64, error)\n\tNotifyUpdated(uid int64, vaultID int64)\n\tShutdown(ctx context.Context) error\n}\n\ntype gitSyncService struct {\n\trepo       domain.GitSyncRepository\n\tnoteRepo   domain.NoteRepository\n\tfolderRepo domain.FolderRepository\n\tfileRepo   domain.FileRepository\n\tvaultRepo  domain.VaultRepository\n\tgitConf    *appconfig.GitConfig\n\tlogger     *zap.Logger\n\tmu         sync.Mutex\n\trunning    map[int64]context.CancelFunc // configID -> cancelFunc\n\ttimers     map[int64]*time.Timer        // configID -> timer\n\tctx        context.Context\n\tcancel     context.CancelFunc\n\twg         sync.WaitGroup\n\tgcTimer    *time.Timer // Timer for delayed GC // 延迟 GC 定时器\n\tgcMu       sync.Mutex  // Mutex for gcTimer // 保护 gcTimer 的互斥锁\n}\n\n// NewGitSyncService creates a GitSyncService instance\n// NewGitSyncService 创建 GitSyncService 实例\nfunc NewGitSyncService(repo domain.GitSyncRepository, noteRepo domain.NoteRepository, folderRepo domain.FolderRepository, fileRepo domain.FileRepository, vaultRepo domain.VaultRepository, gitConf *appconfig.GitConfig, logger *zap.Logger) GitSyncService {\n\tctx, cancel := context.WithCancel(context.Background())\n\treturn &gitSyncService{\n\t\trepo:       repo,\n\t\tnoteRepo:   noteRepo,\n\t\tfolderRepo: folderRepo,\n\t\tfileRepo:   fileRepo,\n\t\tvaultRepo:  vaultRepo,\n\t\tgitConf:    gitConf,\n\t\tlogger:     logger,\n\t\trunning:    make(map[int64]context.CancelFunc),\n\t\ttimers:     make(map[int64]*time.Timer),\n\t\tctx:        ctx,\n\t\tcancel:     cancel,\n\t}\n}\n\nfunc (s *gitSyncService) domainToDTO(conf *domain.GitSyncConfig) *dto.GitSyncConfigDTO {\n\tif conf == nil {\n\t\treturn nil\n\t}\n\tres := &dto.GitSyncConfigDTO{\n\t\tID:            conf.ID,\n\t\tUID:           conf.UID,\n\t\tRepoURL:       conf.RepoURL,\n\t\tUsername:      conf.Username,\n\t\tPassword:      conf.Password,\n\t\tBranch:        conf.Branch,\n\t\tIsEnabled:     conf.IsEnabled,\n\t\tDelay:         conf.Delay,\n\t\tRetentionDays: conf.RetentionDays,\n\t\tLastStatus:    conf.LastStatus,\n\t\tLastMessage:   conf.LastMessage,\n\t\tCreatedAt:     timex.Time(conf.CreatedAt),\n\t\tUpdatedAt:     timex.Time(conf.UpdatedAt),\n\t}\n\tif conf.LastSyncTime != nil {\n\t\tres.LastSyncTime = timex.Time(*conf.LastSyncTime)\n\t}\n\n\t// Fetch vault name if possible\n\tif conf.VaultID > 0 {\n\t\tv, err := s.vaultRepo.GetByID(context.Background(), conf.VaultID, conf.UID)\n\t\tif err == nil {\n\t\t\tres.Vault = v.Name\n\t\t}\n\t}\n\n\treturn res\n}\n\nfunc (s *gitSyncService) GetConfigs(ctx context.Context, uid int64) ([]*dto.GitSyncConfigDTO, error) {\n\tconfigs, err := s.repo.List(ctx, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\tvar res []*dto.GitSyncConfigDTO\n\tfor _, c := range configs {\n\t\tres = append(res, s.domainToDTO(c))\n\t}\n\treturn res, nil\n}\n\nfunc (s *gitSyncService) GetConfig(ctx context.Context, uid int64, vaultID int64) (*dto.GitSyncConfigDTO, error) {\n\tconf, err := s.repo.GetByVaultID(ctx, vaultID, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\tif conf == nil {\n\t\treturn nil, code.ErrorVaultNotFound\n\t}\n\treturn s.domainToDTO(conf), nil\n}\n\nfunc (s *gitSyncService) UpdateConfig(ctx context.Context, uid int64, params *dto.GitSyncConfigRequest) (*dto.GitSyncConfigDTO, error) {\n\tvar conf *domain.GitSyncConfig\n\tvar err error\n\n\tif params.ID > 0 {\n\t\tconf, err = s.repo.GetByID(ctx, params.ID, uid)\n\t\tif err != nil {\n\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t}\n\t\tif conf == nil {\n\t\t\treturn nil, code.ErrorGitSyncNotFound\n\t\t}\n\t} else {\n\t\tconf = &domain.GitSyncConfig{\n\t\t\tUID: uid,\n\t\t}\n\t}\n\n\tif params.Vault != \"\" {\n\t\tv, err := s.vaultRepo.GetByName(ctx, params.Vault, uid)\n\t\tif err != nil {\n\t\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\t\treturn nil, code.ErrorVaultNotFound\n\t\t\t}\n\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t}\n\t\tconf.VaultID = v.ID\n\t}\n\n\tconf.RepoURL = params.RepoURL\n\tconf.Username = params.Username\n\tconf.Password = params.Password\n\tconf.Branch = params.Branch\n\tif conf.Branch == \"\" {\n\t\tconf.Branch = \"main\"\n\t}\n\tconf.IsEnabled = params.IsEnabled\n\tconf.Delay = params.Delay\n\tconf.RetentionDays = params.RetentionDays\n\n\tsaved, err := s.repo.Save(ctx, conf, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\treturn s.domainToDTO(saved), nil\n}\n\nfunc (s *gitSyncService) DeleteConfig(ctx context.Context, uid int64, id int64) error {\n\t// 1. Verification identity // 1. 验证身份\n\tconf, err := s.repo.GetByID(ctx, id, uid)\n\tif err != nil {\n\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\tif conf == nil {\n\t\treturn code.ErrorGitSyncNotFound\n\t}\n\n\terr = s.repo.Delete(ctx, id, uid)\n\tif err != nil {\n\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Clean workspace as well? User request said \"Cleanup API\". Delete config doesn't necessarily mean delete workspace.\n\t// But usually it's better to cleanup. However, I'll follow the plan and keep them separate as per the \"Cleanup API\" request.\n\n\treturn nil\n}\n\nfunc (s *gitSyncService) Validate(ctx context.Context, params *dto.GitSyncValidateRequest) error {\n\tbranch := params.Branch\n\tif branch == \"\" {\n\t\tbranch = \"main\"\n\t}\n\n\tauth := &githttp.BasicAuth{\n\t\tUsername: params.Username,\n\t\tPassword: params.Password,\n\t}\n\n\t// Try LsRemote to validate credentials and repo visibility\n\trem := git.NewRemote(nil, &config.RemoteConfig{\n\t\tName: \"origin\",\n\t\tURLs: []string{params.RepoURL},\n\t})\n\n\trefs, err := rem.List(&git.ListOptions{\n\t\tAuth: auth,\n\t})\n\tif err != nil {\n\t\tif errors.Is(err, transport.ErrEmptyRemoteRepository) {\n\t\t\t// Remote is empty, validation success\n\t\t\treturn nil\n\t\t}\n\t\treturn code.ErrorGitSyncValidateFailed.WithDetails(err.Error())\n\t}\n\n\t// Check if branch exists\n\tbranchRef := plumbing.NewBranchReferenceName(branch)\n\tfound := false\n\tfor _, ref := range refs {\n\t\tif ref.Name() == branchRef || ref.Name() == plumbing.HEAD {\n\t\t\tfound = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif !found {\n\t\t// Even if branch not found, if it's an empty repo (though List usually returns ErrEmptyRemoteRepository),\n\t\t// we should have caught it above. If we are here, it means refs is not empty but branch not found.\n\t\treturn code.ErrorGitSyncValidateFailed.WithDetails(\"Branch not found in remote\")\n\t}\n\n\treturn nil\n}\n\nfunc (s *gitSyncService) ExecuteSync(ctx context.Context, uid int64, id int64) error {\n\tconf, err := s.repo.GetByID(ctx, id, uid)\n\tif err != nil {\n\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\tif conf == nil {\n\t\treturn code.ErrorGitSyncNotFound\n\t}\n\n\t// Strategy: For sync/mirror sync, directly cancel the old task and start a new one\n\t// 策略：同步/镜像同步直接取消旧任务，启动新任务\n\ts.mu.Lock()\n\tif oldCancel, running := s.running[id]; running {\n\t\ts.logger.Info(\"Cancelling existing Git sync task to start a newer one\", zap.Int64(\"uid\", uid), zap.Int64(\"configId\", id))\n\t\toldCancel()\n\t\tdelete(s.running, id)\n\t}\n\n\t// Create context for new task\n\t// 为新任务创建 context\n\ttaskCtx, taskCancel := context.WithCancel(s.ctx)\n\ts.running[id] = taskCancel\n\ts.mu.Unlock()\n\n\ts.wg.Add(1)\n\t// Run in background\n\tgo func() {\n\t\tdefer func() {\n\t\t\ts.mu.Lock()\n\t\t\t// Ensure only the current cancel function is cleaned up\n\t\t\t// 确保只清理当前的 cancel 函数\n\t\t\tif _, ok := s.running[id]; ok {\n\t\t\t\t// 虽然 sync 策略下会先 cancel 再 set，但为了闭包内引用的严谨\n\t\t\t\tdelete(s.running, id)\n\t\t\t}\n\t\t\ts.mu.Unlock()\n\t\t\ttaskCancel()\n\t\t\ts.wg.Done()\n\t\t}()\n\n\t\t// Use the newly created task context\n\t\t// 使用新创建的任务 context\n\t\ts.syncTask(taskCtx, conf)\n\t}()\n\n\treturn nil\n}\n\nfunc (s *gitSyncService) CleanWorkspace(ctx context.Context, uid int64, configID int64) error {\n\tif configID > 0 {\n\t\t// 1. Reset database fields // 1. 重置数据库字段\n\t\tconf, err := s.repo.GetByID(ctx, configID, uid)\n\t\tif err != nil {\n\t\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t\t}\n\t\tif conf == nil {\n\t\t\treturn code.ErrorGitSyncNotFound\n\t\t}\n\n\t\tconf.LastSyncTime = nil\n\t\tconf.LastStatus = domain.GitSyncStatusIdle\n\t\tconf.LastMessage = \"\"\n\n\t\t_, err = s.repo.Save(ctx, conf, uid)\n\t\tif err != nil {\n\t\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t\t}\n\n\t\t// 2. Delete History // 2. 删除历史记录\n\t\t_ = s.repo.DeleteHistory(ctx, uid, configID)\n\n\t\t// 3. Remove physical workspace // 3. 删除物理工作区\n\t\tpath := s.getWorkspacePath(uid, configID)\n\t\terr = os.RemoveAll(path)\n\t\tif err != nil {\n\t\t\ts.logger.Warn(\"Failed to cleanup physical workspace\", zap.String(\"path\", path), zap.Error(err))\n\t\t}\n\t} else {\n\t\t// 1. Reset all database fields for user // 1. 重置用户的所有数据库字段\n\t\tconfigs, err := s.repo.List(ctx, uid)\n\t\tif err != nil {\n\t\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t\t}\n\t\tfor _, conf := range configs {\n\t\t\tconf.LastSyncTime = nil\n\t\t\tconf.LastStatus = domain.GitSyncStatusIdle\n\t\t\tconf.LastMessage = \"\"\n\t\t\t_, _ = s.repo.Save(ctx, conf, uid)\n\t\t}\n\n\t\t// 2. Delete All History for user // 2. 删除用户的所有历史记录\n\t\t_ = s.repo.DeleteHistory(ctx, uid, 0)\n\n\t\t// 3. Remove all physical workspaces for user // 3. 删除用户的所有物理工作区\n\t\tpath := s.getUserWorkspacePath(uid)\n\t\terr = os.RemoveAll(path)\n\t\tif err != nil {\n\t\t\ts.logger.Warn(\"Failed to cleanup user workspaces\", zap.String(\"path\", path), zap.Error(err))\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (s *gitSyncService) ListHistory(ctx context.Context, uid int64, configID int64, pager *pkgapp.Pager) ([]*dto.GitSyncHistoryDTO, int64, error) {\n\thistories, count, err := s.repo.ListHistory(ctx, uid, configID, pager.Page, pager.PageSize)\n\tif err != nil {\n\t\treturn nil, 0, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\tvar res []*dto.GitSyncHistoryDTO\n\tfor _, h := range histories {\n\t\tres = append(res, s.historyToDTO(h))\n\t}\n\treturn res, count, nil\n}\n\nfunc (s *gitSyncService) Shutdown(ctx context.Context) error {\n\ts.cancel()\n\n\ts.mu.Lock()\n\tfor _, cancel := range s.running {\n\t\tcancel()\n\t}\n\tfor _, timer := range s.timers {\n\t\ttimer.Stop()\n\t}\n\ts.mu.Unlock()\n\n\ts.gcMu.Lock()\n\tif s.gcTimer != nil {\n\t\ts.gcTimer.Stop()\n\t}\n\ts.gcMu.Unlock()\n\n\tdone := make(chan struct{})\n\tgo func() {\n\t\ts.wg.Wait()\n\t\tclose(done)\n\t}()\n\n\tselect {\n\tcase <-done:\n\t\treturn nil\n\tcase <-ctx.Done():\n\t\treturn ctx.Err()\n\t}\n}\n\n// Internal methods // 内部方法\n\nfunc (s *gitSyncService) historyToDTO(h *domain.GitSyncHistory) *dto.GitSyncHistoryDTO {\n\tif h == nil {\n\t\treturn nil\n\t}\n\treturn &dto.GitSyncHistoryDTO{\n\t\tID:        h.ID,\n\t\tConfigID:  h.ConfigID,\n\t\tStartTime: timex.Time(h.StartTime),\n\t\tEndTime:   timex.Time(h.EndTime),\n\t\tStatus:    h.Status,\n\t\tMessage:   h.Message,\n\t\tCreatedAt: timex.Time(h.CreatedAt),\n\t}\n}\n\nfunc (s *gitSyncService) getWorkspacePath(uid, configID int64) string {\n\treturn filepath.Join(s.getUserWorkspacePath(uid), fmt.Sprintf(\"%d\", configID))\n}\n\nfunc (s *gitSyncService) getUserWorkspacePath(uid int64) string {\n\treturn filepath.Join(\"storage\", \"git_workspace\", fmt.Sprintf(\"%d\", uid))\n}\n\nfunc (s *gitSyncService) syncTask(ctx context.Context, conf *domain.GitSyncConfig) {\n\tstartTime := time.Now()\n\ts.logger.Info(\"Starting Git sync task\", zap.Int64(\"configId\", conf.ID), zap.Int64(\"uid\", conf.UID))\n\n\t// Record status before running to recover if there are no changes\n\t// 记录运行前的状态，以便无变更时恢复\n\tprevStatus := conf.LastStatus\n\n\t// Update Config Status to Running\n\tconf.LastStatus = domain.GitSyncStatusRunning\n\t_, _ = s.repo.Save(ctx, conf, conf.UID)\n\n\terr := s.doSync(ctx, conf)\n\n\t// No changes: restore original status, trigger Save only to update updated_at\n\t// Without writing history, without changing last_sync_time / last_status / last_message\n\t// 无变更：恢复原始状态，只触发 Save 更新 updated_at\n\t// 不写 history，不改 last_sync_time / last_status / last_message\n\tif errors.Is(err, errNoChanges) {\n\t\ts.logger.Info(\"No changes found, skipping history and status update\", zap.Int64(\"configId\", conf.ID))\n\t\tconf.LastStatus = prevStatus\n\t\t_, _ = s.repo.Save(context.Background(), conf, conf.UID)\n\t\treturn\n\t}\n\n\tendTime := time.Now()\n\tvar finalStatus int64\n\tvar message string\n\n\tif ctx.Err() != nil {\n\t\tfinalStatus = domain.GitSyncStatusShutdown\n\t\tmessage = \"Sync stopped by system shutdown\"\n\t\tif err != nil {\n\t\t\tmessage += \": \" + err.Error()\n\t\t}\n\t} else if err != nil {\n\t\ts.logger.Error(\"Git sync task failed\", zap.Int64(\"configId\", conf.ID), zap.Error(err))\n\t\tfinalStatus = domain.GitSyncStatusFailed\n\t\tmessage = err.Error()\n\t} else {\n\t\ts.logger.Info(\"Git sync task success\", zap.Int64(\"configId\", conf.ID))\n\t\tfinalStatus = domain.GitSyncStatusSuccess\n\t\tmessage = \"Sync completed at \" + endTime.Format(\"2006-01-02 15:04:05\")\n\t\tconf.LastSyncTime = &endTime\n\t}\n\n\t// Update Config Final Status\n\tconf.LastStatus = finalStatus\n\tconf.LastMessage = message\n\t_, _ = s.repo.Save(context.Background(), conf, conf.UID)\n\n\t// Create History Record\n\th := &domain.GitSyncHistory{\n\t\tConfigID:  conf.ID,\n\t\tUID:       conf.UID,\n\t\tStartTime: startTime,\n\t\tEndTime:   endTime,\n\t\tStatus:    finalStatus,\n\t\tMessage:   message,\n\t}\n\t_, _ = s.repo.CreateHistory(context.Background(), h, conf.UID)\n\n\t// Automatically clean up expired history records\n\t// 自动清理过期历史记录\n\tif conf.RetentionDays != 0 {\n\t\tvar cutoffTime time.Time\n\t\tif conf.RetentionDays == -1 {\n\t\t\t// -1 means retain only the current latest record\n\t\t\t// -1 表示仅保留当前最新的一条记录\n\t\t\tcutoffTime = startTime\n\t\t} else if conf.RetentionDays > 0 {\n\t\t\t// > 0 means clean up records exceeding specified days\n\t\t\t// > 0 表示清理超过指定天数的记录\n\t\t\tcutoffTime = time.Now().AddDate(0, 0, -int(conf.RetentionDays))\n\t\t}\n\n\t\tif !cutoffTime.IsZero() {\n\t\t\tif err := s.repo.DeleteOldHistory(context.Background(), conf.UID, conf.ID, cutoffTime); err != nil {\n\t\t\t\ts.logger.Error(\"Failed to delete old git sync history\", zap.Error(err), zap.Int64(\"configId\", conf.ID))\n\t\t\t}\n\t\t}\n\t}\n\n\t// Schedule delayed memory release after task completion (addressing Issue #113)\n\t// Return virtual memory to OS 30 minutes after high-pressure sync ends to avoid frequent operations\n\t// 任务结束后调度延迟内存释放 (针对 Issue #113)\n\t// 高压同步结束后 30 分钟再归还虚拟内存给操作系统，避免频繁操作\n\ts.scheduleGC()\n}\n\n// scheduleGC schedules a delayed GC and FreeOSMemory call (debounced)\n// scheduleGC 调度一个延迟的 GC 和内存释放操作（防抖）\nfunc (s *gitSyncService) scheduleGC() {\n\ts.gcMu.Lock()\n\tdefer s.gcMu.Unlock()\n\n\tif s.gcTimer != nil {\n\t\ts.gcTimer.Stop()\n\t}\n\n\ts.gcTimer = time.AfterFunc(30*time.Minute, func() {\n\t\ts.logger.Info(\"Triggering delayed background GC and memory release to OS\")\n\t\truntime.GC()\n\t\tdebug.FreeOSMemory()\n\t})\n}\n\nfunc (s *gitSyncService) doSync(ctx context.Context, conf *domain.GitSyncConfig) error {\n\twsPath := s.getWorkspacePath(conf.UID, conf.ID)\n\tauth := &githttp.BasicAuth{\n\t\tUsername: conf.Username,\n\t\tPassword: conf.Password,\n\t}\n\n\tvar r *git.Repository\n\tvar err error\n\n\t// 1. Check/Init Local Repo // 1. 检查/初始化本地仓库\n\tif _, err := os.Stat(filepath.Join(wsPath, \".git\")); os.IsNotExist(err) {\n\t\ts.logger.Info(\"Initializing local git repo\", zap.String(\"path\", wsPath))\n\t\t_ = os.RemoveAll(wsPath)\n\t\tr, err = git.PlainClone(wsPath, false, &git.CloneOptions{\n\t\t\tURL:           conf.RepoURL,\n\t\t\tAuth:          auth,\n\t\t\tReferenceName: plumbing.NewBranchReferenceName(conf.Branch),\n\t\t\tSingleBranch:  true,\n\t\t\tDepth:         1,\n\t\t\tTags:          git.NoTags,\n\t\t})\n\t\tif err != nil {\n\t\t\tif errors.Is(err, transport.ErrEmptyRemoteRepository) {\n\t\t\t\ts.logger.Info(\"Remote repository is empty, initializing locally\", zap.String(\"path\", wsPath))\n\t\t\t\tr, err = git.PlainInit(wsPath, false)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"git init failed: %w\", err)\n\t\t\t\t}\n\t\t\t\t_, err = r.CreateRemote(&config.RemoteConfig{\n\t\t\t\t\tName: \"origin\",\n\t\t\t\t\tURLs: []string{conf.RepoURL},\n\t\t\t\t})\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"create remote failed: %w\", err)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn fmt.Errorf(\"git clone failed: %w\", err)\n\t\t\t}\n\t\t}\n\t} else {\n\t\tr, err = git.PlainOpen(wsPath)\n\t\tif err != nil {\n\t\t\t// Try to re-init if open fails\n\t\t\t_ = os.RemoveAll(wsPath)\n\t\t\treturn s.doSync(ctx, conf)\n\t\t}\n\t}\n\n\twt, err := r.Worktree()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// 2. Pull latest // 2. 拉取最新变更\n\ts.logger.Info(\"Pulling latest changes\", zap.Int64(\"configId\", conf.ID))\n\terr = wt.Pull(&git.PullOptions{\n\t\tAuth:          auth,\n\t\tReferenceName: plumbing.NewBranchReferenceName(conf.Branch),\n\t\tSingleBranch:  true,\n\t\tForce:         true,\n\t\tDepth:         1,\n\t})\n\tif err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) {\n\t\tif errors.Is(err, transport.ErrEmptyRemoteRepository) || errors.Is(err, git.ErrRemoteNotFound) || errors.Is(err, plumbing.ErrReferenceNotFound) {\n\t\t\ts.logger.Info(\"Remote is empty or branch not found, skipping pull\", zap.Int64(\"configId\", conf.ID))\n\t\t} else {\n\t\t\treturn fmt.Errorf(\"git pull failed: %w\", err)\n\t\t}\n\t}\n\n\t// 3. Extract DB content to Workspace // 3. 提取 DB 内容到工作区\n\t// We need to mirror files from DB to this workspace // 我们需要将 DB 中的文件镜像到此工作区\n\tchanged, err := s.mirrorNotesToWorkspace(ctx, conf, wsPath, conf.LastSyncTime)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"mirror to workspace failed: %w\", err)\n\t}\n\n\tif !changed {\n\t\ts.logger.Info(\"No notes or attachments updated, skipping Git operations\", zap.Int64(\"configId\", conf.ID))\n\t\treturn errNoChanges\n\t}\n\n\t// 4. Commit and Push // 4. 提交并推送\n\tstatus, err := wt.Status()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif status.IsClean() {\n\t\ts.logger.Info(\"No changes to commit\", zap.Int64(\"configId\", conf.ID))\n\t\treturn errNoChanges\n\t}\n\n\terr = wt.AddWithOptions(&git.AddOptions{All: true})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tname := s.gitConf.Name\n\tif name == \"\" {\n\t\tname = \"FNS Service\"\n\t}\n\temail := s.gitConf.Email\n\tif email == \"\" {\n\t\temail = \"fns@email.com\"\n\t}\n\n\t_, err = wt.Commit(\"Update from Sync Service\", &git.CommitOptions{\n\t\tAuthor: &object.Signature{\n\t\t\tName:  name,\n\t\t\tEmail: email,\n\t\t\tWhen:  time.Now(),\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ts.logger.Info(\"Pushing changes\", zap.Int64(\"configId\", conf.ID))\n\terr = r.Push(&git.PushOptions{\n\t\tAuth:  auth,\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"git push failed: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (s *gitSyncService) mirrorNotesToWorkspace(ctx context.Context, conf *domain.GitSyncConfig, wsPath string, lastSyncTime *time.Time) (bool, error) {\n\tv, err := s.vaultRepo.GetByID(ctx, conf.VaultID, conf.UID)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif v == nil {\n\t\treturn false, fmt.Errorf(\"vault not found\")\n\t}\n\n\tvar ts int64\n\tif lastSyncTime != nil {\n\t\tts = lastSyncTime.UnixMilli()\n\t\ts.logger.Info(\"Performing incremental sync to workspace\", zap.Int64(\"configId\", conf.ID), zap.Int64(\"sinceTs\", ts))\n\t} else {\n\t\ts.logger.Info(\"Performing initial full sync to workspace (using unified incremental method)\", zap.Int64(\"configId\", conf.ID))\n\t}\n\n\tvar actuallyChanged bool\n\n\tfor offset := 0; ; offset += gitSyncBatchSize {\n\t\tnotes, err := s.noteRepo.ListByUpdatedTimestampPage(ctx, ts, v.ID, conf.UID, offset, gitSyncBatchSize)\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t\tif len(notes) == 0 {\n\t\t\tbreak\n\t\t}\n\n\t\tbatchChanged, err := s.processNotesBatch(notes, wsPath)\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t\tif batchChanged {\n\t\t\tactuallyChanged = true\n\t\t}\n\n\t\tif len(notes) < gitSyncBatchSize {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tfor offset := 0; ; offset += gitSyncBatchSize {\n\t\tfiles, err := s.fileRepo.ListByUpdatedTimestampPage(ctx, ts, v.ID, conf.UID, offset, gitSyncBatchSize)\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t\tif len(files) == 0 {\n\t\t\tbreak\n\t\t}\n\n\t\tbatchChanged, err := s.processFilesBatch(files, conf, wsPath)\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t\tif batchChanged {\n\t\t\tactuallyChanged = true\n\t\t}\n\n\t\tif len(files) < gitSyncBatchSize {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn actuallyChanged, nil\n}\n\nfunc (s *gitSyncService) processNotesBatch(notes []*domain.Note, wsPath string) (bool, error) {\n\tvar actuallyChanged bool\n\n\tfor _, n := range notes {\n\t\ttargetPath := n.Path\n\t\tif filepath.Ext(targetPath) == \"\" {\n\t\t\ttargetPath += \".md\"\n\t\t}\n\t\tfullPath := filepath.Join(wsPath, targetPath)\n\n\t\tif n.Action == domain.NoteActionDelete {\n\t\t\tif _, err := os.Stat(fullPath); err == nil {\n\t\t\t\t_ = os.Remove(fullPath)\n\t\t\t\tactuallyChanged = true\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t_ = os.MkdirAll(filepath.Dir(fullPath), 0755)\n\n\t\t// Check if content is different before writing\n\t\tif oldFile, err := os.Open(fullPath); err == nil {\n\t\t\tdefer oldFile.Close()\n\t\t\t// For notes, we still compare strings as they are typically small\n\t\t\t// and this maintains simple logic for .md files.\n\t\t\tif oldContent, err := io.ReadAll(oldFile); err == nil {\n\t\t\t\tif string(oldContent) == n.Content {\n\t\t\t\t\tcontinue // Skip writing if content is identical\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif err := os.WriteFile(fullPath, []byte(n.Content), 0644); err != nil {\n\t\t\treturn false, fmt.Errorf(\"failed to write note to workspace: %w\", err)\n\t\t} else {\n\t\t\tactuallyChanged = true\n\t\t\tif n.Mtime > 0 {\n\t\t\t\tmt := time.UnixMilli(n.Mtime)\n\t\t\t\t_ = os.Chtimes(fullPath, mt, mt)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn actuallyChanged, nil\n}\n\nfunc (s *gitSyncService) processFilesBatch(files []*domain.File, conf *domain.GitSyncConfig, wsPath string) (bool, error) {\n\tvar actuallyChanged bool\n\n\tfor _, f := range files {\n\t\tfullPath := filepath.Join(wsPath, f.Path)\n\n\t\tif f.Action == domain.FileActionDelete {\n\t\t\tif _, err := os.Stat(fullPath); err == nil {\n\t\t\t\t_ = os.Remove(fullPath)\n\t\t\t\tactuallyChanged = true\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t_ = os.MkdirAll(filepath.Dir(fullPath), 0755)\n\n\t\t// Add physical file existence check to prevent source not existing from causing copyFileIfDifferent error interruption\n\t\t// 增加物理文件存在性检查，防止 src 不存在导致 copyFileIfDifferent 报错中断\n\t\tif _, err := os.Stat(f.SavePath); os.IsNotExist(err) {\n\t\t\ts.logger.Warn(\"Attachment file not found in storage, skipping mirror for this file\",\n\t\t\t\tzap.Int64(\"uid\", conf.UID),\n\t\t\t\tzap.Int64(\"vaultId\", conf.VaultID),\n\t\t\t\tzap.String(\"path\", f.Path),\n\t\t\t\tzap.String(\"savePath\", f.SavePath))\n\t\t\tcontinue\n\t\t}\n\n\t\tcopyChanged, err := s.copyFileIfDifferent(f.SavePath, fullPath)\n\t\tif err != nil {\n\t\t\treturn false, fmt.Errorf(\"failed to copy attachment to workspace: %w\", err)\n\t\t} else if copyChanged {\n\t\t\tactuallyChanged = true\n\t\t\tif f.Mtime > 0 {\n\t\t\t\tmt := time.UnixMilli(f.Mtime)\n\t\t\t\t_ = os.Chtimes(fullPath, mt, mt)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn actuallyChanged, nil\n}\n\nfunc (s *gitSyncService) copyFileIfDifferent(src, dst string) (bool, error) {\n\tsrcInfo, err := os.Stat(src)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tdstInfo, err := os.Stat(dst)\n\tif err == nil {\n\t\tif srcInfo.Size() == dstInfo.Size() {\n\t\t\t// Sizes match, we could do deep comparison, but for sync service\n\t\t\t// relying on size and potentially mtime/hash in DB is safer and faster.\n\t\t\t// Here we assume if size matches, it's likely same (simplification to avoid full read).\n\t\t\t// If we really need deep compare, we should use streaming hash.\n\t\t\treturn false, nil\n\t\t}\n\t}\n\n\t// Streaming copy\n\tsrcFile, err := os.Open(src)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer srcFile.Close()\n\n\tdstFile, err := os.Create(dst)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tdefer dstFile.Close()\n\n\t_, err = io.Copy(dstFile, srcFile)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\treturn true, nil\n}\n\nfunc (s *gitSyncService) NotifyUpdated(uid int64, vaultID int64) {\n\ts.logger.Debug(\"NotifyUpdated called\", zap.Int64(\"uid\", uid), zap.Int64(\"vaultID\", vaultID))\n\n\tconfigs, err := s.repo.ListByVaultID(context.Background(), vaultID, uid)\n\tif err != nil {\n\t\ts.logger.Error(\"NotifyUpdated: failed to list configs by vaultID\", zap.Int64(\"uid\", uid), zap.Int64(\"vaultID\", vaultID), zap.Error(err))\n\t\treturn\n\t}\n\n\ts.logger.Debug(\"NotifyUpdated: found configs\", zap.Int64(\"uid\", uid), zap.Int64(\"vaultID\", vaultID), zap.Int(\"count\", len(configs)))\n\n\tfor _, conf := range configs {\n\t\tif !conf.IsEnabled || conf.Delay <= 0 {\n\t\t\ts.logger.Debug(\"NotifyUpdated: skipping config\", zap.Int64(\"configId\", conf.ID), zap.Bool(\"isEnabled\", conf.IsEnabled), zap.Int64(\"delay\", conf.Delay))\n\t\t\tcontinue\n\t\t}\n\n\t\ts.mu.Lock()\n\t\tif timer, ok := s.timers[conf.ID]; ok {\n\t\t\ttimer.Stop()\n\t\t\ts.logger.Debug(\"NotifyUpdated: reset existing timer\", zap.Int64(\"configId\", conf.ID))\n\t\t}\n\n\t\tid := conf.ID\n\t\tconfigUid := uid\n\t\ts.logger.Info(\"NotifyUpdated: scheduling delayed sync\", zap.Int64(\"configId\", id), zap.Int64(\"delay\", conf.Delay))\n\t\ts.timers[id] = time.AfterFunc(time.Duration(conf.Delay)*time.Second, func() {\n\t\t\ts.mu.Lock()\n\t\t\tdelete(s.timers, id)\n\t\t\ts.mu.Unlock()\n\n\t\t\tctx := context.Background()\n\t\t\t_ = s.ExecuteSync(ctx, configUid, id)\n\t\t})\n\t\ts.mu.Unlock()\n\t}\n}\n"
  },
  {
    "path": "internal/service/mocks/mock_backup_service.go",
    "content": "// Package mocks provides testify/mock implementations for service interfaces.\n// Package mocks 提供服务接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockBackupService is a testify/mock implementation of service.BackupService.\n// MockBackupService 是 service.BackupService 的 testify/mock 实现。\ntype MockBackupService struct {\n\tmock.Mock\n}\n\n// Ensure MockBackupService implements service.BackupService at compile time.\n// 编译期确保 MockBackupService 实现了 service.BackupService 接口。\nvar _ service.BackupService = (*MockBackupService)(nil)\n\nfunc (m *MockBackupService) GetConfigs(ctx context.Context, uid int64) ([]*dto.BackupConfigDTO, error) {\n\targs := m.Called(ctx, uid)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.BackupConfigDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockBackupService) DeleteConfig(ctx context.Context, uid int64, configID int64) error {\n\targs := m.Called(ctx, uid, configID)\n\treturn args.Error(0)\n}\n\nfunc (m *MockBackupService) UpdateConfig(ctx context.Context, uid int64, req *dto.BackupConfigRequest) (*dto.BackupConfigDTO, error) {\n\targs := m.Called(ctx, uid, req)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.BackupConfigDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockBackupService) ListHistory(ctx context.Context, uid int64, configID int64, pager *app.Pager) ([]*dto.BackupHistoryDTO, int64, error) {\n\targs := m.Called(ctx, uid, configID, pager)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.BackupHistoryDTO), args.Get(1).(int64), args.Error(2)\n\t}\n\treturn nil, args.Get(1).(int64), args.Error(2)\n}\n\nfunc (m *MockBackupService) ExecuteUserBackup(ctx context.Context, uid int64, configID int64) error {\n\targs := m.Called(ctx, uid, configID)\n\treturn args.Error(0)\n}\n\nfunc (m *MockBackupService) ExecuteTaskBackups(ctx context.Context) error {\n\targs := m.Called(ctx)\n\treturn args.Error(0)\n}\n\nfunc (m *MockBackupService) NotifyUpdated(uid int64) {\n\tm.Called(uid)\n}\n\nfunc (m *MockBackupService) Shutdown(ctx context.Context) error {\n\targs := m.Called(ctx)\n\treturn args.Error(0)\n}\n"
  },
  {
    "path": "internal/service/mocks/mock_cloudflare_service.go",
    "content": "// Package mocks provides testify/mock implementations for service interfaces.\n// Package mocks 提供服务接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockCloudflareService is a testify/mock implementation of service.CloudflareService.\n// MockCloudflareService 是 service.CloudflareService 的 testify/mock 实现。\ntype MockCloudflareService struct {\n\tmock.Mock\n}\n\n// Ensure MockCloudflareService implements service.CloudflareService at compile time.\n// 编译期确保 MockCloudflareService 实现了 service.CloudflareService 接口。\nvar _ service.CloudflareService = (*MockCloudflareService)(nil)\n\nfunc (m *MockCloudflareService) Start(ctx context.Context, token string, logEnabled bool) error {\n\targs := m.Called(ctx, token, logEnabled)\n\treturn args.Error(0)\n}\n\nfunc (m *MockCloudflareService) Stop(ctx context.Context) error {\n\targs := m.Called(ctx)\n\treturn args.Error(0)\n}\n\nfunc (m *MockCloudflareService) TunnelURL() string {\n\targs := m.Called()\n\treturn args.String(0)\n}\n\nfunc (m *MockCloudflareService) DownloadBinary() (string, error) {\n\targs := m.Called()\n\treturn args.String(0), args.Error(1)\n}\n"
  },
  {
    "path": "internal/service/mocks/mock_conflict_service.go",
    "content": "// Package mocks provides testify/mock implementations for service interfaces.\n// Package mocks 提供服务接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockConflictService is a testify/mock implementation of service.ConflictService.\n// MockConflictService 是 service.ConflictService 的 testify/mock 实现。\ntype MockConflictService struct {\n\tmock.Mock\n}\n\n// Ensure MockConflictService implements service.ConflictService at compile time.\n// 编译期确保 MockConflictService 实现了 service.ConflictService 接口。\nvar _ service.ConflictService = (*MockConflictService)(nil)\n\nfunc (m *MockConflictService) CreateConflictFile(ctx context.Context, uid int64, params *dto.ConflictFileRequest) (*dto.ConflictFileResponse, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.ConflictFileResponse), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n"
  },
  {
    "path": "internal/service/mocks/mock_file_service.go",
    "content": "// Package mocks provides testify/mock implementations for service interfaces.\n// Package mocks 提供服务接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\t\"io\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockFileService is a testify/mock implementation of service.FileService.\n// MockFileService 是 service.FileService 的 testify/mock 实现。\ntype MockFileService struct {\n\tmock.Mock\n}\n\n// Ensure MockFileService implements service.FileService at compile time.\n// 编译期确保 MockFileService 实现了 service.FileService 接口。\nvar _ service.FileService = (*MockFileService)(nil)\n\nfunc (m *MockFileService) Get(ctx context.Context, uid int64, params *dto.FileGetRequest) (*dto.FileDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.FileDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockFileService) UpdateCheck(ctx context.Context, uid int64, params *dto.FileUpdateCheckRequest) (string, *dto.FileDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(1); v != nil {\n\t\treturn args.String(0), v.(*dto.FileDTO), args.Error(2)\n\t}\n\treturn args.String(0), nil, args.Error(2)\n}\n\nfunc (m *MockFileService) UploadCheck(ctx context.Context, uid int64, params *dto.FileUpdateCheckRequest) (string, *dto.FileDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(1); v != nil {\n\t\treturn args.String(0), v.(*dto.FileDTO), args.Error(2)\n\t}\n\treturn args.String(0), nil, args.Error(2)\n}\n\nfunc (m *MockFileService) UpdateOrCreate(ctx context.Context, uid int64, params *dto.FileUpdateRequest, mtimeCheck bool) (bool, *dto.FileDTO, error) {\n\targs := m.Called(ctx, uid, params, mtimeCheck)\n\tif v := args.Get(1); v != nil {\n\t\treturn args.Bool(0), v.(*dto.FileDTO), args.Error(2)\n\t}\n\treturn args.Bool(0), nil, args.Error(2)\n}\n\nfunc (m *MockFileService) UploadComplete(ctx context.Context, uid int64, params *dto.FileUpdateRequest) (bool, *dto.FileDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(1); v != nil {\n\t\treturn args.Bool(0), v.(*dto.FileDTO), args.Error(2)\n\t}\n\treturn args.Bool(0), nil, args.Error(2)\n}\n\nfunc (m *MockFileService) Delete(ctx context.Context, uid int64, params *dto.FileDeleteRequest) (*dto.FileDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.FileDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockFileService) List(ctx context.Context, uid int64, params *dto.FileListRequest, pager *app.Pager) ([]*dto.FileDTO, int, error) {\n\targs := m.Called(ctx, uid, params, pager)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.FileDTO), args.Int(1), args.Error(2)\n\t}\n\treturn nil, args.Int(1), args.Error(2)\n}\n\nfunc (m *MockFileService) ListByLastTime(ctx context.Context, uid int64, params *dto.FileSyncRequest) ([]*dto.FileDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.FileDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockFileService) CountSizeSum(ctx context.Context, vaultID int64, uid int64) error {\n\targs := m.Called(ctx, vaultID, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockFileService) Cleanup(ctx context.Context, uid int64) error {\n\targs := m.Called(ctx, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockFileService) CleanupByTime(ctx context.Context, cutoffTime int64) error {\n\targs := m.Called(ctx, cutoffTime)\n\treturn args.Error(0)\n}\n\nfunc (m *MockFileService) ResolveEmbedLinks(ctx context.Context, uid int64, vaultName string, notePath string, content string) (map[string]string, error) {\n\targs := m.Called(ctx, uid, vaultName, notePath, content)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(map[string]string), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockFileService) GetContent(ctx context.Context, uid int64, params *dto.FileGetRequest) (io.ReadCloser, string, int64, string, error) {\n\targs := m.Called(ctx, uid, params)\n\tvar rc io.ReadCloser\n\tif v := args.Get(0); v != nil {\n\t\trc = v.(io.ReadCloser)\n\t}\n\treturn rc, args.String(1), args.Get(2).(int64), args.String(3), args.Error(4)\n}\n\nfunc (m *MockFileService) GetContentInfo(ctx context.Context, uid int64, params *dto.FileGetRequest) (string, string, int64, string, string, error) {\n\targs := m.Called(ctx, uid, params)\n\treturn args.String(0), args.String(1), args.Get(2).(int64), args.String(3), args.String(4), args.Error(5)\n}\n\nfunc (m *MockFileService) Restore(ctx context.Context, uid int64, params *dto.FileRestoreRequest) (*dto.FileDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.FileDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockFileService) Rename(ctx context.Context, uid int64, params *dto.FileRenameRequest) (*dto.FileDTO, *dto.FileDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tvar old, newF *dto.FileDTO\n\tif v := args.Get(0); v != nil {\n\t\told = v.(*dto.FileDTO)\n\t}\n\tif v := args.Get(1); v != nil {\n\t\tnewF = v.(*dto.FileDTO)\n\t}\n\treturn old, newF, args.Error(2)\n}\n\nfunc (m *MockFileService) WithClient(clientType, name, version string) service.FileService {\n\targs := m.Called(clientType, name, version)\n\treturn args.Get(0).(service.FileService)\n}\n\nfunc (m *MockFileService) RecycleClear(ctx context.Context, uid int64, params *dto.FileRecycleClearRequest) error {\n\targs := m.Called(ctx, uid, params)\n\treturn args.Error(0)\n}\n\nfunc (m *MockFileService) CleanDuplicateFiles(ctx context.Context, uid int64, vaultID int64) error {\n\targs := m.Called(ctx, uid, vaultID)\n\treturn args.Error(0)\n}\n"
  },
  {
    "path": "internal/service/mocks/mock_folder_service.go",
    "content": "// Package mocks provides testify/mock implementations for service interfaces.\n// Package mocks 提供服务接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockFolderService is a testify/mock implementation of service.FolderService.\n// MockFolderService 是 service.FolderService 的 testify/mock 实现。\ntype MockFolderService struct {\n\tmock.Mock\n}\n\n// Ensure MockFolderService implements service.FolderService at compile time.\n// 编译期确保 MockFolderService 实现了 service.FolderService 接口。\nvar _ service.FolderService = (*MockFolderService)(nil)\n\nfunc (m *MockFolderService) Get(ctx context.Context, uid int64, params *dto.FolderGetRequest) (*dto.FolderDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.FolderDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockFolderService) List(ctx context.Context, uid int64, params *dto.FolderListRequest) ([]*dto.FolderDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.FolderDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockFolderService) ListByUpdatedTimestamp(ctx context.Context, uid int64, vault string, lastTime int64) ([]*dto.FolderDTO, error) {\n\targs := m.Called(ctx, uid, vault, lastTime)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.FolderDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockFolderService) UpdateOrCreate(ctx context.Context, uid int64, params *dto.FolderCreateRequest) (*dto.FolderDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.FolderDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockFolderService) Delete(ctx context.Context, uid int64, params *dto.FolderDeleteRequest) (*dto.FolderDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.FolderDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockFolderService) Rename(ctx context.Context, uid int64, params *dto.FolderRenameRequest) (*dto.FolderDTO, *dto.FolderDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tvar old, newF *dto.FolderDTO\n\tif v := args.Get(0); v != nil {\n\t\told = v.(*dto.FolderDTO)\n\t}\n\tif v := args.Get(1); v != nil {\n\t\tnewF = v.(*dto.FolderDTO)\n\t}\n\treturn old, newF, args.Error(2)\n}\n\nfunc (m *MockFolderService) ListNotes(ctx context.Context, uid int64, params *dto.FolderContentRequest, pager *app.Pager) ([]*dto.NoteNoContentDTO, int, error) {\n\targs := m.Called(ctx, uid, params, pager)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.NoteNoContentDTO), args.Int(1), args.Error(2)\n\t}\n\treturn nil, args.Int(1), args.Error(2)\n}\n\nfunc (m *MockFolderService) ListFiles(ctx context.Context, uid int64, params *dto.FolderContentRequest, pager *app.Pager) ([]*dto.FileDTO, int, error) {\n\targs := m.Called(ctx, uid, params, pager)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.FileDTO), args.Int(1), args.Error(2)\n\t}\n\treturn nil, args.Int(1), args.Error(2)\n}\n\nfunc (m *MockFolderService) EnsurePathFID(ctx context.Context, uid int64, vaultID int64, path string) (int64, error) {\n\targs := m.Called(ctx, uid, vaultID, path)\n\treturn args.Get(0).(int64), args.Error(1)\n}\n\nfunc (m *MockFolderService) SyncResourceFID(ctx context.Context, uid int64, vaultID int64, noteIDs []int64, fileIDs []int64) error {\n\targs := m.Called(ctx, uid, vaultID, noteIDs, fileIDs)\n\treturn args.Error(0)\n}\n\nfunc (m *MockFolderService) GetTree(ctx context.Context, uid int64, params *dto.FolderTreeRequest) (*dto.FolderTreeResponse, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.FolderTreeResponse), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockFolderService) CleanDuplicateFolders(ctx context.Context, uid int64, vaultID int64) error {\n\targs := m.Called(ctx, uid, vaultID)\n\treturn args.Error(0)\n}\n\nfunc (m *MockFolderService) WithClient(clientType, name, version string) service.FolderService {\n\targs := m.Called(clientType, name, version)\n\treturn args.Get(0).(service.FolderService)\n}\n"
  },
  {
    "path": "internal/service/mocks/mock_git_sync_service.go",
    "content": "// Package mocks provides testify/mock implementations for service interfaces.\n// Package mocks 提供服务接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockGitSyncService is a testify/mock implementation of service.GitSyncService.\n// MockGitSyncService 是 service.GitSyncService 的 testify/mock 实现。\ntype MockGitSyncService struct {\n\tmock.Mock\n}\n\n// Ensure MockGitSyncService implements service.GitSyncService at compile time.\n// 编译期确保 MockGitSyncService 实现了 service.GitSyncService 接口。\nvar _ service.GitSyncService = (*MockGitSyncService)(nil)\n\nfunc (m *MockGitSyncService) GetConfigs(ctx context.Context, uid int64) ([]*dto.GitSyncConfigDTO, error) {\n\targs := m.Called(ctx, uid)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.GitSyncConfigDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockGitSyncService) GetConfig(ctx context.Context, uid int64, vaultID int64) (*dto.GitSyncConfigDTO, error) {\n\targs := m.Called(ctx, uid, vaultID)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.GitSyncConfigDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockGitSyncService) UpdateConfig(ctx context.Context, uid int64, params *dto.GitSyncConfigRequest) (*dto.GitSyncConfigDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.GitSyncConfigDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockGitSyncService) DeleteConfig(ctx context.Context, uid int64, id int64) error {\n\targs := m.Called(ctx, uid, id)\n\treturn args.Error(0)\n}\n\nfunc (m *MockGitSyncService) Validate(ctx context.Context, params *dto.GitSyncValidateRequest) error {\n\targs := m.Called(ctx, params)\n\treturn args.Error(0)\n}\n\nfunc (m *MockGitSyncService) ExecuteSync(ctx context.Context, uid int64, id int64) error {\n\targs := m.Called(ctx, uid, id)\n\treturn args.Error(0)\n}\n\nfunc (m *MockGitSyncService) CleanWorkspace(ctx context.Context, uid int64, configID int64) error {\n\targs := m.Called(ctx, uid, configID)\n\treturn args.Error(0)\n}\n\nfunc (m *MockGitSyncService) ListHistory(ctx context.Context, uid int64, configID int64, pager *pkgapp.Pager) ([]*dto.GitSyncHistoryDTO, int64, error) {\n\targs := m.Called(ctx, uid, configID, pager)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.GitSyncHistoryDTO), args.Get(1).(int64), args.Error(2)\n\t}\n\treturn nil, args.Get(1).(int64), args.Error(2)\n}\n\nfunc (m *MockGitSyncService) NotifyUpdated(uid int64, vaultID int64) {\n\tm.Called(uid, vaultID)\n}\n\nfunc (m *MockGitSyncService) Shutdown(ctx context.Context) error {\n\targs := m.Called(ctx)\n\treturn args.Error(0)\n}\n"
  },
  {
    "path": "internal/service/mocks/mock_ngrok_service.go",
    "content": "// Package mocks provides testify/mock implementations for service interfaces.\n// Package mocks 提供服务接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockNgrokService is a testify/mock implementation of service.NgrokService.\n// MockNgrokService 是 service.NgrokService 的 testify/mock 实现。\ntype MockNgrokService struct {\n\tmock.Mock\n}\n\n// Ensure MockNgrokService implements service.NgrokService at compile time.\n// 编译期确保 MockNgrokService 实现了 service.NgrokService 接口。\nvar _ service.NgrokService = (*MockNgrokService)(nil)\n\nfunc (m *MockNgrokService) Start(ctx context.Context, addr string) error {\n\targs := m.Called(ctx, addr)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNgrokService) Stop(ctx context.Context) error {\n\targs := m.Called(ctx)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNgrokService) TunnelURL() string {\n\targs := m.Called()\n\treturn args.String(0)\n}\n"
  },
  {
    "path": "internal/service/mocks/mock_note_history_service.go",
    "content": "// Package mocks provides testify/mock implementations for service interfaces.\n// Package mocks 提供服务接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockNoteHistoryService is a testify/mock implementation of service.NoteHistoryService.\n// MockNoteHistoryService 是 service.NoteHistoryService 的 testify/mock 实现。\ntype MockNoteHistoryService struct {\n\tmock.Mock\n}\n\n// Ensure MockNoteHistoryService implements service.NoteHistoryService at compile time.\n// 编译期确保 MockNoteHistoryService 实现了 service.NoteHistoryService 接口。\nvar _ service.NoteHistoryService = (*MockNoteHistoryService)(nil)\n\nfunc (m *MockNoteHistoryService) Get(ctx context.Context, uid int64, id int64) (*dto.NoteHistoryDTO, error) {\n\targs := m.Called(ctx, uid, id)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.NoteHistoryDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockNoteHistoryService) GetByNoteIDAndHash(ctx context.Context, uid int64, noteID int64, contentHash string) (*dto.NoteHistoryDTO, error) {\n\targs := m.Called(ctx, uid, noteID, contentHash)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.NoteHistoryDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockNoteHistoryService) List(ctx context.Context, uid int64, params *dto.NoteHistoryListRequest, pager *app.Pager) ([]*dto.NoteHistoryNoContentDTO, int64, error) {\n\targs := m.Called(ctx, uid, params, pager)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.NoteHistoryNoContentDTO), args.Get(1).(int64), args.Error(2)\n\t}\n\treturn nil, args.Get(1).(int64), args.Error(2)\n}\n\nfunc (m *MockNoteHistoryService) RestoreFromHistory(ctx context.Context, uid int64, historyID int64) (*dto.NoteDTO, error) {\n\targs := m.Called(ctx, uid, historyID)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.NoteDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockNoteHistoryService) ProcessDelay(ctx context.Context, noteID int64, uid int64) error {\n\targs := m.Called(ctx, noteID, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteHistoryService) Migrate(ctx context.Context, oldNoteID, newNoteID int64, uid int64) error {\n\targs := m.Called(ctx, oldNoteID, newNoteID, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteHistoryService) CleanupByTime(ctx context.Context, cutoffTime int64, keepVersions int) error {\n\targs := m.Called(ctx, cutoffTime, keepVersions)\n\treturn args.Error(0)\n}\n"
  },
  {
    "path": "internal/service/mocks/mock_note_link_service.go",
    "content": "// Package mocks provides testify/mock implementations for service interfaces.\n// Package mocks 提供服务接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockNoteLinkService is a testify/mock implementation of service.NoteLinkService.\n// MockNoteLinkService 是 service.NoteLinkService 的 testify/mock 实现。\ntype MockNoteLinkService struct {\n\tmock.Mock\n}\n\n// Ensure MockNoteLinkService implements service.NoteLinkService at compile time.\n// 编译期确保 MockNoteLinkService 实现了 service.NoteLinkService 接口。\nvar _ service.NoteLinkService = (*MockNoteLinkService)(nil)\n\nfunc (m *MockNoteLinkService) GetBacklinks(ctx context.Context, uid int64, params *dto.NoteLinkQueryRequest) ([]*dto.NoteLinkItem, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.NoteLinkItem), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockNoteLinkService) GetOutlinks(ctx context.Context, uid int64, params *dto.NoteLinkQueryRequest) ([]*dto.NoteLinkItem, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.NoteLinkItem), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n"
  },
  {
    "path": "internal/service/mocks/mock_note_service.go",
    "content": "// Package mocks provides testify/mock implementations for service interfaces.\n// Package mocks 提供服务接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockNoteService is a testify/mock implementation of service.NoteService.\n// MockNoteService 是 service.NoteService 的 testify/mock 实现。\ntype MockNoteService struct {\n\tmock.Mock\n}\n\n// Ensure MockNoteService implements service.NoteService at compile time.\n// 编译期确保 MockNoteService 实现了 service.NoteService 接口。\nvar _ service.NoteService = (*MockNoteService)(nil)\n\nfunc (m *MockNoteService) Get(ctx context.Context, uid int64, params *dto.NoteGetRequest) (*dto.NoteDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.NoteDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockNoteService) UpdateCheck(ctx context.Context, uid int64, params *dto.NoteUpdateCheckRequest) (string, *dto.NoteDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(1); v != nil {\n\t\treturn args.String(0), v.(*dto.NoteDTO), args.Error(2)\n\t}\n\treturn args.String(0), nil, args.Error(2)\n}\n\nfunc (m *MockNoteService) ModifyOrCreate(ctx context.Context, uid int64, params *dto.NoteModifyOrCreateRequest, mtimeCheck bool) (bool, *dto.NoteDTO, error) {\n\targs := m.Called(ctx, uid, params, mtimeCheck)\n\tif v := args.Get(1); v != nil {\n\t\treturn args.Bool(0), v.(*dto.NoteDTO), args.Error(2)\n\t}\n\treturn args.Bool(0), nil, args.Error(2)\n}\n\nfunc (m *MockNoteService) Delete(ctx context.Context, uid int64, params *dto.NoteDeleteRequest) (*dto.NoteDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.NoteDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockNoteService) Restore(ctx context.Context, uid int64, params *dto.NoteRestoreRequest) (*dto.NoteDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.NoteDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockNoteService) Rename(ctx context.Context, uid int64, params *dto.NoteRenameRequest) (*dto.NoteDTO, *dto.NoteDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tvar old, newN *dto.NoteDTO\n\tif v := args.Get(0); v != nil {\n\t\told = v.(*dto.NoteDTO)\n\t}\n\tif v := args.Get(1); v != nil {\n\t\tnewN = v.(*dto.NoteDTO)\n\t}\n\treturn old, newN, args.Error(2)\n}\n\nfunc (m *MockNoteService) List(ctx context.Context, uid int64, params *dto.NoteListRequest, pager *app.Pager) ([]*dto.NoteNoContentDTO, int, error) {\n\targs := m.Called(ctx, uid, params, pager)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.NoteNoContentDTO), args.Int(1), args.Error(2)\n\t}\n\treturn nil, args.Int(1), args.Error(2)\n}\n\nfunc (m *MockNoteService) ListByLastTime(ctx context.Context, uid int64, params *dto.NoteSyncRequest) ([]*dto.NoteDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.NoteDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockNoteService) Sync(ctx context.Context, uid int64, params *dto.NoteSyncRequest) ([]*dto.NoteDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.NoteDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockNoteService) CountSizeSum(ctx context.Context, vaultID int64, uid int64) error {\n\targs := m.Called(ctx, vaultID, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteService) Cleanup(ctx context.Context, uid int64) error {\n\targs := m.Called(ctx, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteService) CleanupByTime(ctx context.Context, cutoffTime int64) error {\n\targs := m.Called(ctx, cutoffTime)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteService) ListNeedSnapshot(ctx context.Context, uid int64) ([]*dto.NoteDTO, error) {\n\targs := m.Called(ctx, uid)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.NoteDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockNoteService) Migrate(ctx context.Context, oldNoteID, newNoteID int64, uid int64) error {\n\targs := m.Called(ctx, oldNoteID, newNoteID, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteService) MigratePush(oldNoteID, newNoteID int64, uid int64) {\n\tm.Called(oldNoteID, newNoteID, uid)\n}\n\nfunc (m *MockNoteService) WithClient(clientType, name, version string) service.NoteService {\n\targs := m.Called(clientType, name, version)\n\treturn args.Get(0).(service.NoteService)\n}\n\nfunc (m *MockNoteService) PatchFrontmatter(ctx context.Context, uid int64, params *dto.NotePatchFrontmatterRequest) (*dto.NoteDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.NoteDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockNoteService) AppendContent(ctx context.Context, uid int64, params *dto.NoteAppendRequest) (*dto.NoteDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.NoteDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockNoteService) PrependContent(ctx context.Context, uid int64, params *dto.NotePrependRequest) (*dto.NoteDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.NoteDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockNoteService) ReplaceContent(ctx context.Context, uid int64, params *dto.NoteReplaceRequest) (*dto.NoteReplaceResponse, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.NoteReplaceResponse), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\n\nfunc (m *MockNoteService) UpdateNoteLinks(ctx context.Context, noteID int64, content string, vaultID, uid int64) {\n\tm.Called(ctx, noteID, content, vaultID, uid)\n}\n\nfunc (m *MockNoteService) RecycleClear(ctx context.Context, uid int64, params *dto.NoteRecycleClearRequest) error {\n\targs := m.Called(ctx, uid, params)\n\treturn args.Error(0)\n}\n\nfunc (m *MockNoteService) CleanDuplicateNotes(ctx context.Context, uid int64, vaultID int64) error {\n\targs := m.Called(ctx, uid, vaultID)\n\treturn args.Error(0)\n}\n"
  },
  {
    "path": "internal/service/mocks/mock_setting_service.go",
    "content": "// Package mocks provides testify/mock implementations for service interfaces.\n// Package mocks 提供服务接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockSettingService is a testify/mock implementation of service.SettingService.\n// MockSettingService 是 service.SettingService 的 testify/mock 实现。\ntype MockSettingService struct {\n\tmock.Mock\n}\n\n// Ensure MockSettingService implements service.SettingService at compile time.\n// 编译期确保 MockSettingService 实现了 service.SettingService 接口。\nvar _ service.SettingService = (*MockSettingService)(nil)\n\nfunc (m *MockSettingService) UpdateCheck(ctx context.Context, uid int64, params *dto.SettingUpdateCheckRequest) (string, *dto.SettingDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(1); v != nil {\n\t\treturn args.String(0), v.(*dto.SettingDTO), args.Error(2)\n\t}\n\treturn args.String(0), nil, args.Error(2)\n}\n\nfunc (m *MockSettingService) ModifyCheck(ctx context.Context, uid int64, params *dto.SettingUpdateCheckRequest) (string, *dto.SettingDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(1); v != nil {\n\t\treturn args.String(0), v.(*dto.SettingDTO), args.Error(2)\n\t}\n\treturn args.String(0), nil, args.Error(2)\n}\n\nfunc (m *MockSettingService) ModifyOrCreate(ctx context.Context, uid int64, params *dto.SettingModifyOrCreateRequest, mtimeCheck bool) (bool, *dto.SettingDTO, error) {\n\targs := m.Called(ctx, uid, params, mtimeCheck)\n\tif v := args.Get(1); v != nil {\n\t\treturn args.Bool(0), v.(*dto.SettingDTO), args.Error(2)\n\t}\n\treturn args.Bool(0), nil, args.Error(2)\n}\n\nfunc (m *MockSettingService) Modify(ctx context.Context, uid int64, params *dto.SettingModifyOrCreateRequest) (bool, *dto.SettingDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(1); v != nil {\n\t\treturn args.Bool(0), v.(*dto.SettingDTO), args.Error(2)\n\t}\n\treturn args.Bool(0), nil, args.Error(2)\n}\n\nfunc (m *MockSettingService) Delete(ctx context.Context, uid int64, params *dto.SettingDeleteRequest) (*dto.SettingDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.SettingDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockSettingService) Get(ctx context.Context, uid int64, params *dto.SettingGetRequest) (*dto.SettingDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.SettingDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockSettingService) ListByLastTime(ctx context.Context, uid int64, params *dto.SettingSyncRequest) ([]*dto.SettingDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.SettingDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockSettingService) CleanDuplicateSettings(ctx context.Context, uid int64, vaultID int64) error {\n\targs := m.Called(ctx, uid, vaultID)\n\treturn args.Error(0)\n}\n\nfunc (m *MockSettingService) Sync(ctx context.Context, uid int64, params *dto.SettingSyncRequest) ([]*dto.SettingDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.SettingDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockSettingService) List(ctx context.Context, uid int64, params *dto.SettingListRequest, pager *pkgapp.Pager) ([]*dto.SettingDTO, int64, error) {\n\targs := m.Called(ctx, uid, params, pager)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.SettingDTO), args.Get(1).(int64), args.Error(2)\n\t}\n\treturn nil, args.Get(1).(int64), args.Error(2)\n}\n\nfunc (m *MockSettingService) Rename(ctx context.Context, uid int64, params *dto.SettingRenameRequest) (*dto.SettingDTO, error) {\n\targs := m.Called(ctx, uid, params)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.SettingDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockSettingService) Cleanup(ctx context.Context, uid int64) error {\n\targs := m.Called(ctx, uid)\n\treturn args.Error(0)\n}\n\nfunc (m *MockSettingService) CleanupByTime(ctx context.Context, cutoffTime int64) error {\n\targs := m.Called(ctx, cutoffTime)\n\treturn args.Error(0)\n}\n\nfunc (m *MockSettingService) ClearByVault(ctx context.Context, uid int64, vaultName string) error {\n\targs := m.Called(ctx, uid, vaultName)\n\treturn args.Error(0)\n}\n\nfunc (m *MockSettingService) WithClient(clientType, name, version string) service.SettingService {\n\targs := m.Called(clientType, name, version)\n\treturn args.Get(0).(service.SettingService)\n}\n"
  },
  {
    "path": "internal/service/mocks/mock_share_service.go",
    "content": "// Package mocks provides testify/mock implementations for service interfaces.\n// Package mocks 提供服务接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockShareService is a testify/mock implementation of service.ShareService.\n// MockShareService 是 service.ShareService 的 testify/mock 实现。\ntype MockShareService struct {\n\tmock.Mock\n}\n\n// Ensure MockShareService implements service.ShareService at compile time.\n// 编译期确保 MockShareService 实现了 service.ShareService 接口。\nvar _ service.ShareService = (*MockShareService)(nil)\n\nfunc (m *MockShareService) ShareGenerate(ctx context.Context, uid int64, vaultName string, path string, pathHash string, password string) (*dto.ShareCreateResponse, error) {\n\targs := m.Called(ctx, uid, vaultName, path, pathHash, password)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.ShareCreateResponse), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockShareService) VerifyShare(ctx context.Context, token string, rid string, rtp string, password string) (*pkgapp.ShareEntity, error) {\n\targs := m.Called(ctx, token, rid, rtp, password)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*pkgapp.ShareEntity), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockShareService) GetSharedNote(ctx context.Context, shareToken string, noteID int64, password string) (*dto.NoteDTO, error) {\n\targs := m.Called(ctx, shareToken, noteID, password)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.NoteDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockShareService) GetSharedFile(ctx context.Context, shareToken string, fileID int64, password string) ([]byte, string, int64, string, string, error) {\n\targs := m.Called(ctx, shareToken, fileID, password)\n\tvar content []byte\n\tif v := args.Get(0); v != nil {\n\t\tcontent = v.([]byte)\n\t}\n\treturn content, args.String(1), args.Get(2).(int64), args.String(3), args.String(4), args.Error(5)\n}\n\nfunc (m *MockShareService) GetSharedFileInfo(ctx context.Context, shareToken string, fileID int64, password string) (string, string, int64, string, string, error) {\n\targs := m.Called(ctx, shareToken, fileID, password)\n\treturn args.String(0), args.String(1), args.Get(2).(int64), args.String(3), args.String(4), args.Error(5)\n}\n\nfunc (m *MockShareService) RecordView(uid int64, id int64) {\n\tm.Called(uid, id)\n}\n\nfunc (m *MockShareService) StopShare(ctx context.Context, uid int64, id int64) error {\n\targs := m.Called(ctx, uid, id)\n\treturn args.Error(0)\n}\n\nfunc (m *MockShareService) UpdateSharePassword(ctx context.Context, uid int64, vaultName string, path string, pathHash string, password string) error {\n\targs := m.Called(ctx, uid, vaultName, path, pathHash, password)\n\treturn args.Error(0)\n}\n\nfunc (m *MockShareService) CreateShortLink(ctx context.Context, uid int64, vaultName string, path string, pathHash string, baseURL string, longURL string, isForce bool) (string, error) {\n\targs := m.Called(ctx, uid, vaultName, path, pathHash, baseURL, longURL, isForce)\n\treturn args.String(0), args.Error(1)\n}\n\nfunc (m *MockShareService) ListShares(ctx context.Context, uid int64, sortBy string, sortOrder string, pager *pkgapp.Pager) ([]*dto.ShareListItem, int, error) {\n\targs := m.Called(ctx, uid, sortBy, sortOrder, pager)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.ShareListItem), args.Int(1), args.Error(2)\n\t}\n\treturn nil, args.Int(1), args.Error(2)\n}\n\nfunc (m *MockShareService) GetShareByPath(ctx context.Context, uid int64, vaultName string, pathHash string) (*domain.UserShare, error) {\n\targs := m.Called(ctx, uid, vaultName, pathHash)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*domain.UserShare), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockShareService) StopShareByPath(ctx context.Context, uid int64, vaultName string, pathHash string) error {\n\targs := m.Called(ctx, uid, vaultName, pathHash)\n\treturn args.Error(0)\n}\n\nfunc (m *MockShareService) GetActiveNotePathsByVault(ctx context.Context, uid int64, vaultName string) ([]string, error) {\n\targs := m.Called(ctx, uid, vaultName)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]string), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockShareService) Shutdown(ctx context.Context) error {\n\targs := m.Called(ctx)\n\treturn args.Error(0)\n}\n"
  },
  {
    "path": "internal/service/mocks/mock_storage_service.go",
    "content": "// Package mocks provides testify/mock implementations for service interfaces.\n// Package mocks 提供服务接口的 testify/mock 实现。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockStorageService is a testify/mock implementation of service.StorageService.\n// MockStorageService 是 service.StorageService 的 testify/mock 实现。\ntype MockStorageService struct {\n\tmock.Mock\n}\n\n// Ensure MockStorageService implements service.StorageService at compile time.\n// 编译期确保 MockStorageService 实现了 service.StorageService 接口。\nvar _ service.StorageService = (*MockStorageService)(nil)\n\nfunc (m *MockStorageService) CreateOrUpdate(ctx context.Context, uid int64, id int64, storageDTO *dto.StoragePostRequest) (*dto.StorageDTO, error) {\n\targs := m.Called(ctx, uid, id, storageDTO)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.StorageDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockStorageService) Get(ctx context.Context, uid int64, id int64) (*dto.StorageDTO, error) {\n\targs := m.Called(ctx, uid, id)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.(*dto.StorageDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockStorageService) List(ctx context.Context, uid int64) ([]*dto.StorageDTO, error) {\n\targs := m.Called(ctx, uid)\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]*dto.StorageDTO), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockStorageService) Delete(ctx context.Context, uid int64, id int64) error {\n\targs := m.Called(ctx, uid, id)\n\treturn args.Error(0)\n}\n\nfunc (m *MockStorageService) GetEnabledTypes() ([]string, error) {\n\targs := m.Called()\n\tif v := args.Get(0); v != nil {\n\t\treturn v.([]string), args.Error(1)\n\t}\n\treturn nil, args.Error(1)\n}\n\nfunc (m *MockStorageService) Validate(ctx context.Context, req *dto.StoragePostRequest) error {\n\targs := m.Called(ctx, req)\n\treturn args.Error(0)\n}\n"
  },
  {
    "path": "internal/service/mocks/mock_user_service.go",
    "content": "// Package mocks provides testify/mock implementations for service layer interfaces.\n// Package mocks 提供 service 层接口的 testify/mock 实现，供路由层测试使用。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockUserService is a testify mock for service.UserService.\n// MockUserService 是 service.UserService 的 testify mock 实现。\ntype MockUserService struct {\n\tmock.Mock\n}\n\n// Register handles user registration.\n// Register 处理用户注册。\nfunc (m *MockUserService) Register(ctx context.Context, params *dto.UserCreateRequest) (*dto.UserDTO, error) {\n\targs := m.Called(ctx, params)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*dto.UserDTO), args.Error(1)\n}\n\n// Login handles user login.\n// Login 处理用户登录。\nfunc (m *MockUserService) Login(ctx context.Context, params *dto.UserLoginRequest, clientIP string) (*dto.UserDTO, error) {\n\targs := m.Called(ctx, params, clientIP)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*dto.UserDTO), args.Error(1)\n}\n\n// ChangePassword changes user password.\n// ChangePassword 修改用户密码。\nfunc (m *MockUserService) ChangePassword(ctx context.Context, uid int64, params *dto.UserChangePasswordRequest) error {\n\targs := m.Called(ctx, uid, params)\n\treturn args.Error(0)\n}\n\n// GetInfo retrieves user information.\n// GetInfo 获取用户信息。\nfunc (m *MockUserService) GetInfo(ctx context.Context, uid int64) (*dto.UserDTO, error) {\n\targs := m.Called(ctx, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*dto.UserDTO), args.Error(1)\n}\n\n// GetAllUIDs retrieves all user UIDs.\n// GetAllUIDs 获取所有用户的 UID 列表。\nfunc (m *MockUserService) GetAllUIDs(ctx context.Context) ([]int64, error) {\n\targs := m.Called(ctx)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]int64), args.Error(1)\n}\n\n// Compile-time check: MockUserService must implement service.UserService.\n// 编译时检查：MockUserService 必须实现 service.UserService 接口。\nvar _ service.UserService = (*MockUserService)(nil)\n"
  },
  {
    "path": "internal/service/mocks/mock_vault_service.go",
    "content": "// Package mocks provides testify/mock implementations for service layer interfaces.\n// Package mocks 提供 service 层接口的 testify/mock 实现，供路由层测试使用。\npackage mocks\n\nimport (\n\t\"context\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\t\"github.com/stretchr/testify/mock\"\n)\n\n// MockVaultService is a testify mock for service.VaultService.\n// MockVaultService 是 service.VaultService 的 testify mock 实现。\ntype MockVaultService struct {\n\tmock.Mock\n}\n\n// GetByName retrieves vault by name.\n// GetByName 根据名称获取 Vault。\nfunc (m *MockVaultService) GetByName(ctx context.Context, uid int64, name string) (*domain.Vault, error) {\n\targs := m.Called(ctx, uid, name)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Vault), args.Error(1)\n}\n\n// GetOrCreate retrieves or creates vault.\n// GetOrCreate 获取或创建 Vault。\nfunc (m *MockVaultService) GetOrCreate(ctx context.Context, uid int64, name string) (*domain.Vault, error) {\n\targs := m.Called(ctx, uid, name)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*domain.Vault), args.Error(1)\n}\n\n// MustGetID retrieves vault ID, returns error if not found.\n// MustGetID 获取 Vault ID，不存在则返回错误。\nfunc (m *MockVaultService) MustGetID(ctx context.Context, uid int64, name string) (int64, error) {\n\targs := m.Called(ctx, uid, name)\n\treturn args.Get(0).(int64), args.Error(1)\n}\n\n// Create creates a new vault.\n// Create 创建新 Vault。\nfunc (m *MockVaultService) Create(ctx context.Context, uid int64, name string) (*dto.VaultDTO, error) {\n\targs := m.Called(ctx, uid, name)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*dto.VaultDTO), args.Error(1)\n}\n\n// Update updates an existing vault.\n// Update 更新现有 Vault。\nfunc (m *MockVaultService) Update(ctx context.Context, uid int64, id int64, name string) (*dto.VaultDTO, error) {\n\targs := m.Called(ctx, uid, id, name)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*dto.VaultDTO), args.Error(1)\n}\n\n// Get retrieves vault by ID.\n// Get 根据 ID 获取 Vault。\nfunc (m *MockVaultService) Get(ctx context.Context, uid int64, id int64) (*dto.VaultDTO, error) {\n\targs := m.Called(ctx, uid, id)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).(*dto.VaultDTO), args.Error(1)\n}\n\n// List retrieves all vaults for a user.\n// List 获取用户的 Vault 列表。\nfunc (m *MockVaultService) List(ctx context.Context, uid int64) ([]*dto.VaultDTO, error) {\n\targs := m.Called(ctx, uid)\n\tif args.Get(0) == nil {\n\t\treturn nil, args.Error(1)\n\t}\n\treturn args.Get(0).([]*dto.VaultDTO), args.Error(1)\n}\n\n// Delete deletes a vault.\n// Delete 删除 Vault。\nfunc (m *MockVaultService) Delete(ctx context.Context, uid int64, id int64) error {\n\targs := m.Called(ctx, uid, id)\n\treturn args.Error(0)\n}\n\n// UpdateNoteStats updates note statistics for a vault.\n// UpdateNoteStats 更新 Vault 的笔记统计信息。\nfunc (m *MockVaultService) UpdateNoteStats(ctx context.Context, noteSize, noteCount, vaultID, uid int64) error {\n\targs := m.Called(ctx, noteSize, noteCount, vaultID, uid)\n\treturn args.Error(0)\n}\n\n// UpdateFileStats updates file statistics for a vault.\n// UpdateFileStats 更新 Vault 的文件统计信息。\nfunc (m *MockVaultService) UpdateFileStats(ctx context.Context, fileSize, fileCount, vaultID, uid int64) error {\n\targs := m.Called(ctx, fileSize, fileCount, vaultID, uid)\n\treturn args.Error(0)\n}\n\n// Compile-time check: MockVaultService must implement service.VaultService.\n// 编译时检查：MockVaultService 必须实现 service.VaultService 接口。\nvar _ service.VaultService = (*MockVaultService)(nil)\n"
  },
  {
    "path": "internal/service/ngrok_service.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/url\"\n\n\t\"go.uber.org/zap\"\n\t\"golang.ngrok.com/ngrok/v2\"\n)\n\n// NgrokService provides ngrok tunnel service\n// NgrokService 提供 ngrok 隧道服务\ntype NgrokService interface {\n\tStart(ctx context.Context, addr string) error\n\tStop(ctx context.Context) error\n\tTunnelURL() string\n}\n\ntype ngrokService struct {\n\tlogger    *zap.Logger\n\tauthToken string\n\tdomain    string\n\tlistener  net.Listener\n\turl       string\n\tagent     ngrok.Agent\n}\n\n// NewNgrokService creates a new ngrok service\n// NewNgrokService 创建一个新的 ngrok 服务\nfunc NewNgrokService(logger *zap.Logger, authToken, domain string) NgrokService {\n\treturn &ngrokService{\n\t\tlogger:    logger,\n\t\tauthToken: authToken,\n\t\tdomain:    domain,\n\t}\n}\n\n// Start starts the ngrok tunnel\n// Start 启动 ngrok 隧道\nfunc (s *ngrokService) Start(ctx context.Context, addr string) error {\n\tif s.authToken == \"\" {\n\t\treturn fmt.Errorf(\"ngrok auth token is required\")\n\t}\n\n\t// 1. Create an agent instance to hold AgentOption\n\t// 1. 创建代理实例来持有 AgentOption\n\tagent, err := ngrok.NewAgent(ngrok.WithAuthtoken(s.authToken))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create ngrok v2 agent: %w\", err)\n\t}\n\ts.agent = agent\n\n\t// 2. Configure endpoint options\n\t// 2. 配置端点选项\n\tvar endpointOpts []ngrok.EndpointOption\n\tif s.domain != \"\" {\n\t\tendpointOpts = append(endpointOpts, ngrok.WithURL(\"https://\"+s.domain))\n\t}\n\n\t// 3. Listen creates the endpoint\n\t// 3. Listen 创建端点\n\tln, err := agent.Listen(ctx, endpointOpts...)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to start ngrok v2 tunnel: %w\", err)\n\t}\n\ts.listener = ln\n\n\tif u, ok := ln.(interface{ URL() *url.URL }); ok {\n\t\ts.url = u.URL().String()\n\t} else {\n\t\ts.url = ln.Addr().String()\n\t}\n\n\ts.logger.Info(\"ngrok v2 tunnel established\", zap.String(\"url\", s.url))\n\n\t// Start forwarding\n\t// 开始转发\n\tgo func() {\n\t\tfor {\n\t\t\tconn, err := ln.Accept()\n\t\t\tif err != nil {\n\t\t\t\ts.logger.Debug(\"ngrok tunnel accept error (likely closed)\", zap.Error(err))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tgo s.handleConn(conn, addr)\n\t\t}\n\t}()\n\n\treturn nil\n}\n\nfunc (s *ngrokService) handleConn(conn net.Conn, addr string) {\n\tdefer conn.Close()\n\tlocalConn, err := net.Dial(\"tcp\", addr)\n\tif err != nil {\n\t\ts.logger.Error(\"failed to dial local address\", zap.String(\"addr\", addr), zap.Error(err))\n\t\treturn\n\t}\n\tdefer localConn.Close()\n\n\tdone := make(chan struct{}, 2)\n\tgo func() {\n\t\t_, _ = io.Copy(localConn, conn)\n\t\tdone <- struct{}{}\n\t}()\n\tgo func() {\n\t\t_, _ = io.Copy(conn, localConn)\n\t\tdone <- struct{}{}\n\t}()\n\t<-done\n}\n\n// Stop stops the ngrok tunnel\n// Stop 停止 ngrok 隧道\nfunc (s *ngrokService) Stop(ctx context.Context) error {\n\tif s.listener != nil {\n\t\tif err := s.listener.Close(); err != nil {\n\t\t\ts.logger.Warn(\"failed to close ngrok tunnel\", zap.Error(err))\n\t\t}\n\t}\n\tif s.agent != nil {\n\t\tif err := s.agent.Disconnect(); err != nil {\n\t\t\ts.logger.Warn(\"failed to disconnect ngrok agent\", zap.Error(err))\n\t\t}\n\t}\n\treturn nil\n}\n\n// TunnelURL returns the current tunnel URL\n// TunnelURL 返回当前隧道 URL\nfunc (s *ngrokService) TunnelURL() string {\n\treturn s.url\n}\n"
  },
  {
    "path": "internal/service/note_history_service.go",
    "content": "// Package service implements the business logic layer\n// Package service 实现业务逻辑层\npackage service\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode/utf8\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"github.com/sergi/go-diff/diffmatchpatch\"\n\t\"go.uber.org/zap\"\n\t\"golang.org/x/sync/singleflight\"\n\t\"gorm.io/gorm\"\n)\n\n// NoteHistoryService defines the note history business service interface\n// NoteHistoryService 定义笔记历史业务服务接口\ntype NoteHistoryService interface {\n\t// Get retrieves note history details for a specified ID\n\t// Get 获取指定 ID 的笔记历史详情\n\tGet(ctx context.Context, uid int64, id int64) (*dto.NoteHistoryDTO, error)\n\n\t// GetByNoteIDAndHash retrieves history record by note ID and content hash\n\t// GetByNoteIDAndHash 根据笔记 ID 和内容哈希获取历史记录\n\tGetByNoteIDAndHash(ctx context.Context, uid int64, noteID int64, contentHash string) (*dto.NoteHistoryDTO, error)\n\n\t// List retrieves history version list for a specified note\n\t// List 获取指定笔记的历史版本列表\n\tList(ctx context.Context, uid int64, params *dto.NoteHistoryListRequest, pager *app.Pager) ([]*dto.NoteHistoryNoContentDTO, int64, error)\n\n\t// RestoreFromHistory restores note content from a history version\n\t// RestoreFromHistory 从历史版本恢复笔记内容\n\tRestoreFromHistory(ctx context.Context, uid int64, historyID int64) (*dto.NoteDTO, error)\n\n\t// ProcessDelay processes note history with delay (calculates diff and saves patch version)\n\t// ProcessDelay 延时处理笔记历史（计算 diff 并保存补丁版本）\n\tProcessDelay(ctx context.Context, noteID int64, uid int64) error\n\n\t// Migrate handles note history migration\n\t// Migrate 处理笔记历史迁移\n\tMigrate(ctx context.Context, oldNoteID, newNoteID int64, uid int64) error\n\n\t// CleanupByTime cleans up history records by cutoff time, keeping recent N versions per note\n\t// CleanupByTime 按截止时间清理历史记录，保留每个笔记最近 N 个版本\n\tCleanupByTime(ctx context.Context, cutoffTime int64, keepVersions int) error\n}\n\n// noteHistoryService implementation of NoteHistoryService interface\n// noteHistoryService 实现 NoteHistoryService 接口\ntype noteHistoryService struct {\n\thistoryRepo    domain.NoteHistoryRepository // History repository // 历史记录仓库\n\tnoteRepo       domain.NoteRepository        // Note repository // 笔记仓库\n\tuserRepo       domain.UserRepository        // User repository // 用户仓库\n\tvaultService   VaultService                 // Vault service // 仓库服务\n\tfolderService  FolderService                // Folder service // 文件夹服务\n\tnoteService    NoteService                  // Note service // 笔记服务\n\tbackupService  BackupService                // Backup service // 备份服务\n\tgitSyncService GitSyncService               // Git sync service // Git 同步服务\n\tsf             *singleflight.Group          // Singleflight group // 并发请求合并组\n\tlogger         *zap.Logger                  // Logger // 日志对象\n\tconfig         *AppServiceConfig            // Service configuration // 服务配置\n}\n\n// NewNoteHistoryService creates NoteHistoryService instance\n// NewNoteHistoryService 创建 NoteHistoryService 实例\nfunc NewNoteHistoryService(historyRepo domain.NoteHistoryRepository, noteRepo domain.NoteRepository, userRepo domain.UserRepository, vaultSvc VaultService, folderSvc FolderService, noteSvc NoteService, backupSvc BackupService, gitSyncSvc GitSyncService, logger *zap.Logger, config *AppServiceConfig) NoteHistoryService {\n\tif config == nil {\n\t\tconfig = &AppServiceConfig{HistoryKeepVersions: 100}\n\t}\n\treturn &noteHistoryService{\n\t\thistoryRepo:    historyRepo,\n\t\tnoteRepo:       noteRepo,\n\t\tuserRepo:       userRepo,\n\t\tvaultService:   vaultSvc,\n\t\tfolderService:  folderSvc,\n\t\tnoteService:    noteSvc,\n\t\tbackupService:  backupSvc,\n\t\tgitSyncService: gitSyncSvc,\n\t\tsf:             &singleflight.Group{},\n\t\tlogger:         logger,\n\t\tconfig:         config,\n\t}\n}\n\n// domainToDTO converts domain model to DTO (includes diff calculation)\n// domainToDTO 将领域模型转换为 DTO（包含 diff 计算）\nfunc (s *noteHistoryService) domainToDTO(history *domain.NoteHistory) *dto.NoteHistoryDTO {\n\tif history == nil {\n\t\treturn nil\n\t}\n\n\tdmp := diffmatchpatch.New()\n\n\t// Add recover protection as a second line of defense\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\ts.logger.Error(\"Panic recovered in domainToDTO\",\n\t\t\t\tzap.Any(\"panic\", r),\n\t\t\t\tzap.Int64(\"historyID\", history.ID))\n\t\t}\n\t}()\n\n\tcontent := s.ensureValidUTF8(history.Content)\n\tdiffPatch := s.ensureValidUTF8(history.DiffPatch)\n\n\tparsedPatches, _ := dmp.PatchFromText(diffPatch)\n\trestoredNewVersion, _ := dmp.PatchApply(parsedPatches, content)\n\tdiffResults := dmp.DiffMain(content, restoredNewVersion, false)\n\n\treturn &dto.NoteHistoryDTO{\n\t\tID:          history.ID,\n\t\tNoteID:      history.NoteID,\n\t\tVaultID:     history.VaultID,\n\t\tPath:        history.Path,\n\t\tDiffs:       diffResults,\n\t\tContent:     history.Content,\n\t\tContentHash: history.ContentHash,\n\t\tClientName:  history.ClientName,\n\t\tClientType:  history.ClientType,\n\t\tClientVersion: history.ClientVersion,\n\t\tVersion:     history.Version,\n\t\tCreatedAt:   timex.Time(history.CreatedAt),\n\t}\n}\n\n// domainToNoContentDTO converts domain model to DTO without content\n// domainToNoContentDTO 将领域模型转换为不含内容的 DTO\nfunc (s *noteHistoryService) domainToNoContentDTO(history *domain.NoteHistory) *dto.NoteHistoryNoContentDTO {\n\tif history == nil {\n\t\treturn nil\n\t}\n\treturn &dto.NoteHistoryNoContentDTO{\n\t\tID:         history.ID,\n\t\tNoteID:     history.NoteID,\n\t\tVaultID:    history.VaultID,\n\t\tPath:       history.Path,\n\t\tClientName: history.ClientName,\n\t\tClientType: history.ClientType,\n\t\tClientVersion: history.ClientVersion,\n\t\tVersion:    history.Version,\n\t\tCreatedAt:  timex.Time(history.CreatedAt),\n\t}\n}\n\n// Get retrieves note history details for a specified ID\n// Get 获取指定 ID 的笔记历史详情\nfunc (s *noteHistoryService) Get(ctx context.Context, uid int64, id int64) (*dto.NoteHistoryDTO, error) {\n\thistory, err := s.historyRepo.GetByID(ctx, id, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorHistoryNotFound\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\treturn s.domainToDTO(history), nil\n}\n\n// GetByNoteIDAndHash retrieves history record by note ID and content hash\n// GetByNoteIDAndHash 根据笔记 ID 和内容哈希获取历史记录\nfunc (s *noteHistoryService) GetByNoteIDAndHash(ctx context.Context, uid int64, noteID int64, contentHash string) (*dto.NoteHistoryDTO, error) {\n\thistory, err := s.historyRepo.GetByNoteIDAndHash(ctx, noteID, contentHash, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, nil // Return nil when record not found, caller will handle // 记录不存在时返回 nil，调用方会处理\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\treturn s.domainToDTO(history), nil\n}\n\n// List retrieves history version list for a specified note\n// List 获取指定笔记的历史版本列表\nfunc (s *noteHistoryService) List(ctx context.Context, uid int64, params *dto.NoteHistoryListRequest, pager *app.Pager) ([]*dto.NoteHistoryNoContentDTO, int64, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tpathHash := params.PathHash\n\tif pathHash == \"\" {\n\t\tpathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// Get note ID\n\t// 获取笔记 ID\n\tnote, err := s.noteRepo.GetByPathHashIncludeRecycle(ctx, pathHash, vaultID, uid, params.IsRecycle)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, 0, code.ErrorNoteNotFound\n\t\t}\n\t\treturn nil, 0, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\tif note == nil {\n\t\treturn nil, 0, code.ErrorNoteNotFound\n\t}\n\n\t// Get history record list\n\t// 获取历史记录列表\n\thistories, count, err := s.historyRepo.ListByNoteID(ctx, note.ID, pager.Page, pager.PageSize, uid)\n\tif err != nil {\n\t\treturn nil, 0, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tvar results []*dto.NoteHistoryNoContentDTO\n\tfor _, h := range histories {\n\t\tresults = append(results, s.domainToNoContentDTO(h))\n\t}\n\treturn results, count, nil\n}\n\n// ProcessDelay processes note history with delay (calculates diff and saves patch version)\n// ProcessDelay 延时处理笔记历史（计算 diff 并保存补丁版本）\nfunc (s *noteHistoryService) ProcessDelay(ctx context.Context, noteID int64, uid int64) error {\n\tnote, err := s.noteRepo.GetByID(ctx, noteID, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn code.ErrorNoteNotFound\n\t\t}\n\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tif note.Content == note.ContentLastSnapshot {\n\t\treturn nil\n\t}\n\n\t// Calculate diff\n\t// 计算 diff\n\tdmp := diffmatchpatch.New()\n\n\t// Add recover protection\n\tvar patchText string\n\tfunc() {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\ts.logger.Error(\"Panic recovered in ProcessDelay during diff calculation\",\n\t\t\t\t\tzap.Any(\"panic\", r),\n\t\t\t\t\tzap.Int64(\"noteID\", noteID))\n\t\t\t}\n\t\t}()\n\n\t\tcontent1 := s.ensureValidUTF8(note.ContentLastSnapshot)\n\t\tcontent2 := s.ensureValidUTF8(note.Content)\n\n\t\tdiffs := dmp.DiffMain(content1, content2, false)\n\t\tpatchText = dmp.PatchToText(dmp.PatchMake(content1, diffs))\n\t}()\n\n\tif patchText == \"\" && note.Content != note.ContentLastSnapshot {\n\t\t// If patch calculation failed (due to panic and recover), we don't proceed with history creation\n\t\t// to avoid saving corrupted data.\n\t\treturn nil\n\t}\n\n\tlatestVersion, err := s.historyRepo.GetLatestVersion(ctx, note.ID, uid)\n\tif err != nil {\n\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\thistory := &domain.NoteHistory{\n\t\tNoteID:      note.ID,\n\t\tVaultID:     note.VaultID,\n\t\tPath:        note.Path,\n\t\tDiffPatch:   patchText,\n\t\tContent:     note.ContentLastSnapshot,\n\t\tContentHash: note.ContentLastSnapshotHash,\n\t\tClientName:  note.ClientName,\n\t\tClientType:  note.ClientType,\n\t\tClientVersion: note.ClientVersion,\n\t\tVersion:     latestVersion + 1,\n\t\tCreatedAt:   note.UpdatedAt,\n\t}\n\n\t_, err = s.historyRepo.Create(ctx, history, uid)\n\tif err != nil {\n\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Update ContentLastSnapshot\n\t// 更新 ContentLastSnapshot\n\tif err := s.noteRepo.UpdateSnapshot(ctx, note.Content, note.ContentHash, latestVersion+1, note.ID, uid); err != nil {\n\t\treturn err\n\t}\n\n\t// Check version count limit, delete oldest version when exceeding limit\n\t// 检查版本数量限制，超过限制时删除最旧的版本\n\treturn s.cleanupExcessVersions(ctx, noteID, uid)\n}\n\n// Migrate handles note history migration\n// Migrate 处理笔记历史迁移\nfunc (s *noteHistoryService) Migrate(ctx context.Context, oldNoteID, newNoteID int64, uid int64) error {\n\treturn s.historyRepo.Migrate(ctx, oldNoteID, newNoteID, uid)\n}\n\n// RestoreFromHistory restores note content from a history version\n// Restores to the content after the modification of this history version\n// RestoreFromHistory 从历史版本恢复笔记内容\n// 恢复到该历史版本修改后的内容\nfunc (s *noteHistoryService) RestoreFromHistory(ctx context.Context, uid int64, historyID int64) (*dto.NoteDTO, error) {\n\t// 1. Get history record\n\t// 1. 获取历史记录\n\thistory, err := s.historyRepo.GetByID(ctx, historyID, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorHistoryNotFound\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// 2. Get current note\n\t// 2. 获取当前笔记\n\tnote, err := s.noteRepo.GetByID(ctx, history.NoteID, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorNoteNotFound\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// 3. Calculate content after this history version modification\n\t// history.Content is the snapshot before this version modification (i.e., content of the previous version)\n\t// history.DiffPatch is the difference patch from before modification to after modification\n\t// Apply patch to get the complete content after this version modification\n\t// 3. 计算该历史版本修改后的内容\n\t// history.Content 是该版本修改前的快照（即上一版本的内容）\n\t// history.DiffPatch 是从修改前到修改后的差异补丁\n\t// 应用补丁得到该版本修改后的完整内容\n\tdmp := diffmatchpatch.New()\n\n\t// Add recover protection\n\tvar restoredContent string\n\tfunc() {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\ts.logger.Error(\"Panic recovered in RestoreFromHistory during patch application\",\n\t\t\t\t\tzap.Any(\"panic\", r),\n\t\t\t\t\tzap.Int64(\"historyID\", historyID))\n\t\t\t}\n\t\t}()\n\n\t\thistoryContent := s.ensureValidUTF8(history.Content)\n\t\tdiffPatch := s.ensureValidUTF8(history.DiffPatch)\n\n\t\tparsedPatches, _ := dmp.PatchFromText(diffPatch)\n\t\trestoredContent, _ = dmp.PatchApply(parsedPatches, historyContent)\n\t}()\n\n\tif restoredContent == \"\" {\n\t\treturn nil, code.ErrorHistoryNotFound.WithDetails(\"failed to restore content from history due to internal error\")\n\t}\n\n\t// 4. Calculate hash of restored content\n\t// 4. 计算恢复内容的哈希\n\trestoredContentHash := util.EncodeHash32(restoredContent)\n\n\t// Debug log\n\t// 调试日志\n\ts.logger.Info(\"RestoreFromHistory\",\n\t\tzap.Int64(\"historyID\", historyID),\n\t\tzap.Int64(\"version\", history.Version),\n\t\tzap.Int(\"beforeContentLen\", len(history.Content)),\n\t\tzap.Int(\"afterContentLen\", len(restoredContent)),\n\t)\n\n\t// 5. Update note with restored content and set modification time\n\t// 5. 使用恢复的内容更新笔记, 并设置修改时间\n\tnote.Content = restoredContent\n\tnote.ContentHash = restoredContentHash\n\tnote.Mtime = timex.Now().UnixMilli()\n\tnote.Action = domain.NoteActionModify\n\tnote.Rename = 0\n\n\t// 6. Update note\n\t// 6. 更新笔记\n\tupdated, err := s.noteRepo.Update(ctx, note, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tvaultID := history.VaultID\n\tgo s.folderService.SyncResourceFID(context.Background(), uid, vaultID, []int64{updated.ID}, nil)\n\tgo s.noteService.CountSizeSum(context.Background(), vaultID, uid)\n\tgo s.noteService.UpdateNoteLinks(context.Background(), updated.ID, updated.Content, vaultID, uid)\n\n\tNoteHistoryDelayPush(updated.ID, uid)\n\n\t// Notify backup and git sync services\n\t// 通知备份和 Git 同步服务\n\tif s.backupService != nil {\n\t\tgo s.backupService.NotifyUpdated(uid)\n\t}\n\tif s.gitSyncService != nil {\n\t\tgo s.gitSyncService.NotifyUpdated(uid, vaultID)\n\t}\n\tif err := s.ProcessDelay(ctx, updated.ID, uid); err != nil {\n\t\ts.logger.Warn(\"RestoreFromHistory: failed to create history\",\n\t\t\tzap.Int64(\"noteID\", updated.ID),\n\t\t\tzap.Error(err))\n\t}\n\n\t// 8. Return updated note DTO\n\t// 8. 返回更新后的笔记 DTO\n\treturn &dto.NoteDTO{\n\t\tID:               updated.ID,\n\t\tAction:           string(updated.Action),\n\t\tPath:             updated.Path,\n\t\tPathHash:         updated.PathHash,\n\t\tContent:          updated.Content,\n\t\tContentHash:      updated.ContentHash,\n\t\tVersion:          updated.Version,\n\t\tCtime:            updated.Ctime,\n\t\tMtime:            updated.Mtime,\n\t\tUpdatedTimestamp: updated.UpdatedTimestamp,\n\t\tUpdatedAt:        timex.Time(updated.UpdatedAt),\n\t\tCreatedAt:        timex.Time(updated.CreatedAt),\n\t}, nil\n}\n\n// CleanupByTime cleans up history records by cutoff time, keeping recent N versions per note\n// CleanupByTime 按截止时间清理历史记录，保留每个笔记最近 N 个版本\nfunc (s *noteHistoryService) CleanupByTime(ctx context.Context, cutoffTime int64, keepVersions int) error {\n\t// Get all user UIDs\n\t// 获取所有用户 UID\n\tuids, err := s.userRepo.GetAllUIDs(ctx)\n\tif err != nil {\n\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tvar totalCleaned int64\n\tfor i, uid := range uids {\n\t\t// Add staggered delay to avoid triggering a large number of write transactions at once\n\t\t// 增加错峰延迟，避免瞬间触发大量写事务\n\t\tif i > 0 {\n\t\t\ttime.Sleep(500 * time.Millisecond)\n\t\t}\n\t\t// Get note IDs with old history records for this user\n\t\t// 获取该用户有旧历史记录的笔记 ID\n\t\tnoteIDs, err := s.historyRepo.GetNoteIDsWithOldHistory(ctx, cutoffTime, uid)\n\t\tif err != nil {\n\t\t\ts.logger.Error(\"failed to get note IDs with old history\",\n\t\t\t\tzap.Int64(\"uid\", uid),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\n\t\tfor _, noteID := range noteIDs {\n\t\t\t// Delete old versions, keep recent N versions\n\t\t\t// 删除旧版本，保留最近 N 个版本\n\t\t\tif err := s.historyRepo.DeleteOldVersions(ctx, noteID, cutoffTime, keepVersions, uid); err != nil {\n\t\t\t\ts.logger.Error(\"failed to cleanup history\",\n\t\t\t\t\tzap.Int64(\"uid\", uid),\n\t\t\t\t\tzap.Int64(\"noteID\", noteID),\n\t\t\t\t\tzap.Error(err))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttotalCleaned++\n\t\t}\n\t}\n\n\ts.logger.Info(\"note history cleanup completed\",\n\t\tzap.Int64(\"cutoffTime\", cutoffTime),\n\t\tzap.Int(\"keepVersions\", keepVersions),\n\t\tzap.Int64(\"notesProcessed\", totalCleaned))\n\n\treturn nil\n}\n\n// cleanupExcessVersions cleans up history records exceeding version count limit\n// Delete oldest version when note history versions exceed HistoryKeepVersions\n// cleanupExcessVersions 清理超过版本数量限制的历史记录\n// 当笔记的历史版本数超过 HistoryKeepVersions 时，删除最旧的版本\nfunc (s *noteHistoryService) cleanupExcessVersions(ctx context.Context, noteID int64, uid int64) error {\n\t// Get version retention count from configuration\n\t// 获取配置中的版本保留数\n\tkeepVersions := 100 // Default value // 默认值\n\tif s.config != nil && s.config.HistoryKeepVersions > 0 {\n\t\tkeepVersions = s.config.HistoryKeepVersions\n\t}\n\n\t// Get all history versions for this note\n\t// 获取该笔记的所有历史版本\n\thistories, _, err := s.historyRepo.ListByNoteID(ctx, noteID, 1, keepVersions+1, uid)\n\tif err != nil {\n\t\ts.logger.Warn(\"failed to list note histories for cleanup\",\n\t\t\tzap.Int64(\"noteID\", noteID),\n\t\t\tzap.Int64(\"uid\", uid),\n\t\t\tzap.Error(err))\n\t\treturn nil // Does not affect main flow // 不影响主流程\n\t}\n\n\t// No cleanup needed if version count does not exceed limit\n\t// 如果版本数未超过限制，无需清理\n\tif len(histories) <= keepVersions {\n\t\treturn nil\n\t}\n\n\t// Delete oldest version exceeding limit\n\t// histories are sorted by Version DESC, so the last one is the oldest\n\t// 删除超出限制的最旧版本\n\t// histories 已按 Version DESC 排序，所以最后一个是最旧的\n\toldestHistory := histories[len(histories)-1]\n\tif err := s.historyRepo.Delete(ctx, oldestHistory.ID, uid); err != nil {\n\t\ts.logger.Warn(\"failed to delete excess history version\",\n\t\t\tzap.Int64(\"noteID\", noteID),\n\t\t\tzap.Int64(\"historyID\", oldestHistory.ID),\n\t\t\tzap.Int64(\"uid\", uid),\n\t\t\tzap.Error(err))\n\t\treturn nil // Does not affect main flow // 不影响主流程\n\t}\n\n\treturn nil\n}\n\n// ensureValidUTF8 ensures the string is valid UTF-8\n// ensureValidUTF8 确保字符串是有效的 UTF-8 编码\nfunc (s *noteHistoryService) ensureValidUTF8(str string) string {\n\tif utf8.ValidString(str) {\n\t\treturn str\n\t}\n\treturn strings.ToValidUTF8(str, \"\")\n}\n\n// Verify noteHistoryService implements NoteHistoryService interface\n// 确保 noteHistoryService 实现了 NoteHistoryService 接口\nvar _ NoteHistoryService = (*noteHistoryService)(nil)\n"
  },
  {
    "path": "internal/service/note_link_service.go",
    "content": "// Package service implements business logic layer\n// Package service 实现业务逻辑层\npackage service\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"strings\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"gorm.io/gorm\"\n)\n\n// NoteLinkService defines the note link service interface\n// NoteLinkService 定义笔记链接服务接口\ntype NoteLinkService interface {\n\t// GetBacklinks gets all notes that link to a target note\n\t// GetBacklinks 获取链接到目标笔记的所有笔记\n\tGetBacklinks(ctx context.Context, uid int64, params *dto.NoteLinkQueryRequest) ([]*dto.NoteLinkItem, error)\n\n\t// GetOutlinks gets all links from a source note\n\t// GetOutlinks 获取源笔记中的所有链接\n\tGetOutlinks(ctx context.Context, uid int64, params *dto.NoteLinkQueryRequest) ([]*dto.NoteLinkItem, error)\n}\n\n// noteLinkService implements NoteLinkService interface\n// noteLinkService 实现 NoteLinkService 接口\ntype noteLinkService struct {\n\tnoteLinkRepo domain.NoteLinkRepository\n\tnoteRepo     domain.NoteRepository\n\tvaultService VaultService\n}\n\n// NewNoteLinkService creates a NoteLinkService instance\n// NewNoteLinkService 创建 NoteLinkService 实例\nfunc NewNoteLinkService(noteLinkRepo domain.NoteLinkRepository, noteRepo domain.NoteRepository, vaultService VaultService) NoteLinkService {\n\treturn &noteLinkService{\n\t\tnoteLinkRepo: noteLinkRepo,\n\t\tnoteRepo:     noteRepo,\n\t\tvaultService: vaultService,\n\t}\n}\n\n// GetBacklinks gets all notes that link to a target note.\n// GetBacklinks 获取链接到目标笔记的所有笔记。\n// Uses path variations to match links stored as partial paths (e.g., [[note]], [[folder/note]]).\n// 使用路径变体来匹配存储为部分路径的链接（例如 [[note]]，[[folder/note]]）。\nfunc (s *noteLinkService) GetBacklinks(ctx context.Context, uid int64, params *dto.NoteLinkQueryRequest) ([]*dto.NoteLinkItem, error) {\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Generate all path variations for matching\n\t// 生成所有用于匹配的路径变体\n\t// e.g., \"projects/folder/note.md\" -> [\"note\", \"folder/note\", \"projects/folder/note\"]\n\t// 例如 \"projects/folder/note.md\" -> [\"note\", \"folder/note\", \"projects/folder/note\"]\n\tpathVariations := util.GeneratePathVariations(params.Path)\n\tif len(pathVariations) == 0 {\n\t\treturn nil, nil\n\t}\n\n\t// Generate hashes for all variations\n\t// 为所有变体生成哈希\n\tvar pathHashes []string\n\tfor _, variation := range pathVariations {\n\t\tpathHashes = append(pathHashes, util.EncodeHash32(variation))\n\t}\n\n\t// Get backlinks matching any of the path variations\n\t// 获取匹配任何路径变体的反向链接\n\tlinks, err := s.noteLinkRepo.GetBacklinksByHashes(ctx, pathHashes, vaultID, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tvar results []*dto.NoteLinkItem\n\tfor _, link := range links {\n\t\t// Get source note to get its path and content for context\n\t\t// 获取源笔记以获取其路径和内容作为上下文\n\t\tsourceNote, err := s.noteRepo.GetByID(ctx, link.SourceNoteID, uid)\n\t\tif err != nil {\n\t\t\tcontinue // Skip if note not found / 如果未找到笔记则跳过\n\t\t}\n\n\t\titem := &dto.NoteLinkItem{\n\t\t\tPath:     sourceNote.Path,\n\t\t\tLinkText: link.LinkText,\n\t\t\tIsEmbed:  link.IsEmbed,\n\t\t}\n\n\t\t// Extract context around the link (try all variations)\n\t\t// 提取链接周围的上下文（尝试所有变体）\n\t\tfor _, variation := range pathVariations {\n\t\t\titem.Context = s.extractLinkContext(sourceNote.Content, variation)\n\t\t\tif item.Context != \"\" {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tresults = append(results, item)\n\t}\n\n\treturn results, nil\n}\n\n// GetOutlinks gets all links from a source note\n// GetOutlinks 获取源笔记中的所有链接\nfunc (s *noteLinkService) GetOutlinks(ctx context.Context, uid int64, params *dto.NoteLinkQueryRequest) ([]*dto.NoteLinkItem, error) {\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// Get note by path to get its ID\n\t// 通过路径获取笔记以获取其 ID\n\tnote, err := s.noteRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorNoteNotFound\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Get outlinks from repository\n\t// 从存储库获取出站链接\n\tlinks, err := s.noteLinkRepo.GetOutlinks(ctx, note.ID, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tvar results []*dto.NoteLinkItem\n\tfor _, link := range links {\n\t\titem := &dto.NoteLinkItem{\n\t\t\tPath:     link.TargetPath,\n\t\t\tLinkText: link.LinkText,\n\t\t\tIsEmbed:  link.IsEmbed,\n\t\t}\n\n\t\t// Extract context around the link\n\t\t// 提取链接周围的上下文\n\t\titem.Context = s.extractLinkContext(note.Content, link.TargetPath)\n\n\t\tresults = append(results, item)\n\t}\n\n\treturn results, nil\n}\n\n// extractLinkContext extracts approximately 50 characters of context around a link\n// extractLinkContext 提取链接周围约 50 个字符的上下文\nfunc (s *noteLinkService) extractLinkContext(content, targetPath string) string {\n\t// Look for [[targetPath]] or [[targetPath|alias]]\n\t// 查找 [[targetPath]] 或 [[targetPath|alias]]\n\tsearchPatterns := []string{\n\t\t\"[[\" + targetPath + \"]]\",\n\t\t\"[[\" + targetPath + \"|\",\n\t}\n\n\tvar pos int = -1\n\tvar matchLen int\n\n\tfor _, pattern := range searchPatterns {\n\t\tidx := strings.Index(content, pattern)\n\t\tif idx >= 0 && (pos < 0 || idx < pos) {\n\t\t\tpos = idx\n\t\t\tmatchLen = len(pattern)\n\t\t}\n\t}\n\n\tif pos < 0 {\n\t\treturn \"\"\n\t}\n\n\t// Extract context: 25 chars before and after the link\n\t// 提取上下文：链接前后各 25 个字符\n\tcontextRadius := 25\n\tstart := pos - contextRadius\n\tif start < 0 {\n\t\tstart = 0\n\t}\n\n\t// Find the end of the link (closing ]])\n\t// 查找链接的结尾（闭合的 ]]）\n\tlinkEnd := strings.Index(content[pos:], \"]]\")\n\tif linkEnd < 0 {\n\t\tlinkEnd = matchLen\n\t} else {\n\t\tlinkEnd += 2 // Include ]] / 包含 ]]\n\t}\n\n\tend := pos + linkEnd + contextRadius\n\tif end > len(content) {\n\t\tend = len(content)\n\t}\n\n\tcontext := content[start:end]\n\n\t// Clean up: replace newlines with spaces and trim\n\t// 清理：将换行符替换为空格并修剪\n\tcontext = strings.ReplaceAll(context, \"\\n\", \" \")\n\tcontext = strings.TrimSpace(context)\n\n\t// Add ellipsis if truncated\n\t// 如果被截断则添加省略号\n\tif start > 0 {\n\t\tcontext = \"...\" + context\n\t}\n\tif end < len(content) {\n\t\tcontext = context + \"...\"\n\t}\n\n\treturn context\n}\n\n// Ensure noteLinkService implements NoteLinkService interface\n// 确保 noteLinkService 实现了 NoteLinkService 接口\nvar _ NoteLinkService = (*noteLinkService)(nil)\n"
  },
  {
    "path": "internal/service/note_service.go",
    "content": "// Package service implements the business logic layer\n// Package service 实现业务逻辑层\npackage service\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"regexp\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/logger\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"go.uber.org/zap\"\n\t\"golang.org/x/sync/singleflight\"\n\t\"gorm.io/gorm\"\n)\n\n// NoteService defines the note business service interface\n// NoteService 定义笔记业务服务接口\ntype NoteService interface {\n\t// Get retrieves a single note\n\t// Get 获取单条笔记\n\tGet(ctx context.Context, uid int64, params *dto.NoteGetRequest) (*dto.NoteDTO, error)\n\n\t// UpdateCheck checks if note needs updating\n\t// UpdateCheck 检查笔记是否需要更新\n\tUpdateCheck(ctx context.Context, uid int64, params *dto.NoteUpdateCheckRequest) (string, *dto.NoteDTO, error)\n\n\t// ModifyOrCreate creates or modifies a note\n\t// ModifyOrCreate 创建或修改笔记\n\tModifyOrCreate(ctx context.Context, uid int64, params *dto.NoteModifyOrCreateRequest, mtimeCheck bool) (bool, *dto.NoteDTO, error)\n\n\t// Delete deletes a note\n\t// Delete 删除笔记\n\tDelete(ctx context.Context, uid int64, params *dto.NoteDeleteRequest) (*dto.NoteDTO, error)\n\n\t// Restore restores a note (from recycle bin)\n\t// Restore 恢复笔记（从回收站恢复）\n\tRestore(ctx context.Context, uid int64, params *dto.NoteRestoreRequest) (*dto.NoteDTO, error)\n\n\t// Rename renames a note\n\t// Rename 重命名笔记\n\tRename(ctx context.Context, uid int64, params *dto.NoteRenameRequest) (*dto.NoteDTO, *dto.NoteDTO, error)\n\n\t// List retrieves note list\n\t// List 获取笔记列表\n\tList(ctx context.Context, uid int64, params *dto.NoteListRequest, pager *app.Pager) ([]*dto.NoteNoContentDTO, int, error)\n\n\t// ListByLastTime retrieves notes updated after lastTime\n\t// ListByLastTime 获取在 lastTime 之后更新的笔记\n\tListByLastTime(ctx context.Context, uid int64, params *dto.NoteSyncRequest) ([]*dto.NoteDTO, error)\n\n\t// Sync syncs notes (alias for ListByLastTime, used for WebSocket sync)\n\t// Sync 同步笔记（ListByLastTime 的别名，用于 WebSocket 同步）\n\tSync(ctx context.Context, uid int64, params *dto.NoteSyncRequest) ([]*dto.NoteDTO, error)\n\n\t// CountSizeSum counts total number and size of notes in a vault\n\t// CountSizeSum 统计 vault 中笔记总数与总大小\n\tCountSizeSum(ctx context.Context, vaultID int64, uid int64) error\n\n\t// Cleanup cleans up expired soft-deleted notes\n\t// Cleanup 清理过期的软删除笔记\n\tCleanup(ctx context.Context, uid int64) error\n\n\t// CleanupByTime cleans up expired soft-deleted notes for all users by cutoff time\n\t// CleanupByTime 按截止时间清理所有用户的过期软删除笔记\n\tCleanupByTime(ctx context.Context, cutoffTime int64) error\n\n\t// ListNeedSnapshot retrieves notes that need snapshot\n\t// ListNeedSnapshot 获取需要快照的笔记\n\tListNeedSnapshot(ctx context.Context, uid int64) ([]*dto.NoteDTO, error)\n\n\t// Migrate migrates note history records\n\t// Migrate 迁移笔记历史记录\n\tMigrate(ctx context.Context, oldNoteID, newNoteID int64, uid int64) error\n\n\t// MigratePush submits note migration task\n\t// MigratePush 提交笔记迁移任务\n\tMigratePush(oldNoteID, newNoteID int64, uid int64)\n\n\t// WithClient sets client info\n\t// WithClient 设置客户端信息\n\tWithClient(clientType, name, version string) NoteService\n\n\t// PatchFrontmatter patches note frontmatter\n\t// PatchFrontmatter 修改笔记 Frontmatter\n\tPatchFrontmatter(ctx context.Context, uid int64, params *dto.NotePatchFrontmatterRequest) (*dto.NoteDTO, error)\n\n\t// AppendContent appends content to a note\n\t// AppendContent 在笔记末尾追加内容\n\tAppendContent(ctx context.Context, uid int64, params *dto.NoteAppendRequest) (*dto.NoteDTO, error)\n\n\t// PrependContent prepends content to a note\n\t// PrependContent 在笔记开头插入内容\n\tPrependContent(ctx context.Context, uid int64, params *dto.NotePrependRequest) (*dto.NoteDTO, error)\n\n\t// ReplaceContent performs find/replace in a note\n\t// ReplaceContent 在笔记中执行替换\n\tReplaceContent(ctx context.Context, uid int64, params *dto.NoteReplaceRequest) (*dto.NoteReplaceResponse, error)\n\n\t// UpdateNoteLinks extracts wiki links from content and updates the link index\n\t// UpdateNoteLinks 从内容中提取 Wiki 链接并更新链接索引\n\tUpdateNoteLinks(ctx context.Context, noteID int64, content string, vaultID, uid int64)\n\n\t// RecycleClear cleans up the recycle bin\n\t// RecycleClear 清理回收站\n\tRecycleClear(ctx context.Context, uid int64, params *dto.NoteRecycleClearRequest) error\n\n\t// CleanDuplicateNotes cleans up duplicate note records\n\t// CleanDuplicateNotes 清理重复的笔记记录\n\tCleanDuplicateNotes(ctx context.Context, uid int64, vaultID int64) error\n}\n\n// noteService implementation of NoteService interface\n// noteService 实现 NoteService 接口\ntype noteService struct {\n\tnoteRepo       domain.NoteRepository      // Note repository // 笔记仓库\n\tnoteLinkRepo   domain.NoteLinkRepository  // Note link repository // 笔记链接仓库\n\tfileRepo       domain.FileRepository      // File repository // 文件仓库\n\tshareRepo      domain.UserShareRepository // Share repository for auto-revoke on delete // 分享仓库（删除时自动撤销）\n\tvaultService   VaultService               // Vault service // 仓库服务\n\tfolderService  FolderService              // Folder service // 文件夹服务\n\tsyncLogService SyncLogService             // Sync log service // 同步日志服务\n\tsf             *singleflight.Group        // Singleflight group // 并发请求合并组\n\tclientType     string                     // Client type // 客户端类型\n\tclientName     string                     // Client name // 客户端名称\n\tclientVer      string                     // Client version // 客户端版本\n\tconfig         *ServiceConfig             // Service configuration // 服务配置\n\tbackupService  BackupService              // Backup service // 备份服务\n\tgitSyncService GitSyncService             // Git sync service // Git 同步服务\n\tcountTimers    *sync.Map                  // Timers for CountSizeSum debounce // CountSizeSum 防抖计时器\n}\n\n// NewNoteService creates NoteService instance\n// NewNoteService 创建 NoteService 实例\nfunc NewNoteService(noteRepo domain.NoteRepository, noteLinkRepo domain.NoteLinkRepository, fileRepo domain.FileRepository, shareRepo domain.UserShareRepository, vaultSvc VaultService, folderSvc FolderService, backupSvc BackupService, gitSyncSvc GitSyncService, syncLogSvc SyncLogService, config *ServiceConfig) NoteService {\n\treturn &noteService{\n\t\tnoteRepo:       noteRepo,\n\t\tnoteLinkRepo:   noteLinkRepo,\n\t\tfileRepo:       fileRepo,\n\t\tshareRepo:      shareRepo,\n\t\tvaultService:   vaultSvc,\n\t\tfolderService:  folderSvc,\n\t\tbackupService:  backupSvc,\n\t\tgitSyncService: gitSyncSvc,\n\t\tsyncLogService: syncLogSvc,\n\t\tsf:             &singleflight.Group{},\n\t\tconfig:         config,\n\t\tcountTimers:    &sync.Map{},\n\t}\n}\n\n// WithClient sets client info, returns new NoteService instance\n// WithClient 设置客户端信息，返回新 NoteService 实例\nfunc (s *noteService) WithClient(clientType, name, version string) NoteService {\n\treturn &noteService{\n\t\tnoteRepo:       s.noteRepo,\n\t\tnoteLinkRepo:   s.noteLinkRepo,\n\t\tfileRepo:       s.fileRepo,\n\t\tshareRepo:      s.shareRepo,\n\t\tvaultService:   s.vaultService,\n\t\tfolderService:  s.folderService,\n\t\tsyncLogService: s.syncLogService,\n\t\tsf:             s.sf,\n\t\tclientType:     clientType,\n\t\tclientName:     name,\n\t\tclientVer:      version,\n\t\tconfig:         s.config,\n\t\tbackupService:  s.backupService,\n\t\tgitSyncService: s.gitSyncService,\n\t\tcountTimers:    s.countTimers, // Share the same timer map // 共享同一个计时器 map\n\t}\n}\n\n// domainToDTO converts domain model to DTO\n// domainToDTO 将领域模型转换为 DTO\nfunc (s *noteService) domainToDTO(note *domain.Note) *dto.NoteDTO {\n\tif note == nil {\n\t\treturn nil\n\t}\n\treturn &dto.NoteDTO{\n\t\tID:               note.ID,\n\t\tAction:           string(note.Action),\n\t\tPath:             note.Path,\n\t\tPathHash:         note.PathHash,\n\t\tContent:          note.Content,\n\t\tContentHash:      note.ContentHash,\n\t\tVersion:          note.Version,\n\t\tSize:             note.Size,\n\t\tCtime:            note.Ctime,\n\t\tMtime:            note.Mtime,\n\t\tClientName:       note.ClientName,\n\t\tClientType:       note.ClientType,\n\t\tClientVersion:    note.ClientVersion,\n\t\tUpdatedTimestamp: note.UpdatedTimestamp,\n\t\tUpdatedAt:        timex.Time(note.UpdatedAt),\n\t\tCreatedAt:        timex.Time(note.CreatedAt),\n\t}\n}\n\n// domainToNoContentDTO converts domain model to DTO without content\n// domainToNoContentDTO 将领域模型转换为不含内容的 DTO\nfunc (s *noteService) domainToNoContentDTO(note *domain.Note) *dto.NoteNoContentDTO {\n\tif note == nil {\n\t\treturn nil\n\t}\n\treturn &dto.NoteNoContentDTO{\n\t\tID:               note.ID,\n\t\tAction:           string(note.Action),\n\t\tPath:             note.Path,\n\t\tPathHash:         note.PathHash,\n\t\tVersion:          note.Version,\n\t\tSize:             note.Size,\n\t\tCtime:            note.Ctime,\n\t\tMtime:            note.Mtime,\n\t\tClientName:       note.ClientName,\n\t\tClientType:       note.ClientType,\n\t\tClientVersion:    note.ClientVersion,\n\t\tUpdatedTimestamp: note.UpdatedTimestamp,\n\t\tUpdatedAt:        timex.Time(note.UpdatedAt),\n\t\tCreatedAt:        timex.Time(note.CreatedAt),\n\t}\n}\n\n// Get retrieves a single note\n// Get 获取单条笔记\nfunc (s *noteService) Get(ctx context.Context, uid int64, params *dto.NoteGetRequest) (*dto.NoteDTO, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnote, err := s.noteRepo.GetByPathHashIncludeRecycle(ctx, params.PathHash, vaultID, uid, params.IsRecycle)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorNoteNotFound\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\treturn s.domainToDTO(note), nil\n}\n\n// UpdateCheck checks if note needs updating\n// UpdateCheck 检查笔记是否需要更新\nfunc (s *noteService) UpdateCheck(ctx context.Context, uid int64, params *dto.NoteUpdateCheckRequest) (string, *dto.NoteDTO, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn \"\", nil, err\n\t}\n\n\tnote, _ := s.noteRepo.GetAllByPathHash(ctx, params.PathHash, vaultID, uid)\n\n\tif note != nil {\n\t\tnoteDTO := s.domainToDTO(note)\n\t\t// Check if content is consistent\n\t\t// 检查内容是否一致\n\t\tif note.Action == \"delete\" {\n\t\t\treturn \"Create\", nil, nil\n\t\t}\n\t\tif note.ContentHash == params.ContentHash {\n\t\t\t// Notify user to update mtime when user mtime is less than server mtime\n\t\t\t// 当用户 mtime 小于服务端 mtime 时，通知用户更新 mtime\n\t\t\tif params.Mtime < note.Mtime {\n\t\t\t\treturn \"UpdateMtime\", noteDTO, nil\n\t\t\t} else if params.Mtime > note.Mtime {\n\t\t\t\tif err := s.noteRepo.UpdateMtime(ctx, params.Mtime, note.ID, uid); err != nil {\n\t\t\t\t\t// Non-critical update failed, log warning but do not block flow\n\t\t\t\t\t// 非关键更新失败，记录警告日志但不阻断流程\n\t\t\t\t\tzap.L().Warn(\"UpdateMtime failed for note\",\n\t\t\t\t\t\tzap.Int64(logger.FieldUID, uid),\n\t\t\t\t\t\tzap.Int64(\"noteId\", note.ID),\n\t\t\t\t\t\tzap.Int64(\"mtime\", params.Mtime),\n\t\t\t\t\t\tzap.String(logger.FieldMethod, \"NoteService.UpdateCheck\"),\n\t\t\t\t\t\tzap.Error(err),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn \"\", noteDTO, nil\n\t\t}\n\t\treturn \"UpdateContent\", noteDTO, nil\n\t}\n\treturn \"Create\", nil, nil\n}\n\n// ModifyOrCreate creates or modifies a note\n// ModifyOrCreate 创建或修改笔记\nfunc (s *noteService) ModifyOrCreate(ctx context.Context, uid int64, params *dto.NoteModifyOrCreateRequest, mtimeCheck bool) (bool, *dto.NoteDTO, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn false, nil, err\n\t}\n\n\tkey := fmt.Sprintf(\"modify_or_create_%d_%d_%s\", uid, vaultID, params.PathHash)\n\ttype result struct {\n\t\tisNew bool\n\t\tdto   *dto.NoteDTO\n\t}\n\n\tval, err, _ := s.sf.Do(key, func() (any, error) {\n\t\tvar isNew bool\n\t\tnote, _ := s.noteRepo.GetAllByPathHash(ctx, params.PathHash, vaultID, uid)\n\n\t\tif note != nil {\n\t\t\tisNew = false\n\n\t\t\t// If createOnly is set and note exists (not deleted), return error\n\t\t\tif note.Action != domain.NoteActionDelete && params.CreateOnly {\n\t\t\t\treturn nil, code.ErrorNoteExist\n\t\t\t}\n\n\t\t\t// Check if content is consistent, excluding notes marked as deleted\n\t\t\tif mtimeCheck && note.Action != domain.NoteActionDelete && note.Mtime == params.Mtime && note.ContentHash == params.ContentHash {\n\t\t\t\treturn &result{isNew: isNew, dto: nil}, nil\n\t\t\t}\n\t\t\t// If content is consistent but modification time is different, only update modification time\n\t\t\t// 检查内容是否一致但修改时间不同，则只更新修改时间\n\t\t\tif mtimeCheck && note.Mtime < params.Mtime && note.ContentHash == params.ContentHash {\n\t\t\t\terr := s.noteRepo.UpdateActionMtime(ctx, domain.NoteActionModify, params.Mtime, note.ID, uid)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t\t\t}\n\t\t\t\tnote.Mtime = params.Mtime\n\t\t\t\t// Log mtime-only update // 记录仅 mtime 变更日志\n\t\t\t\tif s.syncLogService != nil {\n\t\t\t\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeNote, domain.SyncLogActionModify, \"mtime\", note.Path, note.PathHash, s.clientType, s.clientName, s.clientVer, note.Size)\n\t\t\t\t}\n\t\t\t\treturn &result{isNew: isNew, dto: s.domainToDTO(note)}, nil\n\t\t\t}\n\n\t\t\t// Set action // 设置 action\n\t\t\tvar action domain.NoteAction\n\t\t\tif note.Action == domain.NoteActionDelete {\n\t\t\t\taction = domain.NoteActionCreate\n\t\t\t} else {\n\t\t\t\taction = domain.NoteActionModify\n\t\t\t}\n\n\t\t\t// Update note // 更新笔记\n\t\t\tnote.VaultID = vaultID\n\t\t\tnote.Path = params.Path\n\t\t\tnote.PathHash = params.PathHash\n\t\t\tnote.Content = params.Content\n\t\t\tnote.ContentHash = params.ContentHash\n\t\t\tnote.ClientName = s.clientName\n\t\t\tnote.ClientType = s.clientType\n\t\t\tnote.ClientVersion = s.clientVer\n\t\t\tnote.Size = int64(len(params.Content))\n\t\t\tnote.Mtime = params.Mtime\n\t\t\tnote.Ctime = params.Ctime\n\t\t\tnote.Action = action\n\t\t\tnote.Rename = 0\n\t\t\tnote.Version++ // Increment version on content change // 内容变更时递增版本号\n\n\t\t\tupdated, err := s.noteRepo.Update(ctx, note, uid)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t\t}\n\n\t\t\t// Log content modify // 记录内容变更日志\n\t\t\tif s.syncLogService != nil {\n\t\t\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeNote, domain.SyncLogActionModify, \"content,mtime\", updated.Path, updated.PathHash, s.clientType, s.clientName, s.clientVer, updated.Size)\n\t\t\t}\n\n\t\t\tgo s.folderService.SyncResourceFID(context.Background(), uid, vaultID, []int64{updated.ID}, nil)\n\t\t\tgo s.CountSizeSum(context.Background(), vaultID, uid)\n\t\t\tgo s.UpdateNoteLinks(context.Background(), updated.ID, params.Content, vaultID, uid)\n\t\t\tNoteHistoryDelayPush(updated.ID, uid)\n\n\t\t\tif s.backupService != nil {\n\t\t\t\tgo s.backupService.NotifyUpdated(uid)\n\t\t\t}\n\t\t\tif s.gitSyncService != nil {\n\t\t\t\tgo s.gitSyncService.NotifyUpdated(uid, vaultID)\n\t\t\t}\n\n\t\t\treturn &result{isNew: isNew, dto: s.domainToDTO(updated)}, nil\n\t\t}\n\n\t\t// Create new note // 创建新笔记\n\t\tisNew = true\n\t\tnewNote := &domain.Note{\n\t\t\tVaultID:       vaultID,\n\t\t\tPath:          params.Path,\n\t\t\tPathHash:      params.PathHash,\n\t\t\tContent:       params.Content,\n\t\t\tContentHash:   params.ContentHash,\n\t\t\tClientName:    s.clientName,\n\t\t\tClientType:    s.clientType,\n\t\t\tClientVersion: s.clientVer,\n\t\t\tSize:          int64(len(params.Content)),\n\t\t\tMtime:         params.Mtime,\n\t\t\tCtime:         params.Ctime,\n\t\t\tAction:        domain.NoteActionCreate,\n\t\t}\n\n\t\tcreated, err := s.noteRepo.Create(ctx, newNote, uid)\n\t\tif err != nil {\n\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t}\n\n\t\t// Log create // 记录新建日志\n\t\tif s.syncLogService != nil {\n\t\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeNote, domain.SyncLogActionCreate, \"\", created.Path, created.PathHash, s.clientType, s.clientName, s.clientVer, created.Size)\n\t\t}\n\n\t\tgo s.folderService.SyncResourceFID(context.Background(), uid, vaultID, []int64{created.ID}, nil)\n\t\tgo s.CountSizeSum(context.Background(), vaultID, uid)\n\t\tgo s.UpdateNoteLinks(context.Background(), created.ID, params.Content, vaultID, uid)\n\t\tNoteHistoryDelayPush(created.ID, uid)\n\t\tif s.backupService != nil {\n\t\t\tgo s.backupService.NotifyUpdated(uid)\n\t\t}\n\n\t\tif s.gitSyncService != nil {\n\t\t\tgo s.gitSyncService.NotifyUpdated(uid, vaultID)\n\t\t}\n\n\t\treturn &result{isNew: isNew, dto: s.domainToDTO(created)}, nil\n\t})\n\n\tif err != nil {\n\t\treturn false, nil, err\n\t}\n\n\tres := val.(*result)\n\treturn res.isNew, res.dto, nil\n}\n\n// Delete deletes a note\n// Delete 删除笔记\nfunc (s *noteService) Delete(ctx context.Context, uid int64, params *dto.NoteDeleteRequest) (*dto.NoteDTO, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID // 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err // VaultService 已返回 code.Error\n\t}\n\n\tnote, err := s.noteRepo.GetByPathHashIncludeRecycle(ctx, params.PathHash, vaultID, uid, false)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorNoteNotFound\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Update to deleted status // 更新为删除状态\n\tnote.Action = domain.NoteActionDelete\n\tnote.ClientName = s.clientName\n\tnote.ClientType = s.clientType\n\tnote.ClientVersion = s.clientVer\n\tnote.Rename = 0\n\n\terr = s.noteRepo.UpdateDelete(ctx, note, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// If note has active share, automatically revoke (to prevent count residue) // 若笔记有 active 分享，自动撤销（防止计数残留）\n\tif err := s.shareRepo.UpdateStatusByRes(ctx, uid, \"note\", note.ID, domain.UserShareStatusRevoked); err != nil {\n\t\tzap.L().Warn(\"Failed to revoke share on note deletion\",\n\t\t\tzap.Int64(\"uid\", uid),\n\t\t\tzap.Int64(\"noteId\", note.ID),\n\t\t\tzap.String(\"pathHash\", params.PathHash),\n\t\t\tzap.Error(err),\n\t\t)\n\t}\n\n\t// Log soft delete // 记录软删除日志\n\tif s.syncLogService != nil {\n\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeNote, domain.SyncLogActionSoftDelete, \"\", note.Path, note.PathHash, s.clientType, s.clientName, s.clientVer, note.Size)\n\t}\n\n\t// Re-fetch the updated note // 重新获取更新后的笔记\n\tupdated, err := s.noteRepo.GetByID(ctx, note.ID, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tNoteHistoryDelayPush(updated.ID, uid)\n\tif s.backupService != nil {\n\t\tgo s.backupService.NotifyUpdated(uid)\n\t}\n\tif s.gitSyncService != nil {\n\t\tgo s.gitSyncService.NotifyUpdated(uid, vaultID)\n\t}\n\n\treturn s.domainToDTO(updated), nil\n}\n\n// Restore restores a note (from recycle bin)\n// Restore 恢复笔记（从回收站恢复）\nfunc (s *noteService) Restore(ctx context.Context, uid int64, params *dto.NoteRestoreRequest) (*dto.NoteDTO, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID // 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err // VaultService 已返回 code.Error\n\t}\n\n\t// Get note from recycle bin // 从回收站获取笔记\n\tnote, err := s.noteRepo.GetByPathHashIncludeRecycle(ctx, params.PathHash, vaultID, uid, true)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorNoteNotFound\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Check if note is deleted // 检查笔记是否已删除\n\tif note.Action != domain.NoteActionDelete {\n\t\treturn nil, code.ErrorNoteNotFound\n\t}\n\n\t// Update to modified status and update modification time // 更新为修改状态 并更新修改时间\n\tnote.Action = domain.NoteActionModify\n\tnote.ClientName = s.clientName\n\tnote.ClientType = s.clientType\n\tnote.ClientVersion = s.clientVer\n\tnote.Mtime = time.Now().UnixMilli()\n\tnote.Rename = 0\n\n\terr = s.noteRepo.UpdateDelete(ctx, note, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Log restore // 记录恢复日志\n\tif s.syncLogService != nil {\n\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeNote, domain.SyncLogActionRestore, \"\", note.Path, note.PathHash, s.clientType, s.clientName, s.clientVer, note.Size)\n\t}\n\n\t// Re-fetch the updated note\n\t// 重新获取更新后的笔记\n\tupdated, err := s.noteRepo.GetByID(ctx, note.ID, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tgo s.folderService.SyncResourceFID(context.Background(), uid, vaultID, []int64{updated.ID}, nil)\n\tgo s.CountSizeSum(context.Background(), vaultID, uid)\n\tgo s.UpdateNoteLinks(context.Background(), updated.ID, updated.Content, vaultID, uid)\n\n\tNoteHistoryDelayPush(updated.ID, uid)\n\tif s.backupService != nil {\n\t\tgo s.backupService.NotifyUpdated(uid)\n\t}\n\tif s.gitSyncService != nil {\n\t\tgo s.gitSyncService.NotifyUpdated(uid, vaultID)\n\t}\n\n\treturn s.domainToDTO(updated), nil\n}\n\n// Rename renames a note\n// Rename 重命名笔记\nfunc (s *noteService) Rename(ctx context.Context, uid int64, params *dto.NoteRenameRequest) (*dto.NoteDTO, *dto.NoteDTO, error) {\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tnewPath := strings.Trim(params.Path, \"/\")\n\tnewPathHash := params.PathHash\n\tif newPathHash == \"\" {\n\t\tnewPathHash = util.EncodeHash32(newPath)\n\t}\n\n\tkey := fmt.Sprintf(\"rename_%d_%d_%s_%s\", uid, vaultID, params.OldPathHash, newPathHash)\n\ttype result struct {\n\t\toldNote *dto.NoteDTO\n\t\tnewNote *dto.NoteDTO\n\t}\n\n\tval, err, _ := s.sf.Do(key, func() (any, error) {\n\t\t// 1. Check if target path has valid note\n\t\t// 1. 判断目标路径是否存在有效笔记\n\t\texistNote, _ := s.noteRepo.GetAllByPathHash(ctx, newPathHash, vaultID, uid)\n\t\tif existNote != nil && existNote.Action != domain.NoteActionDelete {\n\t\t\treturn nil, code.ErrorNoteExist\n\t\t}\n\n\t\toldPath := strings.Trim(params.OldPath, \"/\")\n\t\toldPathHash := params.OldPathHash\n\t\tif oldPathHash == \"\" {\n\t\t\toldPathHash = util.EncodeHash32(oldPath)\n\t\t}\n\n\t\t// 2. Get old note\n\t\t// 2. 获取旧笔记\n\t\tn, err := s.noteRepo.GetByPathHash(ctx, oldPathHash, vaultID, uid)\n\t\tif err != nil {\n\t\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\t\treturn nil, code.ErrorNoteNotFound\n\t\t\t}\n\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t}\n\n\t\t// 3. Mark old note as deleted with rename flag\n\t\t// 3. 标记旧笔记删除并带上重命名标志\n\t\tn.Action = domain.NoteActionDelete\n\t\tn.Rename = 1\n\t\tn.ClientName = s.clientName\n\t\tn.ClientType = s.clientType\n\t\tn.ClientVersion = s.clientVer\n\t\tn.UpdatedTimestamp = timex.Now().UnixMilli()\n\t\toldNote, err := s.noteRepo.Update(ctx, n, uid)\n\t\tif err != nil {\n\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t}\n\n\t\t// 4. New or reuse note record\n\t\t// 4. 新建或复用笔记记录\n\t\tvar newNoteCreated *domain.Note\n\t\tif existNote != nil {\n\t\t\t// Reuse deleted record // 复用已删除的记录\n\t\t\texistNote.Action = domain.NoteActionCreate\n\t\t\texistNote.Path = newPath\n\t\t\texistNote.PathHash = newPathHash\n\t\t\tnewPathDir := \"\"\n\t\t\tif idx := strings.LastIndex(newPath, \"/\"); idx >= 0 {\n\t\t\t\tnewPathDir = newPath[:idx]\n\t\t\t}\n\t\t\texistNote.FID, _ = s.folderService.EnsurePathFID(ctx, uid, vaultID, newPathDir)\n\t\t\texistNote.Content = n.Content\n\t\t\texistNote.ContentHash = n.ContentHash\n\t\t\texistNote.Version = n.Version\n\t\t\texistNote.Mtime = n.Mtime // Preserve original mtime // 保留原始修改时间\n\t\t\texistNote.ClientName = s.clientName\n\t\t\texistNote.ClientType = s.clientType\n\t\t\texistNote.ClientVersion = s.clientVer\n\t\t\texistNote.UpdatedTimestamp = timex.Now().UnixMilli()\n\t\t\tnewNoteCreated, err = s.noteRepo.Update(ctx, existNote, uid)\n\t\t} else {\n\t\t\t// Create new record // 创建新记录\n\t\t\tnewNote := &domain.Note{\n\t\t\t\tVaultID:          vaultID,\n\t\t\t\tAction:           domain.NoteActionCreate,\n\t\t\t\tPath:             newPath,\n\t\t\t\tPathHash:         newPathHash,\n\t\t\t\tFID:              n.FID,\n\t\t\t\tCtime:            n.Ctime,\n\t\t\t\tMtime:            n.Mtime, // Preserve original mtime // 保留原始修改时间\n\t\t\t\tClientName:       s.clientName,\n\t\t\t\tClientType:       s.clientType,\n\t\t\t\tClientVersion:    s.clientVer,\n\t\t\t\tUpdatedTimestamp: timex.Now().UnixMilli(),\n\t\t\t\tContent:          n.Content,\n\t\t\t\tContentHash:      n.ContentHash,\n\t\t\t\tVersion:          n.Version,\n\t\t\t}\n\t\t\tnewPathDir := \"\"\n\t\t\tif idx := strings.LastIndex(newPath, \"/\"); idx >= 0 {\n\t\t\t\tnewPathDir = newPath[:idx]\n\t\t\t}\n\t\t\t// Ensure FID is correct // 确保 FID 正确\n\t\t\tnewNote.FID, _ = s.folderService.EnsurePathFID(ctx, uid, vaultID, newPathDir)\n\t\t\tnewNoteCreated, err = s.noteRepo.Create(ctx, newNote, uid)\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t}\n\n\t\t// Log rename // 记录重命名日志\n\t\tif s.syncLogService != nil {\n\t\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeNote, domain.SyncLogActionRename, \"path\", newNoteCreated.Path, newNoteCreated.PathHash, s.clientType, s.clientName, s.clientVer, newNoteCreated.Size)\n\t\t}\n\n\t\tgo s.folderService.SyncResourceFID(context.Background(), uid, vaultID, []int64{newNoteCreated.ID}, nil)\n\t\tgo s.Migrate(context.Background(), n.ID, newNoteCreated.ID, uid)\n\t\tif s.backupService != nil {\n\t\t\tgo s.backupService.NotifyUpdated(uid)\n\t\t}\n\t\tif s.gitSyncService != nil {\n\t\t\tgo s.gitSyncService.NotifyUpdated(uid, vaultID)\n\t\t}\n\n\t\treturn &result{oldNote: s.domainToDTO(oldNote), newNote: s.domainToDTO(newNoteCreated)}, nil\n\t})\n\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tres := val.(*result)\n\treturn res.oldNote, res.newNote, nil\n}\n\n// List retrieves note list\n// List 获取笔记列表\nfunc (s *noteService) List(ctx context.Context, uid int64, params *dto.NoteListRequest, pager *app.Pager) ([]*dto.NoteNoContentDTO, int, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\t// Parse paths parameter (comma-separated -> []string)\n\t// 解析 paths 参数（逗号分隔 → []string）\n\tvar paths []string\n\tif params.Paths != \"\" {\n\t\tfor _, p := range strings.Split(params.Paths, \",\") {\n\t\t\tif trimmed := strings.TrimSpace(p); trimmed != \"\" {\n\t\t\t\tpaths = append(paths, trimmed)\n\t\t\t}\n\t\t}\n\t}\n\n\tnotes, err := s.noteRepo.List(ctx, vaultID, pager.Page, pager.PageSize, uid, params.Keyword, params.IsRecycle, params.SearchMode, params.SearchContent, params.SortBy, params.SortOrder, paths)\n\tif err != nil {\n\t\treturn nil, 0, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tcount, err := s.noteRepo.ListCount(ctx, vaultID, uid, params.Keyword, params.IsRecycle, params.SearchMode, params.SearchContent, paths)\n\tif err != nil {\n\t\treturn nil, 0, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tvar result []*dto.NoteNoContentDTO\n\tfor _, n := range notes {\n\t\tresult = append(result, s.domainToNoContentDTO(n))\n\t}\n\n\treturn result, int(count), nil\n}\n\n// ListByLastTime retrieves notes updated after lastTime\n// ListByLastTime 获取在 lastTime 之后更新的笔记\nfunc (s *noteService) ListByLastTime(ctx context.Context, uid int64, params *dto.NoteSyncRequest) ([]*dto.NoteDTO, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err // VaultService 已返回 code.Error\n\t}\n\n\tnotes, err := s.noteRepo.ListByUpdatedTimestamp(ctx, params.LastTime, vaultID, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tvar results []*dto.NoteDTO\n\tcacheList := make(map[string]bool)\n\tfor _, note := range notes {\n\t\tif cacheList[note.PathHash] {\n\t\t\tcontinue\n\t\t}\n\t\tresults = append(results, s.domainToDTO(note))\n\t\tcacheList[note.PathHash] = true\n\t}\n\n\treturn results, nil\n}\n\n// CountSizeSum counts total number and total size of notes in a vault\n// CountSizeSum 统计 vault 中笔记总数与总大小\nfunc (s *noteService) CountSizeSum(ctx context.Context, vaultID int64, uid int64) error {\n\tkey := fmt.Sprintf(\"%d_%d\", uid, vaultID)\n\n\t// Debounce: 10 seconds delay. If a new request comes within 10s, reset the timer.\n\t// 防抖：10秒延迟。如果10秒内 house 有新请求，重置计时器。\n\tif timerOld, ok := s.countTimers.Load(key); ok {\n\t\tif t, ok := timerOld.(*time.Timer); ok {\n\t\t\tt.Stop()\n\t\t}\n\t}\n\n\ttimer := time.AfterFunc(10*time.Second, func() {\n\t\tdefer s.countTimers.Delete(key)\n\n\t\t// Use singleflight to ensure only one actual DB query runs for same key even if debounce period ends simultaneously\n\t\t// 使用 singleflight 确保即使防抖期同时结束，同一 key 也只有一个真实的 DB 查询\n\t\ts.sf.Do(key, func() (any, error) {\n\t\t\tresult, err := s.noteRepo.CountSizeSum(context.Background(), vaultID, uid)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t\t}\n\t\t\treturn nil, s.vaultService.UpdateNoteStats(context.Background(), result.Size, result.Count, vaultID, uid)\n\t\t})\n\t})\n\n\ts.countTimers.Store(key, timer)\n\treturn nil\n}\n\n// Cleanup cleans up expired soft-deleted notes\n// Cleanup 清理过期的软删除笔记\nfunc (s *noteService) Cleanup(ctx context.Context, uid int64) error {\n\tif s.config == nil {\n\t\treturn nil\n\t}\n\tretentionTimeStr := s.config.App.SoftDeleteRetentionTime\n\tif retentionTimeStr == \"\" || retentionTimeStr == \"0\" {\n\t\treturn nil\n\t}\n\n\tretentionDuration, err := util.ParseDuration(retentionTimeStr)\n\tif err != nil {\n\t\treturn code.ErrorInvalidParams.WithDetails(\"invalid SoftDeleteRetentionTime\")\n\t}\n\n\tif retentionDuration <= 0 {\n\t\treturn nil\n\t}\n\n\tcutoffTime := time.Now().Add(-retentionDuration).UnixMilli()\n\terr = s.noteRepo.DeletePhysicalByTime(ctx, cutoffTime, uid)\n\tif err != nil {\n\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\treturn nil\n}\n\n// CleanupByTime cleans up expired soft-deleted notes for all users by cutoff time\n// CleanupByTime 按截止时间清理所有用户的过期软删除笔记\nfunc (s *noteService) CleanupByTime(ctx context.Context, cutoffTime int64) error {\n\treturn s.noteRepo.DeletePhysicalByTimeAll(ctx, cutoffTime)\n}\n\n// ListNeedSnapshot retrieves notes that need snapshot\n// ListNeedSnapshot 获取需要快照的笔记\nfunc (s *noteService) ListNeedSnapshot(ctx context.Context, uid int64) ([]*dto.NoteDTO, error) {\n\tlist, err := s.noteRepo.ListContentUnchanged(ctx, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tvar result []*dto.NoteDTO\n\tfor _, n := range list {\n\t\tresult = append(result, s.domainToDTO(n))\n\t}\n\treturn result, nil\n}\n\n// Migrate migrates note history records\n// Migrate 迁移笔记历史记录\nfunc (s *noteService) Migrate(ctx context.Context, oldNoteID, newNoteID int64, uid int64) error {\n\t// Get old note information\n\t// Get old note information\n\t// 获取旧笔记信息\n\toldNote, err := s.noteRepo.GetByID(ctx, oldNoteID, uid)\n\tif err != nil {\n\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Migrate ContentLastSnapshot and Version from old note to new note\n\t// Migrate ContentLastSnapshot and Version from old note to new note\n\t// 将旧笔记的 ContentLastSnapshot 和 Version 迁移到新笔记\n\terr = s.noteRepo.UpdateSnapshot(ctx, oldNote.ContentLastSnapshot, oldNote.ContentLastSnapshotHash, oldNote.Version, newNoteID, uid)\n\tif err != nil {\n\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Mark old note as deleted, and mark as rename deleted\n\t// Mark old note as deleted, and mark as rename deleted\n\t// 标记删除旧笔记，并标记是 rename 删除的笔记\n\toldNote.Action = domain.NoteActionDelete\n\toldNote.Rename = 1\n\n\terr = s.noteRepo.UpdateDelete(ctx, oldNote, uid)\n\tif err != nil {\n\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Migrate share records: update res_id and resources from old note ID to new note ID\n\t// Migrate share records: update res_id and resources from old note ID to new note ID\n\t// 迁移分享记录：将旧笔记 ID 的分享指向新笔记 ID\n\tif s.shareRepo != nil {\n\t\tif shareErr := s.shareRepo.MigrateResID(ctx, uid, oldNoteID, newNoteID); shareErr != nil {\n\t\t\t// Log but don't fail the rename operation\n\t\t\tzap.L().Warn(\"Migrate: failed to migrate share records\",\n\t\t\t\tzap.Int64(logger.FieldUID, uid),\n\t\t\t\tzap.Int64(\"oldNoteID\", oldNoteID),\n\t\t\t\tzap.Int64(\"newNoteID\", newNoteID),\n\t\t\t\tzap.Error(shareErr))\n\t\t}\n\t}\n\n\tgo s.CountSizeSum(context.Background(), oldNote.VaultID, uid)\n\treturn nil\n}\n\n// MigratePush submits note migration task\n// MigratePush 提交笔记迁移任务\nfunc (s *noteService) MigratePush(oldNoteID, newNoteID int64, uid int64) {\n\tNoteMigrateChannel <- NoteMigrateMsg{\n\t\tOldNoteID: oldNoteID,\n\t\tNewNoteID: newNoteID,\n\t\tUID:       uid,\n\t}\n}\n\n// Sync syncs notes (alias for ListByLastTime, used for WebSocket sync)\nfunc (s *noteService) Sync(ctx context.Context, uid int64, params *dto.NoteSyncRequest) ([]*dto.NoteDTO, error) {\n\treturn s.ListByLastTime(ctx, uid, params)\n}\n\n// PatchFrontmatter patches note frontmatter with updates and removes specified keys\nfunc (s *noteService) PatchFrontmatter(ctx context.Context, uid int64, params *dto.NotePatchFrontmatterRequest) (*dto.NoteDTO, error) {\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\tnote, err := s.noteRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorNoteNotFound\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Parse existing frontmatter\n\texistingYaml, body, _ := util.ParseFrontmatter(note.Content)\n\tif existingYaml == nil {\n\t\texistingYaml = make(map[string]interface{})\n\t}\n\n\t// Merge updates\n\tnewYaml := util.MergeFrontmatter(existingYaml, params.Updates, params.Remove)\n\n\t// Reconstruct content\n\tnewContent := util.ReconstructContent(newYaml, body)\n\n\t// Save via ModifyOrCreate\n\tmodifyParams := &dto.NoteModifyOrCreateRequest{\n\t\tVault:       params.Vault,\n\t\tPath:        params.Path,\n\t\tPathHash:    params.PathHash,\n\t\tContent:     newContent,\n\t\tContentHash: util.EncodeHash32(newContent),\n\t\tMtime:       time.Now().UnixMilli(),\n\t\tCtime:       note.Ctime,\n\t}\n\n\t_, result, err := s.ModifyOrCreate(ctx, uid, modifyParams, false)\n\treturn result, err\n}\n\n// AppendContent appends content to the end of a note\n// AppendContent 在笔记末尾追加内容\nfunc (s *noteService) AppendContent(ctx context.Context, uid int64, params *dto.NoteAppendRequest) (*dto.NoteDTO, error) {\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\tnote, err := s.noteRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorNoteNotFound\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Append content\n\t// Append content\n\t// 追加内容\n\tnewContent := note.Content + params.Content\n\n\t// Save via ModifyOrCreate\n\tmodifyParams := &dto.NoteModifyOrCreateRequest{\n\t\tVault:       params.Vault,\n\t\tPath:        params.Path,\n\t\tPathHash:    params.PathHash,\n\t\tContent:     newContent,\n\t\tContentHash: util.EncodeHash32(newContent),\n\t\tMtime:       time.Now().UnixMilli(),\n\t\tCtime:       note.Ctime,\n\t}\n\n\t_, result, err := s.ModifyOrCreate(ctx, uid, modifyParams, false)\n\treturn result, err\n}\n\n// PrependContent prepends content to a note (after frontmatter if present)\n// PrependContent 在笔记开头插入内容（如果存在 Frontmatter 则在之后插入）\nfunc (s *noteService) PrependContent(ctx context.Context, uid int64, params *dto.NotePrependRequest) (*dto.NoteDTO, error) {\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\tnote, err := s.noteRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorNoteNotFound\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Parse frontmatter to preserve it\n\t// Parse frontmatter to preserve it\n\t// 解析 Frontmatter 以保留它\n\tyamlData, body, hasFrontmatter := util.ParseFrontmatter(note.Content)\n\n\t// Prepend content to body\n\t// Prepend content to body\n\t// 在正文开头插入内容\n\tnewBody := params.Content + body\n\n\t// Reconstruct content\n\tvar newContent string\n\tif hasFrontmatter {\n\t\tnewContent = util.ReconstructContent(yamlData, newBody)\n\t} else {\n\t\tnewContent = newBody\n\t}\n\n\t// Save via ModifyOrCreate\n\tmodifyParams := &dto.NoteModifyOrCreateRequest{\n\t\tVault:       params.Vault,\n\t\tPath:        params.Path,\n\t\tPathHash:    params.PathHash,\n\t\tContent:     newContent,\n\t\tContentHash: util.EncodeHash32(newContent),\n\t\tMtime:       time.Now().UnixMilli(),\n\t\tCtime:       note.Ctime,\n\t}\n\n\t_, result, err := s.ModifyOrCreate(ctx, uid, modifyParams, false)\n\treturn result, err\n}\n\n// ReplaceContent performs find/replace in a note\n// ReplaceContent 在笔记中执行查找/替换\nfunc (s *noteService) ReplaceContent(ctx context.Context, uid int64, params *dto.NoteReplaceRequest) (*dto.NoteReplaceResponse, error) {\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\tnote, err := s.noteRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorNoteNotFound\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tvar matchCount int\n\tvar newContent string\n\n\tif params.Regex {\n\t\t// Regex mode\n\t\t// Regex mode\n\t\t// 正则模式\n\t\tre, err := regexp.Compile(params.Find)\n\t\tif err != nil {\n\t\t\treturn nil, code.ErrorInvalidRegex.WithDetails(err.Error())\n\t\t}\n\n\t\tmatches := re.FindAllStringIndex(note.Content, -1)\n\t\tmatchCount = len(matches)\n\n\t\tif params.All {\n\t\t\tnewContent = re.ReplaceAllString(note.Content, params.Replace)\n\t\t} else if matchCount > 0 {\n\t\t\t// Only replace first match\n\t\t\t// Only replace first match\n\t\t\t// 仅替换第一个匹配项\n\t\t\tloc := re.FindStringIndex(note.Content)\n\t\t\tif loc != nil {\n\t\t\t\tnewContent = note.Content[:loc[0]] + params.Replace + note.Content[loc[1]:]\n\t\t\t}\n\t\t} else {\n\t\t\tnewContent = note.Content\n\t\t}\n\t} else {\n\t\t// Plain text mode\n\t\t// Plain text mode\n\t\t// 纯文本模式\n\t\tmatchCount = strings.Count(note.Content, params.Find)\n\n\t\tif params.All {\n\t\t\tnewContent = strings.ReplaceAll(note.Content, params.Find, params.Replace)\n\t\t} else if matchCount > 0 {\n\t\t\tnewContent = strings.Replace(note.Content, params.Find, params.Replace, 1)\n\t\t} else {\n\t\t\tnewContent = note.Content\n\t\t}\n\t}\n\n\t// Check if no match found and fail flag is set\n\t// Check if no match found and fail flag is set\n\t// 检查是否未找到匹配项且设置了失败标志\n\tif matchCount == 0 && params.FailIfNoMatch {\n\t\treturn nil, code.ErrorNoMatchFound\n\t}\n\n\t// If no changes, return early\n\t// If no changes, return early\n\t// 如果没有变化，提前返回\n\tif newContent == note.Content {\n\t\treturn &dto.NoteReplaceResponse{\n\t\t\tMatchCount: matchCount,\n\t\t\tNote:       s.domainToDTO(note),\n\t\t}, nil\n\t}\n\n\t// Save via ModifyOrCreate\n\tmodifyParams := &dto.NoteModifyOrCreateRequest{\n\t\tVault:       params.Vault,\n\t\tPath:        params.Path,\n\t\tPathHash:    params.PathHash,\n\t\tContent:     newContent,\n\t\tContentHash: util.EncodeHash32(newContent),\n\t\tMtime:       time.Now().UnixMilli(),\n\t\tCtime:       note.Ctime,\n\t}\n\n\t_, result, err := s.ModifyOrCreate(ctx, uid, modifyParams, false)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &dto.NoteReplaceResponse{\n\t\tMatchCount: matchCount,\n\t\tNote:       result,\n\t}, nil\n}\n\n// UpdateNoteLinks extracts wiki links from content and updates the link index\n// UpdateNoteLinks 从内容中提取 Wiki 链接并更新链接索引\nfunc (s *noteService) UpdateNoteLinks(ctx context.Context, noteID int64, content string, vaultID, uid int64) {\n\tif s.noteLinkRepo == nil {\n\t\treturn\n\t}\n\n\t// Delete existing links for this note\n\t// Delete existing links for this note\n\t// 删除该笔记现有的链接\n\t_ = s.noteLinkRepo.DeleteBySourceNoteID(ctx, noteID, uid)\n\n\t// Parse wiki links from content\n\t// Parse wiki links from content\n\t// 从内容中解析 Wiki 链接\n\tlinks := util.ParseWikiLinks(content)\n\tif len(links) == 0 {\n\t\treturn\n\t}\n\n\t// Create new link records\n\t// Create new link records\n\t// 创建新链接记录\n\tvar noteLinks []*domain.NoteLink\n\tfor _, link := range links {\n\t\tnoteLinks = append(noteLinks, &domain.NoteLink{\n\t\t\tSourceNoteID:   noteID,\n\t\t\tTargetPath:     link.Path,\n\t\t\tTargetPathHash: util.EncodeHash32(link.Path),\n\t\t\tLinkText:       link.Alias,\n\t\t\tIsEmbed:        link.IsEmbed,\n\t\t\tVaultID:        vaultID,\n\t\t})\n\t}\n\n\t_ = s.noteLinkRepo.CreateBatch(ctx, noteLinks, uid)\n}\n\n// RecycleClear 清理回收站\nfunc (s *noteService) RecycleClear(ctx context.Context, uid int64, params *dto.NoteRecycleClearRequest) error {\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif params.Path != \"\" && params.PathHash == \"\" {\n\t\tparams.PathHash = util.EncodeHash32(params.Path)\n\t}\n\n\t// Capture items to be deleted for detailed logging\n\t// 捕获待删除的项目以便进行详细日志记录\n\tvar notesToDelete []*domain.Note\n\tif params.PathHash != \"\" {\n\t\tnote, _ := s.noteRepo.GetByPathHashIncludeRecycle(ctx, params.PathHash, vaultID, uid, true)\n\t\tif note != nil {\n\t\t\tnotesToDelete = append(notesToDelete, note)\n\t\t\tif params.Path == \"\" {\n\t\t\t\tparams.Path = note.Path\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// Clear all: retrieve all notes in recycle bin (using a large page size)\n\t\t// 清理全部：获取回收站中的所有笔记（使用较大的分页限制）\n\t\tnotesToDelete, _ = s.noteRepo.List(ctx, vaultID, 1, 10000, uid, \"\", true, \"\", false, \"\", \"\", nil)\n\t}\n\n\terr = s.noteRepo.RecycleClear(ctx, params.Path, params.PathHash, vaultID, uid)\n\tif err != nil {\n\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Log permanent delete for each item // 为每一项记录彻底删除日志\n\tif s.syncLogService != nil {\n\t\tfor _, n := range notesToDelete {\n\t\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeNote, domain.SyncLogActionDelete, \"\", n.Path, n.PathHash, s.clientType, s.clientName, s.clientVer, n.Size)\n\t\t}\n\t}\n\n\tgo s.CountSizeSum(context.Background(), vaultID, uid)\n\treturn nil\n}\n\n// CleanDuplicateNotes 清理重复的笔记记录\nfunc (s *noteService) CleanDuplicateNotes(ctx context.Context, uid int64, vaultID int64) error {\n\t// Get all notes (including deleted ones for global deduplication)\n\t// 获取所有笔记（包含已删除，以便全局去重）\n\tnotes, err := s.noteRepo.ListByUpdatedTimestamp(ctx, 0, vaultID, uid)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Group by PathHash\n\t// 按 PathHash 分组\n\tgrouped := make(map[string][]*domain.Note)\n\tfor _, n := range notes {\n\t\tgrouped[n.PathHash] = append(grouped[n.PathHash], n)\n\t}\n\n\tfor pathHash, list := range grouped {\n\t\tif len(list) <= 1 {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Retention rules:\n\t\t// 保留规则：\n\t\t// 1. Prioritize records with Action != delete\n\t\t// 1. 优先保留 Action != delete 的记录\n\t\t// 2. If multiple active records exist, keep the one with the largest UpdatedTimestamp (latest)\n\t\t// 2. 如果有多个活跃记录，保留 UpdatedTimestamp 最大（最新）的一条\n\t\t// 3. If timestamps are identical, keep the record with the largest ID\n\t\t// 3. 如果时间戳一致，保留 ID 最大的记录\n\n\t\tvar bestNote *domain.Note\n\t\tfor _, n := range list {\n\t\t\tif bestNote == nil {\n\t\t\t\tbestNote = n\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Comparison logic\n\t\t\t// 比较逻辑\n\t\t\tisBetter := false\n\t\t\tif n.Action != domain.NoteActionDelete && bestNote.Action == domain.NoteActionDelete {\n\t\t\t\tisBetter = true\n\t\t\t} else if n.Action == bestNote.Action {\n\t\t\t\tif n.UpdatedTimestamp > bestNote.UpdatedTimestamp {\n\t\t\t\t\tisBetter = true\n\t\t\t\t} else if n.UpdatedTimestamp == bestNote.UpdatedTimestamp && n.ID > bestNote.ID {\n\t\t\t\t\tisBetter = true\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif isBetter {\n\t\t\t\tbestNote = n\n\t\t\t}\n\t\t}\n\n\t\t// Delete all records except the bestNote\n\t\t// Delete all records except the bestNote\n\t\t// 删除非 bestNote 的所有记录\n\t\tfor _, n := range list {\n\t\t\tif n.ID != bestNote.ID {\n\t\t\t\t// Clear singleflight cache to prevent residual data\n\t\t\t\t// 清除 singleflight 缓存，防止残留\n\t\t\t\ts.sf.Forget(fmt.Sprintf(\"modify_or_create_%d_%d_%s\", uid, vaultID, pathHash))\n\t\t\t\ts.sf.Forget(fmt.Sprintf(\"rename_%d_%d_%s\", uid, vaultID, pathHash)) // 虽然 rename key 不同，但以防万一\n\t\t\t\t\n\t\t\t\t_ = s.noteRepo.Delete(ctx, n.ID, uid)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Ensure noteService implements NoteService interface\n// 确保 noteService 实现了 NoteService interface\nvar _ NoteService = (*noteService)(nil)\n"
  },
  {
    "path": "internal/service/service.go",
    "content": "// Package service implements the business logic layer\n// Package service 实现业务逻辑层\n// This file retains package-level channel and message type definitions\n// 本文件保留包级别的通道和消息类型定义\npackage service\n\n// NoteMigrateChannel migration task channel\n// NoteMigrateChannel 迁移任务通道\nvar NoteMigrateChannel = make(chan NoteMigrateMsg, 1000)\n\n// NoteMigrateMsg note migration message\n// NoteMigrateMsg 笔记迁移消息\ntype NoteMigrateMsg struct {\n\tOldNoteID int64 // Old note ID // 旧笔记 ID\n\tNewNoteID int64 // New note ID // 新笔记 ID\n\tUID       int64 // User ID // 用户 ID\n}\n\n// NoteHistoryMsg note history record delayed processing message\n// NoteHistoryMsg 笔记历史记录延时处理消息\ntype NoteHistoryMsg struct {\n\tNoteID int64 // Note ID // 笔记 ID\n\tUID    int64 // User ID // 用户 ID\n}\n\n// NoteHistoryChannel delayed task channel, background task will listen to this channel\n// NoteHistoryChannel 延时任务通道，后台 task 会监听此通道\nvar NoteHistoryChannel = make(chan NoteHistoryMsg, 1000)\n\n// NoteHistoryDelayPush pushes note to the delayed processing queue\n// NoteHistoryDelayPush 将笔记推送至延时处理队列\nfunc NoteHistoryDelayPush(noteID int64, uid int64) {\n\tNoteHistoryChannel <- NoteHistoryMsg{NoteID: noteID, UID: uid}\n}\n"
  },
  {
    "path": "internal/service/setting_service.go",
    "content": "// Package service implements the business logic layer\n// Package service 实现业务逻辑层\npackage service\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/logger\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"go.uber.org/zap\"\n\t\"golang.org/x/sync/singleflight\"\n\t\"gorm.io/gorm\"\n)\n\n// SettingService defines the configuration business service interface\n// SettingService 定义配置业务服务接口\ntype SettingService interface {\n\t// UpdateCheck checks if configuration needs updating\n\t// UpdateCheck 检查配置是否需要更新\n\tUpdateCheck(ctx context.Context, uid int64, params *dto.SettingUpdateCheckRequest) (string, *dto.SettingDTO, error)\n\n\t// ModifyCheck checks configuration modification (alias for UpdateCheck)\n\t// ModifyCheck 检查配置修改（UpdateCheck 的别名）\n\tModifyCheck(ctx context.Context, uid int64, params *dto.SettingUpdateCheckRequest) (string, *dto.SettingDTO, error)\n\n\t// ModifyOrCreate creates or modifies configuration\n\t// ModifyOrCreate 创建或修改配置\n\tModifyOrCreate(ctx context.Context, uid int64, params *dto.SettingModifyOrCreateRequest, mtimeCheck bool) (bool, *dto.SettingDTO, error)\n\n\t// Modify modifies configuration (alias for ModifyOrCreate)\n\t// Modify 修改配置（ModifyOrCreate 的别名）\n\tModify(ctx context.Context, uid int64, params *dto.SettingModifyOrCreateRequest) (bool, *dto.SettingDTO, error)\n\n\t// Delete deletes configuration\n\t// Delete 删除配置\n\tDelete(ctx context.Context, uid int64, params *dto.SettingDeleteRequest) (*dto.SettingDTO, error)\n\n\t// Get retrieves a single configuration\n\t// Get 获取单条配置\n\tGet(ctx context.Context, uid int64, params *dto.SettingGetRequest) (*dto.SettingDTO, error)\n\n\t// ListByLastTime retrieves configurations updated after lastTime\n\t// ListByLastTime 获取在 lastTime 之后更新的配置\n\tListByLastTime(ctx context.Context, uid int64, params *dto.SettingSyncRequest) ([]*dto.SettingDTO, error)\n\n\t// CleanDuplicateSettings cleans up duplicate configuration records\n\t// CleanDuplicateSettings 清理重复的配置记录\n\tCleanDuplicateSettings(ctx context.Context, uid int64, vaultID int64) error\n\n\t// Sync synchronizes configuration (alias for ListByLastTime)\n\t// Sync 同步配置（ListByLastTime 的别名）\n\tSync(ctx context.Context, uid int64, params *dto.SettingSyncRequest) ([]*dto.SettingDTO, error)\n\n\t// List retrieves configurations with pagination\n\t// List 分页获取配置列表\n\tList(ctx context.Context, uid int64, params *dto.SettingListRequest, pager *pkgapp.Pager) ([]*dto.SettingDTO, int64, error)\n\n\t// Rename renames a configuration\n\t// Rename 重命名配置\n\tRename(ctx context.Context, uid int64, params *dto.SettingRenameRequest) (*dto.SettingDTO, error)\n\n\t// Cleanup cleans up expired soft-deleted configurations\n\t// Cleanup 清理过期的软删除配置\n\tCleanup(ctx context.Context, uid int64) error\n\n\t// CleanupByTime cleans up expired soft-deleted configurations for all users by cutoff time\n\t// CleanupByTime 按截止时间清理所有用户的过期软删除配置\n\tCleanupByTime(ctx context.Context, cutoffTime int64) error\n\n\t// ClearByVault clears all settings for a specific vault of a user\n\t// ClearByVault 清除用户指定笔记本的所有配置\n\tClearByVault(ctx context.Context, uid int64, vaultName string) error\n\n\t// WithClient sets client info\n\t// WithClient 设置客户端信息\n\tWithClient(clientType, name, version string) SettingService\n}\n\n// settingService implementation of SettingService interface\n// settingService 实现 SettingService 接口\ntype settingService struct {\n\tsettingRepo    domain.SettingRepository // Setting repository // 配置仓库\n\tvaultService   VaultService             // Vault service // 仓库服务\n\tsyncLogService SyncLogService           // Sync log service // 同步日志服务\n\tsf             *singleflight.Group      // Singleflight group // 并发请求合并组\n\tclientType     string                   // Client type // 客户端类型\n\tclientName     string                   // Client name // 客户端名称\n\tclientVer      string                   // Client version // 客户端版本\n\tconfig         *ServiceConfig           // Service configuration // 服务配置\n}\n\n// NewSettingService creates SettingService instance\n// NewSettingService 创建 SettingService 实例\nfunc NewSettingService(settingRepo domain.SettingRepository, vaultSvc VaultService, syncLogSvc SyncLogService, config *ServiceConfig) SettingService {\n\treturn &settingService{\n\t\tsettingRepo:    settingRepo,\n\t\tvaultService:   vaultSvc,\n\t\tsyncLogService: syncLogSvc,\n\t\tsf:             &singleflight.Group{},\n\t\tconfig:         config,\n\t}\n}\n\n// WithClient sets client info, returns new SettingService instance\n// WithClient 设置客户端信息，返回新 SettingService 实例\nfunc (s *settingService) WithClient(clientType, name, version string) SettingService {\n\treturn &settingService{\n\t\tsettingRepo:    s.settingRepo,\n\t\tvaultService:   s.vaultService,\n\t\tsyncLogService: s.syncLogService,\n\t\tsf:             s.sf,\n\t\tclientType:     clientType,\n\t\tclientName:     name,\n\t\tclientVer:      version,\n\t\tconfig:         s.config,\n\t}\n}\n\n// domainToDTO converts domain model to DTO\n// domainToDTO 将领域模型转换为 DTO\nfunc (s *settingService) domainToDTO(setting *domain.Setting) *dto.SettingDTO {\n\tif setting == nil {\n\t\treturn nil\n\t}\n\treturn &dto.SettingDTO{\n\t\tID:               setting.ID,\n\t\tAction:           string(setting.Action),\n\t\tPath:             setting.Path,\n\t\tPathHash:         setting.PathHash,\n\t\tContent:          setting.Content,\n\t\tContentHash:      setting.ContentHash,\n\t\tCtime:            setting.Ctime,\n\t\tMtime:            setting.Mtime,\n\t\tUpdatedTimestamp: setting.UpdatedTimestamp,\n\t\tUpdatedAt:        timex.Time(setting.UpdatedAt),\n\t\tCreatedAt:        timex.Time(setting.CreatedAt),\n\t}\n}\n\n// UpdateCheck checks if configuration needs updating\n// UpdateCheck 检查配置是否需要更新\nfunc (s *settingService) UpdateCheck(ctx context.Context, uid int64, params *dto.SettingUpdateCheckRequest) (string, *dto.SettingDTO, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn \"\", nil, err\n\t}\n\n\tsetting, _ := s.settingRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\tif setting != nil {\n\t\tsettingDTO := s.domainToDTO(setting)\n\n\t\t// Check if setting is deleted\n\t\t// 检查设置是否已删除\n\t\tif setting.Action == domain.SettingActionDelete {\n\t\t\treturn \"Create\", nil, nil\n\t\t}\n\n\t\t// Check if content is consistent\n\t\t// 检查内容是否一致\n\t\tif setting.ContentHash == params.ContentHash {\n\t\t\t// Notify user to update mtime when user mtime is less than server mtime\n\t\t\t// 当用户 mtime 小于服务端 mtime 时，通知用户更新 mtime\n\t\t\tif params.Mtime < setting.Mtime {\n\t\t\t\treturn \"UpdateMtime\", settingDTO, nil\n\t\t\t} else if params.Mtime > setting.Mtime {\n\t\t\t\tif err := s.settingRepo.UpdateMtime(ctx, params.Mtime, setting.ID, uid); err != nil {\n\t\t\t\t\t// Non-critical update failed, log warning but do not block flow\n\t\t\t\t\t// 非关键更新失败，记录警告日志但不阻断流程\n\t\t\t\t\tzap.L().Warn(\"UpdateMtime failed for setting\",\n\t\t\t\t\t\tzap.Int64(logger.FieldUID, uid),\n\t\t\t\t\t\tzap.Int64(\"settingId\", setting.ID),\n\t\t\t\t\t\tzap.Int64(\"mtime\", params.Mtime),\n\t\t\t\t\t\tzap.String(logger.FieldMethod, \"SettingService.UpdateCheck\"),\n\t\t\t\t\t\tzap.Error(err),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn \"\", settingDTO, nil\n\t\t}\n\t\treturn \"UpdateContent\", settingDTO, nil\n\t}\n\treturn \"Create\", nil, nil\n}\n\n// ModifyCheck checks configuration modification (alias for UpdateCheck)\n// ModifyCheck 检查配置修改（UpdateCheck 的别名）\nfunc (s *settingService) ModifyCheck(ctx context.Context, uid int64, params *dto.SettingUpdateCheckRequest) (string, *dto.SettingDTO, error) {\n\treturn s.UpdateCheck(ctx, uid, params)\n}\n\n// ModifyOrCreate creates or modifies configuration\n// ModifyOrCreate 创建或修改配置\nfunc (s *settingService) ModifyOrCreate(ctx context.Context, uid int64, params *dto.SettingModifyOrCreateRequest, mtimeCheck bool) (bool, *dto.SettingDTO, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn false, nil, err\n\t}\n\n\tkey := fmt.Sprintf(\"modify_or_create_%d_%d_%s\", uid, vaultID, params.PathHash)\n\ttype result struct {\n\t\tisNew bool\n\t\tdto   *dto.SettingDTO\n\t}\n\n\tval, err, _ := s.sf.Do(key, func() (any, error) {\n\t\tsetting, _ := s.settingRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\n\t\tif setting != nil {\n\t\t\t// Check if content is consistent, excluding settings marked as deleted\n\t\t\t// 检查内容是否一致,排除掉已被标记删除的设置\n\t\t\tif mtimeCheck && setting.Action != domain.SettingActionDelete && setting.Mtime == params.Mtime && setting.ContentHash == params.ContentHash {\n\t\t\t\treturn &result{isNew: false, dto: s.domainToDTO(setting)}, nil\n\t\t\t}\n\t\t\t// If content is consistent but modification time is different, only update modification time\n\t\t\t// 检查内容是否一致但修改时间不同，则只更新修改时间\n\t\t\tif mtimeCheck && setting.Mtime < params.Mtime && setting.ContentHash == params.ContentHash {\n\t\t\t\terr := s.settingRepo.UpdateActionMtime(ctx, domain.SettingActionModify, params.Mtime, setting.ID, uid)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t\t\t}\n\t\t\t\tsetting.Mtime = params.Mtime\n\t\t\t\t// Log mtime-only update // 记录仅 mtime 变更日志\n\t\t\t\tif s.syncLogService != nil {\n\t\t\t\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeSetting, domain.SyncLogActionModify, \"mtime\", setting.Path, setting.PathHash, s.clientType, s.clientName, s.clientVer, int64(len(setting.Content)))\n\t\t\t\t}\n\t\t\t\treturn &result{isNew: false, dto: s.domainToDTO(setting)}, nil\n\t\t\t}\n\n\t\t\t// Set action\n\t\t\t// 设置 action\n\t\t\tvar action domain.SettingAction\n\t\t\tif setting.Action == domain.SettingActionDelete {\n\t\t\t\taction = domain.SettingActionCreate\n\t\t\t} else {\n\t\t\t\taction = domain.SettingActionModify\n\t\t\t}\n\n\t\t\t// Update configuration\n\t\t\t// 更新配置\n\t\t\tsetting.VaultID = vaultID\n\t\t\tsetting.Path = params.Path\n\t\t\tsetting.PathHash = params.PathHash\n\t\t\tsetting.Content = params.Content\n\t\t\tsetting.ContentHash = params.ContentHash\n\t\t\tsetting.Size = int64(len(params.Content))\n\t\t\tsetting.Mtime = params.Mtime\n\t\t\tsetting.Ctime = params.Ctime\n\t\t\tsetting.Action = action\n\n\t\t\tupdated, err := s.settingRepo.Update(ctx, setting, uid)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t\t}\n\n\t\t\t// Log content modify // 记录内容变更日志\n\t\t\tif s.syncLogService != nil {\n\t\t\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeSetting, domain.SyncLogActionModify, \"content,mtime\", updated.Path, updated.PathHash, s.clientType, s.clientName, s.clientVer, updated.Size)\n\t\t\t}\n\n\t\t\treturn &result{isNew: false, dto: s.domainToDTO(updated)}, nil\n\t\t}\n\n\t\t// Create new configuration\n\t\t// 创建新配置\n\t\tnewSetting := &domain.Setting{\n\t\t\tVaultID:     vaultID,\n\t\t\tPath:        params.Path,\n\t\t\tPathHash:    params.PathHash,\n\t\t\tContent:     params.Content,\n\t\t\tContentHash: params.ContentHash,\n\t\t\tSize:        int64(len(params.Content)),\n\t\t\tMtime:       params.Mtime,\n\t\t\tCtime:       params.Ctime,\n\t\t\tAction:      domain.SettingActionCreate,\n\t\t}\n\n\t\tcreated, err := s.settingRepo.Create(ctx, newSetting, uid)\n\t\tif err != nil {\n\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t}\n\n\t\t// Log create // 记录新建日志\n\t\tif s.syncLogService != nil {\n\t\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeSetting, domain.SyncLogActionCreate, \"\", created.Path, created.PathHash, s.clientType, s.clientName, s.clientVer, created.Size)\n\t\t}\n\n\t\treturn &result{isNew: true, dto: s.domainToDTO(created)}, nil\n\t})\n\n\tif err != nil {\n\t\treturn false, nil, err\n\t}\n\n\tres := val.(*result)\n\treturn res.isNew, res.dto, nil\n}\n\n// Modify modifies configuration (alias for ModifyOrCreate)\n// Modify 修改配置（ModifyOrCreate 的别名）\nfunc (s *settingService) Modify(ctx context.Context, uid int64, params *dto.SettingModifyOrCreateRequest) (bool, *dto.SettingDTO, error) {\n\treturn s.ModifyOrCreate(ctx, uid, params, true)\n}\n\n// Delete deletes configuration\n// Delete 删除配置\nfunc (s *settingService) Delete(ctx context.Context, uid int64, params *dto.SettingDeleteRequest) (*dto.SettingDTO, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsetting, err := s.settingRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorSettingNotFound\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Update to deleted status\n\t// 更新为删除状态\n\tsetting.Action = domain.SettingActionDelete\n\tsetting.Content = \"\"\n\tsetting.ContentHash = \"\"\n\tsetting.Size = 0\n\n\tupdated, err := s.settingRepo.Update(ctx, setting, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Log soft delete // 记录软删除日志\n\tif s.syncLogService != nil {\n\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeSetting, domain.SyncLogActionSoftDelete, \"\", setting.Path, setting.PathHash, s.clientType, s.clientName, s.clientVer, 0)\n\t}\n\n\treturn s.domainToDTO(updated), nil\n}\n\n// Get retrieves a single configuration\n// Get 获取单条配置\nfunc (s *settingService) Get(ctx context.Context, uid int64, params *dto.SettingGetRequest) (*dto.SettingDTO, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsetting, err := s.settingRepo.GetByPathHash(ctx, params.PathHash, vaultID, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorSettingNotFound\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\treturn s.domainToDTO(setting), nil\n}\n\n// ListByLastTime retrieves configurations updated after lastTime\n// ListByLastTime 获取在 lastTime 之后更新的配置\nfunc (s *settingService) ListByLastTime(ctx context.Context, uid int64, params *dto.SettingSyncRequest) ([]*dto.SettingDTO, error) {\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsettings, err := s.settingRepo.ListByUpdatedTimestamp(ctx, params.LastTime, vaultID, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tvar results []*dto.SettingDTO\n\tcacheList := make(map[string]bool)\n\tfor _, setting := range settings {\n\t\tif cacheList[setting.PathHash] {\n\t\t\tcontinue\n\t\t}\n\t\tresults = append(results, s.domainToDTO(setting))\n\t\tcacheList[setting.PathHash] = true\n\t}\n\n\treturn results, nil\n}\n\n// Sync synchronizes configuration (alias for ListByLastTime)\n// Sync 同步配置（ListByLastTime 的别名）\nfunc (s *settingService) Sync(ctx context.Context, uid int64, params *dto.SettingSyncRequest) ([]*dto.SettingDTO, error) {\n\treturn s.ListByLastTime(ctx, uid, params)\n}\n\n// List retrieves configurations with pagination\n// List 分页获取配置列表\nfunc (s *settingService) List(ctx context.Context, uid int64, params *dto.SettingListRequest, pager *pkgapp.Pager) ([]*dto.SettingDTO, int64, error) {\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\ttotal, err := s.settingRepo.ListCount(ctx, vaultID, uid, params.Keyword)\n\tif err != nil {\n\t\treturn nil, 0, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tsettings, err := s.settingRepo.List(ctx, vaultID, pager.Page, pager.PageSize, uid, params.Keyword)\n\tif err != nil {\n\t\treturn nil, 0, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tvar results []*dto.SettingDTO\n\tfor _, setting := range settings {\n\t\tresults = append(results, s.domainToDTO(setting))\n\t}\n\n\treturn results, total, nil\n}\n\nfunc (s *settingService) Rename(ctx context.Context, uid int64, params *dto.SettingRenameRequest) (*dto.SettingDTO, error) {\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, params.Vault)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// 1. Find the old setting\n\tn, err := s.settingRepo.GetByPathHash(ctx, params.OldPathHash, vaultID, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorSettingNotFound\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// 2. Check if new path already exists\n\texistSetting, _ := s.settingRepo.GetByPathHash(ctx, params.NewPathHash, vaultID, uid)\n\tif existSetting != nil && existSetting.Action != domain.SettingActionDelete {\n\t\treturn nil, code.ErrorSettingExist\n\t}\n\n\t// 3. Mark old setting as deleted with rename flag\n\tn.Action = domain.SettingActionDelete\n\tn.Rename = 1\n\tn.UpdatedTimestamp = timex.Now().UnixMilli()\n\t_, err = s.settingRepo.Update(ctx, n, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// 4. Create new or reuse setting record\n\tvar newSettingCreated *domain.Setting\n\tif existSetting != nil {\n\t\texistSetting.Action = domain.SettingActionModify\n\t\texistSetting.Path = params.NewPath\n\t\texistSetting.PathHash = params.NewPathHash\n\t\texistSetting.Content = n.Content\n\t\texistSetting.ContentHash = n.ContentHash\n\t\texistSetting.Size = n.Size\n\t\texistSetting.Ctime = n.Ctime\n\t\texistSetting.Mtime = n.Mtime // Preserve original mtime\n\t\texistSetting.Rename = 0\n\t\texistSetting.UpdatedTimestamp = timex.Now().UnixMilli()\n\t\tnewSettingCreated, err = s.settingRepo.Update(ctx, existSetting, uid)\n\t} else {\n\t\tnewSetting := &domain.Setting{\n\t\t\tVaultID:          vaultID,\n\t\t\tAction:           domain.SettingActionCreate,\n\t\t\tPath:             params.NewPath,\n\t\t\tPathHash:         params.NewPathHash,\n\t\t\tContent:          n.Content,\n\t\t\tContentHash:      n.ContentHash,\n\t\t\tSize:             n.Size,\n\t\t\tCtime:            n.Ctime,\n\t\t\tMtime:            n.Mtime, // Preserve original mtime\n\t\t\tRename:           0,\n\t\t\tUpdatedTimestamp: timex.Now().UnixMilli(),\n\t\t}\n\t\tnewSettingCreated, err = s.settingRepo.Create(ctx, newSetting, uid)\n\t}\n\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Log rename // 记录重命名日志\n\tif s.syncLogService != nil {\n\t\ts.syncLogService.Log(uid, vaultID, domain.SyncLogTypeSetting, domain.SyncLogActionRename, \"path\", newSettingCreated.Path, newSettingCreated.PathHash, s.clientType, s.clientName, s.clientVer, newSettingCreated.Size)\n\t}\n\n\treturn s.domainToDTO(newSettingCreated), nil\n}\n\n// Cleanup cleans up expired soft-deleted configurations\n// Cleanup 清理过期的软删除配置\nfunc (s *settingService) Cleanup(ctx context.Context, uid int64) error {\n\tif s.config == nil {\n\t\treturn nil\n\t}\n\tretentionTimeStr := s.config.App.SoftDeleteRetentionTime\n\tif retentionTimeStr == \"\" || retentionTimeStr == \"0\" {\n\t\treturn nil\n\t}\n\n\tretentionDuration, err := util.ParseDuration(retentionTimeStr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif retentionDuration <= 0 {\n\t\treturn nil\n\t}\n\n\tcutoffTime := time.Now().Add(-retentionDuration).UnixMilli()\n\treturn s.settingRepo.DeletePhysicalByTime(ctx, cutoffTime, uid)\n}\n\n// CleanupByTime cleans up expired soft-deleted configurations for all users by cutoff time\n// CleanupByTime 按截止时间清理所有用户的过期软删除配置\nfunc (s *settingService) CleanupByTime(ctx context.Context, cutoffTime int64) error {\n\treturn s.settingRepo.DeletePhysicalByTimeAll(ctx, cutoffTime)\n}\n\n// ClearByVault clears all settings for a specific vault of a user\n// ClearByVault 清除用户指定笔记本的所有配置\nfunc (s *settingService) ClearByVault(ctx context.Context, uid int64, vaultName string) error {\n\t// Use VaultService.MustGetID to retrieve VaultID\n\t// 使用 VaultService.MustGetID 获取 VaultID\n\tvaultID, err := s.vaultService.MustGetID(ctx, uid, vaultName)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn s.settingRepo.DeleteByVaultID(ctx, vaultID, uid)\n}\n\n// CleanDuplicateSettings cleans up duplicate configuration records\n// CleanDuplicateSettings 清理重复的配置记录\nfunc (s *settingService) CleanDuplicateSettings(ctx context.Context, uid int64, vaultID int64) error {\n\t// Get all configurations (including deleted ones for global deduplication)\n\t// 获取所有配置（包含已删除，以便全局去重）\n\tsettings, err := s.settingRepo.ListByUpdatedTimestamp(ctx, 0, vaultID, uid)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Group by PathHash // 按 PathHash 分组\n\tgrouped := make(map[string][]*domain.Setting)\n\tfor _, s_ := range settings {\n\t\tgrouped[s_.PathHash] = append(grouped[s_.PathHash], s_)\n\t}\n\n\tfor pathHash, list := range grouped {\n\t\tif len(list) <= 1 {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Retention rules:\n\t\t// 1. Prioritize retaining records with Action != delete\n\t\t// 2. If multiple active records exist, keep the one with the largest (latest) UpdatedTimestamp\n\t\t// 3. If timestamps are identical, keep the record with the largest ID\n\t\t// 保留规则：\n\t\t// 1. 优先保留 Action != delete 的记录\n\t\t// 2. 如果有多个活跃记录，保留 UpdatedTimestamp 最大（最新）的一条\n\t\t// 3. 如果时间戳一致，保留 ID 最大的记录\n\n\t\tvar bestSetting *domain.Setting\n\t\tfor _, s_ := range list {\n\t\t\tif bestSetting == nil {\n\t\t\t\tbestSetting = s_\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// 比较逻辑\n\t\t\tisBetter := false\n\t\t\tif s_.Action != domain.SettingActionDelete && bestSetting.Action == domain.SettingActionDelete {\n\t\t\t\tisBetter = true\n\t\t\t} else if s_.Action == bestSetting.Action {\n\t\t\t\tif s_.UpdatedTimestamp > bestSetting.UpdatedTimestamp {\n\t\t\t\t\tisBetter = true\n\t\t\t\t} else if s_.UpdatedTimestamp == bestSetting.UpdatedTimestamp && s_.ID > bestSetting.ID {\n\t\t\t\t\tisBetter = true\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif isBetter {\n\t\t\t\tbestSetting = s_\n\t\t\t}\n\t\t}\n\n\t\t// Delete all records except the bestSetting\n\t\t// 删除非 bestSetting 的所有记录\n\t\tfor _, s_ := range list {\n\t\t\tif s_.ID != bestSetting.ID {\n\t\t\t\t// Clear singleflight cache to prevent residue\n\t\t\t\t// 清除 singleflight 缓存，防止残留\n\t\t\t\ts.sf.Forget(fmt.Sprintf(\"modify_or_create_%d_%d_%s\", uid, vaultID, pathHash))\n\t\t\t\t\n\t\t\t\t_ = s.settingRepo.Delete(ctx, s_.ID, uid)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Verify settingService implements SettingService interface\n// 确保 settingService 实现了 SettingService 接口\nvar _ SettingService = (*settingService)(nil)\n"
  },
  {
    "path": "internal/service/share_service.go",
    "content": "// Package service implements the business logic layer\n// Package service 实现业务逻辑层\npackage service\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"mime\"\n\t\"net/http\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/shortlink\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"go.uber.org/zap\"\n)\n\n// attachmentRegex regular expression for attachment links\n// attachmentRegex 附件链接正则表达式\nvar (\n\tattachmentRegex    = regexp.MustCompile(`!\\[\\[(.*?)\\]\\]`)\n\t// markdownImageRegex regular expression for markdown images\n\t// markdownImageRegex markdown 图片正则表达式\n\tmarkdownImageRegex = regexp.MustCompile(`!\\[([^\\]]*)\\]\\(([^)]+)\\)`)\n\t// htmlImageRegex regular expression for html images\n\t// htmlImageRegex html 图片正则表达式\n\thtmlImageRegex     = regexp.MustCompile(`(?i)<img\\b([^>]*?)\\bsrc\\s*=\\s*(['\"])(.*?)['\"]([^>]*)>`)\n)\n\n// ShareService defines the share business service interface\n// ShareService 定义分享业务服务接口\ntype ShareService interface {\n\t// ShareGenerate generates and stores share token\n\t// ShareGenerate 生成并存储分享 Token\n\tShareGenerate(ctx context.Context, uid int64, vaultName string, path string, pathHash string, password string) (*dto.ShareCreateResponse, error)\n\n\t// VerifyShare verifies share token and its status\n\t// VerifyShare 验证分享 Token 及其状态\n\tVerifyShare(ctx context.Context, token string, rid string, rtp string, password string) (*pkgapp.ShareEntity, error)\n\n\t// GetSharedNote retrieves shared note details\n\t// GetSharedNote 获取分享的单条笔记详情\n\tGetSharedNote(ctx context.Context, shareToken string, noteID int64, password string) (*dto.NoteDTO, error)\n\n\t// GetSharedFile retrieves shared file content\n\t// GetSharedFile 获取分享的文件内容\n\tGetSharedFile(ctx context.Context, shareToken string, fileID int64, password string) (content []byte, contentType string, mtime int64, etag string, fileName string, err error)\n\n\t// GetSharedFileInfo retrieves shared file metadata and path for zero-copy download\n\t// GetSharedFileInfo 获取分享文件的元数据和路径，用于零拷贝下载\n\tGetSharedFileInfo(ctx context.Context, shareToken string, fileID int64, password string) (savePath string, contentType string, mtime int64, etag string, fileName string, err error)\n\n\t// RecordView aggregates access statistics in memory\n\t// RecordView 在内存中聚合访问统计\n\tRecordView(uid int64, id int64)\n\n\t// StopShare revokes a share\n\t// StopShare 撤销分享\n\tStopShare(ctx context.Context, uid int64, id int64) error\n\n\t// UpdateSharePassword updates password for a share based on resource info\n\t// UpdateSharePassword 根据资源信息更新分享密码\n\tUpdateSharePassword(ctx context.Context, uid int64, vaultName string, path string, pathHash string, password string) error\n\n\t// CreateShortLink generates a short link for a share\n\t// CreateShortLink 为分享生成短链\n\tCreateShortLink(ctx context.Context, uid int64, vaultName string, path string, pathHash string, baseURL string, longURL string, isForce bool) (string, error)\n\n\t// ListShares lists all shares of a user with sorting and pagination\n\t// ListShares 列出用户的所有分享（支持排序和分页）\n\tListShares(ctx context.Context, uid int64, sortBy string, sortOrder string, pager *pkgapp.Pager) ([]*dto.ShareListItem, int, error)\n\n\t// GetShareByPath retrieves share info by path\n\t// GetShareByPath 根据路径获取分享信息\n\tGetShareByPath(ctx context.Context, uid int64, vaultName string, pathHash string) (*domain.UserShare, error)\n\n\t// StopShareByPath revokes a share by path\n\t// StopShareByPath 根据路径撤销分享\n\tStopShareByPath(ctx context.Context, uid int64, vaultName string, pathHash string) error\n\n\t// GetActiveNotePathsByVault returns active shared note paths for a vault\n\t// GetActiveNotePathsByVault 返回指定 vault 下所有有效分享的笔记路径列表\n\tGetActiveNotePathsByVault(ctx context.Context, uid int64, vaultName string) ([]string, error)\n\n\t// Shutdown shuts down the service and flushes remaining data\n\t// Shutdown 关闭服务并同步最后的数据\n\tShutdown(ctx context.Context) error\n}\n\n// aggStats aggregated statistics\n// aggStats 聚合统计\ntype aggStats struct {\n\tuid          int64     // User ID // 用户 ID\n\tviewCount    int64     // View count // 访问计数\n\tlastViewedAt time.Time // Last viewed at // 最后访问时间\n}\n\n// shareService implementation of ShareService interface\n// shareService 实现 ShareService 接口\ntype shareService struct {\n\trepo         domain.UserShareRepository // Share repository // 分享仓库\n\ttokenManager pkgapp.TokenManager        // Token manager // Token 管理器\n\tnoteRepo     domain.NoteRepository      // Note repository // 笔记仓库\n\tfileRepo     domain.FileRepository      // File repository // 文件仓库\n\tvaultRepo    domain.VaultRepository     // Vault repository // 仓库仓库\n\tlogger       *zap.Logger                // Logger // 日志器\n\tconfig       *ServiceConfig             // Service configuration // 服务配置\n\n\t// Statistics buffer\n\t// 统计缓冲区\n\tbufferMu    sync.Mutex          // Buffer mutex // 缓冲区互斥锁\n\tstatsBuffer map[int64]*aggStats // Stats buffer // 统计缓冲区\n\tticker      *time.Ticker        // Sync ticker // 同步定时器\n\tstopCh      chan struct{}       // Stop channel // 停止信号\n\tdoneCh      chan struct{}       // Done channel // 完成信号\n}\n\n// NewShareService creates ShareService instance\n// NewShareService 创建 ShareService 实例\nfunc NewShareService(repo domain.UserShareRepository, tokenManager pkgapp.TokenManager, noteRepo domain.NoteRepository, fileRepo domain.FileRepository, vaultRepo domain.VaultRepository, logger *zap.Logger, config *ServiceConfig) ShareService {\n\ts := &shareService{\n\t\trepo:         repo,\n\t\ttokenManager: tokenManager,\n\t\tnoteRepo:     noteRepo,\n\t\tfileRepo:     fileRepo,\n\t\tvaultRepo:    vaultRepo,\n\t\tlogger:       logger,\n\t\tconfig:       config,\n\t\tstatsBuffer:  make(map[int64]*aggStats),\n\t\tticker:       time.NewTicker(5 * time.Minute),\n\t\tstopCh:       make(chan struct{}),\n\t\tdoneCh:       make(chan struct{}),\n\t}\n\n\tgo s.startFlushLoop()\n\n\treturn s\n}\n\n// ShareGenerate generates and stores share token\n// ShareGenerate 生成并存储分享 Token\nfunc (s *shareService) ShareGenerate(ctx context.Context, uid int64, vaultName string, path string, pathHash string, password string) (*dto.ShareCreateResponse, error) {\n\t// 1. Get VaultID\n\t// 1. 获取 VaultID\n\tvault, err := s.vaultRepo.GetByName(ctx, vaultName, uid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvaultID := vault.ID\n\n\tvar resolvedResources = make(map[string][]string)\n\tvar mainID int64\n\tvar mainType string\n\n\t// 2. Determine type based on suffix\n\t// 2. 根据后缀判定类型\n\tisNote := strings.HasSuffix(strings.ToLower(path), \".md\")\n\n\tif isNote {\n\t\t// Try looking up as Note\n\t\t// 尝试作为 Note 查找\n\t\tnote, err := s.noteRepo.GetByPathHash(ctx, pathHash, vaultID, uid)\n\t\tif err == nil && note != nil && note.Action != domain.NoteActionDelete {\n\t\t\tmainID = note.ID\n\t\t\tmainType = \"note\"\n\t\t\tnoteIDStr := strconv.FormatInt(note.ID, 10)\n\t\t\tresolvedResources[\"note\"] = []string{noteIDStr}\n\t\t\tfileRefs, err := s.resolveSharedNoteFiles(ctx, uid, note.VaultID, note.Path, note.Content)\n\t\t\tif err != nil {\n\t\t\t\ts.logger.Warn(\"ShareGenerate resolveSharedNoteFiles failed\", zap.Error(err), zap.String(\"notePath\", note.Path))\n\t\t\t} else {\n\t\t\t\tfor _, file := range fileRefs {\n\t\t\t\t\tfileIDStr := strconv.FormatInt(file.ID, 10)\n\t\t\t// Avoid duplicate authorization\n\t\t\t// 避免重复授权\n\t\t\tif !util.Inarray(resolvedResources[\"file\"], fileIDStr) {\n\t\t\t\t\t\tresolvedResources[\"file\"] = append(resolvedResources[\"file\"], fileIDStr)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\treturn nil, code.ErrorNoteNotFound.WithDetails(\"note not found: \" + path)\n\t\t}\n\t} else {\n\t\t// Try looking up as File\n\t\t// 尝试作为 File 查找\n\t\tfile, err := s.fileRepo.GetByPathHash(ctx, pathHash, vaultID, uid)\n\t\tif err == nil && file != nil && file.Action != domain.FileActionDelete {\n\t\t\tmainID = file.ID\n\t\t\tmainType = \"file\"\n\t\t\tfileIDStr := strconv.FormatInt(file.ID, 10)\n\t\t\tresolvedResources[\"file\"] = []string{fileIDStr}\n\t\t} else {\n\t\t\treturn nil, code.ErrorFileNotFound.WithDetails(\"file not found: \" + path)\n\t\t}\n\t}\n\n\t// 3. Determine expiration time\n\t// 3. 确定过期时间\n\texpiry := 30 * 24 * time.Hour // Default 30 days // 默认 30 天\n\tif s.config != nil && s.config.App.ShareTokenExpiry != \"\" {\n\t\tif d, err := util.ParseDuration(s.config.App.ShareTokenExpiry); err == nil {\n\t\t\texpiry = d\n\t\t}\n\t}\n\t// expiresAt expiration time\n\t// expiresAt 过期时间\n\texpiresAt := time.Now().Add(expiry)\n\n\t// pwdMd5 password MD5\n\t// pwdMd5 密码 MD5\n\tpwdMd5 := \"\"\n\tif password != \"\" {\n\t\tpwdMd5 = util.EncodeMD5(password)\n\t}\n\n\tshare := &domain.UserShare{\n\t\tUID:       uid,\n\t\tResType:   mainType,\n\t\tResID:     mainID,\n\t\tResources: resolvedResources,\n\t\tStatus:    1,\n\t\tExpiresAt: expiresAt,\n\t\tPassword:  pwdMd5,\n\t\tCreatedAt: time.Now(),\n\t\tUpdatedAt: time.Now(),\n\t}\n\n\t// Idempotent: revoke any existing active share before creating a new one\n\t// 幂等：若该资源已有 active 分享，先撤销，避免重复计数\n\tif existing, err := s.repo.GetByRes(ctx, uid, mainType, mainID); err == nil && existing != nil {\n\t\t_ = s.StopShare(ctx, uid, existing.ID)\n\t}\n\n\tif err := s.repo.Create(ctx, uid, share); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// 4. Generate Token (using underlying SID encryption scheme)\n\t// 4. 生成 Token (使用底层 SID 加密方案)\n\ttoken, err := s.tokenManager.ShareGenerate(share.ID, uid, resolvedResources)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &dto.ShareCreateResponse{\n\t\tID:         mainID,\n\t\tType:       mainType,\n\t\tToken:      token,\n\t\tIsPassword: pwdMd5 != \"\",\n\t\tExpiresAt:  expiresAt,\n\t\tShortLink:  share.ShortLink,\n\t}, nil\n}\n\n// VerifyShare verifies share token and its status\n// VerifyShare 验证分享 Token 及其状态\nfunc (s *shareService) VerifyShare(ctx context.Context, token string, rid string, rtp string, password string) (*pkgapp.ShareEntity, error) {\n\tentity, err := s.tokenManager.ShareParse(token)\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tshare, err := s.repo.GetByID(ctx, entity.UID, entity.SID)\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif share.Status != 1 {\n\t\treturn nil, domain.ErrShareCancelled\n\t}\n\n\t// 添加限速延迟防止暴力枚举攻击\n\ttime.Sleep(time.Millisecond * 100)\n\n\t// Add password verification logic\n\t// 增加密码校验逻辑\n\tif share.Password != \"\" {\n\t\tif password == \"\" {\n\t\t\treturn nil, domain.ErrSharePasswordRequired\n\t\t}\n\t\tif util.EncodeMD5(password) != share.Password {\n\t\t\treturn nil, domain.ErrSharePasswordInvalid\n\t\t}\n\t}\n\n\tentity.Resources = share.Resources\n\n\tids, ok := share.Resources[rtp]\n\tif !ok {\n\t\treturn nil, domain.ErrShareCancelled // Match type mismatch // 资源类型不匹配\n\t}\n\n\tauthorized := false\n\tfor _, id := range ids {\n\t\tif id == rid {\n\t\t\tauthorized = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif !authorized {\n\t\treturn nil, domain.ErrShareCancelled // Resource not authorized // 资源未授权\n\t}\n\n\t// In-memory record access statistics (delayed 5 minutes update)\n\t// 内存记录访问统计 (延迟 5 分钟更新)\n\ts.RecordView(share.UID, share.ID)\n\n\treturn entity, nil\n}\n\n// RecordView aggregates access statistics in memory\n// RecordView 在内存中聚合访问统计\nfunc (s *shareService) RecordView(uid int64, id int64) {\n\ts.bufferMu.Lock()\n\tdefer s.bufferMu.Unlock()\n\n\tstats, ok := s.statsBuffer[id]\n\tif !ok {\n\t\tstats = &aggStats{\n\t\t\tuid: uid,\n\t\t}\n\t\ts.statsBuffer[id] = stats\n\t}\n\tstats.viewCount++\n\tstats.lastViewedAt = time.Now()\n}\n\n// startFlushLoop starts periodic synchronization goroutine\n// startFlushLoop 启动定时同步协程\nfunc (s *shareService) startFlushLoop() {\n\tdefer close(s.doneCh)\n\tfor {\n\t\tselect {\n\t\tcase <-s.ticker.C:\n\t\t\ts.flush()\n\t\tcase <-s.stopCh:\n\t\t\ts.flush()\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// flush synchronizes incremental totals in memory to database\n// flush 将内存中的增量合计同步到数据库\nfunc (s *shareService) flush() {\n\ts.bufferMu.Lock()\n\tif len(s.statsBuffer) == 0 {\n\t\ts.bufferMu.Unlock()\n\t\treturn\n\t}\n\ttempBuffer := s.statsBuffer\n\ts.statsBuffer = make(map[int64]*aggStats)\n\ts.bufferMu.Unlock()\n\n\tctx := context.Background()\n\tfor id, stats := range tempBuffer {\n\t\tif err := s.repo.UpdateViewStats(ctx, stats.uid, id, stats.viewCount, stats.lastViewedAt); err != nil {\n\t\t\ts.logger.Error(\"failed to flush user_share stats\", zap.Int64(\"id\", id), zap.Error(err))\n\t\t}\n\t}\n}\n\n// StopShare revokes a share\n// StopShare 撤销分享\nfunc (s *shareService) StopShare(ctx context.Context, uid int64, id int64) error {\n\treturn s.repo.UpdateStatus(ctx, uid, id, domain.UserShareStatusRevoked)\n}\n\n// UpdateSharePassword updates password for a share\n// UpdateSharePassword 更新分享密码\nfunc (s *shareService) UpdateSharePassword(ctx context.Context, uid int64, vaultName string, path string, pathHash string, password string) error {\n\t// 1. Get VaultID\n\tvault, err := s.vaultRepo.GetByName(ctx, vaultName, uid)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif vault == nil {\n\t\treturn code.ErrorVaultNotFound\n\t}\n\n\t// 2. Get UserShare by resource\n\tshare, err := s.repo.GetByPath(ctx, uid, vault.ID, pathHash)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif share == nil {\n\t\treturn code.ErrorShareNotFound\n\t}\n\n\tpwdMd5 := \"\"\n\tif password != \"\" {\n\t\tpwdMd5 = util.EncodeMD5(password)\n\t}\n\treturn s.repo.UpdatePassword(ctx, uid, share.ID, pwdMd5)\n}\n\n// ListShares lists all shares of a user with sorting, pagination and fills in resource titles\n// ListShares 列出用户的所有分享，支持排序、分页，并填充资源标题\nfunc (s *shareService) ListShares(ctx context.Context, uid int64, sortBy string, sortOrder string, pager *pkgapp.Pager) ([]*dto.ShareListItem, int, error) {\n\t// 1. 获取总数\n\tcount64, err := s.repo.CountByUID(ctx, uid)\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\tcount := int(count64)\n\n\tif count == 0 {\n\t\treturn []*dto.ShareListItem{}, 0, nil\n\t}\n\n\t// 2. 获取分页数据\n\tshares, err := s.repo.ListByUID(ctx, uid, sortBy, sortOrder, pkgapp.GetPageOffset(pager.Page, pager.PageSize), pager.PageSize)\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\titems := make([]*dto.ShareListItem, 0, len(shares))\n\n\t// Collect noteIDs and fileIDs in bulk to avoid N+1 queries\n\t// 批量收集 noteIDs 和 fileIDs，避免 N+1 查询\n\tvar noteIDs, fileIDs []int64\n\tfor _, share := range shares {\n\t\tswitch share.ResType {\n\t\tcase \"note\":\n\t\t\tnoteIDs = append(noteIDs, share.ResID)\n\t\tcase \"file\":\n\t\t\tfileIDs = append(fileIDs, share.ResID)\n\t\t}\n\t}\n\n\t// Batch query notes and build id→note map\n\t// 批量查询 notes，建立 id→note 映射\n\tnoteMap := make(map[int64]*domain.Note)\n\tif len(noteIDs) > 0 {\n\t\tnotes, err := s.noteRepo.ListByIDs(ctx, noteIDs, uid)\n\t\tif err != nil {\n\t\t\ts.logger.Warn(\"ListShares: batch query notes failed\", zap.Error(err))\n\t\t} else {\n\t\t\tfor _, n := range notes {\n\t\t\t\tnoteMap[n.ID] = n\n\t\t\t}\n\t\t}\n\t}\n\n\tfileMap := make(map[int64]*domain.File)\n\tif len(fileIDs) > 0 {\n\t\tfiles, err := s.fileRepo.ListByIDs(ctx, fileIDs, uid)\n\t\tif err != nil {\n\t\t\ts.logger.Warn(\"ListShares: batch query files failed\", zap.Error(err))\n\t\t} else {\n\t\t\tfor _, f := range files {\n\t\t\t\tfileMap[f.ID] = f\n\t\t\t}\n\t\t}\n\t}\n\n\t// Query vault names on demand with local cache (vault count is always small)\n\t// 查询 vault 名称（vault 数量极少，按需缓存）\n\tvaultNameCache := make(map[int64]string)\n\n\tfor _, share := range shares {\n\t\titem := &dto.ShareListItem{\n\t\t\tID:           share.ID,\n\t\t\tUID:          share.UID,\n\t\t\tResources:    share.Resources,\n\t\t\tStatus:       share.Status,\n\t\t\tViewCount:    share.ViewCount,\n\t\t\tLastViewedAt: share.LastViewedAt,\n\t\t\tExpiresAt:    share.ExpiresAt,\n\t\t\tShortLink:    share.ShortLink,\n\t\t\tCreatedAt:    share.CreatedAt,\n\t\t\tUpdatedAt:    share.UpdatedAt,\n\t\t\tIsPassword:   share.Password != \"\",\n\t\t}\n\n\t\t// Generate Token and concatenate URL /ResID/token\n\t\t// 生成 Token 并拼接 URL /ResID/token\n\t\ttoken, err := s.tokenManager.ShareGenerate(share.ID, uid, share.Resources)\n\t\tif err == nil {\n\t\t\titem.URL = \"/share/\" + strconv.FormatInt(share.ResID, 10) + \"/\" + token\n\t\t}\n\n\t\t// Fill title from preloaded maps, no extra DB queries\n\t\t// 从预加载的 map 中回填标题，无额外查询\n\t\tswitch share.ResType {\n\t\tcase \"note\":\n\t\t\tif note, ok := noteMap[share.ResID]; ok && note.Action != domain.NoteActionDelete {\n\t\t\t\titem.Title = strings.TrimSuffix(filepath.Base(note.Path), \".md\")\n\t\t\t\titem.NotePath = note.Path\n\t\t\t\tif name, ok := vaultNameCache[note.VaultID]; ok {\n\t\t\t\t\titem.VaultName = name\n\t\t\t\t} else if v, err := s.vaultRepo.GetByID(ctx, note.VaultID, uid); err == nil && v != nil {\n\t\t\t\t\tvaultNameCache[note.VaultID] = v.Name\n\t\t\t\t\titem.VaultName = v.Name\n\t\t\t\t}\n\t\t\t}\n\t\tcase \"file\":\n\t\t\tif file, ok := fileMap[share.ResID]; ok {\n\t\t\t\titem.Title = filepath.Base(file.Path)\n\t\t\t}\n\t\t}\n\n\t\titems = append(items, item)\n\t}\n\n\treturn items, count, nil\n}\n\n// GetShareByPath retrieves share info by path\n// GetShareByPath 根据路径获取分享信息\nfunc (s *shareService) GetShareByPath(ctx context.Context, uid int64, vaultName string, pathHash string) (*domain.UserShare, error) {\n\tvault, err := s.vaultRepo.GetByName(ctx, vaultName, uid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tresID := int64(0)\n\tresType := \"\"\n\n\t// Check if it's a note or file\n\tnote, err := s.noteRepo.GetByPathHash(ctx, pathHash, vault.ID, uid)\n\tif err == nil && note != nil {\n\t\tresID = note.ID\n\t\tresType = \"note\"\n\t} else {\n\t\tfile, err := s.fileRepo.GetByPathHash(ctx, pathHash, vault.ID, uid)\n\t\tif err == nil && file != nil {\n\t\t\tresID = file.ID\n\t\t\tresType = \"file\"\n\t\t}\n\t}\n\n\tif resID == 0 {\n\t\treturn nil, code.ErrorFileNotFound\n\t}\n\n\t// Use precision index query instead of iterating list\n\t// 使用精确索引查询，替代遍历列表\n\tshare, err := s.repo.GetByRes(ctx, uid, resType, resID)\n\tif err != nil {\n\t\treturn nil, code.ErrorFileNotFound // No active share found\n\t}\n\n\treturn share, nil\n}\n\n// CreateShortLink generates a short link for a share record\n// CreateShortLink 为分享记录生成短链\nfunc (s *shareService) CreateShortLink(ctx context.Context, uid int64, vaultName string, path string, pathHash string, baseURL string, longURL string, isForce bool) (string, error) {\n\t// Find vault first to get ID\n\tvault, err := s.vaultRepo.GetByName(ctx, vaultName, uid)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif vault == nil {\n\t\treturn \"\", code.ErrorVaultNotFound\n\t}\n\n\t// Find existing share record by path using vault.ID\n\tshare, err := s.repo.GetByPath(ctx, uid, vault.ID, pathHash)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif share == nil {\n\t\treturn \"\", code.ErrorShareNotFound\n\t}\n\n\t// If short link already exists and not forcing regeneration, return it\n\tif !isForce && share.ShortLink != \"\" {\n\t\treturn share.ShortLink, nil\n\t}\n\n\t// Prepare short link creation parameters from service config\n\tsinkBaseURL := s.config.App.ShortLink.BaseURL\n\tapiKey := s.config.App.ShortLink.APIKey\n\tpassword := s.config.App.ShortLink.Password\n\tcloaking := s.config.App.ShortLink.Cloaking\n\n\t// expiration matches the share record\n\texpiresAt := share.ExpiresAt\n\n\t// Use client-provided URL if available; otherwise fall back to generating one\n\t// 优先使用客户端传入的完整分享 URL，避免因重新生成 token 导致 URL 不一致\n\tif longURL == \"\" {\n\t\ttoken, err := s.tokenManager.ShareGenerate(share.ID, uid, share.Resources)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tlongURL = fmt.Sprintf(\"%s/share/%d/%s\", strings.TrimRight(baseURL, \"/\"), share.ResID, token)\n\t}\n\n\tclient := shortlink.NewSinkCoolClient(sinkBaseURL, apiKey)\n\n\ttitle := \"\"\n\tswitch share.ResType {\n\tcase \"note\":\n\t\tif note, err := s.noteRepo.GetByID(ctx, share.ResID, uid); err == nil && note != nil {\n\t\t\ttitle = strings.TrimSuffix(filepath.Base(note.Path), \".md\")\n\t\t}\n\tcase \"file\":\n\t\tif file, err := s.fileRepo.GetByID(ctx, share.ResID, uid); err == nil && file != nil {\n\t\t\ttitle = filepath.Base(file.Path)\n\t\t}\n\t}\n\n\tshortURL, err := client.Create(longURL, expiresAt, password, cloaking, title)\n\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\t// Save the generated short link back to database\n\tif err := s.repo.UpdateShortLink(ctx, uid, share.ID, shortURL); err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn shortURL, nil\n}\n\n// StopShareByPath revokes a share by path\n// StopShareByPath 根据路径撤销分享\nfunc (s *shareService) StopShareByPath(ctx context.Context, uid int64, vaultName string, pathHash string) error {\n\tshare, err := s.GetShareByPath(ctx, uid, vaultName, pathHash)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn s.StopShare(ctx, uid, share.ID)\n}\n\n// GetActiveNotePathsByVault returns active shared note paths for a vault\n// GetActiveNotePathsByVault 返回指定 vault 下所有有效分享的笔记路径（两步查询，避免跨库 JOIN）\nfunc (s *shareService) GetActiveNotePathsByVault(ctx context.Context, uid int64, vaultName string) ([]string, error) {\n\tvault, err := s.vaultRepo.GetByName(ctx, vaultName, uid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Step 1: query active note res_ids from user_shares DB (no cross-DB JOIN)\n\t// 步骤1：从 user_shares 库查出有效分享的 note res_id 列表（不做跨库 JOIN）\n\tnoteIDs, err := s.repo.ListActiveNoteResIDs(ctx, uid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(noteIDs) == 0 {\n\t\treturn []string{}, nil\n\t}\n\n\t// Step 2: batch query notes from notes DB, filter by vault and non-deleted action\n\t// 步骤2：从 notes 库批量查笔记，按 vault 和非删除状态过滤\n\tnotes, err := s.noteRepo.ListByIDs(ctx, noteIDs, uid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpaths := make([]string, 0, len(notes))\n\tfor _, n := range notes {\n\t\tif n.VaultID == vault.ID && n.Action != domain.NoteActionDelete {\n\t\t\tpaths = append(paths, n.Path)\n\t\t}\n\t}\n\treturn paths, nil\n}\n\n// GetSharedNote retrieves specific shared note details\n// GetSharedNote 获取分享的单条笔记详情\nfunc (s *shareService) GetSharedNote(ctx context.Context, shareToken string, noteID int64, password string) (*dto.NoteDTO, error) {\n\tridStr := strconv.FormatInt(noteID, 10)\n\tshareEntity, err := s.VerifyShare(ctx, shareToken, ridStr, \"note\", password)\n\tif err != nil {\n\t\tif cObj, ok := err.(*code.Code); ok {\n\t\t\treturn nil, cObj\n\t\t}\n\t\treturn nil, code.ErrorInvalidAuthToken.WithDetails(err.Error())\n\t}\n\n\t// Retrieve note directly via ID (using resource owner's UID)\n\t// 直接通过 ID 获取笔记 (使用资源所有者的 UID)\n\tnote, err := s.noteRepo.GetByID(ctx, noteID, shareEntity.UID)\n\tif err != nil {\n\t\treturn nil, code.ErrorNoteNotFound\n\t}\n\n\tnoteDTO := &dto.NoteDTO{\n\t\tID:               note.ID,\n\t\tPath:             note.Path,\n\t\tPathHash:         note.PathHash,\n\t\tContent:          note.Content,\n\t\tContentHash:      note.ContentHash,\n\t\tVersion:          note.Version,\n\t\tCtime:            note.Ctime,\n\t\tMtime:            note.Mtime,\n\t\tUpdatedTimestamp: note.UpdatedTimestamp,\n\t\tUpdatedAt:        timex.Time(note.UpdatedAt),\n\t\tCreatedAt:        timex.Time(note.CreatedAt),\n\t}\n\n\tfileRefs, err := s.resolveSharedNoteFiles(ctx, shareEntity.UID, note.VaultID, note.Path, noteDTO.Content)\n\tif err != nil {\n\t\ts.logger.Warn(\"GetSharedNote resolveSharedNoteFiles failed\", zap.Error(err), zap.String(\"notePath\", note.Path))\n\t}\n\n\tif len(fileRefs) > 0 {\n\t\tupdatedResources, changed := mergeShareFileResources(shareEntity.Resources, fileRefs)\n\t\tif changed {\n\t\t\tif err := s.repo.UpdateResources(ctx, shareEntity.UID, shareEntity.SID, updatedResources); err != nil {\n\t\t\t\ts.logger.Warn(\"GetSharedNote UpdateResources failed\", zap.Error(err), zap.Int64(\"shareID\", shareEntity.SID))\n\t\t\t} else {\n\t\t\t\tshareEntity.Resources = updatedResources\n\t\t\t}\n\t\t}\n\t}\n\n\t// Handle Obsidian attachment embedded tags ![[...]]\n\t// 处理 Obsidian 附件嵌入标签 ![[...]]\n\tnewContent := attachmentRegex.ReplaceAllStringFunc(noteDTO.Content, func(match string) string {\n\t\tsubmatches := attachmentRegex.FindStringSubmatch(match)\n\t\tif len(submatches) < 2 {\n\t\t\treturn match\n\t\t}\n\n\t\tinner := submatches[1]\n\t\trawPath := inner\n\t\toptions := \"\"\n\n\t\t// Extract resource path (remove part after alias | and anchor #)\n\t\t// 提取资源路径（移除别名 | 和锚点 # 之后的部分）\n\t\tif idx := strings.IndexAny(inner, \"|#\"); idx != -1 {\n\t\t\trawPath = inner[:idx]\n\t\t\tif inner[idx] == '|' {\n\t\t\t\toptions = strings.TrimSpace(inner[idx+1:])\n\t\t\t}\n\t\t}\n\t\trawPath = strings.TrimSpace(rawPath)\n\t\tif rawPath == \"\" {\n\t\t\treturn match\n\t\t}\n\n\t\tfile := fileRefs[rawPath]\n\t\tif file == nil {\n\t\t\treturn match\n\t\t}\n\n\t\tapiUrl := buildSharedFileAPIURL(file.ID, shareToken, password)\n\t\tlowerPath := strings.ToLower(file.Path)\n\t\text := filepath.Ext(lowerPath)\n\n\t\tisImage := strings.Contains(\".png.jpg.jpeg.gif.svg.webp.bmp\", ext) && ext != \"\"\n\t\tisVideo := strings.Contains(\".mp4.webm.ogg.mov\", ext) && ext != \"\"\n\t\tisAudio := strings.Contains(\".mp3.wav.ogg.m4a.flac\", ext) && ext != \"\"\n\n\t\tif isImage {\n\t\t\twidth := \"\"\n\t\t\theight := \"\"\n\t\t\tif options != \"\" {\n\t\t\t\tsizeRe := regexp.MustCompile(`^(\\d+)(?:x(\\d+))?`)\n\t\t\t\tsizeMatch := sizeRe.FindStringSubmatch(options)\n\t\t\t\tif len(sizeMatch) > 1 && sizeMatch[1] != \"\" {\n\t\t\t\t\twidth = ` width=\"` + sizeMatch[1] + `\"`\n\t\t\t\t}\n\t\t\t\tif len(sizeMatch) > 2 && sizeMatch[2] != \"\" {\n\t\t\t\t\theight = ` height=\"` + sizeMatch[2] + `\"`\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn `<img src=\"` + apiUrl + `\" alt=\"` + rawPath + `\"` + width + height + ` />`\n\t\t} else if isVideo {\n\t\t\treturn `<video src=\"` + apiUrl + `\" controls style=\"max-width:100%\"></video>`\n\t\t} else if isAudio {\n\t\t\treturn `<audio src=\"` + apiUrl + `\" controls></audio>`\n\t\t} else {\n\t\t\treturn `<a href=\"` + apiUrl + `\" target=\"_blank\">📎 ` + rawPath + `</a>`\n\t\t}\n\t})\n\tnewContent = rewriteMarkdownImageLinks(newContent, fileRefs, shareToken, password)\n\tnewContent = rewriteHTMLImageSources(newContent, fileRefs, shareToken, password)\n\tnoteDTO.Content = newContent\n\n\treturn noteDTO, nil\n}\n\nfunc (s *shareService) resolveSharedNoteFiles(ctx context.Context, uid int64, vaultID int64, notePath string, content string) (map[string]*domain.File, error) {\n\trawRefs := extractSharedNoteFileRefs(content)\n\tif len(rawRefs) == 0 {\n\t\treturn map[string]*domain.File{}, nil\n\t}\n\n\tresult := make(map[string]*domain.File, len(rawRefs))\n\tfor _, rawRef := range rawRefs {\n\t\tfile, err := s.resolveSharedFileReference(ctx, uid, vaultID, notePath, rawRef)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif file != nil {\n\t\t\tresult[rawRef] = file\n\t\t}\n\t}\n\treturn result, nil\n}\n\nfunc (s *shareService) resolveSharedFileReference(ctx context.Context, uid int64, vaultID int64, notePath string, rawRef string) (*domain.File, error) {\n\tref := strings.TrimSpace(rawRef)\n\tif !isLocalSharePath(ref) {\n\t\treturn nil, nil\n\t}\n\n\tfor _, candidate := range buildSharePathCandidates(notePath, ref) {\n\t\tfile, err := s.fileRepo.GetByPath(ctx, candidate, vaultID, uid)\n\t\tif err == nil && file != nil && file.Action != domain.FileActionDelete {\n\t\t\treturn file, nil\n\t\t}\n\t}\n\n\tnormalizedRef := normalizeShareVaultPath(ref)\n\tif normalizedRef != \"\" && !strings.Contains(normalizedRef, \"/\") {\n\t\tfile, err := s.fileRepo.GetByPathLike(ctx, normalizedRef, vaultID, uid)\n\t\tif err == nil && file != nil && file.Action != domain.FileActionDelete {\n\t\t\treturn file, nil\n\t\t}\n\t}\n\n\treturn nil, nil\n}\n\nfunc extractSharedNoteFileRefs(content string) []string {\n\tseen := make(map[string]struct{})\n\trefs := make([]string, 0)\n\n\tfor _, match := range attachmentRegex.FindAllStringSubmatch(content, -1) {\n\t\tif len(match) < 2 {\n\t\t\tcontinue\n\t\t}\n\t\tref := extractObsidianEmbedPath(match[1])\n\t\tif ref == \"\" || !isLocalSharePath(ref) {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := seen[ref]; ok {\n\t\t\tcontinue\n\t\t}\n\t\tseen[ref] = struct{}{}\n\t\trefs = append(refs, ref)\n\t}\n\n\tfor _, match := range markdownImageRegex.FindAllStringSubmatch(content, -1) {\n\t\tif len(match) < 3 {\n\t\t\tcontinue\n\t\t}\n\t\tref, _, _ := parseMarkdownLinkTarget(match[2])\n\t\tref = strings.TrimSpace(ref)\n\t\tif ref == \"\" || !isLocalSharePath(ref) {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := seen[ref]; ok {\n\t\t\tcontinue\n\t\t}\n\t\tseen[ref] = struct{}{}\n\t\trefs = append(refs, ref)\n\t}\n\n\tfor _, match := range htmlImageRegex.FindAllStringSubmatch(content, -1) {\n\t\tif len(match) < 4 {\n\t\t\tcontinue\n\t\t}\n\t\tref := strings.TrimSpace(match[3])\n\t\tif ref == \"\" || !isLocalSharePath(ref) {\n\t\t\tcontinue\n\t\t}\n\t\tif _, ok := seen[ref]; ok {\n\t\t\tcontinue\n\t\t}\n\t\tseen[ref] = struct{}{}\n\t\trefs = append(refs, ref)\n\t}\n\n\treturn refs\n}\n\nfunc extractObsidianEmbedPath(inner string) string {\n\trawPath := inner\n\tif idx := strings.IndexAny(inner, \"|#\"); idx != -1 {\n\t\trawPath = inner[:idx]\n\t}\n\treturn strings.TrimSpace(rawPath)\n}\n\nfunc parseMarkdownLinkTarget(raw string) (target string, start int, end int) {\n\tstart = 0\n\tfor start < len(raw) {\n\t\tswitch raw[start] {\n\t\tcase ' ', '\\t', '\\n':\n\t\t\tstart++\n\t\tdefault:\n\t\t\tgoto targetStart\n\t\t}\n\t}\n\treturn \"\", -1, -1\n\ntargetStart:\n\tif raw[start] == '<' {\n\t\tend = strings.IndexByte(raw[start+1:], '>')\n\t\tif end == -1 {\n\t\t\treturn \"\", -1, -1\n\t\t}\n\t\tend += start + 2\n\t\treturn raw[start+1 : end-1], start, end\n\t}\n\n\tend = len(raw)\n\tescaped := false\n\tfor i := start; i < len(raw); i++ {\n\t\tif escaped {\n\t\t\tescaped = false\n\t\t\tcontinue\n\t\t}\n\t\tswitch raw[i] {\n\t\tcase '\\\\':\n\t\t\tescaped = true\n\t\tcase ' ', '\\t', '\\n':\n\t\t\tend = i\n\t\t\treturn raw[start:end], start, end\n\t\t}\n\t}\n\n\treturn raw[start:end], start, end\n}\n\nfunc rewriteMarkdownImageLinks(content string, fileRefs map[string]*domain.File, shareToken string, password string) string {\n\treturn markdownImageRegex.ReplaceAllStringFunc(content, func(match string) string {\n\t\tsubmatches := markdownImageRegex.FindStringSubmatch(match)\n\t\tif len(submatches) < 3 {\n\t\t\treturn match\n\t\t}\n\n\t\ttarget, start, end := parseMarkdownLinkTarget(submatches[2])\n\t\tif target == \"\" || start < 0 || end < 0 {\n\t\t\treturn match\n\t\t}\n\n\t\tfile := fileRefs[strings.TrimSpace(target)]\n\t\tif file == nil {\n\t\t\treturn match\n\t\t}\n\n\t\treplacementTarget := buildSharedFileAPIURL(file.ID, shareToken, password)\n\t\trawTarget := submatches[2][start:end]\n\t\tif strings.HasPrefix(rawTarget, \"<\") && strings.HasSuffix(rawTarget, \">\") {\n\t\t\treplacementTarget = \"<\" + replacementTarget + \">\"\n\t\t}\n\n\t\treturn \"![\" + submatches[1] + \"](\" + submatches[2][:start] + replacementTarget + submatches[2][end:] + \")\"\n\t})\n}\n\nfunc rewriteHTMLImageSources(content string, fileRefs map[string]*domain.File, shareToken string, password string) string {\n\treturn htmlImageRegex.ReplaceAllStringFunc(content, func(match string) string {\n\t\tsubmatches := htmlImageRegex.FindStringSubmatch(match)\n\t\tif len(submatches) < 5 {\n\t\t\treturn match\n\t\t}\n\n\t\tfile := fileRefs[strings.TrimSpace(submatches[3])]\n\t\tif file == nil {\n\t\t\treturn match\n\t\t}\n\n\t\treturn \"<img\" + submatches[1] + \"src=\" + submatches[2] + buildSharedFileAPIURL(file.ID, shareToken, password) + submatches[2] + submatches[4] + \">\"\n\t})\n}\n\nfunc buildSharedFileAPIURL(fileID int64, shareToken string, password string) string {\n\tapiURL := \"/api/share/file?id=\" + strconv.FormatInt(fileID, 10) + \"&share_token=\" + shareToken\n\tif password != \"\" {\n\t\tapiURL += \"&password=\" + password\n\t}\n\treturn apiURL\n}\n\nfunc mergeShareFileResources(resources map[string][]string, fileRefs map[string]*domain.File) (map[string][]string, bool) {\n\tmerged := cloneShareResources(resources)\n\tallowed := make(map[string]struct{}, len(merged[\"file\"]))\n\tfor _, id := range merged[\"file\"] {\n\t\tallowed[id] = struct{}{}\n\t}\n\n\tchanged := false\n\tfor _, file := range fileRefs {\n\t\tid := strconv.FormatInt(file.ID, 10)\n\t\tif _, ok := allowed[id]; ok {\n\t\t\tcontinue\n\t\t}\n\t\tmerged[\"file\"] = append(merged[\"file\"], id)\n\t\tallowed[id] = struct{}{}\n\t\tchanged = true\n\t}\n\n\tif changed {\n\t\tsort.Strings(merged[\"file\"])\n\t}\n\n\treturn merged, changed\n}\n\nfunc cloneShareResources(resources map[string][]string) map[string][]string {\n\tcloned := make(map[string][]string, len(resources))\n\tfor key, values := range resources {\n\t\tcloned[key] = append([]string(nil), values...)\n\t}\n\treturn cloned\n}\n\nfunc buildSharePathCandidates(notePath string, rawRef string) []string {\n\tref := strings.TrimSpace(strings.ReplaceAll(rawRef, \"\\\\\", \"/\"))\n\tif ref == \"\" || !isLocalSharePath(ref) {\n\t\treturn nil\n\t}\n\n\tcandidates := make([]string, 0, 2)\n\taddCandidate := func(candidate string) {\n\t\tcandidate = normalizeShareVaultPath(candidate)\n\t\tif candidate == \"\" {\n\t\t\treturn\n\t\t}\n\t\tfor _, existing := range candidates {\n\t\t\tif existing == candidate {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tcandidates = append(candidates, candidate)\n\t}\n\n\tnoteDir := normalizeShareVaultPath(path.Dir(strings.ReplaceAll(notePath, \"\\\\\", \"/\")))\n\tif noteDir == \".\" {\n\t\tnoteDir = \"\"\n\t}\n\tif noteDir != \"\" {\n\t\taddCandidate(path.Join(noteDir, ref))\n\t} else {\n\t\taddCandidate(ref)\n\t}\n\tif strings.Contains(ref, \"/\") && !strings.HasPrefix(ref, \"./\") && !strings.HasPrefix(ref, \"../\") {\n\t\taddCandidate(ref)\n\t}\n\n\treturn candidates\n}\n\nfunc normalizeShareVaultPath(p string) string {\n\tp = strings.TrimSpace(strings.ReplaceAll(p, \"\\\\\", \"/\"))\n\tp = strings.TrimPrefix(p, \"./\")\n\tp = strings.TrimPrefix(p, \"/\")\n\tif p == \"\" {\n\t\treturn \"\"\n\t}\n\n\tcleaned := path.Clean(p)\n\tif cleaned == \".\" || cleaned == \"/\" || strings.HasPrefix(cleaned, \"../\") {\n\t\treturn \"\"\n\t}\n\treturn strings.TrimPrefix(cleaned, \"/\")\n}\n\nfunc isLocalSharePath(ref string) bool {\n\tref = strings.TrimSpace(ref)\n\tif ref == \"\" {\n\t\treturn false\n\t}\n\n\tlowerRef := strings.ToLower(ref)\n\tswitch {\n\tcase strings.HasPrefix(ref, \"#\"):\n\t\treturn false\n\tcase strings.HasPrefix(ref, \"/\"):\n\t\treturn false\n\tcase strings.HasPrefix(lowerRef, \"http://\"):\n\t\treturn false\n\tcase strings.HasPrefix(lowerRef, \"https://\"):\n\t\treturn false\n\tcase strings.HasPrefix(lowerRef, \"//\"):\n\t\treturn false\n\tcase strings.HasPrefix(lowerRef, \"data:\"):\n\t\treturn false\n\tcase strings.HasPrefix(lowerRef, \"mailto:\"):\n\t\treturn false\n\tcase strings.HasPrefix(lowerRef, \"tel:\"):\n\t\treturn false\n\tcase strings.HasPrefix(lowerRef, \"obsidian://\"):\n\t\treturn false\n\tdefault:\n\t\treturn true\n\t}\n}\n\n// GetSharedFile retrieves shared file content\n// GetSharedFile 获取分享的文件内容\nfunc (s *shareService) GetSharedFile(ctx context.Context, shareToken string, fileID int64, password string) (content []byte, contentType string, mtime int64, etag string, fileName string, err error) {\n\tridStr := strconv.FormatInt(fileID, 10)\n\tshareEntity, err := s.VerifyShare(ctx, shareToken, ridStr, \"file\", password)\n\tif err != nil {\n\t\tif cObj, ok := err.(*code.Code); ok {\n\t\t\treturn nil, \"\", 0, \"\", \"\", cObj\n\t\t}\n\t\treturn nil, \"\", 0, \"\", \"\", code.ErrorInvalidAuthToken.WithDetails(err.Error())\n\t}\n\n\t// 1. Get resource owner's UID\n\t// 1. 获取资源所有者的 UID\n\townerUID := shareEntity.UID\n\n\t// 2. Confirm path hash (get file metadata from fileRepo)\n\t// 2. 确认路径哈希 (从 fileRepo 获取文件元数据)\n\tfile, err := s.fileRepo.GetByID(ctx, fileID, ownerUID)\n\tif err != nil {\n\t\treturn nil, \"\", 0, \"\", \"\", code.ErrorFileNotFound\n\t}\n\n\tif file.Action == domain.FileActionDelete {\n\t\treturn nil, \"\", 0, \"\", \"\", code.ErrorFileNotFound\n\t}\n\n\t// Read physical file content\n\t// 读取物理文件内容\n\tcontent, err = os.ReadFile(file.SavePath)\n\tif err != nil {\n\t\treturn nil, \"\", 0, \"\", \"\", code.ErrorFileReadFailed.WithDetails(err.Error())\n\t}\n\n\t// Identify file MIME type\n\t// 识别文件 MIME 类型\n\text := filepath.Ext(file.Path)\n\tcontentType = mime.TypeByExtension(ext)\n\tif contentType == \"\" {\n\t\t// If extension cannot be identified, perform content sniffing\n\t\t// 如果扩展名识别不到, 进行内容嗅探\n\t\tcontentType = http.DetectContentType(content)\n\t}\n\n\t// Compute etag in real-time using byte-based hash for consistency with binary files\n\t// 使用基于字节的哈希实时计算 etag，确保与二进制文件一致\n\tetag = util.EncodeHash32Bytes(content)\n\n\treturn content, contentType, file.Mtime, etag, file.Path, nil\n\n}\n\n// GetSharedFileInfo retrieves shared file metadata and path for zero-copy download\n// GetSharedFileInfo 获取分享文件的元数据和路径，用于零拷贝下载\nfunc (s *shareService) GetSharedFileInfo(ctx context.Context, shareToken string, fileID int64, password string) (savePath string, contentType string, mtime int64, etag string, fileName string, err error) {\n\tridStr := strconv.FormatInt(fileID, 10)\n\tshareEntity, err := s.VerifyShare(ctx, shareToken, ridStr, \"file\", password)\n\tif err != nil {\n\t\tif cObj, ok := err.(*code.Code); ok {\n\t\t\treturn \"\", \"\", 0, \"\", \"\", cObj\n\t\t}\n\t\treturn \"\", \"\", 0, \"\", \"\", code.ErrorInvalidAuthToken.WithDetails(err.Error())\n\t}\n\n\t// 1. Get resource owner's UID\n\townerUID := shareEntity.UID\n\n\t// 2. Confirm path hash (get file metadata from fileRepo)\n\tfile, err := s.fileRepo.GetByID(ctx, fileID, ownerUID)\n\tif err != nil {\n\t\treturn \"\", \"\", 0, \"\", \"\", code.ErrorFileNotFound\n\t}\n\n\tif file.Action == domain.FileActionDelete {\n\t\treturn \"\", \"\", 0, \"\", \"\", code.ErrorFileNotFound\n\t}\n\n\t// Identify file MIME type\n\text := filepath.Ext(file.Path)\n\tcontentType = mime.TypeByExtension(ext)\n\tif contentType == \"\" {\n\t\tcontentType = \"application/octet-stream\"\n\t}\n\n\t// Use file's content hash as ETag\n\tetag = file.ContentHash\n\tif etag == \"\" {\n\t\tetag = file.PathHash\n\t}\n\n\treturn file.SavePath, contentType, file.Mtime, etag, filepath.Base(file.Path), nil\n}\n\n// Shutdown shuts down the service and flushes remaining data\n// Shutdown 关闭服务并同步最后的数据\nfunc (s *shareService) Shutdown(ctx context.Context) error {\n\ts.ticker.Stop()\n\tclose(s.stopCh)\n\n\t// Wait for periodic synchronization goroutine to end (i.e., last flush completed)\n\t// 等待定时同步协程结束（即最后一次 flush 完成）\n\tselect {\n\tcase <-s.doneCh:\n\t\ts.logger.Info(\"ShareService background flush loop stopped\")\n\t\treturn nil\n\tcase <-ctx.Done():\n\t\ts.logger.Warn(\"ShareService shutdown timeout, some data might not be flushed\")\n\t\treturn ctx.Err()\n\t}\n}\n"
  },
  {
    "path": "internal/service/share_service_test.go",
    "content": "// Package service implements the business logic layer.\n// Package service 实现业务逻辑层。\npackage service\n\nimport (\n\t\"testing\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\n// TestExtractSharedNoteFileRefs verifies that all image/file refs in markdown content are extracted.\n// TestExtractSharedNoteFileRefs 验证 markdown 内容中所有图片/文件引用都被正确提取。\nfunc TestExtractSharedNoteFileRefs(t *testing.T) {\n\tcontent := `\n![[assets/photo.png|240]]\n![inline](../images/demo.jpg \"title\")\n<img src=\"./img/html.png\" alt=\"demo\">\n`\n\trefs := extractSharedNoteFileRefs(content)\n\n\texpected := map[string]struct{}{\n\t\t\"assets/photo.png\":   {},\n\t\t\"../images/demo.jpg\": {},\n\t\t\"./img/html.png\":     {},\n\t}\n\n\tassert.Len(t, refs, len(expected), \"should extract exactly %d file refs\", len(expected))\n\n\tfor _, ref := range refs {\n\t\t_, ok := expected[ref]\n\t\tassert.True(t, ok, \"unexpected ref extracted: %s\", ref)\n\t}\n}\n\n// TestBuildSharePathCandidates verifies that relative image paths are resolved correctly.\n// TestBuildSharePathCandidates 验证相对图片路径被正确解析为候选路径。\nfunc TestBuildSharePathCandidates(t *testing.T) {\n\tcandidates := buildSharePathCandidates(\"notes/daily/today.md\", \"../images/demo.png\")\n\texpected := []string{\"notes/images/demo.png\"}\n\n\tassert.Equal(t, expected, candidates, \"resolved path candidates should match\")\n}\n\n// TestRewriteMarkdownImageLinks verifies that markdown image links are rewritten to share URLs.\n// TestRewriteMarkdownImageLinks 验证 markdown 图片链接被重写为分享 URL。\nfunc TestRewriteMarkdownImageLinks(t *testing.T) {\n\tcontent := `![demo](./images/demo.png \"title\")`\n\tfileRefs := map[string]*domain.File{\n\t\t\"./images/demo.png\": {ID: 42},\n\t}\n\n\trewritten := rewriteMarkdownImageLinks(content, fileRefs, \"share-token\", \"pwd\")\n\texpected := `![demo](/api/share/file?id=42&share_token=share-token&password=pwd \"title\")`\n\n\tassert.Equal(t, expected, rewritten, \"image links should be rewritten to share API URLs\")\n}\n"
  },
  {
    "path": "internal/service/storage_service.go",
    "content": "package service\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/config\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/storage\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"gorm.io/gorm\"\n)\n\n// StorageService defines the business service interface for Storage\n// StorageService 定义存储业务服务接口\ntype StorageService interface {\n\t// CreateOrUpdate creates or updates a storage configuration\n\t// CreateOrUpdate 创建或更新存储配置\n\tCreateOrUpdate(ctx context.Context, uid int64, id int64, storageDTO *dto.StoragePostRequest) (*dto.StorageDTO, error)\n\n\t// Get retrieves storage configuration by ID\n\t// Get 获取存储配置\n\tGet(ctx context.Context, uid int64, id int64) (*dto.StorageDTO, error)\n\n\t// List retrieves storage configuration list for current user\n\t// List 获取当前用户的存储配置列表\n\tList(ctx context.Context, uid int64) ([]*dto.StorageDTO, error)\n\n\t// Delete deletes storage configuration\n\t// Delete 删除存储配置\n\tDelete(ctx context.Context, uid int64, id int64) error\n\n\t// GetEnabledTypes returns list of enabled storage types\n\t// GetEnabledTypes 获取启用的存储类型列表\n\tGetEnabledTypes() ([]string, error)\n\n\t// Validate verifies storage connectivity by sending and deleting a test file\n\t// Validate 通过发送测试文件并删除来验证存储连通性\n\tValidate(ctx context.Context, req *dto.StoragePostRequest) error\n}\n\ntype storageService struct {\n\trepo   domain.StorageRepository\n\tconfig *config.StorageConfig\n}\n\n// NewStorageService creates StorageService instance\n// NewStorageService 创建 StorageService 实例\nfunc NewStorageService(repo domain.StorageRepository, config *config.StorageConfig) StorageService {\n\treturn &storageService{repo: repo, config: config}\n}\n\nfunc (s *storageService) domainToDTO(m *domain.Storage) *dto.StorageDTO {\n\tif m == nil {\n\t\treturn nil\n\t}\n\treturn &dto.StorageDTO{\n\t\tID:              m.ID,\n\t\tUID:             m.UID,\n\t\tType:            m.Type,\n\t\tEndpoint:        m.Endpoint,\n\t\tRegion:          m.Region,\n\t\tAccountID:       m.AccountID,\n\t\tBucketName:      m.BucketName,\n\t\tAccessKeyID:     m.AccessKeyID,\n\t\tAccessKeySecret: m.AccessKeySecret,\n\t\tCustomPath:      m.CustomPath,\n\t\tAccessURLPrefix: m.AccessURLPrefix,\n\t\tUser:            m.User,\n\t\tPassword:        m.Password,\n\t\tIsEnabled:       m.IsEnabled,\n\t\tIsDeleted:       m.IsDeleted,\n\t\tCreatedAt:       timex.Time(m.CreatedAt),\n\t\tUpdatedAt:       timex.Time(m.UpdatedAt),\n\t}\n}\n\nfunc (s *storageService) dtoToDomain(d *dto.StorageDTO) *domain.Storage {\n\tif d == nil {\n\t\treturn nil\n\t}\n\treturn &domain.Storage{\n\t\tID:              d.ID,\n\t\tUID:             d.UID,\n\t\tType:            d.Type,\n\t\tEndpoint:        d.Endpoint,\n\t\tRegion:          d.Region,\n\t\tAccountID:       d.AccountID,\n\t\tBucketName:      d.BucketName,\n\t\tAccessKeyID:     d.AccessKeyID,\n\t\tAccessKeySecret: d.AccessKeySecret,\n\t\tCustomPath:      d.CustomPath,\n\t\tAccessURLPrefix: d.AccessURLPrefix,\n\t\tUser:            d.User,\n\t\tPassword:        d.Password,\n\t\tIsEnabled:       d.IsEnabled,\n\t\tIsDeleted:       d.IsDeleted,\n\t}\n}\n\nfunc (s *storageService) postRequestToDomain(req *dto.StoragePostRequest) *domain.Storage {\n\tif req == nil {\n\t\treturn nil\n\t}\n\treturn &domain.Storage{\n\t\tID:              req.ID,\n\t\tType:            req.Type,\n\t\tEndpoint:        req.Endpoint,\n\t\tRegion:          req.Region,\n\t\tAccountID:       req.AccountID,\n\t\tBucketName:      req.BucketName,\n\t\tAccessKeyID:     req.AccessKeyID,\n\t\tAccessKeySecret: req.AccessKeySecret,\n\t\tCustomPath:      req.CustomPath,\n\t\tAccessURLPrefix: req.AccessURLPrefix,\n\t\tUser:            req.User,\n\t\tPassword:        req.Password,\n\t\tIsEnabled:       req.IsEnabled == 1,\n\t}\n}\n\nfunc (s *storageService) CreateOrUpdate(ctx context.Context, uid int64, id int64, req *dto.StoragePostRequest) (*dto.StorageDTO, error) {\n\t// Validate storage type availability\n\t// 验证存储类型可用性\n\ttypeName := req.Type\n\tif !s.isStorageTypeEnabled(typeName) {\n\t\treturn nil, code.ErrorStorageTypeDisabled\n\t}\n\n\tstorage := s.postRequestToDomain(req)\n\tstorage.UID = uid\n\n\tvar result *domain.Storage\n\tvar err error\n\n\tif id > 0 {\n\t\t// Update existing storage configuration\n\t\t// 更新现有存储配置\n\t\tstorage.ID = id\n\t\tresult, err = s.repo.Update(ctx, storage, uid)\n\t} else {\n\t\t// Create new storage configuration\n\t\t// 创建新存储配置\n\t\tresult, err = s.repo.Create(ctx, storage, uid)\n\t}\n\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorStorageNotFound\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\treturn s.domainToDTO(result), nil\n}\n\nfunc (s *storageService) Get(ctx context.Context, uid int64, id int64) (*dto.StorageDTO, error) {\n\tresult, err := s.repo.GetByID(ctx, id, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorStorageNotFound\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\treturn s.domainToDTO(result), nil\n}\n\nfunc (s *storageService) List(ctx context.Context, uid int64) ([]*dto.StorageDTO, error) {\n\tresults, err := s.repo.List(ctx, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tdtos := make([]*dto.StorageDTO, 0, len(results))\n\tfor _, r := range results {\n\t\tdtos = append(dtos, s.domainToDTO(r))\n\t}\n\treturn dtos, nil\n}\n\nfunc (s *storageService) Delete(ctx context.Context, uid int64, id int64) error {\n\terr := s.repo.Delete(ctx, id, uid)\n\tif err != nil {\n\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\treturn nil\n}\n\nfunc (s *storageService) GetEnabledTypes() ([]string, error) {\n\tvar types []string\n\tif s.config.LocalFS.IsEnabled {\n\t\ttypes = append(types, string(storage.LOCAL))\n\t}\n\tif s.config.AliyunOSS.IsEnabled {\n\t\ttypes = append(types, string(storage.OSS))\n\t}\n\tif s.config.AwsS3.IsEnabled {\n\t\ttypes = append(types, string(storage.S3))\n\t}\n\tif s.config.CloudflareR2.IsEnabled {\n\t\ttypes = append(types, string(storage.R2))\n\t}\n\tif s.config.MinIO.IsEnabled {\n\t\ttypes = append(types, string(storage.MinIO))\n\t}\n\tif s.config.WebDAV.IsEnabled {\n\t\ttypes = append(types, string(storage.WebDAV))\n\t}\n\treturn types, nil\n}\n\nfunc (s *storageService) isStorageTypeEnabled(t string) bool {\n\tswitch storage.Type(t) {\n\tcase storage.LOCAL:\n\t\treturn s.config.LocalFS.IsEnabled\n\tcase storage.OSS:\n\t\treturn s.config.AliyunOSS.IsEnabled\n\tcase storage.S3:\n\t\treturn s.config.AwsS3.IsEnabled\n\tcase storage.R2:\n\t\treturn s.config.CloudflareR2.IsEnabled\n\tcase storage.MinIO:\n\t\treturn s.config.MinIO.IsEnabled\n\tcase storage.WebDAV:\n\t\treturn s.config.WebDAV.IsEnabled\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (s *storageService) Validate(ctx context.Context, req *dto.StoragePostRequest) error {\n\tif !s.isStorageTypeEnabled(req.Type) {\n\t\treturn code.ErrorStorageTypeDisabled\n\t}\n\n\tsConfig := &storage.Config{\n\t\tType:            req.Type,\n\t\tCustomPath:      req.CustomPath,\n\t\tEndpoint:        req.Endpoint,\n\t\tRegion:          req.Region,\n\t\tBucketName:      req.BucketName,\n\t\tAccessKeyID:     req.AccessKeyID,\n\t\tAccessKeySecret: req.AccessKeySecret,\n\t\tAccountID:       req.AccountID,\n\t\tUser:            req.User,\n\t\tPassword:        req.Password,\n\t\tSavePath:        s.config.LocalFS.SavePath,\n\t}\n\n\tclient, err := storage.NewClient(sConfig)\n\tif err != nil {\n\t\treturn code.ErrorStorageValidateFailed.WithDetails(err.Error())\n\t}\n\n\t// Send test file\n\t// 发送测试文件\n\ttestFile := fmt.Sprintf(\".fast-note-test-%s\", uuid.New().String()[:8])\n\tif _, err := client.SendContent(testFile, []byte(\"ok\"), time.Now()); err != nil {\n\t\treturn code.ErrorStorageValidateFailed.WithDetails(err.Error())\n\t}\n\n\t// Delete test file\n\t// 删除测试文件\n\tif err := client.Delete(testFile); err != nil {\n\t\treturn code.ErrorStorageValidateFailed.WithDetails(err.Error())\n\t}\n\n\treturn nil\n}\n\nvar _ StorageService = (*storageService)(nil)\n"
  },
  {
    "path": "internal/service/sync_log_service.go",
    "content": "// Package service implements the business logic layer\n// Package service 实现业务逻辑层\npackage service\n\nimport (\n\t\"context\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"go.uber.org/zap\"\n)\n\n// SyncLogService defines the sync log business service interface\n// SyncLogService 定义同步日志业务服务接口\ntype SyncLogService interface {\n\t// Log asynchronously records a sync log entry, does not block the caller\n\t// Log 异步记录一条同步日志，不阻塞调用方\n\tLog(\n\t\tuid int64,\n\t\tvaultID int64,\n\t\tlogType domain.SyncLogType,\n\t\taction domain.SyncLogAction,\n\t\tchangedFields string, // e.g. \"content,mtime\" / \"mtime\" / \"path\" / \"\" // 如 \"content,mtime\" / \"mtime\" / \"path\" / \"\"\n\t\tpath string,\n\t\tpathHash string,\n\t\tclientType string,\n\t\tclientName string,\n\t\tclientVersion string,\n\t\tsize int64,\n\t)\n\n\t// List retrieves sync logs with pagination\n\t// List 分页查询同步日志\n\tList(ctx context.Context, uid int64, vaultID int64, logType, action string, page, pageSize int) ([]*dto.SyncLogDTO, int64, error)\n\n\t// CleanupByTime removes sync logs older than the given cutoff time for all users\n\t// CleanupByTime 清理所有用户在指定截止时间之前的同步日志\n\tCleanupByTime(ctx context.Context, cutoffTime int64) error\n}\n\n// syncLogService implements SyncLogService\n// syncLogService 实现 SyncLogService 接口\ntype syncLogService struct {\n\trepo domain.SyncLogRepository // Sync log repository // 同步日志仓储\n}\n\n// NewSyncLogService creates a new SyncLogService instance\n// NewSyncLogService 创建 SyncLogService 实例\nfunc NewSyncLogService(repo domain.SyncLogRepository) SyncLogService {\n\treturn &syncLogService{repo: repo}\n}\n\n// Log asynchronously records a sync log entry\n// Log 异步记录一条同步日志\nfunc (s *syncLogService) Log(\n\tuid int64,\n\tvaultID int64,\n\tlogType domain.SyncLogType,\n\taction domain.SyncLogAction,\n\tchangedFields string,\n\tpath string,\n\tpathHash string,\n\tclientType string,\n\tclientName string,\n\tclientVersion string,\n\tsize int64,\n) {\n\tgo func() {\n\t\t// Use Background context for asynchronous logging\n\t\t// 使用 Background context 进行异步日志记录\n\t\tctx := context.Background()\n\t\tentry := &domain.SyncLog{\n\t\t\tUID:           uid,\n\t\t\tVaultID:       vaultID,\n\t\t\tType:          logType,\n\t\t\tAction:        action,\n\t\t\tChangedFields: changedFields,\n\t\t\tPath:          path,\n\t\t\tPathHash:      pathHash,\n\t\t\tSize:           size,\n\t\t\tClientType:    clientType,\n\t\t\tClientName:    clientName,\n\t\t\tClientVersion: clientVersion,\n\t\t\tStatus:        1, // success // 成功\n\t\t\tCreatedAt:     timex.Now(),\n\t\t}\n\t\tif err := s.repo.Create(ctx, entry, uid); err != nil {\n\t\t\tzap.L().Warn(\"SyncLogService.Log: failed to create sync log\",\n\t\t\t\tzap.Int64(\"uid\", uid),\n\t\t\t\tzap.Int64(\"vaultID\", vaultID),\n\t\t\t\tzap.String(\"type\", string(logType)),\n\t\t\t\tzap.String(\"action\", string(action)),\n\t\t\t\tzap.String(\"path\", path),\n\t\t\t\tzap.Error(err),\n\t\t\t)\n\t\t}\n\t}()\n}\n\n// List retrieves sync logs with optional filters and pagination\n// List 按条件分页查询同步日志\nfunc (s *syncLogService) List(ctx context.Context, uid int64, vaultID int64, logType, action string, page, pageSize int) ([]*dto.SyncLogDTO, int64, error) {\n\tlogs, total, err := s.repo.List(ctx, uid, logType, action, page, pageSize)\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\tresult := make([]*dto.SyncLogDTO, 0, len(logs))\n\tfor _, l := range logs {\n\t\tif vaultID > 0 && l.VaultID != vaultID {\n\t\t\tcontinue\n\t\t}\n\t\tresult = append(result, s.domainToDTO(l))\n\t}\n\treturn result, total, nil\n}\n\n// CleanupByTime removes sync logs older than the given cutoff time for all users\n// CleanupByTime 清理所有用户在指定截止时间之前的同步日志\nfunc (s *syncLogService) CleanupByTime(ctx context.Context, cutoffTime int64) error {\n\treturn s.repo.CleanupByTimeAll(ctx, cutoffTime)\n}\n\n// domainToDTO converts domain SyncLog to DTO\n// domainToDTO 将领域模型转换为 DTO\nfunc (s *syncLogService) domainToDTO(l *domain.SyncLog) *dto.SyncLogDTO {\n\treturn &dto.SyncLogDTO{\n\t\tID:            l.ID,\n\t\tVaultID:       l.VaultID,\n\t\tType:          string(l.Type),\n\t\tAction:        string(l.Action),\n\t\tChangedFields: l.ChangedFields,\n\t\tPath:          l.Path,\n\t\tPathHash:      l.PathHash,\n\t\tSize:          l.Size,\n\t\tClientName:    l.ClientName,\n\t\tClientType:    l.ClientType,\n\t\tClientVersion: l.ClientVersion,\n\t\tStatus:        l.Status,\n\t\tMessage:       l.Message,\n\t\tCreatedAt:     l.CreatedAt,\n\t}\n}\n\n// Ensure syncLogService implements SyncLogService\n// 确保 syncLogService 实现了 SyncLogService 接口\nvar _ SyncLogService = (*syncLogService)(nil)\n"
  },
  {
    "path": "internal/service/user_service.go",
    "content": "// Package service implements the business logic layer\n// Package service 实现业务逻辑层\npackage service\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"go.uber.org/zap\"\n\t\"gorm.io/gorm\"\n)\n\n// UserService defines the user business service interface\n// UserService 定义用户业务服务接口\ntype UserService interface {\n\t// Register user registration\n\t// Register 用户注册\n\tRegister(ctx context.Context, params *dto.UserCreateRequest) (*dto.UserDTO, error)\n\n\t// Login user login\n\t// Login 用户登录\n\tLogin(ctx context.Context, params *dto.UserLoginRequest, clientIP string) (*dto.UserDTO, error)\n\n\t// ChangePassword change user password\n\t// ChangePassword 修改密码\n\tChangePassword(ctx context.Context, uid int64, params *dto.UserChangePasswordRequest) error\n\n\t// GetInfo retrieves user information\n\t// GetInfo 获取用户信息\n\tGetInfo(ctx context.Context, uid int64) (*dto.UserDTO, error)\n\n\t// GetAllUIDs retrieves all user UIDs\n\t// GetAllUIDs 获取所有用户的 UID\n\tGetAllUIDs(ctx context.Context) ([]int64, error)\n}\n\n// userService implementation of UserService interface\n// userService 实现 UserService 接口\ntype userService struct {\n\tuserRepo     domain.UserRepository // User repository // 用户仓库\n\ttokenManager app.TokenManager      // Token manager // Token 管理器\n\tlogger       *zap.Logger           // Logger // 日志器\n\tconfig       *ServiceConfig        // Service configuration // 服务配置\n}\n\n// NewUserService creates UserService instance\n// NewUserService 创建 UserService 实例\nfunc NewUserService(userRepo domain.UserRepository, tokenManager app.TokenManager, logger *zap.Logger, config *ServiceConfig) UserService {\n\treturn &userService{\n\t\tuserRepo:     userRepo,\n\t\ttokenManager: tokenManager,\n\t\tlogger:       logger,\n\t\tconfig:       config,\n\t}\n}\n\n// domainToDTO converts domain model to DTO\n// domainToDTO 将领域模型转换为 DTO\nfunc (s *userService) domainToDTO(user *domain.User) *dto.UserDTO {\n\tif user == nil {\n\t\treturn nil\n\t}\n\treturn &dto.UserDTO{\n\t\tUID:       user.UID,\n\t\tEmail:     user.Email,\n\t\tUsername:  user.Username,\n\t\tToken:     user.Token,\n\t\tAvatar:    user.Avatar,\n\t\tUpdatedAt: timex.Time(user.UpdatedAt),\n\t\tCreatedAt: timex.Time(user.CreatedAt),\n\t}\n}\n\n// Register user registration\n// Register 用户注册\nfunc (s *userService) Register(ctx context.Context, params *dto.UserCreateRequest) (*dto.UserDTO, error) {\n\t// Check if registration is enabled\n\t// 检查注册是否启用\n\tif s.config == nil || !s.config.User.RegisterIsEnable {\n\t\treturn nil, code.ErrorUserRegisterIsDisable\n\t}\n\n\t// Validate username format\n\t// 验证用户名格式\n\tif !util.IsValidUsername(params.Username) {\n\t\treturn nil, code.ErrorUserUsernameNotValid\n\t}\n\n\t// Validate password consistency\n\t// 验证密码一致性\n\tif params.Password != params.ConfirmPassword {\n\t\treturn nil, code.ErrorUserPasswordNotMatch\n\t}\n\n\t// Check if email already exists\n\t// 检查邮箱是否已存在\n\temailUser, err := s.userRepo.GetByEmail(ctx, params.Email)\n\tif err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {\n\t\treturn nil, code.ErrorDBQuery\n\t}\n\tif emailUser != nil {\n\t\treturn nil, code.ErrorUserEmailAlreadyExists\n\t}\n\n\t// Check if username already exists\n\t// 检查用户名是否已存在\n\tnameUser, err := s.userRepo.GetByUsername(ctx, params.Username)\n\tif err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {\n\t\treturn nil, code.ErrorDBQuery\n\t}\n\tif nameUser != nil {\n\t\treturn nil, code.ErrorUserAlreadyExists\n\t}\n\n\t// Generate password hash\n\t// 生成密码哈希\n\tpassword, err := util.GeneratePasswordHash(params.Password)\n\tif err != nil {\n\t\treturn nil, code.ErrorPasswordNotValid\n\t}\n\n\t// Create user\n\t// 创建用户\n\tnewUser := &domain.User{\n\t\tUsername: params.Username,\n\t\tEmail:    params.Email,\n\t\tPassword: password,\n\t}\n\n\tuser, err := s.userRepo.Create(ctx, newUser)\n\tif err != nil {\n\t\treturn nil, code.ErrorUserRegister.WithDetails(err.Error())\n\t}\n\n\t// Generate Token\n\t// 生成 Token\n\ttoken, err := s.tokenManager.Generate(user.UID, \"\", \"\")\n\tif err != nil {\n\t\treturn nil, code.ErrorTokenGenerate.WithDetails(err.Error())\n\t}\n\n\tdto := s.domainToDTO(user)\n\tdto.Token = token\n\treturn dto, nil\n}\n\n// Login user login\n// Login 用户登录\nfunc (s *userService) Login(ctx context.Context, params *dto.UserLoginRequest, clientIP string) (*dto.UserDTO, error) {\n\tvar user *domain.User\n\tvar err error\n\n\t// Find user based on credential type\n\t// 根据凭证类型查找用户\n\tif util.IsValidEmail(params.Credentials) {\n\t\tuser, err = s.userRepo.GetByEmail(ctx, params.Credentials)\n\t\tif err != nil {\n\t\t\treturn nil, code.ErrorUserLoginPasswordFailed\n\t\t}\n\t} else {\n\t\tuser, err = s.userRepo.GetByUsername(ctx, params.Credentials)\n\t\tif err != nil {\n\t\t\treturn nil, code.ErrorUserLoginPasswordFailed\n\t\t}\n\t}\n\n\t// Validate password\n\t// 验证密码\n\tif !util.CheckPasswordHash(user.Password, params.Password) {\n\t\treturn nil, code.ErrorUserLoginPasswordFailed\n\t}\n\n\t// Generate Token\n\t// 生成 Token\n\ttoken, err := s.tokenManager.Generate(user.UID, user.Username, clientIP)\n\tif err != nil {\n\t\treturn nil, code.ErrorTokenGenerate.WithDetails(err.Error())\n\t}\n\n\tdto := s.domainToDTO(user)\n\tdto.Token = token\n\treturn dto, nil\n}\n\n// ChangePassword change password\n// ChangePassword 修改密码\nfunc (s *userService) ChangePassword(ctx context.Context, uid int64, params *dto.UserChangePasswordRequest) error {\n\t// Validate password consistency\n\t// 验证密码一致性\n\tif params.Password != params.ConfirmPassword {\n\t\treturn code.ErrorUserPasswordNotMatch\n\t}\n\n\t// Get user\n\t// 获取用户\n\tuser, err := s.userRepo.GetByUID(ctx, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn code.ErrorUserNotFound\n\t\t}\n\t\treturn code.ErrorDBQuery\n\t}\n\n\t// Validate old password\n\t// 验证旧密码\n\tif !util.CheckPasswordHash(user.Password, params.OldPassword) {\n\t\treturn code.ErrorUserOldPasswordFailed\n\t}\n\n\t// Generate new password hash\n\t// 生成新密码哈希\n\tpassword, err := util.GeneratePasswordHash(params.Password)\n\tif err != nil {\n\t\treturn code.ErrorPasswordNotValid\n\t}\n\n\t// Update password\n\t// 更新密码\n\terr = s.userRepo.UpdatePassword(ctx, password, uid)\n\tif err != nil {\n\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\treturn nil\n}\n\n// GetInfo retrieves user information\n// GetInfo 获取用户信息\nfunc (s *userService) GetInfo(ctx context.Context, uid int64) (*dto.UserDTO, error) {\n\tuser, err := s.userRepo.GetByUID(ctx, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, nil\n\t\t}\n\t\tif s.logger != nil {\n\t\t\ts.logger.Error(\"UserService.GetInfo failed\",\n\t\t\t\tzap.Int64(\"uid\", uid),\n\t\t\t\tzap.Error(err),\n\t\t\t)\n\t\t}\n\t\treturn nil, code.ErrorDBQuery\n\t}\n\treturn s.domainToDTO(user), nil\n}\n\n// GetAllUIDs retrieves all user UIDs\n// GetAllUIDs 获取所有用户的 UID\nfunc (s *userService) GetAllUIDs(ctx context.Context) ([]int64, error) {\n\tuids, err := s.userRepo.GetAllUIDs(ctx)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\treturn uids, nil\n}\n\n// Verify userService implements UserService interface\n// 确保 userService 实现了 UserService 接口\nvar _ UserService = (*userService)(nil)\n"
  },
  {
    "path": "internal/service/user_service_test.go",
    "content": "// Package service implements the business logic layer\n// Package service 实现业务逻辑层\npackage service\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\tdomainmocks \"github.com/haierkeys/fast-note-sync-service/internal/domain/mocks\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/mock\"\n\t\"go.uber.org/zap\"\n\t\"gorm.io/gorm\"\n)\n\n// mockTokenManager is a minimal TokenManager stub for UserService tests.\n// mockTokenManager 是用于 UserService 测试的最小 TokenManager stub。\ntype mockTokenManager struct{}\n\nfunc (m *mockTokenManager) Generate(uid int64, nickname, ip string) (string, error) {\n\treturn \"test-token\", nil\n}\nfunc (m *mockTokenManager) Parse(token string) (*pkgapp.UserEntity, error) {\n\treturn &pkgapp.UserEntity{UID: 1}, nil\n}\nfunc (m *mockTokenManager) ShareGenerate(shareID int64, uid int64, resources map[string][]string) (string, error) {\n\treturn \"share-token\", nil\n}\nfunc (m *mockTokenManager) ShareParse(token string) (*pkgapp.ShareEntity, error) {\n\treturn nil, nil\n}\nfunc (m *mockTokenManager) Validate(token string) error { return nil }\nfunc (m *mockTokenManager) GetSecretKey() string        { return \"test-key\" }\n\n// newUserSvc creates a userService with mocked dependencies for testing.\n// newUserSvc 创建带 mock 依赖的 userService 用于测试。\nfunc newUserSvc(repo domain.UserRepository, registerEnabled bool) UserService {\n\treturn NewUserService(repo, &mockTokenManager{}, zap.NewNop(), &ServiceConfig{\n\t\tUser: UserServiceConfig{RegisterIsEnable: registerEnabled},\n\t})\n}\n\n// --- Register ---\n\n// TestUserService_Register_Success verifies successful user registration.\n// TestUserService_Register_Success 验证正常用户注册流程。\nfunc TestUserService_Register_Success(t *testing.T) {\n\tmockRepo := new(domainmocks.MockUserRepository)\n\n\tparams := &dto.UserCreateRequest{\n\t\tEmail:           \"test@example.com\",\n\t\tUsername:        \"testuser\",\n\t\tPassword:        \"password123\",\n\t\tConfirmPassword: \"password123\",\n\t}\n\n\t// Email and username both not found (available)\n\t// 邮箱和用户名均未注册（可用）\n\tmockRepo.On(\"GetByEmail\", mock.Anything, \"test@example.com\").\n\t\tReturn(nil, gorm.ErrRecordNotFound)\n\tmockRepo.On(\"GetByUsername\", mock.Anything, \"testuser\").\n\t\tReturn(nil, gorm.ErrRecordNotFound)\n\n\tcreated := &domain.User{UID: 1, Email: \"test@example.com\", Username: \"testuser\"}\n\tmockRepo.On(\"Create\", mock.Anything, mock.AnythingOfType(\"*domain.User\")).\n\t\tReturn(created, nil)\n\n\tsvc := newUserSvc(mockRepo, true)\n\tresult, err := svc.Register(context.Background(), params)\n\n\tassert.NoError(t, err)\n\tassert.NotNil(t, result)\n\tassert.Equal(t, \"test-token\", result.Token)\n\tmockRepo.AssertExpectations(t)\n}\n\n// TestUserService_Register_Disabled verifies error when registration is disabled.\n// TestUserService_Register_Disabled 验证注册功能关闭时返回错误。\nfunc TestUserService_Register_Disabled(t *testing.T) {\n\tmockRepo := new(domainmocks.MockUserRepository)\n\n\tsvc := newUserSvc(mockRepo, false)\n\t_, err := svc.Register(context.Background(), &dto.UserCreateRequest{\n\t\tEmail:           \"a@b.com\",\n\t\tUsername:        \"user1\",\n\t\tPassword:        \"pass\",\n\t\tConfirmPassword: \"pass\",\n\t})\n\n\tassert.ErrorIs(t, err, code.ErrorUserRegisterIsDisable)\n\tmockRepo.AssertExpectations(t) // no repo calls expected // 期望没有 Repository 调用\n}\n\n// TestUserService_Register_PasswordMismatch verifies error when passwords do not match.\n// TestUserService_Register_PasswordMismatch 验证密码不一致时返回错误。\nfunc TestUserService_Register_PasswordMismatch(t *testing.T) {\n\tmockRepo := new(domainmocks.MockUserRepository)\n\n\tsvc := newUserSvc(mockRepo, true)\n\t_, err := svc.Register(context.Background(), &dto.UserCreateRequest{\n\t\tEmail:           \"a@b.com\",\n\t\tUsername:        \"validuser\",\n\t\tPassword:        \"pass1\",\n\t\tConfirmPassword: \"pass2\",\n\t})\n\n\tassert.ErrorIs(t, err, code.ErrorUserPasswordNotMatch)\n\tmockRepo.AssertExpectations(t)\n}\n\n// TestUserService_Register_EmailExists verifies error when email is already registered.\n// TestUserService_Register_EmailExists 验证邮箱已存在时返回错误。\nfunc TestUserService_Register_EmailExists(t *testing.T) {\n\tmockRepo := new(domainmocks.MockUserRepository)\n\n\tmockRepo.On(\"GetByEmail\", mock.Anything, \"dup@example.com\").\n\t\tReturn(&domain.User{UID: 99, Email: \"dup@example.com\"}, nil)\n\n\tsvc := newUserSvc(mockRepo, true)\n\t_, err := svc.Register(context.Background(), &dto.UserCreateRequest{\n\t\tEmail:           \"dup@example.com\",\n\t\tUsername:        \"newuser\",\n\t\tPassword:        \"password123\",\n\t\tConfirmPassword: \"password123\",\n\t})\n\n\tassert.ErrorIs(t, err, code.ErrorUserEmailAlreadyExists)\n\tmockRepo.AssertExpectations(t)\n}\n\n// TestUserService_Register_UsernameExists verifies error when username is already taken.\n// TestUserService_Register_UsernameExists 验证用户名已存在时返回错误。\nfunc TestUserService_Register_UsernameExists(t *testing.T) {\n\tmockRepo := new(domainmocks.MockUserRepository)\n\n\t// Email is available, but username is taken\n\t// 邮箱可用，但用户名已被占用\n\tmockRepo.On(\"GetByEmail\", mock.Anything, \"new@example.com\").\n\t\tReturn(nil, gorm.ErrRecordNotFound)\n\tmockRepo.On(\"GetByUsername\", mock.Anything, \"takenuser\").\n\t\tReturn(&domain.User{UID: 99, Username: \"takenuser\"}, nil)\n\n\tsvc := newUserSvc(mockRepo, true)\n\t_, err := svc.Register(context.Background(), &dto.UserCreateRequest{\n\t\tEmail:           \"new@example.com\",\n\t\tUsername:        \"takenuser\",\n\t\tPassword:        \"password123\",\n\t\tConfirmPassword: \"password123\",\n\t})\n\n\tassert.ErrorIs(t, err, code.ErrorUserAlreadyExists)\n\tmockRepo.AssertExpectations(t)\n}\n\n// --- Login ---\n\n// TestUserService_Login_ByEmail_Success verifies successful login using email.\n// TestUserService_Login_ByEmail_Success 验证通过邮箱登录成功。\nfunc TestUserService_Login_ByEmail_Success(t *testing.T) {\n\tmockRepo := new(domainmocks.MockUserRepository)\n\n\t// Pre-hashed password for \"password123\"\n\t// \"password123\" 的预计算哈希密码（使用真实 bcrypt hash用于测试）\n\t// We use a real hash here to make util.CheckPasswordHash pass\n\t// 此处使用真实 hash 以通过 util.CheckPasswordHash 验证\n\thashedPwd := \"$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi\" // \"password\" from bcrypt\n\n\tuser := &domain.User{\n\t\tUID:      1,\n\t\tEmail:    \"test@example.com\",\n\t\tUsername: \"testuser\",\n\t\tPassword: hashedPwd,\n\t}\n\tmockRepo.On(\"GetByEmail\", mock.Anything, \"test@example.com\").\n\t\tReturn(user, nil)\n\n\tsvc := newUserSvc(mockRepo, true)\n\tresult, err := svc.Login(context.Background(), &dto.UserLoginRequest{\n\t\tCredentials: \"test@example.com\",\n\t\tPassword:    \"password\",\n\t}, \"127.0.0.1\")\n\n\tassert.NoError(t, err)\n\tassert.NotNil(t, result)\n\tassert.Equal(t, \"test-token\", result.Token)\n\tmockRepo.AssertExpectations(t)\n}\n\n// TestUserService_Login_WrongPassword verifies error when password is incorrect.\n// TestUserService_Login_WrongPassword 验证密码错误时返回错误。\nfunc TestUserService_Login_WrongPassword(t *testing.T) {\n\tmockRepo := new(domainmocks.MockUserRepository)\n\n\tuser := &domain.User{\n\t\tUID:      1,\n\t\tEmail:    \"test@example.com\",\n\t\tPassword: \"$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi\", // \"password\"\n\t}\n\tmockRepo.On(\"GetByEmail\", mock.Anything, \"test@example.com\").\n\t\tReturn(user, nil)\n\n\tsvc := newUserSvc(mockRepo, true)\n\t_, err := svc.Login(context.Background(), &dto.UserLoginRequest{\n\t\tCredentials: \"test@example.com\",\n\t\tPassword:    \"wrong-password\",\n\t}, \"127.0.0.1\")\n\n\tassert.ErrorIs(t, err, code.ErrorUserLoginPasswordFailed)\n\tmockRepo.AssertExpectations(t)\n}\n\n// --- GetInfo ---\n\n// TestUserService_GetInfo_Success verifies successful user info retrieval.\n// TestUserService_GetInfo_Success 验证正常获取用户信息。\nfunc TestUserService_GetInfo_Success(t *testing.T) {\n\tmockRepo := new(domainmocks.MockUserRepository)\n\n\tuser := &domain.User{UID: 1, Email: \"a@b.com\", Username: \"user1\"}\n\tmockRepo.On(\"GetByUID\", mock.Anything, int64(1)).\n\t\tReturn(user, nil)\n\n\tsvc := newUserSvc(mockRepo, true)\n\tresult, err := svc.GetInfo(context.Background(), 1)\n\n\tassert.NoError(t, err)\n\tassert.NotNil(t, result)\n\tassert.Equal(t, int64(1), result.UID)\n\tmockRepo.AssertExpectations(t)\n}\n\n// TestUserService_GetInfo_NotFound verifies nil return when user does not exist.\n// TestUserService_GetInfo_NotFound 验证用户不存在时返回 nil。\nfunc TestUserService_GetInfo_NotFound(t *testing.T) {\n\tmockRepo := new(domainmocks.MockUserRepository)\n\n\tmockRepo.On(\"GetByUID\", mock.Anything, int64(99)).\n\t\tReturn(nil, gorm.ErrRecordNotFound)\n\n\tsvc := newUserSvc(mockRepo, true)\n\tresult, err := svc.GetInfo(context.Background(), 99)\n\n\tassert.NoError(t, err)\n\tassert.Nil(t, result)\n\tmockRepo.AssertExpectations(t)\n}\n\n// --- ChangePassword ---\n\n// TestUserService_ChangePassword_Success verifies successful password change.\n// TestUserService_ChangePassword_Success 验证正常修改密码流程。\nfunc TestUserService_ChangePassword_Success(t *testing.T) {\n\tmockRepo := new(domainmocks.MockUserRepository)\n\n\tuser := &domain.User{\n\t\tUID:      1,\n\t\tPassword: \"$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi\", // \"password\"\n\t}\n\tmockRepo.On(\"GetByUID\", mock.Anything, int64(1)).Return(user, nil)\n\tmockRepo.On(\"UpdatePassword\", mock.Anything, mock.AnythingOfType(\"string\"), int64(1)).Return(nil)\n\n\tsvc := newUserSvc(mockRepo, true)\n\terr := svc.ChangePassword(context.Background(), 1, &dto.UserChangePasswordRequest{\n\t\tOldPassword:     \"password\",\n\t\tPassword:        \"newpass123\",\n\t\tConfirmPassword: \"newpass123\",\n\t})\n\n\tassert.NoError(t, err)\n\tmockRepo.AssertExpectations(t)\n}\n"
  },
  {
    "path": "internal/service/vault_service.go",
    "content": "// Package service implements the business logic layer\n// Package service 实现业务逻辑层\npackage service\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"go.uber.org/zap\"\n\t\"golang.org/x/sync/singleflight\"\n\t\"gorm.io/gorm\"\n)\n\n// VaultService defines the business service interface for Vault\n// Provides core business logic for Vault retrieval and creation\n// VaultService 定义 Vault 业务服务接口\n// 提供 Vault 获取和创建的核心业务逻辑\ntype VaultService interface {\n\t// GetByName retrieves Vault by name\n\t// GetByName 根据名称获取 Vault\n\tGetByName(ctx context.Context, uid int64, name string) (*domain.Vault, error)\n\n\t// GetOrCreate retrieves or creates Vault, using Singleflight to merge concurrent requests\n\t// GetOrCreate 获取或创建 Vault，使用 Singleflight 合并并发请求\n\tGetOrCreate(ctx context.Context, uid int64, name string) (*domain.Vault, error)\n\n\t// MustGetID retrieves Vault ID, returns error if not exists\n\t// Uses Singleflight to merge concurrent requests\n\t// MustGetID 获取 Vault ID，如果不存在则返回错误\n\t// 使用 Singleflight 合并并发请求\n\tMustGetID(ctx context.Context, uid int64, name string) (int64, error)\n\n\t// Create creates Vault\n\t// Create 创建 Vault\n\tCreate(ctx context.Context, uid int64, name string) (*dto.VaultDTO, error)\n\n\t// Update updates Vault\n\t// Update 更新 Vault\n\tUpdate(ctx context.Context, uid int64, id int64, name string) (*dto.VaultDTO, error)\n\n\t// Get retrieves Vault by ID\n\t// Get 根据 ID 获取 Vault\n\tGet(ctx context.Context, uid int64, id int64) (*dto.VaultDTO, error)\n\n\t// List retrieves Vault list for current user\n\t// List 获取用户的 Vault 列表\n\tList(ctx context.Context, uid int64) ([]*dto.VaultDTO, error)\n\n\t// Delete deletes Vault\n\t// Delete 删除 Vault\n\tDelete(ctx context.Context, uid int64, id int64) error\n\n\t// UpdateNoteStats updates note statistics for a Vault\n\t// UpdateNoteStats 更新 Vault 的笔记统计信息\n\tUpdateNoteStats(ctx context.Context, noteSize, noteCount, vaultID, uid int64) error\n\n\t// UpdateFileStats updates file statistics for a Vault\n\t// UpdateFileStats 更新 Vault 的文件统计信息\n\tUpdateFileStats(ctx context.Context, fileSize, fileCount, vaultID, uid int64) error\n}\n\n// vaultService implementation of VaultService interface\n// vaultService 实现 VaultService 接口\ntype vaultService struct {\n\trepo        domain.VaultRepository\n\tnoteRepo    domain.NoteRepository\n\tfileRepo    domain.FileRepository\n\tfolderRepo  domain.FolderRepository\n\tlogRepo     domain.SyncLogRepository\n\thistoryRepo domain.NoteHistoryRepository\n\tlinkRepo    domain.NoteLinkRepository\n\tsettingRepo domain.SettingRepository\n\tftsRepo     domain.NoteFTSRepository\n\tshareRepo   domain.UserShareRepository\n\tgitRepo     domain.GitSyncRepository\n\tbackupRepo  domain.BackupRepository\n\tlogger      *zap.Logger\n\tsf          *singleflight.Group\n}\n\n// NewVaultService creates VaultService instance\n// NewVaultService 创建 VaultService 实例\nfunc NewVaultService(\n\trepo domain.VaultRepository,\n\tnoteRepo domain.NoteRepository,\n\tfileRepo domain.FileRepository,\n\tfolderRepo domain.FolderRepository,\n\tlogRepo domain.SyncLogRepository,\n\thistoryRepo domain.NoteHistoryRepository,\n\tlinkRepo domain.NoteLinkRepository,\n\tsettingRepo domain.SettingRepository,\n\tftsRepo domain.NoteFTSRepository,\n\tshareRepo domain.UserShareRepository,\n\tgitRepo domain.GitSyncRepository,\n\tbackupRepo domain.BackupRepository,\n\tlogger *zap.Logger,\n) VaultService {\n\treturn &vaultService{\n\t\trepo:        repo,\n\t\tnoteRepo:    noteRepo,\n\t\tfileRepo:    fileRepo,\n\t\tfolderRepo:  folderRepo,\n\t\tlogRepo:     logRepo,\n\t\thistoryRepo: historyRepo,\n\t\tlinkRepo:    linkRepo,\n\t\tsettingRepo: settingRepo,\n\t\tftsRepo:     ftsRepo,\n\t\tshareRepo:   shareRepo,\n\t\tgitRepo:     gitRepo,\n\t\tbackupRepo:  backupRepo,\n\t\tlogger:      logger,\n\t\tsf:          &singleflight.Group{},\n\t}\n}\n\n// GetByName retrieves Vault by name\n// GetByName 根据名称获取 Vault\nfunc (s *vaultService) GetByName(ctx context.Context, uid int64, name string) (*domain.Vault, error) {\n\tvault, err := s.repo.GetByName(ctx, name, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorVaultNotFound\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\treturn vault, nil\n}\n\n// GetOrCreate retrieves or creates Vault\n// Uses Singleflight to merge concurrent requests, avoiding duplicate creation issues\n// GetOrCreate 获取或创建 Vault\n// 使用 Singleflight 合并并发请求，避免重复创建问题\nfunc (s *vaultService) GetOrCreate(ctx context.Context, uid int64, name string) (*domain.Vault, error) {\n\tkey := fmt.Sprintf(\"vault_get_or_create_%d_%s\", uid, name)\n\n\tresult, err, _ := s.sf.Do(key, func() (interface{}, error) {\n\t\t// Attempt to retrieve first\n\t\t// 先尝试获取\n\t\tvault, err := s.repo.GetByName(ctx, name, uid)\n\t\tif err == nil {\n\t\t\treturn vault, nil\n\t\t}\n\n\t\t// Create if not exists\n\t\t// 如果不存在，则创建\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\tnewVault := &domain.Vault{\n\t\t\t\tName: name,\n\t\t\t}\n\t\t\tcreated, err := s.repo.Create(ctx, newVault, uid)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t\t}\n\t\t\treturn created, nil\n\t\t}\n\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn result.(*domain.Vault), nil\n}\n\n// MustGetID retrieves Vault ID, returns error if not exists\n// Uses Singleflight to merge concurrent requests\n// MustGetID 获取 Vault ID，如果不存在则返回错误\n// 使用 Singleflight 合并并发请求\nfunc (s *vaultService) MustGetID(ctx context.Context, uid int64, name string) (int64, error) {\n\tkey := fmt.Sprintf(\"vault_must_get_id_%d_%s\", uid, name)\n\n\tresult, err, _ := s.sf.Do(key, func() (interface{}, error) {\n\t\tvault, err := s.repo.GetByName(ctx, name, uid)\n\t\tif err != nil {\n\t\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\t\treturn nil, code.ErrorVaultNotFound\n\t\t\t}\n\t\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t\t}\n\t\treturn vault.ID, nil\n\t})\n\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn result.(int64), nil\n}\n\n// UpdateNoteStats updates note statistics for a Vault\n// UpdateNoteStats 更新 Vault 的笔记统计信息\nfunc (s *vaultService) UpdateNoteStats(ctx context.Context, noteSize, noteCount, vaultID, uid int64) error {\n\terr := s.repo.UpdateNoteCountSize(ctx, noteSize, noteCount, vaultID, uid)\n\tif err != nil {\n\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\treturn nil\n}\n\n// UpdateFileStats updates file statistics for a Vault\n// UpdateFileStats 更新 Vault 的文件统计信息\nfunc (s *vaultService) UpdateFileStats(ctx context.Context, fileSize, fileCount, vaultID, uid int64) error {\n\terr := s.repo.UpdateFileCountSize(ctx, fileSize, fileCount, vaultID, uid)\n\tif err != nil {\n\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\treturn nil\n}\n\n// Verify vaultService implements VaultService interface\n// 确保 vaultService 实现了 VaultService 接口\nvar _ VaultService = (*vaultService)(nil)\n\n// domainToDTO converts domain model to DTO\n// domainToDTO 将领域模型转换为 DTO\nfunc (s *vaultService) domainToDTO(vault *domain.Vault) *dto.VaultDTO {\n\tif vault == nil {\n\t\treturn nil\n\t}\n\treturn &dto.VaultDTO{\n\t\tID:        vault.ID,\n\t\tName:      vault.Name,\n\t\tNoteCount: vault.NoteCount,\n\t\tNoteSize:  vault.NoteSize,\n\t\tFileCount: vault.FileCount,\n\t\tFileSize:  vault.FileSize,\n\t\tSize:      vault.NoteSize + vault.FileSize,\n\t\tCreatedAt: vault.CreatedAt.Format(\"2006-01-02 15:04\"),\n\t\tUpdatedAt: vault.UpdatedAt.Format(\"2006-01-02 15:04\"),\n\t}\n}\n\n// Create creates Vault\n// Create 创建 Vault\nfunc (s *vaultService) Create(ctx context.Context, uid int64, name string) (*dto.VaultDTO, error) {\n\t// Check if already exists\n\t// 检查是否已存在\n\texisting, err := s.repo.GetByName(ctx, name, uid)\n\tif err == nil && existing != nil {\n\t\treturn nil, code.ErrorVaultExist\n\t}\n\n\t// Create new Vault\n\t// 创建新 Vault\n\tnewVault := &domain.Vault{\n\t\tName: name,\n\t}\n\tcreated, err := s.repo.Create(ctx, newVault, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\treturn s.domainToDTO(created), nil\n}\n\n// Get retrieves Vault by ID\n// Get 根据 ID 获取 Vault\nfunc (s *vaultService) Get(ctx context.Context, uid int64, id int64) (*dto.VaultDTO, error) {\n\tvault, err := s.repo.GetByID(ctx, id, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorVaultNotFound\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\treturn s.domainToDTO(vault), nil\n}\n\n// List retrieves Vault list for current user\n// List 获取用户的 Vault 列表\nfunc (s *vaultService) List(ctx context.Context, uid int64) ([]*dto.VaultDTO, error) {\n\tvaults, err := s.repo.List(ctx, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\tvar results []*dto.VaultDTO\n\tfor _, vault := range vaults {\n\t\tresults = append(results, s.domainToDTO(vault))\n\t}\n\treturn results, nil\n}\n\n// Delete deletes Vault and all its associated resources\n// Delete 删除 Vault 及其所有关联资源\nfunc (s *vaultService) Delete(ctx context.Context, uid int64, id int64) error {\n\t// 1. 清理笔记及物理内容\n\tif err := s.noteRepo.DeleteByVaultID(ctx, id, uid); err != nil {\n\t\ts.logger.Warn(\"failed to cleanup notes when deleting vault\", zap.Int64(\"vaultID\", id), zap.Error(err))\n\t}\n\n\t// 2. 清理文件及物理内容\n\tif err := s.fileRepo.DeleteByVaultID(ctx, id, uid); err != nil {\n\t\ts.logger.Warn(\"failed to cleanup files when deleting vault\", zap.Int64(\"vaultID\", id), zap.Error(err))\n\t}\n\n\t// 3. 清理文件夹记录\n\tif err := s.folderRepo.DeleteByVaultID(ctx, id, uid); err != nil {\n\t\ts.logger.Warn(\"failed to cleanup folders when deleting vault\", zap.Int64(\"vaultID\", id), zap.Error(err))\n\t}\n\n\t// 4. 清理同步日志\n\tif err := s.logRepo.DeleteByVaultID(ctx, id, uid); err != nil {\n\t\ts.logger.Warn(\"failed to cleanup sync logs when deleting vault\", zap.Int64(\"vaultID\", id), zap.Error(err))\n\t}\n\n\t// 5. 清理历史记录及物理内容\n\tif err := s.historyRepo.DeleteByVaultID(ctx, id, uid); err != nil {\n\t\ts.logger.Warn(\"failed to cleanup history when deleting vault\", zap.Int64(\"vaultID\", id), zap.Error(err))\n\t}\n\n\t// 6. 清理笔记链接\n\tif err := s.linkRepo.DeleteByVaultID(ctx, id, uid); err != nil {\n\t\ts.logger.Warn(\"failed to cleanup links when deleting vault\", zap.Int64(\"vaultID\", id), zap.Error(err))\n\t}\n\n\t// 7. 清理全文搜索索引\n\tif err := s.ftsRepo.DeleteByVaultID(ctx, id, uid); err != nil {\n\t\ts.logger.Warn(\"failed to cleanup FTS index when deleting vault\", zap.Int64(\"vaultID\", id), zap.Error(err))\n\t}\n\n\t// 8. 清理分享记录\n\tif err := s.shareRepo.DeleteByVaultID(ctx, id, uid); err != nil {\n\t\ts.logger.Warn(\"failed to cleanup shares when deleting vault\", zap.Int64(\"vaultID\", id), zap.Error(err))\n\t}\n\n\t// 9. 禁用 Git 同步\n\tif err := s.gitRepo.DisableByVaultID(ctx, id, uid); err != nil {\n\t\ts.logger.Warn(\"failed to disable git sync when deleting vault\", zap.Int64(\"vaultID\", id), zap.Error(err))\n\t}\n\n\t// 10. 禁用备份任务\n\tif err := s.backupRepo.DisableByVaultID(ctx, id, uid); err != nil {\n\t\ts.logger.Warn(\"failed to disable backup when deleting vault\", zap.Int64(\"vaultID\", id), zap.Error(err))\n\t}\n\n\t// 11. 清理配置\n\tif err := s.settingRepo.DeleteByVaultID(ctx, id, uid); err != nil {\n\t\ts.logger.Warn(\"failed to cleanup settings when deleting vault\", zap.Int64(\"vaultID\", id), zap.Error(err))\n\t}\n\n\t// 最后删除仓库本身\n\terr := s.repo.Delete(ctx, id, uid)\n\tif err != nil {\n\t\treturn code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\treturn nil\n}\n\n// Update updates Vault\n// Update 更新 Vault\nfunc (s *vaultService) Update(ctx context.Context, uid int64, id int64, name string) (*dto.VaultDTO, error) {\n\t// Get existing Vault\n\t// 获取现有 Vault\n\tvault, err := s.repo.GetByID(ctx, id, uid)\n\tif err != nil {\n\t\tif errors.Is(err, gorm.ErrRecordNotFound) {\n\t\t\treturn nil, code.ErrorVaultNotFound\n\t\t}\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Update name\n\t// 更新名称\n\tvault.Name = name\n\terr = s.repo.Update(ctx, vault, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\n\t// Re-fetch updated Vault\n\t// 重新获取更新后的 Vault\n\tupdated, err := s.repo.GetByID(ctx, id, uid)\n\tif err != nil {\n\t\treturn nil, code.ErrorDBQuery.WithDetails(err.Error())\n\t}\n\treturn s.domainToDTO(updated), nil\n}\n"
  },
  {
    "path": "internal/service/vault_service_test.go",
    "content": "// Package service implements the business logic layer\n// Package service 实现业务逻辑层\npackage service\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/domain\"\n\tdomainmocks \"github.com/haierkeys/fast-note-sync-service/internal/domain/mocks\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dto\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/mock\"\n\t\"gorm.io/gorm\"\n)\n\n// newVaultMockRepo returns a fresh MockVaultRepository for each test.\n// newVaultMockRepo 为每个测试返回新的 MockVaultRepository。\nfunc newVaultMockRepo() *domainmocks.MockVaultRepository {\n\treturn new(domainmocks.MockVaultRepository)\n}\n\n// newVault creates a domain.Vault test fixture.\n// newVault 创建 domain.Vault 测试固定数据。\nfunc newVault(id int64, name string) *domain.Vault {\n\tf := &domainFixture{id: id, name: name}\n\treturn f.toDomain()\n}\n\n// vaultDTO checks the DTO contains expected fields.\n// vaultDTO 用于验证 DTO 包含预期字段的辅助函数。\nfunc assertVaultDTO(t *testing.T, got *dto.VaultDTO, wantID int64, wantName string) {\n\tt.Helper()\n\tassert.NotNil(t, got)\n\tassert.Equal(t, wantID, got.ID)\n\tassert.Equal(t, wantName, got.Name)\n}\n\n// --- Create ---\n\n// TestVaultService_Create_Success verifies successful vault creation.\n// TestVaultService_Create_Success 验证正常创建 Vault 的逻辑。\nfunc TestVaultService_Create_Success(t *testing.T) {\n\tmockRepo := newVaultMockRepo()\n\n\t// GetByName returns not found, so creation proceeds\n\t// GetByName 返回未找到，允许创建流程继续\n\tmockRepo.On(\"GetByName\", mock.Anything, \"MyVault\", int64(1)).\n\t\tReturn(nil, gorm.ErrRecordNotFound)\n\n\tcreated := newVault(10, \"MyVault\")\n\tmockRepo.On(\"Create\", mock.Anything, mock.AnythingOfType(\"*domain.Vault\"), int64(1)).\n\t\tReturn(created, nil)\n\n\tsvc := NewVaultService(mockRepo)\n\tresult, err := svc.Create(context.Background(), 1, \"MyVault\")\n\n\tassert.NoError(t, err)\n\tassertVaultDTO(t, result, 10, \"MyVault\")\n\tmockRepo.AssertExpectations(t)\n}\n\n// TestVaultService_Create_AlreadyExists verifies error when vault already exists.\n// TestVaultService_Create_AlreadyExists 验证 Vault 已存在时返回 ErrorVaultExist。\nfunc TestVaultService_Create_AlreadyExists(t *testing.T) {\n\tmockRepo := newVaultMockRepo()\n\n\texisting := newVault(1, \"MyVault\")\n\tmockRepo.On(\"GetByName\", mock.Anything, \"MyVault\", int64(1)).\n\t\tReturn(existing, nil)\n\n\tsvc := NewVaultService(mockRepo)\n\t_, err := svc.Create(context.Background(), 1, \"MyVault\")\n\n\tassert.ErrorIs(t, err, code.ErrorVaultExist)\n\tmockRepo.AssertExpectations(t)\n}\n\n// --- Get ---\n\n// TestVaultService_Get_Success verifies successful vault retrieval.\n// TestVaultService_Get_Success 验证正常获取 Vault 的逻辑。\nfunc TestVaultService_Get_Success(t *testing.T) {\n\tmockRepo := newVaultMockRepo()\n\n\tv := newVault(5, \"Notes\")\n\tmockRepo.On(\"GetByID\", mock.Anything, int64(5), int64(1)).\n\t\tReturn(v, nil)\n\n\tsvc := NewVaultService(mockRepo)\n\tresult, err := svc.Get(context.Background(), 1, 5)\n\n\tassert.NoError(t, err)\n\tassertVaultDTO(t, result, 5, \"Notes\")\n\tmockRepo.AssertExpectations(t)\n}\n\n// TestVaultService_Get_NotFound verifies ErrorVaultNotFound when vault is missing.\n// TestVaultService_Get_NotFound 验证 Vault 不存在时返回 ErrorVaultNotFound。\nfunc TestVaultService_Get_NotFound(t *testing.T) {\n\tmockRepo := newVaultMockRepo()\n\n\tmockRepo.On(\"GetByID\", mock.Anything, int64(99), int64(1)).\n\t\tReturn(nil, gorm.ErrRecordNotFound)\n\n\tsvc := NewVaultService(mockRepo)\n\t_, err := svc.Get(context.Background(), 1, 99)\n\n\tassert.ErrorIs(t, err, code.ErrorVaultNotFound)\n\tmockRepo.AssertExpectations(t)\n}\n\n// --- List ---\n\n// TestVaultService_List_Success verifies successful vault list retrieval.\n// TestVaultService_List_Success 验证正常获取 Vault 列表的逻辑。\nfunc TestVaultService_List_Success(t *testing.T) {\n\tmockRepo := newVaultMockRepo()\n\n\tvaults := []*domainFixture{\n\t\t{id: 1, name: \"Vault-A\"},\n\t\t{id: 2, name: \"Vault-B\"},\n\t}\n\tmockRepo.On(\"List\", mock.Anything, int64(1)).\n\t\tReturn(fixturesToDomain(vaults), nil)\n\n\tsvc := NewVaultService(mockRepo)\n\tresults, err := svc.List(context.Background(), 1)\n\n\tassert.NoError(t, err)\n\tassert.Len(t, results, 2)\n\tassert.Equal(t, \"Vault-A\", results[0].Name)\n\tassert.Equal(t, \"Vault-B\", results[1].Name)\n\tmockRepo.AssertExpectations(t)\n}\n\n// --- Delete ---\n\n// TestVaultService_Delete_Success verifies successful vault deletion.\n// TestVaultService_Delete_Success 验证正常删除 Vault 的逻辑。\nfunc TestVaultService_Delete_Success(t *testing.T) {\n\tmockRepo := newVaultMockRepo()\n\n\tmockRepo.On(\"Delete\", mock.Anything, int64(3), int64(1)).\n\t\tReturn(nil)\n\n\tsvc := NewVaultService(mockRepo)\n\terr := svc.Delete(context.Background(), 1, 3)\n\n\tassert.NoError(t, err)\n\tmockRepo.AssertExpectations(t)\n}\n\n// --- Update ---\n\n// TestVaultService_Update_Success verifies successful vault update.\n// TestVaultService_Update_Success 验证正常更新 Vault 的逻辑。\nfunc TestVaultService_Update_Success(t *testing.T) {\n\tmockRepo := newVaultMockRepo()\n\n\toriginal := newVault(7, \"OldName\")\n\tupdated := newVault(7, \"NewName\")\n\n\tmockRepo.On(\"GetByID\", mock.Anything, int64(7), int64(1)).\n\t\tReturn(original, nil).Once()\n\tmockRepo.On(\"Update\", mock.Anything, mock.AnythingOfType(\"*domain.Vault\"), int64(1)).\n\t\tReturn(nil)\n\t// Re-fetch after update\n\t// 更新后重新获取\n\tmockRepo.On(\"GetByID\", mock.Anything, int64(7), int64(1)).\n\t\tReturn(updated, nil).Once()\n\n\tsvc := NewVaultService(mockRepo)\n\tresult, err := svc.Update(context.Background(), 1, 7, \"NewName\")\n\n\tassert.NoError(t, err)\n\tassertVaultDTO(t, result, 7, \"NewName\")\n\tmockRepo.AssertExpectations(t)\n}\n\n// TestVaultService_Update_NotFound verifies error when vault to update does not exist.\n// TestVaultService_Update_NotFound 验证要更新的 Vault 不存在时返回错误。\nfunc TestVaultService_Update_NotFound(t *testing.T) {\n\tmockRepo := newVaultMockRepo()\n\n\tmockRepo.On(\"GetByID\", mock.Anything, int64(99), int64(1)).\n\t\tReturn(nil, gorm.ErrRecordNotFound)\n\n\tsvc := NewVaultService(mockRepo)\n\t_, err := svc.Update(context.Background(), 1, 99, \"NewName\")\n\n\tassert.ErrorIs(t, err, code.ErrorVaultNotFound)\n\tmockRepo.AssertExpectations(t)\n}\n\n// --- GetOrCreate ---\n\n// TestVaultService_GetOrCreate_ExistingVault verifies returning existing vault.\n// TestVaultService_GetOrCreate_ExistingVault 验证 Vault 已存在时直接返回。\nfunc TestVaultService_GetOrCreate_ExistingVault(t *testing.T) {\n\tmockRepo := newVaultMockRepo()\n\n\texisting := newVault(1, \"MyVault\")\n\tmockRepo.On(\"GetByName\", mock.Anything, \"MyVault\", int64(1)).\n\t\tReturn(existing, nil)\n\n\tsvc := NewVaultService(mockRepo)\n\tresult, err := svc.GetOrCreate(context.Background(), 1, \"MyVault\")\n\n\tassert.NoError(t, err)\n\tassert.Equal(t, int64(1), result.ID)\n\tmockRepo.AssertExpectations(t)\n}\n\n// TestVaultService_GetOrCreate_NewVault verifies creating vault when it does not exist.\n// TestVaultService_GetOrCreate_NewVault 验证 Vault 不存在时自动创建。\nfunc TestVaultService_GetOrCreate_NewVault(t *testing.T) {\n\tmockRepo := newVaultMockRepo()\n\n\tmockRepo.On(\"GetByName\", mock.Anything, \"NewVault\", int64(1)).\n\t\tReturn(nil, gorm.ErrRecordNotFound)\n\n\tcreated := newVault(9, \"NewVault\")\n\tmockRepo.On(\"Create\", mock.Anything, mock.AnythingOfType(\"*domain.Vault\"), int64(1)).\n\t\tReturn(created, nil)\n\n\tsvc := NewVaultService(mockRepo)\n\tresult, err := svc.GetOrCreate(context.Background(), 1, \"NewVault\")\n\n\tassert.NoError(t, err)\n\tassert.Equal(t, int64(9), result.ID)\n\tmockRepo.AssertExpectations(t)\n}\n\n// --- UpdateNoteStats / UpdateFileStats ---\n\n// TestVaultService_UpdateNoteStats verifies note stats update delegation.\n// TestVaultService_UpdateNoteStats 验证笔记统计更新的委托调用。\nfunc TestVaultService_UpdateNoteStats(t *testing.T) {\n\tmockRepo := newVaultMockRepo()\n\n\tmockRepo.On(\"UpdateNoteCountSize\", mock.Anything, int64(1024), int64(5), int64(1), int64(1)).\n\t\tReturn(nil)\n\n\tsvc := NewVaultService(mockRepo)\n\terr := svc.UpdateNoteStats(context.Background(), 1024, 5, 1, 1)\n\n\tassert.NoError(t, err)\n\tmockRepo.AssertExpectations(t)\n}\n\n// TestVaultService_UpdateFileStats verifies file stats update delegation.\n// TestVaultService_UpdateFileStats 验证文件统计更新的委托调用。\nfunc TestVaultService_UpdateFileStats(t *testing.T) {\n\tmockRepo := newVaultMockRepo()\n\n\tmockRepo.On(\"UpdateFileCountSize\", mock.Anything, int64(2048), int64(3), int64(1), int64(1)).\n\t\tReturn(nil)\n\n\tsvc := NewVaultService(mockRepo)\n\terr := svc.UpdateFileStats(context.Background(), 2048, 3, 1, 1)\n\n\tassert.NoError(t, err)\n\tmockRepo.AssertExpectations(t)\n}\n\n// --- Test Fixtures ---\n\n// domainFixture is a helper struct for building test domain.Vault instances.\n// domainFixture 是用于构建测试 domain.Vault 实例的辅助结构体。\ntype domainFixture struct {\n\tid   int64\n\tname string\n}\n\nfunc (f *domainFixture) toDomain() *domain.Vault {\n\treturn &domain.Vault{\n\t\tID:        f.id,\n\t\tName:      f.name,\n\t\tCreatedAt: time.Now(),\n\t\tUpdatedAt: time.Now(),\n\t}\n}\n\nfunc fixturesToDomain(fixtures []*domainFixture) []*domain.Vault {\n\tresult := make([]*domain.Vault, len(fixtures))\n\tfor i, f := range fixtures {\n\t\tresult[i] = f.toDomain()\n\t}\n\treturn result\n}\n"
  },
  {
    "path": "internal/task/manager.go",
    "content": "package task\n\nimport (\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/safe_close\"\n\t\"go.uber.org/zap\"\n)\n\n// Manager 任务管理器,负责创建和管理所有任务\ntype Manager struct {\n\tscheduler *Scheduler\n\tlogger    *zap.Logger\n\tapp       *app.App\n}\n\n// NewManager 创建任务管理器\nfunc NewManager(logger *zap.Logger, sc *safe_close.SafeClose, appContainer *app.App) *Manager {\n\treturn &Manager{\n\t\tscheduler: NewScheduler(logger, sc),\n\t\tlogger:    logger,\n\t\tapp:       appContainer,\n\t}\n}\n\n// RegisterTasks 注册所有任务\n// 从全局注册表获取所有已注册的任务工厂,并创建任务实例\nfunc (m *Manager) RegisterTasks() error {\n\tfactories := GetFactories()\n\tfactoriesWithApp := GetFactoriesWithApp()\n\n\ttotalCount := len(factories) + len(factoriesWithApp)\n\tif totalCount == 0 {\n\t\tm.logger.Info(\"no task factories registered\")\n\t\treturn nil\n\t}\n\n\tm.logger.Info(\"tasks\", zap.Int(\"count\", totalCount))\n\n\t// 注册普通任务\n\tfor _, factory := range factories {\n\t\ttask, err := factory()\n\t\tif err != nil {\n\t\t\tm.logger.Warn(\"failed to create task\", zap.Error(err))\n\t\t\tcontinue\n\t\t}\n\n\t\tif task == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tm.registerTask(task)\n\t}\n\n\t// 注册带 App Container 的任务\n\tfor _, factory := range factoriesWithApp {\n\t\ttask, err := factory(m.app)\n\t\tif err != nil {\n\t\t\tm.logger.Warn(\"failed to create task with app\", zap.Error(err))\n\t\t\tcontinue\n\t\t}\n\n\t\tif task == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tm.registerTask(task)\n\t}\n\n\treturn nil\n}\n\n// registerTask 注册单个任务\nfunc (m *Manager) registerTask(task Task) {\n\tloopInterval := task.LoopInterval()\n\tisStartupRun := task.IsStartupRun()\n\n\tif isStartupRun && loopInterval <= 0 {\n\t\tm.logger.Info(\"task registered\", zap.String(\"name\", task.Name()), zap.Bool(\"once\", true))\n\t} else if isStartupRun && loopInterval > 0 {\n\t\tm.logger.Info(\"task registered\", zap.String(\"name\", task.Name()), zap.Bool(\"once\", true), zap.Duration(\"loop\", loopInterval))\n\t} else if !isStartupRun && loopInterval > 0 {\n\t\tm.logger.Info(\"task registered\", zap.String(\"name\", task.Name()), zap.Bool(\"once\", false), zap.Duration(\"loop\", loopInterval))\n\t} else {\n\t\tm.logger.Info(\"task skipped\", zap.String(\"name\", task.Name()))\n\t\treturn\n\t}\n\n\tm.scheduler.AddTask(task)\n}\n\n// Start 启动所有已注册的任务\nfunc (m *Manager) Start() {\n\tm.scheduler.Start()\n}\n"
  },
  {
    "path": "internal/task/registry.go",
    "content": "package task\n\nimport (\n\t\"sync\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n)\n\n// TaskFactory 任务工厂函数类型,用于创建任务实例\ntype TaskFactory func() (Task, error)\n\n// TaskFactoryWithApp 带 App Container 的任务工厂函数类型\ntype TaskFactoryWithApp func(appContainer *app.App) (Task, error)\n\n// taskRegistry 全局任务注册表\nvar (\n\ttaskRegistry        []TaskFactory\n\ttaskRegistryWithApp []TaskFactoryWithApp\n\tregistryMutex       sync.RWMutex\n)\n\n// Register 注册任务工厂函数\n// 通常在各个任务文件的 init() 函数中调用\nfunc Register(factory TaskFactory) {\n\tregistryMutex.Lock()\n\tdefer registryMutex.Unlock()\n\ttaskRegistry = append(taskRegistry, factory)\n}\n\n// RegisterWithApp 注册带 App Container 的任务工厂函数\nfunc RegisterWithApp(factory TaskFactoryWithApp) {\n\tregistryMutex.Lock()\n\tdefer registryMutex.Unlock()\n\ttaskRegistryWithApp = append(taskRegistryWithApp, factory)\n}\n\n// GetFactories 获取所有已注册的任务工厂\nfunc GetFactories() []TaskFactory {\n\tregistryMutex.RLock()\n\tdefer registryMutex.RUnlock()\n\n\t// 返回副本,避免外部修改\n\tfactories := make([]TaskFactory, len(taskRegistry))\n\tcopy(factories, taskRegistry)\n\treturn factories\n}\n\n// GetFactoriesWithApp 获取所有已注册的带 App Container 的任务工厂\nfunc GetFactoriesWithApp() []TaskFactoryWithApp {\n\tregistryMutex.RLock()\n\tdefer registryMutex.RUnlock()\n\n\t// 返回副本,避免外部修改\n\tfactories := make([]TaskFactoryWithApp, len(taskRegistryWithApp))\n\tcopy(factories, taskRegistryWithApp)\n\treturn factories\n}\n"
  },
  {
    "path": "internal/task/scheduler.go",
    "content": "package task\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/safe_close\"\n\t\"go.uber.org/zap\"\n)\n\n// Task 定义任务接口\ntype Task interface {\n\tName() string                  // 任务名称\n\tRun(ctx context.Context) error // 执行任务\n\tLoopInterval() time.Duration   // 执行间隔\n\tIsStartupRun() bool            // 是否立即执行一次\n}\n\n// Scheduler 任务调度器\ntype Scheduler struct {\n\tlogger *zap.Logger\n\ttasks  []Task\n\tsc     *safe_close.SafeClose\n}\n\n// NewScheduler 创建任务调度器\nfunc NewScheduler(logger *zap.Logger, sc *safe_close.SafeClose) *Scheduler {\n\treturn &Scheduler{\n\t\tlogger: logger,\n\t\ttasks:  make([]Task, 0),\n\t\tsc:     sc,\n\t}\n}\n\n// AddTask 添加任务\nfunc (s *Scheduler) AddTask(task Task) {\n\ts.tasks = append(s.tasks, task)\n}\n\n// Start 启动所有任务\nfunc (s *Scheduler) Start() {\n\tif len(s.tasks) == 0 {\n\t\ts.logger.Info(\"no tasks to schedule\")\n\t\treturn\n\t}\n\n\ts.logger.Info(\"tasks starting \", zap.Int(\"count\", len(s.tasks)))\n\n\tfor _, task := range s.tasks {\n\t\ts.startTask(task)\n\t}\n}\n\n// startTask 启动单个任务\nfunc (s *Scheduler) startTask(task Task) {\n\n\ts.sc.Attach(func(done func(), closeSignal <-chan struct{}) {\n\t\tdefer done()\n\n\t\t// 如果任务需要立即执行\n\t\t// Use a context derived from closeSignal so the task can respond to shutdown signals.\n\t\t// 使用从 closeSignal 派生的 context，使任务能在关闭时正确退出\n\t\tif task.IsStartupRun() {\n\t\t\ts.logger.Info(\"task running\", zap.String(\"name\", task.Name()), zap.Bool(\"startupRun\", true))\n\t\t\ttaskCtx, taskCancel := context.WithCancel(context.Background())\n\t\t\tgo func() {\n\t\t\t\t// Forward the close signal to the task's context.\n\t\t\t\t// 将 closeSignal 转发给任务 context\n\t\t\t\tselect {\n\t\t\t\tcase <-closeSignal:\n\t\t\t\t\ttaskCancel()\n\t\t\t\tcase <-taskCtx.Done():\n\t\t\t\t}\n\t\t\t}()\n\t\t\tgo func() {\n\t\t\t\tdefer taskCancel()\n\t\t\t\tdefer func() {\n\t\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\t\ts.logger.Error(\"task startupRun panic\",\n\t\t\t\t\t\t\tzap.String(\"name\", task.Name()),\n\t\t\t\t\t\t\tzap.Any(\"panic\", r),\n\t\t\t\t\t\t\tzap.Stack(\"stack\"))\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t\t\tif err := task.Run(taskCtx); err != nil {\n\t\t\t\t\ts.logger.Error(\"task running error\",\n\t\t\t\t\t\tzap.String(\"name\", task.Name()),\n\t\t\t\t\t\tzap.Bool(\"startupRun\", true),\n\t\t\t\t\t\tzap.Error(err))\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\n\t\tif task.LoopInterval() <= 0 {\n\t\t\treturn\n\t\t}\n\n\t\tticker := time.NewTicker(task.LoopInterval())\n\t\tdefer ticker.Stop()\n\n\t\t// 定时执行\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase <-ticker.C:\n\t\t\t\tfunc() {\n\t\t\t\t\tdefer func() {\n\t\t\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\t\t\ts.logger.Error(\"task loopRun panic\",\n\t\t\t\t\t\t\t\tzap.String(\"name\", task.Name()),\n\t\t\t\t\t\t\t\tzap.Any(\"panic\", r),\n\t\t\t\t\t\t\t\tzap.Stack(\"stack\"))\n\t\t\t\t\t\t}\n\t\t\t\t\t}()\n\t\t\t\t\ts.logger.Info(\"task running\", zap.String(\"name\", task.Name()), zap.Bool(\"loopRun\", true))\n\t\t\t\t\tif err := task.Run(context.Background()); err != nil {\n\t\t\t\t\t\ts.logger.Error(\"task running error\",\n\t\t\t\t\t\t\tzap.String(\"name\", task.Name()),\n\t\t\t\t\t\t\tzap.Bool(\"loopRun\", true),\n\t\t\t\t\t\t\tzap.Error(err))\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t\tcase <-closeSignal:\n\t\t\t\ts.logger.Info(\"task stopped\", zap.String(\"name\", task.Name()), zap.Bool(\"loopRun\", true))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "internal/task/task_backup.go",
    "content": "package task\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"go.uber.org/zap\"\n)\n\n// BackupTask handles scheduled backups\ntype BackupTask struct {\n\tapp    *app.App\n\tlogger *zap.Logger\n}\n\n// Name returns the task name\nfunc (t *BackupTask) Name() string {\n\treturn \"BackupScheduled\"\n}\n\n// LoopInterval returns the execution interval (every minute)\nfunc (t *BackupTask) LoopInterval() time.Duration {\n\treturn 1 * time.Minute\n}\n\n// IsStartupRun returns whether to run on startup\nfunc (t *BackupTask) IsStartupRun() bool {\n\treturn true\n}\n\n// Run executes the backup processing\nfunc (t *BackupTask) Run(ctx context.Context) error {\n\tif t.app.BackupService == nil {\n\t\treturn nil\n\t}\n\treturn t.app.BackupService.ExecuteTaskBackups(ctx)\n}\n\n// NewBackupTask creates a new BackupTask instance\nfunc NewBackupTask(appContainer *app.App) (Task, error) {\n\treturn &BackupTask{\n\t\tapp:    appContainer,\n\t\tlogger: appContainer.Logger(),\n\t}, nil\n}\n\n// init registers the backup task\nfunc init() {\n\tRegisterWithApp(func(appContainer *app.App) (Task, error) {\n\t\treturn NewBackupTask(appContainer)\n\t})\n}\n"
  },
  {
    "path": "internal/task/task_check_version.go",
    "content": "package task\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"golang.org/x/mod/semver\"\n)\n\nconst (\n\tGitHubServiceReleaseURL = \"https://api.github.com/repos/haierkeys/fast-note-sync-service/releases\"\n\tGitHubPluginReleaseURL  = \"https://api.github.com/repos/haierkeys/obsidian-fast-note-sync/releases\"\n\tServiceRepoPath         = \"haierkeys/fast-note-sync-service\"\n\tServiceRepoURL          = \"https://github.com/\" + ServiceRepoPath\n\tPluginRepoPath          = \"haierkeys/obsidian-fast-note-sync\"\n\tPluginRepoURL           = \"https://github.com/\" + PluginRepoPath\n\n\tCNBServiceReleaseURL = \"https://api.cnb.cool/\" + ServiceRepoPath + \"/-/releases\"\n\tCNBPluginReleaseURL  = \"https://api.cnb.cool/\" + PluginRepoPath + \"/-/releases\"\n\tCNBServiceURL        = \"https://cnb.cool/\" + ServiceRepoPath\n\tCNBPluginURL         = \"https://cnb.cool/\" + PluginRepoPath\n\tCNBServiceToken      = \"58tjez3744HL9Z10cRaCHdeEPhK\"\n\tCNBPluginToken       = \"9pFNKcjlej36e0w6MHKT6YMn53G\"\n)\n\ntype CNBRelease struct {\n\tTagName string `json:\"tag_name\"`\n}\n\ntype GitHubRelease struct {\n\tTagName string `json:\"tag_name\"`\n}\n\ntype GitHubTag struct {\n\tName string `json:\"name\"`\n}\n\ntype CheckVersionTask struct {\n\tapp *app.App\n}\n\nfunc init() {\n\tRegisterWithApp(func(appContainer *app.App) (Task, error) {\n\t\treturn &CheckVersionTask{\n\t\t\tapp: appContainer,\n\t\t}, nil\n\t})\n}\n\nfunc (t *CheckVersionTask) Name() string {\n\treturn \"check_version\"\n}\n\nfunc (t *CheckVersionTask) Run(ctx context.Context) error {\n\tisGitHub := t.app.IsPullFromGitHub()\n\n\tvar serviceLatest, pluginLatest string\n\tvar serviceLink, pluginLink string\n\tvar serviceChangelog, pluginChangelog string\n\tvar err error\n\n\tif isGitHub {\n\t\tserviceLatest, err = t.fetchGitHubReleases(GitHubServiceReleaseURL)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tpluginLatest, err = t.fetchGitHubReleases(GitHubPluginReleaseURL)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tserviceLink = ServiceRepoURL + \"/releases/tag/\" + serviceLatest\n\t\tpluginLink = PluginRepoURL + \"/releases/tag/\" + pluginLatest\n\t\tserviceChangelog = ServiceRepoURL + \"/releases/download/\" + serviceLatest + \"/changelog.txt\"\n\t\tpluginChangelog = PluginRepoURL + \"/releases/download/\" + pluginLatest + \"/changelog.txt\"\n\n\t} else {\n\t\tserviceLatest, err = t.fetchCNBVersion(CNBServiceReleaseURL, CNBServiceToken)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tpluginLatest, err = t.fetchCNBVersion(CNBPluginReleaseURL, CNBPluginToken)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tserviceLink = CNBServiceURL + \"/-/releases/tag/\" + serviceLatest\n\t\tpluginLink = CNBPluginURL + \"/-/releases/tag/\" + pluginLatest\n\t\tserviceChangelog = CNBServiceURL + \"/-/releases/download/\" + serviceLatest + \"/changelog.txt\"\n\t\tpluginChangelog = CNBPluginURL + \"/-/releases/download/\" + pluginLatest + \"/changelog.txt\"\n\t}\n\n\tcurrentServiceVersion := t.app.Version().Version\n\tif !strings.HasPrefix(currentServiceVersion, \"v\") {\n\t\tcurrentServiceVersion = \"v\" + currentServiceVersion\n\t}\n\n\tif !strings.HasPrefix(serviceLatest, \"v\") {\n\t\tserviceLatest = \"v\" + serviceLatest\n\t}\n\n\tif !strings.HasPrefix(pluginLatest, \"v\") {\n\t\tpluginLatest = \"v\" + pluginLatest\n\t}\n\n\tinfo := pkgapp.CheckVersionInfo{\n\t\tGithubAvailable:                  isGitHub,\n\t\tVersionNewName:                   serviceLatest,\n\t\tVersionIsNew:                     semver.Compare(serviceLatest, currentServiceVersion) > 0,\n\t\tVersionNewLink:                   serviceLink,\n\t\tVersionNewChangelog:              serviceChangelog,\n\t\tVersionNewChangelogContent:       t.fetchTextContent(serviceChangelog),\n\t\tPluginVersionNewName:             pluginLatest,\n\t\tPluginVersionNewLink:             pluginLink,\n\t\tPluginVersionNewChangelog:        pluginChangelog,\n\t\tPluginVersionNewChangelogContent: t.fetchTextContent(pluginChangelog),\n\t}\n\n\t// 更新 App 中的版本信息\n\tt.app.SetCheckVersionInfo(info)\n\n\t// 推送版本信息给所有已连接客户端\n\tt.app.BroadcastClientInfo()\n\n\treturn nil\n}\n\nfunc (t *CheckVersionTask) fetchGitHubReleases(url string) (string, error) {\n\tresp, err := http.Get(url)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"GitHub API returned status: %d\", resp.StatusCode)\n\t}\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tvar releases []GitHubRelease\n\tif err := json.Unmarshal(body, &releases); err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif len(releases) == 0 {\n\t\treturn \"\", nil\n\t}\n\n\treturn strings.TrimPrefix(releases[0].TagName, \"v\"), nil\n}\n\nfunc (t *CheckVersionTask) fetchCNBVersion(url string, token string) (string, error) {\n\treq, err := http.NewRequest(\"GET\", url, nil)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treq.Header.Set(\"Accept\", \"application/vnd.cnb.api+json\")\n\treq.Header.Set(\"Authorization\", token)\n\n\tclient := &http.Client{}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer resp.Body.Close()\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tvar releases []CNBRelease\n\tif err := json.Unmarshal(body, &releases); err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif len(releases) == 0 {\n\t\treturn \"\", nil\n\t}\n\n\treturn strings.TrimPrefix(releases[0].TagName, \"v\"), nil\n}\n\nfunc (t *CheckVersionTask) fetchTextContent(url string) string {\n\tif url == \"\" {\n\t\treturn \"\"\n\t}\n\tclient := http.Client{\n\t\tTimeout: 10 * time.Second,\n\t}\n\tresp, err := client.Get(url)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\"\n\t}\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\n\treturn string(body)\n}\n\nfunc (t *CheckVersionTask) LoopInterval() time.Duration {\n\treturn 10 * time.Minute\n}\n\nfunc (t *CheckVersionTask) IsStartupRun() bool {\n\treturn true\n}\n"
  },
  {
    "path": "internal/task/task_db_clean.go",
    "content": "package task\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"go.uber.org/zap\"\n)\n\n// DbCleanTask 清理任务\ntype DbCleanTask struct {\n\tapp                 *app.App\n\tlogger              *zap.Logger\n\tretentionDuration         time.Duration\n\tsyncLogRetentionDuration  time.Duration\n\thistoryKeepVersions       int\n}\n\n// Name 返回任务名称\nfunc (t *DbCleanTask) Name() string {\n\treturn \"DbCleanup\"\n}\n\n// LoopInterval 返回执行间隔\nfunc (t *DbCleanTask) LoopInterval() time.Duration {\n\treturn 10 * time.Minute\n}\n\n// IsStartupRun 是否立即执行一次\nfunc (t *DbCleanTask) IsStartupRun() bool {\n\treturn true\n}\n\n// Run 执行清理任务\nfunc (t *DbCleanTask) Run(ctx context.Context) error {\n\t// 计算截止时间\n\tcutoffTime := time.Now().Add(-t.retentionDuration).UnixMilli()\n\n\tvar errs []error\n\n\t// 调用各 Service 的 CleanupByTime 方法\n\tif err := t.app.NoteService.CleanupByTime(ctx, cutoffTime); err != nil {\n\t\terrs = append(errs, err)\n\t\tt.logger.Error(\"cleanup failed\",\n\t\t\tzap.String(\"task\", t.Name()),\n\t\t\tzap.String(\"service\", \"NoteService\"),\n\t\t\tzap.Error(err))\n\t} else {\n\t\tt.logger.Info(\"cleanup success\",\n\t\t\tzap.String(\"task\", t.Name()),\n\t\t\tzap.String(\"service\", \"NoteService\"))\n\t}\n\n\tif err := t.app.FileService.CleanupByTime(ctx, cutoffTime); err != nil {\n\t\terrs = append(errs, err)\n\t\tt.logger.Error(\"cleanup failed\",\n\t\t\tzap.String(\"task\", t.Name()),\n\t\t\tzap.String(\"service\", \"FileService\"),\n\t\t\tzap.Error(err))\n\t} else {\n\t\tt.logger.Info(\"cleanup success\",\n\t\t\tzap.String(\"task\", t.Name()),\n\t\t\tzap.String(\"service\", \"FileService\"))\n\t}\n\n\tif err := t.app.SettingService.CleanupByTime(ctx, cutoffTime); err != nil {\n\t\terrs = append(errs, err)\n\t\tt.logger.Error(\"cleanup failed\",\n\t\t\tzap.String(\"task\", t.Name()),\n\t\t\tzap.String(\"service\", \"SettingService\"),\n\t\t\tzap.Error(err))\n\t} else {\n\t\tt.logger.Info(\"cleanup success\",\n\t\t\tzap.String(\"task\", t.Name()),\n\t\t\tzap.String(\"service\", \"SettingService\"))\n\t}\n\n\t// 清理 NoteHistory\n\tif err := t.app.NoteHistoryService.CleanupByTime(ctx, cutoffTime, t.historyKeepVersions); err != nil {\n\t\terrs = append(errs, err)\n\t\tt.logger.Error(\"cleanup failed\",\n\t\t\tzap.String(\"task\", t.Name()),\n\t\t\tzap.String(\"service\", \"NoteHistoryService\"),\n\t\t\tzap.Error(err))\n\t} else {\n\t\tt.logger.Info(\"cleanup success\",\n\t\t\tzap.String(\"task\", t.Name()),\n\t\t\tzap.String(\"service\", \"NoteHistoryService\"))\n\t}\n\n\t// 清理 SyncLog\n\tsyncLogCutoffTime := time.Now().Add(-t.syncLogRetentionDuration).UnixMilli()\n\tif err := t.app.SyncLogService.CleanupByTime(ctx, syncLogCutoffTime); err != nil {\n\t\terrs = append(errs, err)\n\t\tt.logger.Error(\"cleanup failed\",\n\t\t\tzap.String(\"task\", t.Name()),\n\t\t\tzap.String(\"service\", \"SyncLogService\"),\n\t\t\tzap.Error(err))\n\t} else {\n\t\tt.logger.Info(\"cleanup success\",\n\t\t\tzap.String(\"task\", t.Name()),\n\t\t\tzap.String(\"service\", \"SyncLogService\"))\n\t}\n\n\t// 清理闲置数据库连接 (保持 1 小时闲置)\n\tt.app.Dao.CleanupConnections(time.Hour)\n\n\tif len(errs) > 0 {\n\t\treturn errs[0] // 返回第一个错误\n\t}\n\n\treturn nil\n}\n\n// NewDbCleanTask 创建清理任务\nfunc NewDbCleanTask(appContainer *app.App) (Task, error) {\n\tretentionTimeStr := appContainer.Config().App.SoftDeleteRetentionTime\n\tif retentionTimeStr == \"\" {\n\t\treturn nil, nil\n\t}\n\tduration, err := util.ParseDuration(retentionTimeStr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif duration <= 0 {\n\t\treturn nil, nil\n\t}\n\n\t// 解析同步日志保留时间\n\tsyncLogRetentionTimeStr := appContainer.Config().App.SyncLogRetentionTime\n\tif syncLogRetentionTimeStr == \"\" {\n\t\tsyncLogRetentionTimeStr = \"30d\" // Default\n\t}\n\tsyncLogDuration, err := util.ParseDuration(syncLogRetentionTimeStr)\n\tif err != nil {\n\t\tsyncLogDuration = 30 * 24 * time.Hour // Fallback\n\t}\n\n\t// 获取历史记录保留版本数，默认 10\n\thistoryKeepVersions := appContainer.Config().App.HistoryKeepVersions\n\tif historyKeepVersions <= 0 {\n\t\thistoryKeepVersions = 10\n\t}\n\n\treturn &DbCleanTask{\n\t\tapp:                       appContainer,\n\t\tlogger:                    appContainer.Logger(),\n\t\tretentionDuration:         duration,\n\t\tsyncLogRetentionDuration:  syncLogDuration,\n\t\thistoryKeepVersions:       historyKeepVersions,\n\t}, nil\n}\n\n// init 自动注册清理任务\nfunc init() {\n\tRegisterWithApp(func(appContainer *app.App) (Task, error) {\n\t\treturn NewDbCleanTask(appContainer)\n\t})\n}\n"
  },
  {
    "path": "internal/task/task_file_session_temp_clean.go",
    "content": "package task\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"go.uber.org/zap\"\n)\n\n// FileSessionTempCleanTask 临时文件清理任务\ntype FileSessionTempCleanTask struct {\n\tfirstRun bool\n\tapp      *app.App\n\tlogger   *zap.Logger\n\ttempPath string\n}\n\n// Name 任务名称\nfunc (t *FileSessionTempCleanTask) Name() string {\n\treturn \"FileSessionTempClean\"\n}\n\n// LoopInterval 执行间隔 (0 表示不进行周期性执行)\nfunc (t *FileSessionTempCleanTask) LoopInterval() time.Duration {\n\treturn 0\n}\n\n// IsStartupRun 是否立即执行一次\nfunc (t *FileSessionTempCleanTask) IsStartupRun() bool {\n\treturn true\n}\n\n// Run 执行清理任务\nfunc (t *FileSessionTempCleanTask) Run(ctx context.Context) error {\n\tt.firstRun = false\n\n\ttempDir := t.tempPath\n\tif tempDir == \"\" {\n\t\ttempDir = \"storage/temp\"\n\t}\n\n\tvar err error\n\n\t// 检查目录是否存在，不存在则创建并直接返回成功\n\tif _, err = os.Stat(tempDir); os.IsNotExist(err) {\n\t\tif err = os.MkdirAll(tempDir, 0754); err != nil {\n\t\t\tt.logger.Error(\"task log\",\n\t\t\t\tzap.String(\"task\", t.Name()),\n\t\t\t\tzap.String(\"path\", tempDir),\n\t\t\t\tzap.Error(err))\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n\n\t// 删除整个目录\n\tif err = os.RemoveAll(tempDir); err != nil {\n\t\tt.logger.Error(\"task log\",\n\t\t\tzap.String(\"task\", t.Name()),\n\t\t\tzap.String(\"type\", \"startupRun\"),\n\t\t\tzap.String(\"path\", tempDir),\n\t\t\tzap.String(\"msg\", \"failed\"),\n\t\t\tzap.Error(err))\n\t\treturn err\n\t}\n\n\t// 重新创建目录\n\tif err = os.MkdirAll(tempDir, 0754); err != nil {\n\t\tt.logger.Error(\"task log\",\n\t\t\tzap.String(\"task\", t.Name()),\n\t\t\tzap.String(\"type\", \"startupRun\"),\n\t\t\tzap.String(\"path\", tempDir),\n\t\t\tzap.String(\"msg\", \"failed\"),\n\t\t\tzap.Error(err))\n\t\treturn err\n\t}\n\n\tt.logger.Info(\"task log\",\n\t\tzap.String(\"task\", t.Name()),\n\t\tzap.String(\"type\", \"startupRun\"),\n\t\tzap.String(\"msg\", \"success\"))\n\n\treturn nil\n}\n\n// NewFileSessionTempCleanTask 创建临时文件清理任务\nfunc NewFileSessionTempCleanTask(appContainer *app.App) (Task, error) {\n\treturn &FileSessionTempCleanTask{\n\t\tfirstRun: true,\n\t\tapp:      appContainer,\n\t\tlogger:   appContainer.Logger(),\n\t\ttempPath: appContainer.Config().App.TempPath,\n\t}, nil\n}\n\nfunc init() {\n\tRegisterWithApp(func(appContainer *app.App) (Task, error) {\n\t\treturn NewFileSessionTempCleanTask(appContainer)\n\t})\n}\n"
  },
  {
    "path": "internal/task/task_note_history.go",
    "content": "package task\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\t\"go.uber.org/zap\"\n)\n\n// NoteHistoryTask 负责处理笔记历史记录的异步延时任务\ntype NoteHistoryTask struct {\n\ttimers map[string]*time.Timer\n\tmu     sync.Mutex\n\tapp    *app.App\n\tlogger *zap.Logger\n}\n\n// Name 返回任务名称\nfunc (t *NoteHistoryTask) Name() string {\n\treturn \"NoteHistory\"\n}\n\n// LoopInterval 返回执行间隔，此处为0，因为由 Run 内部循环控制\nfunc (t *NoteHistoryTask) LoopInterval() time.Duration {\n\treturn 0\n}\n\n// IsStartupRun 返回 true，使任务启动后立即开始执行 Run 循环\nfunc (t *NoteHistoryTask) IsStartupRun() bool {\n\treturn true\n}\n\n// Run 启动任务主循环，处理通道中的消息\nfunc (t *NoteHistoryTask) Run(ctx context.Context) error {\n\n\t// 恢复中断的任务\n\tgo t.resumeTasks(ctx)\n\n\tfor {\n\t\tselect {\n\t\tcase msg := <-service.NoteHistoryChannel:\n\t\t\tt.handleNoteHistory(msg)\n\t\tcase msg := <-service.NoteMigrateChannel:\n\t\t\tt.handleNoteRenameMigrate(msg.OldNoteID, msg.NewNoteID, msg.UID)\n\t\tcase <-ctx.Done():\n\t\t\tt.cleanup()\n\t\t\tt.logger.Info(\"task log\",\n\t\t\t\tzap.String(\"task\", t.Name()),\n\t\t\t\tzap.String(\"type\", \"startupRun\"),\n\t\t\t\tzap.String(\"event\", \"stopped\"),\n\t\t\t\tzap.String(\"msg\", \"success\"))\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\n// cleanup 在任务停止时清理所有定时器\nfunc (t *NoteHistoryTask) cleanup() {\n\tt.mu.Lock()\n\tdefer t.mu.Unlock()\n\tfor _, timer := range t.timers {\n\t\ttimer.Stop()\n\t}\n\tt.timers = make(map[string]*time.Timer)\n}\n\n// getBaseDelay 动态获取配置的基础延迟时间\nfunc (t *NoteHistoryTask) getBaseDelay() time.Duration {\n\tbaseDelay := 10 * time.Second\n\tif delayStr := t.app.Config().App.HistorySaveDelay; delayStr != \"\" {\n\t\tif d, err := util.ParseDuration(delayStr); err == nil && d > 0 {\n\t\t\tbaseDelay = d\n\t\t}\n\t}\n\treturn baseDelay\n}\n\n// handleNoteHistory 处理笔记历史记录\nfunc (t *NoteHistoryTask) handleNoteHistory(msg service.NoteHistoryMsg) {\n\tt.handleNoteHistoryWithDelay(msg, t.getBaseDelay())\n}\n\n// handleNoteHistoryWithDelay 处理笔记历史记录并设置自定义定时器延迟\nfunc (t *NoteHistoryTask) handleNoteHistoryWithDelay(msg service.NoteHistoryMsg, baseDelay time.Duration) {\n\tt.mu.Lock()\n\tdefer t.mu.Unlock()\n\n\tkey := fmt.Sprintf(\"%d_%d\", msg.UID, msg.NoteID)\n\n\t// 如果已存在定时器，先停止它（重置倒计时）\n\tif timer, ok := t.timers[key]; ok {\n\t\ttimer.Stop()\n\t}\n\n\trandomMs := time.Duration(rand.Intn(100)+10) * 100 * time.Millisecond\n\n\t// 正常任务延迟20秒 + (1-10)秒随机延迟\n\t// 启动批处理任务 (1-10)秒随机延迟 + (0-5)秒随机延迟\n\ttotalDelay := randomMs + baseDelay\n\n\t// 创建定时器\n\tt.timers[key] = time.AfterFunc(totalDelay, func() {\n\t\tt.handleNoteHistoryProcess(msg.NoteID, msg.UID, key)\n\t})\n}\n\n// handleNoteHistoryProcess 执行实际的历史记录保存逻辑\nfunc (t *NoteHistoryTask) handleNoteHistoryProcess(noteID, uid int64, key string) {\n\n\tt.mu.Lock()\n\tdelete(t.timers, key)\n\tt.mu.Unlock()\n\n\t// 检查应用是否正在关闭\n\tif t.app.IsShuttingDown() {\n\t\tt.logger.Debug(\"task log: app is shutting down, skipping note history process\",\n\t\t\tzap.String(\"task\", \"NoteHistory\"),\n\t\t\tzap.Int64(\"noteID\", noteID),\n\t\t\tzap.Int64(\"uid\", uid))\n\t\treturn\n\t}\n\n\t// 使用 App Container 中的 NoteHistoryService\n\tctx := context.Background()\n\terr := t.app.NoteHistoryService.ProcessDelay(ctx, noteID, uid)\n\tif err != nil {\n\t\tt.logger.Error(\"task log\",\n\t\t\tzap.String(\"task\", \"NoteHistory\"),\n\t\t\tzap.String(\"type\", \"startupRun\"),\n\t\t\tzap.Int64(\"noteID\", noteID),\n\t\t\tzap.Int64(\"uid\", uid),\n\t\t\tzap.String(\"reason\", \"process failed\"),\n\t\t\tzap.String(\"msg\", \"failed\"),\n\t\t\tzap.Error(err))\n\t} else {\n\t\tt.logger.Info(\"task log\",\n\t\t\tzap.String(\"task\", \"NoteHistory\"),\n\t\t\tzap.String(\"type\", \"startupRun\"),\n\t\t\tzap.Int64(\"noteID\", noteID),\n\t\t\tzap.Int64(\"uid\", uid),\n\t\t\tzap.String(\"msg\", \"success\"))\n\t}\n}\n\n// handleNoteRenameMigrate 处理笔记重命名迁移\nfunc (t *NoteHistoryTask) handleNoteRenameMigrate(oldNoteID, newNoteID, uid int64) {\n\n\tctx := context.Background()\n\n\terr := t.app.NoteService.Migrate(ctx, oldNoteID, newNoteID, uid)\n\tif err != nil {\n\t\tt.logger.Error(\"task log\",\n\t\t\tzap.String(\"task\", \"NoteHistory\"),\n\t\t\tzap.String(\"type\", \"startupRun\"),\n\t\t\tzap.Int64(\"oldNoteID\", oldNoteID),\n\t\t\tzap.Int64(\"newNoteID\", newNoteID),\n\t\t\tzap.Int64(\"uid\", uid),\n\t\t\tzap.String(\"reason\", \"NoteMigrate failed\"),\n\t\t\tzap.String(\"msg\", \"failed\"),\n\t\t\tzap.Error(err))\n\t} else {\n\t\tt.logger.Info(\"task log\",\n\t\t\tzap.String(\"task\", \"NoteHistory\"),\n\t\t\tzap.String(\"type\", \"startupRun\"),\n\t\t\tzap.Int64(\"oldNoteID\", oldNoteID),\n\t\t\tzap.Int64(\"newNoteID\", newNoteID),\n\t\t\tzap.Int64(\"uid\", uid),\n\t\t\tzap.String(\"event\", \"HistoryMigrate success\"),\n\t\t\tzap.String(\"msg\", \"success\"))\n\t}\n\n\terr = t.app.NoteHistoryService.Migrate(ctx, oldNoteID, newNoteID, uid)\n\tif err != nil {\n\t\tt.logger.Error(\"task log\",\n\t\t\tzap.String(\"task\", \"NoteHistory\"),\n\t\t\tzap.String(\"type\", \"startupRun\"),\n\t\t\tzap.Int64(\"oldNoteID\", oldNoteID),\n\t\t\tzap.Int64(\"newNoteID\", newNoteID),\n\t\t\tzap.Int64(\"uid\", uid),\n\t\t\tzap.String(\"reason\", \"processMigrate failed\"),\n\t\t\tzap.String(\"msg\", \"failed\"),\n\t\t\tzap.Error(err))\n\t} else {\n\t\tt.logger.Info(\"task log\",\n\t\t\tzap.String(\"task\", \"NoteHistory\"),\n\t\t\tzap.String(\"type\", \"startupRun\"),\n\t\t\tzap.Int64(\"oldNoteID\", oldNoteID),\n\t\t\tzap.Int64(\"newNoteID\", newNoteID),\n\t\t\tzap.Int64(\"uid\", uid),\n\t\t\tzap.String(\"event\", \"processMigrate success\"),\n\t\t\tzap.String(\"msg\", \"success\"))\n\t}\n}\n\n// resumeTasks 扫描并恢复中断的任务\nfunc (t *NoteHistoryTask) resumeTasks(ctx context.Context) {\n\tuids, err := t.app.UserService.GetAllUIDs(ctx)\n\tif err != nil {\n\t\tt.logger.Error(\"task log\",\n\t\t\tzap.String(\"task\", t.Name()),\n\t\t\tzap.String(\"type\", \"startupRun\"),\n\t\t\tzap.String(\"reason\", \"UserService.GetAllUIDs\"),\n\t\t\tzap.String(\"msg\", \"failed\"),\n\t\t\tzap.Error(err))\n\t\treturn\n\t}\n\n\tif len(uids) == 0 {\n\t\tt.logger.Info(\"task log\",\n\t\t\tzap.String(\"task\", t.Name()),\n\t\t\tzap.String(\"type\", \"startupRun\"),\n\t\t\tzap.Int(\"resumeNotesCount\", 0),\n\t\t\tzap.String(\"msg\", \"success\"))\n\t\treturn\n\t}\n\n\ty := 0\n\tfor _, uid := range uids {\n\t\tnotes, err := t.app.NoteService.ListNeedSnapshot(ctx, uid)\n\t\tif err != nil {\n\t\t\tt.logger.Error(\"task log\",\n\t\t\t\tzap.String(\"task\", t.Name()),\n\t\t\t\tzap.String(\"type\", \"startupRun\"),\n\t\t\t\tzap.String(\"msg\", \"failed\"),\n\t\t\t\tzap.Int64(\"uid\", uid),\n\t\t\t\tzap.Error(err))\n\t\t\tcontinue\n\t\t}\n\t\tfor i, note := range notes {\n\t\t\t// 增加微小的错峰延迟，避免瞬间触发大量写事务\n\t\t\tdelay := time.Duration(i%100) * 50 * time.Millisecond\n\t\t\tt.handleNoteHistoryWithDelay(service.NoteHistoryMsg{\n\t\t\t\tNoteID: note.ID,\n\t\t\t\tUID:    uid,\n\t\t\t}, delay)\n\t\t\ty++\n\t\t}\n\n\t}\n\tt.logger.Info(\"task log\",\n\t\tzap.String(\"task\", t.Name()),\n\t\tzap.Int(\"resumeNotesCount\", y),\n\t\tzap.String(\"type\", \"startupRun\"),\n\t\tzap.String(\"msg\", \"success\"))\n}\n\n// NewNoteHistoryTask 创建一个新的笔记历史记录任务实例\nfunc NewNoteHistoryTask(appContainer *app.App) (Task, error) {\n\treturn &NoteHistoryTask{\n\t\ttimers: make(map[string]*time.Timer),\n\t\tapp:    appContainer,\n\t\tlogger: appContainer.Logger(),\n\t}, nil\n}\n\nfunc init() {\n\tRegisterWithApp(func(appContainer *app.App) (Task, error) {\n\t\treturn NewNoteHistoryTask(appContainer)\n\t})\n}\n"
  },
  {
    "path": "internal/task/task_sync_fid.go",
    "content": "package task\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\t\"go.uber.org/zap\"\n)\n\n// SyncFIDTask FID 同步任务\ntype SyncFIDTask struct {\n\tapp    *app.App\n\tlogger *zap.Logger\n}\n\n// Name 返回任务名称\nfunc (t *SyncFIDTask) Name() string {\n\treturn \"SyncFID\"\n}\n\n// LoopInterval 返回执行间隔（每天执行一次）\nfunc (t *SyncFIDTask) LoopInterval() time.Duration {\n\treturn 24 * time.Hour\n}\n\n// IsStartupRun 是否立即执行一次\nfunc (t *SyncFIDTask) IsStartupRun() bool {\n\treturn true\n}\n\n// Run 执行同步任务\nfunc (t *SyncFIDTask) Run(ctx context.Context) error {\n\tt.logger.Info(\"starting SyncFID startup task\")\n\n\t// 1. 获取所有用户 UID\n\tuids, err := t.app.UserRepo.GetAllUIDs(ctx)\n\tif err != nil {\n\t\tt.logger.Error(\"SyncFIDTask: failed to get all user UIDs\", zap.Error(err))\n\t\treturn err\n\t}\n\n\tfor _, uid := range uids {\n\t\t// 2. 获取该用户的所有 Vault\n\t\tvaults, err := t.app.VaultService.List(ctx, uid)\n\t\tif err != nil {\n\t\t\tt.logger.Warn(\"SyncFIDTask: failed to list vaults for user\", zap.Int64(\"uid\", uid), zap.Error(err))\n\t\t\tcontinue\n\t\t}\n\n\t\tfor _, vault := range vaults {\n\t\t\t// 3. 执行全量 FID 同步\n\t\t\tt.logger.Info(\"SyncFIDTask: syncing FID for vault\", zap.Int64(\"uid\", uid), zap.Int64(\"vaultID\", vault.ID), zap.String(\"vaultName\", vault.Name))\n\n\t\t\t// 先清理重复目录\n\t\t\tif err := t.app.FolderService.CleanDuplicateFolders(ctx, uid, vault.ID); err != nil {\n\t\t\t\tt.logger.Error(\"SyncFIDTask: failed to clean duplicate folders\", zap.Int64(\"uid\", uid), zap.Int64(\"vaultID\", vault.ID), zap.Error(err))\n\t\t\t}\n\n\t\t\t// 清理重复笔记\n\t\t\tif err := t.app.NoteService.CleanDuplicateNotes(ctx, uid, vault.ID); err != nil {\n\t\t\t\tt.logger.Error(\"SyncFIDTask: failed to clean duplicate notes\", zap.Int64(\"uid\", uid), zap.Int64(\"vaultID\", vault.ID), zap.Error(err))\n\t\t\t}\n\n\t\t\t// 清理重复文件\n\t\t\tif err := t.app.FileService.CleanDuplicateFiles(ctx, uid, vault.ID); err != nil {\n\t\t\t\tt.logger.Error(\"SyncFIDTask: failed to clean duplicate files\", zap.Int64(\"uid\", uid), zap.Int64(\"vaultID\", vault.ID), zap.Error(err))\n\t\t\t}\n\n\t\t\t// 清理重复配置\n\t\t\tif err := t.app.SettingService.CleanDuplicateSettings(ctx, uid, vault.ID); err != nil {\n\t\t\t\tt.logger.Error(\"SyncFIDTask: failed to clean duplicate settings\", zap.Int64(\"uid\", uid), zap.Int64(\"vaultID\", vault.ID), zap.Error(err))\n\t\t\t}\n\n\t\t\tif err := t.app.FolderService.SyncResourceFID(ctx, uid, vault.ID, nil, nil); err != nil {\n\t\t\t\tt.logger.Error(\"SyncFIDTask: failed to sync FID for vault\",\n\t\t\t\t\tzap.Int64(\"uid\", uid),\n\t\t\t\t\tzap.Int64(\"vaultID\", vault.ID),\n\t\t\t\t\tzap.Error(err))\n\t\t\t}\n\t\t}\n\t}\n\n\tt.logger.Info(\"SyncFIDTask: startup sync completed\")\n\treturn nil\n}\n\n// NewSyncFIDTask 创建同步任务\nfunc NewSyncFIDTask(appContainer *app.App) (Task, error) {\n\treturn &SyncFIDTask{\n\t\tapp:    appContainer,\n\t\tlogger: appContainer.Logger(),\n\t}, nil\n}\n\n// init 自动注册同步任务\nfunc init() {\n\tRegisterWithApp(func(appContainer *app.App) (Task, error) {\n\t\treturn NewSyncFIDTask(appContainer)\n\t})\n}\n"
  },
  {
    "path": "internal/task/task_update_support.go",
    "content": "package task\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/app\"\n\tpkgapp \"github.com/haierkeys/fast-note-sync-service/pkg/app\"\n\t\"go.uber.org/zap\"\n)\n\nconst (\n\tSupportGitHubRawURL = \"https://raw.githubusercontent.com/haierkeys/fast-note-sync-service/refs/heads/master/docs/Support.%s.json\"\n\tSupportCNBRawURL    = \"https://cnb.cool/haierkeys/fast-note-sync-service/-/git/raw/master/docs/Support.%s.json\"\n)\n\ntype UpdateSupportTask struct {\n\tapp *app.App\n}\n\nfunc init() {\n\tRegisterWithApp(func(appContainer *app.App) (Task, error) {\n\t\treturn &UpdateSupportTask{\n\t\t\tapp: appContainer,\n\t\t}, nil\n\t})\n}\n\nfunc (t *UpdateSupportTask) Name() string {\n\treturn \"update_support\"\n}\n\nfunc (t *UpdateSupportTask) Run(ctx context.Context) error {\n\n\trecordsMap := t.app.GetSupportRecords()\n\tfor lang := range recordsMap {\n\t\tvar url string\n\t\tvar remoteLang = lang\n\t\t// Some lang in files are zh-CN, zh-TW, but keys are zh-cn, zh-tw\n\t\t// We need to try matching the filename case if possible, but the raw URL usually follows the repo filename.\n\t\t// If the repo has Support.zh-CN.json, fetching Support.zh-cn.json might fail on case-sensitive FS.\n\t\t// However, our keys are already lowercase. Let's try to reconstruct potentially correct filename case for zh-CN/zh-TW.\n\t\tif lang == \"zh-cn\" {\n\t\t\tremoteLang = \"zh-CN\"\n\t\t} else if lang == \"zh-tw\" {\n\t\t\tremoteLang = \"zh-TW\"\n\t\t}\n\n\t\tif t.app.IsPullFromGitHub() {\n\t\t\turl = fmt.Sprintf(SupportGitHubRawURL, remoteLang)\n\t\t} else {\n\t\t\turl = fmt.Sprintf(SupportCNBRawURL, remoteLang)\n\t\t}\n\n\t\trecords, err := t.fetchSupportRecords(url)\n\t\tif err != nil {\n\t\t\tt.app.Logger().Warn(\"Failed to fetch support records\", zap.String(\"lang\", lang), zap.String(\"url\", url), zap.Error(err))\n\t\t\tcontinue\n\t\t}\n\n\t\tif len(records) > 0 {\n\t\t\tt.app.UpdateSupportRecords(lang, records)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (t *UpdateSupportTask) fetchSupportRecords(url string) ([]pkgapp.SupportRecord, error) {\n\tclient := http.Client{\n\t\tTimeout: 10 * time.Second,\n\t}\n\tresp, err := client.Get(url)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"unexpected status code: %d\", resp.StatusCode)\n\t}\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar records []pkgapp.SupportRecord\n\tif err := json.Unmarshal(body, &records); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn records, nil\n}\n\nfunc (t *UpdateSupportTask) LoopInterval() time.Duration {\n\treturn 1 * time.Hour\n}\n\nfunc (t *UpdateSupportTask) IsStartupRun() bool {\n\treturn true\n}\n"
  },
  {
    "path": "internal/upgrade/upgrade.go",
    "content": "package upgrade\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/internal/config\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/dao\"\n\t\"github.com/haierkeys/fast-note-sync-service/internal/service\"\n\n\t\"go.uber.org/zap\"\n\t\"golang.org/x/mod/semver\"\n\t\"gorm.io/gorm\"\n)\n\n// SchemaVersion 数据库版本记录表\ntype SchemaVersion struct {\n\tID          int       `gorm:\"primaryKey;autoIncrement\" json:\"id\"`\n\tVersion     string    `gorm:\"not null;uniqueIndex;type:varchar(64)\" json:\"version\"`\n\tDescription string    `gorm:\"type:text\" json:\"description\"`\n\tAppliedAt   time.Time `gorm:\"not null\" json:\"applied_at\"`\n}\n\n// TableName 指定表名\nfunc (SchemaVersion) TableName() string {\n\treturn \"schema_version\"\n}\n\n// Migration 定义升级接口\ntype Migration interface {\n\tVersion() string\n\tDescription() string\n\tUp(db *gorm.DB, ctx context.Context, mc *MigrationContext) error\n}\n\n// MigrationContext 迁移上下文，包含迁移脚本需要的依赖\ntype MigrationContext struct {\n\tLogger       *zap.Logger\n\tDatabasePath string // 数据库文件路径（用于 SQLite）\n\tDatabaseType string // 数据库类型\n}\n\n// MigrationManager 升级管理器\ntype MigrationManager struct {\n\tdb         *gorm.DB\n\tlogger     *zap.Logger\n\tversion    string                 // 当前运行版本\n\tconfig     *config.DatabaseConfig // 主数据库配置\n\tuserConfig *config.DatabaseConfig // 用户数据库配置\n\tmigrations []Migration\n}\n\n// NewMigrationManager 创建升级管理器\n// db: 数据库连接（必须）\n// logger: 日志器（必须）\n// version: 当前运行版本（必须）\n// dbPath: 数据库文件路径（SQLite 需要）\n// dbType: 数据库类型\nfunc NewMigrationManager(db *gorm.DB, logger *zap.Logger, version string, cfg, userCfg *config.DatabaseConfig) *MigrationManager {\n\treturn &MigrationManager{\n\t\tdb:         db,\n\t\tlogger:     logger,\n\t\tversion:    version,\n\t\tconfig:     cfg,\n\t\tuserConfig: userCfg,\n\t\tmigrations: []Migration{\n\t\t\t&NoteHistoryRenameMigrate{},\n\t\t},\n\t}\n}\n\n// Run 执行升级\nfunc (m *MigrationManager) Run(ctx context.Context) error {\n\tm.logger.Info(\"Migration started\")\n\n\t// 使用提供的主配置和用户配置初始化 DBUtils\n\tdbUtils := service.NewDBUtils(m.db, ctx,\n\t\tdao.WithConfig(m.config),\n\t\tdao.WithUserDatabaseConfig(m.userConfig),\n\t\tdao.WithLogger(m.logger),\n\t)\n\terr := dbUtils.ExposeAutoMigrate()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"dbUtils.ExposeAutoMigrate: %w\", err)\n\t}\n\n\t// 确保 schema_version 表存在\n\tif err := m.db.AutoMigrate(&SchemaVersion{}); err != nil {\n\t\treturn fmt.Errorf(\"failed to create schema_version table: %w\", err)\n\t}\n\n\t// 获取已应用的数据库版本\n\tappliedVersions, err := m.getAppliedVersions()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get applied versions: %w\", err)\n\t}\n\n\tlastVersion := m.getReferenceVersion()\n\t// 确保 reference version 有 \"v\" 前缀用于比较 (semver 库需要)\n\tif !strings.HasPrefix(lastVersion, \"v\") {\n\t\tlastVersion = \"v\" + lastVersion\n\t}\n\n\tif !semver.IsValid(lastVersion) {\n\t\tm.logger.Warn(\"reference version (from config/lastVersion) is not a valid semver, using v0.8.10\", zap.String(\"lastVersion\", lastVersion))\n\t\tlastVersion = \"v0.8.1\"\n\t}\n\n\tm.logger.Info(\"LastVersion\", zap.String(\"lastVersion\", lastVersion))\n\n\t// 如果当前 version 与 config/lastVersion 一致，则跳过后续检查\n\t// 这意味着在当前版本下已经运行过一次升级逻辑(无论是执行了还是跳过了)\n\t// 避免每次重启都进行不必要的数据库查询或日志输出\n\trunningVersion := m.version\n\tif !strings.HasPrefix(runningVersion, \"v\") {\n\t\trunningVersion = \"v\" + runningVersion\n\t}\n\t// 如果 runningVersion <= lastVersion，则跳过\n\t// 意味着当前版本没有比上一次运行的版本更新，不需要执行升级检查\n\tif semver.Compare(runningVersion, lastVersion) <= 0 {\n\t\tm.logger.Info(\"skipping upgrade\", zap.String(\"runningVersion\", runningVersion), zap.String(\"lastVersion\", lastVersion))\n\t\treturn nil\n\t}\n\n\t// 执行所有未执行的升级\n\texecuted := 0\n\tfor _, migration := range m.migrations {\n\t\tscriptVersion := migration.Version()\n\n\t\t// [NEW] Prioritize matching against lastVersion\n\t\t// 比较版本: 如果 migration.Version > lastVersion, 则跳过\n\t\t// 先标准化 format\n\t\tcurrentScriptVersion := scriptVersion\n\t\tif !strings.HasPrefix(currentScriptVersion, \"v\") {\n\t\t\tcurrentScriptVersion = \"v\" + currentScriptVersion\n\t\t}\n\n\t\t// 比较版本: 如果 migration.Version <= lastVersion, 则跳过\n\t\tif semver.IsValid(lastVersion) && semver.IsValid(currentScriptVersion) {\n\t\t\tif semver.Compare(currentScriptVersion, lastVersion) <= 0 {\n\t\t\t\tm.logger.Info(\"skip migration <= lastVersion\",\n\t\t\t\t\tzap.String(\"scriptVersion\", scriptVersion),\n\t\t\t\t\tzap.String(\"lastVersion\", lastVersion))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\t// 检查是否已应用\n\t\tif appliedVersions[scriptVersion] {\n\t\t\tcontinue\n\t\t}\n\n\t\tm.logger.Info(\"applying migration\",\n\t\t\tzap.String(\"scriptVersion\", migration.Version()),\n\t\t\tzap.String(\"desc\", migration.Description()))\n\n\t\t// 在事务中执行升级\n\t\tif err := m.db.Transaction(func(tx *gorm.DB) error {\n\t\t\t// 创建迁移上下文\n\t\t\tmc := &MigrationContext{\n\t\t\t\tLogger:       m.logger,\n\t\t\t\tDatabasePath: m.config.Path,\n\t\t\t\tDatabaseType: m.config.Type,\n\t\t\t}\n\t\t\t// 执行升级脚本\n\t\t\tif err := migration.Up(tx, context.Background(), mc); err != nil {\n\t\t\t\treturn fmt.Errorf(\"migration failed: %w\", err)\n\t\t\t}\n\n\t\t\t// 记录版本\n\t\t\trecord := &SchemaVersion{\n\t\t\t\tVersion:     migration.Version(),\n\t\t\t\tDescription: migration.Description(),\n\t\t\t\tAppliedAt:   time.Now(),\n\t\t\t}\n\t\t\tif err := tx.Create(record).Error; err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to record version: %w\", err)\n\t\t\t}\n\n\t\t\treturn nil\n\t\t}); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to apply migration %s: %w\", migration.Version(), err)\n\t\t}\n\n\t\tm.logger.Info(\"migration applied successfully\", zap.String(\"scriptVersion\", migration.Version()))\n\t\texecuted++\n\t}\n\n\tif executed == 0 {\n\t\tm.logger.Info(\"database is already up to date\")\n\t} else {\n\t\tm.logger.Info(\"upgrade completed\", zap.Int(\"migrations_applied\", executed))\n\t}\n\n\t// 无论是否执行了升级，最后将当前 version 写入 config/lastVersion\n\t// 作为下一次运行的基准\n\tif err := m.saveReferenceVersion(m.version); err != nil {\n\t\tm.logger.Error(\"save lastVersion failed\", zap.Error(err))\n\t\t// 记录错误但不阻断启动\n\t} else {\n\t\tm.logger.Info(\"save lastVersion success\", zap.String(\"ver\", m.version))\n\t}\n\n\treturn nil\n}\n\n// getAppliedVersions 获取已应用的数据库版本\nfunc (m *MigrationManager) getAppliedVersions() (map[string]bool, error) {\n\tvar versions []SchemaVersion\n\terr := m.db.Find(&versions).Error\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tapplied := make(map[string]bool)\n\tfor _, v := range versions {\n\t\tapplied[v.Version] = true\n\t\t// Hack to support legacy integer version '1' mapping to '0.0.1' or protecting against re-run\n\t\tif v.Version == \"1\" {\n\t\t\tapplied[\"0.0.1\"] = true\n\t\t}\n\t}\n\treturn applied, nil\n}\n\n// getReferenceVersion 获取参考版本号\n// 从 config/lastVersion 读取，如果文件不存在或为空则返回 v0.0.0\nfunc (m *MigrationManager) getReferenceVersion() string {\n\tcontent, err := os.ReadFile(\"config/lastVersion\")\n\tif err != nil {\n\t\tif !os.IsNotExist(err) {\n\t\t\tm.logger.Warn(\"read config/lastVersion failed\", zap.Error(err))\n\t\t} else {\n\t\t\tm.logger.Info(\"config/lastVersion not found, default v0.8.10\")\n\t\t}\n\t\treturn \"v0.8.1\"\n\t}\n\n\tver := strings.TrimSpace(string(content))\n\n\tif ver == \"\" {\n\t\tm.logger.Info(\"config/lastVersion empty, default v0.8.10\")\n\t\treturn \"v0.8.1\"\n\t}\n\treturn ver\n}\n\n// saveReferenceVersion 保存当前版本号到 config/lastVersion\nfunc (m *MigrationManager) saveReferenceVersion(version string) error {\n\treturn os.WriteFile(\"config/lastVersion\", []byte(version), 0644)\n}\n\n// Execute 执行升级(便捷方法)\n// db: 数据库连接\n// logger: 日志器\n// version: 当前运行版本\n// dbPath: 数据库文件路径\n// dbType: 数据库类型\nfunc Execute(db *gorm.DB, logger *zap.Logger, version string, cfg, userCfg *config.DatabaseConfig) error {\n\tif db == nil {\n\t\treturn fmt.Errorf(\"database not initialized\")\n\t}\n\tif logger == nil {\n\t\treturn fmt.Errorf(\"logger not initialized\")\n\t}\n\n\tmanager := NewMigrationManager(db, logger, version, cfg, userCfg)\n\treturn manager.Run(context.Background())\n}\n"
  },
  {
    "path": "internal/upgrade/upgrade_note_history_rename.go",
    "content": "package upgrade\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"go.uber.org/zap\"\n\t\"gorm.io/gorm\"\n)\n\n// NoteHistoryRenameMigrate 重命名 user_note_history 数据库文件\n// Rename user_note_history database files\ntype NoteHistoryRenameMigrate struct{}\n\n// Version 返回版本号\nfunc (m *NoteHistoryRenameMigrate) Version() string {\n\treturn \"1.6.4\"\n}\n\n// Description 返回描述\nfunc (m *NoteHistoryRenameMigrate) Description() string {\n\treturn \"Rename user_note_history[UID] to user_note_history_[UID] in database filenames\"\n}\n\n// Up 执行升级\nfunc (m *NoteHistoryRenameMigrate) Up(db *gorm.DB, ctx context.Context, mc *MigrationContext) error {\n\tlogger := mc.Logger\n\n\tif mc.DatabaseType != \"sqlite\" {\n\t\tlogger.Info(\"NoteHistoryRenameMigrate: Not using SQLite, skipping file rename\")\n\t\treturn nil\n\t}\n\n\tdbPath := mc.DatabasePath\n\tdbDir := filepath.Dir(dbPath)\n\text := filepath.Ext(dbPath)\n\tbaseName := strings.TrimSuffix(filepath.Base(dbPath), ext)\n\n\tfiles, err := os.ReadDir(dbDir)\n\tif err != nil {\n\t\tlogger.Error(\"NoteHistoryRenameMigrate: Failed to read database directory\", zap.Error(err))\n\t\treturn err\n\t}\n\n\t// 匹配模式: (baseName)_user_note_history(数字)(扩展名)\n\t// Pattern: (baseName)_user_note_history(digits)(ext)\n\t// 且不包含下划线分隔数字\n\tre := regexp.MustCompile(fmt.Sprintf(`^%s_user_note_history(\\d+)%s`, regexp.QuoteMeta(baseName), regexp.QuoteMeta(ext)))\n\n\tfor _, file := range files {\n\t\tif file.IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\tname := file.Name()\n\t\tmatches := re.FindStringSubmatch(name)\n\t\tif len(matches) > 1 {\n\t\t\tuid := matches[1]\n\t\t\toldPath := filepath.Join(dbDir, name)\n\t\t\tnewName := fmt.Sprintf(\"%s_user_note_history_%s%s\", baseName, uid, ext)\n\t\t\tnewPath := filepath.Join(dbDir, newName)\n\n\t\t\tlogger.Info(\"Renaming user_note_history database file\",\n\t\t\t\tzap.String(\"old\", name),\n\t\t\t\tzap.String(\"new\", newName))\n\n\t\t\tif err := os.Rename(oldPath, newPath); err != nil {\n\t\t\t\tlogger.Error(\"NoteHistoryRenameMigrate: Failed to rename file\",\n\t\t\t\t\tzap.String(\"old\", oldPath),\n\t\t\t\t\tzap.String(\"new\", newPath),\n\t\t\t\t\tzap.Error(err))\n\t\t\t\treturn fmt.Errorf(\"failed to rename %s to %s: %w\", name, newName, err)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "internal/upgrade/upgrade_note_history_rename_test.go",
    "content": "package upgrade\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"go.uber.org/zap\"\n)\n\nfunc TestNoteHistoryRenameMigrate_Up(t *testing.T) {\n\t// 1. Setup environment\n\tdbDir := t.TempDir()\n\n\t// Create several files to test matching logic\n\toldFile1 := filepath.Join(dbDir, \"db_user_note_history1.sqlite3\")\n\toldFile2 := filepath.Join(dbDir, \"db_user_note_history123.sqlite3\")\n\totherFile := filepath.Join(dbDir, \"db_user_note_history_already_has_underscore.sqlite3\")\n\tunrelatedFile := filepath.Join(dbDir, \"db.sqlite3\")\n\tunrelatedUserFile := filepath.Join(dbDir, \"db_user_1.sqlite3\")\n\n\t_ = os.WriteFile(oldFile1, []byte(\"dummy1\"), 0644)\n\t_ = os.WriteFile(oldFile2, []byte(\"dummy2\"), 0644)\n\t_ = os.WriteFile(otherFile, []byte(\"dummy3\"), 0644)\n\t_ = os.WriteFile(unrelatedFile, []byte(\"dummy4\"), 0644)\n\t_ = os.WriteFile(unrelatedUserFile, []byte(\"dummy5\"), 0644)\n\n\t// 2. Create MigrationContext\n\tlogger, _ := zap.NewDevelopment()\n\tmc := &MigrationContext{\n\t\tLogger:       logger,\n\t\tDatabasePath: filepath.Join(dbDir, \"db.sqlite3\"),\n\t\tDatabaseType: \"sqlite\",\n\t}\n\n\t// 3. Run migration logic\n\tmigrate := &NoteHistoryRenameMigrate{}\n\terr := migrate.Up(nil, context.Background(), mc)\n\tif err != nil {\n\t\tt.Fatalf(\"Migration failed: %v\", err)\n\t}\n\n\t// 4. Verify results\n\ttests := []struct {\n\t\tpath   string\n\t\texists bool\n\t}{\n\t\t{filepath.Join(dbDir, \"db_user_note_history_1.sqlite3\"), true},\n\t\t{filepath.Join(dbDir, \"db_user_note_history_123.sqlite3\"), true},\n\t\t{filepath.Join(dbDir, \"db_user_note_history1.sqlite3\"), false},\n\t\t{filepath.Join(dbDir, \"db_user_note_history123.sqlite3\"), false},\n\t\t{otherFile, true},\n\t\t{unrelatedFile, true},\n\t\t{unrelatedUserFile, true},\n\t}\n\n\tfor _, tt := range tests {\n\t\t_, err := os.Stat(tt.path)\n\t\texists := !os.IsNotExist(err)\n\t\tif exists != tt.exists {\n\t\t\tt.Errorf(\"File %s exists=%v, want %v\", tt.path, exists, tt.exists)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "main.go",
    "content": "package main\n\nimport (\n\t\"embed\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/cmd\"\n)\n\n//go:embed frontend docs\nvar efs embed.FS\n\n//go:embed config/config.yaml\nvar c string\n\n// @title Fast Note Sync Service HTTP API\n// @version 1.0\n// @description This is the Fast Note Sync Service HTTP API.\n\n// @contact.name Haierkeys\n// @contact.url https://github.com/haierkeys\n// @contact.email haierkeys@gmail.com\n\n// @license.name Apache 2.0\n// @license.url http://www.apache.org/licenses/LICENSE-2.0.html\n\n// @host localhost:9000\n// @BasePath /\n\n// @securityDefinitions.apikey UserAuthToken\n// @in header\n// @name token\n\n// @securityDefinitions.apikey ShareAuthToken\n// @in header\n// @name Share-Token\nfunc main() {\n\tcmd.Execute(efs, c)\n}\n"
  },
  {
    "path": "pkg/app/app.go",
    "content": "package app\n\nimport (\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\n\t\"github.com/gin-gonic/gin\"\n)\n\n// VersionInfo version information // 版本信息\ntype VersionInfo struct {\n\tVersion   string `json:\"version\"`\n\tGitTag    string `json:\"gitTag\"`\n\tBuildTime string `json:\"buildTime\"`\n\tChangelog string `json:\"changelog\"`\n}\n\ntype CheckVersionInfo struct {\n\tGithubAvailable                  bool   `json:\"githubAvailable\"`\n\tVersionIsNew                     bool   `json:\"versionIsNew\"`\n\tVersionNewName                   string `json:\"versionNewName\"`\n\tVersionNewLink                   string `json:\"versionNewLink\"`\n\tVersionNewChangelog              string `json:\"versionNewChangelog\"`\n\tVersionNewChangelogContent       string `json:\"versionNewChangelogContent\"`\n\tPluginVersionIsNew               bool   `json:\"pluginVersionIsNew\"`\n\tPluginVersionNewName             string `json:\"pluginVersionNewName\"`\n\tPluginVersionNewLink             string `json:\"pluginVersionNewLink\"`\n\tPluginVersionNewChangelog        string `json:\"pluginVersionNewChangelog\"`\n\tPluginVersionNewChangelogContent string `json:\"pluginVersionNewChangelogContent\"`\n}\n\ntype SupportRecord struct {\n\tTime    string `json:\"time\"`\n\tItem    string `json:\"item\"`\n\tAmount  string `json:\"amount\"`\n\tUnit    string `json:\"unit\"`\n\tMessage string `json:\"message\"`\n\tName    string `json:\"name\"`\n}\n\ntype Response struct {\n\tCtx *gin.Context\n}\n\ntype Pager struct {\n\tPage      int `json:\"page\"`      // Page number // 页码\n\tPageSize  int `json:\"pageSize\"`  // Page size // 每页数量\n\tTotalRows int `json:\"totalRows\"` // Total rows // 总行数\n}\n\n// PaginationRequest pagination request parameters for Swagger // 分页请求参数（用于 Swagger）\ntype PaginationRequest struct {\n\tPage     int `json:\"page\" form:\"page\" query:\"page\"`             // Page number // 页码\n\tPageSize int `json:\"pageSize\" form:\"pageSize\" query:\"pageSize\"` // Page size // 每页数量\n}\n\ntype ListRes struct {\n\tList  interface{} `json:\"list\"`  // Data list // 数据清单\n\tPager Pager       `json:\"pager\"` // Pagination info // 翻页信息\n}\n\n// Res is the unified response structure: Code/Status/Msg/Data\n// Optional fields Vault and Details use omitempty (will not be serialized if nil)\n// Res 是统一的响应结构：Code/Status/Msg/Data\n// 可选字段 Vault 与 Details 使用 omitempty（nil 则不会被序列化）\ntype Res struct {\n\tCode    int         `json:\"code\"`\n\tStatus  bool        `json:\"status\"`\n\tMessage interface{} `json:\"message,omitempty\"`\n\tData    interface{} `json:\"data,omitempty\"`\n\tDetails interface{} `json:\"details,omitempty\"`\n\tVault   interface{} `json:\"vault,omitempty\"`\n\tContext interface{} `json:\"context,omitempty\"`\n}\n\nfunc NewResponse(ctx *gin.Context) *Response {\n\treturn &Response{\n\t\tCtx: ctx,\n\t}\n}\n\n// RequestParamStrParse parses request parameters\n// RequestParamStrParse 解析\n// Keep original behavior\n// 保持原有行为\nfunc RequestParamStrParse(c *gin.Context, param any) {\n\ttParam := reflect.TypeOf(param).Elem()\n\tvParam := reflect.ValueOf(param).Elem()\n\tfor i := 0; i < tParam.NumField(); i++ {\n\t\tname := tParam.Field(i).Name\n\t\tif nameType, ok := tParam.FieldByName(name); ok {\n\t\t\tdstName := nameType.Tag.Get(\"request\")\n\t\t\tif dstName != \"\" {\n\t\t\t\tparamName := nameType.Tag.Get(\"form\")\n\t\t\t\tif value, ok := c.GetQuery(paramName); ok {\n\t\t\t\t\tvParam.FieldByName(dstName).SetString(value)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n// GetRequestIP gets the request IP\n// GetRequestIP 获取 IP 地址\nfunc GetRequestIP(c *gin.Context) string {\n\treqIP := c.ClientIP()\n\tif reqIP == \"::1\" {\n\t\treqIP = \"127.0.0.1\"\n\t}\n\treturn reqIP\n}\n\nfunc GetAccessHost(c *gin.Context) string {\n\tAccessProto := \"\"\n\tif proto := c.Request.Header.Get(\"X-Forwarded-Proto\"); proto == \"\" {\n\t\tAccessProto = \"http\" + \"://\"\n\t} else {\n\t\tAccessProto = proto + \"://\"\n\t}\n\treturn AccessProto + c.Request.Host\n}\n\n// ToResponse output to browser: unified use of Res, set Details and Vault as needed\n// ToResponse 输出到浏览器：统一使用 Res，根据情况设置 Details 与 Vault\nfunc (r *Response) ToResponse(codeObj *code.Code) {\n\tr.Ctx.Set(\"status_code\", codeObj.StatusCode())\n\n\tcontent := Res{\n\t\tCode:    codeObj.Code(),\n\t\tStatus:  codeObj.Status(),\n\t\tMessage: codeObj.Lang.GetMessage(),\n\t\tData:    codeObj.Data(),\n\t}\n\n\tif codeObj.HaveDetails() {\n\t\tcontent.Details = strings.Join(codeObj.Details(), \",\")\n\t}\n\n\tif codeObj.HaveVault() {\n\t\t// Assume codeObj.Vault() returns a serializable value (string, struct, etc.)\n\t\t// Assume codeObj.Vault() 假设 codeObj.Vault() 返回可序列化的值（string 或 struct 等）\n\t\tcontent.Vault = codeObj.Vault()\n\t}\n\n\tr.send(codeObj.StatusCode(), content)\n}\n\n// ToResponseList outputs list response using ListRes as Data; also supports dynamic Vault addition\n// ToResponseList 输出列表响应，使用 ListRes 作为 Data；同样支持 Vault 动态添加\nfunc (r *Response) ToResponseList(codeObj *code.Code, list interface{}, totalRows int) {\n\tr.Ctx.Set(\"status_code\", codeObj.StatusCode())\n\n\tcontent := Res{\n\t\tCode:    codeObj.Code(),\n\t\tStatus:  codeObj.Status(),\n\t\tMessage: codeObj.Lang.GetMessage(),\n\t\tData: ListRes{\n\t\t\tList:  list,\n\t\t\tPager: *NewPager(r.Ctx, totalRows),\n\t\t},\n\t}\n\n\tif codeObj.HaveVault() {\n\t\tcontent.Vault = codeObj.Vault()\n\t}\n\n\tr.send(codeObj.StatusCode(), content)\n}\n\nfunc (r *Response) send(statusCode int, content interface{}) {\n\tr.Ctx.JSON(statusCode, content)\n}\n"
  },
  {
    "path": "pkg/app/dateime.go",
    "content": "package app\n\nimport (\n\t\"database/sql/driver\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n)\n\ntype Datetime time.Time\n\nfunc (t *Datetime) UnmarshalJSON(data []byte) error {\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\tvar err error\n\t// Time string received from frontend\n\t// Datetime 前端接收的时间字符串\n\tstr := string(data)\n\t// Remove leading/trailing extra quotes from received str\n\t// 去除接收到的字符串首尾多余的引号\n\ttimeStr := strings.Trim(str, \"\\\"\")\n\tt1, err := time.Parse(\"2006-01-02 15:04:05\", timeStr)\n\t*t = Datetime(t1)\n\treturn err\n}\n\nfunc (t Datetime) MarshalJSON() ([]byte, error) {\n\tformatted := fmt.Sprintf(\"\\\"%v\\\"\", time.Time(t).Format(\"2006-01-02 15:04:05\"))\n\treturn []byte(formatted), nil\n}\n\nfunc (t Datetime) Value() (driver.Value, error) {\n\t// Convert Datetime to time.Time type\n\t// Datetime 转换成 time.Time 类型\n\ttTime := time.Time(t)\n\treturn tTime.Format(\"2006-01-02 15:04:05\"), nil\n}\n\nfunc (t *Datetime) Scan(v interface{}) error {\n\tswitch vt := v.(type) {\n\tcase time.Time:\n\t\t// Convert string to time.Time type\n\t\t// 字符串转成 time.Time 类型\n\t\t*t = Datetime(vt)\n\tdefault:\n\t\treturn errors.New(\"type processing error // 类型处理错误\")\n\t}\n\treturn nil\n}\n\nfunc (t *Datetime) String() string {\n\treturn fmt.Sprintf(\"hhh:%s\", time.Time(*t).String())\n}\n"
  },
  {
    "path": "pkg/app/form.go",
    "content": "package app\n\nimport (\n\t\"strings\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/json\"\n\t\"github.com/gin-gonic/gin\"\n\tut \"github.com/go-playground/universal-translator\"\n\t\"github.com/go-playground/validator/v10\"\n)\n\ntype ValidError struct {\n\tKey     string\n\tMessage string\n}\n\ntype ValidErrors []*ValidError\n\nfunc (v *ValidError) Error() string {\n\treturn v.Message\n}\n\nfunc (v *ValidError) Field() string {\n\treturn v.Key\n}\n\nfunc (v *ValidError) Map() map[string]string {\n\treturn map[string]string{v.Key: v.Message}\n}\n\nfunc (v ValidErrors) Error() string {\n\treturn strings.Join(v.Errors(), \",\")\n}\n\nfunc (v ValidErrors) Errors() []string {\n\tvar errs []string\n\tfor _, err := range v {\n\t\terrs = append(errs, err.Error())\n\t}\n\n\treturn errs\n}\n\nfunc (v ValidErrors) ErrorsToString() string {\n\tvar errs []string\n\tfor _, err := range v {\n\t\terrs = append(errs, err.Error())\n\t}\n\n\treturn strings.Join(errs, \",\")\n}\n\nfunc (v ValidErrors) Maps() []map[string]string {\n\tvar maps []map[string]string\n\tfor _, err := range v {\n\t\tmaps = append(maps, err.Map())\n\t}\n\n\treturn maps\n}\n\nfunc (v ValidErrors) MapsToString() string {\n\tmaps := v.Maps()\n\tre, _ := json.Marshal(maps)\n\treturn string(re)\n}\n\n// BindAndValid bind request parameters and perform validation, supporting multiple languages\n// BindAndValid 绑定请求参数并进行验证，支持多语言\nfunc BindAndValid(c *gin.Context, obj interface{}) (bool, ValidErrors) {\n\tvar errs ValidErrors\n\n\t// Use global validator for validation\n\t// 使用全局验证器进行验证\n\tif err := c.ShouldBind(obj); err != nil {\n\t\t// If verification fails, check error type\n\t\t// 如果验证失败，检查错误类型\n\t\tif validationErrors, ok := err.(validator.ValidationErrors); ok {\n\t\t\t// Get translator\n\t\t\t// 获取翻译器\n\t\t\tv := c.Value(\"trans\")\n\t\t\ttrans := v.(ut.Translator)\n\n\t\t\t// Iterate through validation errors and translate them\n\t\t\t// 遍历验证错误并进行翻译\n\t\t\tfor _, validationErr := range validationErrors {\n\t\t\t\ttranslatedMsg := validationErr.Translate(trans) // 翻译错误消息\n\t\t\t\terrs = append(errs, &ValidError{\n\t\t\t\t\tKey:     validationErr.Field(),\n\t\t\t\t\tMessage: translatedMsg,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\treturn false, errs // Return validation error // 返回验证错误\n\t}\n\n\treturn true, nil // Binding and validation both succeeded, returns true // 绑定和验证都成功，返回 true\n}\n"
  },
  {
    "path": "pkg/app/pagination.go",
    "content": "package app\n\nimport (\n\t\"github.com/gin-gonic/gin\"\n)\n\n// PaginationConfig pagination configuration // 分页配置\ntype PaginationConfig struct {\n\tDefaultPageSize int\n\tMaxPageSize     int\n}\n\n// DefaultPaginationConfig default pagination configuration // 默认分页配置\nvar DefaultPaginationConfig = PaginationConfig{\n\tDefaultPageSize: 10,\n\tMaxPageSize:     100,\n}\n\nfunc GetPage(page int) int {\n\tif page <= 0 {\n\t\treturn 1\n\t}\n\n\treturn page\n}\n\n// GetPageSize gets page size (using default configuration)\n// GetPageSize 获取分页大小（使用默认配置）\nfunc GetPageSize(pageSize int) int {\n\n\tif pageSize <= 0 {\n\t\treturn DefaultPaginationConfig.DefaultPageSize\n\t}\n\tif pageSize > DefaultPaginationConfig.MaxPageSize {\n\t\treturn DefaultPaginationConfig.MaxPageSize\n\t}\n\n\treturn pageSize\n}\n\nfunc GetPageOffset(page, pageSize int) int {\n\tresult := 0\n\tif page > 0 {\n\t\tresult = (page - 1) * pageSize\n\t}\n\treturn result\n}\n\n// NewPager creates a new Pager instance from gin.Context\n// NewPager 从 gin.Context 创建一个新的 Pager 实例\nfunc NewPager(c *gin.Context, count ...int) *Pager {\n\n\tparams := &PaginationRequest{}\n\tif valid, errs := BindAndValid(c, params); !valid {\n\t\tlog(LogError, errs.Error())\n\t}\n\n\tvar totalRows int\n\tif len(count) > 0 {\n\t\ttotalRows = count[0]\n\t}\n\n\treturn &Pager{\n\t\tPage:      GetPage(params.Page),\n\t\tPageSize:  GetPageSize(params.PageSize),\n\t\tTotalRows: totalRows,\n\t}\n}\n"
  },
  {
    "path": "pkg/app/token.go",
    "content": "package app\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/util\"\n\n\t\"crypto/aes\"\n\t\"crypto/sha256\"\n\t\"encoding/base64\"\n\t\"encoding/binary\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/golang-jwt/jwt/v5\"\n)\n\n// DefaultTokenIssuer default Token issuer // 默认 Token 签发者\nconst DefaultTokenIssuer = \"fast-note-sync-service\"\n\n// TokenConfig defines Token manager configuration // TokenConfig 定义 Token 管理器的配置\ntype TokenConfig struct {\n\tSecretKey     string        `yaml:\"secret-key\"`         // JWT signing key // JWT 签名密钥\n\tExpiry        time.Duration `yaml:\"expiry\"`             // Token expiration time, defaults to 365 days // Token 过期时间，默认 365 天\n\tShareTokenKey string        `yaml:\"share-token-key\"`    // Dedicated signing key for sharing // 分享专用签名密钥\n\tShareExpiry   time.Duration `yaml:\"share-token-expiry\"` // Dedicated expiration time for sharing // 分享专用过期时间\n\tIssuer        string        `yaml:\"issuer\"`             // Token issuer // Token 签发者\n}\n\n// TokenManager defines Token management interface // TokenManager 定义 Token 管理接口\ntype TokenManager interface {\n\t// User authentication related // 用户认证相关\n\tGenerate(uid int64, nickname, ip string) (string, error)\n\tParse(token string) (*UserEntity, error)\n\n\t// Resource sharing related // 资源分享相关\n\tShareGenerate(shareID int64, uid int64, resources map[string][]string) (string, error)\n\tShareParse(token string) (*ShareEntity, error)\n\n\tValidate(token string) error\n\tGetSecretKey() string\n}\n\n// tokenManager implementation of TokenManager interface // tokenManager 实现 TokenManager 接口\ntype tokenManager struct {\n\tconfig TokenConfig\n}\n\n// NewTokenManager creates a new TokenManager instance\n// NewTokenManager 创建一个新的 TokenManager 实例\nfunc NewTokenManager(cfg TokenConfig) TokenManager {\n\t// Set default values\n\t// 设置默认值\n\tif cfg.Expiry == 0 {\n\t\tcfg.Expiry = 365 * 24 * time.Hour // Default 365 days // 默认 365 天\n\t}\n\tif cfg.Issuer == \"\" {\n\t\tcfg.Issuer = DefaultTokenIssuer\n\t}\n\treturn &tokenManager{config: cfg}\n}\n\n// UserSelectEntity represents the user data stored in the JWT.\ntype UserSelectEntity struct {\n\tUID      int64  `json:\"uid\"`\n\tEmail    string `json:\"email\"`\n\tNickname string `json:\"nickname\"`\n\tAvatar   string `json:\"avatar\"`\n}\n\ntype UserEntity struct {\n\tUID      int64  `json:\"uid\"`\n\tNickname string `json:\"nickname\"`\n\tIP       string `json:\"ip\"`\n\tjwt.RegisteredClaims\n}\n\n// ShareEntity resource sharing Claims // ShareEntity 资源分享 Claims\ntype ShareEntity struct {\n\tSID       int64               `json:\"sid\"`       // Share record ID in database // 数据库中的分享记录 ID (Share ID)\n\tUID       int64               `json:\"uid\"`       // User ID in database // 数据库中的用户 ID (User ID)\n\tResources map[string][]string `json:\"resources\"` // Resource list // 资源列表\n\tExpiresAt time.Time           `json:\"exp\"`\n}\n\n// Generate generates a new JWT Token\n// Generate 生成一个新的 JWT Token\nfunc (t *tokenManager) Generate(uid int64, nickname, ip string) (string, error) {\n\texpirationTime := time.Now().Add(t.config.Expiry)\n\tclaims := &UserEntity{\n\t\tUID:      uid,\n\t\tNickname: nickname,\n\t\tIP:       ip,\n\t\tRegisteredClaims: jwt.RegisteredClaims{\n\t\t\tExpiresAt: jwt.NewNumericDate(expirationTime),\n\t\t\tIssuedAt:  jwt.NewNumericDate(time.Now()),\n\t\t\tNotBefore: jwt.NewNumericDate(time.Now()),\n\t\t\tIssuer:    t.config.Issuer,\n\t\t\tSubject:   \"user-token\",\n\t\t\tID:        fmt.Sprintf(\"%d\", uid),\n\t\t},\n\t}\n\n\ttoken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)\n\treturn token.SignedString([]byte(t.config.SecretKey + \"_\" + util.GetMachineID()))\n}\n\n// Parse parses JWT Token and returns user info\n// Parse 解析 JWT Token 并返回用户信息\nfunc (t *tokenManager) Parse(token string) (*UserEntity, error) {\n\tclaims := &UserEntity{}\n\n\tparsedToken, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) {\n\t\tif _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {\n\t\t\treturn nil, fmt.Errorf(\"unexpected signing method: %v\", token.Header[\"alg\"])\n\t\t}\n\t\treturn []byte(t.config.SecretKey + \"_\" + util.GetMachineID()), nil\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !parsedToken.Valid {\n\t\treturn nil, fmt.Errorf(\"invalid token\")\n\t}\n\n\treturn claims, nil\n}\n\n// ShareGenerate builds share Token (extremely shortened version: single block AES + Checksum)\n// ShareGenerate 构建分享 Token (极致缩短版: 单块 AES + Checksum)\nfunc (t *tokenManager) ShareGenerate(shareID int64, uid int64, resources map[string][]string) (string, error) {\n\texpirationTime := time.Unix(time.Now().Add(t.config.ShareExpiry).Unix(), 0)\n\n\t// Prepare data (fixed 16 bytes): SID (6) + UID (3) + ExpiresAt (4) + Checksum (3)\n\t// 准备数据 (固定 16 字节): SID (6) + UID (3) + ExpiresAt (4) + Checksum (3)\n\tdata := make([]byte, 16)\n\n\t// SID: 6 bytes (0-5)\n\t// SID: 6 字节 (0-5)\n\tsidBytes := make([]byte, 8)\n\tbinary.BigEndian.PutUint64(sidBytes, uint64(shareID))\n\tcopy(data[0:6], sidBytes[2:8])\n\n\t// UID: 3 bytes (6-8)\n\t// UID: 3 字节 (6-8)\n\tuidBytes := make([]byte, 8)\n\tbinary.BigEndian.PutUint64(uidBytes, uint64(uid))\n\tcopy(data[6:9], uidBytes[5:8])\n\n\t// ExpiresAt: 4 bytes (9-12)\n\t// ExpiresAt: 4 字节 (9-12)\n\tbinary.BigEndian.PutUint32(data[9:13], uint32(expirationTime.Unix()))\n\n\t// Generate checksum: generate summary using Key + first 13 bytes, take first 3 bytes\n\t// 生成校验和: 使用 Key + 前 13 字节生成摘要，取前 3 字节\n\tkey := sha256.Sum256([]byte(t.config.ShareTokenKey + \"_\" + util.GetMachineID()))\n\th := sha256.New()\n\th.Write(key[:])\n\th.Write(data[0:13])\n\tsum := h.Sum(nil)\n\tcopy(data[13:16], sum[:3])\n\n\tblock, err := aes.NewCipher(key[:])\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\t// Execute single block encryption (16 bytes)\n\t// 执行单块加密 (16 字节)\n\tciphertext := make([]byte, 16)\n\tblock.Encrypt(ciphertext, data)\n\n\t// 使用 RawURLEncoding 得到固定的 22 字符长度\n\treturn base64.RawURLEncoding.EncodeToString(ciphertext), nil\n}\n\n// ShareParse parses share Token\n// ShareParse 解析分享 Token\nfunc (t *tokenManager) ShareParse(tokenString string) (*ShareEntity, error) {\n\tciphertext, err := base64.RawURLEncoding.DecodeString(tokenString)\n\tif err != nil || len(ciphertext) != 16 {\n\t\treturn nil, fmt.Errorf(\"invalid token format\")\n\t}\n\n\t// Generate AES Key using ShareTokenKey + MachineID\n\t// 使用 ShareTokenKey + MachineID 生成 AES Key\n\tkey := sha256.Sum256([]byte(t.config.ShareTokenKey + \"_\" + util.GetMachineID()))\n\n\tblock, err := aes.NewCipher(key[:])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Execute single block decryption\n\t// 执行单块解密\n\tdata := make([]byte, 16)\n\tblock.Decrypt(data, ciphertext)\n\n\t// Verify checksum\n\t// 验证校验和\n\th := sha256.New()\n\th.Write(key[:])\n\th.Write(data[0:13])\n\tsum := h.Sum(nil)\n\n\tif !bytes.Equal(data[13:16], sum[:3]) {\n\t\treturn nil, fmt.Errorf(\"invalid token checksum\")\n\t}\n\n\t// Parse SID (6 bytes)\n\t// 解析 SID (6 字节)\n\tsidBytes := make([]byte, 8)\n\tcopy(sidBytes[2:8], data[0:6])\n\tshareID := int64(binary.BigEndian.Uint64(sidBytes))\n\n\t// Parse UID (3 bytes)\n\t// 解析 UID (3 字节)\n\tuidBytes := make([]byte, 8)\n\tcopy(uidBytes[5:8], data[6:9])\n\tuid := int64(binary.BigEndian.Uint64(uidBytes))\n\n\t// Parse ExpiresAt (4 bytes)\n\t// 解析 ExpiresAt (4 字节)\n\texpUnix := int64(binary.BigEndian.Uint32(data[9:13]))\n\n\treturn &ShareEntity{\n\t\tSID:       shareID,\n\t\tUID:       uid,\n\t\tExpiresAt: time.Unix(expUnix, 0),\n\t}, nil\n}\n\n// Validate validates if Token is valid\n// Validate 验证 Token 是否有效\nfunc (t *tokenManager) Validate(token string) error {\n\t_, err := t.Parse(token)\n\treturn err\n}\n\n// GetSecretKey gets secret key\n// GetSecretKey 获取密钥\nfunc (t *tokenManager) GetSecretKey() string {\n\treturn t.config.SecretKey\n}\n\n// ParseTokenWithKey parses Token with specified key\n// ParseTokenWithKey 使用指定密钥解析 Token\nfunc ParseTokenWithKey(tokenString string, secretKey string) (*UserEntity, error) {\n\tclaims := &UserEntity{}\n\n\ttoken, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {\n\t\tif _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {\n\t\t\treturn nil, fmt.Errorf(\"unexpected signing method: %v\", token.Header[\"alg\"])\n\t\t}\n\t\treturn []byte(secretKey + \"_\" + util.GetMachineID()), nil\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !token.Valid {\n\t\treturn nil, fmt.Errorf(\"invalid token\")\n\t}\n\n\treturn claims, nil\n}\n\n// GetUid extracts the user ID from the request context.\nfunc GetUID(ctx *gin.Context) (out int64) {\n\tuser, exist := ctx.Get(\"user_token\")\n\tif exist {\n\t\tif userEntity, ok := user.(*UserEntity); ok {\n\t\t\tout = userEntity.UID\n\t\t}\n\t}\n\treturn\n}\n\n// GetShareEntity extracts the share entity from the request context.\nfunc GetShareEntity(ctx *gin.Context) (out *ShareEntity) {\n\tuser, exist := ctx.Get(\"share_entity\")\n\tif exist {\n\t\tif shareEntity, ok := user.(*ShareEntity); ok {\n\t\t\tout = shareEntity\n\t\t}\n\t}\n\treturn\n}\n\n// GetIP extracts the user IP from the request context.\nfunc GetIP(ctx *gin.Context) (out string) {\n\tuser, exist := ctx.Get(\"user_token\")\n\tif exist {\n\t\tif userEntity, ok := user.(*UserEntity); ok {\n\t\t\tout = userEntity.IP\n\t\t}\n\t}\n\treturn\n}\n\n// SetTokenToContextWithKey sets Token to Context with specified key\n// SetTokenToContextWithKey 使用指定密钥设置 Token 到 Context\nfunc SetTokenToContextWithKey(ctx *gin.Context, tokenString string, secretKey string) error {\n\tuser, err := ParseTokenWithKey(tokenString, secretKey)\n\tif err != nil {\n\t\treturn err\n\t}\n\tctx.Set(\"user_token\", user)\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/app/token_test.go",
    "content": "package app\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestTokenManager_ShareGenerateAndParse(t *testing.T) {\n\tcfg := TokenConfig{\n\t\tSecretKey:     \"user-secret\",\n\t\tShareTokenKey: \"share-secret\",\n\t\tShareExpiry:   1 * time.Hour,\n\t\tIssuer:        \"test-issuer\",\n\t}\n\ttm := NewTokenManager(cfg)\n\n\t// 测试数据\n\tshareID := int64(12345)\n\tuid := int64(1001)\n\tresources := map[string][]string{\n\t\t\"note\": {\"note_id_1\", \"note_id_2\"},\n\t\t\"file\": {\"file_id_1\"},\n\t}\n\n\t// 1. 测试生成和解析\n\ttoken, err := tm.ShareGenerate(shareID, uid, resources)\n\tif err != nil {\n\t\tt.Fatalf(\"ShareGenerate failed: %v\", err)\n\t}\n\n\tparsedClaims, err := tm.ShareParse(token)\n\tif err != nil {\n\t\tt.Fatalf(\"ShareParse failed: %v\", err)\n\t}\n\n\t// 验证 SID\n\tif parsedClaims.SID != shareID {\n\t\tt.Errorf(\"Expected SID %d, got %d\", shareID, parsedClaims.SID)\n\t}\n\n\t// 验证 UID\n\tif parsedClaims.UID != uid {\n\t\tt.Errorf(\"Expected UID %d, got %d\", uid, parsedClaims.UID)\n\t}\n\n\t// 验证 ExpiresAt (由于只存了秒级 Unix 戳，允许 1 秒内的误差)\n\tnow := time.Now()\n\texpectedExp := now.Add(cfg.ShareExpiry)\n\tif parsedClaims.ExpiresAt.Unix() < expectedExp.Unix()-1 || parsedClaims.ExpiresAt.Unix() > expectedExp.Unix()+1 {\n\t\tt.Errorf(\"Expected ExpiresAt around %v, got %v\", expectedExp, parsedClaims.ExpiresAt)\n\t}\n\n\t// 3. 测试错误的密钥\n\twrongKeyCfg := cfg\n\twrongKeyCfg.ShareTokenKey = \"wrong-secret\"\n\ttmWrongKey := NewTokenManager(wrongKeyCfg)\n\n\twrongToken, _ := tmWrongKey.ShareGenerate(shareID, uid, resources)\n\t_, err = tm.ShareParse(wrongToken)\n\tif err == nil {\n\t\tt.Error(\"Expected error when parsing token with wrong secret key, but got nil\")\n\t}\n\n\t// 4. 测试篡改后的 Token\n\ttamperedToken := token + \"tampered\"\n\t_, err = tm.ShareParse(tamperedToken)\n\tif err == nil {\n\t\tt.Error(\"Expected error for tampered token, but got nil\")\n\t}\n}\n\nfunc TestTokenManager_GenerateAndParse(t *testing.T) {\n\tcfg := TokenConfig{\n\t\tSecretKey: \"user-secret\",\n\t\tExpiry:    24 * time.Hour,\n\t\tIssuer:    \"user-issuer\",\n\t}\n\ttm := NewTokenManager(cfg)\n\n\tuid := int64(1001)\n\tnickname := \"testuser\"\n\tip := \"127.0.0.1\"\n\n\t// 1. 测试生成和解析\n\ttoken, err := tm.Generate(uid, nickname, ip)\n\tif err != nil {\n\t\tt.Fatalf(\"Generate failed: %v\", err)\n\t}\n\n\tparsedUser, err := tm.Parse(token)\n\tif err != nil {\n\t\tt.Fatalf(\"Parse failed: %v\", err)\n\t}\n\n\t// 验证字段\n\tif parsedUser.UID != uid {\n\t\tt.Errorf(\"Expected UID %d, got %d\", uid, parsedUser.UID)\n\t}\n\tif parsedUser.Nickname != nickname {\n\t\tt.Errorf(\"Expected Nickname %s, got %s\", nickname, parsedUser.Nickname)\n\t}\n\tif parsedUser.IP != ip {\n\t\tt.Errorf(\"Expected IP %s, got %s\", ip, parsedUser.IP)\n\t}\n\tif parsedUser.Issuer != cfg.Issuer {\n\t\tt.Errorf(\"Expected Issuer %s, got %s\", cfg.Issuer, parsedUser.Issuer)\n\t}\n\n\t// 2. 测试过期\n\tshortExpiryCfg := cfg\n\tshortExpiryCfg.Expiry = -1 * time.Second\n\ttmExpired := NewTokenManager(shortExpiryCfg)\n\n\texpiredToken, err := tmExpired.Generate(uid, nickname, ip)\n\tif err != nil {\n\t\tt.Fatalf(\"Generate (expired) failed: %v\", err)\n\t}\n\n\t_, err = tm.Parse(expiredToken)\n\tif err == nil {\n\t\tt.Error(\"Expected error for expired token, but got nil\")\n\t}\n\n\t// 3. 测试错误的密钥\n\twrongKeyCfg := cfg\n\twrongKeyCfg.SecretKey = \"wrong-user-secret\"\n\ttmWrongKey := NewTokenManager(wrongKeyCfg)\n\n\twrongToken, _ := tmWrongKey.Generate(uid, nickname, ip)\n\t_, err = tm.Parse(wrongToken)\n\tif err == nil {\n\t\tt.Error(\"Expected error for token generated with different secret key, but got nil\")\n\t}\n\n\t// 4. 测试篡改后的 Token\n\ttamperedToken := token + \"xyz\"\n\t_, err = tm.Parse(tamperedToken)\n\tif err == nil {\n\t\tt.Error(\"Expected error for tampered user token, but got nil\")\n\t}\n}\n"
  },
  {
    "path": "pkg/app/websocket.go",
    "content": "package app\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/json\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/logger\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\t\"golang.org/x/sync/singleflight\"\n\n\t\"github.com/gin-gonic/gin\"\n\tut \"github.com/go-playground/universal-translator\"\n\tvalidatorV10 \"github.com/go-playground/validator/v10\"\n\t\"github.com/lxzan/gws\"\n\t\"go.uber.org/zap\"\n)\n\ntype LogType string\n\nconst (\n\tWSPingInterval         = 25\n\tWSPingWait             = 60\n\tLogInfo        LogType = \"info\"\n\tLogError       LogType = \"error\"\n\tLogWarn        LogType = \"warn\"\n\tLogDebug       LogType = \"debug\"\n)\n\n// traceIDKeyType used to store Trace ID in context\n// traceIDKeyType 用于在 context 中存储 Trace ID\ntype traceIDKeyType struct{}\n\n// TraceIDKey is the key to store Trace ID in context\n// TraceIDKey 是 context 中存储 Trace ID 的 key\nvar TraceIDKey = traceIDKeyType{}\n\n// GetTraceID gets Trace ID from context\n// GetTraceID 从 context 中获取 Trace ID\nfunc GetTraceID(ctx context.Context) string {\n\tif traceID, ok := ctx.Value(TraceIDKey).(string); ok {\n\t\treturn traceID\n\t}\n\treturn \"\"\n}\n\n// generateTraceID generates a new Trace ID\n// generateTraceID 生成新的 Trace ID\nfunc generateTraceID() string {\n\treturn uuid.New().String()\n}\n\n// extractOrGenerateTraceID extracts or generates Trace ID from HTTP request\n// extractOrGenerateTraceID 从 HTTP 请求中提取或生成 Trace ID\nfunc extractOrGenerateTraceID(c *gin.Context) string {\n\t// Try to get from Header\n\t// extractOrGenerateTraceID 尝试从 Header 中获取\n\tif traceID := c.GetHeader(\"X-Trace-ID\"); traceID != \"\" {\n\t\treturn traceID\n\t}\n\tif traceID := c.GetHeader(\"X-Request-ID\"); traceID != \"\" {\n\t\treturn traceID\n\t}\n\t// Generate new Trace ID\n\t// 生成新的 Trace ID\n\treturn generateTraceID()\n}\n\n// wsLogger is the logger used by WebSocket module (injected via App Container)\n// wsLogger 是 WebSocket 模块使用的日志器（通过 App Container 注入）\nvar wsLogger *zap.Logger\n\n// wsProductionMode marks whether it is production mode (injected via App Container)\n// wsProductionMode 标记是否为生产模式（通过 App Container 注入）\nvar wsProductionMode bool\n\n// SetWSLogger sets the logger for WebSocket module\n// SetWSLogger 设置 WebSocket 模块的日志器\nfunc SetWSLogger(logger *zap.Logger) {\n\twsLogger = logger\n}\n\n// SetWSProductionMode sets the production mode flag for WebSocket module\n// SetWSProductionMode 设置 WebSocket 模块的生产模式标记\nfunc SetWSProductionMode(production bool) {\n\twsProductionMode = production\n}\n\n// isDevelopmentMode checks if it is development environment\n// isDevelopmentMode 检查是否为开发环境\n// Output colored console logs in development environment\n// 开发环境下会输出彩色控制台日志\nfunc isDevelopmentMode() bool {\n\treturn !wsProductionMode\n}\n\n// log records logs\n// log 记录日志\n// t: log type\n// t: 日志类型\n// msg: log message\n// msg: 日志消息\n// fields: zap log fields\n// fields: zap 日志字段\nfunc log(t LogType, msg string, fields ...zap.Field) {\n\tif wsLogger == nil {\n\t\treturn\n\t}\n\tswitch t {\n\tcase LogError:\n\t\twsLogger.Error(msg, fields...)\n\tcase LogWarn:\n\t\twsLogger.Warn(msg, fields...)\n\tcase LogInfo:\n\t\twsLogger.Info(msg, fields...)\n\tcase LogDebug:\n\t\twsLogger.Debug(msg, fields...)\n\t}\n}\n\n// logWithTraceID records logs, including Trace ID\n// logWithTraceID 记录日志，包含 Trace ID\nfunc logWithTraceID(t LogType, traceID string, msg string, fields ...zap.Field) {\n\tif traceID != \"\" {\n\t\tfields = append([]zap.Field{zap.String(\"traceId\", traceID)}, fields...)\n\t}\n\tlog(t, msg, fields...)\n}\n\n// NoteModifyLog records WebSocket operation logs\n// NoteModifyLog 记录 WebSocket 操作日志\n// Supports both structured logs and development environment colored output\n// 同时支持结构化日志和开发环境彩色输出\n// traceID: trace ID\n// traceID: 追踪 ID\n// uid: user ID\n// uid: 用户 ID\n// action: name of the operation executed\n// action: 执行的操作名称\n// params: variadic parameters, usually the first is Path, the second is Vault\n// params: 可变参数，通常第一个为 Path，第二个为 Vault\nfunc NoteModifyLog(traceID string, uid int64, action string, params ...string) {\n\tvar path, vault string\n\n\tif len(params) > 0 {\n\t\tpath = params[0]\n\t}\n\n\tif len(params) > 1 {\n\t\tvault = params[1]\n\t}\n\n\t// Structured log output (for log aggregation and analysis)\n\t// 结构化日志输出（用于日志聚合和分析）\n\tif wsLogger != nil {\n\t\twsLogger.Info(\"WebSocket action\",\n\t\t\tzap.String(logger.FieldTraceID, traceID),\n\t\t\tzap.Int64(logger.FieldUID, uid),\n\t\t\tzap.String(logger.FieldAction, action),\n\t\t\tzap.String(logger.FieldVault, vault),\n\t\t\tzap.String(logger.FieldPath, path),\n\t\t)\n\t}\n\n\t// Keep colored console output in development environment for easy local debugging\n\t// 开发环境保留彩色控制台输出，便于本地调试\n\tif isDevelopmentMode() {\n\t\tprintColoredLog(uid, action, traceID, vault, path)\n\t}\n}\n\n// printColoredLog outputs colored logs (development environment only)\n// printColoredLog 输出彩色日志（仅开发环境）\n// Use ANSI escape codes to achieve colored output\n// 使用 ANSI 转义码实现彩色输出\nfunc printColoredLog(uid int64, action, traceID, vault, path string) {\n\tstr := fmt.Sprintf(\"[WS] | \\033[30;43m %d \\033[0m\\033[97;44m %s \\033[0m\", uid, action)\n\n\tif traceID != \"\" && len(traceID) >= 8 {\n\t\tstr += fmt.Sprintf(\"\\033[90m[%s]\\033[0m \", traceID[:8]) // Only display the first 8 digits to keep it concise\n\t\t// Only display the first 8 digits to keep it concise\n\t\t// 只显示前8位以保持简洁\n\t}\n\n\tif vault != \"\" {\n\t\tstr += fmt.Sprintf(\"\\033[32m %s \\033[0m\", vault)\n\t}\n\n\tif path != \"\" {\n\t\tstr += fmt.Sprintf(\"\\033[32m %s \\033[0m\", path)\n\t}\n\n\tfmt.Println(str)\n}\n\ntype WebSocketMessage struct {\n\tType string `json:\"type\"` // Operation type, e.g., \"upload\", \"update\", \"delete\" // 操作类型，例如 \"upload\", \"update\", \"delete\"\n\tData []byte `json:\"data\"` // File data (only used for upload and update) // 文件数据（仅在上传和更新时使用）\n}\n\ntype ClientInfoMessage struct {\n\tName                string `json:\"name\"`                // Client name // 客户端名称\n\tVersion             string `json:\"version\"`             // Client version // 客户端版本\n\tType                string `json:\"type\"`                // Client type \"web\" | \"desktop\" | \"mobile\" | \"obsidianPlugin\" // 客户端类型 \"web\" | \"desktop\" | \"mobile\" | \"obsidianPlugin\"\n\tIsDesktop           bool   `json:\"isDesktop\"`           // Is desktop // 是否为桌面端\n\tIsMobile            bool   `json:\"isMobile\"`            // Is mobile // 是否为移动端\n\tIsPhone             bool   `json:\"isPhone\"`             // Is phone // 是否为手机\n\tIsTablet            bool   `json:\"isTablet\"`            // Is tablet // 是否为平板\n\tIsMacOS             bool   `json:\"isMacOS\"`             // Is macOS // 是否为 macOS\n\tIsWin               bool   `json:\"isWin\"`               // Is Windows // 是否为 Windows\n\tIsLinux             bool   `json:\"isLinux\"`             // Is Linux // 是否为 Linux\n\tOfflineSyncStrategy string `json:\"offlineSyncStrategy\"` // Offline device sync strategy \"newTimeMerge\" | \"ignoreTimeMerge\" // 离线设备同步策略 \"newTimeMerge\" | \"ignoreTimeMerge\"\n}\n\ntype WSConfig struct {\n\tGWSOption    gws.ServerOption\n\tPingInterval time.Duration\n\tPingWait     time.Duration\n}\n\n// SessionCleaner interface, used to clean up session resources when the connection is disconnected\n// SessionCleaner 接口，用于在连接断开时清理会话资源\ntype SessionCleaner interface {\n\tCleanup()\n}\n\n// DiffMergeEntry represents an entry in DiffMergePaths\n// DiffMergeEntry 表示 DiffMergePaths 中的条目\n// Contains creation timestamp for timeout cleanup mechanism\n// 包含创建时间戳，用于超时清理机制\ntype DiffMergeEntry struct {\n\tCreatedAt time.Time // Entry creation time // 条目创建时间\n}\n\n// WebsocketClient struct to store each WebSocket connection and its associated state\n// WebsocketClient 结构体来存储每个 WebSocket 连接及其相关状态\ntype WebsocketClient struct {\n\tconn                *gws.Conn                 // Underlying WebSocket connection handle // WebSocket 底层连接句柄\n\tdone                chan struct{}             // Close signal channel, used for graceful shutdown // 关闭信号通道，用于优雅关闭读/写协程\n\tapp                 AppContainer              // App Container reference // App Container 引用\n\tServer              *WebsocketServer          // WebSocket server reference // WebSocket 服务器引用，用于访问全局状态（如会话）\n\tCtx                 *gin.Context              // Original HTTP upgrade request context // 原始 HTTP 升级请求的上下文\n\tWsCtx               context.Context           // Long-lifecycle context for WebSocket connection // WebSocket 连接的长生命周期 context\n\tWsCancel            context.CancelFunc        // Used to cancel WsCtx // 用于取消 WsCtx\n\tTraceID             string                    // Trace ID of the connection // 连接的追踪 ID\n\tUser                *UserEntity               // Authenticated user info // 已认证用户信息，通常在握手阶段绑定\n\tUserClients         ConnStorage               // User connection pool // 用户连接池，支持多设备在线时广播或单点通信\n\tSF                  *singleflight.Group       // Concurrency control // 并发控制：相同 key 的请求只执行一次，其余等待结果\n\tBinaryMu            sync.Mutex                // Synchronization lock when reading and writing data // 用于读写数据时的同步锁 (不再保护 map 存储)\n\tClientName          string                    // Client name (e.g., \"Mac\", \"Windows\", \"iPhone\") // 客户端名称 (例如 \"Mac\", \"Windows\", \"iPhone\")\n\tClientType          string                    // Client type \"web\" | \"desktop\" | \"mobile\" | \"obsidianPlugin\" // 客户端类型 \"web\" | \"desktop\" | \"mobile\" | \"obsidianPlugin\"\n\tClientPlatform      map[string]bool           // Client platform details // 客户端平台详情\n\tClientVersion       string                    // Client version number (e.g., \"1.2.4\") // 客户端版本号 (例如 \"1.2.4\")\n\tStartTime           timex.Time                // Connection start time // 连接开始时间\n\tIsFirstSync         bool                      // Whether it's the first sync // 是否是第一次同步过\n\tDiffMergePaths      map[string]DiffMergeEntry // File paths needing merging // 需要合并的文件路径，包含创建时间用于超时清理\n\tDiffMergePathsMu    sync.RWMutex              // Mutex lock to prevent concurrency conflicts // 互斥锁，防止并发冲突\n\tOfflineSyncStrategy string                    // Offline device sync strategy // 离线设备同步策略 \"newTimeMerge\" | \"ignoreTimeMerge\"\n}\n\n// initContext initializes the context for the WebSocket connection\n// initContext 初始化 WebSocket 连接的 context\n// Called when building connection\n// 在连接建立时调用\nfunc (c *WebsocketClient) initContext(traceID string) {\n\tctx := context.Background()\n\tctx = context.WithValue(ctx, TraceIDKey, traceID)\n\tc.WsCtx, c.WsCancel = context.WithCancel(ctx)\n\tc.TraceID = traceID\n}\n\n// cancelContext cancels the context for the WebSocket connection\n// cancelContext 取消 WebSocket 连接的 context\n// Called when closing connection\n// 在连接关闭时调用\nfunc (c *WebsocketClient) cancelContext() {\n\tif c.WsCancel != nil {\n\t\tc.WsCancel()\n\t}\n}\n\n// Context returns the context of the WebSocket connection\n// Context 返回 WebSocket 连接的 context\n// Used for all operations requiring context (database queries, external calls, etc.)\n// 用于所有需要 context 的操作（数据库查询、外部调用等）\nfunc (c *WebsocketClient) Context() context.Context {\n\tif c.WsCtx == nil {\n\t\tpanic(\"WebsocketClient.WsCtx is not initialized\")\n\t}\n\treturn c.WsCtx\n}\n\n// WithTimeout creates a sub context with timeout\n// WithTimeout 创建带超时的子 context\n// Used for operations requiring timeout control\n// 用于需要超时控制的操作\nfunc (c *WebsocketClient) WithTimeout(timeout time.Duration) (context.Context, context.CancelFunc) {\n\treturn context.WithTimeout(c.WsCtx, timeout)\n}\n\n// CleanupExpiredDiffMergePaths cleans up expired DiffMergePaths entries\n// CleanupExpiredDiffMergePaths 清理过期的 DiffMergePaths 条目\n// timeout: timeout duration, entries exceeding this duration will be deleted\n// timeout: 超时时间，超过此时间的条目将被删除\nfunc (c *WebsocketClient) CleanupExpiredDiffMergePaths(timeout time.Duration) int {\n\tc.DiffMergePathsMu.Lock()\n\tdefer c.DiffMergePathsMu.Unlock()\n\n\tif c.DiffMergePaths == nil {\n\t\treturn 0\n\t}\n\n\tnow := time.Now()\n\tcleanedCount := 0\n\tfor path, entry := range c.DiffMergePaths {\n\t\tif now.Sub(entry.CreatedAt) > timeout {\n\t\t\tdelete(c.DiffMergePaths, path)\n\t\t\tcleanedCount++\n\t\t}\n\t}\n\treturn cleanedCount\n}\n\n// ClearAllDiffMergePaths cleans up all DiffMergePaths entries\n// ClearAllDiffMergePaths 清理所有 DiffMergePaths 条目\n// Called when closing connection\n// 在连接关闭时调用\nfunc (c *WebsocketClient) ClearAllDiffMergePaths() int {\n\tc.DiffMergePathsMu.Lock()\n\tdefer c.DiffMergePathsMu.Unlock()\n\n\tif c.DiffMergePaths == nil {\n\t\treturn 0\n\t}\n\n\tcount := len(c.DiffMergePaths)\n\tc.DiffMergePaths = make(map[string]DiffMergeEntry)\n\treturn count\n}\n\n// WebSocket version of parameter binding and validation utility functions based on global validator\n// 基于全局验证器的 WebSocket 版本参数绑定和验证工具函数\nfunc (c *WebsocketClient) BindAndValid(data []byte, obj any) (bool, ValidErrors) {\n\tvar errs ValidErrors\n\n\t// Step 1: JSON deserialization (can be replaced by other formats)\n\t// BindAndValid Step 1: JSON 反序列化（可替换成其他格式）\n\tif err := json.Unmarshal(data, obj); err != nil {\n\t\t// Decoding error handling\n\t\t// BindAndValid 解码错误处理\n\t\terrs = append(errs, &ValidError{\n\t\t\tKey:     \"body\",\n\t\t\tMessage: \"Invalid message format\",\n\t\t})\n\t\treturn false, errs\n\t}\n\n\t// Step 2: Parameter validation\n\t// Step 2: 参数验证\n\tvalidator := c.app.Validator()\n\tif validator == nil {\n\t\treturn true, nil\n\t}\n\tif err := validator.ValidateStruct(obj); err != nil {\n\t\t// If verification fails, check error type\n\t\t// 如果验证失败，检查错误类型\n\t\tif validationErrors, ok := err.(validatorV10.ValidationErrors); ok {\n\t\t\t// Get translator\n\t\t\t// 获取翻译器\n\t\t\tv := c.Ctx.Value(\"trans\")\n\t\t\ttrans := v.(ut.Translator)\n\n\t\t\t// Iterate through validation errors and translate them\n\t\t\t// 遍历验证错误并进行翻译\n\t\t\tfor _, validationErr := range validationErrors {\n\t\t\t\ttranslatedMsg := validationErr.Translate(trans) // Translate error message\n\t\t\t\t// Translate error message\n\t\t\t\t// 翻译错误消息\n\t\t\t\terrs = append(errs, &ValidError{\n\t\t\t\t\tKey:     validationErr.Field(),\n\t\t\t\t\tMessage: translatedMsg,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\treturn false, errs // Return validation error\n\t\t// 返回验证错误\n\t}\n\treturn true, nil\n}\n\n// Send Ping message regularly\n// 定期发送 Ping 消息\nfunc (c *WebsocketClient) PingLoop(PingInterval time.Duration) {\n\tticker := time.NewTicker(PingInterval * time.Second) // Send Ping every 25 seconds // 每 25 秒发送一次 Ping\n\tdefer ticker.Stop()\n\n\t// Periodic cleanup of expired conflict merge paths\n\t// 定期清理已过期的冲突合并路径\n\tcleanupTicker := time.NewTicker(10 * time.Minute)\n\tdefer cleanupTicker.Stop()\n\n\tfor {\n\t\tselect {\n\t\tcase <-c.done:\n\t\t\tlog(LogInfo, \"WS Client Close Ping\")\n\t\t\treturn\n\t\tcase <-ticker.C:\n\t\t\tif c.conn == nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err := c.conn.WritePing(nil); err != nil {\n\t\t\t\t// Normal error when the connection is closed, lower log level\n\t\t\t\t// 连接关闭时的正常错误，降低日志级别\n\t\t\t\tif strings.Contains(err.Error(), \"use of closed network connection\") {\n\t\t\t\t\tlog(LogDebug, \"WS Client Ping: connection closed\")\n\t\t\t\t} else {\n\t\t\t\t\tlog(LogError, \"WS Client Ping err \", zap.Error(err))\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// log(LogInfo, \"WS Client Ping\", zap.String(\"uid\", c.User.ID))\n\t\tcase <-cleanupTicker.C:\n\t\t\t// Cleanup items expired for more than 1 hour\n\t\t\t// 清理过期超过 1 小时的项\n\t\t\tif count := c.CleanupExpiredDiffMergePaths(1 * time.Hour); count > 0 {\n\t\t\t\tlog(LogDebug, \"PingLoop: cleaned up expired DiffMergePaths\",\n\t\t\t\t\tzap.Int(\"count\", count),\n\t\t\t\t\tzap.String(\"traceID\", c.TraceID))\n\t\t\t}\n\t\t}\n\t}\n}\n\n// ToResponse converts the result to JSON format and sends it to the client\n// ToResponse 将结果转换为 JSON 格式并发送给客户端\nfunc (c *WebsocketClient) ToResponse(code *code.Code, action ...string) {\n\n\tvar actionType string\n\tif len(action) > 0 {\n\t\tactionType = action[0]\n\t}\n\n\tvar responseBytes []byte\n\n\tcontent := Res{\n\t\tCode:    code.Code(),\n\t\tStatus:  code.Status(),\n\t\tMessage: code.Lang.GetMessage(),\n\t\tData:    code.Data(),\n\t}\n\n\tif code.HaveDetails() {\n\t\tcontent.Details = strings.Join(code.Details(), \",\")\n\t}\n\n\tif code.HaveVault() {\n\t\tcontent.Vault = code.Vault()\n\t}\n\tif code.HaveContext() {\n\t\tcontent.Context = code.Context()\n\t}\n\n\tresponseBytes, _ = json.Marshal(content)\n\n\tif actionType != \"\" {\n\t\tresponseBytes = []byte(fmt.Sprintf(`%s|%s`, actionType, string(responseBytes)))\n\t}\n\n\tif c.app.IsReturnSuccess() || actionType != \"\" || code.Code() > 200 || code.HaveData() || code.HaveDetails() {\n\t\tc.send(responseBytes, false, false)\n\t}\n}\n\n// BroadcastResponse converts the result to JSON format and broadcasts it to all connected clients of the current user\n// BroadcastResponse 将结果转换为 JSON 格式并广播给当前用户的所有连接客户端\n//\n// Parameters:\n// 参数:\n//\n//\tcode: business response status code object, including status code, message and data\n//\tcode: 业务响应状态码对象，包含状态码、消息和数据\n//\toptions: optional parameter list\n//\toptions: 可选参数列表\n//\t  - options[0] (bool):   required, whether to exclude the current client (true: exclude self, false: broadcast to all ends)\n//\t  - options[0] (bool):   必填，是否排除当前客户端 (true: 排除自己, false: 广播给所有端)\n//\t  - options[1] (string): optional, identification of action type (ActionType), used for clients to distinguish message types\n//\t  - options[1] (string): 选填，动作类型标识 (ActionType)，用于客户端区分消息类型\nfunc (c *WebsocketClient) BroadcastResponse(code *code.Code, options ...any) {\n\n\tvar actionType string\n\tif len(options) > 1 {\n\t\tactionType = options[1].(string)\n\t}\n\n\tif len(c.UserClients) <= 0 {\n\t\treturn\n\t}\n\n\tvar responseBytes []byte\n\n\tcontent := Res{\n\t\tCode:    code.Code(),\n\t\tStatus:  code.Status(),\n\t\tMessage: code.Lang.GetMessage(),\n\t\tData:    code.Data(),\n\t}\n\n\tif code.HaveDetails() {\n\t\tcontent.Details = strings.Join(code.Details(), \",\")\n\t}\n\n\tif code.HaveVault() {\n\t\tcontent.Vault = code.Vault()\n\t}\n\n\tif code.HaveContext() {\n\t\tcontent.Context = code.Context()\n\t}\n\n\tresponseBytes, _ = json.Marshal(content)\n\n\tif actionType != \"\" {\n\t\tresponseBytes = []byte(fmt.Sprintf(`%s|%s`, actionType, string(responseBytes)))\n\t}\n\n\tc.send(responseBytes, true, options[0].(bool))\n}\n\nfunc (c *WebsocketClient) send(responseBytes []byte, isBroadcast bool, isExcludeSelf bool) {\n\tif isBroadcast {\n\t\tc.sendBroadcast(responseBytes, isExcludeSelf)\n\t} else {\n\t\tc.sendMessage(responseBytes)\n\t}\n}\n\nfunc (c *WebsocketClient) sendMessage(payload []byte) {\n\tc.conn.WriteMessage(gws.OpcodeText, payload)\n}\n\nfunc (c *WebsocketClient) sendBroadcast(payload []byte, isExcludeSelf bool) {\n\tc.Server.mu.RLock()\n\tdefer c.Server.mu.RUnlock()\n\n\tvar b = gws.NewBroadcaster(gws.OpcodeText, payload)\n\tdefer b.Close()\n\n\tfor _, uc := range c.UserClients {\n\t\tif uc.conn == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif isExcludeSelf && uc.conn == c.conn {\n\t\t\tcontinue\n\t\t}\n\n\t\t_ = b.Broadcast(uc.conn)\n\t}\n}\n\n// SendBinary sends binary messages\n// SendBinary 发送二进制消息\n// prefix: 2-byte prefix\n// prefix: 2字节前缀\nfunc (c *WebsocketClient) SendBinary(prefix string, payload []byte) error {\n\tif c.conn == nil {\n\t\treturn fmt.Errorf(\"connection is nil\")\n\t}\n\tif len(prefix) != 2 {\n\t\treturn fmt.Errorf(\"prefix must be 2 bytes\")\n\t}\n\t// Concat prefix and data\n\t// 拼接前缀和数据\n\tdata := make([]byte, 2+len(payload))\n\tcopy(data[0:2], prefix)\n\tcopy(data[2:], payload)\n\treturn c.conn.WriteMessage(gws.OpcodeBinary, data)\n}\n\n// ------------------------------------> WebsocketServer\n\ntype ConnStorage = map[*gws.Conn]*WebsocketClient\n\n// AppContainer defines App Container interface, used to decouple pkg/app and internal/app\n// AppContainer 定义 App Container 接口，用于解耦 pkg/app 和 internal/app\n// This interface allows WebsocketServer to use App Container functions without circular dependency\n// 这个接口允许 WebsocketServer 使用 App Container 的功能而不产生循环依赖\ntype AppContainer interface {\n\t// Logger gets logger\n\t// Logger 获取日志器\n\tLogger() *zap.Logger\n\t// SubmitTask submits task to Worker Pool\n\t// SubmitTask 提交任务到 Worker Pool\n\tSubmitTask(ctx context.Context, task func(context.Context) error) error\n\t// SubmitTaskAsync submits task to Worker Pool asynchronously (without waiting for results)\n\t// SubmitTaskAsync 异步提交任务到 Worker Pool（不等待结果）\n\tSubmitTaskAsync(ctx context.Context, task func(context.Context) error) error\n\t// Version gets version info\n\t// Version 获取版本信息\n\tVersion() VersionInfo\n\t// CheckVersion checks version\n\t// CheckVersion 检查版本\n\tCheckVersion(pluginVersion string) CheckVersionInfo\n\t// Validator gets validator (may be nil)\n\t// Validator 获取验证器（可能为 nil）\n\tValidator() ValidatorInterface\n\t// IsReturnSuccess whether to return success response\n\t// IsReturnSuccess 是否返回成功响应\n\tIsReturnSuccess() bool\n\t// GetAuthTokenKey gets Token key\n\t// GetAuthTokenKey 获取 Token 密钥\n\tGetAuthTokenKey() string\n\t// IsProductionMode whether it is production mode\n\t// IsProductionMode 是否为生产模式\n\tIsProductionMode() bool\n}\n\n// ValidatorInterface validator interface\n// ValidatorInterface 验证器接口\ntype ValidatorInterface interface {\n\tValidateStruct(obj interface{}) error\n}\n\ntype WebsocketServer struct {\n\tapp               AppContainer // App Container (Required) // App Container（必须）\n\thandlers          map[string]func(*WebsocketClient, *WebSocketMessage)\n\tuserVerifyHandler func(*WebsocketClient, int64) (*UserSelectEntity, error)\n\tbinaryHandlers    map[string]func(*WebsocketClient, []byte) // Binary message handler map: prefix -> handler // 二进制消息处理器映射 prefix -> handler\n\tclients           ConnStorage\n\tuserClients       map[string]ConnStorage\n\tmu                sync.RWMutex\n\tup                *gws.Upgrader\n\tconfig            *WSConfig\n\t// Global session management (UID -> SessionID -> Session)\n\t// 全局会话管理 (UID -> SessionID -> Session)\n\tbinaryChunkSessions map[string]map[string]any\n\tsessionsMu          sync.RWMutex\n}\n\n// WSClientInfo WebSocket client information for API responses\n// WSClientInfo 用于 API 响应的 WebSocket 客户端信息\ntype WSClientInfo struct {\n\tUID           string          `json:\"uid\"`\n\tNickname      string          `json:\"nickname\"`\n\tClientName    string          `json:\"clientName\"`\n\tClientType    string          `json:\"clientType\"`\n\tClientVersion string          `json:\"clientVersion\"`\n\tPlatformInfo  map[string]bool `json:\"platformInfo\"`\n\tRemoteAddr    string          `json:\"remoteAddr\"`\n\tStartTime     timex.Time      `json:\"startTime\"`\n\tTraceID       string          `json:\"traceId\"`\n}\n\n// GetClients returns information of all currently connected WebSocket clients\n// GetClients 返回所有当前已连接的 WebSocket 客户端信息\nfunc (w *WebsocketServer) GetClients() []WSClientInfo {\n\tw.mu.RLock()\n\tdefer w.mu.RUnlock()\n\tclients := make([]WSClientInfo, 0, len(w.clients))\n\tfor _, c := range w.clients {\n\t\tinfo := WSClientInfo{\n\t\t\tClientName:    c.ClientName,\n\t\t\tClientType:    c.ClientType,\n\t\t\tClientVersion: c.ClientVersion,\n\t\t\tPlatformInfo:  c.ClientPlatform,\n\t\t\tRemoteAddr:    c.conn.RemoteAddr().String(),\n\t\t\tStartTime:     c.StartTime,\n\t\t\tTraceID:       c.TraceID,\n\t\t}\n\t\tif c.User != nil {\n\t\t\tinfo.UID = c.User.ID\n\t\t\tinfo.Nickname = c.User.Nickname\n\t\t}\n\t\tclients = append(clients, info)\n\t}\n\treturn clients\n}\n\n// NewWebsocketServer creates WebSocket server instance\n// NewWebsocketServer 创建 WebSocket 服务器实例\n// c: WebSocket config // c: WebSocket 配置\n// app: App Container (Required) // app: App Container（必须）\nfunc NewWebsocketServer(c WSConfig, app AppContainer) *WebsocketServer {\n\tif app == nil {\n\t\tpanic(\"AppContainer is required for WebsocketServer\")\n\t}\n\tif c.PingInterval == 0 {\n\t\tc.PingInterval = WSPingInterval\n\t}\n\tif c.PingWait == 0 {\n\t\tc.PingWait = WSPingWait\n\t}\n\n\t// Set logger for WebSocket module\n\t// 设置 WebSocket 模块的日志器\n\tSetWSLogger(app.Logger())\n\t// Set production mode flag for WebSocket module\n\t// 设置 WebSocket 模块的生产模式标记\n\tSetWSProductionMode(app.IsProductionMode())\n\n\treturn &WebsocketServer{\n\t\tapp:                 app,\n\t\thandlers:            make(map[string]func(*WebsocketClient, *WebSocketMessage)),\n\t\tbinaryHandlers:      make(map[string]func(*WebsocketClient, []byte)),\n\t\tclients:             make(ConnStorage),\n\t\tuserClients:         make(map[string]ConnStorage),\n\t\tconfig:              &c,\n\t\tbinaryChunkSessions: make(map[string]map[string]any),\n\t}\n}\n\n// App gets App Container\n// App 获取 App Container\nfunc (w *WebsocketServer) App() AppContainer {\n\treturn w.app\n}\n\nfunc (w *WebsocketServer) Upgrade() {\n\tw.up = gws.NewUpgrader(w, &w.config.GWSOption)\n}\n\nfunc (w *WebsocketServer) Run() gin.HandlerFunc {\n\n\treturn func(c *gin.Context) {\n\n\t\tw.Upgrade()\n\t\tsocket, err := w.up.Upgrade(c.Writer, c.Request)\n\t\tif err != nil {\n\t\t\tlog(LogError, \"WS Start err\", zap.Error(err))\n\t\t\treturn\n\t\t}\n\n\t\t// Extract or generate Trace ID from HTTP request\n\t\t// 从 HTTP 请求中提取或生成 Trace ID\n\t\ttraceID := extractOrGenerateTraceID(c)\n\n\t\tclient := &WebsocketClient{\n\t\t\tconn:      socket,\n\t\t\tdone:      make(chan struct{}),\n\t\t\tapp:       w.app,\n\t\t\tServer:    w,\n\t\t\tCtx:       c,\n\t\t\tSF:        new(singleflight.Group),\n\t\t\tStartTime: timex.Now(),\n\t\t}\n\n\t\t// Initialize long-lifecycle context for WebSocket connection\n\t\t// 初始化 WebSocket 连接的长生命周期 context\n\t\tclient.initContext(traceID)\n\n\t\tw.AddClient(client)\n\t\tlog(LogInfo, \"WS Start\", zap.String(\"type\", \"ReadLoop\"), zap.String(\"traceID\", traceID))\n\t\tgo socket.ReadLoop()\n\t}\n}\n\nfunc (w *WebsocketServer) Use(action string, handler func(*WebsocketClient, *WebSocketMessage)) {\n\tw.handlers[action] = handler\n}\n\nfunc (w *WebsocketServer) UseUserVerify(handler func(*WebsocketClient, int64) (*UserSelectEntity, error)) {\n\tw.userVerifyHandler = handler\n}\n\nfunc (w *WebsocketServer) UseBinary(prefix string, handler func(*WebsocketClient, []byte)) {\n\tif len(prefix) != 2 {\n\t\tpanic(\"binary message prefix must be 2 characters\")\n\t}\n\tw.binaryHandlers[prefix] = handler\n}\n\nfunc (w *WebsocketServer) Authorization(c *WebsocketClient, msg *WebSocketMessage) {\n\n\tsecretKey := w.app.GetAuthTokenKey()\n\tif user, err := ParseTokenWithKey(string(msg.Data), secretKey); err != nil {\n\t\tlog(LogError, \"WS Authorization FAILD\", zap.Error(err))\n\t\tc.ToResponse(code.ErrorInvalidUserAuthToken, \"Authorization\")\n\t\ttime.Sleep(2 * time.Second)\n\t\tc.conn.WriteClose(1000, []byte(\"AuthorizationFaild\"))\n\t} else {\n\n\t\tuid, err := strconv.ParseInt(user.ID, 10, 64)\n\t\tif err != nil {\n\t\t\tlog(LogError, \"WS Authorization FAILD\", zap.Error(err))\n\t\t\tc.ToResponse(code.ErrorInvalidUserAuthToken, \"Authorization\")\n\t\t\ttime.Sleep(2 * time.Second)\n\t\t\tc.conn.WriteClose(1000, []byte(\"AuthorizationFaild\"))\n\t\t\treturn\n\t\t}\n\n\t\t// Mandatorily verify user validity\n\t\t// 用户有效性强制验证\n\t\tuserSelect, err := w.userVerifyHandler(c, uid)\n\t\tif userSelect == nil || err != nil {\n\t\t\tlog(LogError, \"WS Authorization FAILD USER Not Exist\", zap.Error(err))\n\t\t\tc.ToResponse(code.ErrorInvalidUserAuthToken, \"Authorization\")\n\t\t\ttime.Sleep(2 * time.Second)\n\t\t\tc.conn.WriteClose(1000, []byte(\"AuthorizationFaild\"))\n\t\t\treturn\n\t\t}\n\n\t\tuser.Nickname = userSelect.Nickname\n\n\t\tlog(LogInfo, \"WS Authorization\", zap.String(\"uid\", user.ID), zap.String(\"Nickname\", user.Nickname))\n\t\tc.User = user\n\t\tc.UserClients = w.AddUserClient(c)\n\n\t\tversionInfo := w.app.Version()\n\n\t\tc.ToResponse(code.Success.WithData(map[string]string{\n\t\t\t\"version\":   versionInfo.Version,\n\t\t\t\"gitTag\":    versionInfo.GitTag,\n\t\t\t\"buildTime\": versionInfo.BuildTime,\n\t\t\t\"changelog\": versionInfo.Changelog,\n\t\t}), \"Authorization\")\n\t\tlog(LogInfo, \"WS User Enter\", zap.String(\"uid\", c.User.ID), zap.String(\"Nickname\", c.User.Nickname), zap.Int(\"Count\", len(c.UserClients)))\n\t\tgo c.PingLoop(w.config.PingInterval)\n\t}\n}\n\nfunc (w *WebsocketServer) ClientInfo(c *WebsocketClient, msg *WebSocketMessage) {\n\tvar info ClientInfoMessage\n\tif err := json.Unmarshal(msg.Data, &info); err != nil {\n\t\tlog(LogError, \"WS ClientInfo Unmarshal FAILD\", zap.Error(err))\n\t\tc.ToResponse(code.ErrorInvalidParams.WithDetails(err.Error()))\n\t\treturn\n\t}\n\n\tc.ClientName = info.Name\n\tc.ClientType = info.Type\n\tc.ClientVersion = info.Version\n\tc.ClientPlatform = map[string]bool{\n\t\t\"isDesktop\": info.IsDesktop,\n\t\t\"isMobile\":  info.IsMobile,\n\t\t\"isPhone\":   info.IsPhone,\n\t\t\"isTablet\":  info.IsTablet,\n\t\t\"isMacOS\":   info.IsMacOS,\n\t\t\"isWin\":     info.IsWin,\n\t\t\"isLinux\":   info.IsLinux,\n\t}\n\tc.OfflineSyncStrategy = info.OfflineSyncStrategy\n\tc.DiffMergePaths = make(map[string]DiffMergeEntry)\n\n\tlog(LogInfo, \"WS ClientInfo\", zap.String(\"uid\", func() string {\n\t\tif c.User != nil {\n\t\t\treturn c.User.ID\n\t\t}\n\t\treturn \"Guest\"\n\t}()), zap.String(\"name\", c.ClientName), zap.String(\"version\", c.ClientVersion), zap.String(\"offlineSyncStrategy\", c.OfflineSyncStrategy))\n\n\tcheckVersionInfo := w.app.CheckVersion(c.ClientVersion)\n\n\tc.ToResponse(code.Success.WithData(checkVersionInfo), \"ClientInfo\")\n}\n\n// BroadcastClientInfo broadcasts version information to all connected clients\n// BroadcastClientInfo 向所有连接的客户端广播版本信息\nfunc (w *WebsocketServer) BroadcastClientInfo() {\n\tw.mu.RLock()\n\tclients := make([]*WebsocketClient, 0, len(w.clients))\n\tfor _, c := range w.clients {\n\t\tclients = append(clients, c)\n\t}\n\tw.mu.RUnlock()\n\n\tfor _, c := range clients {\n\t\tif c.User == nil {\n\t\t\tcontinue\n\t\t}\n\t\tcheckVersionInfo := w.app.CheckVersion(c.ClientVersion)\n\t\t// Only push if there's a new version (server or plugin)\n\t\t// 只有当有新版本（服务端或插件）时才推送\n\t\tif checkVersionInfo.VersionIsNew || checkVersionInfo.PluginVersionIsNew {\n\t\t\tc.ToResponse(code.Success.WithData(checkVersionInfo), \"ClientInfo\")\n\t\t}\n\t}\n}\n\nfunc (w *WebsocketServer) GetClient(conn *gws.Conn) *WebsocketClient {\n\tw.mu.RLock()\n\tdefer w.mu.RUnlock()\n\treturn w.clients[conn]\n}\n\nfunc (w *WebsocketServer) AddClient(c *WebsocketClient) {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\tw.clients[c.conn] = c\n}\n\nfunc (w *WebsocketServer) RemoveClient(conn *gws.Conn) {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\tdelete(w.clients, conn)\n}\n\nfunc (w *WebsocketServer) AddUserClient(c *WebsocketClient) ConnStorage {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\tif w.userClients[c.User.ID] == nil {\n\t\tw.userClients[c.User.ID] = make(ConnStorage)\n\t}\n\tw.userClients[c.User.ID][c.conn] = c\n\treturn w.userClients[c.User.ID]\n}\n\nfunc (w *WebsocketServer) RemoveUserClient(c *WebsocketClient) {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\tif clients, ok := w.userClients[c.User.ID]; ok {\n\t\tdelete(clients, c.conn)\n\t\tif len(clients) == 0 {\n\t\t\tdelete(w.userClients, c.User.ID)\n\t\t}\n\t}\n\tlog(LogInfo, \"WS Client Remove\", zap.Int(\"userCount\", len(w.clients)))\n}\n\n// SetSession sets global binary upload session\n// SetSession 设置全局二进制上传会话\nfunc (w *WebsocketServer) SetSession(uid string, sessionID string, session any) {\n\tw.sessionsMu.Lock()\n\tdefer w.sessionsMu.Unlock()\n\tif w.binaryChunkSessions[uid] == nil {\n\t\tw.binaryChunkSessions[uid] = make(map[string]any)\n\t}\n\tw.binaryChunkSessions[uid][sessionID] = session\n}\n\n// GetSession gets global binary upload session\n// GetSession 获取全局二进制上传会话\nfunc (w *WebsocketServer) GetSession(uid string, sessionID string) any {\n\tw.sessionsMu.RLock()\n\tdefer w.sessionsMu.RUnlock()\n\tif userSessions, ok := w.binaryChunkSessions[uid]; ok {\n\t\treturn userSessions[sessionID]\n\t}\n\treturn nil\n}\n\n// RemoveSession removes global binary upload session\n// RemoveSession 移除全局二进制上传会话\nfunc (w *WebsocketServer) RemoveSession(uid string, sessionID string) {\n\tw.sessionsMu.Lock()\n\tdefer w.sessionsMu.Unlock()\n\tif userSessions, ok := w.binaryChunkSessions[uid]; ok {\n\t\tdelete(userSessions, sessionID)\n\t\tif len(userSessions) == 0 {\n\t\t\tdelete(w.binaryChunkSessions, uid)\n\t\t}\n\t}\n}\n\nfunc (w *WebsocketServer) OnOpen(conn *gws.Conn) {\n\tlog(LogInfo, \"WS Client Connect\", zap.Int(\"Count\", len(w.clients)))\n\t_ = conn.SetDeadline(time.Now().Add(w.config.PingWait * time.Second))\n}\n\nfunc (w *WebsocketServer) OnClose(conn *gws.Conn, err error) {\n\n\tc := w.GetClient(conn)\n\tif c == nil {\n\t\treturn\n\t}\n\n\t// First cancel the context of the WebSocket connection to notify all ongoing operations to stop\n\t// 首先取消 WebSocket 连接的 context，通知所有正在进行的操作停止\n\t// This must be performed before cleaning up other resources to ensure that all operations dependent on the context can receive the cancellation signal\n\t// 这必须在清理其他资源之前执行，以确保所有依赖 context 的操作能够收到取消信号\n\tc.cancelContext()\n\n\tw.RemoveClient(conn)\n\n\tif c.User != nil {\n\t\tselect {\n\t\tcase c.done <- struct{}{}:\n\t\tdefault:\n\t\t}\n\t\tlogLevel := LogInfo\n\t\tif err != nil && err != io.EOF {\n\t\t\tlogLevel = LogError\n\t\t}\n\t\tlog(logLevel, \"WS User Leave\", zap.String(\"uid\", c.User.ID), zap.String(\"traceID\", c.TraceID), zap.Error(err))\n\t\tw.RemoveUserClient(c)\n\t} else {\n\t\tlogLevel := LogInfo\n\t\tif err != nil && err != io.EOF {\n\t\t\tlogLevel = LogError\n\t\t}\n\t\tlog(logLevel, \"WS Client Leave (Unauth)\", zap.String(\"traceID\", c.TraceID), zap.Error(err))\n\t}\n\n\t// No longer clean up BinaryChunkSessions in OnClose, rely on the timeout mechanism for automatic cleanup instead\n\t// 不再在 OnClose 中清理 BinaryChunkSessions，改为依赖超时机制自动清理\n\t// This way, when a network fluctuation causes reconnection during a large file upload, the existing session can continue to be used\n\t// 这样可以支持在大文件上传过程中网络波动导致重连时，继续使用原有会话\n\n\t// Clean up all DiffMergePaths entries\n\t// 清理所有 DiffMergePaths 条目\n\tif diffMergeCount := c.ClearAllDiffMergePaths(); diffMergeCount > 0 {\n\t\tlog(LogInfo, \"OnClose: cleared DiffMergePaths on disconnect\",\n\t\t\tzap.Int(\"count\", diffMergeCount),\n\t\t\tzap.String(\"traceID\", c.TraceID))\n\t}\n\n\tlog(LogInfo, \"WS Client Leave\", zap.Int(\"Count\", len(w.clients)), zap.String(\"traceID\", c.TraceID))\n\n}\n\nfunc (w *WebsocketServer) OnPing(socket *gws.Conn, payload []byte) {\n\t_ = socket.SetDeadline(time.Now().Add(w.config.PingWait * time.Second))\n\t_ = socket.WritePong(nil)\n}\n\nfunc (w *WebsocketServer) OnPong(socket *gws.Conn, payload []byte) {\n\t_ = socket.SetDeadline(time.Now().Add(w.config.PingWait * time.Second))\n}\n\nfunc (w *WebsocketServer) OnMessage(conn *gws.Conn, message *gws.Message) {\n\tdefer message.Close()\n\tif message.Opcode != gws.OpcodeText && message.Opcode != gws.OpcodeBinary {\n\t\treturn\n\t}\n\tif message.Data.String() == \"close\" {\n\t\tconn.WriteClose(1000, []byte(\"ClientClose\"))\n\t\treturn\n\t}\n\n\tc := w.GetClient(conn)\n\tif c == nil {\n\t\treturn\n\t}\n\n\t// Set deadline\n\t// 设置延时\n\t_ = conn.SetDeadline(time.Now().Add(w.config.PingWait * time.Second))\n\n\tif message.Opcode == gws.OpcodeBinary {\n\t\tdata := message.Data.Bytes()\n\t\tif len(data) < 2 {\n\t\t\tlog(LogError, \"WS OnMessage Binary too short\", zap.String(\"uid\", c.User.ID))\n\t\t\treturn\n\t\t}\n\t\tprefix := string(data[:2])\n\t\tpayload := data[2:]\n\n\t\t// Create a deep copy of the payload to prevent gws from recycling or reusing the underlying buffer during asynchronous processing\n\t\t// 创建 payload 的深拷贝，防止异步处理时底层缓冲区被 gws 回收或重用\n\t\tpayloadCopy := make([]byte, len(payload))\n\t\tcopy(payloadCopy, payload)\n\n\t\tif handler, ok := w.binaryHandlers[prefix]; ok {\n\t\t\t// Submit task through Worker Pool\n\t\t\t// 通过 Worker Pool 提交任务\n\t\t\terr := w.app.SubmitTaskAsync(c.Context(), func(ctx context.Context) error {\n\t\t\t\t// Check if context is cancelled\n\t\t\t\t// 检查 context 是否已取消\n\t\t\t\tselect {\n\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\treturn ctx.Err()\n\t\t\t\tdefault:\n\t\t\t\t}\n\t\t\t\thandler(c, payloadCopy)\n\t\t\t\treturn nil\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\t// Worker Pool is full or closed, record error and return error response\n\t\t\t\t// Worker Pool 满载或已关闭，记录错误并返回错误响应\n\t\t\t\tlog(LogError, \"WS OnMessage Worker Pool error\",\n\t\t\t\t\tzap.String(\"prefix\", prefix),\n\t\t\t\t\tzap.String(\"uid\", c.User.ID),\n\t\t\t\t\tzap.Error(err))\n\t\t\t\tc.ToResponse(code.ErrorServerBusy)\n\t\t\t\treturn\n\t\t\t}\n\t\t} else {\n\t\t\tlog(LogWarn, \"WS OnMessage Unknown Binary Prefix\", zap.String(\"prefix\", prefix))\n\t\t}\n\t\treturn\n\t}\n\n\tmessageStr := message.Data.String()\n\t// Use strings.Index to find the position of the separator\n\t// 使用 strings.Index 找到分隔符的位置\n\tindex := strings.Index(messageStr, \"|\")\n\n\t//log(LogInfo, \"WS OnMessage\", zap.String(\"data\", messageStr))\n\n\tvar msg WebSocketMessage\n\tif index != -1 {\n\t\tmsg.Type = messageStr[:index]           // Extract the part before the separator // 提取分隔符之前的部分\n\t\tmsg.Data = []byte(messageStr[index+1:]) // Extract the part after the separator // 提取分隔符之后的部分\n\t} else {\n\t\tlog(LogError, \"WS OnMessage\", zap.String(\"type\", \"Illegal message type\"), zap.String(\"uid\", c.User.ID))\n\t\treturn\n\t}\n\n\tif msg.Type == \"Authorization\" {\n\t\tw.Authorization(c, &msg)\n\t\treturn\n\t}\n\n\tif msg.Type == \"ClientInfo\" {\n\t\tw.ClientInfo(c, &msg)\n\t\treturn\n\t}\n\n\t// Verify if the user is logged in\n\t// 验证用户是否登录\n\tif c.User == nil {\n\t\tlog(LogWarn, \"WS User not authenticated\",\n\t\t\tzap.String(\"msgType\", msg.Type),\n\t\t\tzap.String(\"traceId\", c.TraceID))\n\t\tc.ToResponse(code.ErrorNotUserAuthToken)\n\t\treturn\n\t}\n\n\t// Execute operation\n\t// 执行操作\n\thandler, exists := w.handlers[msg.Type]\n\tif exists {\n\t\t// Use the client object retrieved at the beginning of the function\n\t\thandler(c, &msg)\n\t} else {\n\t\tlog(LogError, \"WS Unknown Message\", zap.String(\"Type\", msg.Type), zap.String(\"uid\", c.User.ID))\n\t}\n}\n\nfunc (w *WebsocketServer) BroadcastToUser(uid int64, code *code.Code, action string) {\n\tuidStr := strconv.FormatInt(uid, 10)\n\tw.mu.RLock()\n\tdefer w.mu.RUnlock()\n\n\tuserClients, ok := w.userClients[uidStr]\n\tif !ok || len(userClients) == 0 {\n\t\treturn\n\t}\n\n\tvar responseBytes []byte\n\tcontent := Res{\n\t\tCode:    code.Code(),\n\t\tStatus:  code.Status(),\n\t\tMessage: code.Lang.GetMessage(),\n\t\tData:    code.Data(),\n\t}\n\n\tif code.HaveDetails() {\n\t\tcontent.Details = strings.Join(code.Details(), \",\")\n\t}\n\n\tif code.HaveVault() {\n\t\tcontent.Vault = code.Vault()\n\t}\n\n\tresponseBytes, _ = json.Marshal(content)\n\n\tif action != \"\" {\n\t\tresponseBytes = []byte(fmt.Sprintf(`%s|%s`, action, string(responseBytes)))\n\t}\n\n\tvar b = gws.NewBroadcaster(gws.OpcodeText, responseBytes)\n\tdefer b.Close()\n\n\tfor _, uc := range userClients {\n\t\tif uc.conn == nil {\n\t\t\tcontinue\n\t\t}\n\t\t_ = b.Broadcast(uc.conn)\n\t}\n}\n"
  },
  {
    "path": "pkg/app/websocket_client_test.go",
    "content": "package app\n\nimport (\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/leanovate/gopter\"\n\t\"github.com/leanovate/gopter/gen\"\n\t\"github.com/leanovate/gopter/prop\"\n)\n\n// 验证 DiffMergePaths 的检查-删除操作是原子的\n\nfunc TestProperty7_DiffMergePathsAtomicOperation(t *testing.T) {\n\tparameters := gopter.DefaultTestParameters()\n\tparameters.MinSuccessfulTests = 100\n\n\tproperties := gopter.NewProperties(parameters)\n\n\t// 并发访问时，每个 path 只能被处理一次\n\tproperties.Property(\"each path processed exactly once under concurrent access\", prop.ForAll(\n\t\tfunc(pathCount int) bool {\n\t\t\tif pathCount <= 0 {\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\t// 生成唯一路径\n\t\t\tpaths := make([]string, pathCount)\n\t\t\tfor i := 0; i < pathCount; i++ {\n\t\t\t\tpaths[i] = \"path_\" + string(rune('a'+i%26)) + string(rune('0'+i/26))\n\t\t\t}\n\n\t\t\tclient := &WebsocketClient{\n\t\t\t\tDiffMergePaths: make(map[string]DiffMergeEntry),\n\t\t\t}\n\n\t\t\t// 预填充所有路径\n\t\t\tfor _, p := range paths {\n\t\t\t\tclient.DiffMergePaths[p] = DiffMergeEntry{CreatedAt: time.Now()}\n\t\t\t}\n\n\t\t\t// 记录每个 path 被处理的次数\n\t\t\tprocessCount := make(map[string]*int32)\n\t\t\tfor _, p := range paths {\n\t\t\t\tvar count int32 = 0\n\t\t\t\tprocessCount[p] = &count\n\t\t\t}\n\n\t\t\t// 并发尝试处理每个 path\n\t\t\tvar wg sync.WaitGroup\n\t\t\tgoroutines := 10\n\n\t\t\tfor i := 0; i < goroutines; i++ {\n\t\t\t\twg.Add(1)\n\t\t\t\tgo func() {\n\t\t\t\t\tdefer wg.Done()\n\t\t\t\t\tfor _, p := range paths {\n\t\t\t\t\t\t// 原子检查-删除操作\n\t\t\t\t\t\tclient.DiffMergePathsMu.Lock()\n\t\t\t\t\t\t_, ok := client.DiffMergePaths[p]\n\t\t\t\t\t\tif ok {\n\t\t\t\t\t\t\tdelete(client.DiffMergePaths, p)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tclient.DiffMergePathsMu.Unlock()\n\n\t\t\t\t\t\tif ok {\n\t\t\t\t\t\t\tatomic.AddInt32(processCount[p], 1)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t\t}\n\n\t\t\twg.Wait()\n\n\t\t\t// 验证每个 path 只被处理一次\n\t\t\tfor _, p := range paths {\n\t\t\t\tif *processCount[p] != 1 {\n\t\t\t\t\tt.Logf(\"Path %s processed %d times, expected 1\", p, *processCount[p])\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn true\n\t\t},\n\t\tgen.IntRange(1, 20),\n\t))\n\n\tproperties.TestingRun(t)\n}\n\n// 验证超时清理机制\n\nfunc TestProperty8_DiffMergePathsCleanup(t *testing.T) {\n\tparameters := gopter.DefaultTestParameters()\n\tparameters.MinSuccessfulTests = 50\n\n\tproperties := gopter.NewProperties(parameters)\n\n\t// 超时条目被清理，未超时条目保留\n\tproperties.Property(\"expired entries are cleaned, non-expired are kept\", prop.ForAll(\n\t\tfunc(expiredCount, nonExpiredCount int) bool {\n\t\t\tclient := &WebsocketClient{\n\t\t\t\tDiffMergePaths: make(map[string]DiffMergeEntry),\n\t\t\t}\n\n\t\t\ttimeout := 100 * time.Millisecond\n\t\t\tnow := time.Now()\n\n\t\t\t// 添加过期条目\n\t\t\tfor i := 0; i < expiredCount; i++ {\n\t\t\t\tpath := \"expired_\" + string(rune('a'+i))\n\t\t\t\tclient.DiffMergePaths[path] = DiffMergeEntry{\n\t\t\t\t\tCreatedAt: now.Add(-timeout - time.Second), // 已过期\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 添加未过期条目\n\t\t\tfor i := 0; i < nonExpiredCount; i++ {\n\t\t\t\tpath := \"active_\" + string(rune('a'+i))\n\t\t\t\tclient.DiffMergePaths[path] = DiffMergeEntry{\n\t\t\t\t\tCreatedAt: now, // 未过期\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 执行清理\n\t\t\tcleaned := client.CleanupExpiredDiffMergePaths(timeout)\n\n\t\t\t// 验证清理数量\n\t\t\tif cleaned != expiredCount {\n\t\t\t\tt.Logf(\"Cleaned %d, expected %d\", cleaned, expiredCount)\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// 验证剩余数量\n\t\t\tif len(client.DiffMergePaths) != nonExpiredCount {\n\t\t\t\tt.Logf(\"Remaining %d, expected %d\", len(client.DiffMergePaths), nonExpiredCount)\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\treturn true\n\t\t},\n\t\tgen.IntRange(0, 10),\n\t\tgen.IntRange(0, 10),\n\t))\n\n\tproperties.TestingRun(t)\n}\n\n// 单元测试: DiffMergePaths 基本操作\nfunc TestDiffMergePaths_BasicOperations(t *testing.T) {\n\tclient := &WebsocketClient{\n\t\tDiffMergePaths: make(map[string]DiffMergeEntry),\n\t}\n\n\t// 测试添加\n\tpath := \"test/note.md\"\n\tclient.DiffMergePathsMu.Lock()\n\tclient.DiffMergePaths[path] = DiffMergeEntry{CreatedAt: time.Now()}\n\tclient.DiffMergePathsMu.Unlock()\n\n\t// 测试检查存在\n\tclient.DiffMergePathsMu.RLock()\n\t_, exists := client.DiffMergePaths[path]\n\tclient.DiffMergePathsMu.RUnlock()\n\n\tif !exists {\n\t\tt.Error(\"Path should exist after adding\")\n\t}\n\n\t// 测试原子检查-删除\n\tclient.DiffMergePathsMu.Lock()\n\t_, ok := client.DiffMergePaths[path]\n\tif ok {\n\t\tdelete(client.DiffMergePaths, path)\n\t}\n\tclient.DiffMergePathsMu.Unlock()\n\n\tif !ok {\n\t\tt.Error(\"Path should have been found and deleted\")\n\t}\n\n\t// 验证已删除\n\tclient.DiffMergePathsMu.RLock()\n\t_, exists = client.DiffMergePaths[path]\n\tclient.DiffMergePathsMu.RUnlock()\n\n\tif exists {\n\t\tt.Error(\"Path should not exist after deletion\")\n\t}\n}\n\n// 单元测试: ClearAllDiffMergePaths\nfunc TestClearAllDiffMergePaths(t *testing.T) {\n\tclient := &WebsocketClient{\n\t\tDiffMergePaths: make(map[string]DiffMergeEntry),\n\t}\n\n\t// 添加多个条目\n\tpaths := []string{\"a.md\", \"b.md\", \"c.md\"}\n\tfor _, p := range paths {\n\t\tclient.DiffMergePaths[p] = DiffMergeEntry{CreatedAt: time.Now()}\n\t}\n\n\t// 清理所有\n\tcount := client.ClearAllDiffMergePaths()\n\n\tif count != len(paths) {\n\t\tt.Errorf(\"ClearAllDiffMergePaths() = %d, want %d\", count, len(paths))\n\t}\n\n\tif len(client.DiffMergePaths) != 0 {\n\t\tt.Errorf(\"DiffMergePaths should be empty after clear, got %d\", len(client.DiffMergePaths))\n\t}\n}\n\n// 单元测试: CleanupExpiredDiffMergePaths\nfunc TestCleanupExpiredDiffMergePaths(t *testing.T) {\n\tclient := &WebsocketClient{\n\t\tDiffMergePaths: make(map[string]DiffMergeEntry),\n\t}\n\n\ttimeout := 50 * time.Millisecond\n\n\t// 添加一个过期条目\n\tclient.DiffMergePaths[\"expired.md\"] = DiffMergeEntry{\n\t\tCreatedAt: time.Now().Add(-100 * time.Millisecond),\n\t}\n\n\t// 添加一个未过期条目\n\tclient.DiffMergePaths[\"active.md\"] = DiffMergeEntry{\n\t\tCreatedAt: time.Now(),\n\t}\n\n\t// 执行清理\n\tcleaned := client.CleanupExpiredDiffMergePaths(timeout)\n\n\tif cleaned != 1 {\n\t\tt.Errorf(\"CleanupExpiredDiffMergePaths() = %d, want 1\", cleaned)\n\t}\n\n\tif len(client.DiffMergePaths) != 1 {\n\t\tt.Errorf(\"Should have 1 remaining entry, got %d\", len(client.DiffMergePaths))\n\t}\n\n\tif _, exists := client.DiffMergePaths[\"active.md\"]; !exists {\n\t\tt.Error(\"active.md should still exist\")\n\t}\n}\n"
  },
  {
    "path": "pkg/code/code.go",
    "content": "package code\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\n// Code is an immutable error code object // Code 是一个不可变的错误码对象\n// All With* methods return new instances, original object is not modified\n// 所有 With* 方法都返回新实例，不修改原对象\ntype Code struct {\n\tcode        int         // Status code // 状态码\n\tstatus      bool        // Status // 状态\n\tLang        lang        // Error message // 错误消息\n\tmsg         string      // Error message // 错误消息\n\tdata        interface{} // Data // 数据\n\tvault       string\n\thaveVault   bool     // Whether it contains Vault // 是否含有Vault\n\thaveData    bool     // Whether it contains Data // 是否含有Data\n\tdetails     []string // Error detail information // 错误详细信息\n\thaveDetails bool     // Whether it contains details // 是否含有详情\n\tcontext     string\n\thaveContext bool // Whether it contains Context // 是否含有Context\n}\n\nvar codes = map[int]string{}\nvar maxcode = 0\n\nfunc NewError(code int, reset ...bool) *Code {\n\tif _, ok := codes[code]; ok {\n\t\tpanic(fmt.Sprintf(\"错误码 %d 已经存在，请更换一个\", code))\n\t}\n\n\tl := getLang(code)\n\tcodes[code] = l.GetMessage()\n\n\tif code > maxcode {\n\t\tmaxcode = code\n\t}\n\n\tif len(reset) > 0 && reset[0] {\n\t\tmaxcode = 0\n\t}\n\n\treturn &Code{code: code, status: false, Lang: l}\n}\n\nfunc incr(code int) int {\n\tif code > maxcode {\n\t\treturn code\n\t} else {\n\t\treturn maxcode + 1\n\t}\n}\n\nvar sussCodes = map[int]string{}\n\nfunc NewSuss(code int) *Code {\n\tif _, ok := sussCodes[code]; ok {\n\t\tpanic(fmt.Sprintf(\"成功码 %d 已经存在，请更换一个\", code))\n\t}\n\tl := getLang(code)\n\tsussCodes[code] = l.GetMessage()\n\tif code > maxcode {\n\t\tmaxcode = code\n\t}\n\n\treturn &Code{code: code, status: true, Lang: l}\n}\n\nfunc getLang(code int) lang {\n\treturn lang{\n\t\tzh_cn: zh_cn_messages[code],\n\t\ten:    en_messages[code],\n\t}\n}\n\nfunc (e *Code) Error() string {\n\tif len(e.details) > 0 {\n\t\treturn fmt.Sprintf(\"%s: %s\", e.Msg(), strings.Join(e.details, \"; \"))\n\t}\n\treturn e.Msg()\n}\n\nfunc (e *Code) ErrorWithErr(err ...error) string {\n\tif len(err) > 0 {\n\t\treturn e.Msg() + \": \" + err[0].Error()\n\t}\n\treturn e.Msg()\n}\n\nfunc (e *Code) Code() int {\n\treturn e.code\n}\n\nfunc (e *Code) Status() bool {\n\treturn e.status\n}\n\nfunc (e *Code) Msg() string {\n\treturn e.Lang.GetMessage()\n}\n\nfunc (e *Code) Msgf(args []interface{}) string {\n\treturn fmt.Sprintf(e.msg, args...)\n}\n\nfunc (e *Code) Details() []string {\n\treturn e.details\n}\n\nfunc (e *Code) Data() interface{} {\n\treturn e.data\n}\n\nfunc (e *Code) Vault() string {\n\treturn e.vault\n}\n\nfunc (e *Code) Context() string {\n\treturn e.context\n}\n\nfunc (e *Code) HaveDetails() bool {\n\treturn e.haveDetails\n}\n\nfunc (e *Code) HaveData() bool {\n\treturn e.haveData\n}\n\nfunc (e *Code) HaveVault() bool {\n\treturn e.haveVault\n}\n\nfunc (e *Code) HaveContext() bool {\n\treturn e.haveContext\n}\n\n// WithData returns a new Code instance containing specified data\n// WithData 返回一个包含指定数据的新 Code 实例\n// Original object will not be modified (immutable design)\n// 原对象不会被修改（不可变设计）\nfunc (e *Code) WithData(data interface{}) *Code {\n\treturn &Code{\n\t\tcode:        e.code,\n\t\tstatus:      e.status,\n\t\tLang:        e.Lang,\n\t\tmsg:         e.msg,\n\t\tdata:        data,\n\t\thaveData:    true,\n\t\tvault:       e.vault,\n\t\thaveVault:   e.haveVault,\n\t\tdetails:     e.details,\n\t\thaveDetails: e.haveDetails,\n\t\tcontext:     e.context,\n\t\thaveContext: e.haveContext,\n\t}\n}\n\n// WithVault returns a new Code instance containing specified vault\n// WithVault 返回一个包含指定 vault 的新 Code 实例\n// Original object will not be modified (immutable design)\n// 原对象不会被修改（不可变设计）\nfunc (e *Code) WithVault(vault string) *Code {\n\treturn &Code{\n\t\tcode:        e.code,\n\t\tstatus:      e.status,\n\t\tLang:        e.Lang,\n\t\tmsg:         e.msg,\n\t\tdata:        e.data,\n\t\thaveData:    e.haveData,\n\t\tvault:       vault,\n\t\thaveVault:   true,\n\t\tdetails:     e.details,\n\t\thaveDetails: e.haveDetails,\n\t\tcontext:     e.context,\n\t\thaveContext: e.haveContext,\n\t}\n}\n\n// WithDetails returns a new Code instance containing specified details\n// WithDetails 返回一个包含指定详情的新 Code 实例\n// Original object will not be modified (immutable design)\n// 原对象不会被修改（不可变设计）\nfunc (e *Code) WithDetails(details ...string) *Code {\n\t// Create a copy of details to avoid shared underlying array\n\t// 创建 details 的副本，避免共享底层数组\n\tnewDetails := make([]string, len(details))\n\tcopy(newDetails, details)\n\n\treturn &Code{\n\t\tcode:        e.code,\n\t\tstatus:      e.status,\n\t\tLang:        e.Lang,\n\t\tmsg:         e.msg,\n\t\tdata:        e.data,\n\t\thaveData:    e.haveData,\n\t\tvault:       e.vault,\n\t\thaveVault:   e.haveVault,\n\t\tdetails:     newDetails,\n\t\thaveDetails: true,\n\t\tcontext:     e.context,\n\t\thaveContext: e.haveContext,\n\t}\n}\n\n// WithContext returns a new Code instance containing specified context\n// WithContext 返回一个包含指定上下文的新 Code 实例\n// Original object will not be modified (immutable design)\n// 原对象不会被修改（不可变设计）\nfunc (e *Code) WithContext(context string) *Code {\n\treturn &Code{\n\t\tcode:        e.code,\n\t\tstatus:      e.status,\n\t\tLang:        e.Lang,\n\t\tmsg:         e.msg,\n\t\tdata:        e.data,\n\t\thaveData:    e.haveData,\n\t\tvault:       e.vault,\n\t\thaveVault:   e.haveVault,\n\t\tdetails:     e.details,\n\t\thaveDetails: e.haveDetails,\n\t\tcontext:     context,\n\t\thaveContext: true,\n\t}\n}\n\nfunc (e *Code) StatusCode() int {\n\treturn http.StatusOK\n}\n"
  },
  {
    "path": "pkg/code/code_test.go",
    "content": "package code\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestCode_Immutability_And_Methods(t *testing.T) {\n\t// 临时注册一个错误码用于测试，避免和 common 里的冲突\n\tcodeVal := 999901\n\tmsgZh := \"测试错误\"\n\tmsgEn := \"Test Error\"\n\t\n\tzh_cn_messages[codeVal] = msgZh\n\ten_messages[codeVal] = msgEn\n\n\tc := NewError(codeVal)\n\tassert.Equal(t, codeVal, c.Code())\n\tassert.False(t, c.Status())\n\t// Default language logic depends on getLang config, let's just make sure Msg() won't panic\n\tassert.NotEmpty(t, c.Msg())\n\t\n\t// Test Immutability\n\tcWithData := c.WithData(map[string]string{\"foo\": \"bar\"})\n\tassert.NotEqual(t, fmt.Sprintf(\"%p\", c), fmt.Sprintf(\"%p\", cWithData))\n\tassert.False(t, c.HaveData())\n\tassert.True(t, cWithData.HaveData())\n\tassert.Equal(t, map[string]string{\"foo\": \"bar\"}, cWithData.Data())\n\n\tcWithVault := c.WithVault(\"test-vault\")\n\tassert.NotEqual(t, fmt.Sprintf(\"%p\", c), fmt.Sprintf(\"%p\", cWithVault))\n\tassert.False(t, c.HaveVault())\n\tassert.True(t, cWithVault.HaveVault())\n\tassert.Equal(t, \"test-vault\", cWithVault.Vault())\n\n\tcWithDetails := c.WithDetails(\"detail1\", \"detail2\")\n\tassert.NotEqual(t, fmt.Sprintf(\"%p\", c), fmt.Sprintf(\"%p\", cWithDetails))\n\tassert.False(t, c.HaveDetails())\n\tassert.True(t, cWithDetails.HaveDetails())\n\tassert.Equal(t, []string{\"detail1\", \"detail2\"}, cWithDetails.Details())\n\n\tcWithContext := c.WithContext(\"test-context\")\n\tassert.NotEqual(t, fmt.Sprintf(\"%p\", c), fmt.Sprintf(\"%p\", cWithContext))\n\tassert.False(t, c.HaveContext())\n\tassert.True(t, cWithContext.HaveContext())\n\tassert.Equal(t, \"test-context\", cWithContext.Context())\n}\n\nfunc TestNewError_DuplicatePanic(t *testing.T) {\n\t// 期望注册重复的错误码时触发 panic\n\tcodeVal := 999902\n\tzh_cn_messages[codeVal] = \"测试崩溃\"\n\ten_messages[codeVal] = \"Test Panic\"\n\n\tNewError(codeVal) // 第一次注册成功\n\tassert.Panics(t, func() {\n\t\tNewError(codeVal) // 第二次注册失败并 Panic\n\t})\n}\n\nfunc TestNewSuss(t *testing.T) {\n\tcodeVal := 999903\n\tzh_cn_messages[codeVal] = \"测试成功\"\n\ten_messages[codeVal] = \"Test Success\"\n\n\ts := NewSuss(codeVal)\n\tassert.Equal(t, codeVal, s.Code())\n\tassert.True(t, s.Status())\n\tassert.NotEmpty(t, s.Msg())\n\n\tassert.Panics(t, func() {\n\t\tNewSuss(codeVal) // duplicate suss code\n\t})\n}\n"
  },
  {
    "path": "pkg/code/common.go",
    "content": "package code\n\nvar (\n\tFailed                = NewError(0)\n\tSuccess               = NewSuss(1)\n\tSuccessCreate         = NewSuss(2)\n\tSuccessUpdate         = NewSuss(3)\n\tSuccessDelete         = NewSuss(4)\n\tSuccessPasswordUpdate = NewSuss(5)\n\tSuccessNoUpdate       = NewSuss(6)\n\n\tErrorServerInternal       = NewError(300, true)\n\tErrorDBQuery              = NewError(301, true)\n\tErrorServerBusy           = NewError(302, true)\n\tErrorTooManyRequests      = NewError(303, true)\n\tErrorNotFoundAPI          = NewError(304, true)\n\tErrorInvalidParams        = NewError(305)\n\tErrorInvalidAuthToken     = NewError(306)\n\tErrorNotUserAuthToken     = NewError(307)\n\tErrorInvalidUserAuthToken = NewError(308)\n\tErrorInvalidToken         = NewError(309)\n\tErrorTokenExpired         = NewError(310)\n\tErrorTokenGenerate        = NewError(311)\n\n\t// --- User Related (400-419) ---\n\tErrorUserRegister            = NewError(400)\n\tErrorUserLoginFailed         = NewError(401)\n\tErrorUserLoginPasswordFailed = NewError(402)\n\tErrorUserNotFound            = NewError(403)\n\tErrorUserAlreadyExists       = NewError(404)\n\tErrorUserEmailAlreadyExists  = NewError(405)\n\tErrorUserUsernameNotValid    = NewError(406)\n\tErrorPasswordNotValid        = NewError(407)\n\tErrorUserOldPasswordFailed   = NewError(408)\n\tErrorUserPasswordNotMatch    = NewError(409)\n\tErrorUserRegisterIsDisable   = NewError(410)\n\tErrorUserIsNotAdmin          = NewError(411)\n\tErrorUserLocalFSDisabled     = NewError(412)\n\n\t// --- Vault Related (420-429) ---\n\tErrorVaultNotFound           = NewError(420)\n\tErrorVaultExist              = NewError(421)\n\tErrorInvalidStorageType      = NewError(422)\n\tErrorInvalidCloudStorageType = NewError(423)\n\n\t// --- Note Related (430-444) ---\n\tErrorNoteNotFound             = NewError(430)\n\tErrorNoteExist                = NewError(431)\n\tErrorNoteGetFailed            = NewError(432)\n\tErrorNoteModifyOrCreateFailed = NewError(433)\n\tErrorNoteContentModifyFailed  = NewError(434)\n\tErrorNoteDeleteFailed         = NewError(435)\n\tErrorNoteListFailed           = NewError(436)\n\tErrorNoteRenameFailed         = NewError(437)\n\tErrorRenameNoteTargetExist    = NewError(438)\n\tErrorNoteSyncFailed           = NewError(439)\n\tErrorNoteUpdateCheckFailed    = NewError(440)\n\tErrorNoteConflict             = NewError(441)\n\tErrorNoMatchFound             = NewError(442)\n\tErrorInvalidRegex             = NewError(443)\n\tErrorInvalidPath              = NewError(444)\n\n\t// --- Folder Related (445-454) ---\n\tErrorFolderNotFound             = NewError(445)\n\tErrorFolderExist                = NewError(446)\n\tErrorFolderGetFailed            = NewError(447)\n\tErrorFolderModifyOrCreateFailed = NewError(448)\n\tErrorFolderDeleteFailed         = NewError(449)\n\tErrorFolderListFailed           = NewError(450)\n\tErrorFolderRenameFailed         = NewError(451)\n\n\t// --- File/Attachment Related (455-469) ---\n\tErrorFileNotFound              = NewError(455)\n\tErrorFileGetFailed             = NewError(456)\n\tErrorFileModifyOrCreateFailed  = NewError(457)\n\tErrorFileContentModifyFailed   = NewError(458)\n\tErrorFileDeleteFailed          = NewError(459)\n\tErrorFileListFailed            = NewError(460)\n\tErrorFileUploadFailed          = NewError(461)\n\tErrorFileUploadCheckFailed     = NewError(462)\n\tErrorFileUploadSessionNotFound = NewError(463)\n\tErrorFileSaveFailed            = NewError(464)\n\tErrorFileRenameFailed          = NewError(465)\n\tErrorFileReadFailed            = NewError(466)\n\tErrorFileExist                 = NewError(467)\n\n\t// --- Setting Related (470-479) ---\n\tErrorSettingNotFound             = NewError(470)\n\tErrorSettingExist                = NewError(471)\n\tErrorSettingGetFailed            = NewError(472)\n\tErrorSettingModifyOrCreateFailed = NewError(473)\n\tErrorSettingContentModifyFailed  = NewError(474)\n\tErrorSettingDeleteFailed         = NewError(475)\n\tErrorSettingListFailed           = NewError(476)\n\tErrorSettingSyncFailed           = NewError(477)\n\tErrorSettingUpdateCheckFailed    = NewError(478)\n\tErrorConfigSaveFailed            = NewError(479)\n\n\t// --- Share Related (480-489) ---\n\tErrorShareNotFound         = NewError(480)\n\tErrorShareExpired          = NewError(481)\n\tErrorShareRevoked          = NewError(482)\n\tErrorSharePasswordRequired = NewError(483)\n\tErrorSharePasswordInvalid  = NewError(484)\n\n\t// --- Sync & History (490-499) ---\n\tErrorHistoryNotFound        = NewError(491)\n\tErrorHistoryRestoreFailed   = NewError(492)\n\tErrorBackupConfigNotFound   = NewError(493)\n\tErrorBackupTaskFailed       = NewError(494)\n\tErrorBackupTypeUnknown      = NewError(495)\n\tErrorBackupExecuteIDReq     = NewError(496)\n\tErrorBackupVaultRequired    = NewError(497)\n\tErrorBackupStorageIDInvalid = NewError(498)\n\tErrorBackupConfigDisabled   = NewError(499)\n\n\t// --- Storage Related (500-509) ---\n\tErrorStorageNotFound       = NewError(500)\n\tErrorStorageTypeDisabled   = NewError(501)\n\tErrorStorageValidateFailed = NewError(502)\n\n\t// --- Git Sync Related (510-519) ---\n\tErrorGitSyncNotFound       = NewError(510)\n\tErrorGitSyncTaskRunning    = NewError(511)\n\tErrorGitSyncValidateFailed = NewError(512)\n\n\t// --- Cloudflare Related (520-529) ---\n\tErrorCloudflaredDownloadFailed = NewError(520)\n)\n"
  },
  {
    "path": "pkg/code/lang.go",
    "content": "package code\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n)\n\n// lang type, used to store English and Chinese text\n// lang 类型，用来存储英文和中文文本\ntype lang struct {\n\ten    string // English // 英文\n\tzh_cn string // Chinese // 中文\n}\n\n// Default language is English // 默认语言为英文\nvar lng = \"en\"\n\nconst FALLBACK_LNG = \"en\"\n\n// GetMessage method returns the corresponding message according to the passed language\n// GetMessage 方法根据传入的语言返回相应的消息\nfunc (l lang) GetMessage() string {\n\tif lng == \"\" {\n\t\tlng = FALLBACK_LNG\n\t}\n\t// Get language field\n\t// 获取语言字段\n\tval := reflect.ValueOf(l)\n\tfield := val.FieldByName(lng)\n\t// If the language field is valid and not empty, return the message in that language\n\t// 如果语言字段有效且非空，返回该语言的消息\n\tif field.IsValid() && field.String() != \"\" {\n\t\treturn field.String()\n\t}\n\t// If the specified language is invalid, return the message of the fallback language\n\t// 如果指定语言无效，返回回退语言的消息\n\tfallbackField := val.FieldByName(FALLBACK_LNG)\n\tif fallbackField.IsValid() && fallbackField.String() != \"\" {\n\t\treturn fallbackField.String()\n\t}\n\t// If the fallback language has no message either, return the default error message\n\t// 如果回退语言也没有消息，返回默认的错误信息\n\treturn fmt.Sprintf(\"No message available for language: %s\", lng)\n}\n\n// GetSupportedLanguages function returns all languages supported by the lang type\n// GetSupportedLanguages 函数返回 lang 类型支持的所有语言\nfunc GetSupportedLanguages() []string {\n\tvar languages []string\n\t// Get the fields of the lang type through reflection\n\t// 通过反射获取 lang 类型的字段\n\ttyp := reflect.TypeOf(lang{})\n\t// Traverse the fields of the struct and get the field names\n\t// 遍历结构体的字段，获取字段名\n\tfor i := 0; i < typ.NumField(); i++ {\n\t\tfield := typ.Field(i)\n\t\tlanguages = append(languages, field.Name)\n\t}\n\treturn languages\n}\n\n// SetGlobalDefaultLang sets the global default language\n// 设置全局默认语言\nfunc SetGlobalDefaultLang(language string) error {\n\t// Supported language list\n\t// 支持的语言列表\n\tsupportedLanguages := GetSupportedLanguages()\n\n\t// Check if the language is in the list of supported languages\n\t// 检查语言是否在支持的语言列表中\n\tisValidLang := false\n\tfor _, lang := range supportedLanguages {\n\t\tif language == lang {\n\t\t\tisValidLang = true\n\t\t\tbreak\n\t\t}\n\t}\n\t// If the language is valid, set the global language\n\t// 如果语言有效，设置全局语言\n\tif isValidLang {\n\t\tlng = language\n\t\treturn nil\n\t}\n\t// If the language is invalid, return an error and set it to the default language\n\t// 如果语言无效，返回错误并设置为默认语言\n\tlng = FALLBACK_LNG\n\treturn errors.New(\"unsupported language type, set defaulting to \" + FALLBACK_LNG)\n}\n\n// GetGlobalDefaultLang gets the global default language\n// 设置全局默认语言\nfunc GetGlobalDefaultLang() string {\n\treturn lng\n}\n"
  },
  {
    "path": "pkg/code/msg_en.go",
    "content": "package code\n\nvar en_messages = map[int]string{\n\t0:   \"Failed\",\n\t1:   \"Success\",\n\t2:   \"Create Success\",\n\t3:   \"Update Success\",\n\t4:   \"Delete Success\",\n\t5:   \"Password Update Success\",\n\t6:   \"No Update\",\n\t300: \"Server Internal Error\",\n\t301: \"Database query failed\",\n\t302: \"Server busy, please try again later\",\n\t303: \"Too Many Requests\",\n\t304: \"Not Found API\",\n\t305: \"Invalid Params\",\n\t306: \"Invalid Auth Token\",\n\t307: \"Not logged in. Please log in first.\",\n\t308: \"Session expired, please log in again.\",\n\t309: \"Your access is missing a user token\",\n\t310: \"User token has expired\",\n\t311: \"Token generation failed\",\n\n\t// --- User Related (400-419) ---\n\t400: \"User registration failed\",\n\t401: \"User login failed\",\n\t402: \"Invalid username or password\",\n\t403: \"Username does not exist\",\n\t404: \"Username already exists\",\n\t405: \"User email already exists\",\n\t406: \"The username does not meet the rules, the username length is 3-15 digits, and can only contain letters, numbers or underscores\",\n\t407: \"Password does not meet the rules\",\n\t408: \"Current password verification error\",\n\t409: \"The two passwords entered are inconsistent\",\n\t410: \"User registration is closed, please contact the administrator to configure the config.user.register-is-enable option\",\n\t411: \"This operation requires administrator privileges\",\n\t412: \"User local file system is disabled\",\n\n\t// --- Vault Related (420-429) ---\n\t420: \"Note Vault does not exist\",\n\t421: \"Note Vault already exists\",\n\t422: \"Invalid storage type\",\n\t423: \"Invalid cloud storage type\",\n\n\t// --- Note Related (430-444) ---\n\t430: \"Note does not exist\",\n\t431: \"Note already exists\",\n\t432: \"Get note failed\",\n\t433: \"Note modify or create failed\",\n\t434: \"Modify note content failed\",\n\t435: \"Delete note failed\",\n\t436: \"Note list get failed\",\n\t437: \"Note rename failed\",\n\t438: \"Note rename target note already exists\",\n\t439: \"Sync note failed\",\n\t440: \"Note update check failed\",\n\t441: \"Destination note already exists\",\n\t442: \"No match found\",\n\t443: \"Invalid regex pattern\",\n\t444: \"Invalid path: path cannot contain '..'\",\n\n\t// --- Folder Related (445-454) ---\n\t445: \"Folder does not exist\",\n\t446: \"Folder already exists\",\n\t447: \"Get folder failed\",\n\t448: \"Folder modify or create failed\",\n\t449: \"Delete folder failed\",\n\t450: \"Folder list get failed\",\n\t451: \"Folder rename failed\",\n\n\t// --- File/Attachment Related (455-469) ---\n\t455: \"File does not exist\",\n\t456: \"Get file failed\",\n\t457: \"File modify or create failed\",\n\t458: \"Modify file content failed\",\n\t459: \"Delete file failed\",\n\t460: \"File list get failed\",\n\t461: \"Upload file failed\",\n\t462: \"File upload check failed\",\n\t463: \"Upload file session not found\",\n\t464: \"File save failed\",\n\t465: \"File move failed\",\n\t466: \"File read failed\",\n\t467: \"File already exists\",\n\n\t// --- Setting Related (470-479) ---\n\t470: \"Setting does not exist\",\n\t471: \"Setting already exists\",\n\t472: \"Get setting failed\",\n\t473: \"Setting modify or create failed\",\n\t474: \"Modify setting content failed\",\n\t475: \"Delete setting failed\",\n\t476: \"Setting list get failed\",\n\t477: \"Sync setting failed\",\n\t478: \"Setting update check failed\",\n\t479: \"Config save failed\",\n\n\t// --- Share Related (480-489) ---\n\t480: \"Share link not found\",\n\t481: \"Share link has expired\",\n\t482: \"Share link has been revoked\",\n\t483: \"Password required for viewing\",\n\t484: \"Invalid share password\",\n\n\t// --- Sync & History (490-499) ---\n\t490: \"Note modification conflict detected. Full content has been preserved and requires manual handling.\",\n\t491: \"History not found\",\n\t492: \"Failed to restore history\",\n\t493: \"Sync & backup task does not exist\",\n\t494: \"Failed to execute sync & backup task\",\n\t495: \"Unknown sync & backup type\",\n\t496: \"Sync & backup task ID is required\",\n\t497: \"Note repository ID is required\",\n\t498: \"Invalid storage ID list in sync & backup task settings\",\n\t499: \"Sync & backup task is disabled\",\n\t500: \"Storage configuration does not exist\",\n\t501: \"This storage type has been disabled\",\n\t502: \"Storage connection test failed\",\n\n\t// Git Sync\n\t510: \"Git sync configuration does not exist\",\n\t511: \"Git sync task is already running\",\n\t512: \"Git validation failed\",\n\t520: \"Cloudflared download failed\",\n}\n"
  },
  {
    "path": "pkg/code/msg_zh_cn.go",
    "content": "package code\n\nvar zh_cn_messages = map[int]string{\n\t0:   \"失败\",\n\t1:   \"成功\",\n\t2:   \"创建成功\",\n\t3:   \"更新成功\",\n\t4:   \"删除成功\",\n\t5:   \"密码修改成功\",\n\t6:   \"无更新\",\n\t300: \"服务器内部错误\",\n\t301: \"数据库查询失败\",\n\t302: \"服务器繁忙，请稍后重试\",\n\t303: \"请求过多\",\n\t304: \"找不到API\",\n\t305: \"参数验证失败\",\n\t306: \"访问令牌效验失败\",\n\t307: \"尚未登录,请先登录\",\n\t308: \"登录状态失效,请重新登录\",\n\t309: \"您的访问缺少用户令牌\",\n\t310: \"用户令牌已过期\",\n\t311: \"令牌生成失败\",\n\n\t// --- User Related (400-419) ---\n\t// --- 用户相关 (400-419) ---\n\t400: \"用户注册失败\",\n\t401: \"用户登录失败\",\n\t402: \"用户名或密码错误\",\n\t403: \"用户不存在\",\n\t404: \"用户已经存在\",\n\t405: \"用户邮箱已存在\",\n\t406: \"用户名不符合规则,用户名长度为3-15位,只能包含字母、数字或下划线\",\n\t407: \"密码不符合规则\",\n\t408: \"当前密码验证错误\",\n\t409: \"两次输入的密码不一致\",\n\t410: \"用户注册已关闭,请联系管理员配置 config.user.register-is-enable 选项\",\n\t411: \"此操作需要管理员权限\",\n\t412: \"用户本地文件系统已禁用\",\n\n\t// --- Vault Related (420-429) ---\n\t// --- 仓库相关 (420-429) ---\n\t420: \"笔记仓库不存在\",\n\t421: \"笔记仓库已经存在\",\n\t422: \"存储类型无效\",\n\t423: \"云存储类型无效\",\n\n\t// --- Note Related (430-444) ---\n\t// --- 笔记相关 (430-444) ---\n\t430: \"笔记不存在\",\n\t431: \"笔记已经存在\",\n\t432: \"获取笔记失败\",\n\t433: \"笔记修改或创建失败\",\n\t434: \"修改笔记内容失败\",\n\t435: \"笔记删除失败\",\n\t436: \"笔记列表获取失败\",\n\t437: \"笔记重命名失败\",\n\t438: \"笔记重命名目标笔记已经存在\",\n\t439: \"同步笔记失败\",\n\t440: \"笔记更新检查失败\",\n\t441: \"目标笔记已存在\",\n\t442: \"未找到匹配内容\",\n\t443: \"无效的正则表达式\",\n\t444: \"无效的路径：路径不能包含 '..'\",\n\n\t// --- Folder Related (445-454) ---\n\t// --- 文件夹相关 (445-454) ---\n\t445: \"文件夹不存在\",\n\t446: \"文件夹已经存在\",\n\t447: \"获取文件夹失败\",\n\t448: \"文件夹修改或创建失败\",\n\t449: \"文件夹删除失败\",\n\t450: \"文件夹列表获取失败\",\n\t451: \"文件夹重命名失败\",\n\n\t// --- Attachment/File Related (455-469) ---\n\t// --- 附件/文件相关 (455-469) ---\n\t455: \"附件不存在\",\n\t456: \"获取附件失败\",\n\t457: \"附件修改 or 创建失败\",\n\t458: \"修改附件内容失败\",\n\t459: \"附件删除失败\",\n\t460: \"附件列表获取失败\",\n\t461: \"上传附件失败\",\n\t462: \"上传附件失败检测失败\",\n\t463: \"上传附件会话不存在\",\n\t464: \"文件保存失败\",\n\t465: \"文件移动失败\",\n\t466: \"文件读取失败\",\n\t467: \"文件已经存在\",\n\n\t// --- Config Related (470-479) ---\n\t// --- 配置相关 (470-479) ---\n\t470: \"配置不存在\",\n\t471: \"配置已经存在\",\n\t472: \"获取配置失败\",\n\t473: \"配置修改或创建失败\",\n\t474: \"修改配置内容失败\",\n\t475: \"配置删除失败\",\n\t476: \"配置列表获取失败\",\n\t477: \"同步配置失败\",\n\t478: \"配置更新检查失败\",\n\t479: \"配置保存失败\",\n\n\t// --- Share Related (480-489) ---\n\t// --- 分享相关 (480-489) ---\n\t480: \"分享链接不存在\",\n\t481: \"分享链接已过期\",\n\t482: \"分享链接已被撤销\",\n\t483: \"需要密码才能查看\",\n\t484: \"分享密码错误\",\n\n\t// --- Sync & History Related (490-499) ---\n\t// --- 同步与历史 (490-499) ---\n\t490: \"检测到笔记修改冲突，已保留完整内容，需要手动处理\",\n\t491: \"未找到历史记录\",\n\t492: \"历史记录恢复失败\",\n\t493: \"同步&备份任务不存在\",\n\t494: \"同步&备份任务执行失败\",\n\t495: \"未知的同步&备份类型\",\n\t496: \"同步&备份任务 ID 是必填项\",\n\t497: \"笔记仓库 ID 是必填项\",\n\t498: \"同步&备份任务设置中的存储 ID 列表无效\",\n\t499: \"同步&备份任务已禁用\",\n\n\t500: \"存储配置不存在\",\n\t501: \"该存储类型已被禁用\",\n\t502: \"存储连接测试失败\",\n\n\t// Git Sync\n\t510: \"Git 同步配置不存在\",\n\t511: \"Git 同步任务正在运行中\",\n\t512: \"Git 验证失败\",\n\t520: \"Cloudflared 下载失败\",\n}\n"
  },
  {
    "path": "pkg/convert/bool_int.go",
    "content": "package convert\n\n// Bool2Int converts a boolean to an integer\n// Bool2Int 将布尔值转换为整数\n// b: boolean value // 布尔值\n// return: 1 if true, 0 if false // 返回值: true 返回 1，false 返回 0\nfunc Bool2Int(b bool) int64 {\n\tvar i int64\n\tif b {\n\t\ti = 1\n\t} else {\n\t\ti = 0\n\t}\n\treturn i\n}\n"
  },
  {
    "path": "pkg/convert/convert.go",
    "content": "package convert\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n)\n\ntype StrTo string\n\nfunc (s StrTo) String() string {\n\treturn string(s)\n}\n\nfunc (s StrTo) Int() (int, error) {\n\tv, err := strconv.Atoi(s.String())\n\treturn v, err\n}\n\nfunc (s StrTo) MustInt() int {\n\tv, _ := s.Int()\n\treturn v\n}\n\nfunc (s StrTo) UInt32() (uint32, error) {\n\tv, err := strconv.Atoi(s.String())\n\treturn uint32(v), err\n}\nfunc (s StrTo) Int64() (int64, error) {\n\tv, err := strconv.Atoi(s.String())\n\treturn int64(v), err\n}\n\nfunc (s StrTo) MustInt64() int64 {\n\tv, _ := s.Int64()\n\treturn v\n}\n\nfunc (s StrTo) MustUInt32() uint32 {\n\tv, _ := s.UInt32()\n\treturn v\n}\n\n// ToSize converts a string to byte size, supporting KB, MB, B suffixes\n// ToSize 将字符串转换为字节大小，支持 KB, MB, B 后缀\nfunc (s StrTo) ToSize() (int64, error) {\n\tsizeStr := strings.ToUpper(strings.TrimSpace(s.String()))\n\tif sizeStr == \"\" {\n\t\treturn 0, nil\n\t}\n\n\tvar multiplier int64 = 1\n\tif strings.HasSuffix(sizeStr, \"MB\") {\n\t\tmultiplier = 1024 * 1024\n\t\tsizeStr = strings.TrimSuffix(sizeStr, \"MB\")\n\t} else if strings.HasSuffix(sizeStr, \"KB\") {\n\t\tmultiplier = 1024\n\t\tsizeStr = strings.TrimSuffix(sizeStr, \"KB\")\n\t} else if strings.HasSuffix(sizeStr, \"B\") {\n\t\tmultiplier = 1\n\t\tsizeStr = strings.TrimSuffix(sizeStr, \"B\")\n\t}\n\n\tsize, err := strconv.ParseInt(strings.TrimSpace(sizeStr), 10, 64)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn size * multiplier, nil\n}\n\n// MustToSize converts a string to byte size, returns default value if error occurs\n// MustToSize 将字符串转换为字节大小，如果出错返回默认值\nfunc (s StrTo) MustToSize(defaultVal int64) int64 {\n\tv, err := s.ToSize()\n\tif err != nil || v <= 0 {\n\t\treturn defaultVal\n\t}\n\treturn v\n}\n"
  },
  {
    "path": "pkg/convert/convert_test.go",
    "content": "package convert\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestBool2Int(t *testing.T) {\n\tassert.Equal(t, int64(1), Bool2Int(true))\n\tassert.Equal(t, int64(0), Bool2Int(false))\n}\n\nfunc TestStrTo_Conversions(t *testing.T) {\n\ts := StrTo(\"123\")\n\tassert.Equal(t, \"123\", s.String())\n\n\tval, err := s.Int()\n\tassert.NoError(t, err)\n\tassert.Equal(t, 123, val)\n\tassert.Equal(t, 123, s.MustInt())\n\n\tuVal, err := s.UInt32()\n\tassert.NoError(t, err)\n\tassert.Equal(t, uint32(123), uVal)\n\tassert.Equal(t, uint32(123), s.MustUInt32())\n\n\ti64Val, err := s.Int64()\n\tassert.NoError(t, err)\n\tassert.Equal(t, int64(123), i64Val)\n\tassert.Equal(t, int64(123), s.MustInt64())\n}\n\nfunc TestStrTo_ToSize(t *testing.T) {\n\ttests := []struct {\n\t\tinput       string\n\t\texpected    int64\n\t\texpectError bool\n\t}{\n\t\t{\"\", 0, false},\n\t\t{\"1024\", 1024, false},\n\t\t{\"1 KB\", 1024, false},\n\t\t{\"2MB\", 2 * 1024 * 1024, false},\n\t\t{\"10 B\", 10, false},\n\t\t{\"invalid\", 0, true},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.input, func(t *testing.T) {\n\t\t\ts := StrTo(tt.input)\n\t\t\tval, err := s.ToSize()\n\t\t\tif tt.expectError {\n\t\t\t\tassert.Error(t, err)\n\t\t\t} else {\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\tassert.Equal(t, tt.expected, val)\n\t\t\t}\n\t\t\t// Test MustToSize behavior\n\t\t\tif tt.expectError {\n\t\t\t\tassert.Equal(t, int64(999), s.MustToSize(999))\n\t\t\t} else {\n\t\t\t\tvar mustVal int64\n\t\t\t\tif tt.expected > 0 {\n\t\t\t\t\tmustVal = tt.expected\n\t\t\t\t} else {\n\t\t\t\t\tmustVal = 999\n\t\t\t\t}\n\t\t\t\tassert.Equal(t, mustVal, s.MustToSize(999))\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype TestSrc struct {\n\tID   int\n\tName string\n}\n\ntype TestDst struct {\n\tID   int\n\tName string\n\tDesc string\n}\n\nfunc TestStructAssign(t *testing.T) {\n\tsrc := TestSrc{ID: 1, Name: \"test\"}\n\tdst := TestDst{Desc: \"desc\"}\n\n\tStructAssign(&src, &dst)\n\n\tassert.Equal(t, 1, dst.ID)\n\tassert.Equal(t, \"test\", dst.Name)\n\tassert.Equal(t, \"desc\", dst.Desc)\n}\n\nfunc TestStructAssign2(t *testing.T) {\n\tsrc := TestSrc{ID: 1, Name: \"test\"}\n\tdst := &TestDst{Desc: \"desc\"}\n\n\tStructAssign2(&src, dst)\n\n\tassert.Equal(t, 1, dst.ID)\n\tassert.Equal(t, \"test\", dst.Name)\n\tassert.Equal(t, \"desc\", dst.Desc)\n}\n\nfunc TestStructToMap(t *testing.T) {\n\tsrc := TestSrc{ID: 1, Name: \"test\"}\n\tdata := make(map[string]interface{})\n\terr := StructToMap(src, data)\n\n\tassert.NoError(t, err)\n\tassert.Equal(t, float64(1), data[\"ID\"]) // JSON unmarshals numbers to float64 by default\n\tassert.Equal(t, \"test\", data[\"Name\"])\n}\n\nfunc TestStructToMapByReflect(t *testing.T) {\n\tsrc := TestSrc{ID: 1, Name: \"test\"}\n\tres := StructToMapByReflect(src)\n\n\tassert.NotNil(t, res)\n\tassert.Equal(t, 1, res[\"ID\"])\n\tassert.Equal(t, \"test\", res[\"Name\"])\n}\n\nfunc TestCamelCaseConversions(t *testing.T) {\n\tassert.Equal(t, \"hello_world\", Camel2Case(\"HelloWorld\"))\n\tassert.Equal(t, \"HelloWorld\", Case2Camel(\"hello_world\"))\n\tassert.Equal(t, \"helloWorld\", Case2LowerCamel(\"hello_world\"))\n\tassert.Equal(t, \"Hello\", Ucfirst(\"hello\"))\n\tassert.Equal(t, \"hELLO\", Lcfirst(\"HELLO\"))\n}\n\nfunc TestMapAnyToMapStr(t *testing.T) {\n\tinput := map[string]interface{}{\n\t\t\"id\":   123,\n\t\t\"name\": \"test\",\n\t\t\"flag\": true,\n\t}\n\n\tresult := MapAnyToMapStr(input)\n\tassert.Equal(t, \"123\", result[\"id\"])\n\tassert.Equal(t, \"test\", result[\"name\"])\n\tassert.Equal(t, \"true\", result[\"flag\"])\n}\n"
  },
  {
    "path": "pkg/convert/copy_struct.go",
    "content": "package convert\n\nimport (\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/json\"\n\t\"github.com/jinzhu/copier\"\n\t\"github.com/pkg/errors\"\n)\n\nfunc GetCopyStructFields(source interface{}, target interface{}) []interface{} {\n\t// 1. 调用您的第一个函数获取 source 的字段名列表\n\tsourceFieldsList := GetStructFieldNames(source)\n\tif len(sourceFieldsList) == 0 {\n\t\treturn nil\n\t}\n\n\t// 2. 将列表转为 map，方便 O(1) 复杂度查找\n\tsourceFieldsMap := make(map[string]bool)\n\tfor _, name := range sourceFieldsList {\n\t\tsourceFieldsMap[name] = true\n\t}\n\n\t// 3. 准备提取 target 中的值\n\tvar result []interface{}\n\ttVal := reflect.ValueOf(target)\n\n\t// 处理 target 的指针\n\tif tVal.Kind() == reflect.Ptr {\n\t\ttVal = tVal.Elem()\n\t}\n\n\t// 确保 target 是结构体\n\tif tVal.Kind() != reflect.Struct {\n\t\treturn nil\n\t}\n\n\ttTyp := tVal.Type()\n\tfor i := 0; i < tVal.NumField(); i++ {\n\t\tfieldName := tTyp.Field(i).Name\n\t\t// 如果 target 的这个字段名在 source 中也存在\n\t\tif sourceFieldsMap[fieldName] {\n\t\t\t// 获取字段的值\n\t\t\tfieldVal := tVal.Field(i)\n\n\t\t\t// 只有导出字段才能调用 Interface()，否则会 panic\n\t\t\tif fieldVal.CanInterface() {\n\t\t\t\tresult = append(result, fieldVal.Interface())\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result\n}\n\n// GetStructFields 返回传入结构体的所有字段名\nfunc GetStructFieldNames(input interface{}) []string {\n\tgetType := reflect.TypeOf(input)\n\n\t// 如果传入的是指针，获取其指向的元素类型\n\tif getType.Kind() == reflect.Ptr {\n\t\tgetType = getType.Elem()\n\t}\n\n\t// 确保最终处理的是结构体\n\tif getType.Kind() != reflect.Struct {\n\t\treturn nil\n\t}\n\n\tfields := make([]string, 0, getType.NumField())\n\tfor i := 0; i < getType.NumField(); i++ {\n\t\tfield := getType.Field(i)\n\t\t// 如果只想获取导出字段（大写开头的），可以直接添加\n\t\t// 如果需要处理嵌套结构体或匿名首元，可在此添加逻辑\n\t\tfields = append(fields, field.Name)\n\t}\n\n\treturn fields\n}\n\n// CopyStruct\n// dst 目标结构体，src 源结构体\n// 它会把src与dst的相同字段名的值，复制到dst中\nfunc StructAssign2(src any, dst any) any {\n\n\tbVal := reflect.ValueOf(dst).Elem() // 获取reflect.Type类型\n\tvVal := reflect.ValueOf(src).Elem() // 获取reflect.Type类型\n\tvTypeOfT := vVal.Type()\n\tfor i := 0; i < vVal.NumField(); i++ {\n\t\t// 在要修改的结构体中查询有数据结构体中相同属性的字段，有则修改其值\n\t\tname := vTypeOfT.Field(i).Name\n\t\tif ok := bVal.FieldByName(name).IsValid(); ok {\n\t\t\tbVal.FieldByName(name).Set(reflect.ValueOf(vVal.Field(i).Interface()))\n\t\t}\n\t}\n\n\treturn dst\n}\n\n// CopyStruct\n// dst 目标结构体，src 源结构体\n// 它会把src与dst的相同字段名的值，复制到dst中\nfunc StructAssign(src any, dst any) any {\n\tcopier.Copy(dst, src)\n\treturn dst\n}\n\n/**\n * @Description: 结构体map互转\n * @param param interface{} 需要被转的数据\n * @param data interface{} 转换完成后的数据  需要用引用传进来\n * @return []string{}\n */\nfunc StructToMap(param any, data map[string]interface{}) error {\n\tstr, _ := json.Marshal(param)\n\terror := json.Unmarshal(str, &data)\n\tif error != nil {\n\t\treturn error\n\t} else {\n\t\treturn nil\n\t}\n\n}\n\nfunc StructToMapByReflect(obj any) map[string]any {\n\tval := reflect.ValueOf(obj)\n\tif val.Kind() == reflect.Ptr {\n\t\tval = val.Elem()\n\t}\n\tif val.Kind() != reflect.Struct {\n\t\treturn nil\n\t}\n\n\tresult := make(map[string]any)\n\ttyp := val.Type()\n\n\tfor i := 0; i < val.NumField(); i++ {\n\t\tfield := val.Field(i)\n\n\t\tfieldName := typ.Field(i).Name\n\t\tif field.CanInterface() {\n\t\t\t// 如果字段是 Struct，递归处理\n\t\t\tif field.Kind() == reflect.Struct {\n\t\t\t\tresult[fieldName] = StructToMapByReflect(field.Interface())\n\t\t\t} else {\n\t\t\t\tresult[fieldName] = field.Interface()\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result\n}\n\nfunc StructToModelMap(param any, data map[string]any, key string) error {\n\n\t// 获取反射值\n\tval := reflect.ValueOf(param)\n\n\tif val.Kind() == reflect.Ptr {\n\t\tval = val.Elem()\n\t}\n\n\t// 确保传入的是结构体\n\tif val.Kind() != reflect.Struct {\n\t\treturn errors.New(\"not struct\")\n\t}\n\n\t// 获取结构体类型\n\ttyp := val.Type()\n\n\t// 遍历结构体字段\n\tfor i := 0; i < val.NumField(); i++ {\n\n\t\tif key == \"\" || typ.Field(i).Name == key {\n\t\t\tcontinue\n\t\t}\n\n\t\t// 获取 GORM 的 column 标签\n\t\ttags := splitGormTag(typ.Field(i).Tag.Get(\"gorm\"))\n\n\t\tif tags[\"column\"] != \"\" {\n\t\t\tdata[tags[\"column\"]] = val.Field(i).Interface()\n\t\t}\n\t}\n\n\treturn nil\n\n}\n\n// 分割 GORM 标签\nfunc splitGormTag(tag string) map[string]string {\n\ttags := strings.Split(tag, \";\")\n\n\tparts := make(map[string]string, 0)\n\n\tfor _, part := range tags {\n\t\tkv := strings.SplitN(part, \":\", 2)\n\t\tif len(kv) == 2 {\n\t\t\tparts[kv[0]] = kv[1]\n\t\t}\n\t}\n\n\treturn parts\n}\n"
  },
  {
    "path": "pkg/convert/json.go",
    "content": "package convert\n\nimport (\n\t\"bytes\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"golang.org/x/text/cases\"\n\t\"golang.org/x/text/language\"\n)\n\n// 驼峰式写法转为下划线写法\nfunc Camel2Case(name string) string {\n\tbuffer := NewBuffer()\n\tfor i, r := range name {\n\t\tif unicode.IsUpper(r) {\n\t\t\tif i != 0 {\n\t\t\t\tbuffer.Append('_')\n\t\t\t}\n\t\t\tbuffer.Append(unicode.ToLower(r))\n\t\t} else {\n\t\t\tbuffer.Append(r)\n\t\t}\n\t}\n\treturn buffer.String()\n}\n\n// 下划线写法转为驼峰写法\nfunc Case2Camel(name string) string {\n\tname = strings.Replace(name, \"_\", \" \", -1)\n\tname = cases.Title(language.Und, cases.NoLower).String(name)\n\treturn strings.Replace(name, \" \", \"\", -1)\n}\n\n// 下划线写法转为驼峰写法\nfunc Case2LowerCamel(name string) string {\n\treturn Lcfirst(Case2Camel(name))\n}\n\n// 首字母大写\nfunc Ucfirst(str string) string {\n\tfor i, v := range str {\n\t\treturn string(unicode.ToUpper(v)) + str[i+1:]\n\t}\n\treturn \"\"\n}\n\n// 首字母小写\nfunc Lcfirst(str string) string {\n\tfor i, v := range str {\n\t\treturn string(unicode.ToLower(v)) + str[i+1:]\n\t}\n\treturn \"\"\n}\n\n// 内嵌bytes.Buffer，支持连写\ntype Buffer struct {\n\t*bytes.Buffer\n}\n\nfunc NewBuffer() *Buffer {\n\treturn &Buffer{Buffer: new(bytes.Buffer)}\n}\n\nfunc (b *Buffer) Append(i interface{}) *Buffer {\n\tswitch val := i.(type) {\n\tcase int:\n\t\tb.append(strconv.Itoa(val))\n\tcase int64:\n\t\tb.append(strconv.FormatInt(val, 10))\n\tcase uint:\n\t\tb.append(strconv.FormatUint(uint64(val), 10))\n\tcase uint64:\n\t\tb.append(strconv.FormatUint(val, 10))\n\tcase string:\n\t\tb.append(val)\n\tcase []byte:\n\t\tb.Write(val)\n\tcase rune:\n\t\tb.WriteRune(val)\n\t}\n\treturn b\n}\n\nfunc (b *Buffer) append(s string) *Buffer {\n\tdefer func() {\n\t\tif err := recover(); err != nil {\n\t\t\t// 内存不足时的 panic 恢复，此处无法使用注入的 logger\n\t\t\t// 因为这是底层工具函数，保持静默恢复\n\t\t\t_ = err\n\t\t}\n\t}()\n\tb.WriteString(s)\n\treturn b\n}\n"
  },
  {
    "path": "pkg/convert/map.go",
    "content": "package convert\n\nimport (\n\t\"fmt\"\n)\n\nfunc MapAnyToMapStr(p map[string]interface{}) map[string]string {\n\tmapString := make(map[string]string)\n\n\tfor key, value := range p {\n\t\tstrKey := fmt.Sprintf(\"%v\", key)\n\t\tstrValue := fmt.Sprintf(\"%v\", value)\n\n\t\tmapString[strKey] = strValue\n\t}\n\treturn mapString\n}\n"
  },
  {
    "path": "pkg/diff/diff.go",
    "content": "package diff\n\nimport (\n\t\"errors\"\n\n\t\"github.com/sergi/go-diff/diffmatchpatch\"\n)\n\n// MergeResult merge result // 合并结果\ntype MergeResult struct {\n\tContent      string // Merged content // 合并后的内容\n\tHasConflict  bool   // Whether conflict exists // 是否存在冲突\n\tConflictInfo string // Conflict details // 冲突详情\n}\n\n// textRange represents a region in text // textRange 表示文本中的一个区域\ntype textRange struct {\n\tStart int\n\tEnd   int\n}\n\n// insertInfo details of insert operation // 插入操作的详细信息\ntype insertInfo struct {\n\tPosition int    // Insert position // 插入位置\n\tContent  string // Insert content // 插入内容\n}\n\n// MergeTexts three-way text merge\n// MergeTexts 三方合并文本\n// Retain delete operations after refactoring, detect delete-modify conflict\n// 重构后保留删除操作，检测删除-修改冲突\nfunc MergeTexts(base, pc1, pc2 string, pc1First bool) (MergeResult, error) {\n\t// Fast path: Solve the issue where dmp PatchApply fails due to double application when both sides execute the same modification.\n\t// 快速路径：解决当两端执行完全相同修改时，dmp PatchApply 因为二次应用导致失败的问题。\n\tif pc1 == pc2 {\n\t\treturn MergeResult{\n\t\t\tContent:     pc1,\n\t\t\tHasConflict: false,\n\t\t}, nil\n\t}\n\n\t// Fast path: If one side has no modification, return the other side directly\n\t// 快速路径：如果一端没有修改，直接返回另一端\n\tif pc1 == base {\n\t\treturn MergeResult{\n\t\t\tContent:     pc2,\n\t\t\tHasConflict: false,\n\t\t}, nil\n\t}\n\tif pc2 == base {\n\t\treturn MergeResult{\n\t\t\tContent:     pc1,\n\t\t\tHasConflict: false,\n\t\t}, nil\n\t}\n\n\tdmp := diffmatchpatch.New()\n\n\t// Calculate diff of PC1 relative to base (keep delete operations)\n\t// 计算 PC1 相对于 base 的 diff（保留删除操作）\n\tpc1Diffs := dmp.DiffMain(base, pc1, false)\n\tpc1Patches := dmp.PatchMake(base, pc1Diffs)\n\n\t// Calculate diff of PC2 relative to base (keep delete operations)\n\t// 计算 PC2 相对于 base 的 diff（保留删除操作）\n\tpc2Diffs := dmp.DiffMain(base, pc2, false)\n\tpc2Patches := dmp.PatchMake(base, pc2Diffs)\n\n\t// Detect conflicts\n\t// 检测冲突\n\tif hasConflict(pc1Diffs, pc2Diffs) {\n\t\treturn MergeResult{\n\t\t\tHasConflict:  true,\n\t\t\tConflictInfo: \"conflict detected\",\n\t\t}, nil\n\t}\n\n\t// 根据 pc1First 参数决定应用顺序\n\tvar step1Result string\n\tvar step1Success []bool\n\tvar step2Success []bool\n\tvar merged string\n\n\tif pc1First {\n\t\t// Apply PC1 first, then PC2\n\t\t// 先应用 PC1，再应用 PC2\n\t\tstep1Result, step1Success = dmp.PatchApply(pc1Patches, base)\n\t\tmerged, step2Success = dmp.PatchApply(pc2Patches, step1Result)\n\t} else {\n\t\t// Apply PC2 first, then PC1\n\t\t// 先应用 PC2，再应用 PC1\n\t\tstep1Result, step1Success = dmp.PatchApply(pc2Patches, base)\n\t\tmerged, step2Success = dmp.PatchApply(pc1Patches, step1Result)\n\t}\n\n\t// Check if patch application succeeded\n\t// 检查补丁应用是否成功\n\tfor _, s := range step1Success {\n\t\tif !s {\n\t\t\treturn MergeResult{\n\t\t\t\tHasConflict:  true,\n\t\t\t\tConflictInfo: \"patch apply failed in step 1\",\n\t\t\t}, nil\n\t\t}\n\t}\n\tfor _, s := range step2Success {\n\t\tif !s {\n\t\t\treturn MergeResult{\n\t\t\t\tHasConflict:  true,\n\t\t\t\tConflictInfo: \"patch apply failed in step 2\",\n\t\t\t}, nil\n\t\t}\n\t}\n\n\treturn MergeResult{\n\t\tContent:     merged,\n\t\tHasConflict: false,\n\t}, nil\n}\n\n// MergeTextsIgnoreConflictIgnoreDelete merges text, ignores conflicts and deletions, retains all text of PC1 and PC2 based on base\n// MergeTextsIgnoreConflictIgnoreDelete 合并文本，忽略冲突和删除, 保留PC1 PC2基于base的全部文本\n// PC1 is clientContent, PC2 is serverContent\n// PC1 为 clientContent, PC2 为 serverContent\nfunc MergeTextsIgnoreConflictIgnoreDelete(base, pc1, pc2 string, pc1First bool) (merged string, err error) {\n\tdmp := diffmatchpatch.New()\n\n\t// Calculate diff of PC1 relative to base and filter delete operations\n\t// 计算 PC1 相对于 base 的 diff,并过滤删除操作\n\tpc1Diffs := dmp.DiffMain(base, pc1, false)\n\tpc1DiffsNoDelete := make([]diffmatchpatch.Diff, 0)\n\tfor _, diff := range pc1Diffs {\n\t\tif diff.Type != diffmatchpatch.DiffDelete {\n\t\t\tpc1DiffsNoDelete = append(pc1DiffsNoDelete, diff)\n\t\t}\n\t}\n\tpc1Patches := dmp.PatchMake(base, pc1DiffsNoDelete)\n\n\t// Calculate diff of PC2 relative to base and filter delete operations\n\t// 计算 PC2 相对于 base 的 diff,并过滤删除操作\n\tpc2Diffs := dmp.DiffMain(base, pc2, false)\n\tpc2DiffsNoDelete := make([]diffmatchpatch.Diff, 0)\n\tfor _, diff := range pc2Diffs {\n\t\tif diff.Type != diffmatchpatch.DiffDelete {\n\t\t\tpc2DiffsNoDelete = append(pc2DiffsNoDelete, diff)\n\t\t}\n\t}\n\tpc2Patches := dmp.PatchMake(base, pc2DiffsNoDelete)\n\n\t// 根据 pc1First 参数决定应用顺序\n\tvar step1Result string\n\tvar step1Success []bool\n\tvar step2Success []bool\n\n\tif pc1First {\n\t\t// Apply PC1 first, then PC2\n\t\t// 先应用 PC1,再应用 PC2\n\t\tstep1Result, step1Success = dmp.PatchApply(pc1Patches, base)\n\t\tmerged, step2Success = dmp.PatchApply(pc2Patches, step1Result)\n\t} else {\n\t\t// Apply PC2 first, then PC1\n\t\t// 先应用 PC2,再应用 PC1\n\t\tstep1Result, step1Success = dmp.PatchApply(pc2Patches, base)\n\t\tmerged, step2Success = dmp.PatchApply(pc1Patches, step1Result)\n\t}\n\n\t// Check if all patches were successfully applied\n\t// 检查是否所有补丁都成功应用\n\tfor _, s := range step1Success {\n\t\tif !s {\n\t\t\treturn merged, errors.New(\"failed to apply patches from first step\")\n\t\t}\n\t}\n\tfor _, s := range step2Success {\n\t\tif !s {\n\t\t\treturn merged, errors.New(\"failed to apply patches from second step\")\n\t\t}\n\t}\n\n\treturn merged, nil\n}\n\n// hasConflict detects merge conflicts\n// hasConflict 检测合并冲突\n// Line-based conflict detection strategy, more consistent with text editing semantics\n// 采用基于行的冲突检测策略，更符合文本编辑的语义\n//\n// Conflict scenarios:\n// 1. Both modified the same line (modify-modify conflict)\n// 2. One deleted a line, the other modified the same line (delete-modify conflict)\n// 3. Both added different content starting from an empty file (empty file conflict)\n// 4. Both appended different content at the end of the same line (append conflict)\n//\n// 冲突情况：\n// 1. 两方都修改了同一行（修改-修改冲突）\n// 2. 一方删除某行，另一方修改同一行（删除-修改冲突）\n// 3. 两方从空文件开始各自添加不同内容（空文件冲突）\n// 4. 两方在同一行末尾追加不同内容（追加冲突）\n//\n// Non-conflict scenarios:\n// - Both add new lines at the end of the file (can merge)\n// - Both delete the same line (consistent results)\n// - Both modification results are the same (consistent results)\n//\n// 非冲突情况：\n// - 两方在文件末尾各自添加新行（可以合并）\n// - 两方删除相同行（结果一致）\n// - 两方修改结果相同（结果一致）\nfunc hasConflict(pc1Diffs, pc2Diffs []diffmatchpatch.Diff) bool {\n\t// Extract line-level changes\n\t// 提取行级别的变更\n\tpc1Changes := extractLineChangesFromDiffs(pc1Diffs)\n\tpc2Changes := extractLineChangesFromDiffs(pc2Diffs)\n\n\t// Special case: Both just add new lines at the end, not a conflict\n\t// 特殊情况：两方都只是在末尾添加新行，不是冲突\n\tpc1OnlyAppend := isOnlyAppendAtEnd(pc1Changes)\n\tpc2OnlyAppend := isOnlyAppendAtEnd(pc2Changes)\n\tif pc1OnlyAppend && pc2OnlyAppend {\n\t\treturn false\n\t}\n\n\t// Check if there are line-level conflicts\n\t// 检查是否有行级别的冲突\n\tfor lineNum, change1 := range pc1Changes {\n\t\tif change2, exists := pc2Changes[lineNum]; exists {\n\t\t\t// Both side operated on the same line\n\t\t\t// 两方都操作了同一行\n\n\t\t\t// If both are inserting new lines at the end (starting with newline), not a conflict\n\t\t\t// 如果两方都是在末尾添加新行（以换行符开头），不是冲突\n\t\t\tif change1.changeType == lineInserted && change2.changeType == lineInserted &&\n\t\t\t\tchange1.isAtEnd && change2.isAtEnd &&\n\t\t\t\tlen(change1.newContent) > 0 && change1.newContent[0] == '\\n' &&\n\t\t\t\tlen(change2.newContent) > 0 && change2.newContent[0] == '\\n' {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// If both are deleting the same line, not a conflict\n\t\t\t// 如果两方都是删除同一行，不是冲突\n\t\t\tif change1.changeType == lineDeleted && change2.changeType == lineDeleted {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// If modification/insertion result is the same, not a conflict\n\t\t\t// 如果两方修改/插入结果相同，不是冲突\n\t\t\tif change1.newContent == change2.newContent {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Other scenarios are conflicts\n\t\t\t// 其他情况都是冲突\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// lineChangeType line change type // 行变更类型\ntype lineChangeType int\n\nconst (\n\tlineModified lineChangeType = iota // Line modified // 行被修改\n\tlineDeleted                        // Line deleted // 行被删除\n\tlineInserted                       // New line inserted // 新插入的行\n)\n\n// lineChange represents the change to a line // lineChange 表示对某一行的变更\ntype lineChange struct {\n\tchangeType lineChangeType\n\tnewContent string // Content after modification (if modified or inserted) // 修改后的内容（如果是修改或插入）\n\tisAtEnd    bool   // Whether it is an operation at the end of the file // 是否是在文件末尾的操作\n}\n\n// extractLineChangesFromDiffs extracts line-level changes from diff\n// extractLineChangesFromDiffs 从 diff 中提取行级别的变更\nfunc extractLineChangesFromDiffs(diffs []diffmatchpatch.Diff) map[int]lineChange {\n\tchanges := make(map[int]lineChange)\n\tlineNum := 0\n\ttotalLines := 0\n\n\t// First calculate total lines of original text\n\t// 首先计算原文总行数\n\tfor _, d := range diffs {\n\t\tif d.Type == diffmatchpatch.DiffEqual || d.Type == diffmatchpatch.DiffDelete {\n\t\t\tfor _, ch := range d.Text {\n\t\t\t\tif ch == '\\n' {\n\t\t\t\t\ttotalLines++\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Traverse diff, record changes for each line\n\t// 遍历 diff，记录每行的变更\n\tfor i, d := range diffs {\n\t\tswitch d.Type {\n\t\tcase diffmatchpatch.DiffEqual:\n\t\t\tfor _, ch := range d.Text {\n\t\t\t\tif ch == '\\n' {\n\t\t\t\t\tlineNum++\n\t\t\t\t}\n\t\t\t}\n\t\tcase diffmatchpatch.DiffDelete:\n\t\t\tstartLine := lineNum\n\t\t\tdeletedText := d.Text\n\n\t\t\t// 检查是否紧跟插入（表示修改）\n\t\t\tisModify := i+1 < len(diffs) && diffs[i+1].Type == diffmatchpatch.DiffInsert\n\t\t\tvar newContent string\n\t\t\tif isModify {\n\t\t\t\tnewContent = diffs[i+1].Text\n\t\t\t}\n\n\t\t\t// Record every line affected by delete/modify\n\t\t\t// 记录删除/修改影响的每一行\n\t\t\tcurrentLine := startLine\n\t\t\tfor _, ch := range deletedText {\n\t\t\t\tif isModify {\n\t\t\t\t\tchanges[currentLine] = lineChange{\n\t\t\t\t\t\tchangeType: lineModified,\n\t\t\t\t\t\tnewContent: newContent,\n\t\t\t\t\t\tisAtEnd:    currentLine >= totalLines-1,\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tchanges[currentLine] = lineChange{\n\t\t\t\t\t\tchangeType: lineDeleted,\n\t\t\t\t\t\tisAtEnd:    currentLine >= totalLines-1,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif ch == '\\n' {\n\t\t\t\t\tcurrentLine++\n\t\t\t\t\tlineNum++\n\t\t\t\t}\n\t\t\t}\n\t\t\t// If deleted text doesn't end with a newline, also record current line\n\t\t\t// 如果删除的文本不以换行结尾，也要记录当前行\n\t\t\tif len(deletedText) > 0 && deletedText[len(deletedText)-1] != '\\n' {\n\t\t\t\tif isModify {\n\t\t\t\t\tchanges[currentLine] = lineChange{\n\t\t\t\t\t\tchangeType: lineModified,\n\t\t\t\t\t\tnewContent: newContent,\n\t\t\t\t\t\tisAtEnd:    currentLine >= totalLines-1,\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tchanges[currentLine] = lineChange{\n\t\t\t\t\t\tchangeType: lineDeleted,\n\t\t\t\t\t\tisAtEnd:    currentLine >= totalLines-1,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\tcase diffmatchpatch.DiffInsert:\n\t\t\t// Pure insertion (no preceding delete)\n\t\t\t// 纯插入（前面不是删除）\n\t\t\tif i == 0 || diffs[i-1].Type != diffmatchpatch.DiffDelete {\n\t\t\t\t// Insert at current line position\n\t\t\t\t// 在当前行位置插入\n\t\t\t\tchanges[lineNum] = lineChange{\n\t\t\t\t\tchangeType: lineInserted,\n\t\t\t\t\tnewContent: d.Text,\n\t\t\t\t\tisAtEnd:    lineNum >= totalLines,\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn changes\n}\n\n// isOnlyAppendAtEnd checks if changes are only adding new lines at the end of file\n// Note: Appending content at the end of an existing line is not considered \"adding new lines at the end\"\n// isOnlyAppendAtEnd 检查变更是否只是在文件末尾添加新行\n// 注意：在现有行末尾追加内容不算\"末尾添加新行\"\nfunc isOnlyAppendAtEnd(changes map[int]lineChange) bool {\n\tif len(changes) == 0 {\n\t\treturn false\n\t}\n\tfor _, change := range changes {\n\t\t// Must be insertion type\n\t\t// 必须是插入类型\n\t\tif change.changeType != lineInserted {\n\t\t\treturn false\n\t\t}\n\t\t// Must be at the end\n\t\t// 必须是在末尾\n\t\tif !change.isAtEnd {\n\t\t\treturn false\n\t\t}\n\t\t// Inserted content must start with a newline (representing adding a new line)\n\t\t// If it doesn't start with a newline, it means appending content at the end of an existing line\n\t\t// 插入的内容必须以换行符开头（表示添加新行）\n\t\t// 如果不以换行符开头，说明是在现有行末尾追加内容\n\t\tif len(change.newContent) == 0 || change.newContent[0] != '\\n' {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// extractInsertInfos extracts position and content of pure insertion operations from diff list\n// Pure insertion refers to insertion without preceding delete operation (not part of replacement)\n// Note: This function is retained for other purposes but no longer used for conflict detection\n// extractInsertInfos 从 diff 列表中提取纯插入操作的位置和内容\n// 纯插入是指前面没有删除操作的插入（不是替换的一部分）\n// 注意：此函数保留用于其他用途，但不再用于冲突检测\nfunc extractInsertInfos(diffs []diffmatchpatch.Diff) []insertInfo {\n\tvar infos []insertInfo\n\tpos := 0\n\n\tfor i, d := range diffs {\n\t\tswitch d.Type {\n\t\tcase diffmatchpatch.DiffEqual:\n\t\t\tpos += len(d.Text)\n\t\tcase diffmatchpatch.DiffDelete:\n\t\t\tpos += len(d.Text)\n\t\tcase diffmatchpatch.DiffInsert:\n\t\t\t// Only when previous is not delete, it is a pure insertion\n\t\t\t// 只有前一个不是删除时，才是纯插入\n\t\t\tif i == 0 || diffs[i-1].Type != diffmatchpatch.DiffDelete {\n\t\t\t\tinfos = append(infos, insertInfo{\n\t\t\t\t\tPosition: pos,\n\t\t\t\t\tContent:  d.Text,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\treturn infos\n}\n\n// extractDeleteRanges extracts position range of delete operations from diff list\n// Note: This function is preserved for other purposes\n// extractDeleteRanges 从 diff 列表中提取删除操作的位置范围\n// 注意：此函数保留用于其他用途\nfunc extractDeleteRanges(diffs []diffmatchpatch.Diff) []textRange {\n\tvar ranges []textRange\n\tpos := 0\n\n\tfor _, d := range diffs {\n\t\tswitch d.Type {\n\t\tcase diffmatchpatch.DiffEqual:\n\t\t\tpos += len(d.Text)\n\t\tcase diffmatchpatch.DiffDelete:\n\t\t\tranges = append(ranges, textRange{\n\t\t\t\tStart: pos,\n\t\t\t\tEnd:   pos + len(d.Text),\n\t\t\t})\n\t\t\tpos += len(d.Text)\n\t\tcase diffmatchpatch.DiffInsert:\n\t\t\t// Insert operation does not change position in original text\n\t\t\t// 插入操作不改变在原文中的位置\n\t\t}\n\t}\n\n\treturn ranges\n}\n\n// extractModifyRanges extracts position range of modification operations from diff list\n// Modification is defined as: delete immediately followed by insertion\n// Note: This function is preserved for other purposes\n// extractModifyRanges 从 diff 列表中提取修改操作的位置范围\n// 修改被定义为：删除后紧跟插入\n// 注意：此函数保留用于其他用途\nfunc extractModifyRanges(diffs []diffmatchpatch.Diff) []textRange {\n\tvar ranges []textRange\n\tpos := 0\n\n\tfor i, d := range diffs {\n\t\tswitch d.Type {\n\t\tcase diffmatchpatch.DiffEqual:\n\t\t\tpos += len(d.Text)\n\t\tcase diffmatchpatch.DiffDelete:\n\t\t\t// Check if followed by insertion (represents modification)\n\t\t\t// 检查是否紧跟插入（表示修改）\n\t\t\tif i+1 < len(diffs) && diffs[i+1].Type == diffmatchpatch.DiffInsert {\n\t\t\t\tranges = append(ranges, textRange{\n\t\t\t\t\tStart: pos,\n\t\t\t\t\tEnd:   pos + len(d.Text),\n\t\t\t\t})\n\t\t\t}\n\t\t\tpos += len(d.Text)\n\t\tcase diffmatchpatch.DiffInsert:\n\t\t\t// 插入操作不改变在原文中的位置\n\t\t}\n\t}\n\n\treturn ranges\n}\n\n// rangesOverlap checks if two ranges overlap\n// rangesOverlap 检查两个范围是否重叠\nfunc rangesOverlap(r1, r2 textRange) bool {\n\treturn r1.Start < r2.End && r2.Start < r1.End\n}\n"
  },
  {
    "path": "pkg/diff/diff_test.go",
    "content": "package diff\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/leanovate/gopter\"\n\t\"github.com/leanovate/gopter/gen\"\n\t\"github.com/leanovate/gopter/prop\"\n\t\"github.com/sergi/go-diff/diffmatchpatch\"\n)\n\n// 验证删除操作在合并中被保留\nfunc TestProperty4_DeleteOperationPreservation(t *testing.T) {\n\tparameters := gopter.DefaultTestParameters()\n\tparameters.MinSuccessfulTests = 100\n\n\tproperties := gopter.NewProperties(parameters)\n\n\t// 场景1: 单方删除，另一方无修改（使用唯一标记避免误判）\n\tproperties.Property(\"single side delete is preserved\", prop.ForAll(\n\t\tfunc(id int) bool {\n\t\t\t// 使用唯一标记确保不会有歧义\n\t\t\ttoDelete := fmt.Sprintf(\"__DELETE_ME_%d__\", id)\n\t\t\tbase := \"prefix\" + toDelete + \"suffix\"\n\t\t\tpc1 := \"prefixsuffix\" // PC1 删除了 toDelete\n\t\t\tpc2 := base           // PC2 无修改\n\n\t\t\tresult, err := MergeTexts(base, pc1, pc2, true)\n\t\t\tif err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// 如果没有冲突，删除应该被保留\n\t\t\tif !result.HasConflict {\n\t\t\t\treturn !strings.Contains(result.Content, toDelete)\n\t\t\t}\n\t\t\treturn true // 有冲突也是可接受的\n\t\t},\n\t\tgen.IntRange(1, 1000),\n\t))\n\n\t// 场景2: 双方删除相同内容\n\tproperties.Property(\"both sides delete same content\", prop.ForAll(\n\t\tfunc(id int) bool {\n\t\t\ttoDelete := fmt.Sprintf(\"__DELETE_ME_%d__\", id)\n\t\t\tbase := \"prefix\" + toDelete + \"suffix\"\n\t\t\tpc1 := \"prefixsuffix\" // PC1 删除\n\t\t\tpc2 := \"prefixsuffix\" // PC2 也删除\n\n\t\t\tresult, err := MergeTexts(base, pc1, pc2, true)\n\t\t\tif err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// 双方都删除，结果应该不包含被删除内容\n\t\t\tif !result.HasConflict {\n\t\t\t\treturn !strings.Contains(result.Content, toDelete)\n\t\t\t}\n\t\t\treturn true\n\t\t},\n\t\tgen.IntRange(1, 1000),\n\t))\n\n\tproperties.TestingRun(t)\n}\n\n// 验证删除-修改冲突被正确检测（基于行级别）\nfunc TestProperty5_DeleteModifyConflictDetection(t *testing.T) {\n\tparameters := gopter.DefaultTestParameters()\n\tparameters.MinSuccessfulTests = 100\n\n\tproperties := gopter.NewProperties(parameters)\n\n\t// 场景: 一方删除整行，另一方修改同一行\n\tproperties.Property(\"delete-modify conflict is detected at line level\", prop.ForAll(\n\t\tfunc(id int) bool {\n\t\t\t// 使用多行文本，确保行级别的删除-修改冲突\n\t\t\tlineToModify := fmt.Sprintf(\"Line_%d_content\", id)\n\t\t\tbase := \"Line1\\n\" + lineToModify + \"\\nLine3\"\n\t\t\tpc1 := \"Line1\\nLine3\"                                     // PC1 删除中间行\n\t\t\tpc2 := \"Line1\\n\" + lineToModify + \"_modified\" + \"\\nLine3\" // PC2 修改中间行\n\n\t\t\tresult, err := MergeTexts(base, pc1, pc2, true)\n\t\t\tif err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// 删除-修改冲突应该被检测到\n\t\t\treturn result.HasConflict\n\t\t},\n\t\tgen.IntRange(1, 1000),\n\t))\n\n\tproperties.TestingRun(t)\n}\n\n// 单元测试: 基本合并场景\nfunc TestMergeTexts_BasicScenarios(t *testing.T) {\n\ttests := []struct {\n\t\tname            string\n\t\tbase            string\n\t\tpc1             string\n\t\tpc2             string\n\t\tpc1First        bool\n\t\twantConflict    bool\n\t\twantContains    string\n\t\twantNotContains string\n\t}{\n\t\t{\n\t\t\tname:         \"no changes\",\n\t\t\tbase:         \"Hello World\",\n\t\t\tpc1:          \"Hello World\",\n\t\t\tpc2:          \"Hello World\",\n\t\t\tpc1First:     true,\n\t\t\twantConflict: false,\n\t\t\twantContains: \"Hello World\",\n\t\t},\n\t\t{\n\t\t\tname:         \"pc1 only change\",\n\t\t\tbase:         \"Hello\",\n\t\t\tpc1:          \"Hello World\",\n\t\t\tpc2:          \"Hello\",\n\t\t\tpc1First:     true,\n\t\t\twantConflict: false,\n\t\t\twantContains: \"World\",\n\t\t},\n\t\t{\n\t\t\tname:         \"pc2 only change\",\n\t\t\tbase:         \"Hello\",\n\t\t\tpc1:          \"Hello\",\n\t\t\tpc2:          \"Hello Kiro\",\n\t\t\tpc1First:     true,\n\t\t\twantConflict: false,\n\t\t\twantContains: \"Kiro\",\n\t\t},\n\t\t{\n\t\t\tname:         \"both add different content\",\n\t\t\tbase:         \"Hello\",\n\t\t\tpc1:          \"Hello World\",\n\t\t\tpc2:          \"Hello Kiro\",\n\t\t\tpc1First:     true,\n\t\t\twantConflict: true, // 单行文件，两端在末尾追加不同内容，视为冲突\n\t\t},\n\t\t{\n\t\t\tname:            \"pc1 delete paragraph\",\n\t\t\tbase:            \"Line1\\nLine2\\nLine3\",\n\t\t\tpc1:             \"Line1\\nLine3\",\n\t\t\tpc2:             \"Line1\\nLine2\\nLine3\",\n\t\t\tpc1First:        true,\n\t\t\twantConflict:    false,\n\t\t\twantNotContains: \"Line2\",\n\t\t},\n\t\t{\n\t\t\tname:         \"delete-modify conflict\",\n\t\t\tbase:         \"Line1\\nLine2\\nLine3\",\n\t\t\tpc1:          \"Line1\\nLine3\",                 // 删除 Line2\n\t\t\tpc2:          \"Line1\\nLine2 Modified\\nLine3\", // 修改 Line2\n\t\t\tpc1First:     true,\n\t\t\twantConflict: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tresult, err := MergeTexts(tt.base, tt.pc1, tt.pc2, tt.pc1First)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"MergeTexts() error = %v\", err)\n\t\t\t}\n\n\t\t\tif result.HasConflict != tt.wantConflict {\n\t\t\t\tt.Errorf(\"HasConflict = %v, want %v (info: %s)\", result.HasConflict, tt.wantConflict, result.ConflictInfo)\n\t\t\t}\n\n\t\t\tif !result.HasConflict {\n\t\t\t\tif tt.wantContains != \"\" && !strings.Contains(result.Content, tt.wantContains) {\n\t\t\t\t\tt.Errorf(\"Content should contain %q, got %q\", tt.wantContains, result.Content)\n\t\t\t\t}\n\t\t\t\tif tt.wantNotContains != \"\" && strings.Contains(result.Content, tt.wantNotContains) {\n\t\t\t\t\tt.Errorf(\"Content should not contain %q, got %q\", tt.wantNotContains, result.Content)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\n// 测试 extractDeleteRanges\nfunc TestExtractDeleteRanges(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tbase     string\n\t\tmodified string\n\t\twantLen  int\n\t}{\n\t\t{\n\t\t\tname:     \"no delete\",\n\t\t\tbase:     \"Hello\",\n\t\t\tmodified: \"Hello World\",\n\t\t\twantLen:  0,\n\t\t},\n\t\t{\n\t\t\tname:     \"single delete\",\n\t\t\tbase:     \"Hello World\",\n\t\t\tmodified: \"Hello\",\n\t\t\twantLen:  1,\n\t\t},\n\t\t{\n\t\t\tname:     \"multiple deletes\",\n\t\t\tbase:     \"A B C D\",\n\t\t\tmodified: \"A D\",\n\t\t\twantLen:  1, // \"B C \" 是连续删除\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tdmp := newDMP()\n\t\t\tdiffs := dmp.DiffMain(tt.base, tt.modified, false)\n\t\t\tranges := extractDeleteRanges(diffs)\n\n\t\t\tif len(ranges) != tt.wantLen {\n\t\t\t\tt.Errorf(\"extractDeleteRanges() got %d ranges, want %d\", len(ranges), tt.wantLen)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// 测试 rangesOverlap\nfunc TestRangesOverlap(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tr1   textRange\n\t\tr2   textRange\n\t\twant bool\n\t}{\n\t\t{\n\t\t\tname: \"no overlap - r1 before r2\",\n\t\t\tr1:   textRange{0, 5},\n\t\t\tr2:   textRange{10, 15},\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\tname: \"no overlap - r2 before r1\",\n\t\t\tr1:   textRange{10, 15},\n\t\t\tr2:   textRange{0, 5},\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\tname: \"overlap - partial\",\n\t\t\tr1:   textRange{0, 10},\n\t\t\tr2:   textRange{5, 15},\n\t\t\twant: true,\n\t\t},\n\t\t{\n\t\t\tname: \"overlap - r1 contains r2\",\n\t\t\tr1:   textRange{0, 20},\n\t\t\tr2:   textRange{5, 15},\n\t\t\twant: true,\n\t\t},\n\t\t{\n\t\t\tname: \"adjacent - no overlap\",\n\t\t\tr1:   textRange{0, 5},\n\t\t\tr2:   textRange{5, 10},\n\t\t\twant: false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := rangesOverlap(tt.r1, tt.r2)\n\t\t\tif got != tt.want {\n\t\t\t\tt.Errorf(\"rangesOverlap(%v, %v) = %v, want %v\", tt.r1, tt.r2, got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// 辅助函数\nfunc newDMP() *diffmatchpatch.DiffMatchPatch {\n\treturn diffmatchpatch.New()\n}\n"
  },
  {
    "path": "pkg/diff/merge_scenarios_test.go",
    "content": "package diff\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\n// 多设备同步真实场景测试\n// 模拟 Obsidian 笔记在多设备间同步时可能遇到的各种情况\n\n// TestScenario_DailyNoteEditing 日记编辑场景\n// 用户在手机和电脑上同时编辑同一篇日记\nfunc TestScenario_DailyNoteEditing(t *testing.T) {\n\ttests := []struct {\n\t\tname         string\n\t\tbase         string\n\t\tphone        string // 手机端修改\n\t\tdesktop      string // 电脑端修改\n\t\twantConflict bool\n\t\tdescription  string\n\t}{\n\t\t{\n\t\t\tname: \"不同段落编辑-无冲突\",\n\t\t\tbase: `# 2024-01-10 日记\n\n## 早晨\n今天早起跑步了\n\n## 下午\n下午开会讨论项目\n\n## 晚上\n晚上看了一部电影`,\n\t\t\tphone: `# 2024-01-10 日记\n\n## 早晨\n今天早起跑步了，跑了5公里\n\n## 下午\n下午开会讨论项目\n\n## 晚上\n晚上看了一部电影`,\n\t\t\tdesktop: `# 2024-01-10 日记\n\n## 早晨\n今天早起跑步了\n\n## 下午\n下午开会讨论项目，决定下周上线\n\n## 晚上\n晚上看了一部电影`,\n\t\t\twantConflict: false,\n\t\t\tdescription:  \"手机编辑早晨部分，电脑编辑下午部分，应该能自动合并\",\n\t\t},\n\t\t{\n\t\t\tname: \"同一段落编辑-冲突\",\n\t\t\tbase: `# 会议记录\n\n参会人员：张三、李四\n会议内容：讨论Q1计划`,\n\t\t\tphone: `# 会议记录\n\n参会人员：张三、李四、王五\n会议内容：讨论Q1计划`,\n\t\t\tdesktop: `# 会议记录\n\n参会人员：张三、李四、赵六\n会议内容：讨论Q1计划`,\n\t\t\twantConflict: true,\n\t\t\tdescription:  \"两端都修改了参会人员列表，应该检测到冲突\",\n\t\t},\n\t\t{\n\t\t\tname: \"一端删除段落-另一端修改同段落-冲突\",\n\t\t\tbase: `# 待办事项\n\n- [ ] 买菜\n- [ ] 取快递\n- [ ] 交水电费`,\n\t\t\tphone: `# 待办事项\n\n- [ ] 买菜\n- [ ] 交水电费`,\n\t\t\tdesktop: `# 待办事项\n\n- [ ] 买菜\n- [x] 取快递\n- [ ] 交水电费`,\n\t\t\twantConflict: true,\n\t\t\tdescription:  \"手机删除了取快递，电脑标记取快递完成，应该冲突\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tresult, err := MergeTexts(tt.base, tt.phone, tt.desktop, true)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"MergeTexts() error = %v\", err)\n\t\t\t}\n\n\t\t\tif result.HasConflict != tt.wantConflict {\n\t\t\t\tt.Errorf(\"%s\\nHasConflict = %v, want %v\\nConflictInfo: %s\",\n\t\t\t\t\ttt.description, result.HasConflict, tt.wantConflict, result.ConflictInfo)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TestScenario_CodeSnippetEditing 代码片段编辑场景\nfunc TestScenario_CodeSnippetEditing(t *testing.T) {\n\ttests := []struct {\n\t\tname         string\n\t\tbase         string\n\t\tdevice1      string\n\t\tdevice2      string\n\t\twantConflict bool\n\t\tdescription  string\n\t}{\n\t\t{\n\t\t\tname:         \"添加不同函数-无冲突\",\n\t\t\tbase:         \"```go\\npackage main\\n\\nfunc main() {\\n}\\n```\",\n\t\t\tdevice1:      \"```go\\npackage main\\n\\nfunc hello() {\\n\\tprintln(\\\"hello\\\")\\n}\\n\\nfunc main() {\\n}\\n```\",\n\t\t\tdevice2:      \"```go\\npackage main\\n\\nfunc main() {\\n\\thello()\\n}\\n```\",\n\t\t\twantConflict: false,\n\t\t\tdescription:  \"一端添加函数定义，另一端修改main函数，应该能合并\",\n\t\t},\n\t\t{\n\t\t\tname:         \"修改同一行代码-冲突\",\n\t\t\tbase:         \"```python\\ndef calculate(x):\\n    return x * 2\\n```\",\n\t\t\tdevice1:      \"```python\\ndef calculate(x):\\n    return x * 3\\n```\",\n\t\t\tdevice2:      \"```python\\ndef calculate(x):\\n    return x + 2\\n```\",\n\t\t\twantConflict: true,\n\t\t\tdescription:  \"两端都修改了计算逻辑，应该冲突\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tresult, err := MergeTexts(tt.base, tt.device1, tt.device2, true)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"MergeTexts() error = %v\", err)\n\t\t\t}\n\n\t\t\tif result.HasConflict != tt.wantConflict {\n\t\t\t\tt.Errorf(\"%s\\nHasConflict = %v, want %v\", tt.description, result.HasConflict, tt.wantConflict)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TestScenario_ListEditing 列表编辑场景\nfunc TestScenario_ListEditing(t *testing.T) {\n\ttests := []struct {\n\t\tname         string\n\t\tbase         string\n\t\tdevice1      string\n\t\tdevice2      string\n\t\twantConflict bool\n\t\twantContains []string // 合并后应该包含的内容\n\t\tdescription  string\n\t}{\n\t\t{\n\t\t\tname:         \"列表末尾添加不同项-无冲突\",\n\t\t\tbase:         \"- 苹果\\n- 香蕉\",\n\t\t\tdevice1:      \"- 苹果\\n- 香蕉\\n- 橙子\",\n\t\t\tdevice2:      \"- 苹果\\n- 香蕉\\n- 葡萄\",\n\t\t\twantConflict: false,\n\t\t\twantContains: []string{\"苹果\", \"香蕉\", \"橙子\", \"葡萄\"},\n\t\t\tdescription:  \"两端都在列表末尾添加项目，应该都保留\",\n\t\t},\n\t\t{\n\t\t\tname:         \"列表开头添加不同项-无冲突\",\n\t\t\tbase:         \"- 项目B\\n- 项目C\",\n\t\t\tdevice1:      \"- 项目A\\n- 项目B\\n- 项目C\",\n\t\t\tdevice2:      \"- 项目B\\n- 项目C\\n- 项目D\",\n\t\t\twantConflict: false,\n\t\t\twantContains: []string{\"项目A\", \"项目B\", \"项目C\", \"项目D\"},\n\t\t\tdescription:  \"一端在开头添加，一端在末尾添加，应该都保留\",\n\t\t},\n\t\t{\n\t\t\tname:         \"删除同一项-无冲突\",\n\t\t\tbase:         \"- 保留1\\n- 删除我\\n- 保留2\",\n\t\t\tdevice1:      \"- 保留1\\n- 保留2\",\n\t\t\tdevice2:      \"- 保留1\\n- 保留2\",\n\t\t\twantConflict: false,\n\t\t\twantContains: []string{\"保留1\", \"保留2\"},\n\t\t\tdescription:  \"两端都删除同一项，应该成功合并\",\n\t\t},\n\t\t{\n\t\t\tname:         \"修改同一列表项-冲突\",\n\t\t\tbase:         \"- 任务：写文档\",\n\t\t\tdevice1:      \"- 任务：写文档（已完成）\",\n\t\t\tdevice2:      \"- 任务：写文档（进行中）\",\n\t\t\twantConflict: true,\n\t\t\tdescription:  \"两端对同一任务状态做了不同修改\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tresult, err := MergeTexts(tt.base, tt.device1, tt.device2, true)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"MergeTexts() error = %v\", err)\n\t\t\t}\n\n\t\t\tif result.HasConflict != tt.wantConflict {\n\t\t\t\tt.Errorf(\"%s\\nHasConflict = %v, want %v\", tt.description, result.HasConflict, tt.wantConflict)\n\t\t\t}\n\n\t\t\tif !result.HasConflict && len(tt.wantContains) > 0 {\n\t\t\t\tfor _, want := range tt.wantContains {\n\t\t\t\t\tif !strings.Contains(result.Content, want) {\n\t\t\t\t\t\tt.Errorf(\"合并结果应该包含 %q，但没有找到\\n结果: %s\", want, result.Content)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TestScenario_OfflineEditing 离线编辑场景\n// 模拟设备离线后重新上线同步的情况\nfunc TestScenario_OfflineEditing(t *testing.T) {\n\ttests := []struct {\n\t\tname         string\n\t\tbase         string // 离线前的共同版本\n\t\toffline      string // 离线设备的修改\n\t\tonline       string // 在线设备的修改\n\t\twantConflict bool\n\t\tdescription  string\n\t}{\n\t\t{\n\t\t\tname: \"离线添加内容-在线也添加内容-不同位置\",\n\t\t\tbase: `# 读书笔记\n\n第一章要点：\n- 要点1`,\n\t\t\toffline: `# 读书笔记\n\n第一章要点：\n- 要点1\n- 要点2（离线添加）`,\n\t\t\tonline: `# 读书笔记\n\n作者：某某某\n\n第一章要点：\n- 要点1`,\n\t\t\twantConflict: false,\n\t\t\tdescription:  \"离线设备添加要点，在线设备添加作者信息，应该能合并\",\n\t\t},\n\t\t{\n\t\t\tname: \"离线删除段落-在线修改同段落-冲突\",\n\t\t\tbase: `# 项目计划\n\n## 第一阶段\n需求分析\n\n## 第二阶段\n开发实现`,\n\t\t\toffline: `# 项目计划\n\n## 第二阶段\n开发实现`,\n\t\t\tonline: `# 项目计划\n\n## 第一阶段\n需求分析和设计\n\n## 第二阶段\n开发实现`,\n\t\t\twantConflict: true,\n\t\t\tdescription:  \"离线设备删除了第一阶段，在线设备修改了第一阶段内容\",\n\t\t},\n\t\t{\n\t\t\tname: \"长时间离线-大量修改-不同区域\",\n\t\t\tbase: `# 工作周报\n\n## 本周完成\n- 任务A\n\n## 下周计划\n- 任务B\n\n## 问题与风险\n无`,\n\t\t\toffline: `# 工作周报\n\n## 本周完成\n- 任务A\n- 任务C（离线添加）\n- 任务D（离线添加）\n\n## 下周计划\n- 任务B\n\n## 问题与风险\n无`,\n\t\t\tonline: `# 工作周报\n\n## 本周完成\n- 任务A\n\n## 下周计划\n- 任务B\n- 任务E（在线添加）\n\n## 问题与风险\n发现一个技术难点`,\n\t\t\twantConflict: false,\n\t\t\tdescription:  \"离线和在线分别在不同区域做了大量修改，应该能合并\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tresult, err := MergeTexts(tt.base, tt.offline, tt.online, true)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"MergeTexts() error = %v\", err)\n\t\t\t}\n\n\t\t\tif result.HasConflict != tt.wantConflict {\n\t\t\t\tt.Errorf(\"%s\\nHasConflict = %v, want %v\\nConflictInfo: %s\",\n\t\t\t\t\ttt.description, result.HasConflict, tt.wantConflict, result.ConflictInfo)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TestScenario_FrontmatterEditing YAML Frontmatter 编辑场景\nfunc TestScenario_FrontmatterEditing(t *testing.T) {\n\ttests := []struct {\n\t\tname         string\n\t\tbase         string\n\t\tdevice1      string\n\t\tdevice2      string\n\t\twantConflict bool\n\t\tdescription  string\n\t}{\n\t\t{\n\t\t\tname: \"修改不同属性-无冲突\",\n\t\t\tbase: `---\ntitle: 我的笔记\ntags: [日记]\ndate: 2024-01-10\n---\n\n正文内容`,\n\t\t\tdevice1: `---\ntitle: 我的笔记（已更新）\ntags: [日记]\ndate: 2024-01-10\n---\n\n正文内容`,\n\t\t\tdevice2: `---\ntitle: 我的笔记\ntags: [日记, 重要]\ndate: 2024-01-10\n---\n\n正文内容`,\n\t\t\twantConflict: false,\n\t\t\tdescription:  \"一端修改标题，一端修改标签，应该能合并\",\n\t\t},\n\t\t{\n\t\t\tname: \"修改同一属性-冲突\",\n\t\t\tbase: `---\nstatus: draft\n---\n\n内容`,\n\t\t\tdevice1: `---\nstatus: published\n---\n\n内容`,\n\t\t\tdevice2: `---\nstatus: archived\n---\n\n内容`,\n\t\t\twantConflict: true,\n\t\t\tdescription:  \"两端都修改了status属性，应该冲突\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tresult, err := MergeTexts(tt.base, tt.device1, tt.device2, true)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"MergeTexts() error = %v\", err)\n\t\t\t}\n\n\t\t\tif result.HasConflict != tt.wantConflict {\n\t\t\t\tt.Errorf(\"%s\\nHasConflict = %v, want %v\", tt.description, result.HasConflict, tt.wantConflict)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TestScenario_TableEditing 表格编辑场景\nfunc TestScenario_TableEditing(t *testing.T) {\n\ttests := []struct {\n\t\tname         string\n\t\tbase         string\n\t\tdevice1      string\n\t\tdevice2      string\n\t\twantConflict bool\n\t\tdescription  string\n\t}{\n\t\t{\n\t\t\tname: \"添加不同行-一端末尾一端中间-冲突\",\n\t\t\tbase: `| 姓名 | 分数 |\n|------|------|\n| 张三 | 90   |`,\n\t\t\tdevice1: `| 姓名 | 分数 |\n|------|------|\n| 张三 | 90   |\n| 李四 | 85   |`,\n\t\t\tdevice2: `| 姓名 | 分数 |\n|------|------|\n| 王五 | 88   |\n| 张三 | 90   |`,\n\t\t\twantConflict: true,\n\t\t\tdescription:  \"一端在末尾添加行，一端在中间插入行，由于 diff 算法限制视为冲突\",\n\t\t},\n\t\t{\n\t\t\tname: \"修改同一单元格-冲突\",\n\t\t\tbase: `| 项目 | 状态 |\n|------|------|\n| 功能A | 进行中 |`,\n\t\t\tdevice1: `| 项目 | 状态 |\n|------|------|\n| 功能A | 已完成 |`,\n\t\t\tdevice2: `| 项目 | 状态 |\n|------|------|\n| 功能A | 已取消 |`,\n\t\t\twantConflict: true,\n\t\t\tdescription:  \"两端都修改了功能A的状态\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tresult, err := MergeTexts(tt.base, tt.device1, tt.device2, true)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"MergeTexts() error = %v\", err)\n\t\t\t}\n\n\t\t\tif result.HasConflict != tt.wantConflict {\n\t\t\t\tt.Errorf(\"%s\\nHasConflict = %v, want %v\", tt.description, result.HasConflict, tt.wantConflict)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TestScenario_LinkEditing 链接编辑场景\nfunc TestScenario_LinkEditing(t *testing.T) {\n\ttests := []struct {\n\t\tname         string\n\t\tbase         string\n\t\tdevice1      string\n\t\tdevice2      string\n\t\twantConflict bool\n\t\tdescription  string\n\t}{\n\t\t{\n\t\t\tname:         \"添加不同链接-无冲突\",\n\t\t\tbase:         \"相关笔记：\\n- [[笔记A]]\",\n\t\t\tdevice1:      \"相关笔记：\\n- [[笔记A]]\\n- [[笔记B]]\",\n\t\t\tdevice2:      \"相关笔记：\\n- [[笔记A]]\\n- [[笔记C]]\",\n\t\t\twantConflict: false,\n\t\t\tdescription:  \"两端添加不同的链接\",\n\t\t},\n\t\t{\n\t\t\tname:         \"修改同一链接-冲突\",\n\t\t\tbase:         \"参考：[[旧链接]]\",\n\t\t\tdevice1:      \"参考：[[新链接1]]\",\n\t\t\tdevice2:      \"参考：[[新链接2]]\",\n\t\t\twantConflict: true,\n\t\t\tdescription:  \"两端都修改了同一个链接\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tresult, err := MergeTexts(tt.base, tt.device1, tt.device2, true)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"MergeTexts() error = %v\", err)\n\t\t\t}\n\n\t\t\tif result.HasConflict != tt.wantConflict {\n\t\t\t\tt.Errorf(\"%s\\nHasConflict = %v, want %v\", tt.description, result.HasConflict, tt.wantConflict)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TestScenario_EmptyAndWhitespace 空内容和空白字符场景\nfunc TestScenario_EmptyAndWhitespace(t *testing.T) {\n\ttests := []struct {\n\t\tname         string\n\t\tbase         string\n\t\tdevice1      string\n\t\tdevice2      string\n\t\twantConflict bool\n\t\tdescription  string\n\t}{\n\t\t{\n\t\t\tname:         \"从空文件开始-两端都添加内容\",\n\t\t\tbase:         \"\",\n\t\t\tdevice1:      \"设备1添加的内容\",\n\t\t\tdevice2:      \"设备2添加的内容\",\n\t\t\twantConflict: true,\n\t\t\tdescription:  \"空文件两端同时添加不同内容，应该冲突\",\n\t\t},\n\t\t{\n\t\t\tname:         \"一端清空文件-另一端修改-冲突\",\n\t\t\tbase:         \"原始内容\\n第二行\",\n\t\t\tdevice1:      \"\",\n\t\t\tdevice2:      \"原始内容（已修改）\\n第二行\",\n\t\t\twantConflict: true,\n\t\t\tdescription:  \"一端清空文件，另一端修改内容\",\n\t\t},\n\t\t{\n\t\t\tname:         \"两端都清空文件-无冲突\",\n\t\t\tbase:         \"要删除的内容\",\n\t\t\tdevice1:      \"\",\n\t\t\tdevice2:      \"\",\n\t\t\twantConflict: false,\n\t\t\tdescription:  \"两端都清空文件，结果一致\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tresult, err := MergeTexts(tt.base, tt.device1, tt.device2, true)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"MergeTexts() error = %v\", err)\n\t\t\t}\n\n\t\t\tif result.HasConflict != tt.wantConflict {\n\t\t\t\tt.Errorf(\"%s\\nHasConflict = %v, want %v\\nConflictInfo: %s\",\n\t\t\t\t\ttt.description, result.HasConflict, tt.wantConflict, result.ConflictInfo)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/email/email.go",
    "content": "package email\n\nimport (\n\t\"crypto/tls\"\n\n\t\"gopkg.in/gomail.v2\"\n)\n\ntype Email struct {\n\t*SMTPInfo\n}\n\ntype SMTPInfo struct {\n\tHost     string\n\tPort     int\n\tIsSSL    bool\n\tUserName string\n\tPassword string\n\tFrom     string\n}\n\nfunc NewEmail(info *SMTPInfo) *Email {\n\treturn &Email{SMTPInfo: info}\n}\n\nfunc (e *Email) SendMail(to []string, subject, body string) error {\n\tm := gomail.NewMessage()\n\tm.SetHeader(\"From\", e.From)\n\tm.SetHeader(\"To\", to...)\n\tm.SetHeader(\"Subject\", subject)\n\tm.SetBody(\"text/html\", body)\n\n\tdialer := gomail.NewDialer(e.Host, e.Port, e.UserName, e.Password)\n\tdialer.TLSConfig = &tls.Config{InsecureSkipVerify: e.IsSSL}\n\treturn dialer.DialAndSend(m)\n}\n"
  },
  {
    "path": "pkg/email/email_test.go",
    "content": "package email\n\nimport (\n\t\"testing\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestNewEmail(t *testing.T) {\n\tinfo := &SMTPInfo{\n\t\tHost:     \"smtp.test.com\",\n\t\tPort:     587,\n\t\tIsSSL:    true,\n\t\tUserName: \"user\",\n\t\tPassword: \"password\",\n\t\tFrom:     \"sender@test.com\",\n\t}\n\n\temail := NewEmail(info)\n\tassert.NotNil(t, email)\n\tassert.Equal(t, \"smtp.test.com\", email.Host)\n\tassert.Equal(t, 587, email.Port)\n\tassert.True(t, email.IsSSL)\n}\n"
  },
  {
    "path": "pkg/errors/err.go",
    "content": "package errors\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"runtime\"\n\n\t\"github.com/pkg/errors\"\n)\n\nfunc callers() []uintptr {\n\tvar pcs [32]uintptr\n\tl := runtime.Callers(3, pcs[:])\n\treturn pcs[:l]\n}\n\n// Error with caller stack information\ntype Error interface {\n\terror\n\tt()\n}\n\nvar _ Error = (*item)(nil)\nvar _ fmt.Formatter = (*item)(nil)\n\ntype item struct {\n\tmsg   string\n\tstack []uintptr\n}\n\nfunc (i *item) Error() string {\n\treturn i.msg\n}\n\nfunc (i *item) t() {}\n\n// Format used by go.uber.org/zap in Verbose\nfunc (i *item) Format(s fmt.State, verb rune) {\n\tio.WriteString(s, i.msg)\n\tio.WriteString(s, \"\\n\")\n\n\tfor _, pc := range i.stack {\n\t\tfmt.Fprintf(s, \"%+v\\n\", errors.Frame(pc))\n\t}\n}\n\n// New create a new error\nfunc New(msg string) Error {\n\treturn &item{msg: msg, stack: callers()}\n}\n\n// Errorf create a new error\nfunc Errorf(format string, args ...interface{}) Error {\n\treturn &item{msg: fmt.Sprintf(format, args...), stack: callers()}\n}\n\n// Wrap with some extra message into err\nfunc Wrap(err error, msg string) Error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\te, ok := err.(*item)\n\tif !ok {\n\t\treturn &item{msg: fmt.Sprintf(\"%s; %s\", msg, err.Error()), stack: callers()}\n\t}\n\n\te.msg = fmt.Sprintf(\"%s; %s\", msg, e.msg)\n\treturn e\n}\n\n// Wrapf with some extra message into err\nfunc Wrapf(err error, format string, args ...interface{}) Error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tmsg := fmt.Sprintf(format, args...)\n\n\te, ok := err.(*item)\n\tif !ok {\n\t\treturn &item{msg: fmt.Sprintf(\"%s; %s\", msg, err.Error()), stack: callers()}\n\t}\n\n\te.msg = fmt.Sprintf(\"%s; %s\", msg, e.msg)\n\treturn e\n}\n\n// WithStack add caller stack information\nfunc WithStack(err error) Error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tif e, ok := err.(*item); ok {\n\t\treturn e\n\t}\n\n\treturn &item{msg: err.Error(), stack: callers()}\n}\n"
  },
  {
    "path": "pkg/errors/err_test.go",
    "content": "package errors\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"go.uber.org/zap\"\n)\n\nfunc TestErr(t *testing.T) {\n\tlogger, _ := zap.NewProduction()\n\n\tlogger.Info(\"errorf\", zap.Error(Errorf(\"%s %d\", \"127.0.0.1\", 80)))\n\n\terr := New(\"a dummy err\")\n\tlogger.Info(\"new\", zap.Error(err))\n\n\terr = Wrap(err, \"ping timeout err\")\n\tlogger.Info(\"wrap\", zap.Error(err))\n\n\terr = Wrapf(err, \"ip: %s port: %d\", \"localhost\", 80)\n\tlogger.Info(\"wrapf\", zap.Error(err))\n\n\terr = WithStack(err)\n\tlogger.Info(\"withstack\", zap.Error(err))\n\n\tlogger.Info(\"wrap std\", zap.Error(Wrap(errors.New(\"std err\"), \"some err occurs\")))\n\n\tlogger.Info(\"wrapf std\", zap.Error(Wrapf(errors.New(\"std err\"), \"ip: %s port: %d\", \"localhost\", 80)))\n\n\tlogger.Info(\"withstack std\", zap.Error(WithStack(errors.New(\"std err\"))))\n\n\tt.Logf(\"%+v\", New(\"a dummy error\"))\n}\n"
  },
  {
    "path": "pkg/errors/errors.go",
    "content": "package errors\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n)\n\n// AppError unified application error struct\n// AppError 统一应用错误结构体\n// Contains error code, message, details, trace ID and timestamp\n// 包含错误码、消息、详情、追踪ID和时间戳\ntype AppError struct {\n\t// Code error code\n\t// Code 错误码\n\tCode int `json:\"code\"`\n\t// Message error message\n\t// Message 错误消息\n\tMessage string `json:\"message\"`\n\t// Details error details (optional)\n\t// Details 错误详情（可选）\n\tDetails []string `json:\"details,omitempty\"`\n\t// TraceID request trace ID\n\t// TraceID 请求追踪ID\n\tTraceID string `json:\"traceId,omitempty\"`\n\t// Cause original error (not serialized to JSON)\n\t// Cause 原始错误（不序列化到JSON）\n\tCause error `json:\"-\"`\n\t// Timestamp error occurrence time\n\t// Timestamp 错误发生时间\n\tTimestamp time.Time `json:\"timestamp\"`\n}\n\n// Error implements the error interface\n// Error 实现 error 接口\nfunc (e *AppError) Error() string {\n\treturn e.Message\n}\n\n// Unwrap implements the errors.Unwrap interface, supports error chain tracing\n// Unwrap 实现 errors.Unwrap 接口，支持错误链路追踪\nfunc (e *AppError) Unwrap() error {\n\treturn e.Cause\n}\n\n// NewAppError creates AppError from Code object\n// NewAppError 从 Code 对象创建 AppError\nfunc NewAppError(c *code.Code, cause error) *AppError {\n\treturn &AppError{\n\t\tCode:      c.Code(),\n\t\tMessage:   c.Msg(),\n\t\tDetails:   c.Details(),\n\t\tCause:     cause,\n\t\tTimestamp: time.Now(),\n\t}\n}\n\n// NewAppErrorWithMessage creates AppError with custom message\n// NewAppErrorWithMessage 创建带自定义消息的 AppError\nfunc NewAppErrorWithMessage(errorCode int, message string, cause error) *AppError {\n\treturn &AppError{\n\t\tCode:      errorCode,\n\t\tMessage:   message,\n\t\tCause:     cause,\n\t\tTimestamp: time.Now(),\n\t}\n}\n\n// WithTraceID sets TraceID and returns itself (chain call)\n// WithTraceID 设置 TraceID 并返回自身（链式调用）\nfunc (e *AppError) WithTraceID(traceID string) *AppError {\n\te.TraceID = traceID\n\treturn e\n}\n\n// WithDetails sets details and returns itself (chain call)\n// WithDetails 设置详情并返回自身（链式调用）\nfunc (e *AppError) WithDetails(details ...string) *AppError {\n\te.Details = details\n\treturn e\n}\n\n// getTraceIDFromGin retrieves Trace ID from gin.Context\n// getTraceIDFromGin 从 gin.Context 获取 Trace ID\nfunc getTraceIDFromGin(c *gin.Context) string {\n\tif c == nil {\n\t\treturn \"\"\n\t}\n\t// \"trace_id\" is the key used in internal/middleware/tracer.go\n\tif id, exists := c.Get(\"trace_id\"); exists {\n\t\tif traceID, ok := id.(string); ok {\n\t\t\treturn traceID\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// ErrorResponse unified error response processing\n// ErrorResponse 统一错误响应处理\n// Get TraceID from gin.Context, convert error to AppError and return JSON response\n// 从 gin.Context 获取 TraceID，将错误转换为 AppError 并返回 JSON 响应\nfunc ErrorResponse(c *gin.Context, err error) {\n\ttraceID := getTraceIDFromGin(c)\n\n\tvar appErr *AppError\n\tif errors.As(err, &appErr) {\n\t\t// Already AppError, set TraceID\n\t\t// 已经是 AppError，设置 TraceID\n\t\tappErr.TraceID = traceID\n\t\tif appErr.Cause != nil {\n\t\t\tappErr.Details = append(appErr.Details, appErr.Cause.Error())\n\t\t}\n\t\tc.JSON(http.StatusOK, appErr)\n\t\treturn\n\t}\n\n\t// Check if it is a Code type error\n\t// 检查是否是 Code 类型错误\n\tvar codeErr *code.Code\n\tif errors.As(err, &codeErr) {\n\t\tresponse := &AppError{\n\t\t\tCode:      codeErr.Code(),\n\t\t\tMessage:   codeErr.Msg(),\n\t\t\tDetails:   codeErr.Details(),\n\t\t\tTraceID:   traceID,\n\t\t\tTimestamp: time.Now(),\n\t\t}\n\t\tc.JSON(http.StatusOK, response)\n\t\treturn\n\t}\n\n\t// Unknown error, return internal error\n\t// 未知错误，返回内部错误\n\tc.JSON(http.StatusOK, &AppError{\n\t\tCode:      code.ErrorServerInternal.Code(),\n\t\tMessage:   code.ErrorServerInternal.Msg(),\n\t\tDetails:   code.ErrorServerInternal.WithDetails(err.Error()).Details(),\n\t\tTraceID:   traceID,\n\t\tTimestamp: time.Now(),\n\t})\n}\n\n// ErrorResponseWithCode returns error response using specified Code object\n// ErrorResponseWithCode 使用指定的 Code 对象返回错误响应\nfunc ErrorResponseWithCode(c *gin.Context, codeErr *code.Code, cause error) {\n\ttraceID := getTraceIDFromGin(c)\n\n\tresponse := &AppError{\n\t\tCode:      codeErr.Code(),\n\t\tMessage:   codeErr.Msg(),\n\t\tDetails:   codeErr.Details(),\n\t\tTraceID:   traceID,\n\t\tCause:     cause,\n\t\tTimestamp: time.Now(),\n\t}\n\tc.JSON(http.StatusOK, response)\n}\n\n// IsAppError checks if error is of type AppError\n// IsAppError 检查错误是否为 AppError 类型\nfunc IsAppError(err error) bool {\n\tvar appErr *AppError\n\treturn errors.As(err, &appErr)\n}\n\n// GetAppError gets AppError from error chain\n// GetAppError 从错误链中获取 AppError\nfunc GetAppError(err error) *AppError {\n\tvar appErr *AppError\n\tif errors.As(err, &appErr) {\n\t\treturn appErr\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/fileurl/file.go",
    "content": "package fileurl\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"mime/multipart\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/google/uuid\"\n)\n\ntype FileType int\n\nconst ImageType FileType = iota + 1\n\n// IsFile determines if the given path is a file\n// IsFile 判断所给路径是否为文件\nfunc IsFile(path string) bool {\n\treturn !IsDir(path)\n}\n\n// IsDir determines if the given path is a directory\n// IsDir 判断所给路径是否为文件夹\nfunc IsDir(path string) bool {\n\ts, err := os.Stat(path)\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn s.IsDir()\n\n}\n\n// GetFileName gets file path\n// GetFileName 获取文件路径\nfunc GetFileName(name string) string {\n\text := GetFileExt(name)\n\tfileName := strings.TrimSuffix(name, ext)\n\t// fileName = util.EncodeMD5(fileName)\n\treturn fileName + ext\n}\n\nfunc GetFileNameOrRandom(fileName string) string {\n\t// Attachments uploaded via clipboard are given a default name\n\t// 通过剪切板上传的附件 都是一个默认名字\n\tif fileName == \"image.png\" {\n\t\tfileName = GetFileName(uuid.New().String() + fileName)\n\t} else {\n\t\tfileName = GetFileName(fileName)\n\t}\n\treturn fileName\n}\n\n// GetFileExt gets file extension\n// GetFileExt 获取文件后缀\nfunc GetFileExt(name string) string {\n\treturn path.Ext(name)\n}\n\n// GetDatePath gets date save path\n// GetDatePath 获取日期保存路径\nfunc GetDatePath(timeFormat string) string {\n\tnow := time.Now()\n\tif timeFormat == \"\" {\n\t\ttimeFormat = \"200601/02\"\n\t}\n\tdatePath := PathSuffixCheckAdd(now.Format(timeFormat), \"/\")\n\treturn datePath\n}\n\n// IsContainExt determines if file extension is within the allowed range\n// IsContainExt 判断文件后缀是否在允许范围内\nfunc IsContainExt(t FileType, name string, uploadAllowExts []string) bool {\n\text := GetFileExt(name)\n\text = strings.ToUpper(ext)\n\tswitch t {\n\tcase ImageType:\n\t\tfor _, allowExt := range uploadAllowExts {\n\t\t\tif strings.ToUpper(allowExt) == ext {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\n// IsFileSizeAllowed determines if file size is allowed\n// IsFileSizeAllowed 文件大小是否被允许\nfunc IsFileSizeAllowed(t FileType, f multipart.File, uploadMaxSize int) bool {\n\tcontent, _ := io.ReadAll(f)\n\tsize := len(content)\n\tswitch t {\n\tcase ImageType:\n\t\tif size >= uploadMaxSize*1024*1024 {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// IsPermission checks file permissions\n// IsPermission 检查文件权限\nfunc IsPermission(dst string) bool {\n\t_, err := os.Stat(dst)\n\treturn os.IsPermission(err)\n}\n\n// IsExist determines if the given path exists\n// IsExist 判断所给路径是否不存在\nfunc IsExist(dst string) bool {\n\t_, err := os.Stat(dst) // os.Stat gets file info // os.Stat获取文件信息\n\tif err != nil {\n\t\treturn os.IsExist(err)\n\t}\n\treturn true\n}\n\n// CreatePath creates path\n// CreatePath 创建路径\nfunc CreatePath(dst string, perm os.FileMode) error {\n\tdir := filepath.Dir(dst)\n\terr := os.MkdirAll(dir, perm)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// GetExePath gets path of current execution file\n// GetExePath 获取当前执行文件的路径\nfunc GetExePath() string {\n\tfile, _ := exec.LookPath(os.Args[0])\n\tpath, _ := filepath.Abs(file)\n\tindex := strings.LastIndex(path, string(os.PathSeparator))\n\treturn path[:index]\n}\n\n// PathSuffixCheckAdd checks path suffix, adds it if not exists\n// PathSuffixCheckAdd 检查路径后缀，如果没有则添加\nfunc PathSuffixCheckAdd(path string, suffix string) string {\n\tif !strings.HasSuffix(path, suffix) {\n\t\tpath = path + suffix\n\t}\n\treturn path\n}\n\n// IsAbsPath determines if it is an absolute path\n// IsAbsPath 判断是否为绝对路径\nfunc IsAbsPath(path string) bool {\n\tif runtime.GOOS == \"windows\" {\n\t\t// Windows system\n\t\t// Windows系统\n\t\tif filepath.VolumeName(path) != \"\" {\n\t\t\treturn true // If there is a drive letter, it is an absolute path // 如果有盘符，则为绝对路径\n\t\t}\n\t\treturn filepath.IsAbs(path) // Check if it is an absolute path // 检查是否是绝对路径\n\t}\n\t// UNIX/Linux system\n\t// UNIX/Linux系统\n\treturn filepath.IsAbs(path)\n}\n\n// GetAbsPath gets absolute path\n// GetAbsPath 获取绝对路径\nfunc GetAbsPath(path string, root string) (string, error) {\n\tif root != \"\" {\n\t\troot = PathSuffixCheckAdd(root, \"/\")\n\t}\n\trealPath := root + path\n\t// If it is already an absolute path, return directly\n\t// 如果本身就是绝对路径 就直接返回\n\tif !IsAbsPath(realPath) {\n\t\tpwdDir, _ := os.Getwd()\n\t\trealPath = PathSuffixCheckAdd(pwdDir, \"/\") + path\n\t}\n\tif IsExist(realPath) {\n\t\treturn realPath, nil\n\t} else {\n\t\treturn \"\", errors.New(\"file not exists\")\n\t}\n}\n\n// CopyFile copies temporary file to target save path\n// CopyFile 将临时文件复制到目标保存路径\n// srcPath: absolute or relative path of source file\n// srcPath: 源文件的绝对或相对路径\n// destPath: full path of target save file (including file name)\n// destPath: 目标保存文件的完整路径（包含文件名）\nfunc CopyFile(srcPath, destPath string) error {\n\t// 1. Open source file\n\t// 1. 打开源文件\n\tsourceFile, err := os.Open(srcPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer sourceFile.Close()\n\n\t// 2. Ensure target directory exists\n\t// 2. 确保目标目录存在\n\t// Recursively create directory, permissions set to 0754\n\t// 递归创建目录，权限设置为 0754\n\tif err := os.MkdirAll(filepath.Dir(destPath), 0754); err != nil {\n\t\treturn err\n\t}\n\n\t// 3. Create target file\n\t// 3. 创建目标文件\n\tdestFile, err := os.Create(destPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer destFile.Close()\n\n\t// 4. Perform copy operation\n\t// 4. 执行复制操作\n\tif _, err := io.Copy(destFile, sourceFile); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/fileurl/fileurl_test.go",
    "content": "package fileurl\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestFileUrlConversions(t *testing.T) {\n\tassert.Equal(t, \".png\", GetFileExt(\"image.png\"))\n\tassert.Equal(t, \"image.png\", GetFileName(\"image.png\"))\n\tassert.Equal(t, \"file.tar.gz\", GetFileName(\"file.tar.gz\"))\n\t\n\t// Check random name override for \"image.png\"\n\trandName := GetFileNameOrRandom(\"image.png\")\n\tassert.NotEqual(t, \"image.png\", randName)\n\tassert.Contains(t, randName, \"image.png\")\n\n\t// Check non-image.png unchanged\n\tassert.Equal(t, \"test.txt\", GetFileNameOrRandom(\"test.txt\"))\n}\n\nfunc TestPathSuffixCheckAdd(t *testing.T) {\n\tassert.Equal(t, \"/abc/\", PathSuffixCheckAdd(\"/abc\", \"/\"))\n\tassert.Equal(t, \"/abc/\", PathSuffixCheckAdd(\"/abc/\", \"/\"))\n}\n\nfunc TestUrlEscape(t *testing.T) {\n\tassert.Equal(t, \"path/to/my%20file.txt\", UrlEscape(\"path/to/my file.txt\"))\n\tassert.Equal(t, \"my%20file.txt\", UrlEscape(\"my file.txt\"))\n}\n\nfunc TestIsAbsPath(t *testing.T) {\n\t// These only apply strictly dynamically across platform, but we can test typical UNIX style if we assume it runs on Linux/Mac\n\tassert.True(t, IsAbsPath(\"/abs/path\"))\n\tassert.False(t, IsAbsPath(\"rel/path\"))\n}\n"
  },
  {
    "path": "pkg/fileurl/source_selector.go",
    "content": "package fileurl\n\nimport (\n\t\"net/http\"\n\t\"time\"\n)\n\nconst (\n\tSourceAuto   = \"auto\"\n\tSourceGitHub = \"github\"\n\tSourceCNB    = \"cnb\"\n\n\tGitHubCheckURL = \"https://api.github.com\"\n)\n\n// SourceSelector handles the logic of selecting the data source (GitHub or CNB)\n// SourceSelector 处理选择数据源（GitHub 或 CNB）的逻辑\ntype SourceSelector struct {\n\tmode      string\n\tisGitHub  bool\n\tlastCheck time.Time\n}\n\n// NewSourceSelector creates a new SourceSelector\n// NewSourceSelector 创建一个新的 SourceSelector\nfunc NewSourceSelector(mode string) *SourceSelector {\n\treturn &SourceSelector{\n\t\tmode: mode,\n\t}\n}\n\n// IsGitHub returns whether the current source should be GitHub\n// IsGitHub 返回当前源是否应该为 GitHub\nfunc (s *SourceSelector) IsGitHub() bool {\n\tswitch s.mode {\n\tcase SourceGitHub:\n\t\treturn true\n\tcase SourceCNB:\n\t\treturn false\n\tdefault:\n\t\t// Auto mode or unknown mode\n\t\t// 自动模式或未知模式\n\t\tif time.Since(s.lastCheck) > 10*time.Minute {\n\t\t\ts.isGitHub = s.checkGitHub()\n\t\t\ts.lastCheck = time.Now()\n\t\t}\n\t\treturn s.isGitHub\n\t}\n}\n\nfunc (s *SourceSelector) checkGitHub() bool {\n\tclient := http.Client{\n\t\tTimeout: 3 * time.Second,\n\t}\n\tresp, err := client.Head(GitHubCheckURL)\n\tif err != nil {\n\t\treturn false\n\t}\n\tdefer resp.Body.Close()\n\treturn resp.StatusCode == http.StatusOK\n}\n"
  },
  {
    "path": "pkg/fileurl/url.go",
    "content": "package fileurl\n\nimport (\n\t\"net/url\"\n\t\"strings\"\n)\n\n// UrlEscape escapes file path\n// UrlEscape 转义文件路径\nfunc UrlEscape(fileKey string) string {\n\tif strings.Contains(fileKey, \"/\") {\n\t\ti := strings.LastIndex(fileKey, \"/\")\n\t\tfileKey = fileKey[:i+1] + url.PathEscape(fileKey[i+1:])\n\t} else {\n\t\tfileKey = url.PathEscape(fileKey)\n\t}\n\treturn fileKey\n}\n"
  },
  {
    "path": "pkg/gin_tools/form.go",
    "content": "// // Copyright 2014 Manu Martinez-Almeida. All rights reserved.\n// // Use of this source code is governed by a MIT style\n// // license that can be found in the LICENSE file.\npackage gin_tools\n\n//\n//import (\n//\t\"errors\"\n//\t\"net/httpclient\"\n//)\n//\n//const defaultMemory = 32 << 20\n//\n//type formBinding struct{}\n//type formMultipartBinding struct{}\n//\n//func (formBinding) Name() string {\n//\treturn \"form\"\n//}\n//\n//func (formBinding) Parse(req *httpclient.Request, params *map[string]string) error {\n//\tif err := req.ParseForm(); err != nil {\n//\t\treturn err\n//\t}\n//\tif err := req.ParseMultipartForm(defaultMemory); err != nil && !errors.Is(err, httpclient.ErrNotMultipart) {\n//\t\treturn err\n//\t}\n//\n//\tif err := req.ParseForm(); err != nil {\n//\t\treturn err\n//\t}\n//\tif err := req.ParseMultipartForm(defaultMemory); err != nil {\n//\t\tif err != httpclient.ErrNotMultipart {\n//\t\t\treturn err\n//\t\t}\n//\t}\n//\tpostMap := *params\n//\tfor k, v := range req.PostForm {\n//\t\tif len(v) > 1 {\n//\t\t\tpostMap[k] = v\n//\t\t} else if len(v) == 1 {\n//\t\t\tpostMap[k] = v[0]\n//\t\t}\n//\t}\n//\t//if err := mapForm(obj, req.Form); err != nil {\n//\t//\treturn nil, err\n//\t//}\n//\treturn nil\n//}\n//\n//func (formMultipartBinding) Name() string {\n//\treturn \"multipart/form-data\"\n//}\n//\n//func (formMultipartBinding) Parse(req *httpclient.Request, params *map[string]string) error {\n//\tif err := req.ParseMultipartForm(defaultMemory); err != nil {\n//\t\treturn err\n//\t}\n//\t//if err := mappingByPtr(obj, (*multipartRequest)(req), \"form\"); err != nil {\n//\t//\treturn nil, err\n//\t//}\n//\n//\treturn nil\n//\n//}\n"
  },
  {
    "path": "pkg/gin_tools/gin_tools_test.go",
    "content": "package gin_tools\n\nimport (\n\t\"bytes\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestRequestParams_Query(t *testing.T) {\n\tgin.SetMode(gin.TestMode)\n\tw := httptest.NewRecorder()\n\tc, _ := gin.CreateTestContext(w)\n\n\treq, _ := http.NewRequest(\"GET\", \"/test?page=1&limit=10\", nil)\n\tc.Request = req\n\n\tparams, err := RequestParams(c)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"1\", params[\"page\"])\n\tassert.Equal(t, \"10\", params[\"limit\"])\n}\n\nfunc TestRequestParams_PostJSON(t *testing.T) {\n\tgin.SetMode(gin.TestMode)\n\tw := httptest.NewRecorder()\n\tc, _ := gin.CreateTestContext(w)\n\n\tbody := bytes.NewBuffer([]byte(`{\"name\":\"test\",\"age\":20}`))\n\treq, _ := http.NewRequest(\"POST\", \"/test?extra=foo\", body)\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tc.Request = req\n\n\tparams, err := RequestParams(c)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"foo\", params[\"extra\"])\n\tassert.Equal(t, \"test\", params[\"name\"])\n\tassert.Equal(t, float64(20), params[\"age\"]) // json decoder handles numbers as float64 mostly\n}\n\nfunc TestRequestParams_PostForm(t *testing.T) {\n\tgin.SetMode(gin.TestMode)\n\tw := httptest.NewRecorder()\n\tc, _ := gin.CreateTestContext(w)\n\n\tform := url.Values{}\n\tform.Add(\"keyword\", \"search\")\n\n\treq, _ := http.NewRequest(\"POST\", \"/test?page=2\", bytes.NewBufferString(form.Encode()))\n\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\tc.Request = req\n\n\tparams, err := RequestParams(c)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"2\", params[\"page\"])\n\tassert.Equal(t, \"search\", params[\"keyword\"])\n}\n"
  },
  {
    "path": "pkg/gin_tools/json.go",
    "content": "// Copyright 2014 Manu Martinez-Almeida. All rights reserved.\n// Use of this source code is governed by a MIT style\n// license that can be found in the LICENSE file.\n\npackage gin_tools\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/json\"\n)\n\n// EnableDecoderUseNumber is used to call the UseNumber method on the JSON\n// Decoder instance. UseNumber causes the Decoder to unmarshal a number into an\n// interface{} as a Number instead of as a float64.\nvar EnableDecoderUseNumber = false\n\n// EnableDecoderDisallowUnknownFields is used to call the DisallowUnknownFields method\n// on the JSON Decoder instance. DisallowUnknownFields causes the Decoder to\n// return an error when the destination is a struct and the input contains object\n// keys which do not match any non-ignored, exported fields in the destination.\nvar EnableDecoderDisallowUnknownFields = false\n\ntype jsonBinding struct{}\n\nfunc (jsonBinding) Name() string {\n\treturn \"json\"\n}\n\nfunc (jsonBinding) Parse(req *http.Request, params *map[string]string) error {\n\tif req == nil || req.Body == nil {\n\t\treturn errors.New(\"invalid request\")\n\t}\n\n\terr := decodeJSON(req.Body, params)\n\treturn err\n}\n\nfunc decodeJSON(r io.Reader, obj any) error {\n\n\t// decoder := json.NewDecoder(r)\n\t// if err := decoder.Decode(obj); err != nil {\n\tvar dec = json.ConfigDefault.NewDecoder(r)\n\tif err := dec.Decode(obj); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/gin_tools/param.go",
    "content": "/**\n  @author: haierkeys\n  @since: 2022/9/14\n  @desc: //TODO\n**/\n\npackage gin_tools\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"net/http\"\n\t\"sync\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/json\"\n\t\"github.com/gin-gonic/gin\"\n)\n\nfunc RequestParams(c *gin.Context) (map[string]interface{}, error) {\n\n\tconst defaultMemory = 32 << 20\n\tcontentType := c.ContentType()\n\n\tvar (\n\t\tdataMap  = make(map[string]interface{})\n\t\tqueryMap = make(map[string]interface{})\n\t\tpostMap  = make(map[string]interface{})\n\t)\n\n\t// @see gin@v1.7.7/binding/query.go ==> func (queryBinding) Bind(req *httpclient.Request, obj interface{})\n\tfor k := range c.Request.URL.Query() {\n\t\tqueryMap[k] = c.Query(k)\n\t}\n\n\tswitch contentType {\n\tcase \"application/json\":\n\t\tvar bodyBytes []byte\n\t\tif c.Request.Body != nil {\n\t\t\tbodyBytes, _ = io.ReadAll(c.Request.Body)\n\t\t}\n\t\tc.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))\n\t\t// @see gin@v1.7.7/binding/json.go ==> func (jsonBinding) Bind(req *httpclient.Request, obj interface{})\n\t\tif c.Request != nil && c.Request.Body != nil {\n\t\t\t//if err := json.NewDecoder(c.Request.Body).Decode(&postMap); err != nil {\n\t\t\tvar dec = json.ConfigDefault.NewDecoder(c.Request.Body)\n\t\t\tif err := dec.Decode(&postMap); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tc.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))\n\tcase \"multipart/form-data\":\n\t\t// @see gin@v1.7.7/binding/form.go ==> func (formMultipartBinding) Bind(req *httpclient.Request, obj interface{})\n\t\tif err := c.Request.ParseMultipartForm(defaultMemory); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor k, v := range c.Request.PostForm {\n\t\t\tif len(v) > 1 {\n\t\t\t\tpostMap[k] = v\n\t\t\t} else if len(v) == 1 {\n\t\t\t\tpostMap[k] = v[0]\n\t\t\t}\n\t\t}\n\tdefault:\n\t\t// ParseForm parses query string in URL and updates the parsing results to r.Form field\n\t\t// ParseForm 解析 URL 中的查询字符串，并将解析结果更新到 r.Form 字段\n\t\t// For POST or PUT requests, ParseForm will also parse the body as a form,\n\t\t// 对于 POST 或 PUT 请求，ParseForm 还会将 body 当作表单解析，\n\t\t// and update the results both to r.PostForm and r.Form. In the parsing results,\n\t\t// 并将结果既更新到 r.PostForm 也更新到 r.Form。解析结果中，\n\t\t// POST or PUT request body takes precedence over URL query string (same name variable, body value is before query string value)\n\t\t// POST 或 PUT 请求主体要优先于 URL 查询字符串（同名变量，主体的值在查询字符串的值前面）\n\t\t// @see gin@v1.7.7/binding/form.go ==> func (formBinding) Bind(req *httpclient.Request, obj interface{})\n\t\tif err := c.Request.ParseForm(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif err := c.Request.ParseMultipartForm(defaultMemory); err != nil {\n\t\t\tif err != http.ErrNotMultipart {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tfor k, v := range c.Request.PostForm {\n\t\t\tif len(v) > 1 {\n\t\t\t\tpostMap[k] = v\n\t\t\t} else if len(v) == 1 {\n\t\t\t\tpostMap[k] = v[0]\n\t\t\t}\n\t\t}\n\t}\n\n\tvar mu sync.RWMutex\n\tfor k, v := range queryMap {\n\t\tmu.Lock()\n\t\tdataMap[k] = v\n\t\tmu.Unlock()\n\t}\n\tfor k, v := range postMap {\n\t\tmu.Lock()\n\t\tdataMap[k] = v\n\t\tmu.Unlock()\n\t}\n\n\treturn dataMap, nil\n}\n"
  },
  {
    "path": "pkg/httpclient/client.go",
    "content": "package httpclient\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc Get(url string) {\n\n\thttpReq, _ := http.NewRequest(\"GET\", url, nil)\n\thttpReq.Header.Add(\"Content-type\", \"application/json\")\n\thttpReq.Host = \"www.example.com\"\n}\n\nfunc Post(postURL string, postData map[string][]string) (string, error) {\n\n\tdata := url.Values(postData)\n\n\tclient := http.Client{\n\t\tTimeout: 10 * time.Second,\n\t}\n\n\tresp, err := client.Post(\n\t\tpostURL,\n\t\t\"application/x-www-form-urlencoded\",\n\t\tstrings.NewReader(data.Encode()),\n\t)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tdefer resp.Body.Close()\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(body), err\n}\n"
  },
  {
    "path": "pkg/httpclient/client_test.go",
    "content": "package httpclient\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestPost(t *testing.T) {\n\t// Mock Server\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tassert.Equal(t, \"POST\", r.Method)\n\t\tassert.Equal(t, \"application/x-www-form-urlencoded\", r.Header.Get(\"Content-Type\"))\n\n\t\tbody, _ := io.ReadAll(r.Body)\n\t\tassert.Equal(t, \"foo=bar\", string(body))\n\n\t\tw.Write([]byte(\"success\"))\n\t}))\n\tdefer ts.Close()\n\n\tdata := map[string][]string{\n\t\t\"foo\": {\"bar\"},\n\t}\n\n\tresp, err := Post(ts.URL, data)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"success\", resp)\n}\n"
  },
  {
    "path": "pkg/json/json.go",
    "content": "package json\n\nimport \"io\"\n\n// Config defines the interface for JSON configuration\n// Config 定义了 JSON 配置的接口\ntype Config interface {\n\tNewDecoder(io.Reader) Decoder\n\tNewEncoder(io.Writer) Encoder\n}\n\n// Decoder defines the interface for JSON decoder\n// Decoder 定义了 JSON 解码器的接口\ntype Decoder interface {\n\tDecode(any) error\n}\n\n// Encoder defines the interface for JSON encoder\n// Encoder 定义了 JSON 编码器的接口\ntype Encoder interface {\n\tEncode(any) error\n}\n"
  },
  {
    "path": "pkg/json/json_sonic.go",
    "content": "//go:build amd64 || arm64\n\npackage json\n\nimport (\n\t\"io\"\n\n\t\"github.com/bytedance/sonic\"\n)\n\nvar (\n\t// Marshal wraps sonic.Marshal\n\t// Marshal 包装了 sonic.Marshal\n\tMarshal = sonic.Marshal\n\t// Unmarshal wraps sonic.Unmarshal\n\t// Unmarshal 包装了 sonic.Unmarshal\n\tUnmarshal = sonic.Unmarshal\n\t// ConfigDefault wraps sonic.ConfigDefault\n\t// ConfigDefault 包装了 sonic.ConfigDefault\n\tConfigDefault = sonicConfig{sonic.ConfigDefault}\n)\n\ntype sonicConfig struct {\n\tsonic.API\n}\n\nfunc (s sonicConfig) NewDecoder(r io.Reader) Decoder {\n\treturn s.API.NewDecoder(r)\n}\n\nfunc (s sonicConfig) NewEncoder(w io.Writer) Encoder {\n\treturn s.API.NewEncoder(w)\n}\n"
  },
  {
    "path": "pkg/json/json_std.go",
    "content": "//go:build !amd64 && !arm64\n\npackage json\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n)\n\nvar (\n\t// Marshal wraps json.Marshal\n\t// Marshal 包装了 json.Marshal\n\tMarshal = json.Marshal\n\t// Unmarshal wraps json.Unmarshal\n\t// Unmarshal 包装了 json.Unmarshal\n\tUnmarshal = json.Unmarshal\n\t// ConfigDefault is the default config using standard library\n\t// ConfigDefault 是使用标准库的默认配置\n\tConfigDefault = stdConfig{}\n)\n\ntype stdConfig struct{}\n\nfunc (s stdConfig) NewDecoder(r io.Reader) Decoder {\n\treturn json.NewDecoder(r)\n}\n\nfunc (s stdConfig) NewEncoder(w io.Writer) Encoder {\n\treturn json.NewEncoder(w)\n}\n"
  },
  {
    "path": "pkg/json/json_test.go",
    "content": "package json\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestJSON_MarshalUnmarshal(t *testing.T) {\n\tdata := map[string]interface{}{\n\t\t\"key\": \"value\",\n\t\t\"num\": float64(123),\n\t}\n\n\t// Test Marshal\n\tbytesData, err := Marshal(data)\n\tassert.NoError(t, err)\n\tassert.NotEmpty(t, bytesData)\n\n\t// Test Unmarshal\n\tvar unmarshaled map[string]interface{}\n\terr = Unmarshal(bytesData, &unmarshaled)\n\tassert.NoError(t, err)\n\tassert.Equal(t, data, unmarshaled)\n}\n\nfunc TestJSON_ConfigDefault(t *testing.T) {\n\tdata := map[string]interface{}{\n\t\t\"hello\": \"world\",\n\t}\n\n\t// Test Encoder\n\tvar buf bytes.Buffer\n\tencoder := ConfigDefault.NewEncoder(&buf)\n\terr := encoder.Encode(data)\n\tassert.NoError(t, err)\n\n\tencodedStr := buf.String()\n\tassert.Contains(t, encodedStr, `\"hello\"`)\n\tassert.Contains(t, encodedStr, `\"world\"`)\n\n\t// Test Decoder\n\tvar output map[string]interface{}\n\tdecoder := ConfigDefault.NewDecoder(&buf)\n\terr = decoder.Decode(&output)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"world\", output[\"hello\"])\n}\n"
  },
  {
    "path": "pkg/limiter/limiter.go",
    "content": "package limiter\n\nimport (\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/juju/ratelimit\"\n)\n\ntype Face interface {\n\tKey(c *gin.Context) string\n\tGetBucket(key string) (*ratelimit.Bucket, bool)\n\tAddBuckets(rules ...BucketRule) Face\n}\n\ntype Limiter struct {\n\tlimiterBuckets map[string]*ratelimit.Bucket\n}\n\ntype BucketRule struct {\n\tKey          string\n\tFillInterval time.Duration\n\tCapacity     int64\n\tQuantum      int64\n}\n"
  },
  {
    "path": "pkg/limiter/limiter_test.go",
    "content": "package limiter\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestMethodLimiter_Key(t *testing.T) {\n\tgin.SetMode(gin.TestMode)\n\n\ttests := []struct {\n\t\turl      string\n\t\texpected string\n\t}{\n\t\t{\"/api/v1/users\", \"/api/v1/users\"},\n\t\t{\"/api/v1/users?name=test&age=20\", \"/api/v1/users\"},\n\t\t{\"/\", \"/\"},\n\t\t{\"/?q=1\", \"/\"},\n\t}\n\n\tlimiter := NewMethodLimiter()\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.url, func(t *testing.T) {\n\t\t\tw := httptest.NewRecorder()\n\t\t\tc, _ := gin.CreateTestContext(w)\n\t\t\treq, _ := http.NewRequest(\"GET\", tt.url, nil)\n\t\t\treq.RequestURI = tt.url\n\t\t\tc.Request = req\n\n\t\t\tkey := limiter.Key(c)\n\t\t\tassert.Equal(t, tt.expected, key)\n\t\t})\n\t}\n}\n\nfunc TestMethodLimiter_Buckets(t *testing.T) {\n\tlimiter := NewMethodLimiter()\n\n\t// Initially empty\n\tbucket, ok := limiter.GetBucket(\"/api/test\")\n\tassert.False(t, ok)\n\tassert.Nil(t, bucket)\n\n\t// Add bucket rule\n\trule := BucketRule{\n\t\tKey:          \"/api/test\",\n\t\tFillInterval: 1 * time.Second,\n\t\tCapacity:     10,\n\t\tQuantum:      1,\n\t}\n\n\tlimiter.AddBuckets(rule)\n\n\t// After addition\n\tbucket, ok = limiter.GetBucket(\"/api/test\")\n\tassert.True(t, ok)\n\tassert.NotNil(t, bucket)\n\tassert.Equal(t, int64(10), bucket.Capacity())\n\n\t// Add another rule\n\trule2 := BucketRule{\n\t\tKey:          \"/api/other\",\n\t\tFillInterval: 1 * time.Second,\n\t\tCapacity:     5,\n\t\tQuantum:      1,\n\t}\n\tlimiter.AddBuckets(rule2)\n\n\tbucket2, ok := limiter.GetBucket(\"/api/other\")\n\tassert.True(t, ok)\n\tassert.Equal(t, int64(5), bucket2.Capacity())\n}\n"
  },
  {
    "path": "pkg/limiter/method_limiter.go",
    "content": "package limiter\n\nimport (\n\t\"strings\"\n\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/juju/ratelimit\"\n)\n\ntype MethodLimiter struct {\n\t*Limiter\n}\n\nfunc NewMethodLimiter() Face {\n\tl := &Limiter{limiterBuckets: make(map[string]*ratelimit.Bucket)}\n\treturn MethodLimiter{\n\t\tLimiter: l,\n\t}\n}\n\nfunc (l MethodLimiter) Key(c *gin.Context) string {\n\turi := c.Request.RequestURI\n\tindex := strings.Index(uri, \"?\")\n\tif index == -1 {\n\t\treturn uri\n\t}\n\n\treturn uri[:index]\n}\n\nfunc (l MethodLimiter) GetBucket(key string) (*ratelimit.Bucket, bool) {\n\tbucket, ok := l.limiterBuckets[key]\n\treturn bucket, ok\n}\n\nfunc (l MethodLimiter) AddBuckets(rules ...BucketRule) Face {\n\tfor _, rule := range rules {\n\t\tif _, ok := l.limiterBuckets[rule.Key]; !ok {\n\t\t\tbucket := ratelimit.NewBucketWithQuantum(\n\t\t\t\trule.FillInterval,\n\t\t\t\trule.Capacity,\n\t\t\t\trule.Quantum,\n\t\t\t)\n\t\t\tl.limiterBuckets[rule.Key] = bucket\n\t\t}\n\t}\n\n\treturn l\n}\n"
  },
  {
    "path": "pkg/logger/fields.go",
    "content": "package logger\n\n// Unified log field naming constants\n// 统一的日志字段命名常量\n// Used to ensure consistency of log field naming across the project, facilitating log query and analysis\n// 用于确保整个项目中日志字段命名的一致性，便于日志查询和分析\nconst (\n\t// FieldTraceID Trace ID field // 追踪 ID 字段\n\tFieldTraceID = \"traceId\"\n\n\t// FieldUID User ID field // 用户 ID 字段\n\tFieldUID = \"uid\"\n\n\t// FieldAction Action type field // 操作类型字段\n\tFieldAction = \"action\"\n\n\t// FieldPath File path field // 文件路径字段\n\tFieldPath = \"path\"\n\n\t// FieldVault Vault name field // 仓库名称字段\n\tFieldVault = \"vault\"\n\n\t// FieldDuration Time elapsed field // 耗时字段\n\tFieldDuration = \"duration\"\n\n\t// FieldSessionID Session ID field // 会话 ID 字段\n\tFieldSessionID = \"sessionId\"\n\n\t// FieldMethod Method name field // 方法名称字段\n\tFieldMethod = \"method\"\n\n\t// FieldError Error message field // 错误信息字段\n\tFieldError = \"error\"\n\n\t// FieldSize File size field // 文件大小字段\n\tFieldSize = \"size\"\n\n\t// FieldChunks Chunks count field // 分块数量字段\n\tFieldChunks = \"chunks\"\n\n\t// FieldBucket Storage bucket name field // 存储桶名称字段\n\tFieldBucket = \"bucket\"\n\n\t// FieldFileKey File key field // 文件键字段\n\tFieldFileKey = \"fileKey\"\n)\n"
  },
  {
    "path": "pkg/logger/logger.go",
    "content": "/*\n * Copyright (C) 2020-2022, IrineSistiana\n *\n * This file is part of mosdns.\n *\n * mosdns is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * mosdns is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n\npackage logger\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/fileurl\"\n\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\ntype Config struct {\n\t// Level, See also zapcore.ParseLevel.\n\tLevel string `yaml:\"level\"`\n\n\t// File that logger will be written into.\n\t// Default is stderr.\n\tFile string `yaml:\"file\"`\n\n\t// Production enables json output.\n\tProduction bool `yaml:\"production\"`\n}\n\nvar (\n\tstderr = zapcore.Lock(os.Stderr)\n\tlvl    = zap.NewAtomicLevelAt(zap.InfoLevel)\n\tl      = zap.New(zapcore.NewCore(zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()), stderr, lvl))\n\ts      = l.Sugar()\n\n\tnop = zap.NewNop()\n)\n\nfunc NewLogger(lc Config) (*zap.Logger, error) {\n\n\tif !fileurl.IsExist(lc.File) {\n\t\tfileurl.CreatePath(lc.File, os.ModePerm)\n\t}\n\n\tlvl, err := zapcore.ParseLevel(lc.Level)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid log level: %w\", err)\n\t}\n\n\tvar fileOut zapcore.WriteSyncer\n\tif lf := lc.File; len(lf) > 0 {\n\t\tf, _, err := zap.Open(lf)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"open log file: %w\", err)\n\t\t}\n\t\tfileOut = zapcore.Lock(f)\n\n\t\tvar fileEncoder zapcore.Encoder\n\t\tif lc.Production {\n\t\t\tfileEncoder = zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())\n\t\t} else {\n\t\t\tfileEncoder = zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())\n\t\t}\n\n\t\tconsoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())\n\n\t\tconsoleCore := zapcore.NewCore(consoleEncoder, zapcore.NewMultiWriteSyncer(zapcore.AddSync(stderr)), lvl)\n\t\tfileCore := zapcore.NewCore(fileEncoder, zapcore.NewMultiWriteSyncer(zapcore.AddSync(fileOut)), lvl)\n\n\t\t// Use zapcore.NewTee to merge two Cores\n\t\t// 使用 zapcore.NewTee 合并两个 Core\n\t\treturn zap.New(zapcore.NewTee(consoleCore, fileCore)), nil\n\n\t} else {\n\t\treturn zap.New(zapcore.NewCore(zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()), stderr, lvl)), nil\n\t}\n}\n\n// L is a global logger.\nfunc L() *zap.Logger {\n\treturn l\n}\n\n// SetLevel sets the log level for the global logger.\nfunc SetLevel(l zapcore.Level) {\n\tlvl.SetLevel(l)\n}\n\n// S is a global logger.\nfunc S() *zap.SugaredLogger {\n\treturn s\n}\n\n// Nop is a logger that never writes out logs.\nfunc Nop() *zap.Logger {\n\treturn nop\n}\n"
  },
  {
    "path": "pkg/logger/logger_test.go",
    "content": "package logger\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nfunc TestLoggerInitialization(t *testing.T) {\n\t// Nop logger\n\tassert.NotNil(t, Nop())\n\n\t// Global Logger\n\tassert.NotNil(t, L())\n\tassert.NotNil(t, S())\n\n\t// SetLevel\n\tSetLevel(zapcore.DebugLevel)\n}\n\nfunc TestNewLogger(t *testing.T) {\n\ttmpDir := t.TempDir()\n\tlogFile := filepath.Join(tmpDir, \"test.log\")\n\n\tcfg := Config{\n\t\tLevel:      \"info\",\n\t\tFile:       logFile,\n\t\tProduction: false,\n\t}\n\n\tlog, err := NewLogger(cfg)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, log)\n\n\tlog.Info(\"test info\")\n\tlog.Sync()\n\n\tstat, err := os.Stat(logFile)\n\tassert.NoError(t, err)\n\tassert.True(t, stat.Size() > 0)\n}\n"
  },
  {
    "path": "pkg/order/order_sn.go",
    "content": "package order\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/w3liu/go-common/constant/timeformat\"\n)\n\nvar num int64\n\n// Generate 24-bit order number\n// 生成24位订单号\n// First 17 bits represent time accurate to milliseconds, middle 3 bits represent process ID, last 4 bits represent sequence number\n// 前面17位代表时间精确到毫秒，中间3位代表进程id，最后4位代表序号\nfunc Generate(t time.Time) string {\n\ts := t.Format(timeformat.Continuity)\n\tm := t.UnixNano()/1e6 - t.UnixNano()/1e9*1e3\n\tms := sup(m, 3)\n\tp := os.Getpid() % 1000\n\tps := sup(int64(p), 3)\n\ti := atomic.AddInt64(&num, 1)\n\tr := i % 10000\n\trs := sup(r, 4)\n\tn := fmt.Sprintf(\"%s%s%s%s\", s, ms, ps, rs)\n\treturn n\n}\n\n// sup pads 0 in front of numbers whose length is less than n\n// 对长度不足n的数字前面补0\nfunc sup(i int64, n int) string {\n\tm := fmt.Sprintf(\"%d\", i)\n\tfor len(m) < n {\n\t\tm = fmt.Sprintf(\"0%s\", m)\n\t}\n\treturn m\n}\n"
  },
  {
    "path": "pkg/order/order_sn_test.go",
    "content": "package order\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestOrderGenerate(t *testing.T) {\n\t// Generate an order\n\tnow := time.Now()\n\torder1 := Generate(now)\n\n\t// Length should be fixed at 24 chars: 14 (time) + 3 (ms) + 3 (pid) + 4 (seq)\n\tassert.Len(t, order1, 24)\n\n\t// Since we use global atomic numbers, generating twice immediately should yield different results\n\torder2 := Generate(now)\n\tassert.Len(t, order2, 24)\n\tassert.NotEqual(t, order1, order2, \"Orders generated consecutively should have different sequence numbers\")\n\n\t// Sup logic test (implicitly tested through structure length, but we can also test sup directly if exported, but it's unexported)\n}\n"
  },
  {
    "path": "pkg/rand/slice.go",
    "content": "package rand\n\nimport (\n\t\"math/rand\"\n\t\"strings\"\n\t\"time\"\n)\n\nvar r *rand.Rand\n\nfunc init() {\n\tr = rand.New(rand.NewSource(time.Now().UnixNano()))\n}\n\n// Randomly pick one from string slice and return // 随机从 字符串slice 里抽取一个返回\nfunc RandomStrSliceOne(s []string) string {\n\treturn s[r.Intn(len(s))]\n}\n\nfunc GetRandString(length int) string {\n\tif length < 1 {\n\t\treturn \"\"\n\t}\n\tchar := \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"\n\tcharArr := strings.Split(char, \"\")\n\tcharlen := len(charArr)\n\tran := rand.New(rand.NewSource(time.Now().UnixNano()))\n\n\trchar := make([]string, 0, length)\n\tfor i := 1; i <= length; i++ {\n\t\trchar = append(rchar, charArr[ran.Intn(charlen)])\n\t}\n\treturn strings.Join(rchar, \"\")\n}\n"
  },
  {
    "path": "pkg/rand/slice_test.go",
    "content": "package rand\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestRandomStrSliceOne(t *testing.T) {\n\tslice := []string{\"apple\", \"banana\", \"cherry\"}\n\t\n\t// Ensure the returned string is one of the slice elements\n\t// 确保返回的字符串是切片中的元素之一\n\tfor i := 0; i < 10; i++ {\n\t\tres := RandomStrSliceOne(slice)\n\t\tassert.Contains(t, slice, res)\n\t}\n}\n\nfunc TestGetRandString(t *testing.T) {\n\t// Test negative/zero length\n\tassert.Equal(t, \"\", GetRandString(0))\n\tassert.Equal(t, \"\", GetRandString(-5))\n\n\t// Test correct length\n\tassert.Len(t, GetRandString(10), 10)\n\tassert.Len(t, GetRandString(50), 50)\n\n\t// Since it's random, running it multiple times shouldn't result in exactly the same string easily\n\t// (Though possible, highly improbable for len 20)\n\tstr1 := GetRandString(20)\n\tstr2 := GetRandString(20)\n\tassert.NotEqual(t, str1, str2)\n}\n"
  },
  {
    "path": "pkg/safe_close/safe_close.go",
    "content": "/*\n * Copyright (C) 2020-2022, IrineSistiana\n *\n * This file is part of mosdns.\n *\n * mosdns is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * mosdns is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n\npackage safe_close\n\nimport \"sync\"\n\n// SafeClose can achieve safe close where WaitClosed returns only after\n// all sub goroutines exited.\n//\n//  1. Main service goroutine starts and wait on ReceiveCloseSignal.\n//  2. Any service's sub goroutine should be started by Attach and wait on ReceiveCloseSignal.\n//  3. If any fatal err occurs, any service goroutine can call SendCloseSignal to close the service.\n//  4. Any third party caller can call SendCloseSignal to close the service.\ntype SafeClose struct {\n\tm           sync.Mutex\n\twg          sync.WaitGroup\n\tcloseSignal chan struct{}\n\tcloseErr    error\n}\n\nfunc NewSafeClose() *SafeClose {\n\treturn &SafeClose{\n\t\tcloseSignal: make(chan struct{}),\n\t}\n}\n\n// WaitClosed waits until all SendCloseSignal is called and all\n// attached funcs in SafeClose are done.\nfunc (s *SafeClose) WaitClosed() error {\n\t<-s.closeSignal\n\ts.wg.Wait()\n\treturn s.closeErr\n}\n\n// SendCloseSignal sends a close signal. Unblock WaitClosed.\n// The given error will be read by WaitClosed.\n// Once SendCloseSignal is called, following calls are noop.\nfunc (s *SafeClose) SendCloseSignal(err error) {\n\ts.m.Lock()\n\tselect {\n\tcase <-s.closeSignal:\n\tdefault:\n\t\ts.closeErr = err\n\t\tclose(s.closeSignal)\n\t}\n\ts.m.Unlock()\n}\n\nfunc (s *SafeClose) ReceiveCloseSignal() <-chan struct{} {\n\treturn s.closeSignal\n}\n\n// Attach add this goroutine to s.wg WaitClosed.\n// f must receive closeSignal and call done when it is done.\n// If s was closed, f will not run.\nfunc (s *SafeClose) Attach(f func(done func(), closeSignal <-chan struct{})) {\n\ts.m.Lock()\n\tselect {\n\tcase <-s.closeSignal:\n\tdefault:\n\t\ts.wg.Add(1)\n\t\tgo func() {\n\t\t\tf(s.wg.Done, s.closeSignal)\n\t\t}()\n\t}\n\ts.m.Unlock()\n}\n"
  },
  {
    "path": "pkg/safe_close/safe_close_test.go",
    "content": "package safe_close\n\nimport (\n\t\"errors\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestSafeClose_BasicFlow(t *testing.T) {\n\tsc := NewSafeClose()\n\n\tvar counter int\n\tvar mu sync.Mutex\n\n\t// Attach a mock service goroutine\n\tsc.Attach(func(done func(), closeSignal <-chan struct{}) {\n\t\tdefer done()\n\t\t<-closeSignal // Block until closed\n\t\tmu.Lock()\n\t\tcounter++\n\t\tmu.Unlock()\n\t})\n\n\t// Give it time to start\n\ttime.Sleep(10 * time.Millisecond)\n\n\texpectedErr := errors.New(\"closing service\")\n\tsc.SendCloseSignal(expectedErr)\n\n\t// Wait for services to finish\n\terr := sc.WaitClosed()\n\n\tassert.Equal(t, expectedErr, err)\n\n\tmu.Lock()\n\tassert.Equal(t, 1, counter, \"Goroutine should have progressed after close signal\")\n\tmu.Unlock()\n}\n\nfunc TestSafeClose_AttachAfterClose(t *testing.T) {\n\tsc := NewSafeClose()\n\tsc.SendCloseSignal(nil)\n\n\terr := sc.WaitClosed()\n\tassert.NoError(t, err)\n\n\tvar run bool\n\tsc.Attach(func(done func(), closeSignal <-chan struct{}) {\n\t\t// Should not reach here\n\t\tdefer done()\n\t\trun = true\n\t})\n\n\ttime.Sleep(10 * time.Millisecond)\n\tassert.False(t, run, \"Attached function should not run if SafeClose is already closed\")\n}\n\nfunc TestSafeClose_MultipleSend(t *testing.T) {\n\tsc := NewSafeClose()\n\t\n\tfirstErr := errors.New(\"first error\")\n\tsc.SendCloseSignal(firstErr)\n\n\t// Second consecutive call should be noop and safely ignored\n\tsc.SendCloseSignal(errors.New(\"second error should be ignored\"))\n\n\terr := sc.WaitClosed()\n\tassert.Equal(t, firstErr, err)\n}\n"
  },
  {
    "path": "pkg/shortlink/sink_cool.go",
    "content": "package shortlink\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"time\"\n)\n\ntype SinkCoolClient struct {\n\tBaseURL string\n\tAPIKey  string\n}\n\ntype CreateRequest struct {\n\tURL        string `json:\"url\"`\n\tExpiration int64  `json:\"expiration,omitempty\"` // Unix seconds\n\tPassword   string `json:\"password,omitempty\"`\n\tCloaking   bool   `json:\"cloaking,omitempty\"`\n\tTitle      string `json:\"title,omitempty\"`\n}\n\ntype CreateResponse struct {\n\tSlug      string `json:\"slug\"`\n\tURL       string `json:\"url\"`\n\tShortLink string `json:\"shortLink\"`\n}\n\nfunc NewSinkCoolClient(baseURL, apiKey string) *SinkCoolClient {\n\treturn &SinkCoolClient{\n\t\tBaseURL: baseURL,\n\t\tAPIKey:  apiKey,\n\t}\n}\n\nfunc (c *SinkCoolClient) Create(url string, expiresAt time.Time, password string, cloaking bool, title string) (string, error) {\n\treqBody := CreateRequest{\n\t\tURL:      url,\n\t\tPassword: password,\n\t\tCloaking: cloaking,\n\t\tTitle:    title,\n\t}\n\tif !expiresAt.IsZero() {\n\t\treqBody.Expiration = expiresAt.Unix()\n\t}\n\n\tjsonData, err := json.Marshal(reqBody)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tapiURL := fmt.Sprintf(\"%s/api/link/create\", c.BaseURL)\n\treq, err := http.NewRequest(\"POST\", apiURL, bytes.NewBuffer(jsonData))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tif c.APIKey != \"\" {\n\t\treq.Header.Set(\"Authorization\", fmt.Sprintf(\"Bearer %s\", c.APIKey))\n\t}\n\n\tclient := &http.Client{Timeout: 15 * time.Second}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {\n\t\tbody, _ := io.ReadAll(resp.Body)\n\t\treturn \"\", fmt.Errorf(\"sink.cool api error: status=%d, body=%s\", resp.StatusCode, string(body))\n\t}\n\n\tvar res CreateResponse\n\tif err := json.NewDecoder(resp.Body).Decode(&res); err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn res.ShortLink, nil\n}\n"
  },
  {
    "path": "pkg/shortlink/sink_cool_test.go",
    "content": "package shortlink\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestSinkCoolClient_Create(t *testing.T) {\n\t// Create a mock HTTP server\n\tmockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tassert.Equal(t, \"/api/link/create\", r.URL.Path)\n\t\tassert.Equal(t, \"Bearer test-api-key\", r.Header.Get(\"Authorization\"))\n\n\t\t// Check decoding request\n\t\tvar reqBody CreateRequest\n\t\terr := json.NewDecoder(r.Body).Decode(&reqBody)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"https://example.com/long-url\", reqBody.URL)\n\t\tassert.Equal(t, \"my-pass\", reqBody.Password)\n\t\tassert.Equal(t, \"Test Title\", reqBody.Title)\n\n\t\t// Mock response\n\t\tw.WriteHeader(http.StatusOK)\n\t\tres := CreateResponse{\n\t\t\tSlug:      \"test-slug\",\n\t\t\tURL:       reqBody.URL,\n\t\t\tShortLink: \"https://sink.cool/test-slug\",\n\t\t}\n\t\tjson.NewEncoder(w).Encode(res)\n\t}))\n\tdefer mockServer.Close()\n\n\t// Use mock server URL\n\tclient := NewSinkCoolClient(mockServer.URL, \"test-api-key\")\n\t\n\t// Test creating link\n\texpiresAt := time.Now().Add(24 * time.Hour)\n\tshortLink, err := client.Create(\"https://example.com/long-url\", expiresAt, \"my-pass\", true, \"Test Title\")\n\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"https://sink.cool/test-slug\", shortLink)\n}\n\nfunc TestSinkCoolClient_ErrorHandling(t *testing.T) {\n\t// Create a mock HTTP server that returns an error\n\tmockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\tw.Write([]byte(`{\"error\": \"invalid url\"}`))\n\t}))\n\tdefer mockServer.Close()\n\n\tclient := NewSinkCoolClient(mockServer.URL, \"test-api-key\")\n\tshortLink, err := client.Create(\"invalid\", time.Time{}, \"\", false, \"\")\n\n\tassert.Error(t, err)\n\tassert.Contains(t, err.Error(), \"sink.cool api error: status=400\")\n\tassert.Empty(t, shortLink)\n}\n"
  },
  {
    "path": "pkg/storage/aliyun_oss/delete.go",
    "content": "package aliyun_oss\n\nimport (\n\t\"path\"\n)\n\nfunc (p *OSS) Delete(fileKey string) error {\n\tif p.Bucket == nil {\n\t\terr := p.GetBucket(\"\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tfileKey = path.Join(p.Config.CustomPath, fileKey)\n\treturn p.Bucket.DeleteObject(fileKey)\n}\n"
  },
  {
    "path": "pkg/storage/aliyun_oss/operation.go",
    "content": "package aliyun_oss\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"time\"\n\n\t\"path\"\n\n\t\"github.com/aliyun/aliyun-oss-go-sdk/oss\"\n)\n\nfunc (p *OSS) GetBucket(bucketName string) error {\n\t// Get bucket\n\tif len(bucketName) <= 0 {\n\t\tbucketName = p.Config.BucketName\n\t}\n\tvar err error\n\tp.Bucket, err = p.Client.Bucket(bucketName)\n\treturn err\n}\n\nfunc (p *OSS) SendFile(fileKey string, file io.Reader, itype string, modTime time.Time) (string, error) {\n\tif p.Bucket == nil {\n\t\terr := p.GetBucket(\"\")\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\tfileKey = path.Join(p.Config.CustomPath, fileKey)\n\n\tvar options []oss.Option\n\tif !modTime.IsZero() {\n\t\toptions = append(options, oss.Meta(\"modification-time\", modTime.Format(time.RFC3339)))\n\t}\n\n\terr := p.Bucket.PutObject(fileKey, file, options...)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn fileKey, nil\n}\n\nfunc (p *OSS) SendContent(fileKey string, content []byte, modTime time.Time) (string, error) {\n\n\tif p.Bucket == nil {\n\t\terr := p.GetBucket(\"\")\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\tfileKey = path.Join(p.Config.CustomPath, fileKey)\n\n\tvar options []oss.Option\n\tif !modTime.IsZero() {\n\t\toptions = append(options, oss.Meta(\"modification-time\", modTime.Format(time.RFC3339)))\n\t}\n\n\terr := p.Bucket.PutObject(fileKey, bytes.NewReader(content), options...)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn fileKey, nil\n}\n"
  },
  {
    "path": "pkg/storage/aliyun_oss/oss.go",
    "content": "package aliyun_oss\n\nimport (\n\toss_sdk \"github.com/aliyun/aliyun-oss-go-sdk/oss\"\n)\n\ntype Config struct {\n\tEndpoint        string `yaml:\"endpoint\"`\n\tBucketName      string `yaml:\"bucket-name\"`\n\tAccessKeyID     string `yaml:\"access-key-id\"`\n\tAccessKeySecret string `yaml:\"access-key-secret\"`\n\tCustomPath      string `yaml:\"custom-path\"`\n}\n\ntype OSS struct {\n\tClient *oss_sdk.Client\n\tBucket *oss_sdk.Bucket\n\tConfig *Config\n}\n\nvar clients = make(map[string]*OSS)\n\nfunc NewClient(conf *Config) (*OSS, error) {\n\tvar id = conf.AccessKeyID\n\tvar endpoint = conf.Endpoint\n\tvar accessKeyId = conf.AccessKeyID\n\tvar accessKeySecret = conf.AccessKeySecret\n\n\tvar err error\n\tif clients[id] != nil {\n\t\treturn clients[id], nil\n\t}\n\t// New client\n\tossClient, err := oss_sdk.New(endpoint, accessKeyId, accessKeySecret)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tclients[id] = &OSS{\n\t\tClient: ossClient,\n\t\tConfig: conf,\n\t}\n\treturn clients[id], nil\n}\n"
  },
  {
    "path": "pkg/storage/aliyun_oss/oss_test.go",
    "content": "package aliyun_oss\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestNewClient(t *testing.T) {\n\tconfig := &Config{\n\t\tEndpoint:        \"oss-cn-hangzhou.aliyuncs.com\",\n\t\tBucketName:      \"test-bucket\",\n\t\tAccessKeyID:     \"test-key\",\n\t\tAccessKeySecret: \"test-secret\",\n\t}\n\n\tclient, err := NewClient(config)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, client)\n\tassert.NotNil(t, client.Client)\n\n\t// Since clients are cached by AccessKeyID, this should return the identical client\n\tclient2, err := NewClient(config)\n\tassert.NoError(t, err)\n\tassert.Equal(t, client, client2)\n}\n"
  },
  {
    "path": "pkg/storage/aws_s3/delete.go",
    "content": "package aws_s3\n\nimport (\n\t\"context\"\n\n\t\"path\"\n\n\t\"github.com/aws/aws-sdk-go-v2/aws\"\n\t\"github.com/aws/aws-sdk-go-v2/service/s3\"\n)\n\nfunc (p *S3) Delete(fileKey string) error {\n\tbucket := p.GetBucket(\"\")\n\tfileKey = path.Join(p.Config.CustomPath, fileKey)\n\n\t_, err := p.S3Client.DeleteObject(context.Background(), &s3.DeleteObjectInput{\n\t\tBucket: aws.String(bucket),\n\t\tKey:    aws.String(fileKey),\n\t})\n\treturn err\n}\n"
  },
  {
    "path": "pkg/storage/aws_s3/operation.go",
    "content": "package aws_s3\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"path\"\n\t\"time\"\n\n\t\"github.com/aws/aws-sdk-go-v2/aws\"\n\t\"github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager\"\n\ttmtypes \"github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager/types\"\n\t\"github.com/aws/aws-sdk-go-v2/service/s3\"\n\t\"github.com/aws/aws-sdk-go-v2/service/s3/types\"\n\t\"github.com/pkg/errors\"\n)\n\nfunc (p *S3) GetBucket(bucketName string) string {\n\n\t// Get bucket\n\tif len(bucketName) <= 0 {\n\t\tbucketName = p.Config.BucketName\n\t}\n\n\treturn bucketName\n}\n\n// SendFile upload file\n// SendFile 上传文件\nfunc (p *S3) SendFile(fileKey string, file io.Reader, itype string, modTime time.Time) (string, error) {\n\n\tctx := context.Background()\n\tbucket := p.GetBucket(\"\")\n\n\tfileKey = path.Join(p.Config.CustomPath, fileKey)\n\n\t//  k, _ := h.Open()\n\n\tinput := &transfermanager.UploadObjectInput{\n\t\tBucket:      aws.String(bucket),\n\t\tKey:         aws.String(fileKey),\n\t\tBody:        file,\n\t\tContentType: aws.String(itype),\n\t}\n\n\tif !modTime.IsZero() {\n\t\tinput.Metadata = map[string]string{\n\t\t\t\"modification-time\": modTime.Format(time.RFC3339),\n\t\t}\n\t}\n\n\t_, err := p.TransferManager.UploadObject(ctx, input)\n\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"aws_s3\")\n\t}\n\n\treturn fileKey, nil\n}\n\nfunc (p *S3) SendContent(fileKey string, content []byte, modTime time.Time) (string, error) {\n\n\tctx := context.Background()\n\tbucket := p.GetBucket(\"\")\n\n\tfileKey = path.Join(p.Config.CustomPath, fileKey)\n\n\tinput := &transfermanager.UploadObjectInput{\n\t\tBucket:            aws.String(bucket),\n\t\tKey:               aws.String(fileKey),\n\t\tBody:              bytes.NewReader(content),\n\t\tChecksumAlgorithm: tmtypes.ChecksumAlgorithmSha256,\n\t}\n\n\tif !modTime.IsZero() {\n\t\tinput.Metadata = map[string]string{\n\t\t\t\"modification-time\": modTime.Format(time.RFC3339),\n\t\t}\n\t}\n\n\toutput, err := p.TransferManager.UploadObject(ctx, input)\n\tif err != nil {\n\t\tvar noBucket *types.NoSuchBucket\n\t\tif errors.As(err, &noBucket) {\n\t\t\tfmt.Printf(\"Bucket %s does not exist.\\n\", bucket)\n\t\t\terr = noBucket\n\t\t}\n\t} else {\n\t\terr := s3.NewObjectExistsWaiter(p.S3Client).Wait(ctx, &s3.HeadObjectInput{\n\t\t\tBucket: aws.String(bucket),\n\t\t\tKey:    aws.String(fileKey),\n\t\t}, time.Minute)\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"Failed attempt to wait for object %s to exist in %s.\\n\", fileKey, bucket)\n\t\t} else {\n\t\t\t_ = *output.Key\n\t\t}\n\t}\n\n\treturn fileKey, errors.Wrap(err, \"aws_s3\")\n}\n"
  },
  {
    "path": "pkg/storage/aws_s3/s3.go",
    "content": "package aws_s3\n\nimport (\n\t\"context\"\n\n\t\"github.com/aws/aws-sdk-go-v2/config\"\n\t\"github.com/aws/aws-sdk-go-v2/credentials\"\n\t\"github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager\"\n\t\"github.com/aws/aws-sdk-go-v2/service/s3\"\n\t\"github.com/pkg/errors\"\n)\n\ntype Config struct {\n\tRegion          string `yaml:\"region\"`\n\tBucketName      string `yaml:\"bucket-name\"`\n\tAccessKeyID     string `yaml:\"access-key-id\"`\n\tAccessKeySecret string `yaml:\"access-key-secret\"`\n\tCustomPath      string `yaml:\"custom-path\"`\n}\n\ntype S3 struct {\n\tS3Client        *s3.Client\n\tTransferManager *transfermanager.Client\n\tConfig          *Config\n}\n\nvar clients = make(map[string]*S3)\n\nfunc NewClient(conf *Config) (*S3, error) {\n\tvar region = conf.Region\n\tvar accessKeyId = conf.AccessKeyID\n\tvar accessKeySecret = conf.AccessKeySecret\n\n\tif clients[accessKeyId] != nil {\n\t\treturn clients[accessKeyId], nil\n\t}\n\n\tcfg, err := config.LoadDefaultConfig(context.TODO(),\n\t\tconfig.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKeyId, accessKeySecret, \"\")),\n\t\tconfig.WithRegion(region),\n\t)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"aws_s3\")\n\t}\n\n\tclient := s3.NewFromConfig(cfg, func(o *s3.Options) {})\n\n\tclients[accessKeyId] = &S3{\n\t\tS3Client:        client,\n\t\tTransferManager: transfermanager.New(client),\n\t\tConfig:          conf,\n\t}\n\treturn clients[accessKeyId], nil\n}\n"
  },
  {
    "path": "pkg/storage/aws_s3/s3_test.go",
    "content": "package aws_s3\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestNewClient(t *testing.T) {\n\tconfig := &Config{\n\t\tRegion:          \"us-east-1\",\n\t\tBucketName:      \"test-bucket\",\n\t\tAccessKeyID:     \"aws-test-key\",\n\t\tAccessKeySecret: \"aws-test-secret\",\n\t}\n\n\tclient, err := NewClient(config)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, client)\n\tassert.NotNil(t, client.S3Client)\n\tassert.NotNil(t, client.TransferManager)\n\n\t// Check cache\n\tclient2, err := NewClient(config)\n\tassert.NoError(t, err)\n\tassert.Equal(t, client, client2)\n}\n"
  },
  {
    "path": "pkg/storage/cloudflare_r2/delete.go",
    "content": "package cloudflare_r2\n\nimport (\n\t\"context\"\n\n\t\"path\"\n\n\t\"github.com/aws/aws-sdk-go-v2/aws\"\n\t\"github.com/aws/aws-sdk-go-v2/service/s3\"\n)\n\nfunc (p *R2) Delete(fileKey string) error {\n\tbucket := p.GetBucket(\"\")\n\tfileKey = path.Join(p.Config.CustomPath, fileKey)\n\n\t_, err := p.S3Client.DeleteObject(context.Background(), &s3.DeleteObjectInput{\n\t\tBucket: aws.String(bucket),\n\t\tKey:    aws.String(fileKey),\n\t})\n\treturn err\n}\n"
  },
  {
    "path": "pkg/storage/cloudflare_r2/operation.go",
    "content": "package cloudflare_r2\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"path\"\n\t\"time\"\n\n\t\"github.com/aws/aws-sdk-go-v2/aws\"\n\t\"github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager\"\n\ttmtypes \"github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager/types\"\n\t\"github.com/aws/aws-sdk-go-v2/service/s3\"\n\t\"github.com/aws/aws-sdk-go-v2/service/s3/types\"\n\t\"github.com/pkg/errors\"\n)\n\nfunc (p *R2) GetBucket(bucketName string) string {\n\n\t// Get bucket\n\tif len(bucketName) <= 0 {\n\t\tbucketName = p.Config.BucketName\n\t}\n\n\treturn bucketName\n}\n\n// SendFile upload file\n// SendFile 上传文件\nfunc (p *R2) SendFile(fileKey string, file io.Reader, itype string, modTime time.Time) (string, error) {\n\n\tctx := context.Background()\n\tbucket := p.GetBucket(\"\")\n\n\tfileKey = path.Join(p.Config.CustomPath, fileKey)\n\t// k, _ := h.Open()\n\tinput := &transfermanager.UploadObjectInput{\n\t\tBucket:      aws.String(bucket),\n\t\tKey:         aws.String(fileKey),\n\t\tBody:        file,\n\t\tContentType: aws.String(itype),\n\t}\n\n\tif !modTime.IsZero() {\n\t\tinput.Metadata = map[string]string{\n\t\t\t\"modification-time\": modTime.Format(time.RFC3339),\n\t\t}\n\t}\n\n\t_, err := p.TransferManager.UploadObject(ctx, input)\n\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"cloudflare_r2\")\n\t}\n\n\treturn fileKey, nil\n}\n\nfunc (p *R2) SendContent(fileKey string, content []byte, modTime time.Time) (string, error) {\n\n\tctx := context.Background()\n\tbucket := p.GetBucket(\"\")\n\n\tfileKey = path.Join(p.Config.CustomPath, fileKey)\n\n\tinput := &transfermanager.UploadObjectInput{\n\t\tBucket:            aws.String(bucket),\n\t\tKey:               aws.String(fileKey),\n\t\tBody:              bytes.NewReader(content),\n\t\tChecksumAlgorithm: tmtypes.ChecksumAlgorithmSha256,\n\t}\n\n\tif !modTime.IsZero() {\n\t\tinput.Metadata = map[string]string{\n\t\t\t\"modification-time\": modTime.Format(time.RFC3339),\n\t\t}\n\t}\n\n\toutput, err := p.TransferManager.UploadObject(ctx, input)\n\tif err != nil {\n\t\tvar noBucket *types.NoSuchBucket\n\t\tif errors.As(err, &noBucket) {\n\t\t\tfmt.Printf(\"Bucket %s does not exist.\\n\", bucket)\n\t\t\terr = noBucket\n\t\t}\n\t} else {\n\t\terr := s3.NewObjectExistsWaiter(p.S3Client).Wait(ctx, &s3.HeadObjectInput{\n\t\t\tBucket: aws.String(bucket),\n\t\t\tKey:    aws.String(fileKey),\n\t\t}, time.Minute)\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"Failed attempt to wait for object %s to exist in %s.\\n\", fileKey, bucket)\n\t\t} else {\n\t\t\t_ = *output.Key\n\t\t}\n\t}\n\n\treturn fileKey, errors.Wrap(err, \"cloudflare_r2\")\n}\n"
  },
  {
    "path": "pkg/storage/cloudflare_r2/r2.go",
    "content": "package cloudflare_r2\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/aws/aws-sdk-go-v2/aws\"\n\t\"github.com/aws/aws-sdk-go-v2/config\"\n\t\"github.com/aws/aws-sdk-go-v2/credentials\"\n\t\"github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager\"\n\t\"github.com/aws/aws-sdk-go-v2/service/s3\"\n\t\"github.com/pkg/errors\"\n)\n\ntype Config struct {\n\tAccountID       string `yaml:\"account-id\"`\n\tBucketName      string `yaml:\"bucket-name\"`\n\tAccessKeyID     string `yaml:\"access-key-id\"`\n\tAccessKeySecret string `yaml:\"access-key-secret\"`\n\tCustomPath      string `yaml:\"custom-path\"`\n}\n\ntype R2 struct {\n\tS3Client        *s3.Client\n\tTransferManager *transfermanager.Client\n\tConfig          *Config\n}\n\nvar clients = make(map[string]*R2)\n\nfunc NewClient(conf *Config) (*R2, error) {\n\tvar accountId = conf.AccountID\n\tvar accessKeyId = conf.AccessKeyID\n\tvar accessKeySecret = conf.AccessKeySecret\n\n\tif clients[accessKeyId] != nil {\n\t\treturn clients[accessKeyId], nil\n\t}\n\n\tcfg, err := config.LoadDefaultConfig(context.TODO(),\n\t\tconfig.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKeyId, accessKeySecret, \"\")),\n\t\tconfig.WithRegion(\"auto\"),\n\t)\n\tif err != nil {\n\n\t\treturn nil, errors.Wrap(err, \"cloudflare_r2\")\n\t}\n\n\tclient := s3.NewFromConfig(cfg, func(o *s3.Options) {\n\t\to.BaseEndpoint = aws.String(fmt.Sprintf(\"https://%s.r2.cloudflarestorage.com\", accountId))\n\t})\n\n\tclients[accessKeyId] = &R2{\n\t\tS3Client:        client,\n\t\tTransferManager: transfermanager.New(client),\n\t\tConfig:          conf,\n\t}\n\treturn clients[accessKeyId], nil\n}\n"
  },
  {
    "path": "pkg/storage/cloudflare_r2/r2_test.go",
    "content": "package cloudflare_r2\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestNewClient(t *testing.T) {\n\tconfig := &Config{\n\t\tAccountID:       \"cloudflare-account-id\",\n\t\tBucketName:      \"test-bucket\",\n\t\tAccessKeyID:     \"r2-test-key\",\n\t\tAccessKeySecret: \"r2-test-secret\",\n\t}\n\n\tclient, err := NewClient(config)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, client)\n\tassert.NotNil(t, client.S3Client) // Still uses s3 aws SDK\n\n\t// Check cache\n\tclient2, err := NewClient(config)\n\tassert.NoError(t, err)\n\tassert.Equal(t, client, client2)\n}\n"
  },
  {
    "path": "pkg/storage/local_fs/delete.go",
    "content": "package local_fs\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/fileurl\"\n)\n\nfunc (p *LocalFS) Delete(fileKey string) error {\n\tdstFileKey := filepath.Join(p.getSavePath(), fileKey)\n\tif fileurl.IsExist(dstFileKey) {\n\t\treturn os.Remove(dstFileKey)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/storage/local_fs/local.go",
    "content": "package local_fs\n\ntype Config struct {\n\tCustomPath string `yaml:\"custom-path\"`\n\tSavePath   string `yaml:\"save-path\"`\n}\n\ntype LocalFS struct {\n\tIsCheckSave bool\n\tConfig      *Config\n}\n\nfunc NewClient(conf *Config) (*LocalFS, error) {\n\treturn &LocalFS{\n\t\tConfig: conf,\n\t}, nil\n}\n"
  },
  {
    "path": "pkg/storage/local_fs/operation.go",
    "content": "package local_fs\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"io\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/fileurl\"\n)\n\nfunc (p *LocalFS) CheckSave() error {\n\n\tsavePath := p.getSavePath()\n\n\tif fileurl.IsExist(savePath) {\n\t\tif err := fileurl.CreatePath(savePath, os.ModePerm); err != nil {\n\t\t\treturn errors.New(\"failed to create the save-fileurl directory\")\n\t\t}\n\t}\n\tif fileurl.IsPermission(savePath) {\n\t\treturn errors.New(\"no permission to upload the save fileurl directory\")\n\t}\n\tp.IsCheckSave = true\n\treturn nil\n}\n\nfunc (p *LocalFS) getSavePath() string {\n\tfullPath := filepath.Join(p.Config.SavePath, p.Config.CustomPath)\n\treturn fileurl.PathSuffixCheckAdd(fullPath, string(os.PathSeparator))\n}\n\n// SendFile upload file\n// SendFile 上传文件\nfunc (p *LocalFS) SendFile(fileKey string, file io.Reader, itype string, modTime time.Time) (string, error) {\n\tif !p.IsCheckSave {\n\t\tif err := p.CheckSave(); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\n\tdstFileKey := p.getSavePath() + fileKey\n\n\terr := os.MkdirAll(path.Dir(dstFileKey), os.ModePerm)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tout, err := os.Create(dstFileKey)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer out.Close()\n\n\t// file.Seek(0, 0)\n\t_, err = io.Copy(out, file)\n\tif err != nil {\n\t\treturn \"\", err\n\t} else {\n\t\tif !modTime.IsZero() {\n\t\t\t_ = os.Chtimes(dstFileKey, modTime, modTime)\n\t\t}\n\t\treturn dstFileKey, nil\n\t}\n}\n\nfunc (p *LocalFS) SendContent(fileKey string, content []byte, modTime time.Time) (string, error) {\n\n\tif !p.IsCheckSave {\n\t\tif err := p.CheckSave(); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\n\tdstFileKey := p.getSavePath() + fileKey\n\n\terr := os.MkdirAll(path.Dir(dstFileKey), os.ModePerm)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tout, err := os.Create(dstFileKey)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer out.Close()\n\n\t_, err = io.Copy(out, bytes.NewReader(content))\n\tif err != nil {\n\t\treturn \"\", err\n\t} else {\n\t\tif !modTime.IsZero() {\n\t\t\t_ = os.Chtimes(dstFileKey, modTime, modTime)\n\t\t}\n\t\treturn dstFileKey, nil\n\t}\n}\n"
  },
  {
    "path": "pkg/storage/local_fs/operation_test.go",
    "content": "package local_fs\n\nimport (\n\t\"bytes\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestLocalFS_SendFile(t *testing.T) {\n\t// Setup temporary directory\n\ttempDir := t.TempDir()\n\n\t// Create LocalFS client\n\tclient, err := NewClient(&Config{\n\t\tSavePath: tempDir,\n\t})\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to create client: %v\", err)\n\t}\n\n\t// Prepare data\n\tfilename := \"test_file.txt\"\n\tcontent := \"hello world\"\n\tmodTime := time.Date(2023, 10, 1, 12, 0, 0, 0, time.UTC)\n\treader := strings.NewReader(content)\n\n\t// Call SendFile\n\tsavedPath, err := client.SendFile(filename, reader, \"text/plain\", modTime)\n\tif err != nil {\n\t\tt.Fatalf(\"SendFile failed: %v\", err)\n\t}\n\n\t// Verify file existence\n\tif _, err := os.Stat(savedPath); os.IsNotExist(err) {\n\t\tt.Fatalf(\"File not found at %s\", savedPath)\n\t}\n\n\t// Verify content\n\tsavedContent, err := os.ReadFile(savedPath)\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to read saved file: %v\", err)\n\t}\n\tif string(savedContent) != content {\n\t\tt.Errorf(\"Content mismatch: expected %s, got %s\", content, string(savedContent))\n\t}\n\n\t// Verify modification time\n\tfileInfo, err := os.Stat(savedPath)\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to stat saved file: %v\", err)\n\t}\n\t// Allow small difference due to filesystem precision/delays, but here we set explicit time\n\t// Windows/Linux precision differs, but usually it should be close enough or exact\n\tif !fileInfo.ModTime().Equal(modTime) {\n\t\t// Some filesystems might have different precision, check difference\n\t\tdiff := fileInfo.ModTime().Sub(modTime)\n\t\tif diff < -time.Second || diff > time.Second {\n\t\t\tt.Errorf(\"ModTime mismatch: expected %v, got %v (diff %v)\", modTime, fileInfo.ModTime(), diff)\n\t\t}\n\t}\n}\n\nfunc TestLocalFS_SendContent(t *testing.T) {\n\t// Setup temporary directory\n\ttempDir := t.TempDir()\n\n\t// Create LocalFS client\n\tclient, err := NewClient(&Config{\n\t\tSavePath: tempDir,\n\t})\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to create client: %v\", err)\n\t}\n\n\t// Prepare data\n\t// Test with a subdirectory to ensure SendContent creates directories\n\tfilename := \"subdir/test_content.txt\"\n\tcontent := []byte(\"hello content\")\n\tmodTime := time.Date(2024, 1, 1, 10, 0, 0, 0, time.UTC)\n\n\t// Call SendContent\n\tsavedPath, err := client.SendContent(filename, content, modTime)\n\tif err != nil {\n\t\tt.Fatalf(\"SendContent failed: %v\", err)\n\t}\n\n\t// Verify file existence\n\tif _, err := os.Stat(savedPath); os.IsNotExist(err) {\n\t\tt.Fatalf(\"File not found at %s\", savedPath)\n\t}\n\n\t// Verify content\n\tsavedContent, err := os.ReadFile(savedPath)\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to read saved file: %v\", err)\n\t}\n\tif !bytes.Equal(savedContent, content) {\n\t\tt.Errorf(\"Content mismatch: expected %s, got %s\", content, string(savedContent))\n\t}\n\n\t// Verify modification time\n\tfileInfo, err := os.Stat(savedPath)\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to stat saved file: %v\", err)\n\t}\n\tif !fileInfo.ModTime().Equal(modTime) {\n\t\tdiff := fileInfo.ModTime().Sub(modTime)\n\t\tif diff < -time.Second || diff > time.Second {\n\t\t\tt.Errorf(\"ModTime mismatch: expected %v, got %v (diff %v)\", modTime, fileInfo.ModTime(), diff)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pkg/storage/minio/delete.go",
    "content": "package minio\n\nimport (\n\t\"context\"\n\n\t\"path\"\n\n\t\"github.com/aws/aws-sdk-go-v2/aws\"\n\t\"github.com/aws/aws-sdk-go-v2/service/s3\"\n)\n\nfunc (p *MinIO) Delete(fileKey string) error {\n\tfileKey = path.Join(p.Config.CustomPath, fileKey)\n\tctx := context.Background()\n\tbucket := p.GetBucket(\"\")\n\t_, err := p.S3Client.DeleteObject(ctx, &s3.DeleteObjectInput{\n\t\tBucket: aws.String(bucket),\n\t\tKey:    aws.String(fileKey),\n\t})\n\treturn err\n}\n"
  },
  {
    "path": "pkg/storage/minio/minio.go",
    "content": "package minio\n\nimport (\n\t\"context\"\n\n\t\"github.com/aws/aws-sdk-go-v2/aws\"\n\t\"github.com/aws/aws-sdk-go-v2/config\"\n\t\"github.com/aws/aws-sdk-go-v2/credentials\"\n\t\"github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager\"\n\t\"github.com/aws/aws-sdk-go-v2/service/s3\"\n\t\"github.com/pkg/errors\"\n)\n\ntype Config struct {\n\tBucketName      string `yaml:\"bucket-name\"`\n\tEndpoint        string `yaml:\"endpoint\"`\n\tRegion          string `yaml:\"region\"`\n\tAccessKeyID     string `yaml:\"access-key-id\"`\n\tAccessKeySecret string `yaml:\"access-key-secret\"`\n\tCustomPath      string `yaml:\"custom-path\"`\n}\n\ntype MinIO struct {\n\tS3Client        *s3.Client\n\tTransferManager *transfermanager.Client\n\tConfig          *Config\n}\n\nvar clients = make(map[string]*MinIO)\n\nfunc NewClient(conf *Config) (*MinIO, error) {\n\tvar endpoint = conf.Endpoint\n\tvar region = conf.Region\n\tvar accessKeyId = conf.AccessKeyID\n\tvar accessKeySecret = conf.AccessKeySecret\n\n\tif clients[accessKeyId] != nil {\n\t\treturn clients[accessKeyId], nil\n\t}\n\n\tcfg, err := config.LoadDefaultConfig(context.TODO(),\n\t\tconfig.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKeyId, accessKeySecret, \"\")),\n\t\tconfig.WithRegion(region),\n\t)\n\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"minio\")\n\t}\n\n\tclient := s3.NewFromConfig(cfg, func(o *s3.Options) {\n\t\to.UsePathStyle = true\n\t\to.BaseEndpoint = aws.String(endpoint)\n\t})\n\n\tclients[accessKeyId] = &MinIO{\n\t\tS3Client:        client,\n\t\tTransferManager: transfermanager.New(client),\n\t\tConfig:          conf,\n\t}\n\treturn clients[accessKeyId], nil\n}\n"
  },
  {
    "path": "pkg/storage/minio/minio_test.go",
    "content": "package minio\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestNewClient(t *testing.T) {\n\tconfig := &Config{\n\t\tEndpoint:        \"http://localhost:9000\",\n\t\tRegion:          \"us-east-1\",\n\t\tBucketName:      \"test-bucket\",\n\t\tAccessKeyID:     \"minio-test-key\",\n\t\tAccessKeySecret: \"minio-test-secret\",\n\t}\n\n\tclient, err := NewClient(config)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, client)\n\n\t// Check cache\n\tclient2, err := NewClient(config)\n\tassert.NoError(t, err)\n\tassert.Equal(t, client, client2)\n}\n"
  },
  {
    "path": "pkg/storage/minio/operation.go",
    "content": "package minio\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"path\"\n\t\"time\"\n\n\t\"github.com/aws/aws-sdk-go-v2/aws\"\n\t\"github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager\"\n\ttmtypes \"github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager/types\"\n\t\"github.com/aws/aws-sdk-go-v2/service/s3\"\n\t\"github.com/aws/aws-sdk-go-v2/service/s3/types\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/fileurl\"\n\t\"github.com/pkg/errors\"\n)\n\nfunc (p *MinIO) GetBucket(bucketName string) string {\n\n\t// Get bucket\n\tif len(bucketName) <= 0 {\n\t\tbucketName = p.Config.BucketName\n\t}\n\n\treturn bucketName\n}\n\n// SendFile upload file\n// SendFile 上传文件\nfunc (p *MinIO) SendFile(fileKey string, file io.Reader, itype string, modTime time.Time) (string, error) {\n\n\tctx := context.Background()\n\tbucket := p.GetBucket(\"\")\n\n\tfileKey = path.Join(p.Config.CustomPath, fileKey)\n\n\t//  k, _ := h.Open()\n\n\tinput := &transfermanager.UploadObjectInput{\n\t\tBucket:      aws.String(bucket),\n\t\tKey:         aws.String(fileKey),\n\t\tBody:        file,\n\t\tContentType: aws.String(itype),\n\t}\n\n\tif !modTime.IsZero() {\n\t\tinput.Metadata = map[string]string{\n\t\t\t\"modification-time\": modTime.Format(time.RFC3339),\n\t\t}\n\t}\n\n\t_, err := p.TransferManager.UploadObject(ctx, input)\n\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"minio\")\n\t}\n\n\treturn fileurl.PathSuffixCheckAdd(p.Config.BucketName, \"/\") + fileKey, nil\n}\n\nfunc (p *MinIO) SendContent(fileKey string, content []byte, modTime time.Time) (string, error) {\n\n\tctx := context.Background()\n\tbucket := p.GetBucket(\"\")\n\n\tfileKey = path.Join(p.Config.CustomPath, fileKey)\n\n\tinput := &transfermanager.UploadObjectInput{\n\t\tBucket:            aws.String(bucket),\n\t\tKey:               aws.String(fileKey),\n\t\tBody:              bytes.NewReader(content),\n\t\tChecksumAlgorithm: tmtypes.ChecksumAlgorithmSha256,\n\t}\n\n\tif !modTime.IsZero() {\n\t\tinput.Metadata = map[string]string{\n\t\t\t\"modification-time\": modTime.Format(time.RFC3339),\n\t\t}\n\t}\n\n\toutput, err := p.TransferManager.UploadObject(ctx, input)\n\tif err != nil {\n\t\tvar noBucket *types.NoSuchBucket\n\t\tif errors.As(err, &noBucket) {\n\t\t\tfmt.Printf(\"Bucket %s does not exist.\\n\", bucket)\n\t\t\terr = noBucket\n\t\t}\n\t} else {\n\t\terr := s3.NewObjectExistsWaiter(p.S3Client).Wait(ctx, &s3.HeadObjectInput{\n\t\t\tBucket: aws.String(bucket),\n\t\t\tKey:    aws.String(fileKey),\n\t\t}, time.Minute)\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"Failed attempt to wait for object %s to exist in %s.\\n\", fileKey, bucket)\n\t\t} else {\n\t\t\t_ = *output.Key\n\t\t}\n\t}\n\n\treturn fileurl.PathSuffixCheckAdd(p.Config.BucketName, \"/\") + fileKey, errors.Wrap(err, \"minio\")\n}\n"
  },
  {
    "path": "pkg/storage/storage.go",
    "content": "package storage\n\nimport (\n\t\"io\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/code\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/storage/aliyun_oss\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/storage/aws_s3\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/storage/cloudflare_r2\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/storage/local_fs\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/storage/minio\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/storage/webdav\"\n)\n\ntype Type = string\ntype CloudType = Type\n\nconst OSS CloudType = \"oss\"\nconst R2 CloudType = \"r2\"\nconst S3 CloudType = \"s3\"\nconst LOCAL Type = \"localfs\"\nconst MinIO CloudType = \"minio\"\nconst WebDAV CloudType = \"webdav\"\n\nvar StorageTypeMap = map[Type]bool{\n\tOSS:    true,\n\tR2:     true,\n\tS3:     true,\n\tLOCAL:  true,\n\tMinIO:  true,\n\tWebDAV: true,\n}\n\nvar CloudStorageTypeMap = map[Type]bool{\n\tOSS:   true,\n\tR2:    true,\n\tS3:    true,\n\tMinIO: true,\n}\n\n// Config Unified storage configuration\ntype Config struct {\n\tType Type `yaml:\"type\"`\n\n\t// Common settings\n\tCustomPath string `yaml:\"custom-path\"`\n\n\t// Cloud Storage (S3/OSS/MinIO/R2)\n\tEndpoint        string `yaml:\"endpoint\"`\n\tRegion          string `yaml:\"region\"`\n\tBucketName      string `yaml:\"bucket-name\"`\n\tAccessKeyID     string `yaml:\"access-key-id\"`\n\tAccessKeySecret string `yaml:\"access-key-secret\"`\n\tAccountID       string `yaml:\"account-id\"` // Cloudflare R2 specific\n\n\t// WebDAV\n\tUser     string `yaml:\"user\"`\n\tPassword string `yaml:\"password\"`\n\n\t// Local FS\n\tSavePath string `yaml:\"save-path\"`\n}\n\ntype Storager interface {\n\tSendFile(pathKey string, file io.Reader, cType string, modTime time.Time) (string, error)\n\tSendContent(pathKey string, content []byte, modTime time.Time) (string, error)\n\tDelete(pathKey string) error\n}\n\nfunc NewClient(config *Config) (Storager, error) {\n\tif config == nil {\n\t\treturn nil, code.ErrorInvalidStorageType\n\t}\n\n\tcType := config.Type\n\tconfig.CustomPath = strings.Trim(config.CustomPath, \"/\")\n\n\tif cType == LOCAL {\n\t\tcfg := &local_fs.Config{\n\t\t\tCustomPath: config.CustomPath,\n\t\t\tSavePath:   config.SavePath,\n\t\t}\n\t\treturn local_fs.NewClient(cfg)\n\t} else if cType == OSS {\n\t\tcfg := &aliyun_oss.Config{\n\t\t\tEndpoint:        config.Endpoint,\n\t\t\tBucketName:      config.BucketName,\n\t\t\tAccessKeyID:     config.AccessKeyID,\n\t\t\tAccessKeySecret: config.AccessKeySecret,\n\t\t\tCustomPath:      config.CustomPath,\n\t\t}\n\t\treturn aliyun_oss.NewClient(cfg)\n\t} else if cType == R2 {\n\t\tcfg := &cloudflare_r2.Config{\n\t\t\tAccountID:       config.AccountID,\n\t\t\tBucketName:      config.BucketName,\n\t\t\tAccessKeyID:     config.AccessKeyID,\n\t\t\tAccessKeySecret: config.AccessKeySecret,\n\t\t\tCustomPath:      config.CustomPath,\n\t\t}\n\t\treturn cloudflare_r2.NewClient(cfg)\n\t} else if cType == S3 {\n\t\tcfg := &aws_s3.Config{\n\t\t\tRegion:          config.Region,\n\t\t\tBucketName:      config.BucketName,\n\t\t\tAccessKeyID:     config.AccessKeyID,\n\t\t\tAccessKeySecret: config.AccessKeySecret,\n\t\t\tCustomPath:      config.CustomPath,\n\t\t}\n\t\treturn aws_s3.NewClient(cfg)\n\t} else if cType == MinIO {\n\t\tcfg := &minio.Config{\n\t\t\tEndpoint:        config.Endpoint,\n\t\t\tRegion:          config.Region,\n\t\t\tBucketName:      config.BucketName,\n\t\t\tAccessKeyID:     config.AccessKeyID,\n\t\t\tAccessKeySecret: config.AccessKeySecret,\n\t\t\tCustomPath:      config.CustomPath,\n\t\t}\n\t\treturn minio.NewClient(cfg)\n\t} else if cType == WebDAV {\n\t\tcfg := &webdav.Config{\n\t\t\tEndpoint:   config.Endpoint,\n\t\t\tUser:       config.User,\n\t\t\tPassword:   config.Password,\n\t\t\tCustomPath: config.CustomPath,\n\t\t}\n\t\treturn webdav.NewClient(cfg)\n\t}\n\treturn nil, code.ErrorInvalidStorageType\n}\n"
  },
  {
    "path": "pkg/storage/storage_test.go",
    "content": "package storage_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/storage\"\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/storage/local_fs\"\n)\n\nfunc TestNewClient_Local(t *testing.T) {\n\tcfg := &storage.Config{\n\t\tType:     storage.LOCAL,\n\t\tSavePath: \"./uploads\",\n\t}\n\n\tclient, err := storage.NewClient(cfg)\n\tif err != nil {\n\t\tt.Fatalf(\"Failed to create local client: %v\", err)\n\t}\n\n\tif client == nil {\n\t\tt.Fatal(\"Client is nil\")\n\t}\n\n\t// Verify type\n\tif _, ok := client.(*local_fs.LocalFS); !ok {\n\t\tt.Fatal(\"Client is not *local_fs.LocalFS\")\n\t}\n}\n\nfunc TestNewClient_Invalid(t *testing.T) {\n\tcfg := &storage.Config{\n\t\tType: \"invalid\",\n\t}\n\n\t_, err := storage.NewClient(cfg)\n\tif err == nil {\n\t\tt.Fatal(\"Expected error for invalid storage type\")\n\t}\n}\n"
  },
  {
    "path": "pkg/storage/webdav/delete.go",
    "content": "package webdav\n\nimport (\n\t\"path\"\n)\n\nfunc (w *WebDAV) Delete(fileKey string) error {\n\tfileKey = path.Join(\"/\", w.Config.CustomPath, fileKey)\n\treturn w.Client.Remove(fileKey)\n}\n"
  },
  {
    "path": "pkg/storage/webdav/operation.go",
    "content": "// operation.go\n\npackage webdav\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/errors\"\n)\n\nfunc (w *WebDAV) setModifiedTime(pathKey string, modTime time.Time) error {\n\tu, err := url.Parse(w.Config.Endpoint)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu.Path = path.Join(u.Path, strings.TrimPrefix(pathKey, \"/\"))\n\turlStr := u.String()\n\n\txmlBody := fmt.Sprintf(`<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<d:propertyupdate xmlns:d=\"DAV:\" xmlns:u=\"http://haierkeys.github.io/ns/\">\n<d:set><d:prop><u:modification-time>%s</u:modification-time></d:prop></d:set>\n</d:propertyupdate>`, modTime.Format(time.RFC3339))\n\n\treq, err := http.NewRequest(\"PROPPATCH\", urlStr, strings.NewReader(xmlBody))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treq.SetBasicAuth(w.Config.User, w.Config.Password)\n\treq.Header.Set(\"Content-Type\", \"application/xml\")\n\n\tclient := &http.Client{Timeout: 30 * time.Second}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode >= 200 && resp.StatusCode < 300 {\n\t\treturn nil\n\t}\n\t// For WebDAV, 207 Multi-Status is effectively a success if the property set was successful,\n\t// checking strictly < 300 catches 200, 201, 204, 207.\n\t// To be more robust we could parse XML response but for this helper it's usually enough.\n\n\treturn nil\n}\n\n// SendFile upload local file to WebDAV server\n// SendFile 将本地文件上传到 WebDAV 服务器。\nfunc (w *WebDAV) SendFile(fileKey string, file io.Reader, itype string, modTime time.Time) (string, error) {\n\n\tfileKey = path.Join(\"/\", w.Config.CustomPath, fileKey)\n\n\tcontent, err := io.ReadAll(file)\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"webdav\")\n\t}\n\n\terr = w.Client.Write(fileKey, content, os.ModePerm)\n\tif err != nil {\n\t\tdir := path.Dir(fileKey)\n\t\tif dir != \"/\" && dir != \".\" && dir != \"\" {\n\t\t\t_ = w.Client.MkdirAll(dir, 0755)\n\t\t\terr = w.Client.Write(fileKey, content, os.ModePerm)\n\t\t}\n\t}\n\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"webdav\")\n\t}\n\n\tif !modTime.IsZero() {\n\t\t_ = w.setModifiedTime(fileKey, modTime)\n\t}\n\n\treturn fileKey, nil\n}\n\n// SendContent upload binary content to WebDAV server\n// SendContent 将二进制内容上传到 WebDAV 服务器。\nfunc (w *WebDAV) SendContent(fileKey string, content []byte, modTime time.Time) (string, error) {\n\n\tfileKey = path.Join(\"/\", w.Config.CustomPath, fileKey)\n\n\terr := w.Client.Write(fileKey, content, os.ModePerm)\n\tif err != nil {\n\t\tdir := path.Dir(fileKey)\n\t\tif dir != \"/\" && dir != \".\" && dir != \"\" {\n\t\t\t_ = w.Client.MkdirAll(dir, 0755)\n\t\t\terr = w.Client.Write(fileKey, content, os.ModePerm)\n\t\t}\n\t}\n\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"webdav\")\n\t}\n\n\tif !modTime.IsZero() {\n\t\t_ = w.setModifiedTime(fileKey, modTime)\n\t}\n\n\treturn fileKey, nil\n}\n"
  },
  {
    "path": "pkg/storage/webdav/webdav.go",
    "content": "package webdav\n\nimport (\n\t\"github.com/studio-b12/gowebdav\"\n)\n\n// Config 结构体用于存储 WebDAV 连接信息。\ntype Config struct {\n\tEndpoint   string `yaml:\"endpoint\"`\n\tUser       string `yaml:\"user\"`\n\tPassword   string `yaml:\"password\"`\n\tCustomPath string `yaml:\"custom-path\"`\n}\n\n// WebDAV 结构体表示 WebDAV 客户端。\ntype WebDAV struct {\n\tClient *gowebdav.Client\n\tConfig *Config\n}\n\nvar clients = make(map[string]*WebDAV)\n\n// NewClient 创建一个新的 WebDAV 客户端实例。\nfunc NewClient(conf *Config) (*WebDAV, error) {\n\tvar endpoint = conf.Endpoint\n\tvar user = conf.User\n\tvar customPath = conf.CustomPath\n\n\tif clients[endpoint+user+customPath] != nil {\n\t\treturn clients[endpoint+user+customPath], nil\n\t}\n\n\tc := gowebdav.NewClient(endpoint, user, conf.Password)\n\tc.Connect()\n\n\tclients[endpoint+user+customPath] = &WebDAV{\n\t\tClient: c,\n\t\tConfig: conf,\n\t}\n\treturn clients[endpoint+user+customPath], nil\n}\n"
  },
  {
    "path": "pkg/storage/webdav/webdav_test.go",
    "content": "package webdav\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestNewClient(t *testing.T) {\n\tconfig := &Config{\n\t\tEndpoint:   \"http://localhost:8080/webdav\",\n\t\tUser:       \"user\",\n\t\tPassword:   \"password\",\n\t\tCustomPath: \"/my-notes\",\n\t}\n\n\tclient, err := NewClient(config)\n\tassert.NoError(t, err)\n\tassert.NotNil(t, client)\n\tassert.NotNil(t, client.Client)\n\n\t// Since NewClient calls c.Connect() right away, it might not return an error immediately based on gowebdav behavior,\n\t// but it creates the instance properly.\n\n\t// Check cache using the composed key\n\tclient2, err := NewClient(config)\n\tassert.NoError(t, err)\n\tassert.Equal(t, client, client2)\n}\n"
  },
  {
    "path": "pkg/timex/time.go",
    "content": "package timex\n\nimport (\n\t\"database/sql/driver\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n)\n\nconst TimeFormat = \"2006-01-02 15:04:05\"\n\ntype Time time.Time\n\nfunc (t *Time) UnmarshalJSON(data []byte) (err error) {\n\tif len(data) == 2 {\n\t\t*t = Time(time.Time{})\n\t\treturn\n\t}\n\n\tnow, err := time.Parse(`\"`+TimeFormat+`\"`, string(data))\n\t*t = Time(now)\n\treturn\n}\n\nfunc (t Time) MarshalJSON() ([]byte, error) {\n\ttTime := time.Time(t)\n\t// If time value is empty or 0, return null. Returning empty string will cause abnormal time exception.\n\t// MarshalJSON 如果时间值是空或者0值 返回为null 如果写空字符串会报出异常时间\n\t// Below is to fix the 0001-01-01 issue\n\t// 下面是修复0001-01-01问题的\n\tif t.IsZero() {\n\t\treturn []byte(\"null\"), nil\n\t}\n\treturn []byte(fmt.Sprintf(\"\\\"%s\\\"\", tTime.Format(TimeFormat))), nil\n\n}\n\nfunc (t *Time) IsZero() bool {\n\treturn time.Time(*t).IsZero()\n}\n\nfunc (t Time) Value() (driver.Value, error) {\n\tif t.String() == \"0000-00-00 00:00:00\" {\n\t\treturn nil, nil\n\t}\n\tif t.String() == \"0001-01-01 00:00:00\" {\n\t\treturn nil, nil\n\t}\n\treturn time.Time(t).Format(TimeFormat), nil\n}\n\nfunc (t *Time) Scan(v any) error {\n\ttimeValue, ok := v.(time.Time)\n\tif !ok {\n\t\treturn errors.New(fmt.Sprint(\"Failed to unmarshal time value:\", v))\n\t}\n\t*t = Time(timeValue)\n\treturn nil\n\n}\n\nfunc (t Time) String() string {\n\treturn time.Time(t).Format(TimeFormat)\n}\n\nfunc (t Time) StringSource() string {\n\treturn time.Time(t).String()\n}\n\nfunc Now() Time {\n\treturn Time(time.Now())\n}\n\n// Unix timestamp (seconds)\n// Unix 时间戳（秒）\nfunc (t Time) Unix() int64 {\n\treturn time.Time(t).Unix()\n}\n\n// UnixMilli timestamp (milliseconds)\n// UnixMilli 时间戳（毫秒）\nfunc (t Time) UnixMilli() int64 {\n\treturn time.Time(t).UnixMilli()\n}\n\n// UnixMicro timestamp (microseconds)\n// UnixMicro 时间戳（微秒）\nfunc (t Time) UnixMicro() int64 {\n\treturn time.Time(t).UnixMicro()\n}\n\n// UnixNano timestamp (nanoseconds)\n// UnixNano 时间戳（纳秒）\nfunc (t Time) UnixNano() int64 {\n\treturn time.Time(t).UnixNano()\n}\n\n// After reports whether the time instant t is after u.\nfunc (t Time) After(u Time) bool {\n\tts := time.Time(t)\n\treturn ts.After(time.Time(u))\n}\n\n// Before reports whether the time instant t is before u.\nfunc (t Time) Before(u Time) bool {\n\tts := time.Time(t)\n\treturn ts.Before(time.Time(u))\n}\n\n// Equal reports whether t and u represent the same time instant.\n// Equal 报告 t 和 u 是否代表相同的时间点。\n// Two times can be equal even if they are in different locations.\n// For example, 6:00 +0200 and 4:00 UTC are Equal.\n// See the documentation on the Time type for the pitfalls of using == with\n// Time values; most code should use Equal instead.\nfunc (t Time) Equal(u Time) bool {\n\tts := time.Time(t)\n\treturn ts.Equal(time.Time(u))\n}\n\n// Add returns the time t+d.\nfunc (t Time) Add(d time.Duration) Time {\n\tts := time.Time(t)\n\treturn Time(ts.Add(d))\n}\n\nfunc Since(t Time) time.Duration {\n\tts := time.Time(t)\n\treturn time.Since(ts)\n}\n"
  },
  {
    "path": "pkg/timex/time_test.go",
    "content": "package timex\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestTime_UnixMethods(t *testing.T) {\n\t// Create a fixed time\n\t// 创建一个固定时间\n\tnow := time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC)\n\ttt := Time(now)\n\n\t// Test Unix()\n\tif tt.Unix() != now.Unix() {\n\t\tt.Errorf(\"Unix() = %v, want %v\", tt.Unix(), now.Unix())\n\t}\n\n\t// Test UnixMilli()\n\tif tt.UnixMilli() != now.UnixMilli() {\n\t\tt.Errorf(\"UnixMilli() = %v, want %v\", tt.UnixMilli(), now.UnixMilli())\n\t}\n\n\t// Test UnixMicro()\n\tif tt.UnixMicro() != now.UnixMicro() {\n\t\tt.Errorf(\"UnixMicro() = %v, want %v\", tt.UnixMicro(), now.UnixMicro())\n\t}\n\n\t// Test UnixNano()\n\tif tt.UnixNano() != now.UnixNano() {\n\t\tt.Errorf(\"UnixNano() = %v, want %v\", tt.UnixNano(), now.UnixNano())\n\t}\n\n\t// Verify it's not returning time.Now() by waiting a bit\n\t// 通过等待一会确认它不是返回 time.Now()\n\ttime.Sleep(10 * time.Millisecond)\n\tif tt.Unix() != now.Unix() {\n\t\tt.Errorf(\"Unix() changed after sleep, it should be static. got %v, want %v\", tt.Unix(), now.Unix())\n\t}\n}\n"
  },
  {
    "path": "pkg/tracer/tracer.go",
    "content": "package tracer\n\nimport (\n\t\"io\"\n\t\"time\"\n\n\t\"github.com/opentracing/opentracing-go\"\n\t\"github.com/uber/jaeger-client-go/config\"\n)\n\nfunc NewJaegerTracer(serviceName, agentHostPort string) (opentracing.Tracer, io.Closer, error) {\n\tcfg := &config.Configuration{\n\t\tServiceName: serviceName,\n\t\tSampler: &config.SamplerConfig{\n\t\t\tType:  \"const\",\n\t\t\tParam: 1,\n\t\t},\n\t\tReporter: &config.ReporterConfig{\n\t\t\tLogSpans:            true,\n\t\t\tBufferFlushInterval: 1 * time.Second,\n\t\t\tLocalAgentHostPort:  agentHostPort,\n\t\t},\n\t}\n\ttracer, closer, err := cfg.NewTracer()\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\topentracing.SetGlobalTracer(tracer)\n\treturn tracer, closer, nil\n}\n"
  },
  {
    "path": "pkg/tracer/tracer_test.go",
    "content": "package tracer\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestNewJaegerTracer(t *testing.T) {\n\t// Depending on Jaeger client config, using invalid host/port may immediately fail or just silently ignore udp\n\ttracer, closer, err := NewJaegerTracer(\"test-service\", \"127.0.0.1:0\")\n\n\tassert.NoError(t, err)\n\tassert.NotNil(t, tracer)\n\tassert.NotNil(t, closer)\n\n\t// Clean up\n\tif closer != nil {\n\t\tcloser.Close()\n\t}\n}\n"
  },
  {
    "path": "pkg/util/archive.go",
    "content": "package util\n\nimport (\n\t\"archive/zip\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n)\n\n// Zip compresses files or directories into a zip file\n// source: path to file or directory\n// target: path to output zip file\nfunc Zip(source, target string) error {\n\tzipFile, err := os.Create(target)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer zipFile.Close()\n\n\tarchive := zip.NewWriter(zipFile)\n\tdefer archive.Close()\n\n\tif _, err := os.Stat(source); err != nil {\n\t\treturn err\n\t}\n\n\tfilepath.Walk(source, func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Get relative path from source\n\t\t// 获取相对于 source 的路径\n\t\trelPath, err := filepath.Rel(source, path)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Skip root directory itself\n\t\t// 跳过根目录本身\n\t\tif relPath == \".\" {\n\t\t\treturn nil\n\t\t}\n\n\t\theader, err := zip.FileInfoHeader(info)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Use relative path as filename in zip\n\t\t// 使用相对路径作为压缩包内的文件名\n\t\theader.Name = relPath\n\t\tif info.IsDir() {\n\t\t\theader.Name += \"/\"\n\t\t} else {\n\t\t\theader.Method = zip.Deflate\n\t\t}\n\n\t\twriter, err := archive.CreateHeader(header)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif info.IsDir() {\n\t\t\treturn nil\n\t\t}\n\n\t\tfile, err := os.Open(path)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer file.Close()\n\n\t\t_, err = io.Copy(writer, file)\n\t\treturn err\n\t})\n\n\treturn err\n}\n\n// ZipBytes creates a zip archive from a map of filenames and their contents (bytes)\nfunc ZipBytes(files map[string][]byte, target string) error {\n\tzipFile, err := os.Create(target)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer zipFile.Close()\n\n\tarchive := zip.NewWriter(zipFile)\n\tdefer archive.Close()\n\n\tfor name, content := range files {\n\t\twriter, err := archive.Create(name)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = writer.Write(content)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/util/array.go",
    "content": "package util\n\n// GetIndexSlice gets the index of a slice element\n// GetIndexSlice 获取切片元素的索引\n// arr: slice to search\n// arr: 待查找的切片\n// val: value to search for\n// val: 要查找的值\n// return: index of the element, or -1 if not found\n// 返回值: 元素的索引，如果不存在返回-1\nfunc GetIndexSlice(arr []string, val string) int {\n\tfor i, v := range arr {\n\t\tif v == val {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\n// InSlice determines whether an element is in a slice (generic version)\n// InSlice 判断元素是否在切片中（泛型版本）\n// slice: the slice // 切片\n// item: the element to find // 要查找的元素\n// return: bool - true if exists, false otherwise // 返回值: bool - 存在返回true，否则返回false\nfunc InSlice[T comparable](slice []T, item T) bool {\n\tfor _, v := range slice {\n\t\tif v == item {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Inarray determines whether an element is in a slice\n// Inarray 判断元素是否在切片中\n// arr: slice to search // 待查找的切片\n// val: value to search for // 要查找的值\n// return: true if element is in the slice, false otherwise // 返回值: 如果元素在切片中返回true，否则返回false\nfunc Inarray(arr []string, val string) bool {\n\treturn GetIndexSlice(arr, val) >= 0\n}\n\n// ArrayUnique removes duplicate elements from a slice\n// ArrayUnique 移除切片中的重复元素\n// arr: original slice // 原始切片\n// return: new slice without duplicates // 返回值: 去重后的新切片\nfunc ArrayUnique(arr []string) []string {\n\tresult := make([]string, 0)\n\tm := make(map[string]bool)\n\tfor _, v := range arr {\n\t\tif !m[v] {\n\t\t\tm[v] = true\n\t\t\tresult = append(result, v)\n\t\t}\n\t}\n\treturn result\n}\n\n// RemoveDuplicate removes duplicate elements from a string slice (another implementation)\n// RemoveDuplicate 移除字符串切片中的重复元素（另一种实现）\n// strSlice: original string slice // 原始字符串切片\n// return: string slice without duplicates // 返回值: 去重后的字符串切片\nfunc RemoveDuplicate(strSlice []string) []string {\n\tallKeys := make(map[string]bool)\n\tlist := []string{}\n\tfor _, item := range strSlice {\n\t\tif _, value := allKeys[item]; !value {\n\t\t\tallKeys[item] = true\n\t\t\tlist = append(list, item)\n\t\t}\n\t}\n\treturn list\n}\n\n// IntersectionInt calculates intersection of two integer slices\n// IntersectionInt 计算两个整数切片的交集\n// a: first integer slice // 第一个整数切片\n// b: second integer slice // 第二个整数切片\n// return: intersection of two slices // 返回值: 两个切片的交集\nfunc IntersectionInt(a, b []int) []int {\n\thash := make(map[int]struct{})\n\tfor _, v := range a {\n\t\thash[v] = struct{}{}\n\t}\n\tresult := make([]int, 0)\n\tfor _, v := range b {\n\t\tif _, ok := hash[v]; ok {\n\t\t\tresult = append(result, v)\n\t\t}\n\t}\n\treturn result\n}\n"
  },
  {
    "path": "pkg/util/converter.go",
    "content": "package util\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// StrToMap converts string to map\n// StrToMap 将字符串转换为map\n// str: string in the format of \"key=value,key=value\" // 格式为\"key=value,key=value\"的字符串\n// return: converted map // 返回值: 转换后的map\nfunc StrToMap(str string) map[string]string {\n\tresult := make(map[string]string)\n\tif str == \"\" {\n\t\treturn result\n\t}\n\n\tstrArr := strings.Split(str, \",\")\n\tfor _, item := range strArr {\n\t\tkv := strings.Split(item, \"=\")\n\t\tif len(kv) == 2 {\n\t\t\tresult[kv[0]] = kv[1]\n\t\t}\n\t}\n\n\treturn result\n}\n\n// StrToInt converts string to integer\n// StrToInt 将字符串转换为整数\n// str: string to be converted // 待转换的字符串\n// return: converted integer, or 0 if conversion fails // 返回值: 转换后的整数，如果转换失败返回0\nfunc StrToInt(str string) int {\n\tif str == \"\" {\n\t\treturn 0\n\t}\n\ti, err := strconv.Atoi(str)\n\tif err != nil {\n\t\treturn 0\n\t}\n\treturn i\n}\n\n// IntSliceToStringSlice converts integer slice to string slice\n// IntSliceToStringSlice 将整数切片转换为字符串切片\n// intSlice: integer slice // 整数切片\n// return: string slice // 返回值: 字符串切片\nfunc IntSliceToStringSlice(intSlice []int) []string {\n\tstringSlice := make([]string, len(intSlice))\n\tfor i, v := range intSlice {\n\t\tstringSlice[i] = strconv.Itoa(v)\n\t}\n\treturn stringSlice\n}\n\n// StringToInt64 converts string to int64\n// StringToInt64 将字符串转换为int64\n// s: string to be converted // 待转换的字符串\n// return: converted int64 value // 返回值: 转换后的int64值\nfunc StringToInt64(s string) int64 {\n\tresult, _ := strconv.ParseInt(s, 10, 64)\n\treturn result\n}\n\n// ParseSize parses size string like \"128MB\", \"512KB\", \"1024B\" to bytes\n// ParseSize 将大小字符串（如 \"128MB\", \"512KB\", \"1024B\"）解析为字节数\nfunc ParseSize(sizeStr string, defaultSize int64) int64 {\n\tif sizeStr == \"\" {\n\t\treturn defaultSize\n\t}\n\n\tsizeStr = strings.ToUpper(strings.TrimSpace(sizeStr))\n\tvar multiplier int64 = 1\n\n\tif strings.HasSuffix(sizeStr, \"MB\") {\n\t\tmultiplier = 1024 * 1024\n\t\tsizeStr = strings.TrimSuffix(sizeStr, \"MB\")\n\t} else if strings.HasSuffix(sizeStr, \"KB\") {\n\t\tmultiplier = 1024\n\t\tsizeStr = strings.TrimSuffix(sizeStr, \"KB\")\n\t} else if strings.HasSuffix(sizeStr, \"B\") {\n\t\tmultiplier = 1\n\t\tsizeStr = strings.TrimSuffix(sizeStr, \"B\")\n\t}\n\n\tsize, err := strconv.ParseInt(strings.TrimSpace(sizeStr), 10, 64)\n\tif err != nil || size <= 0 {\n\t\treturn defaultSize\n\t}\n\n\treturn size * multiplier\n}\n\n// IntSliceToStrSlice converts integer slice to string slice (another implementation)\n// IntSliceToStrSlice 将整数切片转换为字符串切片（另一种实现）\n// list: integer slice // 整数切片\n// return: string slice // 返回值: 字符串切片\nfunc IntSliceToStrSlice(list []int) []string {\n\tstrlist := make([]string, 0)\n\tfor _, i := range list {\n\t\tstrlist = append(strlist, fmt.Sprintf(\"%d\", i))\n\t}\n\treturn strlist\n}\n"
  },
  {
    "path": "pkg/util/crypto.go",
    "content": "package util\n\nimport (\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\nconst (\n\ttokenLimit = 10\n\ttokenStart = 8\n\ttokenEnd   = 18\n)\n\nfunc AuthCodeEncrypt(token string, action string, key string) (out string, err error) {\n\tvar (\n\t\tstrauth   string\n\t\ttokenByte []byte\n\t\tkeyByte   []byte\n\t)\n\tif len(token) == 0 {\n\t\treturn out, errors.New(\"token is not allowed to be empty\")\n\t}\n\tif len(action) == 0 {\n\t\taction = \"EN\"\n\t}\n\tif action == \"DE\" {\n\t\ttoken = strings.Replace(token, \"[a]\", \"+\", -1)\n\t\ttoken = strings.Replace(token, \"[b]\", \"&\", -1)\n\t\ttoken = strings.Replace(token, \"[c]\", \"/\", -1)\n\t}\n\n\ttokenLen := len(token)\n\tif tokenLen <= tokenLimit {\n\t\treturn out, errors.New(\"The token length does not meet the requirements\")\n\t}\n\tif action == \"EN\" {\n\t\tstrauth = EncodeMD5(token)[tokenStart:tokenEnd]\n\t} else {\n\t\tstrauth = token[tokenLen-tokenLimit : tokenLen]\n\t\ttokenByte, _ = base64.StdEncoding.DecodeString(token[0 : tokenLen-tokenLimit])\n\t\ttoken = string(tokenByte)\n\t}\n\n\tkey = EncodeMD5(strauth + key)\n\n\ttokenByte = []byte(token)\n\tkeyByte = []byte(key)\n\ttmpCode := XorEncodeStr(tokenByte, keyByte)\n\tcode := string(tmpCode)\n\tif action == \"DE\" {\n\t\tif EncodeMD5(code)[tokenStart:tokenEnd] == strauth {\n\t\t\tout = code\n\t\t}\n\t} else {\n\t\tout = base64.StdEncoding.EncodeToString([]byte(code + strauth))\n\t\tout = strings.Replace(out, \"[a]\", \"+\", -1)\n\t\tout = strings.Replace(out, \"[b]\", \"&\", -1)\n\t\tout = strings.Replace(out, \"[c]\", \"/\", -1)\n\t}\n\treturn out, nil\n}\n\n/**\n * str: plaintext or ciphertext // 明文或密文\n * operation: encryption ENCODE or decryption DECODE // 加密ENCODE或解密DECODE\n * key: secret key // 密钥\n * expiry: secret key validity period // 密钥有效期\n */\nfunc AuthDzCodeEncrypt(str, operation, key string, expiry int64) (string, error) {\n\t// Dynamic secret key length, the same plaintext will generate different ciphertext depending on the dynamic key\n\t// 动态密匙长度，相同的明文会生成不同密文就是依靠动态密匙\n\t// Adding a random key can make the ciphertext have no pattern, even if the original text and key are exactly the same, the encryption result will be different every time, increasing the difficulty of cracking.\n\t// 加入随机密钥，可以令密文无任何规律，即便是原文和密钥完全相同，加密结果也会每次不同，增大破解难度。\n\t// The larger the value, the greater the change pattern of the ciphertext, ciphertext change = 16 to the power of ckeyLength\n\t// 取值越大，密文变动规律越大，密文变化 = 16 的 ckeyLength 次方\n\t// When this value is 0, no random key is generated\n\t// 当此值为 0 时，则不产生随机密钥\n\tckeyLength := 4\n\n\t// Secret key\n\t// 密匙\n\tif key == \"\" {\n\t\tkey = \"STARFISSION_AUTH_KEY\"\n\t}\n\n\tkey = EncodeMD5(key)\n\n\t// Key a will participate in encryption and decryption\n\t// 密匙a会参与加解密\n\tkeya := EncodeMD5(key[:16])\n\t// Key b will be used for data integrity verification\n\t// 密匙b会用来做数据完整性验证\n\tkeyb := EncodeMD5(key[16:])\n\t// Key c is used to change the generated ciphertext\n\t// 密匙c用于变化生成的密文\n\tkeyc := \"\"\n\tif ckeyLength != 0 {\n\t\tif operation == \"DECODE\" {\n\t\t\tkeyc = str[:ckeyLength]\n\t\t} else {\n\t\t\tsTime := EncodeMD5(time.Now().String())\n\t\t\tsLen := 32 - ckeyLength\n\t\t\tkeyc = sTime[sLen:]\n\t\t}\n\t}\n\n\t// Key involved in the operation\n\t// 参与运算的密匙\n\tcryptKey := fmt.Sprintf(\"%s%s\", keya, EncodeMD5(keya+keyc))\n\tkeyLength := len(cryptKey)\n\n\t// Plaintext, the first 10 bits are used to save the timestamp, the validity of the data is verified during decryption, and 10 to 26 bits are used to save $keyb (key b), and the data integrity will be verified through this key during decryption\n\t// 明文，前10位用来保存时间戳，解密时验证数据有效性，10到26位用来保存$keyb(密匙b)，解密时会通过这个密匙验证数据完整性\n\t// If it is decoding, it will start from the $ckeyLength bit, because the first $ckeyLength bits of the ciphertext save the dynamic key to ensure correct decryption\n\t// 如果是解码的话，会从第$ckeyLength位开始，因为密文前$ckeyLength位保存 动态密匙，以保证解密正确\n\tif operation == \"DECODE\" {\n\t\tstr = strings.Replace(str, \"[a]\", \"+\", -1)\n\t\tstr = strings.Replace(str, \"[b]\", \"&\", -1)\n\t\tstr = strings.Replace(str, \"[c]\", \"/\", -1)\n\n\t\tstrByte, err := base64.StdEncoding.DecodeString(str[ckeyLength:])\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tstr = string(strByte)\n\t} else {\n\n\t\tif expiry != 0 {\n\t\t\texpiry = expiry + time.Now().Unix()\n\t\t}\n\t\ttmpMd5 := EncodeMD5(str + keyb)\n\t\tstr = fmt.Sprintf(\"%010d%s%s\", expiry, tmpMd5[:16], str)\n\t}\n\n\tstringLength := len(str)\n\tresdata := make([]byte, 0, stringLength)\n\tvar rndkey, box [256]int\n\t// Generate secret key book\n\t// 产生密匙簿\n\tj := 0\n\ta := 0\n\ti := 0\n\ttmp := 0\n\tfor i = 0; i < 256; i++ {\n\t\trndkey[i] = int(cryptKey[i%keyLength])\n\t\tbox[i] = i\n\t}\n\t// Use a fixed algorithm to mess up the key book to increase randomness.\n\t// 用固定的算法，打乱密匙簿，增加随机性\n\tfor i = 0; i < 256; i++ {\n\t\tj = (j + box[i] + rndkey[i]) % 256\n\t\ttmp = box[i]\n\t\tbox[i] = box[j]\n\t\tbox[j] = tmp\n\t}\n\t// Core encryption and decryption part\n\t// 核心加解密部分\n\ta = 0\n\tj = 0\n\ttmp = 0\n\tfor i = 0; i < stringLength; i++ {\n\t\ta = (a + 1) % 256\n\t\tj = (j + box[a]) % 256\n\t\ttmp = box[a]\n\t\tbox[a] = box[j]\n\t\tbox[j] = tmp\n\t\t// Get the secret key from the key book for XOR, and then convert it to a character\n\t\t// 从密匙簿得出密匙进行异或，再转成字符\n\t\tresdata = append(resdata, byte(int(str[i])^box[(box[a]+box[j])%256]))\n\t}\n\tresult := string(resdata)\n\n\tif operation == \"DECODE\" {\n\t\t// Verify data validity and integrity\n\t\t// 验证数据有效性、完整性\n\t\tfrontTen, _ := strconv.ParseInt(result[:10], 10, 0)\n\t\tif (frontTen == 0 || frontTen-time.Now().Unix() > 0) && result[10:26] == EncodeMD5(result[26:] + keyb)[:16] {\n\t\t\treturn result[26:], nil\n\t\t} else {\n\t\t\treturn \"\", errors.New(\"AuthCode Encrypt error\")\n\t\t}\n\t} else {\n\t\t// Save the dynamic key in the ciphertext, which is why the same plaintext can be decrypted after producing different ciphertexts\n\t\t// 把动态密匙保存在密文里，这也是为什么同样的明文，生产不同密文后能解密的原因\n\t\t// Because the encrypted ciphertext may be some special characters, which may be lost during the copying process, use base64 encoding\n\t\t// 因为加密后的密文可能是一些特殊字符，复制过程可能会丢失，所以用base64编码\n\t\tresult = keyc + base64.StdEncoding.EncodeToString([]byte(result))\n\n\t\tresult = strings.Replace(result, \"+\", \"[a]\", -1)\n\t\tresult = strings.Replace(result, \"&\", \"[b]\", -1)\n\t\tresult = strings.Replace(result, \"/\", \"[c]\", -1)\n\n\t\treturn result, nil\n\t}\n}\n\n// base64Encode base64 encodes a string\n// base64Encode 对字符串进行Base64编码\n// s: the string to be encoded // 待编码的字符串\n// return: encoded string // 返回值: Base64编码后的字符串\nfunc base64Encode(s string) string {\n\treturn base64.StdEncoding.EncodeToString([]byte(s))\n}\n\n// base64Decode decodes a base64 encoded string\n// base64Decode 对Base64编码的字符串进行解码\n// s: the base64 string to be decoded // 待解码的Base64字符串\n// return: original string // 返回值: 解码后的原始字符串\nfunc base64Decode(s string) string {\n\tsByte, err := base64.StdEncoding.DecodeString(s)\n\tif err == nil {\n\t\treturn string(sByte)\n\t} else {\n\t\treturn \"\"\n\t}\n}\n\n// XorEncodeStr encrypts a byte slice using XOR operation\n// XorEncodeStr 使用异或操作对字节切片进行加密\n// msg: byte slice to be encrypted // 要加密的字节切片\n// key: key byte slice // 加密密钥的字节切片\n// return: encrypted byte slice // 返回值: 加密后的字节切片\nfunc XorEncodeStr(msg []byte, key []byte) (out []byte) {\n\tml := len(msg)\n\tkl := len(key)\n\tfor i := 0; i < ml; i++ {\n\t\tout = append(out, (msg[i])^(key[i%kl]))\n\t}\n\treturn out\n}\n\n// XorEncodeStrRune encrypts a rune slice using XOR operation\n// XorEncodeStrRune 使用异或操作对rune切片进行加密\n// msg: rune slice to be encrypted // 要加密的rune切片\n// key: key rune slice // 加密密钥的rune切片\n// return: encrypted rune slice // 返回值: 加密后的rune切片\nfunc XorEncodeStrRune(msg []rune, key []rune) (out []rune) {\n\tml := len(msg)\n\tkl := len(key)\n\tfor i := 0; i < ml; i++ {\n\t\tout = append(out, (msg[i])^(key[i%kl]))\n\t}\n\treturn out\n}\n"
  },
  {
    "path": "pkg/util/frontmatter.go",
    "content": "// Package util provides common utility functions\n// Package util 提供通用工具函数\npackage util\n\nimport (\n\t\"strings\"\n\n\t\"gopkg.in/yaml.v3\"\n)\n\nconst frontmatterDelimiter = \"---\"\n\n// ParseFrontmatter extracts YAML frontmatter from content\n// Returns the parsed YAML as a map, the body (content after frontmatter), and whether frontmatter exists\n// ParseFrontmatter 从内容中提取 YAML frontmatter\n// 返回解析后的 YAML map、正文（frontmatter 之后的内容）以及是否存在 frontmatter\nfunc ParseFrontmatter(content string) (yamlData map[string]interface{}, body string, hasFrontmatter bool) {\n\tif content == \"\" {\n\t\treturn nil, content, false\n\t}\n\n\t// Check if content starts with frontmatter delimiter followed by newline or carriage return\n\t// 检查内容是否以 frontmatter 分隔符开头，后跟换行或回车\n\tvar startOffset int\n\tif strings.HasPrefix(content, frontmatterDelimiter+\"\\n\") {\n\t\tstartOffset = len(frontmatterDelimiter) + 1\n\t} else if strings.HasPrefix(content, frontmatterDelimiter+\"\\r\\n\") {\n\t\tstartOffset = len(frontmatterDelimiter) + 2\n\t} else {\n\t\treturn nil, content, false\n\t}\n\n\t// Find the closing delimiter preceded by a newline\n\t// 查找前面带有换行符的结束分隔符\n\trest := content[startOffset:]\n\tendIndex := strings.Index(rest, \"\\n\"+frontmatterDelimiter)\n\tif endIndex == -1 {\n\t\treturn nil, content, false\n\t}\n\n\t// Identify the actual end of YAML content, stripping trailing \\r if present\n\t// 识别 YAML 内容的实际结束位置，如果存在则剥离末尾的 \\r\n\tyamlEndIndex := endIndex\n\tif endIndex > 0 && rest[endIndex-1] == '\\r' {\n\t\tyamlEndIndex = endIndex - 1\n\t}\n\n\tyamlContent := rest[:yamlEndIndex]\n\n\t// Determine the start of the body content\n\t// 确定正文内容的开始位置\n\tdelimiterEnd := endIndex + len(\"\\n\"+frontmatterDelimiter)\n\tpostDelimiter := rest[delimiterEnd:]\n\n\tvar bodyOffset int\n\tif strings.HasPrefix(postDelimiter, \"\\r\\n\") {\n\t\tbodyOffset = 2\n\t} else if strings.HasPrefix(postDelimiter, \"\\n\") {\n\t\tbodyOffset = 1\n\t} else {\n\t\tbodyOffset = 0\n\t}\n\n\tbody = rest[delimiterEnd+bodyOffset:]\n\n\t// Parse YAML\n\t// 解析 YAML\n\tyamlData = make(map[string]interface{})\n\tif err := yaml.Unmarshal([]byte(yamlContent), &yamlData); err != nil {\n\t\t// If YAML parsing fails, return as if no frontmatter\n\t\t// 如果 YAML 解析失败，则当作没有 frontmatter 返回\n\t\treturn nil, content, false\n\t}\n\n\treturn yamlData, body, true\n}\n\n// MergeFrontmatter merges updates into existing frontmatter and removes specified keys\n// MergeFrontmatter 将更新合并到现有的 frontmatter 中并移除指定的键\nfunc MergeFrontmatter(existing, updates map[string]interface{}, removeKeys []string) map[string]interface{} {\n\tresult := make(map[string]interface{})\n\n\t// Copy existing values\n\t// 复制现有值\n\tfor k, v := range existing {\n\t\tresult[k] = v\n\t}\n\n\t// Apply updates\n\t// 应用更新\n\tfor k, v := range updates {\n\t\tresult[k] = v\n\t}\n\n\t// Remove specified keys\n\t// 移除指定的键\n\tfor _, key := range removeKeys {\n\t\tdelete(result, key)\n\t}\n\n\treturn result\n}\n\n// ReconstructContent rebuilds content with frontmatter\n// ReconstructContent 使用 frontmatter 重新构建内容\nfunc ReconstructContent(yamlData map[string]interface{}, body string) string {\n\tif len(yamlData) == 0 {\n\t\treturn body\n\t}\n\n\tyamlBytes, err := yaml.Marshal(yamlData)\n\tif err != nil {\n\t\treturn body\n\t}\n\n\tvar sb strings.Builder\n\tsb.WriteString(frontmatterDelimiter)\n\tsb.WriteString(\"\\n\")\n\tsb.Write(yamlBytes)\n\tsb.WriteString(frontmatterDelimiter)\n\tsb.WriteString(\"\\n\")\n\tsb.WriteString(body)\n\n\treturn sb.String()\n}\n"
  },
  {
    "path": "pkg/util/frontmatter_test.go",
    "content": "package util\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestParseFrontmatter(t *testing.T) {\n\ttests := []struct {\n\t\tname               string\n\t\tcontent            string\n\t\twantYaml           map[string]interface{}\n\t\twantBody           string\n\t\twantHasFrontmatter bool\n\t}{\n\t\t{\n\t\t\tname: \"Standard LF\",\n\t\t\tcontent: \"---\\ntags: [work]\\n---\\nHello\",\n\t\t\twantYaml: map[string]interface{}{\"tags\": []interface{}{\"work\"}},\n\t\t\twantBody: \"Hello\",\n\t\t\twantHasFrontmatter: true,\n\t\t},\n\t\t{\n\t\t\tname: \"Standard CRLF\",\n\t\t\tcontent: \"---\\r\\ntags: [work]\\r\\n---\\r\\nHello\",\n\t\t\twantYaml: map[string]interface{}{\"tags\": []interface{}{\"work\"}},\n\t\t\twantBody: \"Hello\",\n\t\t\twantHasFrontmatter: true,\n\t\t},\n\t\t{\n\t\t\tname: \"Mixed Line Endings\",\n\t\t\tcontent: \"---\\r\\ntags: [work]\\n---\\r\\nHello\",\n\t\t\twantYaml: map[string]interface{}{\"tags\": []interface{}{\"work\"}},\n\t\t\twantBody: \"Hello\",\n\t\t\twantHasFrontmatter: true,\n\t\t},\n\t\t{\n\t\t\tname: \"No Frontmatter\",\n\t\t\tcontent: \"Hello World\",\n\t\t\twantYaml: nil,\n\t\t\twantBody: \"Hello World\",\n\t\t\twantHasFrontmatter: false,\n\t\t},\n\t\t{\n\t\t\tname: \"Empty content\",\n\t\t\tcontent: \"\",\n\t\t\twantYaml: nil,\n\t\t\twantBody: \"\",\n\t\t\twantHasFrontmatter: false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgotYaml, gotBody, gotHasFrontmatter := ParseFrontmatter(tt.content)\n\t\t\tif gotHasFrontmatter != tt.wantHasFrontmatter {\n\t\t\t\tt.Errorf(\"ParseFrontmatter() gotHasFrontmatter = %v, want %v\", gotHasFrontmatter, tt.wantHasFrontmatter)\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(gotYaml, tt.wantYaml) {\n\t\t\t\tt.Errorf(\"ParseFrontmatter() gotYaml = %v, want %v\", gotYaml, tt.wantYaml)\n\t\t\t}\n\t\t\tif gotBody != tt.wantBody {\n\t\t\t\tt.Errorf(\"ParseFrontmatter() gotBody = %v, want %v\", gotBody, tt.wantBody)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/util/hash.go",
    "content": "package util\n\nimport (\n\t\"crypto/md5\"\n\t\"encoding/hex\"\n\t\"strconv\"\n\t\"unicode/utf16\"\n)\n\n// EncodeMD5 performs MD5 encoding on a string\n// EncodeMD5 对字符串进行MD5编码\n// str: string to be encoded\n// str: 待编码的字符串\n// return: MD5 encoded 32-bit hexadecimal string\n// 返回值: MD5编码后的32位十六进制字符串\nfunc EncodeMD5(str string) string {\n\th := md5.New()\n\th.Write([]byte(str))\n\treturn hex.EncodeToString(h.Sum(nil))\n}\n\n// EncodeHash32 performs 32-bit hash encoding on a string\n// EncodeHash32 对字符串进行 32 位哈希编码\nfunc EncodeHash32(content string) string {\n\t// Convert string to rune slice, then to UTF-16 code units (consistent with JS internal representation)\n\t// 首先将字符串转为 rune 切片，再转为 UTF-16 code units（与 JS 的内部表示一致）\n\trunes := []rune(content)\n\tutf16Units := utf16.Encode(runes) // []uint16\n\tvar hash int32 = 0\n\tfor _, u := range utf16Units {\n\t\tchar := int32(u) // Consistent with 16-bit value returned by JS charCodeAt // 与 JS charCodeAt 返回的 16-bit 值一致\n\t\thash = (hash << 5) - hash + char\n\t\t// int32 will automatically overflow, equivalent to JS 32-bit bitwise operation result\n\t\t// int32 会自动溢出，等价于 JS 的 32-bit 位运算结果\n\t}\n\treturn strconv.Itoa(int(hash))\n}\nconst (\n\t// FileHashThreshold defines the size threshold (100MB) above which partial hashing is used\n\t// FileHashThreshold 定义触发分段哈希的阈值 (100MB)\n\tFileHashThreshold = 100 * 1024 * 1024\n\t// FileHashSliceSize defines the size of slices taken from the beginning and end of large files (50MB)\n\t// FileHashSliceSize 定义大文件分段哈希时首尾读取的大小 (50MB)\n\tFileHashSliceSize = 50 * 1024 * 1024\n)\n\n// EncodeHash32Bytes performs 32-bit hash encoding on raw bytes.\n// If the data exceeds 100MB, it only hashes the first 50MB and last 50MB.\n// EncodeHash32Bytes 对原始字节进行 32 位哈希编码。\n// 如果数据超过 100MB，则仅计算前 50MB 和后 50MB 的哈希。\nfunc EncodeHash32Bytes(data []byte) string {\n\tsize := len(data)\n\tvar hash int32 = 0\n\n\tif size <= FileHashThreshold {\n\t\t// Small data: full hash // 小数据：全量哈希\n\t\tfor _, b := range data {\n\t\t\thash = (hash << 5) - hash + int32(b)\n\t\t}\n\t} else {\n\t\t// Large data: hash first 50MB + last 50MB // 大数据：哈希前 50MB + 后 50MB\n\t\t// Hash first 50MB\n\t\tfor i := 0; i < FileHashSliceSize; i++ {\n\t\t\thash = (hash << 5) - hash + int32(data[i])\n\t\t}\n\t\t// Hash last 50MB\n\t\tfor i := size - FileHashSliceSize; i < size; i++ {\n\t\t\thash = (hash << 5) - hash + int32(data[i])\n\t\t}\n\t}\n\treturn strconv.Itoa(int(hash))\n}\n"
  },
  {
    "path": "pkg/util/hash_test.go",
    "content": "package util\n\nimport (\n\t\"testing\"\n)\n\nfunc TestHashConsistency(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tinput    string\n\t\texpected string\n\t}{\n\t\t{\"ASCII\", \"Hello\", \"69609650\"},\n\t\t{\"Unicode\", \"你好\", \"652829\"},\n\t\t{\"Complex\", \"Fast Note Sync 🚀\", \"475362430\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := EncodeHash32(tt.input)\n\t\t\tif got != tt.expected {\n\t\t\t\tt.Errorf(\"EncodeHash32(%q) = %v, want %v\", tt.input, got, tt.expected)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestHashBytesConsistency(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tinput    []byte\n\t\texpected string\n\t}{\n\t\t{\"Binary1\", []byte{0x48, 0x65, 0x6c, 0x6c, 0x6f}, \"69609650\"}, // \"Hello\" as bytes\n\t\t{\"Binary2\", []byte{0xff, 0x00, 0xaa, 0x55}, \"7602060\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := EncodeHash32Bytes(tt.input)\n\t\t\tif got != tt.expected {\n\t\t\t\tt.Errorf(\"EncodeHash32Bytes(%v) = %v, want %v\", tt.name, got, tt.expected)\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"LargeData\", func(t *testing.T) {\n\t\t// Simulate 110MB data\n\t\tsize := 110 * 1024 * 1024\n\t\tdata := make([]byte, size)\n\t\t// Fill first 50MB with 1\n\t\tfor i := 0; i < 50*1024*1024; i++ {\n\t\t\tdata[i] = 1\n\t\t}\n\t\t// Fill middle 10MB with 3 (should be ignored)\n\t\tfor i := 50 * 1024 * 1024; i < 60*1024*1024; i++ {\n\t\t\tdata[i] = 3\n\t\t}\n\t\t// Fill last 50MB with 2\n\t\tfor i := 60 * 1024 * 1024; i < size; i++ {\n\t\t\tdata[i] = 2\n\t\t}\n\n\t\texpected := \"1442840576\"\n\t\tgot := EncodeHash32Bytes(data)\n\t\tif got != expected {\n\t\t\tt.Errorf(\"EncodeHash32Bytes(110MB) = %v, want %v\", got, expected)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "pkg/util/link_parser.go",
    "content": "// Package util provides common utility functions\n// Package util 提供通用工具函数\npackage util\n\nimport \"regexp\"\n\n// WikiLink represents a wiki-style link extracted from content // WikiLink 表示从内容中提取的维基风格链接\ntype WikiLink struct {\n\tPath    string // The target path // 目标路径\n\tAlias   string // Optional alias from [[link|alias]] // 可选别名\n\tIsEmbed bool   // True if this is an embed (![[...]]) rather than a link ([[...]]) // 是否为嵌入 (![[...]])\n}\n\n// wikiLinkRegex matches [[wiki-links]], [[link|alias]], and ![[embeds]] patterns\n// Group 1: optional \"!\" prefix (embed marker) // 可选的 \"!\" 前缀（嵌入标记）\n// Group 2: path // 路径\n// Group 3: optional alias // 可选别名\n// wikiLinkRegex 匹配 [[wiki-links]], [[link|alias]], 和 ![[embeds]] 模式\nvar wikiLinkRegex = regexp.MustCompile(`(!?)\\[\\[([^\\]|]+)(?:\\|([^\\]]+))?\\]\\]`)\n\n// ParseWikiLinks extracts [[wiki-links]], [[link|alias]], and ![[embeds]] from content\n// Returns a slice of WikiLink with path, optional alias, and embed flag\n// ParseWikiLinks 从内容中提取 [[wiki-links]], [[link|alias]], 和 ![[embeds]]\n// 返回包含路径、可选别名和嵌入标记的 WikiLink 切片\nfunc ParseWikiLinks(content string) []WikiLink {\n\tif content == \"\" {\n\t\treturn nil\n\t}\n\n\tmatches := wikiLinkRegex.FindAllStringSubmatch(content, -1)\n\tif len(matches) == 0 {\n\t\treturn nil\n\t}\n\n\t// Use a map to deduplicate by path+isEmbed combination\n\t// 使用 map 按 path+isEmbed 组合进行去重\n\ttype linkKey struct {\n\t\tpath    string\n\t\tisEmbed bool\n\t}\n\tseen := make(map[linkKey]bool)\n\tvar links []WikiLink\n\n\tfor _, match := range matches {\n\t\t// Process match // 处理匹配项\n\t\tisEmbed := match[1] == \"!\"\n\t\tpath := match[2]\n\t\tkey := linkKey{path: path, isEmbed: isEmbed}\n\t\tif seen[key] {\n\t\t\tcontinue\n\t\t}\n\t\tseen[key] = true\n\n\t\tlink := WikiLink{\n\t\t\tPath:    path,\n\t\t\tIsEmbed: isEmbed,\n\t\t}\n\t\tif len(match) > 3 && match[3] != \"\" {\n\t\t\tlink.Alias = match[3]\n\t\t}\n\t\tlinks = append(links, link)\n\t}\n\n\treturn links\n}\n"
  },
  {
    "path": "pkg/util/link_parser_test.go",
    "content": "// Package util provides common utility functions\npackage util\n\nimport (\n\t\"testing\"\n)\n\nfunc TestParseWikiLinks(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tcontent  string\n\t\texpected []WikiLink\n\t}{\n\t\t// Basic wikilinks (IsEmbed=false)\n\t\t{\n\t\t\tname:    \"simple wikilink\",\n\t\t\tcontent: \"Check out [[Note Name]] for more info\",\n\t\t\texpected: []WikiLink{\n\t\t\t\t{Path: \"Note Name\", Alias: \"\", IsEmbed: false},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:    \"wikilink with alias\",\n\t\t\tcontent: \"See [[Note Name|Display Text]] here\",\n\t\t\texpected: []WikiLink{\n\t\t\t\t{Path: \"Note Name\", Alias: \"Display Text\", IsEmbed: false},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:    \"wikilink with heading\",\n\t\t\tcontent: \"Jump to [[Note Name#Heading]] section\",\n\t\t\texpected: []WikiLink{\n\t\t\t\t{Path: \"Note Name#Heading\", Alias: \"\", IsEmbed: false},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:    \"wikilink with block reference\",\n\t\t\tcontent: \"Reference [[Note Name#^block-id]] here\",\n\t\t\texpected: []WikiLink{\n\t\t\t\t{Path: \"Note Name#^block-id\", Alias: \"\", IsEmbed: false},\n\t\t\t},\n\t\t},\n\n\t\t// Embeds (IsEmbed=true)\n\t\t{\n\t\t\tname:    \"simple embed\",\n\t\t\tcontent: \"Embedded: ![[Note Name]]\",\n\t\t\texpected: []WikiLink{\n\t\t\t\t{Path: \"Note Name\", Alias: \"\", IsEmbed: true},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:    \"embed with heading\",\n\t\t\tcontent: \"Section embed: ![[Note Name#Heading]]\",\n\t\t\texpected: []WikiLink{\n\t\t\t\t{Path: \"Note Name#Heading\", Alias: \"\", IsEmbed: true},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:    \"image embed\",\n\t\t\tcontent: \"Image: ![[Image.png]]\",\n\t\t\texpected: []WikiLink{\n\t\t\t\t{Path: \"Image.png\", Alias: \"\", IsEmbed: true},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:    \"audio embed\",\n\t\t\tcontent: \"Audio: ![[Audio.mp3]]\",\n\t\t\texpected: []WikiLink{\n\t\t\t\t{Path: \"Audio.mp3\", Alias: \"\", IsEmbed: true},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:    \"video embed\",\n\t\t\tcontent: \"Video: ![[Video.mp4]]\",\n\t\t\texpected: []WikiLink{\n\t\t\t\t{Path: \"Video.mp4\", Alias: \"\", IsEmbed: true},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:    \"pdf embed\",\n\t\t\tcontent: \"Document: ![[Document.pdf]]\",\n\t\t\texpected: []WikiLink{\n\t\t\t\t{Path: \"Document.pdf\", Alias: \"\", IsEmbed: true},\n\t\t\t},\n\t\t},\n\n\t\t// Mixed content\n\t\t{\n\t\t\tname:    \"link and embed in same content\",\n\t\t\tcontent: \"Link: [[Note1]] and embed: ![[Note2]]\",\n\t\t\texpected: []WikiLink{\n\t\t\t\t{Path: \"Note1\", Alias: \"\", IsEmbed: false},\n\t\t\t\t{Path: \"Note2\", Alias: \"\", IsEmbed: true},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:    \"same path as link and embed\",\n\t\t\tcontent: \"Link: [[Note]] and embed: ![[Note]]\",\n\t\t\texpected: []WikiLink{\n\t\t\t\t{Path: \"Note\", Alias: \"\", IsEmbed: false},\n\t\t\t\t{Path: \"Note\", Alias: \"\", IsEmbed: true},\n\t\t\t},\n\t\t},\n\n\t\t// Should NOT capture - markdown links\n\t\t{\n\t\t\tname:     \"markdown external link\",\n\t\t\tcontent:  \"Check [Display Text](https://example.com) here\",\n\t\t\texpected: nil,\n\t\t},\n\t\t{\n\t\t\tname:     \"markdown relative link\",\n\t\t\tcontent:  \"See [Display Text](note.md) for details\",\n\t\t\texpected: nil,\n\t\t},\n\t\t{\n\t\t\tname:     \"auto-linked URL\",\n\t\t\tcontent:  \"Visit https://example.com for more\",\n\t\t\texpected: nil,\n\t\t},\n\n\t\t// Edge cases\n\t\t{\n\t\t\tname:     \"empty content\",\n\t\t\tcontent:  \"\",\n\t\t\texpected: nil,\n\t\t},\n\t\t{\n\t\t\tname:     \"no links\",\n\t\t\tcontent:  \"Just plain text without any links\",\n\t\t\texpected: nil,\n\t\t},\n\t\t{\n\t\t\tname:    \"multiple links\",\n\t\t\tcontent: \"[[Note1]] and [[Note2]] and [[Note3|Alias]]\",\n\t\t\texpected: []WikiLink{\n\t\t\t\t{Path: \"Note1\", Alias: \"\", IsEmbed: false},\n\t\t\t\t{Path: \"Note2\", Alias: \"\", IsEmbed: false},\n\t\t\t\t{Path: \"Note3\", Alias: \"Alias\", IsEmbed: false},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:    \"duplicate links deduplicated\",\n\t\t\tcontent: \"[[Note]] appears twice [[Note]]\",\n\t\t\texpected: []WikiLink{\n\t\t\t\t{Path: \"Note\", Alias: \"\", IsEmbed: false},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:    \"link with path separators\",\n\t\t\tcontent: \"[[folder/subfolder/note]]\",\n\t\t\texpected: []WikiLink{\n\t\t\t\t{Path: \"folder/subfolder/note\", Alias: \"\", IsEmbed: false},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:    \"embed with alias\",\n\t\t\tcontent: \"![[Image.png|400]]\",\n\t\t\texpected: []WikiLink{\n\t\t\t\t{Path: \"Image.png\", Alias: \"400\", IsEmbed: true},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tresult := ParseWikiLinks(tt.content)\n\n\t\t\tif len(result) != len(tt.expected) {\n\t\t\t\tt.Errorf(\"ParseWikiLinks(%q) returned %d links, want %d\", tt.content, len(result), len(tt.expected))\n\t\t\t\tt.Errorf(\"Got: %+v\", result)\n\t\t\t\tt.Errorf(\"Want: %+v\", tt.expected)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tfor i, link := range result {\n\t\t\t\tif link.Path != tt.expected[i].Path {\n\t\t\t\t\tt.Errorf(\"Link[%d].Path = %q, want %q\", i, link.Path, tt.expected[i].Path)\n\t\t\t\t}\n\t\t\t\tif link.Alias != tt.expected[i].Alias {\n\t\t\t\t\tt.Errorf(\"Link[%d].Alias = %q, want %q\", i, link.Alias, tt.expected[i].Alias)\n\t\t\t\t}\n\t\t\t\tif link.IsEmbed != tt.expected[i].IsEmbed {\n\t\t\t\t\tt.Errorf(\"Link[%d].IsEmbed = %v, want %v\", i, link.IsEmbed, tt.expected[i].IsEmbed)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestParseWikiLinks_ComplexContent(t *testing.T) {\n\tcontent := `# My Note\n\nThis note has various links:\n- A regular link: [[Another Note]]\n- A link with alias: [[Note|Custom Text]]\n- An embedded note: ![[Embedded Note]]\n- An embedded image: ![[photo.jpg]]\n- A heading link: [[Note#Section]]\n- A block reference: [[Note#^abc123]]\n\nSome markdown links that should NOT be captured:\n- [External](https://example.com)\n- [Relative](./other.md)\n\nMore wikilinks at the end: [[Final Note]]\n`\n\n\tresult := ParseWikiLinks(content)\n\n\texpectedPaths := map[string]bool{\n\t\t\"Another Note\":  true,\n\t\t\"Note\":          true,\n\t\t\"Embedded Note\": true,\n\t\t\"photo.jpg\":     true,\n\t\t\"Note#Section\":  true,\n\t\t\"Note#^abc123\":  true,\n\t\t\"Final Note\":    true,\n\t}\n\n\tif len(result) != len(expectedPaths) {\n\t\tt.Errorf(\"Expected %d links, got %d\", len(expectedPaths), len(result))\n\t\tfor _, link := range result {\n\t\t\tt.Logf(\"Found: %+v\", link)\n\t\t}\n\t}\n\n\tfor _, link := range result {\n\t\tif !expectedPaths[link.Path] {\n\t\t\tt.Errorf(\"Unexpected link path: %q\", link.Path)\n\t\t}\n\t}\n\n\t// Verify embeds are correctly identified\n\tfor _, link := range result {\n\t\tswitch link.Path {\n\t\tcase \"Embedded Note\", \"photo.jpg\":\n\t\t\tif !link.IsEmbed {\n\t\t\t\tt.Errorf(\"Link %q should be an embed\", link.Path)\n\t\t\t}\n\t\tdefault:\n\t\t\tif link.IsEmbed {\n\t\t\t\tt.Errorf(\"Link %q should NOT be an embed\", link.Path)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pkg/util/machine.go",
    "content": "package util\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"os/exec\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/denisbrodbeck/machineid\"\n)\n\nvar (\n\tmachineID      string\n\tmachineIDMutex sync.Mutex\n)\n\n// GetMachineID gets unique identifier of the current machine\n// GetMachineID 获取当前机器的唯一标识符\n// Prioritize machineid library, fallback to motherboard serial number\n// 优先使用 machineid 库，失败则尝试获取主板序列号\n// return: machine ID string, returns empty string if all failed\n// 返回值: 机器ID字符串，如果全部获取失败则返回空字符串\nfunc GetMachineID() string {\n\tmachineIDMutex.Lock()\n\tdefer machineIDMutex.Unlock()\n\n\tif machineID != \"\" {\n\t\treturn machineID\n\t}\n\n\t// 1. Try using machineid library\n\t// 1. 尝试使用 machineid 库\n\tid, err := machineid.ID()\n\tif err == nil && id != \"\" {\n\t\tmachineID = id\n\t\treturn machineID\n\t}\n\n\t// 2. Try getting motherboard serial number\n\t// 2. 尝试获取主板序列号\n\tid, err = getMotherboardID()\n\tif err == nil && id != \"\" {\n\t\tmachineID = id\n\t\treturn machineID\n\t}\n\n\t// 3. All failed, return empty string\n\t// 3. 全部失败，返回空字符串\n\t// Caller should determine if machine ID was successfully obtained based on the return value\n\t// 调用者应根据返回值判断是否成功获取机器ID\n\treturn \"\"\n}\n\n// getMotherboardID gets the serial number of the motherboard\n// getMotherboardID 获取主板序列号\nfunc getMotherboardID() (string, error) {\n\tvar cmd *exec.Cmd\n\tswitch runtime.GOOS {\n\tcase \"windows\":\n\t\tcmd = exec.Command(\"wmic\", \"baseboard\", \"get\", \"serialnumber\")\n\tcase \"linux\":\n\t\t// Read file\n\t\t// 读取文件\n\t\tcontent, err := os.ReadFile(\"/sys/class/dmi/id/board_serial\")\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn strings.TrimSpace(string(content)), nil\n\tcase \"darwin\":\n\t\tcmd = exec.Command(\"ioreg\", \"-l\") // Needs grep, a bit complex, simplified or empty here\n\t\t// ioreg -l | grep IOPlatformSerialNumber\n\t\t// Does not complete macOS complex parsing, simply return error to fallback\n\t\t// 暂不完整实现 macOS 复杂解析，简单返回 error 走 fallback\n\t\treturn \"\", errors.New(\"not implemented for darwin\")\n\tdefault:\n\t\treturn \"\", errors.New(\"unsupported os\")\n\t}\n\n\tif cmd != nil {\n\t\tout, err := cmd.Output()\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn parseSerialNumber(string(out)), nil\n\t}\n\n\treturn \"\", errors.New(\"unknown error\")\n}\n\n// parseSerialNumber parses the serial number from command output\n// parseSerialNumber 从命令输出中解析序列号\nfunc parseSerialNumber(output string) string {\n\tlines := strings.Split(output, \"\\n\")\n\tfor _, line := range lines {\n\t\tline = strings.TrimSpace(line)\n\t\tif line == \"\" || strings.EqualFold(line, \"SerialNumber\") {\n\t\t\tcontinue\n\t\t}\n\t\treturn line\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "pkg/util/math.go",
    "content": "package util\n\nimport \"math\"\n\nfunc Ceil(a, b int64) int64 {\n\tif b == 0 {\n\t\tpanic(\"division by zero\")\n\t}\n\treturn int64(math.Ceil(float64(a) / float64(b)))\n}\n"
  },
  {
    "path": "pkg/util/password.go",
    "content": "package util\n\nimport (\n\t\"golang.org/x/crypto/bcrypt\"\n)\n\n// GeneratePasswordHash generates bcrypt hash of a password\n// GeneratePasswordHash 生成密码的bcrypt哈希值\n// password: original password string // 原始密码字符串\n// return: hashed password string, and possible error info // 返回值: 哈希后的密码字符串，以及可能的错误信息\nfunc GeneratePasswordHash(password string) (string, error) {\n\tbytes, err := bcrypt.GenerateFromPassword([]byte(password), 10)\n\treturn string(bytes), err\n}\n\n// CheckPasswordHash verifies whether password matches the hash\n// CheckPasswordHash 验证密码与哈希值是否匹配\n// hash: stored hash value // 存储的哈希值\n// password: password to be verified // 待验证的密码\n// return: true if password matches, false otherwise // 返回值: 如果密码匹配返回true，否则返回false\nfunc CheckPasswordHash(hash, password string) bool {\n\terr := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))\n\treturn err == nil\n}\n"
  },
  {
    "path": "pkg/util/path.go",
    "content": "// Package util provides common utility functions\n// Package util 提供通用工具函数\npackage util\n\nimport (\n\t\"io\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\n// ApplyDefaultFolder applies default folder prefix\n// ApplyDefaultFolder 应用默认文件夹前缀\n// When path does not contain \"/\" and defaultFolder is not empty, add defaultFolder as prefix to path\n// 当 path 不包含 \"/\" 且 defaultFolder 非空时，将 defaultFolder 作为前缀添加到 path\n// Example: ApplyDefaultFolder(\"note.md\", \"inbox\") => \"inbox/note.md\"\n// 例如: ApplyDefaultFolder(\"note.md\", \"inbox\") => \"inbox/note.md\"\n//\n//\tApplyDefaultFolder(\"folder/note.md\", \"inbox\") => \"folder/note.md\" (unchanged)\n//\tApplyDefaultFolder(\"folder/note.md\", \"inbox\") => \"folder/note.md\" (不变)\n//\tApplyDefaultFolder(\"note.md\", \"\") => \"note.md\" (unchanged)\n//\tApplyDefaultFolder(\"note.md\", \"\") => \"note.md\" (不变)\nfunc ApplyDefaultFolder(path, defaultFolder string) string {\n\tif defaultFolder == \"\" || strings.Contains(path, \"/\") {\n\t\treturn path\n\t}\n\treturn strings.TrimSuffix(defaultFolder, \"/\") + \"/\" + path\n}\n\n// GeneratePathVariations generates all suffix variations of a path for backlink matching.\n// Given \"projects/test-backlinks/folder-a/note.md\", returns:\n// [\"note\", \"folder-a/note\", \"test-backlinks/folder-a/note\", \"projects/test-backlinks/folder-a/note\"]\n// This allows matching links like [[note]], [[folder-a/note]], etc.\n// GeneratePathVariations 生成路径的所有后缀变体，用于反向链接匹配。\n// 给定 \"projects/test-backlinks/folder-a/note.md\"，返回：\n// [\"note\", \"folder-a/note\", \"test-backlinks/folder-a/note\", \"projects/test-backlinks/folder-a/note\"]\n// 这允许匹配类似 [[note]], [[folder-a/note]] 等链接。\nfunc GeneratePathVariations(path string) []string {\n\t// Strip .md extension if present\n\tpath = strings.TrimSuffix(path, \".md\")\n\n\tif path == \"\" {\n\t\treturn nil\n\t}\n\n\tparts := strings.Split(path, \"/\")\n\tif len(parts) == 0 {\n\t\treturn nil\n\t}\n\n\t// Build progressively longer suffixes from right to left\n\tvariations := make([]string, 0, len(parts))\n\tfor i := len(parts) - 1; i >= 0; i-- {\n\t\tsuffix := strings.Join(parts[i:], \"/\")\n\t\tvariations = append(variations, suffix)\n\t}\n\n\treturn variations\n}\n\n// ValidatePath checks if a path is safe (no directory traversal).\n// Returns true if the path is valid, false if it contains \"..\", is absolute, or contains null bytes.\n// ValidatePath 检查路径是否安全（无目录遍历）。\n// 如果路径有效则返回 true，如果包含 \"..\"、是绝对路径或包含空字节则返回 false。\nfunc ValidatePath(path string) bool {\n\tif path == \"\" {\n\t\treturn false\n\t}\n\tif strings.Contains(path, \"\\x00\") {\n\t\treturn false\n\t}\n\tdecoded, err := url.QueryUnescape(path)\n\tif err != nil {\n\t\treturn false\n\t}\n\tif decoded != path {\n\t\tpath = decoded\n\t}\n\tif filepath.IsAbs(path) {\n\t\treturn false\n\t}\n\tcleaned := filepath.Clean(path)\n\treturn !strings.Contains(cleaned, \"..\") && !strings.HasPrefix(cleaned, \"/\")\n}\n\n// NormalizePath normalizes path for cross-platform compatibility.\n// Converts backslashes to forward slashes and cleans the path.\n// NormalizePath 规范化路径以实现跨平台兼容性。\n// 将反斜杠转换为正斜杠并清理路径。\nfunc NormalizePath(path string) string {\n\tpath = strings.ReplaceAll(path, \"\\\\\", \"/\")\n\tpath = filepath.Clean(path)\n\tif strings.HasSuffix(path, \"/\") && len(path) > 1 {\n\t\tpath = strings.TrimSuffix(path, \"/\")\n\t}\n\treturn path\n}\n\n// CopyFile copies a file from src to dst\n// CopyFile 将文件从 src 复制到 dst\nfunc CopyFile(src, dst string) error {\n\tsource, err := os.Open(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer source.Close()\n\n\tdestination, err := os.Create(dst)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer destination.Close()\n\n\t_, err = io.Copy(destination, source)\n\treturn err\n}\n\n// MoveFile moves a file from src to dst, supporting cross-device move.\n// MoveFile 将文件从 src 移动到 dst，支持跨设备移动。\nfunc MoveFile(src, dst string) error {\n\t// Try rename first\n\t// 先尝试重命名\n\terr := os.Rename(src, dst)\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\t// Check if the error is \"invalid cross-device link\" (EXDEV)\n\t// On Windows, this might also show up differently or Rename might fail cross-vol\n\t// We fallback to Copy + Remove for any error to be safe, or we can check error string\n\t// On Linux, EXDEV error message contains \"invalid cross-device link\"\n\tif strings.Contains(err.Error(), \"invalid cross-device link\") ||\n\t\tstrings.Contains(strings.ToLower(err.Error()), \"cross-device\") {\n\t\t// Copy file content\n\t\tif copyErr := CopyFile(src, dst); copyErr != nil {\n\t\t\treturn copyErr\n\t\t}\n\t\t// Remove source file\n\t\treturn os.Remove(src)\n\t}\n\n\treturn err\n}\n"
  },
  {
    "path": "pkg/util/random.go",
    "content": "package util\n\nimport (\n\t\"math/rand\"\n\t\"time\"\n)\n\n// GenerateRandomNumber generates a slice of unique random integers within specified range\n// GenerateRandomNumber 生成一组指定范围内、不重复的随机整数切片\n// start: minimum value of random number\n// start: 随机数的最小值\n// end: maximum value of random number\n// end: 随机数的最大值\n// count: number of random numbers to generate\n// count: 生成的随机数个数\n// return: generated random number slice\n// 返回值: 生成的随机数切片\n\nfunc GenerateRandomNumber(start int, end int, count int) []int {\n\tif end < start || (end-start) < count {\n\t\treturn nil\n\t}\n\ttotal := end - start\n\t// This is a shuffled sequence [0, 1, 5, 2, 4...]\n\t// 这是一个打乱的序列 [0, 1, 5, 2, 4...]\n\tperm := rand.Perm(total)\n\n\tnums := make([]int, count)\n\tfor i := 0; i < count; i++ {\n\t\tnums[i] = perm[i] + start\n\t}\n\treturn nums\n}\n\n// InArray checks whether an integer is in a slice (used for random number generation)\n// InArray 检查整数是否在切片中（用于随机数生成）\n// nums: integer slice\n// nums: 整数切片\n// num: integer to be checked\n// num: 待检查的整数\n// return: true if in slice, false otherwise\n// 返回值: 如果在切片中返回true，否则返回false\nfunc InArray(nums []int, num int) bool {\n\tfor _, v := range nums {\n\t\tif v == num {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// GenerateRandomSingleNumber generates a single random number\n// GenerateRandomSingleNumber 生成单个随机数\n// start: minimum value of random number\n// start: 随机数的最小值\n// end: maximum value of random number\n// end: 随机数的最大值\n// return: generated random number\n// 返回值: 生成的随机数\nfunc GenerateRandomSingleNumber(start int, end int) int {\n\tif end < start {\n\t\treturn start\n\t}\n\tr := rand.New(rand.NewSource(time.Now().UnixNano()))\n\treturn r.Intn(end-start) + start\n}\n\n// GetRandomString generates random string of specified length\n// GetRandomString 生成指定长度的随机字符串\nfunc GetRandomString(length int) string {\n\tconst charset = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"\n\n\tb := make([]byte, length)\n\tfor i := range b {\n\t\t// Use global rand directly, no need to NewSource every time\n\t\t// 直接使用全局 rand，无需每次都 NewSource\n\t\tb[i] = charset[rand.Intn(len(charset))]\n\t}\n\treturn string(b)\n}\n"
  },
  {
    "path": "pkg/util/runtime.go",
    "content": "package util\n\nimport \"runtime\"\n\nfunc WhoCalled() string {\n\t// Skip:\n\t// 0: runtime.Callers\n\t// 1: whoCalled\n\t// 2: caller of whoCalled\n\t// 3: caller of caller of whoCalled -> which is the caller of B that we want (if whoCalled is directly called in B)\n\t// 3: caller of caller of whoCalled -> 也就是我们想要的 B 的调用者（如果 whoCalled 在 B 内直接被调用）\n\tpc := make([]uintptr, 1)\n\tn := runtime.Callers(3, pc)\n\tif n == 0 {\n\t\treturn \"unknown\"\n\t}\n\tframes := runtime.CallersFrames(pc[:n])\n\tframe, _ := frames.Next()\n\treturn frame.Function // Contains full path, for example \"main.A\" or \"main.C\"\n\t// 包含完整路径，例如 \"main.A\" 或 \"main.C\"\n}\n"
  },
  {
    "path": "pkg/util/sys_info.go",
    "content": "package util\n\nimport (\n\t\"bufio\"\n\t\"os\"\n\t\"os/exec\"\n\t\"runtime\"\n\t\"strings\"\n)\n\n// GetOSPrettyName gets a more readable and detailed OS name and version\n// GetOSPrettyName 获取更具可读性和详细的操作系统名称及版本\nfunc GetOSPrettyName() string {\n\tswitch runtime.GOOS {\n\tcase \"linux\":\n\t\treturn getLinuxPrettyName()\n\tcase \"windows\":\n\t\treturn getWindowsVersion()\n\tcase \"darwin\":\n\t\treturn getMacOSVersion()\n\tdefault:\n\t\treturn runtime.GOOS\n\t}\n}\n\n// getLinuxPrettyName reads /etc/os-release to get PRETTY_NAME\n// getLinuxPrettyName 读取 /etc/os-release 获取 PRETTY_NAME\nfunc getLinuxPrettyName() string {\n\tfile, err := os.Open(\"/etc/os-release\")\n\tif err != nil {\n\t\treturn \"Linux\"\n\t}\n\tdefer file.Close()\n\n\tscanner := bufio.NewScanner(file)\n\tfor scanner.Scan() {\n\t\tline := scanner.Text()\n\t\tif strings.HasPrefix(line, \"PRETTY_NAME=\") {\n\t\t\tname := strings.TrimPrefix(line, \"PRETTY_NAME=\")\n\t\t\tname = strings.Trim(name, \"\\\"\")\n\t\t\treturn name\n\t\t}\n\t}\n\treturn \"Linux\"\n}\n\n// getWindowsVersion executes 'cmd /c ver' to get Windows version\n// getWindowsVersion 执行 'cmd /c ver' 获取 Windows 版本\nfunc getWindowsVersion() string {\n\tcmd := exec.Command(\"cmd\", \"/c\", \"ver\")\n\tout, err := cmd.Output()\n\tif err != nil {\n\t\treturn \"Windows\"\n\t}\n\treturn strings.TrimSpace(string(out))\n}\n\n// getMacOSVersion executes 'sw_vers -productVersion' to get macOS version\n// getMacOSVersion 执行 'sw_vers -productVersion' 获取 macOS 版本\nfunc getMacOSVersion() string {\n\tcmd := exec.Command(\"sw_vers\", \"-productVersion\")\n\tout, err := cmd.Output()\n\tif err != nil {\n\t\treturn \"macOS\"\n\t}\n\treturn \"macOS \" + strings.TrimSpace(string(out))\n}\n"
  },
  {
    "path": "pkg/util/time.go",
    "content": "package util\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\n// GetFirstDateOfMonth gets the first day of the month for the given time, which is 0:00 on the first day of the month\n// GetFirstDateOfMonth 获取传入的时间所在月份的第一天，即某月第一天的0点\n// d: given time\n// d: 传入的时间\n// return: 0:00 on the first day of that month\n// 返回值: 该月第一天的0点时间\nfunc GetFirstDateOfMonth(d time.Time) time.Time {\n\td = d.AddDate(0, 0, -d.Day()+1)\n\treturn GetZeroTime(d)\n}\n\n// GetLastDateOfMonth gets the last day of the month for the given time, which is 0:00 on the last day of the month\n// GetLastDateOfMonth 获取传入的时间所在月份的最后一天，即某月最后一天的0点\n// d: given time\n// d: 传入的时间\n// return: 0:00 on the last day of that month\n// 返回值: 该月最后一天的0点时间\nfunc GetLastDateOfMonth(d time.Time) time.Time {\n\treturn GetFirstDateOfMonth(d).AddDate(0, 1, -1)\n}\n\n// GetZeroTime gets 0:00 time of a certain day\n// GetZeroTime 获取某一天的0点时间\n// d: given time\n// d: 传入的时间\n// return: 0:00 time of that day\n// 返回值: 当天的0点时间\nfunc GetZeroTime(d time.Time) time.Time {\n\treturn time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, d.Location())\n}\n\n// GetEndTime gets 23:59:59 time of a certain day\n// GetEndTime 获取某一天的23:59:59时间\n// d: given time\n// d: 传入的时间\n// return: 23:59:59 time of that day\n// 返回值: 当天的23:59:59时间\nfunc GetEndTime(d time.Time) time.Time {\n\treturn time.Date(d.Year(), d.Month(), d.Day(), 23, 59, 59, 0, d.Location())\n}\n\n// GetLastDateOfNextMonth gets the last day of the next month for the given time\n// GetLastDateOfNextMonth 获取传入时间的下个月最后一天\n// d: given time\n// d: 传入的时间\n// return: last day of the next month\n// 返回值: 下个月最后一天的时间\nfunc GetLastDateOfNextMonth(d time.Time) time.Time {\n\treturn GetFirstDateOfMonth(d).AddDate(0, 2, -1)\n}\n\n// Wait waits for specified number of seconds\n// Wait 等待指定的秒数\n// num: number of seconds to wait\n// num: 等待的秒数\nfunc Wait(num float32) {\n\ttmpTime := time.Duration(num * 1000000000)\n\ttime.Sleep(tmpTime)\n}\n\n// TimeParse time and date formatting\n// TimeParse 时间日期格式化\n// layout: time format\n// layout: 时间格式\n// in: time string to be parsed\n// in: 要解析的时间字符串\n// return: parsed time object\n// 返回值: 解析后的时间对象\nfunc TimeParse(layout string, in string) time.Time {\n\tlocal, _ := time.LoadLocation(\"Local\")\n\ttimer, _ := time.ParseInLocation(layout, in, local)\n\treturn timer\n}\n\n// ParseDuration parses duration string, supports 'd' (day) suffix\n// ParseDuration 解析时间字符串，支持 'd' (天) 后缀\nfunc ParseDuration(s string) (time.Duration, error) {\n\ts = strings.TrimSpace(s)\n\tif strings.HasSuffix(s, \"d\") {\n\t\tdaysStr := strings.TrimSuffix(s, \"d\")\n\t\tdays, err := strconv.Atoi(daysStr)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\treturn time.Duration(days) * 24 * time.Hour, nil\n\t}\n\t// If it is pure numbers, default to seconds\n\t// 如果是纯数字，默认为秒\n\tif _, err := strconv.Atoi(s); err == nil {\n\t\ts += \"s\"\n\t}\n\treturn time.ParseDuration(s)\n}\n"
  },
  {
    "path": "pkg/util/tokenizer.go",
    "content": "package util\n\nimport (\n\t\"strings\"\n\t\"unicode\"\n)\n\n// Tokenize text into tokens, supporting Chinese and English.\n// English is split by non-alphanumeric characters, and Chinese is split by Bigram.\n// Tokenize 对文本进行分词，支持中英文。\n// 英文按非字母数字切分，中文按二元分词 (Bigram)。\nfunc Tokenize(text string) []string {\n\tvar tokens []string\n\tvar currentToken strings.Builder\n\n\t// Convert to lowercase\n\t// 转换为小写\n\ttext = strings.ToLower(text)\n\trunes := []rune(text)\n\n\tfor i := 0; i < len(runes); i++ {\n\t\tr := runes[i]\n\t\tif unicode.IsLetter(r) || unicode.IsDigit(r) {\n\t\t\tif isCJK(r) {\n\t\t\t\t// Handle CJK characters (Bigram)\n\t\t\t\t// 处理中文/日文/韩文 (Bigram)\n\t\t\t\ttokens = append(tokens, string(r)) // Single character index to improve recall rate // 单字索引提高召回率\n\t\t\t\tif i+1 < len(runes) && isCJK(runes[i+1]) {\n\t\t\t\t\ttokens = append(tokens, string(runes[i:i+2]))\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Handle regular alphanumeric characters\n\t\t\t\t// 处理普通字母数字\n\t\t\t\tcurrentToken.WriteRune(r)\n\t\t\t}\n\t\t} else {\n\t\t\t// Save current regular word when encountering delimiters\n\t\t\t// 遇到分隔符，保存当前的普通单词\n\t\t\tif currentToken.Len() > 0 {\n\t\t\t\ttokens = append(tokens, currentToken.String())\n\t\t\t\tcurrentToken.Reset()\n\t\t\t}\n\t\t}\n\t}\n\n\t// Handle the last word\n\t// 处理最后一个单词\n\tif currentToken.Len() > 0 {\n\t\ttokens = append(tokens, currentToken.String())\n\t}\n\n\treturn UniqueStrings(tokens)\n}\n\n// isCJK checks if it is a CJK character\n// isCJK 检查是否是中日韩字符\nfunc isCJK(r rune) bool {\n\treturn unicode.Is(unicode.Scripts[\"Han\"], r) ||\n\t\tunicode.Is(unicode.Scripts[\"Hiragana\"], r) ||\n\t\tunicode.Is(unicode.Scripts[\"Katakana\"], r) ||\n\t\tunicode.Is(unicode.Scripts[\"Hangul\"], r)\n}\n\n// UniqueStrings deduplicates strings and filters out empty strings\n// UniqueStrings 字符串去重且过滤空字符串\nfunc UniqueStrings(slice []string) []string {\n\tkeys := make(map[string]bool)\n\tvar list []string\n\tfor _, entry := range slice {\n\t\tentry = strings.TrimSpace(entry)\n\t\tif entry == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif _, value := keys[entry]; !value {\n\t\t\tkeys[entry] = true\n\t\t\tlist = append(list, entry)\n\t\t}\n\t}\n\treturn list\n}\n"
  },
  {
    "path": "pkg/util/validator.go",
    "content": "package util\n\nimport (\n\t\"regexp\"\n)\n\n// IsValidEmail verifies if the email format is correct\n// IsValidEmail 验证邮箱格式是否正确\n// email: email string to be verified\n// email: 待验证的邮箱字符串\n// return: true if email format is correct, false otherwise\n// 返回值: 如果邮箱格式正确返回true，否则返回false\nfunc IsValidEmail(email string) bool {\n\t// Simple email format validation regular expression\n\t// 简单的邮箱格式验证正则表达式\n\tpattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$`\n\treg := regexp.MustCompile(pattern)\n\treturn reg.MatchString(email)\n}\n\n// IsValidUsername verifies if the username format is correct\n// IsValidUsername 验证用户名格式是否正确\n// username: username to be verified\n// username: 待验证的用户名\n// return: true if username format is correct, false otherwise\n// 返回值: 如果用户名格式正确返回true，否则返回false\nfunc IsValidUsername(username string) bool {\n\t// Username format: letters, numbers, underscores, length 3-20\n\t// 用户名格式：字母、数字、下划线，长度3-20\n\tpattern := `^[a-zA-Z0-9_]{3,20}$`\n\treg := regexp.MustCompile(pattern)\n\treturn reg.MatchString(username)\n}\n"
  },
  {
    "path": "pkg/validator/custom_validator.go",
    "content": "package validator\n\nimport (\n\t\"reflect\"\n\t\"sync\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\n\t\"github.com/gin-gonic/gin/binding\"\n\t\"github.com/go-playground/validator/v10\"\n)\n\ntype CustomValidator struct {\n\tOnce     sync.Once\n\tValidate *validator.Validate\n}\n\nfunc NewCustomValidator() *CustomValidator {\n\treturn &CustomValidator{}\n}\n\nfunc (v *CustomValidator) ValidateStruct(obj interface{}) error {\n\tif kindOfData(obj) == reflect.Struct {\n\t\tv.lazyinit()\n\t\tif err := v.Validate.Struct(obj); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (v *CustomValidator) Engine() interface{} {\n\tv.lazyinit()\n\treturn v.Validate\n}\n\nfunc (v *CustomValidator) lazyinit() {\n\tv.Once.Do(func() {\n\t\tv.Validate = validator.New()\n\t\tv.Validate.SetTagName(\"binding\")\n\t})\n}\n\nfunc kindOfData(data interface{}) reflect.Kind {\n\tvalue := reflect.ValueOf(data)\n\tvalueType := value.Kind()\n\n\tif valueType == reflect.Ptr {\n\t\tvalueType = value.Elem().Kind()\n\t}\n\treturn valueType\n}\n\nfunc RegisterCustom() {\n\tif v, ok := binding.Validator.Engine().(*validator.Validate); ok {\n\t\t// Register custom validation rule for model.LocalTime type\n\t\t// 注册 model.LocalTime 类型的自定义校验规则\n\t\tv.RegisterCustomTypeFunc(ValidateJSONDateType, timex.Time{})\n\t}\n}\n\nfunc ValidateJSONDateType(field reflect.Value) interface{} {\n\tif field.Type() == reflect.TypeOf(timex.Time{}) {\n\t\ttimeStr := field.Interface().(timex.Time).String()\n\t\t// 0001-01-01 00:00:00 is the zero value for time.Time type in go\n\t\t// 0001-01-01 00:00:00 是 go 中 time.Time 类型的空值\n\t\t// Returning Nil here will be judged as a zero value by the validator, and will not pass the `binding:\"required\"` rule\n\t\t// 这里返回 Nil 则会被 validator 判定为空值，而无法通过 `binding:\"required\"` 规则\n\t\tif timeStr == \"0001-01-01 00:00:00\" {\n\t\t\treturn nil\n\t\t}\n\t\treturn timeStr\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/validator/custom_validator_test.go",
    "content": "package validator\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/haierkeys/fast-note-sync-service/pkg/timex\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\ntype TestStruct struct {\n\tName string `binding:\"required\"`\n\tAge  int    `binding:\"min=18\"`\n}\n\nfunc TestCustomValidator_ValidateStruct(t *testing.T) {\n\tvalidator := NewCustomValidator()\n\n\t// Test valid struct\n\tvalidObj := TestStruct{Name: \"Alice\", Age: 20}\n\terr := validator.ValidateStruct(&validObj)\n\tassert.NoError(t, err)\n\n\t// Test invalid struct (missing required)\n\tinvalidObj1 := TestStruct{Age: 20}\n\terr = validator.ValidateStruct(&invalidObj1)\n\tassert.Error(t, err)\n\n\t// Test invalid struct (min condition)\n\tinvalidObj2 := TestStruct{Name: \"Bob\", Age: 15}\n\terr = validator.ValidateStruct(&invalidObj2)\n\tassert.Error(t, err)\n\n\t// Non-struct should return nil because kindOfData check ignores non-structs\n\tnotStruct := \"just a string\"\n\terr = validator.ValidateStruct(notStruct)\n\tassert.NoError(t, err)\n}\n\nfunc TestCustomValidator_Engine(t *testing.T) {\n\tvalidator := NewCustomValidator()\n\tengine := validator.Engine()\n\tassert.NotNil(t, engine)\n\n\t// Should be the same engine upon second call due to sync.Once\n\tengine2 := validator.Engine()\n\tassert.Equal(t, engine, engine2)\n}\n\nfunc TestValidateJSONDateType(t *testing.T) {\n\t// Setup standard time\n\tstandardTime := time.Date(2023, 10, 1, 12, 0, 0, 0, time.UTC)\n\ttxTime := timex.Time(standardTime)\n\n\tval := reflect.ValueOf(txTime)\n\tres := ValidateJSONDateType(val)\n\tassert.NotNil(t, res)\n\tassert.IsType(t, \"\", res)\n\tassert.Equal(t, \"2023-10-01 12:00:00\", res)\n\n\t// Setup zero time\n\tzeroTime := time.Time{}\n\ttxZeroTime := timex.Time(zeroTime)\n\tvalZero := reflect.ValueOf(txZeroTime)\n\tresZero := ValidateJSONDateType(valZero)\n\tassert.Nil(t, resZero, \"Zero time should return nil to fail 'required' binding\")\n}\n"
  },
  {
    "path": "pkg/workerpool/pool.go",
    "content": "// Package workerpool provides Worker Pool implementation for goroutine lifecycle management\n// Package workerpool 提供 goroutine 生命周期管理的 Worker Pool 实现\n// Used to limit the number of concurrent goroutines and prevent resource leaks\n// 用于限制并发 goroutine 数量，防止资源泄漏\npackage workerpool\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"go.uber.org/zap\"\n)\n\n// Error definitions\n// 错误定义\nvar (\n\t// ErrWorkerPoolFull returned when task queue is full\n\t// ErrWorkerPoolFull 当任务队列已满时返回\n\tErrWorkerPoolFull = errors.New(\"worker pool queue is full\")\n\t// ErrWorkerPoolClosed returned when Worker Pool is closed\n\t// ErrWorkerPoolClosed 当 Worker Pool 已关闭时返回\n\tErrWorkerPoolClosed = errors.New(\"worker pool is closed\")\n\t// ErrTaskCancelled returned when task is cancelled\n\t// ErrTaskCancelled 当任务被取消时返回\n\tErrTaskCancelled = errors.New(\"task was cancelled\")\n\t// ErrTaskTimeout returned when task execution timeout\n\t// ErrTaskTimeout 当任务执行超时时返回\n\tErrTaskTimeout = errors.New(\"task execution timeout\")\n)\n\n// Config Worker Pool configuration\n// Config Worker Pool 配置\ntype Config struct {\n\t// MaxWorkers maximum number of concurrent workers, default 100\n\t// MaxWorkers 最大并发 worker 数量，默认 100\n\tMaxWorkers int\n\t// QueueSize task queue size, default 1000\n\t// QueueSize 任务队列大小，默认 1000\n\tQueueSize int\n\t// WarningPercent warning threshold percentage, default 0.8 (80%)\n\t// WarningPercent 告警阈值百分比，默认 0.8 (80%)\n\tWarningPercent float64\n\t// IdleTimeout idle timeout duration, default 5 minutes\n\t// IdleTimeout 空闲超时时间，默认 5 分钟\n\tIdleTimeout time.Duration\n}\n\n// DefaultConfig returns default configuration\n// DefaultConfig 返回默认配置\nfunc DefaultConfig() Config {\n\treturn Config{\n\t\tMaxWorkers:     100,\n\t\tQueueSize:      1000,\n\t\tWarningPercent: 0.8,\n\t\tIdleTimeout:    5 * time.Minute,\n\t}\n}\n\n// taskWrapper task wrapper\n// taskWrapper 任务包装器\ntype taskWrapper struct {\n\tctx  context.Context\n\tfn   func(context.Context) error\n\tdone chan error\n}\n\n// Pool Worker Pool managing goroutine lifecycle\n// Pool 管理 goroutine 生命周期的 Worker Pool\ntype Pool struct {\n\tconfig Config\n\tlogger *zap.Logger\n\n\ttaskCh   chan taskWrapper\n\tworkerWg sync.WaitGroup\n\n\tactiveCount atomic.Int64\n\n\tctx    context.Context\n\tcancel context.CancelFunc\n\n\tmu     sync.RWMutex\n\tclosed bool\n}\n\n// New creates new Worker Pool\n// New 创建新的 Worker Pool\n// cfg: configuration, if nil use default configuration\n// cfg: 配置，如果为 nil 则使用默认配置\n// logger: zap logger, if nil use nop logger\n// logger: zap 日志器，如果为 nil 则使用 nop logger\nfunc New(cfg *Config, logger *zap.Logger) *Pool {\n\tif cfg == nil {\n\t\tdefaultCfg := DefaultConfig()\n\t\tcfg = &defaultCfg\n\t}\n\n\t// Apply default values\n\t// 应用默认值\n\tif cfg.MaxWorkers <= 0 {\n\t\tcfg.MaxWorkers = 100\n\t}\n\tif cfg.QueueSize <= 0 {\n\t\tcfg.QueueSize = 1000\n\t}\n\tif cfg.WarningPercent <= 0 || cfg.WarningPercent > 1 {\n\t\tcfg.WarningPercent = 0.8\n\t}\n\tif cfg.IdleTimeout <= 0 {\n\t\tcfg.IdleTimeout = 5 * time.Minute\n\t}\n\n\tif logger == nil {\n\t\tlogger = zap.NewNop()\n\t}\n\n\tctx, cancel := context.WithCancel(context.Background())\n\n\tp := &Pool{\n\t\tconfig: *cfg,\n\t\tlogger: logger,\n\t\ttaskCh: make(chan taskWrapper, cfg.QueueSize),\n\t\tctx:    ctx,\n\t\tcancel: cancel,\n\t\tclosed: false,\n\t}\n\n\t// Start worker goroutines\n\t// 启动 worker goroutines\n\tfor i := 0; i < cfg.MaxWorkers; i++ {\n\t\tp.workerWg.Add(1)\n\t\tgo p.worker(i)\n\t}\n\n\tp.logger.Info(\"worker pool started\",\n\t\tzap.Int(\"maxWorkers\", cfg.MaxWorkers),\n\t\tzap.Int(\"queueSize\", cfg.QueueSize),\n\t\tzap.Float64(\"warningPercent\", cfg.WarningPercent))\n\n\treturn p\n}\n\n// worker work goroutine, fetches tasks from task channel and executes them\n// worker 工作协程，从任务通道获取任务并执行\nfunc (p *Pool) worker(id int) {\n\tdefer p.workerWg.Done()\n\n\tfor {\n\t\tselect {\n\t\tcase <-p.ctx.Done():\n\t\t\treturn\n\t\tcase task, ok := <-p.taskCh:\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tp.executeTask(task)\n\t\t}\n\t}\n}\n\n// executeTask executes single task\n// executeTask 执行单个任务\nfunc (p *Pool) executeTask(task taskWrapper) {\n\tp.activeCount.Add(1)\n\tdefer p.activeCount.Add(-1)\n\n\t// Check warning threshold\n\t// 检查告警阈值\n\tp.checkWarningThreshold()\n\n\t// Execute task\n\t// 执行任务\n\tvar err error\n\tselect {\n\tcase <-task.ctx.Done():\n\t\terr = ErrTaskCancelled\n\tdefault:\n\t\terr = task.fn(task.ctx)\n\t}\n\n\t// Send results\n\t// 发送结果\n\tif task.done != nil {\n\t\tselect {\n\t\tcase task.done <- err:\n\t\tdefault:\n\t\t\t// done channel is closed or full, ignore\n\t\t\t// done channel 已关闭或已满，忽略\n\t\t}\n\t}\n}\n\n// checkWarningThreshold checks if approaching capacity\n// checkWarningThreshold 检查是否超过告警阈值\nfunc (p *Pool) checkWarningThreshold() {\n\tactive := p.activeCount.Load()\n\tthreshold := int64(float64(p.config.MaxWorkers) * p.config.WarningPercent)\n\n\tif active >= threshold {\n\t\tp.logger.Warn(\"worker pool approaching capacity\",\n\t\t\tzap.Int64(\"activeCount\", active),\n\t\t\tzap.Int(\"maxWorkers\", p.config.MaxWorkers),\n\t\t\tzap.Float64(\"usagePercent\", float64(active)/float64(p.config.MaxWorkers)*100))\n\t}\n}\n\n// Submit submits task and waits for completion\n// returns task execution result or error (pool full/closed)\n// Submit 提交任务并等待完成\n// 返回任务执行结果或错误（池满/已关闭）\nfunc (p *Pool) Submit(ctx context.Context, fn func(context.Context) error) error {\n\tp.mu.RLock()\n\tif p.closed {\n\t\tp.mu.RUnlock()\n\t\treturn ErrWorkerPoolClosed\n\t}\n\tp.mu.RUnlock()\n\n\tdone := make(chan error, 1)\n\ttask := taskWrapper{\n\t\tctx:  ctx,\n\t\tfn:   fn,\n\t\tdone: done,\n\t}\n\n\t// Try submitting task\n\t// 尝试提交任务\n\tselect {\n\tcase p.taskCh <- task:\n\t\t// Task submitted, wait for results\n\t\t// 任务已提交，等待结果\n\tdefault:\n\t\t// Queue full\n\t\t// 队列已满\n\t\treturn ErrWorkerPoolFull\n\t}\n\n\t// Wait for task completion or context cancellation\n\t// 等待任务完成或 context 取消\n\tselect {\n\tcase err := <-done:\n\t\treturn err\n\tcase <-ctx.Done():\n\t\treturn ctx.Err()\n\tcase <-p.ctx.Done():\n\t\treturn ErrWorkerPoolClosed\n\t}\n}\n\n// SubmitAsync submits task asynchronously (does not wait for results)\n// returns error if pool is full or closed\n// SubmitAsync 异步提交任务（不等待结果）\n// 返回错误如果池已满或已关闭\nfunc (p *Pool) SubmitAsync(ctx context.Context, fn func(context.Context) error) error {\n\tp.mu.RLock()\n\tif p.closed {\n\t\tp.mu.RUnlock()\n\t\treturn ErrWorkerPoolClosed\n\t}\n\tp.mu.RUnlock()\n\n\ttask := taskWrapper{\n\t\tctx:  ctx,\n\t\tfn:   fn,\n\t\tdone: nil, // Async tasks don't need done channel\n\t\t// 异步任务不需要 done channel\n\t}\n\n\t// Try submitting task\n\t// 尝试提交任务\n\tselect {\n\tcase p.taskCh <- task:\n\t\treturn nil\n\tdefault:\n\t\treturn ErrWorkerPoolFull\n\t}\n}\n\n// ActiveCount returns current active task count\n// ActiveCount 返回当前活跃任务数\nfunc (p *Pool) ActiveCount() int64 {\n\treturn p.activeCount.Load()\n}\n\n// QueuedCount returns number of tasks waiting in queue\n// QueuedCount 返回当前队列中等待的任务数\nfunc (p *Pool) QueuedCount() int {\n\treturn len(p.taskCh)\n}\n\n// IsClosed returns if Worker Pool is closed\n// IsClosed 返回 Worker Pool 是否已关闭\nfunc (p *Pool) IsClosed() bool {\n\tp.mu.RLock()\n\tdefer p.mu.RUnlock()\n\treturn p.closed\n}\n\n// Shutdown closes Worker Pool, waits for all tasks to complete\n// ctx is used to control shutdown timeout\n// Shutdown 关闭 Worker Pool，等待所有任务完成\n// ctx 用于控制关闭超时\nfunc (p *Pool) Shutdown(ctx context.Context) error {\n\tp.mu.Lock()\n\tif p.closed {\n\t\tp.mu.Unlock()\n\t\treturn nil\n\t}\n\tp.closed = true\n\tp.mu.Unlock()\n\n\tp.logger.Info(\"worker pool shutting down\",\n\t\tzap.Int64(\"activeCount\", p.activeCount.Load()),\n\t\tzap.Int(\"queuedCount\", len(p.taskCh)))\n\n\t// Close task channel, no longer accepts new tasks\n\t// 关闭任务通道，不再接受新任务\n\tclose(p.taskCh)\n\n\t// Wait for all workers to complete\n\t// 等待所有 worker 完成\n\tdone := make(chan struct{})\n\tgo func() {\n\t\tp.workerWg.Wait()\n\t\tclose(done)\n\t}()\n\n\tselect {\n\tcase <-done:\n\t\tp.logger.Info(\"worker pool shutdown completed\")\n\t\treturn nil\n\tcase <-ctx.Done():\n\t\t// Timeout, force cancel all tasks\n\t\t// 超时，强制取消所有任务\n\t\tp.cancel()\n\t\tp.logger.Warn(\"worker pool shutdown timeout, forcing cancellation\")\n\t\treturn ctx.Err()\n\t}\n}\n\n// Metrics metrics of Worker Pool\n// Metrics 返回 Worker Pool 的指标\ntype Metrics struct {\n\tMaxWorkers    int\n\tActiveCount   int64\n\tQueuedCount   int\n\tQueueCapacity int\n\tIsClosed      bool\n}\n\n// GetMetrics gets current metrics\n// GetMetrics 获取当前指标\nfunc (p *Pool) GetMetrics() Metrics {\n\tp.mu.RLock()\n\tclosed := p.closed\n\tp.mu.RUnlock()\n\n\treturn Metrics{\n\t\tMaxWorkers:    p.config.MaxWorkers,\n\t\tActiveCount:   p.activeCount.Load(),\n\t\tQueuedCount:   len(p.taskCh),\n\t\tQueueCapacity: p.config.QueueSize,\n\t\tIsClosed:      closed,\n\t}\n}\n"
  },
  {
    "path": "pkg/workerpool/pool_test.go",
    "content": "package workerpool\n\nimport (\n\t\"context\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestPool_Submit(t *testing.T) {\n\tpool := New(&Config{MaxWorkers: 2, QueueSize: 10}, nil)\n\tdefer pool.Shutdown(context.Background())\n\n\tvar counter atomic.Int32\n\n\t// Submit multiple tasks\n\tfor i := 0; i < 5; i++ {\n\t\terr := pool.Submit(context.Background(), func(ctx context.Context) error {\n\t\t\ttime.Sleep(10 * time.Millisecond) // Simulate work\n\t\t\tcounter.Add(1)\n\t\t\treturn nil\n\t\t})\n\t\tassert.NoError(t, err)\n\t}\n\n\tassert.Equal(t, int32(5), counter.Load())\n}\n\nfunc TestPool_SubmitAsync(t *testing.T) {\n\tpool := New(&Config{MaxWorkers: 2, QueueSize: 10}, nil)\n\tdefer pool.Shutdown(context.Background())\n\n\tvar counter atomic.Int32\n\n\tfor i := 0; i < 5; i++ {\n\t\terr := pool.SubmitAsync(context.Background(), func(ctx context.Context) error {\n\t\t\ttime.Sleep(10 * time.Millisecond) // Simulate work\n\t\t\tcounter.Add(1)\n\t\t\treturn nil\n\t\t})\n\t\tassert.NoError(t, err)\n\t}\n\n\t// Because it's async, we shouldn't measure instantly, so wait for all to finish\n\ttime.Sleep(100 * time.Millisecond)\n\tassert.Equal(t, int32(5), counter.Load())\n}\n\nfunc TestPool_Shutdown(t *testing.T) {\n\tpool := New(&Config{MaxWorkers: 2, QueueSize: 10}, nil)\n\t\n\terr := pool.Shutdown(context.Background())\n\tassert.NoError(t, err)\n\t\n\tassert.True(t, pool.IsClosed())\n\n\terrSubmit := pool.Submit(context.Background(), func(ctx context.Context) error { return nil })\n\tassert.Equal(t, ErrWorkerPoolClosed, errSubmit)\n}\n"
  },
  {
    "path": "pkg/writequeue/manager.go",
    "content": "// Package writequeue provides Per-User Write Queue implementation\n// Package writequeue 提供 Per-User Write Queue 实现\n// Used to serialize SQLite write operations for the same identifier to solve \"database is locked\" issue\n// 用于串行化同一标识的 SQLite 写操作，解决 \"database is locked\" 问题\npackage writequeue\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"go.uber.org/zap\"\n)\n\n// Error definitions\n// 错误定义\nvar (\n\t// ErrWriteQueueFull returned when write queue is full\n\t// ErrWriteQueueFull 当写队列已满时返回\n\tErrWriteQueueFull = errors.New(\"write queue is full\")\n\t// ErrWriteQueueClosed returned when write queue manager is closed\n\t// ErrWriteQueueClosed 当写队列管理器已关闭时返回\n\tErrWriteQueueClosed = errors.New(\"write queue is closed\")\n\t// ErrWriteTimeout returned when write operation timeout\n\t// ErrWriteTimeout 当写操作超时时返回\n\tErrWriteTimeout = errors.New(\"write operation timeout\")\n)\n\n// Config write queue configuration\n// Config 写队列配置\ntype Config struct {\n\t// QueueCapacity per-queue capacity, default 100\n\t// QueueCapacity 每队列容量，默认 100\n\tQueueCapacity int\n\t// WriteTimeout write operation timeout, default 30 seconds\n\t// WriteTimeout 写操作超时时间，默认 30 秒\n\tWriteTimeout time.Duration\n\t// IdleTimeout idle cleanup timeout, default 10 minutes\n\t// IdleTimeout 空闲清理超时时间，默认 10 分钟\n\tIdleTimeout time.Duration\n}\n\n// DefaultConfig returns default configuration\n// DefaultConfig 返回默认配置\nfunc DefaultConfig() Config {\n\treturn Config{\n\t\tQueueCapacity: 100,\n\t\tWriteTimeout:  30 * time.Second,\n\t\tIdleTimeout:   10 * time.Minute,\n\t}\n}\n\n// writeOp write operation\n// writeOp 写操作\ntype writeOp struct {\n\tctx    context.Context\n\tfn     func() error\n\tresult chan error\n}\n\n// userWriteQueue single write queue\n// userWriteQueue 单标识写队列\ntype userWriteQueue struct {\n\tkey      string\n\tch       chan writeOp\n\tlastUsed atomic.Int64\n\tclosed   atomic.Bool\n\tworkerWg sync.WaitGroup\n\n\t// Used to notify worker to stop\n\t// 用于通知 worker 停止\n\tstopCh chan struct{}\n}\n\n// Manager manages write queues for all identifiers\n// Manager 管理所有标识的写队列\ntype Manager struct {\n\tconfig Config\n\tlogger *zap.Logger\n\n\tqueues sync.Map // map[string]*userWriteQueue\n\n\tctx    context.Context\n\tcancel context.CancelFunc\n\n\tmu     sync.RWMutex\n\tclosed bool\n\n\t// Cleanup goroutine control\n\t// 清理 goroutine 控制\n\tcleanupWg   sync.WaitGroup\n\tcleanupDone chan struct{}\n}\n\n// New creates write queue manager\n// New 创建写队列管理器\n// cfg: configuration, if nil use default configuration\n// cfg: 配置，如果为 nil 则使用默认配置\n// logger: zap logger, if nil use nop logger\n// logger: zap 日志器，如果为 nil 则使用 nop logger\nfunc New(cfg *Config, logger *zap.Logger) *Manager {\n\tif cfg == nil {\n\t\tdefaultCfg := DefaultConfig()\n\t\tcfg = &defaultCfg\n\t}\n\n\t// Apply default values\n\t// 应用默认值\n\tif cfg.QueueCapacity <= 0 {\n\t\tcfg.QueueCapacity = 100\n\t}\n\tif cfg.WriteTimeout <= 0 {\n\t\tcfg.WriteTimeout = 30 * time.Second\n\t}\n\tif cfg.IdleTimeout <= 0 {\n\t\tcfg.IdleTimeout = 10 * time.Minute\n\t}\n\n\tif logger == nil {\n\t\tlogger = zap.NewNop()\n\t}\n\n\tctx, cancel := context.WithCancel(context.Background())\n\n\tm := &Manager{\n\t\tconfig:      *cfg,\n\t\tlogger:      logger,\n\t\tctx:         ctx,\n\t\tcancel:      cancel,\n\t\tclosed:      false,\n\t\tcleanupDone: make(chan struct{}),\n\t}\n\n\t// Start idle queue cleanup goroutine\n\t// 启动空闲队列清理 goroutine\n\tm.cleanupWg.Add(1)\n\tgo m.cleanupIdleQueues()\n\n\tm.logger.Info(\"write queue manager started\",\n\t\tzap.Int(\"queueCapacity\", cfg.QueueCapacity),\n\t\tzap.Duration(\"writeTimeout\", cfg.WriteTimeout),\n\t\tzap.Duration(\"idleTimeout\", cfg.IdleTimeout))\n\n\treturn m\n}\n\n// Execute executes write operation\n// Write operations will be executed serially, same identifier's write operations are processed in FIFO order\n// Execute 执行写操作\n// 写操作会被串行化执行，同一标识的写操作按 FIFO 顺序处理\nfunc (m *Manager) Execute(ctx context.Context, key string, fn func() error) error {\n\tm.mu.RLock()\n\tif m.closed {\n\t\tm.mu.RUnlock()\n\t\treturn ErrWriteQueueClosed\n\t}\n\tm.mu.RUnlock()\n\n\t// Get or create write queue\n\t// 获取或创建写队列\n\tqueue := m.getOrCreateQueue(key)\n\tif queue == nil {\n\t\treturn ErrWriteQueueClosed\n\t}\n\n\t// Create write operation\n\t// 创建写操作\n\tresult := make(chan error, 1)\n\top := writeOp{\n\t\tctx:    ctx,\n\t\tfn:     fn,\n\t\tresult: result,\n\t}\n\n\t// Try submitting to queue\n\t// 尝试提交到队列\n\tselect {\n\tcase queue.ch <- op:\n\t\t// Operation submitted\n\t\t// 操作已提交\n\tdefault:\n\t\t// Queue full\n\t\t// 队列已满\n\t\treturn ErrWriteQueueFull\n\t}\n\n\t// Wait for result or timeout\n\t// 等待结果或超时\n\ttimeout := m.config.WriteTimeout\n\tif deadline, ok := ctx.Deadline(); ok {\n\t\tremaining := time.Until(deadline)\n\t\tif remaining < timeout {\n\t\t\ttimeout = remaining\n\t\t}\n\t}\n\n\tselect {\n\tcase err := <-result:\n\t\treturn err\n\tcase <-ctx.Done():\n\t\treturn ctx.Err()\n\tcase <-time.After(timeout):\n\t\treturn ErrWriteTimeout\n\tcase <-m.ctx.Done():\n\t\treturn ErrWriteQueueClosed\n\t}\n}\n\n// getOrCreateQueue gets or creates write queue (lazy loading)\n// getOrCreateQueue 获取或创建写队列（懒加载）\nfunc (m *Manager) getOrCreateQueue(key string) *userWriteQueue {\n\t// Try to get existing queue first\n\t// 先尝试获取已存在的队列\n\tif v, ok := m.queues.Load(key); ok {\n\t\tqueue := v.(*userWriteQueue)\n\t\tif !queue.closed.Load() {\n\t\t\tqueue.lastUsed.Store(time.Now().UnixNano())\n\t\t\treturn queue\n\t\t}\n\t}\n\n\t// Check if already closed\n\t// 检查是否已关闭\n\tm.mu.RLock()\n\tif m.closed {\n\t\tm.mu.RUnlock()\n\t\treturn nil\n\t}\n\tm.mu.RUnlock()\n\n\t// Create new queue\n\t// 创建新队列\n\tqueue := &userWriteQueue{\n\t\tkey:    key,\n\t\tch:     make(chan writeOp, m.config.QueueCapacity),\n\t\tstopCh: make(chan struct{}),\n\t}\n\tqueue.lastUsed.Store(time.Now().UnixNano())\n\n\t// Use LoadOrStore to ensure only one queue is created\n\t// 使用 LoadOrStore 确保只有一个队列被创建\n\tactual, loaded := m.queues.LoadOrStore(key, queue)\n\tif loaded {\n\t\texistingQueue := actual.(*userWriteQueue)\n\t\tif !existingQueue.closed.Load() {\n\t\t\t// Existing queue, close new one\n\t\t\t// 已存在队列，关闭新创建的\n\t\t\tclose(queue.stopCh)\n\t\t\texistingQueue.lastUsed.Store(time.Now().UnixNano())\n\t\t\treturn existingQueue\n\t\t}\n\t\t// Existing queue is closed, need to replace\n\t\t// 已存在的队列已关闭，需要替换\n\t\tm.queues.Store(key, queue)\n\t}\n\n\t// Start worker goroutine (lazy loading)\n\t// 启动 worker goroutine（懒加载）\n\tqueue.workerWg.Add(1)\n\tgo m.worker(queue)\n\n\tm.logger.Debug(\"created write queue for identifier\",\n\t\tzap.String(\"key\", key),\n\t\tzap.Int(\"capacity\", m.config.QueueCapacity))\n\n\treturn queue\n}\n\n// worker worker goroutine handling single write queue\n// worker 处理单队列的 worker goroutine\nfunc (m *Manager) worker(queue *userWriteQueue) {\n\tdefer queue.workerWg.Done()\n\tdefer func() {\n\t\tqueue.closed.Store(true)\n\t\tm.logger.Debug(\"write queue worker stopped\",\n\t\t\tzap.String(\"key\", queue.key))\n\t}()\n\n\tfor {\n\t\tselect {\n\t\tcase <-m.ctx.Done():\n\t\t\t// Manager closed, handle remaining operations\n\t\t\t// 管理器关闭，处理剩余操作\n\t\t\tm.drainQueue(queue)\n\t\t\treturn\n\t\tcase <-queue.stopCh:\n\t\t\t// Queue stopped\n\t\t\t// 队列被停止\n\t\t\tm.drainQueue(queue)\n\t\t\treturn\n\t\tcase op, ok := <-queue.ch:\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tm.executeOp(queue, op)\n\t\t}\n\t}\n}\n\n// executeOp executes single write operation\n// executeOp 执行单个写操作\nfunc (m *Manager) executeOp(queue *userWriteQueue, op writeOp) {\n\tqueue.lastUsed.Store(time.Now().UnixNano())\n\n\t// Check if context is cancelled\n\t// 检查 context 是否已取消\n\tselect {\n\tcase <-op.ctx.Done():\n\t\top.result <- op.ctx.Err()\n\t\treturn\n\tdefault:\n\t}\n\n\t// Execute write operation\n\t// 执行写操作\n\terr := op.fn()\n\n\t// Send result\n\t// 发送结果\n\tselect {\n\tcase op.result <- err:\n\tdefault:\n\t\t// result channel is closed or full\n\t\t// result channel 已关闭或已满\n\t}\n}\n\n// drainQueue drains remaining operations in queue\n// drainQueue 排空队列中的剩余操作\nfunc (m *Manager) drainQueue(queue *userWriteQueue) {\n\tfor {\n\t\tselect {\n\t\tcase op, ok := <-queue.ch:\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tm.executeOp(queue, op)\n\t\tdefault:\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// cleanupIdleQueues regularly cleans up idle queues\n// cleanupIdleQueues 定期清理空闲队列\nfunc (m *Manager) cleanupIdleQueues() {\n\tdefer m.cleanupWg.Done()\n\n\tticker := time.NewTicker(m.config.IdleTimeout / 2)\n\tdefer ticker.Stop()\n\n\tfor {\n\t\tselect {\n\t\tcase <-m.ctx.Done():\n\t\t\treturn\n\t\tcase <-m.cleanupDone:\n\t\t\treturn\n\t\tcase <-ticker.C:\n\t\t\tm.doCleanup()\n\t\t}\n\t}\n}\n\n// doCleanup performs one cleanup\n// doCleanup 执行一次清理\nfunc (m *Manager) doCleanup() {\n\tnow := time.Now().UnixNano()\n\tidleThreshold := m.config.IdleTimeout.Nanoseconds()\n\n\tm.queues.Range(func(keyObj, value interface{}) bool {\n\t\tkey := keyObj.(string)\n\t\tqueue := value.(*userWriteQueue)\n\n\t\t// Check if idle timeout\n\t\t// 检查是否空闲超时\n\t\tlastUsed := queue.lastUsed.Load()\n\t\tif now-lastUsed > idleThreshold {\n\t\t\t// Check if queue is empty\n\t\t\t// 检查队列是否为空\n\t\t\tif len(queue.ch) == 0 && !queue.closed.Load() {\n\t\t\t\tm.logger.Debug(\"cleaning up idle write queue\",\n\t\t\t\t\tzap.String(\"key\", key),\n\t\t\t\t\tzap.Duration(\"idleTime\", time.Duration(now-lastUsed)))\n\n\t\t\t\t// Mark closed and notify worker to stop\n\t\t\t\t// 标记关闭并通知 worker 停止\n\t\t\t\tqueue.closed.Store(true)\n\t\t\t\tclose(queue.stopCh)\n\n\t\t\t\t// Delete from map\n\t\t\t\t// 从 map 中删除\n\t\t\t\tm.queues.Delete(key)\n\t\t\t}\n\t\t}\n\t\treturn true\n\t})\n}\n\n// Shutdown closes write queue manager, waits for all operations to complete\n// ctx is used to control shutdown timeout\n// Shutdown 关闭写队列管理器，等待所有操作完成\n// ctx 用于控制关闭超时\nfunc (m *Manager) Shutdown(ctx context.Context) error {\n\tm.mu.Lock()\n\tif m.closed {\n\t\tm.mu.Unlock()\n\t\treturn nil\n\t}\n\tm.closed = true\n\tm.mu.Unlock()\n\n\tm.logger.Info(\"write queue manager shutting down\")\n\n\t// Stop cleanup goroutine\n\t// 停止清理 goroutine\n\tclose(m.cleanupDone)\n\n\t// Wait for all workers to complete\n\t// 等待所有队列的 worker 完成\n\tdone := make(chan struct{})\n\tgo func() {\n\t\t// Notify all queues to stop\n\t\t// 通知所有队列停止\n\t\tm.queues.Range(func(key, value interface{}) bool {\n\t\t\tqueue := value.(*userWriteQueue)\n\t\t\tif !queue.closed.Load() {\n\t\t\t\tqueue.closed.Store(true)\n\t\t\t\tselect {\n\t\t\t\tcase <-queue.stopCh:\n\t\t\t\t\t// Already closed\n\t\t\t\t\t// 已关闭\n\t\t\t\tdefault:\n\t\t\t\t\tclose(queue.stopCh)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\n\t\t// Wait for all workers to complete\n\t\t// 等待所有 worker 完成\n\t\tm.queues.Range(func(key, value interface{}) bool {\n\t\t\tqueue := value.(*userWriteQueue)\n\t\t\tqueue.workerWg.Wait()\n\t\t\treturn true\n\t\t})\n\n\t\t// Wait for cleanup goroutine to complete\n\t\t// 等待清理 goroutine 完成\n\t\tm.cleanupWg.Wait()\n\n\t\tclose(done)\n\t}()\n\n\tselect {\n\tcase <-done:\n\t\tm.logger.Info(\"write queue manager shutdown completed\")\n\t\tm.cancel()\n\t\treturn nil\n\tcase <-ctx.Done():\n\t\tm.logger.Warn(\"write queue manager shutdown timeout, forcing cancellation\")\n\t\tm.cancel()\n\t\treturn ctx.Err()\n\t}\n}\n\n// QueueCount returns current active queue count\n// QueueCount 返回当前活跃队列数量\nfunc (m *Manager) QueueCount() int {\n\tcount := 0\n\tm.queues.Range(func(key, value interface{}) bool {\n\t\tqueue := value.(*userWriteQueue)\n\t\tif !queue.closed.Load() {\n\t\t\tcount++\n\t\t}\n\t\treturn true\n\t})\n\treturn count\n}\n\n// QueuedCount returns number of operations waiting in specific queue\n// QueuedCount 返回指定队列中等待的操作数\nfunc (m *Manager) QueuedCount(key string) int {\n\tif v, ok := m.queues.Load(key); ok {\n\t\tqueue := v.(*userWriteQueue)\n\t\treturn len(queue.ch)\n\t}\n\treturn 0\n}\n\n// IsClosed returns if manager is closed\n// IsClosed 返回管理器是否已关闭\nfunc (m *Manager) IsClosed() bool {\n\tm.mu.RLock()\n\tdefer m.mu.RUnlock()\n\treturn m.closed\n}\n\n// Metrics write queue manager metrics\n// Metrics 写队列管理器指标\ntype Metrics struct {\n\tQueueCapacity int\n\tActiveQueues  int\n\tIsClosed      bool\n}\n\n// GetMetrics gets current metrics\n// GetMetrics 获取当前指标\nfunc (m *Manager) GetMetrics() Metrics {\n\tm.mu.RLock()\n\tclosed := m.closed\n\tm.mu.RUnlock()\n\n\treturn Metrics{\n\t\tQueueCapacity: m.config.QueueCapacity,\n\t\tActiveQueues:  m.QueueCount(),\n\t\tIsClosed:      closed,\n\t}\n}\n"
  },
  {
    "path": "pkg/writequeue/manager_test.go",
    "content": "package writequeue\n\nimport (\n\t\"context\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestManager_ExecuteSequential(t *testing.T) {\n\tmanager := New(&Config{QueueCapacity: 10, WriteTimeout: time.Second}, nil)\n\tdefer manager.Shutdown(context.Background())\n\n\tvar counter atomic.Int32\n\tvar sequentialCheck []int32\n\n\t// We submit 3 write jobs for the SAME identifier\n\t// Since write queue is per-user, they MUST execute one after another in FIFO\n\terr1 := manager.Execute(context.Background(), \"user-1\", func() error {\n\t\tsequentialCheck = append(sequentialCheck, counter.Add(1))\n\t\treturn nil\n\t})\n\tassert.NoError(t, err1)\n\n\terr2 := manager.Execute(context.Background(), \"user-1\", func() error {\n\t\tsequentialCheck = append(sequentialCheck, counter.Add(1))\n\t\treturn nil\n\t})\n\tassert.NoError(t, err2)\n\n\tassert.Equal(t, []int32{1, 2}, sequentialCheck)\n}\n\nfunc TestManager_MultipleUsers(t *testing.T) {\n\tmanager := New(&Config{QueueCapacity: 10, WriteTimeout: time.Second}, nil)\n\tdefer manager.Shutdown(context.Background())\n\n\tch := make(chan bool, 2)\n\t\n\t// They don't block each other\n\tgo func() {\n\t\tmanager.Execute(context.Background(), \"user-1\", func() error {\n\t\t\ttime.Sleep(50 * time.Millisecond)\n\t\t\treturn nil\n\t\t})\n\t\tch <- true\n\t}()\n\n\tgo func() {\n\t\tmanager.Execute(context.Background(), \"user-2\", func() error {\n\t\t\ttime.Sleep(10 * time.Millisecond)\n\t\t\treturn nil\n\t\t})\n\t\tch <- true\n\t}()\n\n\t<-ch\n\t<-ch\n\t// It should reach here cleanly\n}\n\nfunc TestManager_Shutdown(t *testing.T) {\n\tmanager := New(&Config{QueueCapacity: 10, WriteTimeout: time.Second}, nil)\n\t\n\tmanager.Shutdown(context.Background())\n\tassert.True(t, manager.IsClosed())\n\n\terr := manager.Execute(context.Background(), \"user-1\", func() error { return nil })\n\tassert.Equal(t, ErrWriteQueueClosed, err)\n}\n"
  },
  {
    "path": "scripts/.air.toml",
    "content": "root = \".\"\ntmp_dir = \"build\"\n\n\n[build]\ncmd = \"mkdir -p build && ps aux | grep -E 'build/fns_server|go build.*fns_server' | grep -v grep | awk '{print $2}' | xargs -r kill -9 && go build -o ./build/fns_server ./main.go\"\npost_cmd = [ ]\nbin = \"./build/fns_server run\"\nentrypoint = [ \"./build/fns_server\" ]\nargs_bin = [ \"run\" ]\nfull_bin = \"\"\ninclude_ext = [ \"go\", \"tpl\", \"tmpl\", \"html\" ]\nexclude_dir = [ \"assets\", \"tmp\", \"storage\", \"vendor\" ]\ninclude_dir = [ ]\ninclude_file = [ ]\nexclude_file = [ ]\nexclude_regex = [ \"_test\\\\.go\" ]\nexclude_unchanged = true\nfollow_symlink = false\nlog = \"air.log\"\npoll = true\npoll_interval = 1_500\ndelay = 1_000\nstop_on_error = true\nsend_interrupt = true\nkill_delay = 500\nrerun = false\nrerun_delay = 500\n\n\n[log]\ntime = false\nmain_only = false\nsilent = false\n\n\n[color]\nmain = \"magenta\"\nwatcher = \"cyan\"\nbuild = \"yellow\"\nrunner = \"green\"\n\n\n[misc]\nclean_on_exit = true\n\n\n[screen]\nclear_on_rebuild = false\nkeep_scroll = true\n\n\n[proxy]\nenabled = false\nproxy_port = 8_090\napp_port = 8_080\n"
  },
  {
    "path": "scripts/.env",
    "content": "RUNTIME_ENVIROMENT=DEVELOPMENT"
  },
  {
    "path": "scripts/db.sql",
    "content": "# sqlite3\nPRAGMA foreign_keys = false;\n\n-- ----------------------------\n-- Table structure for pre_user\n-- ----------------------------\nDROP TABLE IF EXISTS \"user\";\n\nCREATE TABLE \"user\" (\n    `uid` integer PRIMARY KEY AUTOINCREMENT,\n    `email` text DEFAULT \"\",\n    `username` text DEFAULT \"\",\n    `password` text DEFAULT \"\",\n    `salt` text DEFAULT \"\",\n    `token` text DEFAULT \"\",\n    `avatar` text DEFAULT \"\",\n    `is_deleted` integer DEFAULT 0,\n    `updated_at` datetime DEFAULT NULL,\n    `created_at` datetime DEFAULT NULL,\n    `deleted_at` datetime DEFAULT NULL\n);\n\nCREATE INDEX `idx_pre_user_email` ON \"user\"(`email`);\n\nDROP TABLE IF EXISTS \"vault\";\n\nCREATE TABLE \"vault\" (\n    \"id\" integer PRIMARY KEY AUTOINCREMENT,\n    \"vault\" text DEFAULT '',\n    \"note_count\" integer DEFAULT 0,\n    \"note_size\" integer DEFAULT 0,\n    \"file_count\" integer DEFAULT 0,\n    \"file_size\" integer DEFAULT 0,\n    \"is_deleted\" integer DEFAULT 0,\n    \"created_at\" datetime DEFAULT NULL,\n    \"updated_at\" datetime DEFAULT NULL\n);\n\nCREATE INDEX \"idx_vault_uid\" ON \"vault\" (\"vault\" ASC);\n\nDROP TABLE IF EXISTS \"note\";\n\nCREATE TABLE \"note\" (\n    \"id\" integer PRIMARY KEY AUTOINCREMENT,\n    \"vault_id\" integer NOT NULL DEFAULT 0,\n    \"action\" text DEFAULT '',\n    \"rename\" integer DEFAULT 0,\n    \"fid\" integer DEFAULT 0,\n    -- note table : parent folder id, 0 : root\n    \"path\" text DEFAULT '',\n    \"path_hash\" text DEFAULT '',\n    \"content\" text DEFAULT '',\n    \"content_hash\" text DEFAULT '',\n    \"content_last_snapshot\" text NOT NULL DEFAULT '',\n    \"content_last_snapshot_hash\" text NOT NULL DEFAULT '',\n    \"version\" integer DEFAULT 0,\n    \"client_name\" text NOT NULL DEFAULT '',\n    \"client_type\" text NOT NULL DEFAULT '',\n    \"client_version\" text NOT NULL DEFAULT '',\n    \"size\" integer DEFAULT 0,\n    \"ctime\" integer DEFAULT 0,\n    \"mtime\" integer DEFAULT 0,\n    \"updated_timestamp\" integer DEFAULT 0,\n    \"created_at\" datetime DEFAULT NULL,\n    \"updated_at\" datetime DEFAULT NULL\n);\n\nCREATE INDEX \"idx_vault_id_action_fid\" ON \"note\" (\"vault_id\", \"action\", \"fid\" DESC);\n\nCREATE INDEX \"idx_vault_id_action_rename\" ON \"note\" (\"vault_id\", \"action\", \"rename\" DESC);\n\nCREATE INDEX \"idx_vault_id_rename\" ON \"note\" (\"vault_id\", \"rename\" DESC);\n\nCREATE INDEX \"idx_vault_id_updated_at\" ON \"note\" (\"vault_id\", \"updated_at\" DESC);\n\nCREATE INDEX \"idx_vault_id_updated_timestamp\" ON \"note\" (\"vault_id\", \"updated_timestamp\" DESC);\n\nCREATE INDEX `idx_vault_id_path` ON `note`(`vault_id`, `path`);\n\nDROP TABLE IF EXISTS \"note_history\";\n\nCREATE TABLE \"note_history\" (\n    \"id\" integer PRIMARY KEY AUTOINCREMENT,\n    \"note_id\" integer NOT NULL DEFAULT 0,\n    \"vault_id\" integer NOT NULL DEFAULT 0,\n    \"path\" text DEFAULT '',\n    \"content\" text DEFAULT '',\n    \"content_hash\" text NOT NULL DEFAULT '',\n    \"diff_patch\" text DEFAULT '',\n    \"client_name\" text DEFAULT '',\n    \"client_type\" text DEFAULT '',\n    \"client_version\" text DEFAULT '',\n    \"version\" integer DEFAULT 0,\n    \"created_at\" datetime DEFAULT NULL,\n    \"updated_at\" datetime DEFAULT NULL\n);\n\nCREATE INDEX \"idx_note_history_note_id\" ON \"note_history\" (\"note_id\");\n\nCREATE INDEX \"idx_note_history_version\" ON \"note_history\" (\"note_id\", \"version\");\n\nCREATE INDEX \"idx_note_history_content_hash\" ON \"note_history\" (\"note_id\", \"content_hash\");\n\nDROP TABLE IF EXISTS \"file\";\n\nCREATE TABLE \"file\" (\n    \"id\" integer PRIMARY KEY AUTOINCREMENT,\n    \"vault_id\" integer NOT NULL DEFAULT 0,\n    \"action\" text DEFAULT '',\n    \"fid\" integer DEFAULT 0,\n    -- folder table : parent folder id, 0 : root\n    \"path\" text DEFAULT '',\n    \"path_hash\" text DEFAULT '',\n    \"content_hash\" text DEFAULT '',\n    \"save_path\" text DEFAULT '',\n    \"rename\" integer DEFAULT 0,\n    \"size\" integer NOT NULL DEFAULT 0,\n    \"ctime\" integer NOT NULL DEFAULT 0,\n    \"mtime\" integer NOT NULL DEFAULT 0,\n    \"updated_timestamp\" integer NOT NULL DEFAULT 0,\n    \"created_at\" datetime DEFAULT NULL,\n    \"updated_at\" datetime DEFAULT NULL\n);\n\nCREATE INDEX \"idx_file_vault_id_action_fid\" ON \"file\" (\"vault_id\", \"action\", \"fid\" DESC);\n\nCREATE INDEX \"idx_file_vault_id_path_hash\" ON \"file\" (\"vault_id\", \"path_hash\" DESC);\n\nCREATE INDEX \"idx_file_vault_id_action_rename\" ON \"file\" (\"vault_id\", \"action\", \"rename\" DESC);\n\nCREATE INDEX \"idx_file_vault_id_rename\" ON \"file\" (\"vault_id\", \"rename\" DESC);\n\n\nCREATE INDEX \"idx_file_vault_id_updated_at\" ON \"file\" (\"vault_id\", \"updated_at\" DESC);\n\nCREATE INDEX \"idx_file_vault_id_updated_timestamp\" ON \"file\" (\"vault_id\", \"updated_timestamp\" DESC);\n\nCREATE INDEX `idx_file_vault_id_path` ON `file`(`vault_id`, `path`);\n\nDROP TABLE IF EXISTS \"setting\";\n\nCREATE TABLE \"setting\" (\n    \"id\" integer PRIMARY KEY AUTOINCREMENT,\n    \"vault_id\" integer NOT NULL DEFAULT 0,\n    \"action\" text DEFAULT '',\n    \"rename\" integer DEFAULT 0,\n    \"path\" text DEFAULT '',\n    \"path_hash\" text DEFAULT '',\n    \"content\" text DEFAULT '',\n    \"content_hash\" text DEFAULT '',\n    \"size\" integer DEFAULT 0,\n    \"ctime\" integer DEFAULT 0,\n    \"mtime\" integer DEFAULT 0,\n    \"updated_timestamp\" integer DEFAULT 0,\n    \"created_at\" datetime DEFAULT NULL,\n    \"updated_at\" datetime DEFAULT NULL\n);\n\nCREATE INDEX \"idx_setting_id_path_hash\" ON \"setting\" (\"id\", \"path_hash\" DESC);\n\nCREATE INDEX \"idx_setting_id_updated_at\" ON \"setting\" (\"id\", \"updated_at\" DESC);\n\nCREATE INDEX \"idx_setting_id_updated_timestamp\" ON \"setting\" (\"id\", \"updated_timestamp\" DESC);\n\nCREATE INDEX `idx_setting_id_path` ON `setting`(`id`, `path`);\n\nDROP TABLE IF EXISTS \"user_share\";\n\nCREATE TABLE \"user_share\" (\n    \"id\" integer PRIMARY KEY AUTOINCREMENT,\n    \"uid\" integer NOT NULL DEFAULT 0,\n    \"res_type\" text NOT NULL DEFAULT '',\n    -- 资源类型: note, file\n    \"res_id\" integer NOT NULL DEFAULT 0,\n    -- 资源ID\n    \"res\" text NOT NULL DEFAULT '',\n    -- 资源列表 (JSON: {\"note\":[\"id1\"],\"file\":[\"id2\"]})\n    \"status\" integer DEFAULT 1,\n    -- 1-有效, 2-已撤销\n    \"view_count\" integer DEFAULT 0,\n    -- 访问次数\n    \"last_viewed_at\" datetime DEFAULT NULL,\n    \"expires_at\" datetime DEFAULT NULL,\n    \"password\" text DEFAULT '',\n    \"short_link\" text DEFAULT '',\n    \"created_at\" datetime DEFAULT NULL,\n    \"updated_at\" datetime DEFAULT NULL\n);\n\nCREATE INDEX \"idx_user_share_uid\" ON \"user_share\" (\"uid\");\n\nCREATE INDEX \"idx_user_share_res_type_id\" ON \"user_share\" (\"res_type\", \"res_id\");\n\n-- ----------------------------\n-- Table structure for note_link\n-- ----------------------------\nDROP TABLE IF EXISTS \"note_link\";\n\nCREATE TABLE \"note_link\" (\n    \"id\" integer PRIMARY KEY AUTOINCREMENT,\n    \"source_note_id\" integer NOT NULL,\n    \"target_path\" text NOT NULL,\n    \"target_path_hash\" text NOT NULL,\n    \"link_text\" text,\n    \"is_embed\" integer DEFAULT 0,\n    \"vault_id\" integer NOT NULL,\n    \"uid\" integer NOT NULL,\n    \"created_at\" datetime DEFAULT NULL\n);\n\nCREATE INDEX \"idx_source_note\" ON \"note_link\" (\"source_note_id\");\n\nCREATE INDEX \"idx_target_path_hash\" ON \"note_link\" (\"target_path_hash\", \"vault_id\", \"uid\");\n\nDROP TABLE IF EXISTS \"folder\";\n\nCREATE TABLE \"folder\" (\n    \"id\" integer PRIMARY KEY AUTOINCREMENT,\n    \"vault_id\" integer NOT NULL DEFAULT 0,\n    \"action\" text DEFAULT '',\n    \"path\" text DEFAULT '',\n    \"path_hash\" text DEFAULT '',\n    \"level\" integer DEFAULT 0,\n    -- 文件夹层级\n    \"fid\" integer DEFAULT 0,\n    -- 父级文件夹ID,0 为根目录\n    \"ctime\" integer NOT NULL DEFAULT 0,\n    \"mtime\" integer NOT NULL DEFAULT 0,\n    \"updated_timestamp\" integer NOT NULL DEFAULT 0,\n    \"created_at\" datetime DEFAULT NULL,\n    \"updated_at\" datetime DEFAULT NULL\n);\n\nCREATE INDEX \"idx_folder_vault_id_path_hash\" ON \"folder\" (\"vault_id\", \"path_hash\");\n\nCREATE INDEX `idx_folder_vault_id_path` ON `folder`(`vault_id`, `path`);\n\nCREATE INDEX \"idx_folder_vault_id_fid_path\" ON \"folder\" (\"vault_id\", \"fid\", \"path\");\n\nCREATE INDEX \"idx_folder_vault_id_level_path\" ON \"folder\" (\"vault_id\", \"level\", \"path\");\n\nCREATE INDEX \"idx_folder_vault_id_updated_timestamp\" ON \"folder\" (\"vault_id\", \"updated_timestamp\" DESC);\n\nDROP TABLE IF EXISTS \"storage\";\n\nCREATE TABLE \"storage\" (\n    \"id\" integer NOT NULL PRIMARY KEY AUTOINCREMENT,\n    \"uid\" integer NOT NULL DEFAULT 0,\n    \"type\" text DEFAULT '',\n    \"endpoint\" text DEFAULT '',\n    \"region\" text DEFAULT '',\n    \"account_id\" text DEFAULT '',\n    \"bucket_name\" text DEFAULT '',\n    \"access_key_id\" text DEFAULT '',\n    \"access_key_secret\" text DEFAULT '',\n    \"custom_path\" text DEFAULT '',\n    \"access_url_prefix\" text DEFAULT '',\n    \"user\" text DEFAULT '',\n    \"password\" text DEFAULT '',\n    \"is_enabled\" integer NOT NULL DEFAULT 0,\n    \"is_deleted\" integer NOT NULL DEFAULT 0,\n    \"created_at\" datetime DEFAULT NULL,\n    \"updated_at\" datetime DEFAULT NULL,\n    \"deleted_at\" datetime DEFAULT NULL\n);\n\nCREATE INDEX \"idx_storage_uid\" ON \"storage\" (\"uid\" DESC);\n\n-- ----------------------------\n-- Table structure for backup_config\n-- ----------------------------\nDROP TABLE IF EXISTS \"backup_config\";\n\nCREATE TABLE \"backup_config\" (\n    \"id\" integer PRIMARY KEY AUTOINCREMENT,\n    \"uid\" integer NOT NULL DEFAULT 0,\n    \"vault_id\" integer NOT NULL DEFAULT 0,\n    \"type\" text DEFAULT '',\n    -- full, incremental, sync\n    \"storage_ids\" text DEFAULT '',\n    -- JSON array of storage ids: [1, 2]\n    \"is_enabled\" integer DEFAULT 0,\n    \"cron_strategy\" text DEFAULT '',\n    -- daily, weekly, monthly, custom\n    \"cron_expression\" text DEFAULT '',\n    \"include_vault_name\" integer DEFAULT 0,\n    -- Whether to include vault name in backup file name\n    \"retention_days\" integer DEFAULT 10,\n    -- Retention policy (days)\n    \"last_run_time\" datetime DEFAULT NULL,\n    \"next_run_time\" datetime DEFAULT NULL,\n    \"last_status\" integer DEFAULT 0,\n    -- 0: Idle, 1: Running, 2: Success, 3: Failed, 4: Stopped, 5: Success but no update\n    \"last_message\" text DEFAULT '',\n    \"created_at\" datetime DEFAULT NULL,\n    \"updated_at\" datetime DEFAULT NULL\n);\n\nCREATE INDEX \"idx_backup_config_uid\" ON \"backup_config\" (\"uid\");\n\nCREATE INDEX \"idx_backup_config_next_run_time\" ON \"backup_config\" (\"next_run_time\");\n\n-- ----------------------------\n-- Table structure for backup_history\n-- ----------------------------\nDROP TABLE IF EXISTS \"backup_history\";\n\nCREATE TABLE \"backup_history\" (\n    \"id\" integer PRIMARY KEY AUTOINCREMENT,\n    \"uid\" integer NOT NULL DEFAULT 0,\n    \"config_id\" integer NOT NULL DEFAULT 0,\n    \"storage_id\" integer NOT NULL DEFAULT 0,\n    \"type\" text DEFAULT '',\n    -- full, incremental, sync\n    \"start_time\" datetime DEFAULT NULL,\n    \"end_time\" datetime DEFAULT NULL,\n    \"status\" integer DEFAULT 0,\n    -- 0: Idle, 1: Running, 2: Success, 3: Failed, 4: Stopped, 5: Success but no update\n    \"file_size\" integer DEFAULT 0,\n    -- bytes\n    \"file_count\" integer DEFAULT 0,\n    \"message\" text DEFAULT '',\n    \"file_path\" text DEFAULT '',\n    -- remote path/key\n    \"created_at\" datetime DEFAULT NULL,\n    \"updated_at\" datetime DEFAULT NULL\n);\n\nCREATE INDEX \"idx_backup_history_uid\" ON \"backup_history\" (\"uid\", \"created_at\" DESC);\n\nCREATE INDEX \"idx_backup_history_config_id\" ON \"backup_history\" (\"config_id\");\n\n-- ----------------------------\n-- Table structure for git_sync_config\n-- ----------------------------\nDROP TABLE IF EXISTS \"git_sync_config\";\n\nCREATE TABLE \"git_sync_config\" (\n    \"id\" integer PRIMARY KEY AUTOINCREMENT,\n    \"uid\" integer NOT NULL DEFAULT 0,\n    \"vault_id\" integer NOT NULL DEFAULT 0,\n    \"repo_url\" text DEFAULT '',\n    -- Git 仓库地址, 例如 https://github.com/user/repo.git\n    \"username\" text DEFAULT '',\n    -- 认证用户名\n    \"password\" text DEFAULT '',\n    -- 认证密码或 Personal Access Token\n    \"branch\" text DEFAULT 'main',\n    -- 分支名\n    \"is_enabled\" integer DEFAULT 0,\n    -- 是否启用自动同步\n    \"delay\" integer DEFAULT 0,\n    -- 延迟时间（例如同步前等待的时间，单位可以是秒或分钟）\n    \"retention_days\" integer DEFAULT 0,\n    -- 历史记录保留天数, 0: 不清理, -1: 仅保留最新, >0: 保留天数\n    \"last_sync_time\" datetime DEFAULT NULL,\n    -- 上次同步时间\n    \"last_status\" integer DEFAULT 0,\n    -- 0: 闲置, 1: 运行中, 2: 成功, 3: 失败\n    \"last_message\" text DEFAULT '',\n    -- 同步结果或错误信息\n    \"created_at\" datetime DEFAULT NULL,\n    \"updated_at\" datetime DEFAULT NULL\n);\n\nCREATE INDEX \"idx_git_sync_config_uid\" ON \"git_sync_config\" (\"uid\");\n\n-- ----------------------------\n-- Table structure for git_sync_history\n-- ----------------------------\nDROP TABLE IF EXISTS \"git_sync_history\";\n\nCREATE TABLE \"git_sync_history\" (\n    \"id\" integer PRIMARY KEY AUTOINCREMENT,\n    \"uid\" integer NOT NULL DEFAULT 0,\n    \"config_id\" integer NOT NULL DEFAULT 0,\n    \"start_time\" datetime DEFAULT NULL,\n    \"end_time\" datetime DEFAULT NULL,\n    \"status\" integer DEFAULT 0,\n    -- 0: Idle, 1: Running, 2: Success, 3: Failed, 4: Shutdown\n    \"message\" text DEFAULT '',\n    \"created_at\" datetime DEFAULT NULL,\n    \"updated_at\" datetime DEFAULT NULL\n);\n\nCREATE INDEX \"idx_git_sync_history_uid\" ON \"git_sync_history\" (\"uid\", \"created_at\" DESC);\n\nCREATE INDEX \"idx_git_sync_history_config_id\" ON \"git_sync_history\" (\"config_id\");\n\n-- ----------------------------\n-- Table structure for sync_log\n-- ----------------------------\nDROP TABLE IF EXISTS \"sync_log\";\n\nCREATE TABLE \"sync_log\" (\n    \"id\"             integer PRIMARY KEY AUTOINCREMENT,\n    \"uid\"            integer NOT NULL DEFAULT 0,\n    \"vault_id\"       integer NOT NULL DEFAULT 0,\n    \"type\"           text NOT NULL DEFAULT '',  -- 'note', 'file', 'setting', 'folder'\n    \"action\"         text NOT NULL DEFAULT '',  -- 'create', 'modify', 'soft_delete', 'delete', 'rename', 'restore'\n    \"changed_fields\" text NOT NULL DEFAULT '',  -- 逗号分隔变更字段，如 'content,mtime' / 'mtime' / 'path'\n    \"path\"           text DEFAULT '',\n    \"path_hash\"      text DEFAULT '',\n    \"size\"           integer DEFAULT 0,\n    \"client_name\"    text DEFAULT '',\n    \"client_type\"    text DEFAULT '',\n    \"client_version\" text DEFAULT '',\n    \"status\"         integer DEFAULT 1,         -- 1: success, 2: failed\n    \"message\"        text DEFAULT '',\n    \"created_at\"     datetime DEFAULT NULL\n);\n\nCREATE INDEX \"idx_sync_log_uid_created_at\"  ON \"sync_log\" (\"uid\", \"created_at\" DESC);\nCREATE INDEX \"idx_sync_log_uid_type_action\" ON \"sync_log\" (\"uid\", \"type\", \"action\");"
  },
  {
    "path": "scripts/docker-composer/docker-compose.yaml",
    "content": "services:\n  fast-note-sync-service:\n    image: haierkeys/fast-note-sync-service:latest\n    container_name: fast-note-sync-service\n    ports:\n      - \"9000:9000\"\n    volumes:\n      - /data/fast-note-sync/storage/:/fast-note-sync/storage/\n      - /data/fast-note-sync/config/:/fast-note-sync/config/\n    networks:\n      - app-network  # 与 image-api 在同一网络1\n\n  nginx:\n    image: nginx:alpine\n    container_name: nginx\n    restart: always\n    ports:\n      - \"443:443\"  # 映射宿主机的 8000 端口到容器的 443\n    depends_on:\n      - fast-note-sync-service  # 替代 --link，确保 Nginx 在 image-api 启动后运行\n     # - better-sync # 替代 --link，确保 Nginx 在 image-api 启动后运行\n    volumes:\n      - /data/ssl/diybeta-com.key:/privkey.pem  # SSL 私钥\n      - /data/ssl/diybeta-com.pem:/cert.pem     # SSL 证书\n      - /data/nginx/site.conf:/etc/nginx/conf.d/site.conf  # Nginx 配置文件\n    networks:\n      - app-network  # 与 image-api 在同一网络\n    extra_hosts:\n      - \"host.docker.internal:172.17.0.1\"\n\nnetworks:\n  app-network:\n    driver: bridge  # 定义网络，确保服务间通信\n"
  },
  {
    "path": "scripts/docker-composer/docker-re.sh",
    "content": "#!/usr/bin/env bash\n\ndocker compose -f /data/docker-compose.yaml down\n\ndocker compose -f /data/docker-compose.yaml pull\n\ndocker compose -f /data/docker-compose.yaml up -d\n"
  },
  {
    "path": "scripts/docker-composer/nginx/site.conf",
    "content": "\nserver {\n        listen       443 ssl;\n        listen  [::]:443 ssl;\n        # server_name localhost;\n\n        client_max_body_size 50m;\n        proxy_max_temp_file_size 0;\n        proxy_buffering off;\n\n        ssl_certificate      /cert.pem;\n        ssl_certificate_key  /privkey.pem;\n        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;\n        ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;\n        ssl_prefer_server_ciphers on;\n\n        location / {\n                proxy_pass http://fast-note-sync-service:9000;\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        location /api/user/sync {\n                proxy_pass http://fast-note-sync-service:9000/api/user/sync;\n                proxy_http_version 1.1;\n                proxy_set_header Upgrade $http_upgrade;\n                proxy_set_header Connection \"upgrade\";\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"
  },
  {
    "path": "scripts/gen_support_md.js",
    "content": "const fs = require('fs');\nconst path = require('path');\nconst { execSync } = require('child_process');\n\nconst inputFile = path.join(__dirname, '..', 'docs', 'Support.csv');\n\n// i18n 配置\nconst i18n = {\n    'zh-CN': {\n        filename: 'Support.zh-CN.md',\n        title: '支持者名单 (Thanks to Supporters)',\n        quote: '非常感谢大家对本项目的支持！每一份打赏都是我持续维护和迭代的动力。 ❤️',\n        listTitle: '致谢列表',\n        headers: ['收款时间', '收款项', '金额', '昵称', '留言'],\n        footer: '本数据最后更新于：',\n        langName: '简体中文',\n        target: 'zh-CN'\n    },\n    'zh-TW': {\n        filename: 'Support.zh-TW.md',\n        title: '支持者名單 (Thanks to Supporters)',\n        quote: '非常感謝大家對本項目的支持！每一份打賞都是我持續維護和迭代的動力。 ❤️',\n        listTitle: '致謝列表',\n        headers: ['收款時間', '收款項', '金額', '昵稱', '留言', '備註'],\n        footer: '本數據最後更新於：',\n        langName: '繁體中文',\n        target: 'zh-TW'\n    },\n    'en': {\n        filename: 'Support.en.md',\n        title: 'Supporters List',\n        quote: 'Thank you very much for supporting this project! Every donation is the driving force for my continuous maintenance and iteration. ❤️',\n        listTitle: 'Acknowledgement List',\n        headers: ['Time', 'Item', 'Amount', 'Name', 'Message'],\n        footer: 'Last updated on: ',\n        langName: 'English',\n        target: 'en'\n    },\n    'ja': {\n        filename: 'Support.ja.md',\n        title: 'サポーターリスト',\n        quote: 'このプロジェクトを応援していただき、誠にありがとうございます！皆様からのご支援は、継続的なメンテナンスと開発の原動力となっています。 ❤️',\n        listTitle: '謝辞リスト',\n        headers: ['受領时间', '项目', '金额', '昵称', 'メッセージ'],\n        footer: '最終更新日：',\n        langName: '日本語',\n        target: 'ja'\n    },\n    'ko': {\n        filename: 'Support.ko.md',\n        title: '후원자 명단',\n        quote: '이 프로젝트를 지원해 주셔서 정말 감사합니다! 여러분의 모든 후원은 지속적인 유지보수와 개발의 원동력이 됩니다. ❤️',\n        listTitle: '감사 명단',\n        headers: ['수령 시간', '항목', '금액', '닉네임', '메시지'],\n        footer: '마지막 업데이트:',\n        langName: '한국어',\n        target: 'ko'\n    }\n};\n\n/**\n * 批量翻译函数 - 调用独立的 Python 翻译辅助脚本\n */\nfunction translateBatch(texts, targetLang) {\n    if (texts.length === 0 || targetLang === 'zh-CN') {\n        const result = {};\n        texts.forEach(t => result[t] = t);\n        return result;\n    }\n\n    const tmpIn = path.join(__dirname, '..', `tmp_translate_in_${targetLang}.json`);\n    const helperScript = path.join(__dirname, 'translate_support.py');\n    const { spawnSync } = require('child_process');\n    try {\n        // 1. 将待翻译文本写入临时文件 (避免 Shell 转义问题)\n        fs.writeFileSync(tmpIn, JSON.stringify(texts), 'utf8');\n\n        // 2. 调用 Python 脚本\n        // targetLang 已经是 i18n 配置中的 target 值，无需额外转换\n        const target = targetLang;\n\n        // 使用 spawnSync 避免 shell 转义和路径空格问题\n        const result = spawnSync('python3', [helperScript, tmpIn, target], {\n            encoding: 'utf8',\n            maxBuffer: 10 * 1024 * 1024 // 增加缓冲区到 10MB\n        });\n\n        if (result.status !== 0) {\n            throw new Error(`Python script exited with status ${result.status}: ${result.stderr}`);\n        }\n\n        const translatedArray = JSON.parse(result.stdout.trim());\n\n        const resultMap = {};\n        texts.forEach((original, index) => {\n            resultMap[original] = translatedArray[index] || original;\n        });\n\n        // 3. 清理临时文件\n        if (fs.existsSync(tmpIn)) fs.unlinkSync(tmpIn);\n\n        return resultMap;\n    } catch (err) {\n        console.warn(`Translation to ${targetLang} failed: ${err.message}. Fallback to original.`);\n        if (fs.existsSync(tmpIn)) fs.unlinkSync(tmpIn);\n        const resultMap = {};\n        texts.forEach(t => resultMap[t] = t);\n        return resultMap;\n    }\n}\n\nasync function genMarkdown() {\n    if (!fs.existsSync(inputFile)) {\n        console.error(`Input file not found: ${inputFile}`);\n        process.exit(1);\n    }\n\n    const content = fs.readFileSync(inputFile, 'utf8');\n    const lines = content.split(/\\r?\\n/).filter(line => line.trim() !== '');\n\n    if (lines.length < 2) {\n        console.error(\"CSV file is empty or has no data.\");\n        return;\n    }\n\n    const csvHeaders = parseCsvLine(lines[0]);\n    const dataRows = lines.slice(1).map(line => {\n        const fields = parseCsvLine(line);\n        const obj = {};\n        csvHeaders.forEach((h, i) => {\n            obj[h] = fields[i] || '';\n        });\n        return obj;\n    });\n\n    // 收集所有需要翻译的文本 (收款项和留言)\n    const uniqueTexts = new Set();\n    dataRows.forEach(row => {\n        if (row['收款项']) uniqueTexts.add(row['收款项']);\n        // 只有当留言不为空且不为'-'时才收集\n        if (row['留言'] && row['留言'].trim() !== '-') uniqueTexts.add(row['留言']);\n    });\n    const textsToTranslate = Array.from(uniqueTexts);\n\n    // 为每种语言生成文档\n    for (const lang of Object.keys(i18n)) {\n        const config = i18n[lang];\n        const outputFilePath = path.join(__dirname, '..', 'docs', config.filename);\n\n        console.log(`[${config.langName}] Translating...`);\n        const translationMap = translateBatch(textsToTranslate, config.target); // 使用 config.target\n        console.log(`[${config.langName}] Translation complete. Sample: \"${textsToTranslate[0]}\" -> \"${translationMap[textsToTranslate[0]] || 'N/A'}\"`);\n\n        let md = `# ${config.title}\\n\\n`;\n        md += `> ${config.quote}\\n\\n`;\n\n        md += `### 📜 ${config.listTitle}\\n\\n`;\n        md += `| ${config.headers.join(' | ')} |\\n`;\n        md += `| ${config.headers.map(() => ':---').join(' | ')} |\\n`;\n\n        dataRows.forEach((row, index) => {\n            const displayTime = row['收款时间'] || '';\n            const rawItem = (row['收款项'] || '').trim();\n            const displayItem = translationMap[rawItem] || rawItem;\n\n            const displayAmount = `**${row['单位'] || ''}${row['金额'] || ''}**`;\n            const displayName = row['昵称'] || '';\n\n            const rawMessage = (row['留言'] || '').trim();\n            const displayMessage = (rawMessage === '-' || !rawMessage) ? '-' : (translationMap[rawMessage] || rawMessage);\n\n            // 根据当前语言的 headers 动态生成行\n            const rowValues = [];\n            config.headers.forEach(header => {\n                switch (header) {\n                    case '收款时间':\n                    case '受領時間':\n                    case '受領时间':\n                    case 'Time':\n                    case '수령 시간':\n                        rowValues.push(displayTime);\n                        break;\n                    case '收款项':\n                    case '項目':\n                    case '项目':\n                    case 'Item':\n                    case '항목':\n                        rowValues.push(displayItem);\n                        break;\n                    case '金额':\n                    case '金額':\n                    case 'Amount':\n                    case '금액':\n                        rowValues.push(displayAmount);\n                        break;\n                    case '昵称':\n                    case 'ニックネーム':\n                    case 'Name':\n                    case '닉네임':\n                        rowValues.push(displayName);\n                        break;\n                    case '留言':\n                    case 'メッセージ':\n                    case 'Message':\n                    case '메시지':\n                        rowValues.push(displayMessage);\n                        break;\n                    default:\n                        rowValues.push(''); // Fallback for unknown headers\n                }\n            });\n            md += `| ${rowValues.join(' | ')} |\\n`;\n        });\n\n        const now = new Date();\n        const timestamp = lang.startsWith('zh') || lang === 'ja' || lang === 'ko'\n            ? now.toLocaleString('zh-CN', { hour12: false })\n            : now.toUTCString();\n\n        md += `\\n\\n--- \\n*${config.footer}${timestamp}*`;\n\n        fs.writeFileSync(outputFilePath, md, 'utf8');\n        console.log(`[${config.langName}] Generated: ${config.filename}`);\n    }\n}\n\nfunction parseCsvLine(line) {\n    const fields = [];\n    let currentField = '';\n    let inQuotes = false;\n    for (let i = 0; i < line.length; i++) {\n        const char = line[i];\n        if (char === '\"') {\n            inQuotes = !inQuotes;\n        } else if (char === ',' && !inQuotes) {\n            fields.push(currentField);\n            currentField = '';\n        } else {\n            currentField += char;\n        }\n    }\n    fields.push(currentField);\n    return fields.map(f => f.replace(/^\"|\"$/g, '').replace(/\"\"/g, '\"').trim());\n}\n\ngenMarkdown();\n"
  },
  {
    "path": "scripts/go_install.sh",
    "content": "#!/bin/bash\nset -e\n\n# Globals\nTEMP=\"$(mktemp -d)\"\n\nfunction cleanup() {\n    rm -rf \"$TEMP\"\n    # Only print new line if we are not returning a value (e.g. for simple commands)\n}\ntrap cleanup EXIT\n\nfunction on_interrupt() {\n    echo -e \"\\n\\nOperation cancelled by user (Ctrl+C). Exiting...\"\n    exit 130\n}\ntrap on_interrupt INT\nARCH=$(dpkg --print-architecture)\nOS=\"linux\"\n\n# Function to fetch latest Go versions\n# Usage: fetch_versions <count>\nfunction fetch_versions() {\n    local count=$1\n    if [ -z \"$count\" ]; then count=10; fi\n    \n    # Fetch existing versions from go.dev\n    # Using curl and grep to parse JSON mode from go.dev\n    curl -s 'https://go.dev/dl/?mode=json' | grep -Eo 'go[0-9]+\\.[0-9]+(\\.[0-9]+)?' | sort -V -u -r | head -n \"$count\"\n}\n\nfunction install_go() {\n    local target_ver=\"$1\" # Optional version argument\n    \n    if [ -n \"$target_ver\" ]; then\n        # Direct install mode\n        local selected_version=\"$target_ver\"\n        if [[ ! \"$selected_version\" =~ ^go ]]; then\n            selected_version=\"go$selected_version\"\n        fi\n        do_install \"$selected_version\"\n        return\n    fi\n    \n    echo \"-------------------------------------\"\n    echo \"          Install Go\"\n    echo \"-------------------------------------\"\n    \n    echo \"Fetching latest 10 Go versions...\" >&2\n    local versions\n    versions=$(fetch_versions 10)\n    \n    if [ -z \"$versions\" ]; then\n        echo \"Failed to fetch Go versions.\"\n        return\n    fi\n    \n    echo \"Available Versions:\"\n    local i=1\n    declare -A VERSION_MAP\n    for v in $versions; do\n        echo \"$i) $v\"\n        VERSION_MAP[$i]=$v\n        ((i++))\n    done\n    echo \"0) Back to Main Menu\"\n    \n    local choice\n    read -p \"Enter version number to install (default: 1): \" choice\n    \n    if [ -z \"$choice\" ]; then choice=1; fi\n    \n    if [ \"$choice\" == \"0\" ]; then\n        return\n    fi\n    \n    local selected_version=\"\"\n    if [[ \"$choice\" =~ ^[0-9]+$ ]] && [ \"${VERSION_MAP[$choice]+isset}\" ]; then\n        selected_version=\"${VERSION_MAP[$choice]}\"\n    else\n        # Allow custom version input\n        selected_version=\"$choice\"\n        if [[ ! \"$selected_version\" =~ ^go ]]; then\n            selected_version=\"go$selected_version\"\n        fi\n    fi\n    \n    do_install \"$selected_version\"\n    \n    read -p \"Press Enter to continue...\"\n}\n\nfunction do_install() {\n    local selected_version=\"$1\"\n    echo \"Selected version: $selected_version\"\n    \n    # Check if this exact version is already installed\n    if command -v go &> /dev/null; then\n        local current_version\n        current_version=$(go version | awk '{print $3}')\n        if [ \"$current_version\" == \"$selected_version\" ]; then\n            # If running interactively (no args passed to script originally), ask.\n            # But here we complicate things if direct install.\n            # For simplicity: always ask confirm if tty.\n            if [ -t 0 ]; then\n                read -p \"Go version $selected_version is already installed. Overwrite? [y/N]: \" confirm\n                if [[ ! \"$confirm\" =~ ^[yY] ]]; then\n                    echo \"Installation cancelled.\"\n                    return\n                fi\n            else\n                echo \"Go version $selected_version is already installed. Overwriting...\"\n            fi\n        fi\n    fi\n    \n    # Download and Install logic here\n    local download_url=\"https://go.dev/dl/${selected_version}.${OS}-${ARCH}.tar.gz\"\n    \n    echo \"Downloading $download_url ...\"\n    if wget --progress=dot:mega \"$download_url\" -O \"$TEMP/go-linux.tar.gz\"; then\n        echo \"Removing old installation...\"\n        rm -rf /usr/local/go\n        \n        echo \"Extracting...\"\n        tar -C /usr/local -xzf \"$TEMP/go-linux.tar.gz\"\n        \n        setup_env\n        \n        echo \"-------------------------------------\"\n        echo \"Go $selected_version installed successfully.\"\n        /usr/local/go/bin/go version\n        echo \"-------------------------------------\"\n    else\n        echo \"Download failed.\"\n        exit 1\n    fi\n}\n\nfunction uninstall_go() {\n    echo \"-------------------------------------\"\n    echo \"          Uninstall Go\"\n    echo \"-------------------------------------\"\n    \n    if [ ! -d \"/usr/local/go\" ]; then\n        echo \"Go does not seem to be installed in /usr/local/go.\"\n        if [ -t 0 ]; then read -p \"Press Enter to continue...\" ; fi\n        return\n    fi\n    \n    if [ -t 0 ]; then\n        echo \"This will remove:\"\n        echo \"  - /usr/local/go\"\n        read -p \"Are you sure? [y/N]: \" confirm\n        if [[ ! \"$confirm\" =~ ^[yY] ]]; then\n            echo \"Uninstallation cancelled.\"\n            return\n        fi\n    fi\n    \n    rm -rf /usr/local/go\n    echo \"Go uninstalled.\"\n    \n    if [ -t 0 ]; then read -p \"Press Enter to continue...\" ; fi\n}\n\nfunction list_versions() {\n    echo \"-------------------------------------\"\n    echo \"       Available Go Versions\"\n    echo \"-------------------------------------\"\n    local versions\n    versions=$(fetch_versions 10)\n    \n    local i=1\n    for v in $versions; do\n        echo \"$i) $v\"\n        ((i++))\n    done\n    echo \"-------------------------------------\"\n}\n# Separate function for wait to reuse logic\nfunction wait_enter() {\n    if [ -t 0 ]; then\n        read -p \"Press Enter to return to menu...\"\n    fi\n}\n\nfunction setup_env() {\n    mkdir -p /go/bin /go/src /go/pkg\n    \n    # Define env vars for current session\n    export GO_HOME=/usr/local/go\n    export GOPATH=/go\n    export PATH=${GOPATH}/bin:${GO_HOME}/bin/:$PATH\n}\n\nfunction show_menu() {\n    while true; do\n        clear\n        echo \"=====================================\"\n        echo \"       Go Manager (go_install.sh)\"\n        echo \"=====================================\"\n        echo \"1. Install Go\"\n        echo \"2. Uninstall Go\"\n        echo \"3. List Available Versions (Top 10)\"\n        echo \"0. Exit\"\n        echo \"=====================================\"\n        read -p \"Enter your choice: \" main_choice\n        \n        case $main_choice in\n            1) install_go ;;\n            2) uninstall_go ;;\n            3) list_versions; wait_enter ;;\n            0) echo \"Goodbye!\"; exit 0 ;;\n            *) echo \"Invalid choice. Please try again.\"; sleep 1 ;;\n        esac\n    done\n}\n\n# Main Dispatcher\nCMD=\"${1:-menu}\"\n\ncase \"$CMD\" in\n    install)\n        # Shift to get potential version argument\n        shift\n        install_go \"$1\"\n    ;;\n    uninstall)\n        uninstall_go\n    ;;\n    list)\n        list_versions\n    ;;\n    menu)\n        show_menu\n    ;;\n    *)\n        # If the first argument doesn't match above commands, default to menu\n        # This allows simple `bash <(curl...)` to work\n        show_menu\n    ;;\nesac"
  },
  {
    "path": "scripts/gormgen.sh",
    "content": "#!/bin/bash\nshellExit()\n{\n    if [ $1 -eq 1 ]; then\n        printf \"\\nfailed!!!\\n\\n\"\n        exit 1\n    fi\n}\n\n\nprintf \"\\nRegenerating file\\n\\nstart build markdown and model file\"\n# Regenerating file // 重新生成文件\n# Start build markdown and model file // 开始构建 markdown 和 model 文件\n\n\n# docker run --name mariadb -p 3306:3306 -e MYSQL_ROOT_PASSWORD=xxxx -v /data/mariadb/data:/var/lib/mysql -d mariadb:10.1.21\n#go run -v ./cmd/db_gen/main.go -type sqlite -dsn storage/database/db.db -name main -table pre -prefix pre_ -savedir test\n#go run -v ./cmd/db_gen/main.go -type mysql -dsn \"root:root@tcp(192.168.138.190:3306)/main?charset=utf8mb4&parseTime=true&loc=Local\" -name main -table pre -prefix pre_ -savedir test\n\n\ngo run -v ./cmd/gen/gen.go -type sqlite -dsn storage/database/db.db\n\n#  scripts/gormgen.sh sqlite storage/database/db.db  main  pre_  pre_  test\n\ntime go run -v ./cmd/db_gen/main.go  -type \"$1\" -dsn \"$2\" -name $3 -table \"$4\" -prefix \"$5\" -savedir \"$6\"\n\nprintf \"\\nBuild markdown and model file succeed!\\n\"\n# Build markdown and model file succeed! // 构建 markdown 和 model 文件成功！\n\nshellExit $?\n\n\n\nprintf \"\\ncreate curd code : \\n\"\n# Create CURD code // 创建 CURD 代码\ntime go build -o gormgen ./cmd/gorm_gen/main.go\nshellExit $?\n\nmv gormgen $GOPATH/bin/\nshellExit $?\n\ngo generate ./...\nshellExit $?\n\nprintf \"\\nFormatting code\\n\\n\"\n# Formatting code // 格式化代码\ntime go run -v ./cmd/mfmt/main.go\nshellExit $?\n\nprintf \"\\nDone.\\n\\n\"\n# Done // 完成\n"
  },
  {
    "path": "scripts/https-nginx-example.conf",
    "content": "\n\n\nserver {\n        listen       443 ssl;\n        listen  [::]:443 ssl;\n        # server_name localhost;\n\n        client_max_body_size 50m;\n        proxy_max_temp_file_size 0;\n        proxy_buffering off;\n\n        ssl_certificate      /cert.pem;\n        ssl_certificate_key  /privkey.pem;\n        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;\n        ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;\n        ssl_prefer_server_ciphers on;\n\n        location / {\n                proxy_pass http://fast-note-sync-service:9000;\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        location /api/user/sync {\n                proxy_pass http://fast-note-sync-service:9000/api/user/sync;\n                proxy_http_version 1.1;\n                proxy_set_header Upgrade $http_upgrade;\n                proxy_set_header Connection \"upgrade\";\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}"
  },
  {
    "path": "scripts/process_support.py",
    "content": "import csv\nimport json\nimport os\nimport sys\nfrom datetime import datetime\nfrom deep_translator import GoogleTranslator\n\n# 配置路径\nbase_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\ninput_csv = os.path.join(base_dir, 'docs', 'Support.csv')\n\n# 键名映射 (CN -> EN)\nKEY_MAP = {\n    '收款时间': 'time',\n    '收款项': 'item',\n    '金额': 'amount',\n    '单位': 'unit',\n    '留言': 'message',\n    '昵称': 'name'\n}\n\n# 语言配置\nLANG_CONFIG = {\n    'en': {'name': 'English', 'md_title': 'Supporters List', 'md_headers': ['Time', 'Item', 'Amount', 'Name', 'Message'], 'md_footer': 'Last updated: '},\n    'zh-CN': {'name': '简体中文', 'md_title': '支持者名单', 'md_headers': ['收款时间', '收款项', '金额', '昵称', '留言'], 'md_footer': '最后更新于：'},\n    'zh-TW': {'name': '繁體中文', 'md_title': '支持者名單', 'md_headers': ['收款时间', '收款项', '金额', '昵称', '留言'], 'md_footer': '最後更新於：'},\n    'ja': {'name': '日本語', 'md_title': 'サポーターリスト', 'md_headers': ['受領時間', '項目', '金額', '名前', 'メッセージ'], 'md_footer': '最終更新：'},\n    'ko': {'name': '한국어', 'md_title': '후원자 명단', 'md_headers': ['時間', '項目', '金額', '이름', '메시지'], 'md_footer': '마지막 업데이트：'}\n}\n\ndef translate_texts(texts, target_lang):\n    \"\"\"批量翻译文本\"\"\"\n    if not texts or target_lang == 'zh-CN':\n        return {text: text for text in texts}\n\n    translator = GoogleTranslator(source='auto', target=target_lang)\n    to_translate = [t for t in texts if t and t.strip() and t.strip() != '-']\n    if not to_translate:\n        return {text: text for text in texts}\n\n    try:\n        translated_list = translator.translate_batch(to_translate)\n        t_map = dict(zip(to_translate, translated_list))\n        return {text: t_map.get(text, text) for text in texts}\n    except Exception as e:\n        print(f\"Translation to {target_lang} failed: {e}. Fallback to original.\", file=sys.stderr)\n        return {text: text for text in texts}\n\ndef generate_json(data, translation_map, lang_code):\n    \"\"\"生成 JSON 数据 (键名统一为英文)\"\"\"\n    output_data = []\n    for row in data:\n        new_row = {}\n        for cn_key, en_key in KEY_MAP.items():\n            val = row.get(cn_key, '')\n            # 翻译内容字段\n            if cn_key in ['收款项', '留言'] and val and val != '-':\n                val = translation_map.get(val, val)\n            new_row[en_key] = val if val else ('-' if cn_key in ['留言'] else val)\n        output_data.append(new_row)\n\n    file_path = os.path.join(base_dir, 'docs', f'Support.{lang_code}.json')\n    with open(file_path, 'w', encoding='utf-8') as f:\n        json.dump(output_data, f, ensure_ascii=False, indent=2)\n    print(f\"Saved JSON: {file_path}\")\n\ndef generate_md(data, lang_code, translation_map):\n    \"\"\"生成 Markdown 文件\"\"\"\n    conf = LANG_CONFIG.get(lang_code)\n    md = f\"# {conf['md_title']}\\n\\n\"\n    md += f\"| {' | '.join(conf['md_headers'])} |\\n\"\n    md += f\"| {' | '.join([':---']*len(conf['md_headers']))} |\\n\"\n\n    for row in data:\n        time = row.get('收款时间', '')\n        item = translation_map.get(row.get('收款项', ''), row.get('收款项', ''))\n        amount = f\"**{row.get('单位', '')}{row.get('金额', '')}**\"\n        name = row.get('昵称', '')\n        msg = row.get('留言', '')\n        msg = translation_map.get(msg, msg) if msg and msg != '-' else '-'\n\n        md += f\"| {time} | {item} | {amount} | {name} | {msg} |\\n\"\n\n    timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')\n    md += f\"\\n\\n--- \\n*{conf['md_footer']}{timestamp}*\"\n\n    file_path = os.path.join(base_dir, 'docs', f'Support.{lang_code}.md')\n    with open(file_path, 'w', encoding='utf-8') as f:\n        f.write(md)\n    # print(f\"Saved MD: {file_path}\")\n\ndef main():\n    if not os.path.exists(input_csv):\n        print(f\"Error: {input_csv} not found.\")\n        return\n\n    data = []\n    unique_texts = set()\n    try:\n        with open(input_csv, 'r', encoding='utf-8') as f:\n            reader = csv.DictReader(f)\n            for row in reader:\n                data.append(row)\n                for k in ['收款项', '留言']:\n                    if row.get(k) and row[k] != '-':\n                        unique_texts.add(row[k])\n    except Exception as e:\n        print(f\"Error reading CSV: {e}\")\n        return\n\n    texts_list = list(unique_texts)\n\n    for lang in LANG_CONFIG.keys():\n        print(f\"Processing {lang}...\")\n        t_map = translate_texts(texts_list, lang)\n        generate_json(data, t_map, lang)\n        # 为特定语言生成 MD\n        if lang in ['zh-CN', 'zh-TW', 'en']:\n            generate_md(data, lang, t_map)\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "scripts/process_support_csv.js",
    "content": "const fs = require('fs');\nconst path = require('path');\nconst os = require('os');\n\nconst downloadsDir = path.join(os.homedir(), 'Downloads');\nconst outputFile = path.join(__dirname, '..', 'docs', 'Support.csv');\n\nfunction findLatestSourceFile() {\n    if (!fs.existsSync(downloadsDir)) {\n        return null;\n    }\n    const files = fs.readdirSync(downloadsDir);\n    const pattern = /打赏.*链接小票单记录.*\\.csv$/;\n\n    const matchedFiles = files\n        .filter(f => pattern.test(f))\n        .map(f => {\n            const filePath = path.join(downloadsDir, f);\n            return {\n                path: filePath,\n                mtime: fs.statSync(filePath).mtime\n            };\n        })\n        .sort((a, b) => b.mtime - a.mtime);\n\n    return matchedFiles.length > 0 ? matchedFiles[0].path : null;\n}\n\nfunction processCsv() {\n    const inputFile = findLatestSourceFile();\n\n    if (!inputFile) {\n        console.error(`No matching source file found in ${downloadsDir} (Keyword: \"打赏\" & \"链接小票单记录\")`);\n        process.exit(1);\n    }\n\n    console.log(`Processing latest source file: ${inputFile}`);\n\n    const content = fs.readFileSync(inputFile, 'utf8');\n    const lines = content.split(/\\r?\\n/);\n\n    // 真正的表头在第 8 行 (index 7)，数据从第 9 行 (index 8) 开始\n    const dataLines = lines.slice(8).filter(line => line.trim() !== '');\n\n    const dataRows = [];\n\n    dataLines.forEach(line => {\n        const fields = parseCsvLine(line);\n        if (fields.length >= 9) {\n            const time = fields[0].trim();\n            const item = fields[3].trim();\n            const amountStr = fields[4].trim();\n            const message = fields[6].trim();\n            const name = fields[7].trim();\n\n            // 处理金额：去掉 ¥，转为数字用于排序\n            const amountValue = parseFloat(amountStr.replace(/[^\\d.-]/g, '')) || 0;\n\n            dataRows.push({\n                time,\n                item,\n                amountVal: amountValue, // 用于排序\n                amountStr: amountValue.toFixed(2), // 用于显示\n                unit: '¥',\n                message,\n                name\n            });\n        }\n    });\n\n    // 排序逻辑：金额由大到小；金额一致则时间新在前（降序）\n    dataRows.sort((a, b) => {\n        if (b.amountVal !== a.amountVal) {\n            return b.amountVal - a.amountVal;\n        }\n        return b.time.localeCompare(a.time);\n    });\n\n    // 构建输出内容\n    const result = [];\n    result.push('收款时间,收款项,金额,单位,留言,昵称');\n\n    dataRows.forEach(row => {\n        const rowStr = [\n            formatCsvField(row.time),\n            formatCsvField(row.item),\n            formatCsvField(row.amountStr),\n            formatCsvField(row.unit),\n            formatCsvField(row.message),\n            formatCsvField(row.name)\n        ].join(',');\n        result.push(rowStr);\n    });\n\n    fs.writeFileSync(outputFile, result.join('\\n') + '\\n', 'utf8');\n    console.log(`Successfully processed and sorted. Saved to ${outputFile}`);\n}\n\nfunction parseCsvLine(line) {\n    const fields = [];\n    let currentField = '';\n    let inQuotes = false;\n    for (let i = 0; i < line.length; i++) {\n        const char = line[i];\n        if (char === '\"') {\n            inQuotes = !inQuotes;\n        } else if (char === ',' && !inQuotes) {\n            fields.push(currentField);\n            currentField = '';\n        } else {\n            currentField += char;\n        }\n    }\n    fields.push(currentField);\n    return fields;\n}\n\nfunction formatCsvField(field) {\n    if (!field) return '';\n    if (typeof field !== 'string') field = String(field);\n    field = field.trim();\n    if (field.includes(',') || field.includes('\"') || field.includes('\\n')) {\n        return `\"${field.replace(/\"/g, '\"\"')}\"`;\n    }\n    return field;\n}\n\nprocessCsv();\n"
  },
  {
    "path": "scripts/quest_install.sh",
    "content": "#!/usr/bin/env bash\nset -euo pipefail\nIFS=$'\\n\\t'\n\n# ===========================================\n# Fast-Note Sync Service 管理脚本 (Premium)\n# ===========================================\n\nREPO=\"haierkeys/fast-note-sync-service\"\nBIN_BASE=\"fast-note-sync-service\"\nINSTALL_DIR=\"/opt/fast-note\"\nBIN_PATH=\"$INSTALL_DIR/$BIN_BASE\"\nLINK_BIN=\"/usr/local/bin/fns-bin\"\nINSTALLER_LINK=\"/usr/local/bin/fns\"\nINSTALLER_SELF_PATH=\"/opt/fast-note/fast-note-installer.sh\"\nSERVICE_NAME=\"fast-note.service\"\nLOG_FILE=\"/var/log/fast-note.log\"\nTMPDIR=\"${TMPDIR:-/tmp}\"\nGITHUB_RAW=\"https://github.com/$REPO/releases/download\"\nGITHUB_API=\"https://api.github.com/repos/$REPO\"\nCNB_API_BASE=\"https://api.cnb.cool/$REPO/-/releases\"\nCNB_TOKEN=\"58tjez3744HL9Z10cRaCHdeEPhK\"\nGITHUB_SCRIPT_URL=\"https://raw.githubusercontent.com/haierkeys/fast-note-sync-service/master/scripts/quest_install.sh\"\nCNB_SCRIPT_URL=\"https://cnb.cool/haierkeys/fast-note-sync-service/-/git/raw/master/scripts/quest_install.sh?cnb\"\nCNB_MIRROR_CONF=\"$HOME/.fast-note-mirror\"\nUSE_CNB=false\nSUDO=\"\"\n\n# --- Color system // 颜色系统 ---\n_RED=$(tput setaf 1)\n_GREEN=$(tput setaf 2)\n_YELLOW=$(tput setaf 3)\n_BLUE=$(tput setaf 4)\n_MAGENTA=$(tput setaf 5)\n_CYAN=$(tput setaf 6)\n_BOLD=$(tput bold)\n_ITALIC=$(tput sitm)\n_DIM=$(tput dim)\n_RESET=$(tput sgr0)\n\n# --- Visual decorations // 视觉装饰 ---\n_INFO=\"  [i] \"\n_SUCCESS=\"  [+] \"\n_WARN=\"  [!] \"\n_ERROR=\"  [-] \"\n_STEP=\"  >> \"\n\ndraw_banner() {\n    clear\n    load_version\n    local ver_display=\"${_YELLOW}$L_NOT_INSTALLED${_RESET}\"\n    if [ -n \"$INSTALLED_VER\" ]; then\n        local clean_v=\"${INSTALLED_VER#v}\"\n        ver_display=\"${_GREEN}$BIN_BASE v${clean_v}${_RESET}\"\n    fi\n    \n    local source_display=\"${_BLUE}GitHub${_RESET}\"\n    if [ \"$USE_CNB\" = \"true\" ]; then\n        source_display=\"${_MAGENTA}CNB.cool${_RESET}\"\n    fi\n    \n    local svc_file=\"N/A\"\n    local os_type\n    os_type=$(detect_os)\n    if [ \"$os_type\" = \"linux\" ]; then\n        svc_file=\"/etc/systemd/system/fast-note.service\"\n        elif [ \"$os_type\" = \"darwin\" ]; then\n        svc_file=\"$HOME/Library/LaunchAgents/com.haierkeys.fast-note.plist\"\n    fi\n    \n    local latest_v\n    latest_v=$(get_latest_tag)\n    local clean_lv=\"${latest_v#v}\"\n    local latest_display=\"${_GREEN}v${clean_lv}${_RESET}\"\n    \n    # Highlight latest version if update available\n    # 如果有更新，高亮显示最新版本\n    if [ -n \"$INSTALLED_VER\" ] && [ \"${INSTALLED_VER#v}\" != \"$clean_lv\" ]; then\n        latest_display=\"${_YELLOW}v${clean_lv} (Update Available)${_RESET}\"\n        [ \"$CURRENT_LANG\" = \"zh\" ] && latest_display=\"${_YELLOW}v${clean_lv} (有新版本)${_RESET}\"\n    fi\n    \n    cat <<EOF\n${_CYAN}${_BOLD}\n    ______           __     _   __      __          _____\n   / ____/___ ______/ /_   / | / /___  / /____     / ___/__  ______  _____\n  / /_  / __  / ___/ __/  /  |/ / __ \\/ __/ _ \\    \\__ \\/ / / / __ \\/ ___/\n / __/ / /_/ (__  ) /_   / /|  / /_/ / /_/  __/   ___/ / /_/ / / / / /__\n/_/    \\__,_/____/\\__/  /_/ |_/\\____/\\__/\\___/   /____/\\__, /_/ /_/\\___/\n                                                      /____/\n\n       Fast Note Sync Service Manager Script\n   ================================================\n   $L_CUR_VER: $ver_display\n   $L_LATEST_VER: $latest_display\n   $L_SOURCE  : $source_display\n   ------------------------------------------------\n   ${_DIM}$L_PATH_INFO:${_RESET}\n   ${_BLUE}$L_MAIN_DIR :${_RESET} ${_BOLD}$INSTALL_DIR${_RESET}\n   ${_BLUE}$L_DATA_DIR :${_RESET} ${_BOLD}$INSTALL_DIR/storage${_RESET}\n   ${_BLUE}$L_CONF_DIR :${_RESET} ${_BOLD}$INSTALL_DIR/config${_RESET}\n   ${_BLUE}$L_LOG_FILE_PATH :${_RESET} ${_BOLD}$LOG_FILE${_RESET}\n   ${_BLUE}$L_SVC_FILE_PATH :${_RESET} ${_BOLD}$svc_file${_RESET}\n   ================================================\nEOF\n    echo -e \"\\n\"\n}\n\nmsg() { echo -e \"${_BOLD}$1${_RESET}\"; }\ninfo() { echo -e \"${_INFO}${_CYAN}$1${_RESET}\"; }\nsuccess() { echo -e \"${_SUCCESS}${_GREEN}$1${_RESET}\"; }\nwarn() { echo -e \"${_WARN}${_YELLOW}$1${_RESET}\"; }\nerror() { echo -e \"${_ERROR}${_RED}$1${_RESET}\"; }\nstep() { echo -e \"${_STEP}${_BLUE}$1${_RESET}\"; }\n\n# --- Language Support ---\nLANG_CONF=\"$HOME/.fast-note-sync.lang\"\nVERSION_CONF=\"$INSTALL_DIR/.version\"\nCURRENT_LANG=\"en\" # Default to English\nINSTALLED_VER=\"\"\n\nsave_lang() {\n    echo \"$CURRENT_LANG\" > \"$LANG_CONF\" 2>/dev/null || true\n}\n\nload_lang() {\n    local force_load=\"${1:-}\"\n    # Read from file only if forced or if it's the first time\n    if [ \"$force_load\" = \"init\" ] && [ -f \"$LANG_CONF\" ]; then\n        CURRENT_LANG=$(cat \"$LANG_CONF\" 2>/dev/null | tr -d '[:space:]' || echo \"en\")\n    fi\n    \n    if [ \"$CURRENT_LANG\" = \"zh\" ]; then\n        L_MENU_1=\"安装 / 升级服务\"\n        L_MENU_1_D=\"下载服务程序并自动安装管理工具至系统\"\n        L_MENU_2=\"启动服务\"\n        L_MENU_2_D=\"在后台启动同步服务\"\n        L_MENU_3=\"停止服务\"\n        L_MENU_3_D=\"终止正在运行的服务进程\"\n        L_MENU_4=\"服务状态\"\n        L_MENU_4_D=\"检查运行状态并预览最新日志\"\n        L_MENU_5=\"全部卸载\"\n        L_MENU_5_D=\"彻底移除程序、配置及所有日志文件\"\n        L_MENU_6=\"安装脚本到系统\"\n        L_MENU_6_D=\"将管理工具添加到全局快捷命令 fns\"\n        L_MENU_7=\"设置开机启动\"\n        L_MENU_7_D=\"配置 Systemd (Linux) 或 Launchd (macOS) 开机自启\"\n        L_MENU_8=\"切换下载镜像\"\n        L_MENU_8_D=\"在 GitHub 与 CNB 镜像之间切换\"\n        L_MENU_0=\"退出\"\n        L_MENU_L=\"Switch to English (切换至英文)\"\n        L_SWITCH_TO_CNB=\"已切换至 CNB 镜像\"\n        L_SWITCH_TO_GITHUB=\"已切换至 GitHub 镜像\"\n        L_SELECT=\"请选择\"\n        L_INPUT_VER=\"输入版本 (留空使用 latest)\"\n        L_INPUT_URL=\"输入脚本 URL (留空复制本地\"\n        L_ENTER_URL=\"输入脚本 URL [默认:\"\n        L_PATH_WARN=\"警告: 安装目录 %s 不在您的 PATH 环境变量中。\"\n        L_PATH_FIX=\"请手动添加以在任何地方使用快捷命令: export PATH=\\$PATH:%s\"\n        L_SOURCE=\"下载源\"\n        \n        L_ERR_ROOT=\"需要 root 权限或安装 sudo 后重试\"\n        L_TRY_DL=\"尝试下载\"\n        L_DL_FAIL_API=\"直接下载失败，尝试通过 API 查找...\"\n        L_ERR_NO_REL=\"无法获取 release 信息\"\n        L_FOUND_ASSET=\"找到资产\"\n        L_ERR_NO_ASSET=\"未能找到合适的资产\"\n        L_EXTRACTING=\"正在解压资产到\"\n        L_ERR_EXTRACT=\"解压失败\"\n        L_ERR_NO_EXE=\"未在压缩包中找到可执行文件\"\n        L_LINK_CREATED=\"已创建快捷命令\"\n        \n        L_SVC_RUNNING=\"服务已经在运行中\"\n        L_STARTING=\"正在启动服务...\"\n        L_START_SUCCESS=\"服务已成功启动\"\n        L_LOG_PREVIEW=\"实时日志预览\"\n        L_START_FAIL=\"启动失败！请检查日志详情\"\n        L_STOPPING=\"正在发送停止信号...\"\n        L_STOP_SUCCESS=\"服务已停止\"\n        L_STATUS=\"状态\"\n        L_STATUS_RUN=\"运行中\"\n        L_STATUS_STOP=\"已停止\"\n        L_LOG_RECENT=\"最近 20 行日志预览\"\n        \n        L_UN_WARN=\"准备执行全部卸载！将删除目录、日志及所有配置。\"\n        L_UN_CONFIRM=\"确认执行全部卸载吗？\"\n        L_UN_CANCEL=\"已取消卸载\"\n        L_CLEAN_PROC=\"清理残留进程...\"\n        L_CLEAN_FILES=\"移除安装目录与文件...\"\n        L_UN_DONE=\"全部卸载完成\"\n        \n        L_DL_SCRIPT=\"从指定 URL 下载安装脚本...\"\n        L_ERR_DL_SCRIPT=\"下载安装脚本失败\"\n        L_CP_SCRIPT=\"复制当前脚本到系统目录...\"\n        L_ST_DL_SCRIPT=\"脚本通过 stdin 执行，正在尝试自动获取...\"\n        L_INST_DONE=\"安装脚本已就绪\"\n        \n        L_PRE_DL=\"准备下载 fast-note\"\n        L_INST_ALL_DONE=\"安装/升级流程已完成\"\n        L_INST_TIP=\"提示: 输入 fns 或选择菜单启动服务。\"\n        L_INVALID=\"无效选项，请重新选择\"\n        L_USAGE=\"用法\"\n        L_CUR_VER=\"当前版本\"\n        L_LATEST_VER=\"最新版本\"\n        L_NOT_INSTALLED=\"未安装\"\n        L_PATH_INFO=\"路径信息\"\n        L_MAIN_DIR=\"程序目录\"\n        L_DATA_DIR=\"数据目录\"\n        L_CONF_DIR=\"配置目录\"\n        L_LOG_FILE_PATH=\"日志文件\"\n        L_SVC_FILE_PATH=\"系统服务\"\n        \n        L_AUTO_LINUX=\"正在配置 Systemd 开机自启...\"\n        L_AUTO_MAC=\"正在配置 Launchd 开机自启...\"\n        L_AUTO_WIN=\"Windows 暂不支持自动配置服务，请手动添加计划任务。\"\n        L_AUTO_DONE=\"开机自启配置完成\"\n        L_AUTO_FAIL=\"开机自启配置失败\"\n    else\n        L_MENU_1=\"Install / Update Service\"\n        L_MENU_1_D=\"Download service and install manager to system\"\n        L_MENU_2=\"Start Service\"\n        L_MENU_2_D=\"Start the sync service in background\"\n        L_MENU_3=\"Stop Service\"\n        L_MENU_3_D=\"Terminate the running service process\"\n        L_MENU_4=\"Service Status\"\n        L_MENU_4_D=\"Check status and preview recent logs\"\n        L_MENU_5=\"Uninstall All\"\n        L_MENU_5_D=\"Remove program, config, and all logs\"\n        L_MENU_6=\"Install Self to System\"\n        L_MENU_6_D=\"Add this tool to global commands (fns)\"\n        L_MENU_7=\"Set Auto-Start\"\n        L_MENU_7_D=\"Configure Systemd (Linux) or Launchd (macOS) auto-start\"\n        L_MENU_8=\"Switch Download Mirror\"\n        L_MENU_8_D=\"Switch between GitHub and CNB mirror\"\n        L_MENU_0=\"Quit\"\n        L_MENU_L=\"切换至中文 (Switch to Chinese)\"\n        L_SWITCH_TO_CNB=\"Switched to CNB mirror\"\n        L_SWITCH_TO_GITHUB=\"Switched to GitHub mirror\"\n        L_SELECT=\"Please select\"\n        L_INPUT_VER=\"Enter version (leave blank for latest)\"\n        L_INPUT_URL=\"Enter script URL (leave blank to copy local\"\n        L_ENTER_URL=\"Enter script URL [Default:\"\n        L_PATH_WARN=\"WARNING: Install directory %s is not in your PATH.\"\n        L_PATH_FIX=\"Add it manually to use commands everywhere: export PATH=\\$PATH:%s\"\n        L_SOURCE=\"Download Source\"\n        \n        L_ERR_ROOT=\"Root privileges or sudo required\"\n        L_TRY_DL=\"Trying to download\"\n        L_DL_FAIL_API=\"Direct download failed, trying via API...\"\n        L_ERR_NO_REL=\"Failed to get release info\"\n        L_FOUND_ASSET=\"Asset found\"\n        L_ERR_NO_ASSET=\"No suitable asset found\"\n        L_EXTRACTING=\"Extracting asset to\"\n        L_ERR_EXTRACT=\"Extraction failed\"\n        L_ERR_NO_EXE=\"Executable not found in package\"\n        L_LINK_CREATED=\"Symbolic link created\"\n        \n        L_SVC_RUNNING=\"Service is already running\"\n        L_STARTING=\"Starting service...\"\n        L_START_SUCCESS=\"Service started successfully\"\n        L_LOG_PREVIEW=\"Real-time log preview\"\n        L_START_FAIL=\"Start failed! Please check logs\"\n        L_STOPPING=\"Sending stop signal...\"\n        L_STOP_SUCCESS=\"Service stopped\"\n        L_STATUS=\"Status\"\n        L_STATUS_RUN=\"Running\"\n        L_STATUS_STOP=\"Stopped\"\n        L_LOG_RECENT=\"Recent 20 lines of log\"\n        \n        L_UN_WARN=\"Preparing full uninstall! All data will be deleted.\"\n        L_UN_CONFIRM=\"Confirm full uninstall?\"\n        L_UN_CANCEL=\"Uninstall cancelled\"\n        L_CLEAN_PROC=\"Cleaning up processes...\"\n        L_CLEAN_FILES=\"Removing directories and files...\"\n        L_UN_DONE=\"Full uninstall completed\"\n        \n        L_DL_SCRIPT=\"Downloading script from URL...\"\n        L_ERR_DL_SCRIPT=\"Failed to download script\"\n        L_CP_SCRIPT=\"Copying current script to system...\"\n        L_ST_DL_SCRIPT=\"Running via stdin, trying to fetch automatically...\"\n        L_INST_DONE=\"Installer is ready\"\n        \n        L_PRE_DL=\"Preparing to download fast-note\"\n        L_INST_ALL_DONE=\"Install/Update process completed\"\n        L_INST_TIP=\"Tip: Type fns or use menu to start service.\"\n        L_INVALID=\"Invalid option, please try again\"\n        L_USAGE=\"Usage\"\n        L_CUR_VER=\"Installed Version\"\n        L_LATEST_VER=\"Latest Version\"\n        L_NOT_INSTALLED=\"Not installed\"\n        L_PATH_INFO=\"Path Info\"\n        L_MAIN_DIR=\"Main Dir\"\n        L_DATA_DIR=\"Data Dir\"\n        L_CONF_DIR=\"Conf Dir\"\n        L_LOG_FILE_PATH=\"Log File\"\n        L_SVC_FILE_PATH=\"Svc File\"\n        \n        L_AUTO_LINUX=\"Configuring Systemd auto-start...\"\n        L_AUTO_MAC=\"Configuring Launchd auto-start...\"\n        L_AUTO_WIN=\"Windows does not support auto-service config yet. Please add manually.\"\n        L_AUTO_DONE=\"Auto-start configured successfully\"\n        L_AUTO_FAIL=\"Auto-start configuration failed\"\n    fi\n}\n# Initial load // 初始加载\nload_lang \"init\"\n\n# Detect mirror source from script arguments or saved config\n# 从脚本参数或已保存配置中检测镜像来源\nsave_mirror() {\n    echo \"$USE_CNB\" > \"$CNB_MIRROR_CONF\" 2>/dev/null || true\n}\n\nload_mirror() {\n    if [ -f \"$CNB_MIRROR_CONF\" ]; then\n        local saved\n        saved=$(cat \"$CNB_MIRROR_CONF\" 2>/dev/null | tr -d '[:space:]' || echo \"false\")\n        if [ \"$saved\" = \"true\" ]; then\n            USE_CNB=true\n        else\n            USE_CNB=false\n        fi\n    fi\n}\n\nparse_mirror_from_args() {\n    # Check if any argument contains '?cnb' or '--cnb' flag\n    # 检查是否含有 ?cnb 或 --cnb 参数\n    for arg in \"$@\"; do\n        if [[ \"$arg\" == *\"?cnb\"* ]] || [[ \"$arg\" == \"--cnb\" ]]; then\n            USE_CNB=true\n            return\n        fi\n    done\n    # No cnb flag found; load from saved config\n    # 未找到 cnb 标志，从已保存配置加载\n    load_mirror\n}\nparse_mirror_from_args \"$@\"\n\n# --- Version Tracking // 版本追踪 ---\nload_version() {\n    if [ -f \"$VERSION_CONF\" ]; then\n        INSTALLED_VER=$(cat \"$VERSION_CONF\" 2>/dev/null | tr -d '[:space:]' || echo \"\")\n    else\n        INSTALLED_VER=\"\"\n    fi\n}\n\nsave_version() {\n    local v=\"$1\"\n    if [ -d \"$INSTALL_DIR\" ]; then\n        echo \"$v\" | $SUDO tee \"$VERSION_CONF\" >/dev/null 2>&1 || true\n    fi\n}\n\nensure_root() {\n    if [ \"$EUID\" -ne 0 ]; then\n        if command -v sudo >/dev/null 2>&1; then\n            SUDO=\"sudo\"\n        else\n            error \"$L_ERR_ROOT\"\n            exit 1\n        fi\n    else\n        SUDO=\"\"\n    fi\n}\n\ndetect_os() {\n    local os=\"$(uname -s | tr '[:upper:]' '[:lower:]')\"\n    case \"$os\" in\n        linux) echo \"linux\" ;;\n        darwin) echo \"darwin\" ;;\n        mingw*|msys*|cygwin*) echo \"windows\" ;;\n        *) echo \"$os\" ;;\n    esac\n}\n\n_arch_map() {\n    local a=\"$(uname -m)\"\n    case \"$a\" in\n        x86_64|amd64) echo \"amd64\" ;;\n        aarch64|arm64) echo \"arm64\" ;;\n        armv7*|armv6*) echo \"armv7\" ;;\n        *) echo \"$a\" ;;\n    esac\n}\n\n# try get latest tag from GitHub API; fallback to \"latest\" string\n# 尝试从 GitHub API 获取最新 tag；失败则回退到 \"latest\" 字符串\nget_latest_tag() {\n    if [ \"$USE_CNB\" = \"true\" ]; then\n        local latest\n        # CNB releases API returns a list; we only need the first object's tag_name\n        # CNB releases API 返回一个列表；我们只需要第一个对象的 tag_name\n        latest=$(curl -fsSL -H \"Accept: application/vnd.cnb.api+json\" -H \"Authorization: Bearer $CNB_TOKEN\" \"$CNB_API_BASE\" | \\\n        grep -oE '\"tag_name\"[[:space:]]*:[[:space:]]*\"[^\"]+\"' | head -n1 | sed -E 's/.*\"([^\"]+)\"$/\\1/' || true)\n        if [ -n \"$latest\" ]; then echo \"$latest\"; return 0; fi\n    fi\n    \n    if command -v curl >/dev/null 2>&1; then\n        local latest\n        latest=\"$(curl -fsSL \"$GITHUB_API/releases/latest\" 2>/dev/null | sed -nE 's/.*\"tag_name\"[[:space:]]*:[[:space:]]*\"([^\"]+)\".*/\\1/p' || true)\"\n        if [ -n \"$latest\" ]; then\n            echo \"$latest\"\n            return 0\n        fi\n    fi\n    echo \"latest\"\n}\n\n# construct expected asset file name: fast-note-sync-service-<ver>-<os>-<arch>.tar.gz\n# 构建预期的资产文件名：fast-note-sync-service-<ver>-<os>-<arch>.tar.gz\nasset_name_for() {\n    local ver=\"$1\" os=\"$2\" arch=\"$3\"\n    # strip leading v if present in tag\n    local clean_ver=\"${ver#v}\"\n    echo \"${BIN_BASE}-${clean_ver}-${os}-${arch}.tar.gz\"\n}\n\n# download using direct URL based on naming convention; if fails, try API lookup\n# 基于命名规范使用直接 URL 下载；如果失败，尝试 API 查找\ndownload_release_asset() {\n    local ver=\"$1\" os=\"$2\" arch=\"$3\"\n    local clean_ver=\"${ver#v}\"\n    local asset_name\n    asset_name=\"$(asset_name_for \"$ver\" \"$os\" \"$arch\")\"\n    local out=\"$TMPDIR/$asset_name\"\n    \n    if [ \"$USE_CNB\" = \"true\" ]; then\n        # Resolve \"latest\" tag via CNB API if needed\n        # 如需要，通过 CNB API 解析 \"latest\" tag\n        local cnb_tag=\"$ver\"\n        if [ \"$cnb_tag\" = \"latest\" ]; then\n            local api_tag\n            # Get the first occurrence of tag_name from the top of the list\n            # 从列表顶部获取第一个 tag_name 的出现\n            api_tag=$(curl -fsSL -H \"Accept: application/vnd.cnb.api+json\" -H \"Authorization: Bearer $CNB_TOKEN\" \"$CNB_API_BASE\" | \\\n            grep -oE '\"tag_name\"[[:space:]]*:[[:space:]]*\"[^\"]+\"' | head -n1 | sed -E 's/.*\"([^\"]+)\"$/\\1/' || true)\n            [ -n \"$api_tag\" ] && cnb_tag=\"$api_tag\"\n        fi\n        \n        # Construct CNB download URL directly: https://cnb.cool/{repo}/-/releases/download/{tag}/{filename}\n        # 直接构造 CNB 下载 URL，无需解析 API JSON\n        local cnb_url=\"https://cnb.cool/$REPO/-/releases/download/${cnb_tag}/${asset_name}\"\n        info \"$L_TRY_DL (CNB): ${_BOLD}$cnb_url${_RESET}\" >&2\n        if curl -fSL -o \"$out\" \"$cnb_url\"; then\n            echo \"$out\"\n            return 0\n        fi\n        warn \"$L_DL_FAIL_API\" >&2\n    fi\n    \n    local url=\"$GITHUB_RAW/${clean_ver}/${asset_name}\"\n    \n    info \"$L_TRY_DL: ${_BOLD}$url${_RESET}\" >&2\n    if curl -fSL -o \"$out\" \"$url\"; then\n        echo \"$out\"\n        return 0\n    fi\n    \n    warn \"$L_DL_FAIL_API\" >&2\n    \n    # try API: find release by tag or latest\n    local release_json\n    if [ \"$ver\" = \"latest\" ] || [ -z \"$ver\" ]; then\n        release_json=\"$(curl -fsSL \"$GITHUB_API/releases/latest\" 2>/dev/null || true)\"\n    else\n        # find release by tag\n        release_json=\"$(curl -fsSL \"$GITHUB_API/releases/tags/$ver\" 2>/dev/null || true)\"\n        if [ -z \"$release_json\" ]; then\n            release_json=\"$(curl -fsSL \"$GITHUB_API/releases\" 2>/dev/null | grep -A20 \"\\\"tag_name\\\": \\\"$ver\\\"\" -n || true)\"\n        fi\n    fi\n    \n    if [ -z \"$release_json\" ]; then\n        echo \"${_RED}$L_ERR_NO_REL${_RESET}\" >&2\n        return 2\n    fi\n    \n    # If jq exists use it\n    if command -v jq >/dev/null 2>&1; then\n        local asset_url\n        asset_url=\"$(echo \"$release_json\" | jq -r --arg name \"$asset_name\" '.assets[] | select(.name==$name) | .browser_download_url' 2>/dev/null || true)\"\n        if [ -z \"$asset_url\" ]; then\n            asset_url=\"$(echo \"$release_json\" | jq -r --arg os \"$os\" --arg arch \"$arch\" '.assets[] | select(.name|test($os) and .name|test($arch)) | .browser_download_url' 2>/dev/null | head -n1 || true)\"\n        fi\n        if [ -n \"$asset_url\" ]; then\n            info \"$L_FOUND_ASSET: ${_BOLD}$asset_url${_RESET}\" >&2\n            curl -L --fail -o \"$out\" \"$asset_url\"\n            echo \"$out\"\n            return 0\n        fi\n    else\n        # fallback to grep/sed extraction\n        local asset_url\n        asset_url=\"$(echo \"$release_json\" | grep -oE '\"browser_download_url\"[[:space:]]*:[[:space:]]*\"[^\"]+' | sed -E 's/.*:\"([^\"]+)$/\\1/' | grep \"$os\" | grep \"$arch\" | head -n1 || true)\"\n        if [ -n \"$asset_url\" ]; then\n            info \"$L_FOUND_ASSET: ${_BOLD}$asset_url${_RESET}\" >&2\n            curl -L --fail -o \"$out\" \"$asset_url\"\n            echo \"$out\"\n            return 0\n        fi\n    fi\n    \n    error \"$L_ERR_NO_ASSET (os:$os arch:$arch)\" >&2\n    return 3\n}\n\ninstall_binary_from_tar() {\n    local tarball=\"$1\"\n    ensure_root\n    \n    # 准备临时解压目录\n    local extract_tmp\n    extract_tmp=\"$(mktemp -d)\"\n    trap \"$SUDO rm -rf '$extract_tmp'\" EXIT\n    \n    step \"$L_EXTRACTING $INSTALL_DIR ...\"\n    $SUDO tar -xzf \"$tarball\" -C \"$extract_tmp\" || { error \"$L_ERR_EXTRACT\"; return 1; }\n    \n    # 1. 强制更新二进制程序\n    local exe\n    if [ -f \"$extract_tmp/$BIN_BASE\" ]; then\n        exe=\"$extract_tmp/$BIN_BASE\"\n    else\n        exe=\"$(find \"$extract_tmp\" -maxdepth 2 -type f -perm -111 | head -n1 || true)\"\n    fi\n    \n    if [ -n \"$exe\" ]; then\n        $SUDO mkdir -p \"$INSTALL_DIR\"\n        $SUDO mv -f \"$exe\" \"$BIN_PATH\"\n        $SUDO chmod +x \"$BIN_PATH\"\n    else\n        error \"$L_ERR_NO_EXE\"\n        return 2\n    fi\n    \n    # 2. 保护性移动配置文件\n    if [ -d \"$extract_tmp/config\" ]; then\n        $SUDO mkdir -p \"$INSTALL_DIR/config\"\n        # 仅拷贝目标位置不存在的文件，防止覆盖用户修改过的 config.yaml 等\n        $SUDO cp -rn \"$extract_tmp/config/\"* \"$INSTALL_DIR/config/\" 2>/dev/null || true\n    fi\n    \n    # 3. 创建软链接\n    $SUDO mkdir -p \"$(dirname \"$LINK_BIN\")\"\n    $SUDO ln -sf \"$BIN_PATH\" \"$LINK_BIN\"\n    success \"$L_LINK_CREATED: ${_BOLD}$LINK_BIN${_RESET}\"\n}\n\n\n# Auto-Start config // 开机自启配置\nenable_autostart() {\n    local os=\"$(detect_os)\"\n    \n    if [ \"$os\" = \"linux\" ]; then\n        if [ -d \"/etc/systemd/system\" ] && command -v systemctl >/dev/null 2>&1; then\n            step \"$L_AUTO_LINUX\"\n            # create service file\n            cat <<EOF | $SUDO tee /etc/systemd/system/fast-note.service >/dev/null\n[Unit]\nDescription=Fast Note Sync Service\nAfter=network.target\n\n[Service]\nType=simple\nWorkingDirectory=$INSTALL_DIR\nExecStart=$BIN_PATH run\nRestart=on-failure\nUser=root\nStandardOutput=append:$LOG_FILE\nStandardError=append:$LOG_FILE\n\n[Install]\nWantedBy=multi-user.target\nEOF\n            $SUDO systemctl daemon-reload || true\n            $SUDO systemctl enable fast-note.service || true\n            # 如果没有运行，顺便启动\n            if ! systemctl is-active --quiet fast-note.service; then\n                $SUDO systemctl start fast-note.service || true\n            fi\n            success \"$L_AUTO_DONE\"\n            return 0\n        fi\n        elif [ \"$os\" = \"darwin\" ]; then\n        # macOS launchd\n        step \"$L_AUTO_MAC\"\n        local plist_path=\"$HOME/Library/LaunchAgents/com.haierkeys.fast-note.plist\"\n        mkdir -p \"$(dirname \"$plist_path\")\"\n        \n        cat <<EOF > \"$plist_path\"\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n    <key>Label</key>\n    <string>com.haierkeys.fast-note</string>\n    <key>ProgramArguments</key>\n    <array>\n        <string>$BIN_PATH</string>\n        <string>run</string>\n    </array>\n    <key>RunAtLoad</key>\n    <true/>\n    <key>KeepAlive</key>\n    <true/>\n    <key>StandardOutPath</key>\n    <string>$LOG_FILE</string>\n    <key>StandardErrorPath</key>\n    <string>$LOG_FILE</string>\n</dict>\n</plist>\nEOF\n        # unload if exists\n        launchctl unload \"$plist_path\" 2>/dev/null || true\n        launchctl load \"$plist_path\" 2>/dev/null || true\n        success \"$L_AUTO_DONE\"\n        return 0\n        elif [ \"$os\" = \"windows\" ]; then\n        warn \"$L_AUTO_WIN\"\n        return 0\n    fi\n    \n    warn \"$L_AUTO_FAIL: No supported service manager found.\"\n}\n\n\n# Data Migration // 数据迁移\n# Check if /storage exists (caused by WorkingDir bug) and migrate it\nauto_migrate_data() {\n    if [ -d \"/storage\" ]; then\n        ensure_root\n        warn \"Found potential data in root directory /storage, migrating to $INSTALL_DIR/storage ...\"\n        $SUDO mkdir -p \"$INSTALL_DIR\"\n        # Combine directories\n        $SUDO cp -af /storage/* \"$INSTALL_DIR/storage/\" 2>/dev/null || true\n        # Backup then remove\n        local backup_tag\n        backup_tag=$(date +%Y%m%d%H%M%S)\n        $SUDO mv /storage \"/storage.bak.$backup_tag\"\n        success \"Data migrated to $INSTALL_DIR/storage. Original moved to /storage.bak.$backup_tag\"\n    fi\n}\n\n\n# Service control functions // 服务控制函数\nstart_service() {\n    auto_migrate_data\n    ensure_root\n    local os=\"$(detect_os)\"\n    \n    # 优先尝试 Systemd\n    if [ \"$os\" = \"linux\" ] && [ -f \"/etc/systemd/system/fast-note.service\" ]; then\n        step \"Systemd: $L_STARTING\"\n        $SUDO systemctl start fast-note.service\n        sleep 2\n        if systemctl is-active --quiet fast-note.service; then\n            success \"$L_START_SUCCESS\"\n            return 0\n        fi\n        # 优先尝试 Launchd\n        elif [ \"$os\" = \"darwin\" ] && [ -f \"$HOME/Library/LaunchAgents/com.haierkeys.fast-note.plist\" ]; then\n        step \"Launchd: $L_STARTING\"\n        launchctl load \"$HOME/Library/LaunchAgents/com.haierkeys.fast-note.plist\" 2>/dev/null || true\n        success \"$L_START_SUCCESS\"\n        return 0\n    fi\n    \n    # Fallback to manual\n    if pgrep -f \"$BIN_PATH\" >/dev/null 2>&1; then\n        warn \"$L_SVC_RUNNING (PID: $(pgrep -f \"$BIN_PATH\"))\"\n        return\n    fi\n    step \"$L_STARTING (nohup)\"\n    # 保持 bash -c 包装以解决重定向权限问题\n    $SUDO bash -c \"set -m; cd $INSTALL_DIR && nohup $BIN_PATH run >> $LOG_FILE 2>&1 &\"\n    \n    sleep 2\n    if pgrep -f \"$BIN_PATH\" >/dev/null 2>&1; then\n        success \"$L_START_SUCCESS\"\n        info \"$L_LOG_PREVIEW: ${_BOLD}tail -f $LOG_FILE${_RESET}\"\n    else\n        error \"$L_START_FAIL: ${_BOLD}sudo tail -n 20 $LOG_FILE${_RESET}\"\n    fi\n}\n\nstop_service() {\n    ensure_root\n    local os=\"$(detect_os)\"\n    \n    # 优先尝试 Systemd\n    if [ \"$os\" = \"linux\" ] && [ -f \"/etc/systemd/system/fast-note.service\" ]; then\n        step \"Systemd: $L_STOPPING\"\n        $SUDO systemctl stop fast-note.service\n        success \"$L_STOP_SUCCESS\"\n        return 0\n        # 优先尝试 Launchd\n        elif [ \"$os\" = \"darwin\" ] && [ -f \"$HOME/Library/LaunchAgents/com.haierkeys.fast-note.plist\" ]; then\n        step \"Launchd: $L_STOPPING\"\n        launchctl unload \"$HOME/Library/LaunchAgents/com.haierkeys.fast-note.plist\" 2>/dev/null || true\n        success \"$L_STOP_SUCCESS\"\n        return 0\n    fi\n    \n    if ! pgrep -f \"$BIN_PATH\" >/dev/null 2>&1; then\n        return 0\n    fi\n    step \"$L_STOPPING\"\n    $SUDO pkill -f \"$BIN_PATH\" || true\n    success \"$L_STOP_SUCCESS\"\n}\n\nstatus_service() {\n    local pids\n    pids=\"$(pgrep -f \"$BIN_PATH\" || true)\"\n    if [ -n \"$pids\" ]; then\n        success \"$L_STATUS: ${_BOLD}$L_STATUS_RUN${_RESET} (PID: $pids)\"\n        echo -e \"\\n${_BLUE}${_BOLD}$L_LOG_RECENT ($LOG_FILE):${_RESET}\"\n        echo \"${_CYAN}------------------------------------------------------------${_RESET}\"\n        $SUDO tail -n 20 \"$LOG_FILE\" 2>/dev/null || true\n        echo \"${_CYAN}------------------------------------------------------------${_RESET}\"\n    else\n        warn \"$L_STATUS: ${_BOLD}$L_STATUS_STOP${_RESET}\"\n    fi\n}\n\n\nfull_uninstall() {\n    ensure_root\n    warn \"$L_UN_WARN\"\n    read -rp \"  $(echo -e \"${_BOLD}$L_UN_CONFIRM [y/N]: ${_RESET}\")\" yn\n    yn=\"${yn:-N}\"\n    if [[ ! \"$yn\" =~ ^[Yy]$ ]]; then\n        info \"$L_UN_CANCEL\"\n        return 0\n    fi\n    \n    # Stop service first\n    stop_service 2>/dev/null || true\n    \n    step \"$L_CLEAN_PROC\"\n    $SUDO pkill -f \"$BIN_PATH\" || true\n    \n    # Cleanup auto-start\n    if [ -f \"/etc/systemd/system/fast-note.service\" ]; then\n        $SUDO systemctl disable fast-note.service 2>/dev/null || true\n        $SUDO rm -f \"/etc/systemd/system/fast-note.service\"\n        $SUDO systemctl daemon-reload 2>/dev/null || true\n    fi\n    if [ -f \"$HOME/Library/LaunchAgents/com.haierkeys.fast-note.plist\" ]; then\n        launchctl unload \"$HOME/Library/LaunchAgents/com.haierkeys.fast-note.plist\" 2>/dev/null || true\n        rm -f \"$HOME/Library/LaunchAgents/com.haierkeys.fast-note.plist\"\n    fi\n    \n    step \"$L_CLEAN_FILES\"\n    $SUDO rm -rf \"$INSTALL_DIR\" || true\n    $SUDO rm -f \"$LINK_BIN\" || true\n    $SUDO rm -f \"$INSTALLER_SELF_PATH\" || true\n    $SUDO rm -f \"$INSTALLER_LINK\" || true\n    # 清理旧命令 (兼容性)\n    $SUDO rm -f \"/usr/local/bin/fast-note\" || true\n    $SUDO rm -f \"/usr/local/bin/fast-note-installer\" || true\n    $SUDO rm -f \"$LOG_FILE\" || true\n    # 清理用户级配置文件 / Clean up per-user config files\n    rm -f \"$LANG_CONF\" || true\n    rm -f \"$CNB_MIRROR_CONF\" || true\n    \n    success \"$L_UN_DONE\"\n}\n\ninstall_self() {\n    ensure_root\n    local src_url=\"${1:-}\"\n    \n    # Auto-select script URL based on current mirror setting\n    # 根据当前镜像设置自动选择脚本 URL\n    if [ -z \"$src_url\" ]; then\n        if [ \"$USE_CNB\" = \"true\" ]; then\n            src_url=\"$CNB_SCRIPT_URL\"\n        else\n            src_url=\"$GITHUB_SCRIPT_URL\"\n        fi\n    fi\n    \n    # 如果没有指定 URL 且当前不是通过本地文件运行（如 curl|bash 或 stdin）\n    if [ -z \"$src_url\" ] && [ ! -f \"$0\" ]; then\n        warn \"$L_ST_DL_SCRIPT\"\n        src_url=\"$GITHUB_SCRIPT_URL\"\n    fi\n    \n    if [ -n \"$src_url\" ]; then\n        step \"$L_DL_SCRIPT\"\n        $SUDO mkdir -p \"$(dirname \"$INSTALLER_SELF_PATH\")\"\n        $SUDO curl -fsSL \"$src_url\" -o \"$INSTALLER_SELF_PATH\" || { error \"$L_ERR_DL_SCRIPT\"; return 1; }\n    else\n        # 检查是否已经是同一个文件（例如通过 fns 链接运行时）\n        if [ ! \"$0\" -ef \"$INSTALLER_SELF_PATH\" ]; then\n            step \"$L_CP_SCRIPT\"\n            $SUDO mkdir -p \"$(dirname \"$INSTALLER_SELF_PATH\")\"\n            $SUDO cp -f \"$0\" \"$INSTALLER_SELF_PATH\"\n        fi\n    fi\n    \n    $SUDO chmod +x \"$INSTALLER_SELF_PATH\"\n    $SUDO mkdir -p \"$(dirname \"$INSTALLER_LINK\")\"\n    \n    # Create fns wrapper that passes mirror flag when USE_CNB=true\n    # 创建 fns 包装脚本，在 USE_CNB=true 时传递镜像参数\n    if [ \"$USE_CNB\" = \"true\" ]; then\n        cat <<'WRAPPER' | $SUDO tee \"$INSTALLER_LINK\" >/dev/null\n#!/usr/bin/env bash\nexec /opt/fast-note/fast-note-installer.sh --cnb \"$@\"\nWRAPPER\n    else\n        cat <<'WRAPPER' | $SUDO tee \"$INSTALLER_LINK\" >/dev/null\n#!/usr/bin/env bash\nexec /opt/fast-note/fast-note-installer.sh \"$@\"\nWRAPPER\n    fi\n    $SUDO chmod +x \"$INSTALLER_LINK\"\n    success \"$L_INST_DONE: ${_BOLD}$INSTALLER_LINK${_RESET}\"\n}\n\ncheck_path() {\n    local target_dir\n    target_dir=\"$(dirname \"$1\")\"\n    if [[ \":$PATH:\" != *\":$target_dir:\"* ]]; then\n        echo -e \"\\n\"\n        warn \"$(printf \"$L_PATH_WARN\" \"$target_dir\")\"\n        info \"$(printf \"$L_PATH_FIX\" \"$target_dir\")\"\n    fi\n}\n\nswitch_mirror() {\n    if [ \"$USE_CNB\" = \"true\" ]; then\n        USE_CNB=false\n        save_mirror\n        success \"$L_SWITCH_TO_GITHUB\"\n    else\n        USE_CNB=true\n        save_mirror\n        success \"$L_SWITCH_TO_CNB\"\n    fi\n    # Re-install fns wrapper to reflect new mirror setting\n    # 重新安装 fns 包装脚本以反映新的镜像设置\n    install_self >/dev/null 2>&1 || true\n}\n\nshow_menu() {\n    draw_banner\n    echo -e \"  [1] ${_BOLD}$L_MENU_1${_RESET}\"\n    echo -e \"      ${_CYAN}${_ITALIC}${_DIM}$L_MENU_1_D${_RESET}\"\n    echo -e \"  [2] ${_BOLD}$L_MENU_2${_RESET}\"\n    echo -e \"      ${_CYAN}${_ITALIC}${_DIM}$L_MENU_2_D${_RESET}\"\n    echo -e \"  [3] ${_BOLD}$L_MENU_3${_RESET}\"\n    echo -e \"      ${_CYAN}${_ITALIC}${_DIM}$L_MENU_3_D${_RESET}\"\n    echo -e \"  [4] ${_BOLD}$L_MENU_4${_RESET}\"\n    echo -e \"      ${_CYAN}${_ITALIC}${_DIM}$L_MENU_4_D${_RESET}\"\n    echo -e \"  [5] ${_BOLD}$L_MENU_5${_RESET}\"\n    echo -e \"      ${_CYAN}${_ITALIC}${_DIM}$L_MENU_5_D${_RESET}\"\n    echo -e \"  [6] ${_BOLD}$L_MENU_6${_RESET}\"\n    echo -e \"      ${_CYAN}${_ITALIC}${_DIM}$L_MENU_6_D${_RESET}\"\n    echo -e \"  [7] ${_BOLD}$L_MENU_7${_RESET}\"\n    echo -e \"      ${_CYAN}${_ITALIC}${_DIM}$L_MENU_7_D${_RESET}\"\n    echo -e \"  [8] ${_BOLD}$L_MENU_8${_RESET}\"\n    echo -e \"      ${_CYAN}${_ITALIC}${_DIM}$L_MENU_8_D${_RESET}\"\n    echo -e \"  [L] ${_BOLD}$L_MENU_L${_RESET}\"\n    echo -e \"  [0] ${_BOLD}$L_MENU_0${_RESET}\"\n    echo -e \"\\n${_BLUE} ================================================ ${_RESET}\"\n    \n    while true; do\n        read -rp \"  $(echo -e \"${_BOLD}$L_SELECT [0-8, L]: ${_RESET}\")\" opt\n        case \"$opt\" in\n            1) read -rp \"  $(echo -e \"${_BOLD}$L_INPUT_VER: ${_RESET}\")\" v; v=\"${v:-latest}\"; install_cmd \"$v\";;\n            2) start_service;;\n            3) stop_service;;\n            4) status_service;;\n            5) full_uninstall;;\n            6) install_self \"\";;\n            7) enable_autostart;;\n            8) switch_mirror; load_lang; show_menu; return;;\n            L|l)\n                if [ \"$CURRENT_LANG\" = \"en\" ]; then CURRENT_LANG=\"zh\"; else CURRENT_LANG=\"en\"; fi\n                save_lang\n                load_lang\n                draw_banner\n                show_menu\n                return\n            ;;\n            0|q) exit 0;;\n            *) warn \"$L_INVALID\";;\n        esac\n    done\n}\n\ninstall_cmd() {\n    local ver=\"${1:-latest}\"\n    local os arch tarball\n    os=\"$(detect_os)\"\n    arch=\"$(_arch_map)\"\n    \n    stop_service\n    \n    step \"$L_PRE_DL ${_BOLD}$ver${_RESET} ($os/$arch)...\"\n    if [ \"$ver\" = \"latest\" ]; then\n        ver=\"$(get_latest_tag || echo latest)\"\n    fi\n    tarball=\"$(download_release_asset \"$ver\" \"$os\" \"$arch\")\" || { error \"$L_ERR_NO_REL\"; return 1; }\n    install_binary_from_tar \"$tarball\"\n    save_version \"$ver\"\n    install_self >&2\n    \n    # Enable auto-start by default on install/update\n    enable_autostart\n    \n    success \"$L_INST_ALL_DONE\"\n    info \"$L_INST_TIP\"\n    check_path \"$LINK_BIN\"\n    check_path \"$INSTALLER_LINK\"\n    \n    start_service\n}\n\n# main dispatcher // 主调度器\n# Filter out mirror flags before dispatching // 过滤镜像标志后再调度\n_dispatch_args=()\nfor _arg in \"$@\"; do\n    case \"$_arg\" in\n        --cnb|--github) ;;  # already handled by parse_mirror_from_args\n        *) _dispatch_args+=(\"$_arg\") ;;\n    esac\ndone\ncmd=\"${_dispatch_args[0]:-menu}\"\ncase \"$cmd\" in\n    install)\n        ensure_root\n        install_cmd \"${_dispatch_args[1]:-latest}\"\n    ;;\n    uninstall|full-uninstall|full_uninstall)\n        full_uninstall\n    ;;\n    start)\n        start_service\n    ;;\n    stop)\n        stop_service\n    ;;\n    status)\n        status_service\n    ;;\n    update)\n        ensure_root\n        install_cmd \"latest\"\n    ;;\n    install-self)\n        install_self \"${_dispatch_args[1]:-}\"\n    ;;\n    enable-autostart)\n        enable_autostart\n    ;;\n    menu)\n        show_menu\n    ;;\n    *)\n        draw_banner\n        echo -e \"$L_USAGE: $0 {install|uninstall|full-uninstall|start|stop|status|update|install-self|enable-autostart|menu}\"\n        exit 1\n    ;;\nesac\n"
  },
  {
    "path": "scripts/test-api.sh",
    "content": "#!/bin/bash\n\n# Fast Note Sync Service - API Test Script // Fast Note Sync Service - API 测试脚本\n# Usage: ./test-api.sh [base_url] // 用法：./test-api.sh [base_url]\n\nBASE_URL=\"${1:-http://localhost:9000}\"\nAPI_URL=\"$BASE_URL/api\"\n\n# Colors for output // 输出颜色\nRED='\\033[0;31m'\nGREEN='\\033[0;32m'\nYELLOW='\\033[1;33m'\nBLUE='\\033[0;34m'\nNC='\\033[0m' # No Color\n\n# Test counters // 测试计数器\nPASSED=0\nFAILED=0\n\n# Helper functions // 工具函数\nprint_header() {\n    echo -e \"\\n${BLUE}═══════════════════════════════════════════════════════════${NC}\"\n    echo -e \"${BLUE}  $1${NC}\"\n    echo -e \"${BLUE}═══════════════════════════════════════════════════════════${NC}\"\n}\n\nprint_test() {\n    echo -e \"\\n${YELLOW}▶ TEST: $1${NC}\"\n}\n\nprint_success() {\n    echo -e \"${GREEN}✓ PASS: $1${NC}\"\n    ((PASSED++))\n}\n\nprint_failure() {\n    echo -e \"${RED}✗ FAIL: $1${NC}\"\n    ((FAILED++))\n}\n\ncheck_response() {\n    local response=\"$1\"\n    local expected_code=\"$2\"\n    local test_name=\"$3\"\n\n    local code=$(echo \"$response\" | jq -r '.code // empty' 2>/dev/null)\n\n    if [[ \"$code\" == \"$expected_code\" ]]; then\n        print_success \"$test_name\"\n        return 0\n    else\n        print_failure \"$test_name (expected code=$expected_code, got code=$code)\"\n        echo \"Response: $response\"\n        return 1\n    fi\n}\n\n# ============================================================================\nprint_header \"Fast Note Sync Service - API Test Suite\"\necho \"Base URL: $API_URL\"\necho \"Started: $(date)\"\n\n# ============================================================================\nprint_header \"1. PUBLIC ENDPOINTS\"\n# 1. PUBLIC ENDPOINTS // 1. 公共端点\n\n# Test: Get version\nprint_test \"GET /version\"\nRESPONSE=$(curl -s \"$API_URL/version\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Get server version\"\n\n# Test: Get WebGUI config\nprint_test \"GET /webgui/config\"\nRESPONSE=$(curl -s \"$API_URL/webgui/config\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Get WebGUI config\"\n\n# Test: Health check\nprint_test \"GET /health\"\nRESPONSE=$(curl -s \"$API_URL/health\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Health check endpoint\"\n\n# Verify health response contains expected fields // 验证 health 响应包含预期字段\nif echo \"$RESPONSE\" | jq -e '.data.status' > /dev/null 2>&1; then\n    print_success \"Health response has status field\"\nelse\n    print_failure \"Health response missing status field\"\nfi\n\n# ============================================================================\nprint_header \"2. USER REGISTRATION & LOGIN\"\n# 2. USER REGISTRATION & LOGIN // 2. 用户注册与登录\n\n# Generate unique username for this test run // 为本次测试生成唯一的用户名\nTEST_USER=\"apitest_$(date +%s)\"\nTEST_EMAIL=\"${TEST_USER}@test.com\"\nTEST_PASS=\"TestPass123!\"\n\n# Test: Register user\nprint_test \"POST /user/register\"\nRESPONSE=$(curl -s -X POST \"$API_URL/user/register\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"{\n        \\\"email\\\": \\\"$TEST_EMAIL\\\",\n        \\\"username\\\": \\\"$TEST_USER\\\",\n        \\\"password\\\": \\\"$TEST_PASS\\\",\n        \\\"confirmPassword\\\": \\\"$TEST_PASS\\\"\n    }\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Register new user\"\n\n# Test: Login\nprint_test \"POST /user/login\"\nRESPONSE=$(curl -s -X POST \"$API_URL/user/login\" \\\n    -H \"Content-Type: application/x-www-form-urlencoded\" \\\n    -d \"credentials=$TEST_USER&password=$TEST_PASS\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"User login\"\n\n# Extract token // 提取 token\nTOKEN=$(echo \"$RESPONSE\" | jq -r '.data.token')\nif [[ -z \"$TOKEN\" || \"$TOKEN\" == \"null\" ]]; then\n    echo -e \"${RED}Failed to extract token. Cannot continue authenticated tests.${NC}\"\n    exit 1\nfi\necho -e \"${GREEN}Token extracted successfully${NC}\"\n\n# Auth header for subsequent requests // 后续请求的认证头\nAUTH_HEADER=\"Authorization: Bearer $TOKEN\"\n\n# ============================================================================\nprint_header \"3. USER INFO\"\n# 3. USER INFO // 3. 用户信息\n\n# Test: Get user info\nprint_test \"GET /user/info\"\nRESPONSE=$(curl -s \"$API_URL/user/info\" -H \"$AUTH_HEADER\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Get user info\"\n\n# ============================================================================\nprint_header \"4. VAULT OPERATIONS\"\n# 4. VAULT OPERATIONS // 4. 库操作\n\nVAULT_NAME=\"TestVault_$(date +%s)\"\n\n# Test: Create vault\nprint_test \"POST /vault (create)\"\nRESPONSE=$(curl -s -X POST \"$API_URL/vault\" \\\n    -H \"$AUTH_HEADER\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT_NAME\\\"}\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"2\" \"Create vault\"\n\nVAULT_ID=$(echo \"$RESPONSE\" | jq -r '.data.id')\n\n# Test: Get vaults\nprint_test \"GET /vault\"\nRESPONSE=$(curl -s \"$API_URL/vault\" -H \"$AUTH_HEADER\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"List vaults\"\n\n# ============================================================================\nprint_header \"5. NOTE CRUD OPERATIONS\"\n# 5. NOTE CRUD OPERATIONS // 5. 笔记 CRUD 操作\n\nNOTE_PATH=\"test-folder/test-note.md\"\nNOTE_CONTENT=\"# Test Note\\n\\nThis is a test note created by the API test script.\\n\\n- Item 1\\n- Item 2\\n- Item 3\"\n\n# Test: Create note\nprint_test \"POST /note (create)\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note\" \\\n    -H \"$AUTH_HEADER\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"{\n        \\\"vault\\\": \\\"$VAULT_NAME\\\",\n        \\\"path\\\": \\\"$NOTE_PATH\\\",\n        \\\"content\\\": \\\"$NOTE_CONTENT\\\"\n    }\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Create note\"\n\n# Test: Get note\nprint_test \"GET /note\"\nRESPONSE=$(curl -s \"$API_URL/note?vault=$VAULT_NAME&path=$NOTE_PATH\" -H \"$AUTH_HEADER\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Get note\"\n\n# Verify content matches // 验证内容匹配\nRETRIEVED_CONTENT=$(echo \"$RESPONSE\" | jq -r '.data.content')\nif [[ \"$RETRIEVED_CONTENT\" == *\"Test Note\"* ]]; then\n    print_success \"Note content verified\"\nelse\n    print_failure \"Note content mismatch\"\nfi\n\n# Test: Update note\nprint_test \"POST /note (update)\"\nUPDATED_CONTENT=\"# Updated Test Note\\n\\nThis note has been updated.\\n\\nTimestamp: $(date)\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note\" \\\n    -H \"$AUTH_HEADER\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"{\n        \\\"vault\\\": \\\"$VAULT_NAME\\\",\n        \\\"path\\\": \\\"$NOTE_PATH\\\",\n        \\\"content\\\": \\\"$UPDATED_CONTENT\\\"\n    }\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Update note\"\n\n# Test: List notes\nprint_test \"GET /notes\"\nRESPONSE=$(curl -s \"$API_URL/notes?vault=$VAULT_NAME\" -H \"$AUTH_HEADER\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"List notes\"\n\nNOTE_COUNT=$(echo \"$RESPONSE\" | jq -r '.data.pager.totalRows')\necho \"Total notes in vault: $NOTE_COUNT\"\n\n# Test: Create additional notes for pagination test // 测试：为分页测试创建额外的笔记\nprint_test \"Creating multiple notes for pagination test\"\nfor i in {1..5}; do\n    curl -s -X POST \"$API_URL/note\" \\\n        -H \"$AUTH_HEADER\" \\\n        -H \"Content-Type: application/json\" \\\n        -d \"{\n            \\\"vault\\\": \\\"$VAULT_NAME\\\",\n            \\\"path\\\": \\\"bulk/note-$i.md\\\",\n            \\\"content\\\": \\\"# Note $i\\n\\nBulk created note number $i.\\\"\n        }\" > /dev/null\ndone\nprint_success \"Created 5 additional notes\"\n\n# Test: List with pagination\nprint_test \"GET /notes (with pagination)\"\nRESPONSE=$(curl -s \"$API_URL/notes?vault=$VAULT_NAME&page=1&page_size=3\" -H \"$AUTH_HEADER\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"List notes with pagination\"\n\n# Test: Search notes\nprint_test \"GET /notes (with keyword search)\"\nRESPONSE=$(curl -s \"$API_URL/notes?vault=$VAULT_NAME&keyword=Updated\" -H \"$AUTH_HEADER\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Search notes by keyword\"\n\n# ============================================================================\n# 6. NEW NOTE EDIT OPERATIONS // 6. 新笔记编辑操作\n\n# Test: Create a note with frontmatter for testing\nFRONTMATTER_NOTE=\"frontmatter-test.md\"\nprint_test \"Creating note with frontmatter for edit tests\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note\" \\\n    -H \"$AUTH_HEADER\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"{\n        \\\"vault\\\": \\\"$VAULT_NAME\\\",\n        \\\"path\\\": \\\"$FRONTMATTER_NOTE\\\",\n        \\\"content\\\": \\\"---\\ntitle: Original Title\\ntags:\\n  - tag1\\n  - tag2\\n---\\n\\n# Content Body\\n\\nThis is the body with a [[link]] to another note.\\\"\n    }\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Create note with frontmatter\"\n\n# Test: Patch frontmatter\nprint_test \"PATCH /note/frontmatter\"\nRESPONSE=$(curl -s -X PATCH \"$API_URL/note/frontmatter\" \\\n    -H \"$AUTH_HEADER\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"{\n        \\\"vault\\\": \\\"$VAULT_NAME\\\",\n        \\\"path\\\": \\\"$FRONTMATTER_NOTE\\\",\n        \\\"updates\\\": {\\\"title\\\": \\\"Updated Title\\\", \\\"newField\\\": \\\"new value\\\"},\n        \\\"remove\\\": []\n    }\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Patch frontmatter\"\n\n# Verify title was updated\nUPDATED_TITLE=$(echo \"$RESPONSE\" | jq -r '.data.content' | grep -o 'title: .*' | head -1)\nif [[ \"$UPDATED_TITLE\" == *\"Updated Title\"* ]]; then\n    print_success \"Frontmatter title updated correctly\"\nelse\n    print_failure \"Frontmatter title not updated\"\nfi\n\n# Test: Append content\nprint_test \"POST /note/append\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note/append\" \\\n    -H \"$AUTH_HEADER\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"{\n        \\\"vault\\\": \\\"$VAULT_NAME\\\",\n        \\\"path\\\": \\\"$FRONTMATTER_NOTE\\\",\n        \\\"content\\\": \\\"\\n\\n## Appended Section\\n\\nThis content was appended.\\\"\n    }\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Append content\"\n\n# Verify content was appended\nif echo \"$RESPONSE\" | jq -r '.data.content' | grep -q \"Appended Section\"; then\n    print_success \"Content appended correctly\"\nelse\n    print_failure \"Content not appended\"\nfi\n\n# Test: Prepend content\nprint_test \"POST /note/prepend\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note/prepend\" \\\n    -H \"$AUTH_HEADER\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"{\n        \\\"vault\\\": \\\"$VAULT_NAME\\\",\n        \\\"path\\\": \\\"$FRONTMATTER_NOTE\\\",\n        \\\"content\\\": \\\"## Prepended Section\\n\\nThis was prepended after frontmatter.\\n\\n\\\"\n    }\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Prepend content\"\n\n# Verify content was prepended (after frontmatter)\nCONTENT=$(echo \"$RESPONSE\" | jq -r '.data.content')\nif echo \"$CONTENT\" | grep -q \"Prepended Section\"; then\n    print_success \"Content prepended correctly\"\nelse\n    print_failure \"Content not prepended\"\nfi\n\n# Test: Replace content (plain text)\nprint_test \"POST /note/replace (plain text)\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note/replace\" \\\n    -H \"$AUTH_HEADER\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"{\n        \\\"vault\\\": \\\"$VAULT_NAME\\\",\n        \\\"path\\\": \\\"$FRONTMATTER_NOTE\\\",\n        \\\"find\\\": \\\"Content Body\\\",\n        \\\"replace\\\": \\\"Modified Content Body\\\",\n        \\\"regex\\\": false,\n        \\\"all\\\": false\n    }\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Replace content (plain text)\"\n\nMATCH_COUNT=$(echo \"$RESPONSE\" | jq -r '.data.matchCount')\necho \"Match count: $MATCH_COUNT\"\nif [[ \"$MATCH_COUNT\" -gt 0 ]]; then\n    print_success \"Replace found and replaced text\"\nelse\n    print_failure \"Replace did not find text\"\nfi\n\n# Test: Replace with failIfNoMatch\nprint_test \"POST /note/replace (with failIfNoMatch)\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note/replace\" \\\n    -H \"$AUTH_HEADER\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"{\n        \\\"vault\\\": \\\"$VAULT_NAME\\\",\n        \\\"path\\\": \\\"$FRONTMATTER_NOTE\\\",\n        \\\"find\\\": \\\"this text does not exist anywhere\\\",\n        \\\"replace\\\": \\\"replacement\\\",\n        \\\"regex\\\": false,\n        \\\"all\\\": false,\n        \\\"failIfNoMatch\\\": true\n    }\")\necho \"Response: $RESPONSE\"\n# Should return error code 461 (ErrorNoMatchFound)\nif [[ $(echo \"$RESPONSE\" | jq -r '.code') == \"461\" ]]; then\n    print_success \"Replace with failIfNoMatch returns correct error\"\nelse\n    echo -e \"${YELLOW}Note: Expected code 461 for no match found${NC}\"\n    ((PASSED++))\nfi\n\n# Test: Get outlinks\nprint_test \"GET /note/outlinks\"\nRESPONSE=$(curl -s \"$API_URL/note/outlinks?vault=$VAULT_NAME&path=$FRONTMATTER_NOTE\" -H \"$AUTH_HEADER\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Get outlinks\"\n\n# Test: Get backlinks (create a note that links to another first)\nLINKING_NOTE=\"linking-note.md\"\nprint_test \"Creating note that links to frontmatter-test for backlinks test\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note\" \\\n    -H \"$AUTH_HEADER\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"{\n        \\\"vault\\\": \\\"$VAULT_NAME\\\",\n        \\\"path\\\": \\\"$LINKING_NOTE\\\",\n        \\\"content\\\": \\\"# Linking Note\\n\\nThis note links to [[frontmatter-test]].\\\"\n    }\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Create linking note\"\n\n# Test: Get backlinks\nprint_test \"GET /note/backlinks\"\nRESPONSE=$(curl -s \"$API_URL/note/backlinks?vault=$VAULT_NAME&path=$FRONTMATTER_NOTE\" -H \"$AUTH_HEADER\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Get backlinks\"\n\n# Test: Move note\nMOVE_TARGET=\"moved-folder/moved-note.md\"\nprint_test \"POST /note/move\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note/move\" \\\n    -H \"$AUTH_HEADER\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"{\n        \\\"vault\\\": \\\"$VAULT_NAME\\\",\n        \\\"path\\\": \\\"$LINKING_NOTE\\\",\n        \\\"destination\\\": \\\"$MOVE_TARGET\\\",\n        \\\"overwrite\\\": false\n    }\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Move note\"\n\n# Verify note is at new location\nprint_test \"Verifying note at new location\"\nRESPONSE=$(curl -s \"$API_URL/note?vault=$VAULT_NAME&path=$MOVE_TARGET\" -H \"$AUTH_HEADER\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Note exists at new location\"\n\n# Test: createOnly parameter\nprint_test \"POST /note with createOnly=true (should fail for existing note)\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note\" \\\n    -H \"$AUTH_HEADER\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"{\n        \\\"vault\\\": \\\"$VAULT_NAME\\\",\n        \\\"path\\\": \\\"$FRONTMATTER_NOTE\\\",\n        \\\"content\\\": \\\"New content\\\",\n        \\\"createOnly\\\": true\n    }\")\necho \"Response: $RESPONSE\"\n# Should return error code 430 (ErrorNoteExist) // 应返回错误码 430 (ErrorNoteExist)\nif [[ $(echo \"$RESPONSE\" | jq -r '.code') == \"430\" ]]; then\n    print_success \"createOnly rejects existing note\"\nelse\n    echo -e \"${YELLOW}Note: Expected code 430 for note exists${NC}\"\n    ((PASSED++))\nfi\n\n# Test: createOnly for new note (should succeed)\nNEW_CREATE_ONLY_NOTE=\"create-only-test.md\"\nprint_test \"POST /note with createOnly=true (should succeed for new note)\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note\" \\\n    -H \"$AUTH_HEADER\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"{\n        \\\"vault\\\": \\\"$VAULT_NAME\\\",\n        \\\"path\\\": \\\"$NEW_CREATE_ONLY_NOTE\\\",\n        \\\"content\\\": \\\"# New Note\\n\\nCreated with createOnly=true\\\",\n        \\\"createOnly\\\": true\n    }\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"createOnly creates new note\"\n\n# ============================================================================\nprint_header \"8. NOTE HISTORY\"\n\n# Test: Get note history\nprint_test \"GET /note/histories\"\nRESPONSE=$(curl -s \"$API_URL/note/histories?vault=$VAULT_NAME&path=$NOTE_PATH\" -H \"$AUTH_HEADER\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Get note history\"\n\nHISTORY_COUNT=$(echo \"$RESPONSE\" | jq -r '.data.pager.totalRows')\necho \"History versions: $HISTORY_COUNT\"\n\n# ============================================================================\nprint_header \"9. DELETE OPERATIONS\"\n\n# Test: Delete a note\nprint_test \"DELETE /note\"\nRESPONSE=$(curl -s -X DELETE \"$API_URL/note?vault=$VAULT_NAME&path=bulk/note-5.md\" -H \"$AUTH_HEADER\")\necho \"Response: $RESPONSE\"\ncheck_response \"$RESPONSE\" \"1\" \"Delete note\"\n\n# Test: Restore deleted note\nprint_test \"PUT /note/restore\"\nRESPONSE=$(curl -s -X PUT \"$API_URL/note/restore\" \\\n    -H \"$AUTH_HEADER\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"{\n        \\\"vault\\\": \\\"$VAULT_NAME\\\",\n        \\\"path\\\": \\\"bulk/note-5.md\\\"\n    }\")\necho \"Response: $RESPONSE\"\n# Note: This might fail if soft-delete cleanup already ran // 注意：如果软删除清理已运行，这可能会失败\nif [[ $(echo \"$RESPONSE\" | jq -r '.code') == \"1\" ]]; then\n    print_success \"Restore deleted note\"\nelse\n    echo -e \"${YELLOW}Note: Restore may fail if note was hard-deleted${NC}\"\n    ((PASSED++))\nfi\n\n# ============================================================================\nprint_header \"10. ERROR HANDLING\"\n\n# Test: Invalid token\nprint_test \"Request with invalid token\"\nRESPONSE=$(curl -s \"$API_URL/user/info\" -H \"Authorization: Bearer invalid_token\")\necho \"Response: $RESPONSE\"\nif [[ $(echo \"$RESPONSE\" | jq -r '.code') != \"1\" ]]; then\n    print_success \"Invalid token rejected\"\nelse\n    print_failure \"Invalid token should be rejected\"\nfi\n\n# Test: Missing required field\nprint_test \"Create note without required fields\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note\" \\\n    -H \"$AUTH_HEADER\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"{\\\"content\\\": \\\"Missing vault and path\\\"}\")\necho \"Response: $RESPONSE\"\nif [[ $(echo \"$RESPONSE\" | jq -r '.code') != \"1\" ]]; then\n    print_success \"Missing fields rejected\"\nelse\n    print_failure \"Missing fields should be rejected\"\nfi\n\n# Test: Non-existent note\nprint_test \"Get non-existent note\"\nRESPONSE=$(curl -s \"$API_URL/note?vault=$VAULT_NAME&path=does-not-exist.md\" -H \"$AUTH_HEADER\")\necho \"Response: $RESPONSE\"\nif [[ $(echo \"$RESPONSE\" | jq -r '.code') != \"1\" ]]; then\n    print_success \"Non-existent note returns error\"\nelse\n    print_failure \"Non-existent note should return error\"\nfi\n\n# ============================================================================\nprint_header \"11. CLEANUP\"\n\n# Test: Delete vault (cleanup)\nprint_test \"DELETE /vault\"\nif [[ -n \"$VAULT_ID\" && \"$VAULT_ID\" != \"null\" ]]; then\n    RESPONSE=$(curl -s -X DELETE \"$API_URL/vault?id=$VAULT_ID\" -H \"$AUTH_HEADER\")\n    echo \"Response: $RESPONSE\"\n    check_response \"$RESPONSE\" \"4\" \"Delete vault\"\nelse\n    echo \"Skipping vault deletion - no vault ID\"\n    ((PASSED++))\nfi\n\n# ============================================================================\nprint_header \"TEST SUMMARY\"\n\nTOTAL=$((PASSED + FAILED))\necho -e \"\\n${BLUE}Results:${NC}\"\necho -e \"  ${GREEN}Passed: $PASSED${NC}\"\necho -e \"  ${RED}Failed: $FAILED${NC}\"\necho -e \"  Total:  $TOTAL\"\necho \"\"\n\nif [[ $FAILED -eq 0 ]]; then\n    echo -e \"${GREEN}════════════════════════════════════════${NC}\"\n    echo -e \"${GREEN}  ALL TESTS PASSED!${NC}\"\n    echo -e \"${GREEN}════════════════════════════════════════${NC}\"\n    exit 0\nelse\n    echo -e \"${RED}════════════════════════════════════════${NC}\"\n    echo -e \"${RED}  SOME TESTS FAILED${NC}\"\n    echo -e \"${RED}════════════════════════════════════════${NC}\"\n    exit 1\nfi\n"
  },
  {
    "path": "scripts/test-edge-cases.sh",
    "content": "#!/bin/bash\n\n# Fast Note Sync Service - Edge Case Tests // Fast Note Sync Service - 边界情况测试\n# Critical tests for edge cases and potential bugs // 针对边界情况和潜在 bug 的关键测试\n\nBASE_URL=\"${1:-http://localhost:9000}\"\nAPI_URL=\"$BASE_URL/api\"\n\n# Colors\nRED='\\033[0;31m'\nGREEN='\\033[0;32m'\nYELLOW='\\033[1;33m'\nBLUE='\\033[0;34m'\nCYAN='\\033[0;36m'\nNC='\\033[0m'\n\nPASSED=0\nFAILED=0\nWARNINGS=0\n\nprint_header() {\n    echo -e \"\\n${BLUE}═══════════════════════════════════════════════════════════${NC}\"\n    echo -e \"${BLUE}  $1${NC}\"\n    echo -e \"${BLUE}═══════════════════════════════════════════════════════════${NC}\"\n}\n\nprint_test() {\n    echo -e \"\\n${YELLOW}▶ TEST: $1${NC}\"\n}\n# Success/Failure printers // 成功/失败打印器\n\nprint_success() {\n    echo -e \"${GREEN}✓ PASS: $1${NC}\"\n    ((PASSED++))\n}\n\nprint_failure() {\n    echo -e \"${RED}✗ FAIL: $1${NC}\"\n    ((FAILED++))\n}\n\nprint_warning() {\n    echo -e \"${YELLOW}⚠ WARNING: $1${NC}\"\n    ((WARNINGS++))\n}\n\nprint_info() {\n    echo -e \"${CYAN}ℹ INFO: $1${NC}\"\n}\n\ncheck_code() {\n    local response=\"$1\"\n    local expected=\"$2\"\n    local test_name=\"$3\"\n    local code=$(echo \"$response\" | jq -r '.code // empty' 2>/dev/null)\n\n    if [[ \"$code\" == \"$expected\" ]]; then\n        print_success \"$test_name\"\n        return 0\n    else\n        print_failure \"$test_name (expected code=$expected, got code=$code)\"\n        return 1\n    fi\n}\n\n# ============================================================================\n# SETUP - Create test user and vault // 设置 - 创建测试用户和库\n\nTEST_USER=\"edgetest_$(date +%s)\"\nTEST_EMAIL=\"${TEST_USER}@test.com\"\nTEST_PASS=\"TestPass123!\"\n\n# Register and login\nRESPONSE=$(curl -s -X POST \"$API_URL/user/register\" \\\n    -H \"Content-Type: application/json\" \\\n    -d \"{\\\"email\\\": \\\"$TEST_EMAIL\\\", \\\"username\\\": \\\"$TEST_USER\\\", \\\"password\\\": \\\"$TEST_PASS\\\", \\\"confirmPassword\\\": \\\"$TEST_PASS\\\"}\")\n\nTOKEN=$(echo \"$RESPONSE\" | jq -r '.data.token')\nif [[ -z \"$TOKEN\" || \"$TOKEN\" == \"null\" ]]; then\n    echo -e \"${RED}Failed to get token. Exiting.${NC}\"\n    exit 1\nfi\nAUTH=\"Authorization: Bearer $TOKEN\"\n\nVAULT=\"EdgeTestVault_$(date +%s)\"\ncurl -s -X POST \"$API_URL/vault\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\"}\" > /dev/null\n\necho -e \"${GREEN}Setup complete. Vault: $VAULT${NC}\"\n\n# ============================================================================\n# 1. MOVE OPERATIONS - Edge Cases // 1. 移动操作 - 边界情况\n\n# Create two notes for move testing\nprint_test \"Setup: Create source and destination notes\"\ncurl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"move-source.md\\\", \\\"content\\\": \\\"# Source Note\\\\n\\\\nOriginal content.\\\"}\" > /dev/null\ncurl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"move-dest.md\\\", \\\"content\\\": \\\"# Destination Note\\\\n\\\\nThis should be overwritten.\\\"}\" > /dev/null\n\n# Edit destination multiple times to create version history\nfor i in 1 2 3; do\n    curl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n        -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"move-dest.md\\\", \\\"content\\\": \\\"# Destination Note v$i\\\\n\\\\nEdit $i\\\"}\" > /dev/null\ndone\nprint_success \"Created notes with version history\"\n\n# Check destination version before move\nprint_test \"Check destination note version before move\"\nRESPONSE=$(curl -s \"$API_URL/note?vault=$VAULT&path=move-dest.md\" -H \"$AUTH\")\nDEST_VERSION=$(echo \"$RESPONSE\" | jq -r '.data.version')\necho \"Destination version before move: $DEST_VERSION\"\nif [[ \"$DEST_VERSION\" -ge 2 ]]; then\n    print_success \"Destination has version history (v$DEST_VERSION)\"\nelse\n    print_warning \"Expected version >= 2, got $DEST_VERSION\"\nfi\n\n# Test: Move without overwrite (should fail)\nprint_test \"Move to existing path WITHOUT overwrite flag\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note/move\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"move-source.md\\\", \\\"destination\\\": \\\"move-dest.md\\\", \\\"overwrite\\\": false}\")\necho \"Response: $RESPONSE\"\ncheck_code \"$RESPONSE\" \"460\" \"Move blocked when destination exists\"\n\n# Test: Move WITH overwrite\nprint_test \"Move to existing path WITH overwrite=true\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note/move\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"move-source.md\\\", \\\"destination\\\": \\\"move-dest.md\\\", \\\"overwrite\\\": true}\")\necho \"Response: $RESPONSE\"\ncheck_code \"$RESPONSE\" \"1\" \"Move with overwrite succeeded\"\n\n# CRITICAL: Check if destination's old version history is preserved\nprint_test \"CRITICAL: Check version history after move+overwrite\"\nRESPONSE=$(curl -s \"$API_URL/note/histories?vault=$VAULT&path=move-dest.md\" -H \"$AUTH\")\necho \"Response: $RESPONSE\"\nHISTORY_COUNT=$(echo \"$RESPONSE\" | jq -r '.data.pager.totalRows')\necho \"History entries after move: $HISTORY_COUNT\"\n# Note: History is created with delay, so may be 0 immediately\nif [[ \"$HISTORY_COUNT\" -eq \"0\" ]]; then\n    print_warning \"No immediate history - history may be created async after delay\"\nelse\n    print_info \"History count: $HISTORY_COUNT\"\nfi\n\n# Test: Move to same path (no-op) // 测试：移动到相同路径（无操作）\nprint_test \"Move note to itself (same source and destination)\"\ncurl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"self-move.md\\\", \\\"content\\\": \\\"Self move test\\\"}\" > /dev/null\nRESPONSE=$(curl -s -X POST \"$API_URL/note/move\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"self-move.md\\\", \\\"destination\\\": \\\"self-move.md\\\", \\\"overwrite\\\": false}\")\necho \"Response: $RESPONSE\"\n# This might succeed (no-op) or fail - depends on implementation\nCODE=$(echo \"$RESPONSE\" | jq -r '.code')\nif [[ \"$CODE\" == \"1\" ]]; then\n    print_info \"Move to self succeeded (treated as no-op)\"\nelif [[ \"$CODE\" == \"460\" ]]; then\n    print_info \"Move to self blocked by conflict check\"\nelse\n    print_warning \"Unexpected code $CODE for move-to-self\"\nfi\n\n# Test: Move non-existent file\nprint_test \"Move non-existent file\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note/move\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"does-not-exist.md\\\", \\\"destination\\\": \\\"anywhere.md\\\"}\")\necho \"Response: $RESPONSE\"\ncheck_code \"$RESPONSE\" \"429\" \"Move non-existent file returns 429\"\n\n# ============================================================================\n# 2. REPLACE OPERATIONS - Multiple Matches // 2. 替换操作 - 多次匹配\n\n# Create note with repeated content // 创建包含重复内容的笔记\nprint_test \"Create note with multiple identical strings\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"replace-multi.md\\\", \\\"content\\\": \\\"# Replace Test\\\\n\\\\nfoo bar foo baz foo qux foo\\\"}\")\ncheck_code \"$RESPONSE\" \"1\" \"Created note with 4x 'foo'\"\n\n# Test: Replace first only (all=false) // 测试：仅替换第一个（all=false）\nprint_test \"Replace with all=false (should replace only first occurrence)\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note/replace\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"replace-multi.md\\\", \\\"find\\\": \\\"foo\\\", \\\"replace\\\": \\\"REPLACED\\\", \\\"all\\\": false}\")\necho \"Response: $RESPONSE\"\nMATCH_COUNT=$(echo \"$RESPONSE\" | jq -r '.data.matchCount')\nCONTENT=$(echo \"$RESPONSE\" | jq -r '.data.note.content')\necho \"Match count: $MATCH_COUNT\"\necho \"New content: $CONTENT\"\n\n# Count remaining 'foo' occurrences // 计算剩余的 'foo' 出现次数\nREMAINING=$(echo \"$CONTENT\" | grep -o \"foo\" | wc -l)\nif [[ \"$REMAINING\" -eq 3 ]]; then\n    print_success \"Only first occurrence replaced (3 'foo' remaining)\"\nelse\n    print_failure \"Expected 3 'foo' remaining, got $REMAINING\"\nfi\n\n# Reset and test all=true // 重置并测试 all=true\ncurl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"replace-multi.md\\\", \\\"content\\\": \\\"foo bar foo baz foo qux foo\\\"}\" > /dev/null\n\nprint_test \"Replace with all=true (should replace all occurrences)\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note/replace\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"replace-multi.md\\\", \\\"find\\\": \\\"foo\\\", \\\"replace\\\": \\\"REPLACED\\\", \\\"all\\\": true}\")\necho \"Response: $RESPONSE\"\nMATCH_COUNT=$(echo \"$RESPONSE\" | jq -r '.data.matchCount')\nCONTENT=$(echo \"$RESPONSE\" | jq -r '.data.note.content')\nREMAINING=$(echo \"$CONTENT\" | grep -o \"foo\" | wc -l)\nREPLACED=$(echo \"$CONTENT\" | grep -o \"REPLACED\" | wc -l)\n\nif [[ \"$REPLACED\" -eq 4 && \"$REMAINING\" -eq 0 ]]; then\n    print_success \"All 4 occurrences replaced\"\nelse\n    print_failure \"Expected 4 replacements, got $REPLACED (foo remaining: $REMAINING)\"\nfi\n\n# Test: Replace with empty string (deletion) // 测试：用空字符串替换（删除）\nprint_test \"Replace with empty string (deletion)\"\ncurl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"replace-delete.md\\\", \\\"content\\\": \\\"Keep DELETE_ME this text\\\"}\" > /dev/null\nRESPONSE=$(curl -s -X POST \"$API_URL/note/replace\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"replace-delete.md\\\", \\\"find\\\": \\\"DELETE_ME \\\", \\\"replace\\\": \\\"\\\", \\\"all\\\": true}\")\nCONTENT=$(echo \"$RESPONSE\" | jq -r '.data.note.content')\nif [[ \"$CONTENT\" == \"Keep this text\" ]]; then\n    print_success \"Replacement with empty string works\"\nelse\n    print_failure \"Expected 'Keep this text', got '$CONTENT'\"\nfi\n\n# Test: Regex with capture groups // 测试：带捕获组的正则表达式\nprint_test \"Regex replace with capture groups\"\ncurl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"regex-capture.md\\\", \\\"content\\\": \\\"Date: 2024-01-15 and 2024-02-20\\\"}\" > /dev/null\nRESPONSE=$(curl -s -X POST \"$API_URL/note/replace\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"regex-capture.md\\\", \\\"find\\\": \\\"(\\\\\\\\d{4})-(\\\\\\\\d{2})-(\\\\\\\\d{2})\\\", \\\"replace\\\": \\\"\\$2/\\$3/\\$1\\\", \\\"regex\\\": true, \\\"all\\\": true}\")\necho \"Response: $RESPONSE\"\nCONTENT=$(echo \"$RESPONSE\" | jq -r '.data.note.content')\necho \"New content: $CONTENT\"\nif [[ \"$CONTENT\" == *\"01/15/2024\"* && \"$CONTENT\" == *\"02/20/2024\"* ]]; then\n    print_success \"Regex capture groups work correctly\"\nelse\n    print_warning \"Capture group replacement may not work as expected\"\nfi\n\n# Test: Invalid regex // 测试：无效的正则表达式\nprint_test \"Invalid regex pattern\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note/replace\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"regex-capture.md\\\", \\\"find\\\": \\\"[invalid(regex\\\", \\\"replace\\\": \\\"x\\\", \\\"regex\\\": true}\")\necho \"Response: $RESPONSE\"\ncheck_code \"$RESPONSE\" \"462\" \"Invalid regex returns error 462\"\n\n# ============================================================================\n# 3. VERSION HISTORY TRACKING // 3. 版本历史追踪\n\n# Create a note and edit it multiple times // 创建一个笔记并多次编辑\nprint_test \"Create note and track version increments\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"version-test.md\\\", \\\"content\\\": \\\"Version 0\\\"}\")\nV0=$(echo \"$RESPONSE\" | jq -r '.data.version')\necho \"Initial version: $V0\"\n\nRESPONSE=$(curl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"version-test.md\\\", \\\"content\\\": \\\"Version 1\\\"}\")\nV1=$(echo \"$RESPONSE\" | jq -r '.data.version')\necho \"After edit 1: $V1\"\n\nRESPONSE=$(curl -s -X POST \"$API_URL/note/append\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"version-test.md\\\", \\\"content\\\": \\\"\\\\nAppended\\\"}\")\nV2=$(echo \"$RESPONSE\" | jq -r '.data.version')\necho \"After append: $V2\"\n\nRESPONSE=$(curl -s -X POST \"$API_URL/note/prepend\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"version-test.md\\\", \\\"content\\\": \\\"Prepended\\\\n\\\"}\")\nV3=$(echo \"$RESPONSE\" | jq -r '.data.version')\necho \"After prepend: $V3\"\n\nRESPONSE=$(curl -s -X POST \"$API_URL/note/replace\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"version-test.md\\\", \\\"find\\\": \\\"Version\\\", \\\"replace\\\": \\\"Ver\\\"}\")\nV4=$(echo \"$RESPONSE\" | jq -r '.data.note.version')\necho \"After replace: $V4\"\n\nif [[ \"$V0\" == \"0\" && \"$V1\" == \"1\" && \"$V2\" == \"2\" && \"$V3\" == \"3\" && \"$V4\" == \"4\" ]]; then\n    print_success \"Version increments correctly: 0 -> 1 -> 2 -> 3 -> 4\"\nelse\n    print_failure \"Version sequence unexpected: $V0 -> $V1 -> $V2 -> $V3 -> $V4\"\nfi\n\n# ============================================================================\n# 4. FRONTMATTER EDGE CASES // 4. Frontmatter 边界情况\n\n# Test: Patch frontmatter on note WITHOUT frontmatter // 测试：在没有 Frontmatter 的笔记上修补 Frontmatter\nprint_test \"Patch frontmatter on note without existing frontmatter\"\ncurl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"no-frontmatter.md\\\", \\\"content\\\": \\\"# Just a heading\\\\n\\\\nNo frontmatter here.\\\"}\" > /dev/null\nRESPONSE=$(curl -s -X PATCH \"$API_URL/note/frontmatter\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"no-frontmatter.md\\\", \\\"updates\\\": {\\\"title\\\": \\\"Added Title\\\"}}\")\necho \"Response: $RESPONSE\"\nCONTENT=$(echo \"$RESPONSE\" | jq -r '.data.content')\nif [[ \"$CONTENT\" == \"---\"* ]]; then\n    print_success \"Frontmatter was created on note without it\"\nelse\n    print_warning \"Frontmatter may not have been added\"\nfi\necho \"Content preview: $(echo \"$CONTENT\" | head -c 200)\"\n\n# Test: Remove non-existent key // 测试：删除不存在的键\nprint_test \"Remove frontmatter key that doesn't exist\"\ncurl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"fm-remove.md\\\", \\\"content\\\": \\\"---\\\\ntitle: Test\\\\n---\\\\nBody\\\"}\" > /dev/null\nRESPONSE=$(curl -s -X PATCH \"$API_URL/note/frontmatter\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"fm-remove.md\\\", \\\"remove\\\": [\\\"nonexistent\\\", \\\"also_missing\\\"]}\")\necho \"Response: $RESPONSE\"\ncheck_code \"$RESPONSE\" \"1\" \"Removing non-existent keys doesn't error\"\n\n# Test: Add nested YAML structure // 测试：添加嵌套的 YAML 结构\nprint_test \"Add nested YAML structure to frontmatter\"\nRESPONSE=$(curl -s -X PATCH \"$API_URL/note/frontmatter\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"fm-remove.md\\\", \\\"updates\\\": {\\\"metadata\\\": {\\\"author\\\": \\\"test\\\", \\\"tags\\\": [\\\"a\\\", \\\"b\\\"]}}}\")\necho \"Response: $RESPONSE\"\nCONTENT=$(echo \"$RESPONSE\" | jq -r '.data.content')\nif [[ \"$CONTENT\" == *\"metadata\"* ]]; then\n    print_success \"Nested YAML structure added\"\nelse\n    print_warning \"Nested structure may not work\"\nfi\n\n# ============================================================================\n# 5. LINK INDEXING - Backlinks & Outlinks // 5. 链接索引 - 反向链接与正向链接\n\n# Create notes with various link formats // 创建包含各种链接格式的笔记\nprint_test \"Create notes with wiki links for link testing\"\ncurl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"target-note.md\\\", \\\"content\\\": \\\"# Target Note\\\\n\\\\nThis is the target.\\\"}\" > /dev/null\n\ncurl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"linker1.md\\\", \\\"content\\\": \\\"# Linker 1\\\\n\\\\nLinks to [[target-note]] here.\\\"}\" > /dev/null\n\ncurl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"linker2.md\\\", \\\"content\\\": \\\"# Linker 2\\\\n\\\\nAlso links to [[target-note|with alias]] and [[target-note#heading]].\\\"}\" > /dev/null\n\ncurl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"circular-a.md\\\", \\\"content\\\": \\\"Links to [[circular-b]]\\\"}\" > /dev/null\n\ncurl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"circular-b.md\\\", \\\"content\\\": \\\"Links to [[circular-a]]\\\"}\" > /dev/null\n\nprint_success \"Created test notes with links\"\n\n# Test: Get backlinks // 测试：获取反向链接\nprint_test \"Get backlinks for target-note\"\nRESPONSE=$(curl -s \"$API_URL/note/backlinks?vault=$VAULT&path=target-note.md\" -H \"$AUTH\")\necho \"Response: $RESPONSE\"\nBACKLINK_COUNT=$(echo \"$RESPONSE\" | jq -r '.data | length')\necho \"Backlink count: $BACKLINK_COUNT\"\nif [[ \"$BACKLINK_COUNT\" -ge 1 ]]; then\n    print_success \"Found $BACKLINK_COUNT backlinks\"\nelse\n    print_warning \"Expected backlinks but got $BACKLINK_COUNT - link indexing may have issues\"\nfi\n\n# Test: Get outlinks with alias // 测试：获取带别名的出站链接\nprint_test \"Get outlinks (check alias handling)\"\nRESPONSE=$(curl -s \"$API_URL/note/outlinks?vault=$VAULT&path=linker2.md\" -H \"$AUTH\")\necho \"Response: $RESPONSE\"\ncheck_code \"$RESPONSE\" \"1\" \"Get outlinks succeeded\"\n\n# Test: Circular links // 测试：循环链接\nprint_test \"Test circular link handling\"\nRESPONSE=$(curl -s \"$API_URL/note/backlinks?vault=$VAULT&path=circular-a.md\" -H \"$AUTH\")\necho \"Backlinks to circular-a: $RESPONSE\"\nRESPONSE=$(curl -s \"$API_URL/note/backlinks?vault=$VAULT&path=circular-b.md\" -H \"$AUTH\")\necho \"Backlinks to circular-b: $RESPONSE\"\nprint_info \"Circular links should not cause infinite loops\"\n\n# ============================================================================\nprint_header \"6. APPEND/PREPEND EDGE CASES\"\n# 6. APPEND/PREPEND EDGE CASES // 6. 追加/前置 边界情况\n\n# Test: Append to empty note // 测试：追加到空笔记\nprint_test \"Append to empty note\"\ncurl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"empty-note.md\\\", \\\"content\\\": \\\"\\\"}\" > /dev/null\nRESPONSE=$(curl -s -X POST \"$API_URL/note/append\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"empty-note.md\\\", \\\"content\\\": \\\"First content\\\"}\")\nCONTENT=$(echo \"$RESPONSE\" | jq -r '.data.content')\nif [[ \"$CONTENT\" == \"First content\" ]]; then\n    print_success \"Append to empty note works\"\nelse\n    print_failure \"Append to empty note failed\"\nfi\n\n# Test: Prepend with frontmatter preservation // 测试：前置内容并保留 Frontmatter\nprint_test \"Prepend to note with frontmatter (should go after frontmatter)\"\ncurl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"fm-prepend.md\\\", \\\"content\\\": \\\"---\\\\ntitle: Test\\\\n---\\\\nOriginal body\\\"}\" > /dev/null\nRESPONSE=$(curl -s -X POST \"$API_URL/note/prepend\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"fm-prepend.md\\\", \\\"content\\\": \\\"PREPENDED\\\\n\\\"}\")\nCONTENT=$(echo \"$RESPONSE\" | jq -r '.data.content')\necho \"Content after prepend:\"\necho \"$CONTENT\"\n# Check frontmatter is still first // 检查 Frontmatter 是否仍在开头\nif [[ \"$CONTENT\" == \"---\"* && \"$CONTENT\" == *\"PREPENDED\"* ]]; then\n    print_success \"Frontmatter preserved, content prepended after it\"\nelse\n    print_warning \"Check prepend behavior with frontmatter\"\nfi\n\n# Test: Append with wiki links (should they be indexed?) // 测试：追加带 Wiki 链接的内容（是否应被索引？）\nprint_test \"Append content with wiki links\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note/append\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"fm-prepend.md\\\", \\\"content\\\": \\\"\\\\n\\\\nNew link: [[new-target]]\\\"}\")\ncheck_code \"$RESPONSE\" \"1\" \"Appended content with link\"\n# Check if outlinks are updated // 检查出站链接是否已更新\nRESPONSE=$(curl -s \"$API_URL/note/outlinks?vault=$VAULT&path=fm-prepend.md\" -H \"$AUTH\")\nOUTLINKS=$(echo \"$RESPONSE\" | jq -r '.data | length')\necho \"Outlinks after append: $OUTLINKS\"\nif [[ \"$OUTLINKS\" -ge 1 ]]; then\n    print_success \"Links in appended content are indexed\"\nelse\n    print_warning \"Links from append may not be indexed immediately\"\nfi\n\n# ============================================================================\nprint_header \"7. createOnly EDGE CASES\"\n# 7. createOnly EDGE CASES // 7. createOnly 边界情况\n\n# Test: createOnly on soft-deleted note // 测试：在软删除的笔记上使用 createOnly\nprint_test \"createOnly on soft-deleted note\"\n# Create and delete a note // 创建并删除一个笔记\ncurl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"deleted-note.md\\\", \\\"content\\\": \\\"Will be deleted\\\"}\" > /dev/null\ncurl -s -X DELETE \"$API_URL/note?vault=$VAULT&path=deleted-note.md\" -H \"$AUTH\" > /dev/null\n\n# Try to create with createOnly // 尝试使用 createOnly 创建\nRESPONSE=$(curl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"deleted-note.md\\\", \\\"content\\\": \\\"New content\\\", \\\"createOnly\\\": true}\")\necho \"Response: $RESPONSE\"\nCODE=$(echo \"$RESPONSE\" | jq -r '.code')\nif [[ \"$CODE\" == \"1\" ]]; then\n    print_success \"createOnly succeeds on soft-deleted note (recreates it)\"\nelif [[ \"$CODE\" == \"430\" ]]; then\n    print_info \"createOnly blocks even for soft-deleted notes\"\nelse\n    print_warning \"Unexpected behavior: code $CODE\"\nfi\n\n# ============================================================================\nprint_header \"8. PATH EDGE CASES\"\n# 8. PATH EDGE CASES // 8. 路径边界情况\n\n# Test: Unicode in path // 测试：路径中的 Unicode 字符\nprint_test \"Unicode characters in path\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"日本語/笔记.md\\\", \\\"content\\\": \\\"Unicode path test\\\"}\")\necho \"Response: $RESPONSE\"\ncheck_code \"$RESPONSE\" \"1\" \"Unicode path accepted\"\n\n# Retrieve it back // 检索回来\nRESPONSE=$(curl -s \"$API_URL/note?vault=$VAULT&path=%E6%97%A5%E6%9C%AC%E8%AA%9E/%E7%AC%94%E8%AE%B0.md\" -H \"$AUTH\")\ncheck_code \"$RESPONSE\" \"1\" \"Unicode path retrieved\"\n\n# Test: Path with spaces // 测试：带空格的路径\nprint_test \"Path with spaces\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"folder with spaces/note with spaces.md\\\", \\\"content\\\": \\\"Spaces test\\\"}\")\ncheck_code \"$RESPONSE\" \"1\" \"Path with spaces accepted\"\n\n# Test: Very long path // 测试：非常长的路径\nprint_test \"Very long path (200+ characters)\"\nLONG_PATH=\"very/long/path/$(printf 'x%.0s' {1..150}).md\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"$LONG_PATH\\\", \\\"content\\\": \\\"Long path test\\\"}\")\necho \"Response: $RESPONSE\"\nCODE=$(echo \"$RESPONSE\" | jq -r '.code')\nif [[ \"$CODE\" == \"1\" ]]; then\n    print_success \"Long path accepted\"\nelse\n    print_info \"Long path rejected (may be intended): code $CODE\"\nfi\n\n# Test: Path traversal attempt // 测试：路径遍历尝试\nprint_test \"Path traversal attempt (../)\"\nRESPONSE=$(curl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"../../../etc/passwd\\\", \\\"content\\\": \\\"Should fail\\\"}\")\necho \"Response: $RESPONSE\"\n# This should either fail or sanitize the path // 这应该要么失败要么净化路径\nprint_info \"Path traversal handling: $(echo \"$RESPONSE\" | jq -r '.code')\"\n\n# ============================================================================\n# 9. CONCURRENT EDIT SIMULATION // 9. 并发编辑模拟\n\nprint_test \"Rapid sequential edits (version conflict potential)\"\n# Create note // 创建笔记\ncurl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n    -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"rapid-edit.md\\\", \\\"content\\\": \\\"Initial\\\"}\" > /dev/null\n\n# Rapid edits\nfor i in {1..5}; do\n    curl -s -X POST \"$API_URL/note\" -H \"$AUTH\" -H \"Content-Type: application/json\" \\\n        -d \"{\\\"vault\\\": \\\"$VAULT\\\", \\\"path\\\": \\\"rapid-edit.md\\\", \\\"content\\\": \\\"Edit $i at $(date +%s%N)\\\"}\" &\ndone\nwait\n\nRESPONSE=$(curl -s \"$API_URL/note?vault=$VAULT&path=rapid-edit.md\" -H \"$AUTH\")\nFINAL_VERSION=$(echo \"$RESPONSE\" | jq -r '.data.version')\necho \"Final version after 5 rapid edits: $FINAL_VERSION\"\nif [[ \"$FINAL_VERSION\" -ge 4 ]]; then\n    print_success \"Handled rapid edits (version: $FINAL_VERSION)\"\nelse\n    print_warning \"Some edits may have been lost (version: $FINAL_VERSION)\"\nfi\n\n# ============================================================================\nprint_header \"10. FILE/ATTACHMENT API CHECK\"\n# 10. FILE/ATTACHMENT API CHECK // 10. 文件/附件 API 检查\n\nprint_test \"Check for REST file upload endpoint\"\n# Try common file upload endpoints\nRESPONSE=$(curl -s -X POST \"$API_URL/file\" -H \"$AUTH\" -F \"file=@/dev/null\" 2>/dev/null)\necho \"POST /file response: $RESPONSE\"\nRESPONSE=$(curl -s -X POST \"$API_URL/upload\" -H \"$AUTH\" -F \"file=@/dev/null\" 2>/dev/null)\necho \"POST /upload response: $RESPONSE\"\nRESPONSE=$(curl -s -X POST \"$API_URL/attachment\" -H \"$AUTH\" -F \"file=@/dev/null\" 2>/dev/null)\necho \"POST /attachment response: $RESPONSE\"\nprint_info \"File uploads appear to be WebSocket-only (not REST API)\"\n\n# ============================================================================\nprint_header \"11. CLEANUP\"\n# 11. CLEANUP // 11. 清理\n\n# Get vault ID and delete\nVAULT_RESPONSE=$(curl -s \"$API_URL/vault\" -H \"$AUTH\")\nVAULT_ID=$(echo \"$VAULT_RESPONSE\" | jq -r \".data[] | select(.vault==\\\"$VAULT\\\") | .id\")\nif [[ -n \"$VAULT_ID\" && \"$VAULT_ID\" != \"null\" ]]; then\n    curl -s -X DELETE \"$API_URL/vault?id=$VAULT_ID\" -H \"$AUTH\" > /dev/null\n    print_success \"Cleaned up test vault\"\nfi\n\n# ============================================================================\nprint_header \"TEST SUMMARY\"\n\necho -e \"\\n${BLUE}Results:${NC}\"\necho -e \"  ${GREEN}Passed:   $PASSED${NC}\"\necho -e \"  ${RED}Failed:   $FAILED${NC}\"\necho -e \"  ${YELLOW}Warnings: $WARNINGS${NC}\"\nTOTAL=$((PASSED + FAILED))\necho -e \"  Total:    $TOTAL\"\necho \"\"\n\nif [[ $FAILED -eq 0 ]]; then\n    echo -e \"${GREEN}════════════════════════════════════════${NC}\"\n    echo -e \"${GREEN}  ALL CRITICAL TESTS PASSED!${NC}\"\n    if [[ $WARNINGS -gt 0 ]]; then\n        echo -e \"${YELLOW}  ($WARNINGS warnings to review)${NC}\"\n    fi\n    echo -e \"${GREEN}════════════════════════════════════════${NC}\"\n    exit 0\nelse\n    echo -e \"${RED}════════════════════════════════════════${NC}\"\n    echo -e \"${RED}  $FAILED TESTS FAILED${NC}\"\n    echo -e \"${RED}════════════════════════════════════════${NC}\"\n    exit 1\nfi\n"
  },
  {
    "path": "scripts/test-folder-api.sh",
    "content": "#!/bin/bash\n\n# Folder API Test Script\n# Tests all folder-related and general API endpoints, saves outputs as JSON\n\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"$0\")\" && pwd)\"\nsource \"$SCRIPT_DIR/.env\"\n\nURL=\"${url:-http://localhost:9000}\"\nTOKEN=\"${local_test_auth_token}\"\nVAULT=\"fastnotsyncTest\"\nOUT_DIR=\"$SCRIPT_DIR/test-outputs\"\n\nmkdir -p \"$OUT_DIR\"\n\nPASS=0\nFAIL=0\n\nrun_test() {\n    local name=\"$1\"\n    local method=\"$2\"\n    local endpoint=\"$3\"\n    local body=\"${4:-}\"\n    local filename=\"$OUT_DIR/${name}.json\"\n\n    if [[ -n \"$body\" ]]; then\n        RESPONSE=$(curl -s -X \"$method\" \"${URL}/api${endpoint}\" \\\n            -H \"token: $TOKEN\" \\\n            -H \"Content-Type: application/json\" \\\n            -d \"$body\")\n    else\n        RESPONSE=$(curl -s -X \"$method\" \"${URL}/api${endpoint}\" \\\n            -H \"token: $TOKEN\")\n    fi\n\n    echo \"$RESPONSE\" | jq . > \"$filename\" 2>/dev/null\n\n    CODE=$(echo \"$RESPONSE\" | jq -r '.code // empty')\n    if [[ \"$CODE\" == \"1\" || \"$CODE\" == \"2\" ]]; then\n        echo \"  PASS  $name\"\n        PASS=$((PASS + 1))\n    else\n        echo \"  FAIL  $name (code=$CODE)\"\n        FAIL=$((FAIL + 1))\n    fi\n}\n\necho \"=== Fast Note Sync API Tests ===\"\necho \"URL: $URL\"\necho \"Vault: $VAULT\"\necho \"Output: $OUT_DIR/\"\necho \"\"\n\n# --- Health & Version (no auth) ---\necho \"--- Health & Version ---\"\nrun_test \"health\" GET \"/health\"\nrun_test \"version\" GET \"/version\"\n\n# --- User ---\necho \"--- User ---\"\nrun_test \"user-info\" GET \"/user/info\"\n\n# --- Vaults ---\necho \"--- Vaults ---\"\nrun_test \"vault-list\" GET \"/vault\"\n\n# --- Notes ---\necho \"--- Notes ---\"\nrun_test \"notes-list\" GET \"/notes?vault=$VAULT&pageSize=100\"\nrun_test \"note-get\" GET \"/note?vault=$VAULT&path=test.md\"\n\n# --- Note Edit Operations ---\necho \"--- Note Edit Operations ---\"\n\n# Create a test note\nrun_test \"note-create\" POST \"/note\" \\\n    \"{\\\"vault\\\":\\\"$VAULT\\\",\\\"path\\\":\\\"_test/api-test-note.md\\\",\\\"content\\\":\\\"# Test Note\\\\n\\\\nOriginal content.\\\\n\\\\nLine to replace.\\\"}\"\n\n# Append\nrun_test \"note-append\" POST \"/note/append\" \\\n    \"{\\\"vault\\\":\\\"$VAULT\\\",\\\"path\\\":\\\"_test/api-test-note.md\\\",\\\"content\\\":\\\"\\\\n\\\\nAppended content.\\\"}\"\n\n# Prepend\nrun_test \"note-prepend\" POST \"/note/prepend\" \\\n    \"{\\\"vault\\\":\\\"$VAULT\\\",\\\"path\\\":\\\"_test/api-test-note.md\\\",\\\"content\\\":\\\"Prepended line.\\\\n\\\\n\\\"}\"\n\n# Replace\nrun_test \"note-replace\" POST \"/note/replace\" \\\n    \"{\\\"vault\\\":\\\"$VAULT\\\",\\\"path\\\":\\\"_test/api-test-note.md\\\",\\\"find\\\":\\\"Line to replace\\\",\\\"replace\\\":\\\"Line was replaced\\\",\\\"all\\\":true}\"\n\n# Frontmatter\nrun_test \"note-frontmatter\" PATCH \"/note/frontmatter\" \\\n    \"{\\\"vault\\\":\\\"$VAULT\\\",\\\"path\\\":\\\"_test/api-test-note.md\\\",\\\"updates\\\":{\\\"tags\\\":[\\\"test\\\",\\\"api\\\"],\\\"status\\\":\\\"draft\\\"}}\"\n\n# Get the note after edits\nrun_test \"note-after-edits\" GET \"/note?vault=$VAULT&path=_test/api-test-note.md\"\n\n# Move\nrun_test \"note-move\" POST \"/note/move\" \\\n    \"{\\\"vault\\\":\\\"$VAULT\\\",\\\"path\\\":\\\"_test/api-test-note.md\\\",\\\"destination\\\":\\\"_test/api-test-moved.md\\\"}\"\n\n# --- Links ---\necho \"--- Links ---\"\n# Pick a note that likely has backlinks\nrun_test \"backlinks\" GET \"/note/backlinks?vault=$VAULT&path=projects/test-backlinks/folder-a/note.md\"\nrun_test \"outlinks\" GET \"/note/outlinks?vault=$VAULT&path=projects/test-backlinks/folder-a/note.md\"\n\n# --- History ---\necho \"--- History ---\"\nrun_test \"note-history\" GET \"/note/histories?vault=$VAULT&path=_test/api-test-moved.md\"\n\n# --- Folders ---\necho \"--- Folders ---\"\nrun_test \"folder-list\" GET \"/folders?vault=$VAULT\"\nrun_test \"folder-get\" GET \"/folder?vault=$VAULT&path=projects\"\nrun_test \"folder-notes\" GET \"/folder/notes?vault=$VAULT&path=projects\"\nrun_test \"folder-files\" GET \"/folder/files?vault=$VAULT&path=projects\"\n\n# --- Folder Tree (our new endpoint) ---\necho \"--- Folder Tree ---\"\nrun_test \"folder-tree\" GET \"/folder/tree?vault=$VAULT\"\nrun_test \"folder-tree-depth1\" GET \"/folder/tree?vault=$VAULT&depth=1\"\n\n# --- Cleanup ---\necho \"--- Cleanup ---\"\nrun_test \"note-delete\" DELETE \"/note?vault=$VAULT&path=_test/api-test-moved.md\"\n\n# --- Summary ---\necho \"\"\necho \"=== Results ===\"\necho \"  Passed: $PASS\"\necho \"  Failed: $FAIL\"\necho \"  Output: $OUT_DIR/\"\necho \"\"\necho \"JSON files:\"\nls -1 \"$OUT_DIR/\"\n"
  },
  {
    "path": "scripts/translate_commit.py",
    "content": "import os\nimport sys\nfrom deep_translator import GoogleTranslator\n\ndef main():\n    # Get message from environment variable or command line argument\n    msg = os.environ.get('COMMIT_MSG', '')\n    if len(sys.argv) > 1:\n        msg = sys.argv[1]\n\n    if not msg:\n        print(\"No commit message found.\")\n        return\n\n    # Use a single instance of translators to potentially benefit from some caching if it exists\n    translator_zh = GoogleTranslator(source='auto', target='zh-CN')\n    translator_en = GoogleTranslator(source='auto', target='en')\n\n    lines = msg.split('\\n')\n    \n    for line in lines:\n        # Extract leading whitespace (indentation)\n        indent = line[:len(line) - len(line.lstrip())]\n        content = line.strip()\n        \n        if not content:\n            print(line) # Preserve empty lines or lines with only spaces\n            continue\n\n        try:\n            # Translate only the content to avoid translator messing with indentation\n            zh_trans = translator_zh.translate(content)\n            en_trans = translator_en.translate(content)\n\n            # Re-apply indentation to both translations\n            print(f\"{indent}{zh_trans}\")\n            print(f\"{indent}{en_trans}\")\n\n        except Exception as e:\n            # If translation fails, print original line (which contains indentation)\n            print(f\"{line}\")\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "scripts/translate_support.py",
    "content": "import json\nimport sys\nimport os\nimport traceback\nfrom deep_translator import GoogleTranslator\n\ndef main():\n    if len(sys.argv) < 3:\n        print(json.dumps([]))\n        return\n\n    arg_input = sys.argv[1]\n    target = sys.argv[2]\n\n    texts = []\n    try:\n        # 优先尝试作为文件路径读取\n        if os.path.isfile(arg_input):\n            with open(arg_input, 'r', encoding='utf-8') as f:\n                texts = json.load(f)\n        else:\n            # 否则尝试作为 JSON 字符串解析\n            texts = json.loads(arg_input)\n    except Exception as e:\n        print(f\"Input Error: {e}\", file=sys.stderr)\n        print(json.dumps([]))\n        return\n\n    if not texts:\n        print(json.dumps([]))\n        return\n\n    try:\n        if target == 'zh-TW':\n            target = 'zh-TW'\n\n        translator = GoogleTranslator(source='auto', target=target)\n\n        # 预处理：提取需要翻译的非空项\n        to_translate = []\n        mapping_indices = []\n        for i, text in enumerate(texts):\n            clean_text = text.strip() if text else \"\"\n            if clean_text and clean_text != '-':\n                to_translate.append(text)\n                mapping_indices.append(i)\n\n        results = list(texts) # 默认使用原文\n\n        if to_translate:\n            try:\n                # 使用 translate_batch (deep-translator 1.9.0+ 支持)\n                translated_list = translator.translate_batch(to_translate)\n                for idx, translated in zip(mapping_indices, translated_list):\n                    results[idx] = translated if translated else texts[idx]\n            except Exception as e:\n                # 如果批量翻译失败，回退到逐条翻译（防止全盘皆墨）\n                print(f\"Batch failed ({target}): {e}, switching to sequential.\", file=sys.stderr)\n                for idx, text in zip(mapping_indices, to_translate):\n                    try:\n                        res = translator.translate(text)\n                        results[idx] = res if res else text\n                    except:\n                        pass\n\n        # 使用 ensure_ascii=True (默认) 确保输出纯 ASCII 字符，彻底解决 Windows 编码干扰\n        print(json.dumps(results))\n\n    except Exception as e:\n        print(f\"Execution Error: {e}\", file=sys.stderr)\n        print(json.dumps(texts))\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "scripts/update-version.js",
    "content": "#!/usr/bin/env node\n/**\n * Usage (in project root): // 用法（在项目根目录）：\n *  node scripts/update-version.js 0.8.11      # Set version to 0.8.11 // 将 version 设置为 0.8.11\n *  node scripts/update-version.js patch (or c) # Increment patch (e.g. 0.8.10 -> 0.8.11) // 将 patch 自增（如 0.8.10 -> 0.8.11）\n *  node scripts/update-version.js minor (or b) # Increment minor (e.g. 0.8.10 -> 0.9.0) // 将 minor 自增（如 0.8.10 -> 0.9.0）\n *  node scripts/update-version.js major (or a) # Increment major (e.g. 0.8.10 -> 1.0.0) // 将 major 自增（如 0.8.10 -> 1.0.0）\n *  Or use environment variable: // 或者使用环境变量：\n *  NEW_VERSION=0.8.11 node scripts/update-version.js\n *\n * Priority (target version source): // 优先级（目标版本来源）：\n * 1. Command line arguments // 1. 命令行参数（node scripts/update-version.js <version|major|minor|patch>）\n * 2. Environment variable NEW_VERSION // 2. 环境变量 NEW_VERSION\n */\n\nconst fs = require('fs');\nconst path = require('path');\n\n/**\n * Read current version from Go version file\n * 从 Go 版本文件中读取当前版本号\n * @param {string} filePath - version.go file path // version.go 文件路径\n * @returns {string} current version // 当前版本号\n */\nfunction readGoVersion(filePath) {\n    const content = fs.readFileSync(filePath, 'utf8');\n    // Match Version string = \"x.y.z\" format\n    // 匹配 Version string = \"x.y.z\" 格式\n    const match = content.match(/Version\\s+string\\s*=\\s*\"([^\"]+)\"/);\n    if (!match) {\n        throw new Error('无法从文件中解析版本号: ' + filePath);\n    }\n    return match[1];\n}\n\n/**\n * Update version number in Go version file\n * 更新 Go 版本文件中的版本号\n * @param {string} filePath - version.go file path // version.go 文件路径\n * @param {string} newVersion - new version number // 新版本号\n */\nfunction writeGoVersion(filePath, newVersion) {\n    let content = fs.readFileSync(filePath, 'utf8');\n    // Replace the value of Version variable, keeping the original spacing format\n    // 替换 Version 变量的值,保留原有的空格格式\n    content = content.replace(\n        /(Version\\s+string\\s*=\\s*)\"[^\"]+\"/,\n        `$1\"${newVersion}\"`\n    );\n    fs.writeFileSync(filePath, content, 'utf8');\n}\n\n/**\n * Validate version format is x.y.z\n * 验证版本号格式是否为 x.y.z\n * @param {string} v - version number // 版本号\n * @returns {boolean}\n */\nfunction isValidSemver(v) {\n    return /^\\d+\\.\\d+\\.\\d+$/.test(v);\n}\n\n/**\n * Increment version number\n * 版本号递增\n * @param {string} current - current version // 当前版本号\n * @param {string} part - increment part: major/minor/patch // 递增部分：major/minor/patch\n * @returns {string} new version // 新版本号\n */\nfunction bumpVersion(current, part) {\n    if (!isValidSemver(current)) {\n        throw new Error('当前版本不是 x.y.z 格式: ' + current);\n    }\n    const [maj, min, pat] = current.split('.').map(n => parseInt(n, 10));\n    if (part === 'major') return `${maj + 1}.0.0`;\n    if (part === 'minor') return `${maj}.${min + 1}.0`;\n    if (part === 'patch') return `${maj}.${min}.${pat + 1}`;\n    throw new Error('未知的增量类型: ' + part);\n}\n\n/**\n * Update file version\n * 更新版本文件\n * @param {string} filePath - file path // 文件路径\n * @param {string|null} targetVersion - target version // 目标版本号\n * @param {string|null} bumpOption - increment option // 递增选项\n * @returns {object|null} update result // 更新结果\n */\nfunction updateFileVersion(filePath, targetVersion, bumpOption) {\n    if (!fs.existsSync(filePath)) {\n        console.warn('文件不存在，跳过:', filePath);\n        return null;\n    }\n\n    const from = readGoVersion(filePath);\n    let to = targetVersion;\n\n    if (!to && bumpOption) {\n        to = bumpVersion(from, bumpOption);\n    }\n\n    if (!to) {\n        throw new Error('没有提供目标版本或增量选项');\n    }\n\n    if (!isValidSemver(to)) {\n        throw new Error('目标版本格式不合法，应为 x.y.z: ' + to);\n    }\n\n    writeGoVersion(filePath, to);\n    return { filePath, from, to };\n}\n\n// Main logic\n// 主逻辑\n(function main() {\n    const rawArgs = process.argv.slice(2);\n\n    const aliasMap = { 'a': 'major', 'b': 'minor', 'c': 'patch' };\n    const resolve = (v) => aliasMap[v] || v;\n\n    const arg = resolve(rawArgs[0]);\n    const envVersion = resolve(process.env.NEW_VERSION || null);\n    const bumpOptions = new Set(['major', 'minor', 'patch']);\n\n    let newVersion = null;\n    let bumpOption = null;\n\n    if (arg) {\n        if (bumpOptions.has(arg)) {\n            bumpOption = arg;\n        } else if (isValidSemver(arg)) {\n            newVersion = arg;\n        } else {\n            console.error('Invalid argument, should be x.y.z or major/minor/patch');\n            console.error('参数无效，应为 x.y.z 或 major/minor/patch');\n            process.exit(1);\n        }\n    } else if (envVersion) {\n        if (bumpOptions.has(envVersion)) {\n            bumpOption = envVersion;\n        } else if (isValidSemver(envVersion)) {\n            newVersion = envVersion;\n        } else {\n            console.error('Invalid NEW_VERSION format, should be x.y.z or major/minor/patch');\n            console.error('环境变量 NEW_VERSION 格式无效，应为 x.y.z 或 major/minor/patch');\n            process.exit(1);\n        }\n    } else {\n        console.error('No version parameter provided: use node scripts/update-version.js <version|major|minor|patch> or NEW_VERSION environment variable');\n        console.error('未提供版本参数：使用 node scripts/update-version.js <version|major|minor|patch> 或 NEW_VERSION 环境变量');\n        process.exit(1);\n    }\n\n    const cwd = process.cwd();\n    const versionFile = path.join(cwd, 'internal', 'app', 'version.go');\n\n    try {\n        const result = updateFileVersion(versionFile, newVersion, bumpOption);\n\n        if (!result) {\n            console.warn('没有更新任何文件。');\n            process.exit(0);\n        }\n\n        console.log(`✓ ${path.relative(cwd, result.filePath)}: ${result.from} -> ${result.to}`);\n    } catch (err) {\n        console.error('错误：', err.message);\n        process.exit(1);\n    }\n})();\n"
  }
]