commit c77f4a2cb91ca5920f0d25d75b2f39a9b51e9b3a Author: AzenKain Date: Mon Apr 13 18:05:27 2026 +0700 init diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..6181b12 --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,25 @@ +name: Gitea Auto Deploy +run-name: ${{ gitea.actor }} pushed code 🚀 + +on: [push] + +jobs: + Deploy-Container: + runs-on: ubuntu-latest + + steps: + - name: Check out latest code + uses: actions/checkout@v4 + + - name: Stop and remove old containers + run: | + docker compose down || true + + - name: Remove unused Docker resources + run: | + docker system prune -a --volumes -f + + - name: Build and restart containers + run: | + docker compose pull + docker compose up -d --build \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fb882c1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +.history/ +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions +test1.json +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + + +*.exe diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7d301fa --- /dev/null +++ b/Dockerfile @@ -0,0 +1,55 @@ +FROM oven/bun:canary-alpine AS base + +FROM base AS deps + +WORKDIR /app + +COPY package.json bun.lock ./ +RUN bun install --frozen-lockfile + +# Rebuild the source code only when needed +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Next.js collects completely anonymous telemetry data about general usage. +# Learn more here: https://nextjs.org/telemetry +# Disable telemetry during the build +ENV NEXT_TELEMETRY_DISABLED=1 + +RUN bun run build + +# Production image, copy all the files and run next +FROM base AS runner +WORKDIR /app + +ENV NODE_ENV=production + +# Disable telemetry +ENV NEXT_TELEMETRY_DISABLED=1 + +RUN adduser --system --uid 1001 nextjs + +COPY --from=builder /app/public ./public +COPY --from=builder /app/messages ./messages + +# Set the correct permission for prerender cache +RUN mkdir .next +RUN chown nextjs:bun .next + +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=builder --chown=nextjs:bun /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:bun /app/.next/static ./.next/static + +USER nextjs + +EXPOSE 3000 + +ENV PORT=3000 + +# Set hostname to localhost +ENV HOSTNAME=0.0.0.0 + +CMD ["bun", "server.js"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..0326a51 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# Firefly SrTools + +![Next.js](https://img.shields.io/badge/Next.js-black?style=for-the-badge&logo=next.js&logoColor=white) +![Bun](https://img.shields.io/badge/Bun-%23000000.svg?style=for-the-badge&logo=bun&logoColor=white) +![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?style=for-the-badge&logo=typescript&logoColor=white) +![License](https://img.shields.io/badge/License-MIT-blue?style=for-the-badge) + +**Firefly SrTools** is a modern, high-performance web toolkit designed to streamline workflows and provide essential services. This project is built upon the powerful synergy of the **Next.js** framework and the ultra-fast **Bun.js** runtime, ensuring lightning-fast development and deployment. + +--- + +## Live Sites + +The application is deployed and available at the following URLs: + +| Role | URL | +| :--- | :--- | +| **Main Site** | [srtools.punklorde.org](https://srtools.punklorde.org) | + +--- + +## Key Features + +* **Optimized Performance:** Leveraging Bun for dependency management, scripting, and development server speed. +* **Next.js App Router:** Modern routing and data fetching using the latest Next.js architecture. +* **Server-Side Rendering (SSR):** Enhanced SEO and faster initial load times for improved user experience. +* **Robust Type Safety:** Built with TypeScript for reliable and maintainable code. +* **Modular Design:** Clean separation of concerns with reusable components and utilities. + +## Technology Stack + +* **Frontend Framework:** [Next.js 15](https://nextjs.org/) (React) +* **Runtime & Package Manager:** [Bun](https://bun.sh/) +* **Language:** TypeScript +* **Styling:** (Tailwind CSS, DaisyUI) +* **State Management:** (Zustand) + +--- + +## Prerequisites + +Ensure you have **Bun** installed on your system. + +Install Bun (macOS, WSL, Linux): +```bash +curl -fsSL [https://bun.sh/install](https://bun.sh/install) | bash +``` + +## Development + +```bash +bun install +``` +```bash +bun run dev +``` +```bash +bun run build +bun run start +``` + + diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..b3ae8a6 --- /dev/null +++ b/bun.lock @@ -0,0 +1,1408 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "firefly-tools", + "dependencies": { + "@next/bundle-analyzer": "16.1.6", + "@tanstack/react-query": "^5.90.20", + "axios": "^1.13.4", + "fast-average-color": "^9.5.0", + "framer-motion": "^12.29.2", + "html2canvas-pro": "^1.6.6", + "lru-cache": "^11.2.5", + "lucide-react": "^0.563.0", + "next": "16.1.6", + "next-intl": "^4.8.2", + "prismjs": "^1.30.0", + "react": "19.2.4", + "react-dom": "19.2.4", + "react-select": "^5.10.2", + "react-simple-code-editor": "^0.14.1", + "react-toastify": "^11.0.5", + "sharp": "^0.34.5", + "zod": "^4.3.6", + "zustand": "^5.0.11", + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.1.18", + "@types/jest": "^30.0.0", + "@types/node": "^25.1.0", + "@types/react": "19.2.6", + "@types/react-dom": "19.2.3", + "baseline-browser-mapping": "^2.9.19", + "daisyui": "^5.5.14", + "eslint": "^9.39.2", + "eslint-config-next": "16.1.6", + "tailwind-scrollbar": "^4.0.2", + "tailwindcss": "^4.1.18", + "ts-to-zod": "^5.1.0", + "typescript": "^5.9.3", + }, + }, + }, + "overrides": { + "@types/react": "19.2.6", + "@types/react-dom": "19.2.3", + }, + "packages": { + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], + + "@babel/code-frame": ["@babel/code-frame@7.28.6", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q=="], + + "@babel/compat-data": ["@babel/compat-data@7.28.6", "", {}, "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg=="], + + "@babel/core": ["@babel/core@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/template": "^7.28.6", "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw=="], + + "@babel/generator": ["@babel/generator@7.28.6", "", { "dependencies": { "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], + + "@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="], + + "@babel/parser": ["@babel/parser@7.28.6", "", { "dependencies": { "@babel/types": "^7.28.6" }, "bin": "./bin/babel-parser.js" }, "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ=="], + + "@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="], + + "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], + + "@babel/traverse": ["@babel/traverse@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.6", "@babel/template": "^7.28.6", "@babel/types": "^7.28.6", "debug": "^4.3.1" } }, "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg=="], + + "@babel/types": ["@babel/types@7.28.6", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg=="], + + "@clack/core": ["@clack/core@1.0.0-alpha.4", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-VCtU+vjyKPMSakVrB9q1bOnXN7QW/w4+YQDQCOF59GrzydW+169i0fVx/qzRRXJgt8KGj/pZZ/JxXroFZIDByg=="], + + "@clack/prompts": ["@clack/prompts@1.0.0-alpha.4", "", { "dependencies": { "@clack/core": "1.0.0-alpha.4", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-KnmtDF2xQGoI5AlBme9akHtvCRV0RKAARUXHBQO2tMwnY8B08/4zPWigT7uLK25UPrMCEqnyQPkKRjNdhPbf8g=="], + + "@discoveryjs/json-ext": ["@discoveryjs/json-ext@0.5.7", "", {}, "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw=="], + + "@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], + + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], + + "@emotion/babel-plugin": ["@emotion/babel-plugin@11.13.5", "", { "dependencies": { "@babel/helper-module-imports": "^7.16.7", "@babel/runtime": "^7.18.3", "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", "@emotion/serialize": "^1.3.3", "babel-plugin-macros": "^3.1.0", "convert-source-map": "^1.5.0", "escape-string-regexp": "^4.0.0", "find-root": "^1.1.0", "source-map": "^0.5.7", "stylis": "4.2.0" } }, "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ=="], + + "@emotion/cache": ["@emotion/cache@11.14.0", "", { "dependencies": { "@emotion/memoize": "^0.9.0", "@emotion/sheet": "^1.4.0", "@emotion/utils": "^1.4.2", "@emotion/weak-memoize": "^0.4.0", "stylis": "4.2.0" } }, "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA=="], + + "@emotion/hash": ["@emotion/hash@0.9.2", "", {}, "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="], + + "@emotion/memoize": ["@emotion/memoize@0.9.0", "", {}, "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="], + + "@emotion/react": ["@emotion/react@11.14.0", "", { "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", "@emotion/cache": "^11.14.0", "@emotion/serialize": "^1.3.3", "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", "@emotion/utils": "^1.4.2", "@emotion/weak-memoize": "^0.4.0", "hoist-non-react-statics": "^3.3.1" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA=="], + + "@emotion/serialize": ["@emotion/serialize@1.3.3", "", { "dependencies": { "@emotion/hash": "^0.9.2", "@emotion/memoize": "^0.9.0", "@emotion/unitless": "^0.10.0", "@emotion/utils": "^1.4.2", "csstype": "^3.0.2" } }, "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA=="], + + "@emotion/sheet": ["@emotion/sheet@1.4.0", "", {}, "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg=="], + + "@emotion/unitless": ["@emotion/unitless@0.10.0", "", {}, "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="], + + "@emotion/use-insertion-effect-with-fallbacks": ["@emotion/use-insertion-effect-with-fallbacks@1.2.0", "", { "peerDependencies": { "react": ">=16.8.0" } }, "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg=="], + + "@emotion/utils": ["@emotion/utils@1.4.2", "", {}, "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA=="], + + "@emotion/weak-memoize": ["@emotion/weak-memoize@0.4.0", "", {}, "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], + + "@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="], + + "@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.3", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ=="], + + "@eslint/js": ["@eslint/js@9.39.2", "", {}, "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA=="], + + "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], + + "@floating-ui/core": ["@floating-ui/core@1.7.4", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.7.5", "", { "dependencies": { "@floating-ui/core": "^1.7.4", "@floating-ui/utils": "^0.2.10" } }, "sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="], + + "@formatjs/ecma402-abstract": ["@formatjs/ecma402-abstract@3.1.0", "", { "dependencies": { "@formatjs/fast-memoize": "3.1.0", "@formatjs/intl-localematcher": "0.8.0", "decimal.js": "^10.6.0", "tslib": "^2.8.1" } }, "sha512-CjP1sUzM7XiQW6YluDreN+dMvcKZysO/J4ikvuDjDyd6nSOoSqAK9gvD1s75ZFaJVXtYOsz+y3CUXPZ1sKxcxw=="], + + "@formatjs/fast-memoize": ["@formatjs/fast-memoize@3.1.0", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-b5mvSWCI+XVKiz5WhnBCY3RJ4ZwfjAidU0yVlKa3d3MSgKmH1hC3tBGEAtYyN5mqL7N0G5x0BOUYyO8CEupWgg=="], + + "@formatjs/icu-messageformat-parser": ["@formatjs/icu-messageformat-parser@3.5.0", "", { "dependencies": { "@formatjs/ecma402-abstract": "3.1.0", "@formatjs/icu-skeleton-parser": "2.1.0", "tslib": "^2.8.1" } }, "sha512-Q01XuvtbDVCJQsG/E2MSfMZ+UdUoZV8v4Aex8tTH44SqKJZCeu5LjuclaKFUS0o1YoXndfEinJen5k1T1GR1vg=="], + + "@formatjs/icu-skeleton-parser": ["@formatjs/icu-skeleton-parser@2.1.0", "", { "dependencies": { "@formatjs/ecma402-abstract": "3.1.0", "tslib": "^2.8.1" } }, "sha512-wNer4imHDFBVAJnMb2OGoSyM4wL/uuLnuo5mrenliqkDaNjRbG4jzlJcwTTDEBhai8iCjnzUsE7xwNJC29SfWw=="], + + "@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.5.10", "", { "dependencies": { "tslib": "2" } }, "sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q=="], + + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], + + "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + + "@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="], + + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], + + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], + + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], + + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], + + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], + + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], + + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], + + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], + + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], + + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], + + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], + + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], + + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], + + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], + + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], + + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], + + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], + + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], + + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], + + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], + + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + + "@jest/diff-sequences": ["@jest/diff-sequences@30.0.1", "", {}, "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw=="], + + "@jest/expect-utils": ["@jest/expect-utils@30.2.0", "", { "dependencies": { "@jest/get-type": "30.1.0" } }, "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA=="], + + "@jest/get-type": ["@jest/get-type@30.1.0", "", {}, "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA=="], + + "@jest/pattern": ["@jest/pattern@30.0.1", "", { "dependencies": { "@types/node": "*", "jest-regex-util": "30.0.1" } }, "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA=="], + + "@jest/schemas": ["@jest/schemas@30.0.5", "", { "dependencies": { "@sinclair/typebox": "^0.34.0" } }, "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA=="], + + "@jest/types": ["@jest/types@30.2.0", "", { "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", "@types/istanbul-lib-coverage": "^2.0.6", "@types/istanbul-reports": "^3.0.4", "@types/node": "*", "@types/yargs": "^17.0.33", "chalk": "^4.1.2" } }, "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], + + "@next/bundle-analyzer": ["@next/bundle-analyzer@16.1.6", "", { "dependencies": { "webpack-bundle-analyzer": "4.10.1" } }, "sha512-ee2kagdTaeEWPlotgdTOqFHYcD3e2m2bbE3I9Rq2i6ABYi5OgopmtEUe8NM23viaYxLV2tDH/2nd5+qKoEr6cw=="], + + "@next/env": ["@next/env@16.1.6", "", {}, "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ=="], + + "@next/eslint-plugin-next": ["@next/eslint-plugin-next@16.1.6", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-/Qq3PTagA6+nYVfryAtQ7/9FEr/6YVyvOtl6rZnGsbReGLf0jZU6gkpr1FuChAQpvV46a78p4cmHOVP8mbfSMQ=="], + + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.1.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw=="], + + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.1.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ=="], + + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.1.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw=="], + + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.1.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ=="], + + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ=="], + + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg=="], + + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.1.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw=="], + + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.1.6", "", { "os": "win32", "cpu": "x64" }, "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="], + + "@oclif/core": ["@oclif/core@4.8.0", "", { "dependencies": { "ansi-escapes": "^4.3.2", "ansis": "^3.17.0", "clean-stack": "^3.0.1", "cli-spinners": "^2.9.2", "debug": "^4.4.3", "ejs": "^3.1.10", "get-package-type": "^0.1.0", "indent-string": "^4.0.0", "is-wsl": "^2.2.0", "lilconfig": "^3.1.3", "minimatch": "^9.0.5", "semver": "^7.7.3", "string-width": "^4.2.3", "supports-color": "^8", "tinyglobby": "^0.2.14", "widest-line": "^3.1.0", "wordwrap": "^1.0.0", "wrap-ansi": "^7.0.0" } }, "sha512-jteNUQKgJHLHFbbz806aGZqf+RJJ7t4gwF4MYa8fCwCxQ8/klJNWc0MvaJiBebk7Mc+J39mdlsB4XraaCKznFw=="], + + "@parcel/watcher": ["@parcel/watcher@2.5.6", "", { "dependencies": { "detect-libc": "^2.0.3", "is-glob": "^4.0.3", "node-addon-api": "^7.0.0", "picomatch": "^4.0.3" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.6", "@parcel/watcher-darwin-arm64": "2.5.6", "@parcel/watcher-darwin-x64": "2.5.6", "@parcel/watcher-freebsd-x64": "2.5.6", "@parcel/watcher-linux-arm-glibc": "2.5.6", "@parcel/watcher-linux-arm-musl": "2.5.6", "@parcel/watcher-linux-arm64-glibc": "2.5.6", "@parcel/watcher-linux-arm64-musl": "2.5.6", "@parcel/watcher-linux-x64-glibc": "2.5.6", "@parcel/watcher-linux-x64-musl": "2.5.6", "@parcel/watcher-win32-arm64": "2.5.6", "@parcel/watcher-win32-ia32": "2.5.6", "@parcel/watcher-win32-x64": "2.5.6" } }, "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ=="], + + "@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.6", "", { "os": "android", "cpu": "arm64" }, "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A=="], + + "@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA=="], + + "@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg=="], + + "@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.6", "", { "os": "freebsd", "cpu": "x64" }, "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng=="], + + "@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.6", "", { "os": "linux", "cpu": "arm" }, "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ=="], + + "@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.6", "", { "os": "linux", "cpu": "arm" }, "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg=="], + + "@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA=="], + + "@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA=="], + + "@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ=="], + + "@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg=="], + + "@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q=="], + + "@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.6", "", { "os": "win32", "cpu": "ia32" }, "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g=="], + + "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw=="], + + "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], + + "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], + + "@schummar/icu-type-parser": ["@schummar/icu-type-parser@1.21.5", "", {}, "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw=="], + + "@sinclair/typebox": ["@sinclair/typebox@0.34.48", "", {}, "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA=="], + + "@swc/core": ["@swc/core@1.15.11", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.25" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.15.11", "@swc/core-darwin-x64": "1.15.11", "@swc/core-linux-arm-gnueabihf": "1.15.11", "@swc/core-linux-arm64-gnu": "1.15.11", "@swc/core-linux-arm64-musl": "1.15.11", "@swc/core-linux-x64-gnu": "1.15.11", "@swc/core-linux-x64-musl": "1.15.11", "@swc/core-win32-arm64-msvc": "1.15.11", "@swc/core-win32-ia32-msvc": "1.15.11", "@swc/core-win32-x64-msvc": "1.15.11" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-iLmLTodbYxU39HhMPaMUooPwO/zqJWvsqkrXv1ZI38rMb048p6N7qtAtTp37sw9NzSrvH6oli8EdDygo09IZ/w=="], + + "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.15.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QoIupRWVH8AF1TgxYyeA5nS18dtqMuxNwchjBIwJo3RdwLEFiJq6onOx9JAxHtuPwUkIVuU2Xbp+jCJ7Vzmgtg=="], + + "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.15.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-S52Gu1QtPSfBYDiejlcfp9GlN+NjTZBRRNsz8PNwBgSE626/FUf2PcllVUix7jqkoMC+t0rS8t+2/aSWlMuQtA=="], + + "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.15.11", "", { "os": "linux", "cpu": "arm" }, "sha512-lXJs8oXo6Z4yCpimpQ8vPeCjkgoHu5NoMvmJZ8qxDyU99KVdg6KwU9H79vzrmB+HfH+dCZ7JGMqMF//f8Cfvdg=="], + + "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.15.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-chRsz1K52/vj8Mfq/QOugVphlKPWlMh10V99qfH41hbGvwAU6xSPd681upO4bKiOr9+mRIZZW+EfJqY42ZzRyA=="], + + "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.15.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-PYftgsTaGnfDK4m6/dty9ryK1FbLk+LosDJ/RJR2nkXGc8rd+WenXIlvHjWULiBVnS1RsjHHOXmTS4nDhe0v0w=="], + + "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.15.11", "", { "os": "linux", "cpu": "x64" }, "sha512-DKtnJKIHiZdARyTKiX7zdRjiDS1KihkQWatQiCHMv+zc2sfwb4Glrodx2VLOX4rsa92NLR0Sw8WLcPEMFY1szQ=="], + + "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.15.11", "", { "os": "linux", "cpu": "x64" }, "sha512-mUjjntHj4+8WBaiDe5UwRNHuEzLjIWBTSGTw0JT9+C9/Yyuh4KQqlcEQ3ro6GkHmBGXBFpGIj/o5VMyRWfVfWw=="], + + "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.15.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-ZkNNG5zL49YpaFzfl6fskNOSxtcZ5uOYmWBkY4wVAvgbSAQzLRVBp+xArGWh2oXlY/WgL99zQSGTv7RI5E6nzA=="], + + "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.15.11", "", { "os": "win32", "cpu": "ia32" }, "sha512-6XnzORkZCQzvTQ6cPrU7iaT9+i145oLwnin8JrfsLG41wl26+5cNQ2XV3zcbrnFEV6esjOceom9YO1w9mGJByw=="], + + "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.15.11", "", { "os": "win32", "cpu": "x64" }, "sha512-IQ2n6af7XKLL6P1gIeZACskSxK8jWtoKpJWLZmdXTDj1MGzktUy4i+FvpdtxFmJWNavRWH1VmTr6kAubRDHeKw=="], + + "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], + + "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], + + "@swc/types": ["@swc/types@0.1.25", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.1.18", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.18" } }, "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.18", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.18", "@tailwindcss/oxide-darwin-arm64": "4.1.18", "@tailwindcss/oxide-darwin-x64": "4.1.18", "@tailwindcss/oxide-freebsd-x64": "4.1.18", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", "@tailwindcss/oxide-linux-x64-musl": "4.1.18", "@tailwindcss/oxide-wasm32-wasi": "4.1.18", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" } }, "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.18", "", { "os": "android", "cpu": "arm64" }, "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.18", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.18", "", { "os": "darwin", "cpu": "x64" }, "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.18", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18", "", { "os": "linux", "cpu": "arm" }, "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.18", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.0", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.18", "", { "os": "win32", "cpu": "arm64" }, "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.18", "", { "os": "win32", "cpu": "x64" }, "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q=="], + + "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.18", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "postcss": "^8.4.41", "tailwindcss": "4.1.18" } }, "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g=="], + + "@tanstack/query-core": ["@tanstack/query-core@5.90.20", "", {}, "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg=="], + + "@tanstack/react-query": ["@tanstack/react-query@5.90.20", "", { "dependencies": { "@tanstack/query-core": "5.90.20" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-vXBxa+qeyveVO7OA0jX1z+DeyCA4JKnThKv411jd5SORpBKgkcVnYKCiBgECvADvniBX7tobwBmg01qq9JmMJw=="], + + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/istanbul-lib-coverage": ["@types/istanbul-lib-coverage@2.0.6", "", {}, "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w=="], + + "@types/istanbul-lib-report": ["@types/istanbul-lib-report@3.0.3", "", { "dependencies": { "@types/istanbul-lib-coverage": "*" } }, "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA=="], + + "@types/istanbul-reports": ["@types/istanbul-reports@3.0.4", "", { "dependencies": { "@types/istanbul-lib-report": "*" } }, "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ=="], + + "@types/jest": ["@types/jest@30.0.0", "", { "dependencies": { "expect": "^30.0.0", "pretty-format": "^30.0.0" } }, "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], + + "@types/node": ["@types/node@25.1.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA=="], + + "@types/parse-json": ["@types/parse-json@4.0.2", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="], + + "@types/prismjs": ["@types/prismjs@1.26.5", "", {}, "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ=="], + + "@types/react": ["@types/react@19.2.6", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w=="], + + "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + + "@types/react-transition-group": ["@types/react-transition-group@4.4.12", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w=="], + + "@types/stack-utils": ["@types/stack-utils@2.0.3", "", {}, "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="], + + "@types/yargs": ["@types/yargs@17.0.35", "", { "dependencies": { "@types/yargs-parser": "*" } }, "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg=="], + + "@types/yargs-parser": ["@types/yargs-parser@21.0.3", "", {}, "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.54.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.54.0", "@typescript-eslint/type-utils": "8.54.0", "@typescript-eslint/utils": "8.54.0", "@typescript-eslint/visitor-keys": "8.54.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.54.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.54.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.54.0", "@typescript-eslint/types": "8.54.0", "@typescript-eslint/typescript-estree": "8.54.0", "@typescript-eslint/visitor-keys": "8.54.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA=="], + + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.54.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.54.0", "@typescript-eslint/types": "^8.54.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.54.0", "", { "dependencies": { "@typescript-eslint/types": "8.54.0", "@typescript-eslint/visitor-keys": "8.54.0" } }, "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg=="], + + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.54.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.54.0", "", { "dependencies": { "@typescript-eslint/types": "8.54.0", "@typescript-eslint/typescript-estree": "8.54.0", "@typescript-eslint/utils": "8.54.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.54.0", "", {}, "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.54.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.54.0", "@typescript-eslint/tsconfig-utils": "8.54.0", "@typescript-eslint/types": "8.54.0", "@typescript-eslint/visitor-keys": "8.54.0", "debug": "^4.4.3", "minimatch": "^9.0.5", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.54.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.54.0", "@typescript-eslint/types": "8.54.0", "@typescript-eslint/typescript-estree": "8.54.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.54.0", "", { "dependencies": { "@typescript-eslint/types": "8.54.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA=="], + + "@typescript/vfs": ["@typescript/vfs@1.6.2", "", { "dependencies": { "debug": "^4.1.1" }, "peerDependencies": { "typescript": "*" } }, "sha512-hoBwJwcbKHmvd2QVebiytN1aELvpk9B74B4L1mFm/XT1Q/VOYAWl2vQ9AWRFtQq8zmz6enTpfTV8WRc4ATjW/g=="], + + "@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="], + + "@unrs/resolver-binding-android-arm64": ["@unrs/resolver-binding-android-arm64@1.11.1", "", { "os": "android", "cpu": "arm64" }, "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g=="], + + "@unrs/resolver-binding-darwin-arm64": ["@unrs/resolver-binding-darwin-arm64@1.11.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g=="], + + "@unrs/resolver-binding-darwin-x64": ["@unrs/resolver-binding-darwin-x64@1.11.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ=="], + + "@unrs/resolver-binding-freebsd-x64": ["@unrs/resolver-binding-freebsd-x64@1.11.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw=="], + + "@unrs/resolver-binding-linux-arm-gnueabihf": ["@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1", "", { "os": "linux", "cpu": "arm" }, "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw=="], + + "@unrs/resolver-binding-linux-arm-musleabihf": ["@unrs/resolver-binding-linux-arm-musleabihf@1.11.1", "", { "os": "linux", "cpu": "arm" }, "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw=="], + + "@unrs/resolver-binding-linux-arm64-gnu": ["@unrs/resolver-binding-linux-arm64-gnu@1.11.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ=="], + + "@unrs/resolver-binding-linux-arm64-musl": ["@unrs/resolver-binding-linux-arm64-musl@1.11.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w=="], + + "@unrs/resolver-binding-linux-ppc64-gnu": ["@unrs/resolver-binding-linux-ppc64-gnu@1.11.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA=="], + + "@unrs/resolver-binding-linux-riscv64-gnu": ["@unrs/resolver-binding-linux-riscv64-gnu@1.11.1", "", { "os": "linux", "cpu": "none" }, "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ=="], + + "@unrs/resolver-binding-linux-riscv64-musl": ["@unrs/resolver-binding-linux-riscv64-musl@1.11.1", "", { "os": "linux", "cpu": "none" }, "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew=="], + + "@unrs/resolver-binding-linux-s390x-gnu": ["@unrs/resolver-binding-linux-s390x-gnu@1.11.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg=="], + + "@unrs/resolver-binding-linux-x64-gnu": ["@unrs/resolver-binding-linux-x64-gnu@1.11.1", "", { "os": "linux", "cpu": "x64" }, "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w=="], + + "@unrs/resolver-binding-linux-x64-musl": ["@unrs/resolver-binding-linux-x64-musl@1.11.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA=="], + + "@unrs/resolver-binding-wasm32-wasi": ["@unrs/resolver-binding-wasm32-wasi@1.11.1", "", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.11" }, "cpu": "none" }, "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ=="], + + "@unrs/resolver-binding-win32-arm64-msvc": ["@unrs/resolver-binding-win32-arm64-msvc@1.11.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw=="], + + "@unrs/resolver-binding-win32-ia32-msvc": ["@unrs/resolver-binding-win32-ia32-msvc@1.11.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ=="], + + "@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="], + + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "acorn-walk": ["acorn-walk@8.3.4", "", { "dependencies": { "acorn": "^8.11.0" } }, "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g=="], + + "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + + "ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="], + + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "ansis": ["ansis@3.17.0", "", {}, "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], + + "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="], + + "array-includes": ["array-includes@3.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.0", "es-object-atoms": "^1.1.1", "get-intrinsic": "^1.3.0", "is-string": "^1.1.1", "math-intrinsics": "^1.1.0" } }, "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ=="], + + "array.prototype.findlast": ["array.prototype.findlast@1.2.5", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ=="], + + "array.prototype.findlastindex": ["array.prototype.findlastindex@1.2.6", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-shim-unscopables": "^1.1.0" } }, "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ=="], + + "array.prototype.flat": ["array.prototype.flat@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg=="], + + "array.prototype.flatmap": ["array.prototype.flatmap@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg=="], + + "array.prototype.tosorted": ["array.prototype.tosorted@1.1.4", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3", "es-errors": "^1.3.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA=="], + + "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="], + + "ast-types-flow": ["ast-types-flow@0.0.8", "", {}, "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ=="], + + "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], + + "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="], + + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + + "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], + + "axe-core": ["axe-core@4.11.1", "", {}, "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A=="], + + "axios": ["axios@1.13.4", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg=="], + + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], + + "babel-plugin-macros": ["babel-plugin-macros@3.1.0", "", { "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", "resolve": "^1.19.0" } }, "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "base64-arraybuffer": ["base64-arraybuffer@1.0.2", "", {}, "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.19", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg=="], + + "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], + + "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001766", "", {}, "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + + "ci-info": ["ci-info@4.4.0", "", {}, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="], + + "clean-stack": ["clean-stack@3.0.1", "", { "dependencies": { "escape-string-regexp": "4.0.0" } }, "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg=="], + + "cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], + + "cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], + + "cli-truncate": ["cli-truncate@5.1.1", "", { "dependencies": { "slice-ansi": "^7.1.0", "string-width": "^8.0.0" } }, "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A=="], + + "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="], + + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "cosmiconfig": ["cosmiconfig@7.1.0", "", { "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.10.0" } }, "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "css-line-break": ["css-line-break@2.1.0", "", { "dependencies": { "utrie": "^1.0.2" } }, "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "daisyui": ["daisyui@5.5.14", "", {}, "sha512-L47rvw7I7hK68TA97VB8Ee0woHew+/ohR6Lx6Ah/krfISOqcG4My7poNpX5Mo5/ytMxiR40fEaz6njzDi7cuSg=="], + + "damerau-levenshtein": ["damerau-levenshtein@1.0.8", "", {}, "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="], + + "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="], + + "data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="], + + "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="], + + "debounce": ["debounce@1.2.1", "", {}, "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], + + "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], + + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="], + + "dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "duplexer": ["duplexer@0.1.2", "", {}, "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="], + + "ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.282", "", {}, "sha512-FCPkJtpst28UmFzd903iU7PdeVTfY0KAeJy+Lk0GLZRwgwYHn/irRcaCbQQOmr5Vytc/7rcavsYLvTM8RiHYhQ=="], + + "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "enhanced-resolve": ["enhanced-resolve@5.18.4", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q=="], + + "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], + + "error-ex": ["error-ex@1.3.4", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="], + + "es-abstract": ["es-abstract@1.24.1", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-iterator-helpers": ["es-iterator-helpers@1.2.2", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.1", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.3.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.5", "safe-array-concat": "^1.1.3" } }, "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "es-shim-unscopables": ["es-shim-unscopables@1.1.0", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw=="], + + "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@9.39.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.2", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw=="], + + "eslint-config-next": ["eslint-config-next@16.1.6", "", { "dependencies": { "@next/eslint-plugin-next": "16.1.6", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", "eslint-plugin-react-hooks": "^7.0.0", "globals": "16.4.0", "typescript-eslint": "^8.46.0" }, "peerDependencies": { "eslint": ">=9.0.0", "typescript": ">=3.3.1" }, "optionalPeers": ["typescript"] }, "sha512-vKq40io2B0XtkkNDYyleATwblNt8xuh3FWp8SpSz3pt7P01OkBFlKsJZ2mWt5WsCySlDQLckb1zMY9yE9Qy0LA=="], + + "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.9", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", "resolve": "^1.22.4" } }, "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g=="], + + "eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@3.10.1", "", { "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "^4.4.0", "get-tsconfig": "^4.10.0", "is-bun-module": "^2.0.0", "stable-hash": "^0.0.5", "tinyglobby": "^0.2.13", "unrs-resolver": "^1.6.2" }, "peerDependencies": { "eslint": "*", "eslint-plugin-import": "*", "eslint-plugin-import-x": "*" }, "optionalPeers": ["eslint-plugin-import", "eslint-plugin-import-x"] }, "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ=="], + + "eslint-module-utils": ["eslint-module-utils@2.12.1", "", { "dependencies": { "debug": "^3.2.7" } }, "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw=="], + + "eslint-plugin-import": ["eslint-plugin-import@2.32.0", "", { "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", "array.prototype.findlastindex": "^1.2.6", "array.prototype.flat": "^1.3.3", "array.prototype.flatmap": "^1.3.3", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.12.1", "hasown": "^2.0.2", "is-core-module": "^2.16.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", "object.values": "^1.2.1", "semver": "^6.3.1", "string.prototype.trimend": "^1.0.9", "tsconfig-paths": "^3.15.0" }, "peerDependencies": { "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA=="], + + "eslint-plugin-jsx-a11y": ["eslint-plugin-jsx-a11y@6.10.2", "", { "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", "array.prototype.flatmap": "^1.3.2", "ast-types-flow": "^0.0.8", "axe-core": "^4.10.0", "axobject-query": "^4.1.0", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", "hasown": "^2.0.2", "jsx-ast-utils": "^3.3.5", "language-tags": "^1.0.9", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "safe-regex-test": "^1.0.3", "string.prototype.includes": "^2.0.1" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q=="], + + "eslint-plugin-react": ["eslint-plugin-react@7.37.5", "", { "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA=="], + + "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@7.0.1", "", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "hermes-parser": "^0.25.1", "zod": "^3.25.0 || ^4.0.0", "zod-validation-error": "^3.5.0 || ^4.0.0" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA=="], + + "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], + + "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + + "esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="], + + "expect": ["expect@30.2.0", "", { "dependencies": { "@jest/expect-utils": "30.2.0", "@jest/get-type": "30.1.0", "jest-matcher-utils": "30.2.0", "jest-message-util": "30.2.0", "jest-mock": "30.2.0", "jest-util": "30.2.0" } }, "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw=="], + + "fast-average-color": ["fast-average-color@9.5.0", "", {}, "sha512-nC6x2YIlJ9xxgkMFMd1BNoM1ctMjNoRKfRliPmiEWW3S6rLTHiQcy9g3pt/xiKv/D0NAAkhb9VyV+WJFvTqMGg=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-glob": ["fast-glob@3.3.1", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + + "filelist": ["filelist@1.0.4", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "find-root": ["find-root@1.1.0", "", {}, "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], + + "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], + + "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], + + "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], + + "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], + + "framer-motion": ["framer-motion@12.29.2", "", { "dependencies": { "motion-dom": "^12.29.2", "motion-utils": "^12.29.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-lSNRzBJk4wuIy0emYQ/nfZ7eWhqud2umPKw2QAQki6uKhZPKm2hRQHeQoHTG9MIvfobb+A/LbEWPJU794ZUKrg=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="], + + "functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="], + + "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], + + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + + "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-package-type": ["get-package-type@0.1.0", "", {}, "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="], + + "get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="], + + "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "gzip-size": ["gzip-size@6.0.0", "", { "dependencies": { "duplexer": "^0.1.2" } }, "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q=="], + + "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], + + "has-proto": ["has-proto@1.2.0", "", { "dependencies": { "dunder-proto": "^1.0.0" } }, "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="], + + "hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="], + + "hoist-non-react-statics": ["hoist-non-react-statics@3.3.2", "", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="], + + "html-escaper": ["html-escaper@2.0.2", "", {}, "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="], + + "html2canvas-pro": ["html2canvas-pro@1.6.6", "", { "dependencies": { "css-line-break": "^2.1.0", "text-segmentation": "^1.0.3" } }, "sha512-5mRhTXZhv4B0kIcsn3bFBjol2o8vzP35mhtxdXBGPA3V3gZd6Sa2PIIFbT//DiqAX8UuywlcJit5jRKej4nV4Q=="], + + "icu-minify": ["icu-minify@4.8.2", "", { "dependencies": { "@formatjs/icu-messageformat-parser": "^3.4.0" } }, "sha512-LHBQV+skKkjZSPd590pZ7ZAHftUgda3eFjeuNwA8/15L8T8loCNBktKQyTlkodAU86KovFXeg/9WntlAo5wA5A=="], + + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], + + "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], + + "intl-messageformat": ["intl-messageformat@11.1.1", "", { "dependencies": { "@formatjs/ecma402-abstract": "3.1.0", "@formatjs/fast-memoize": "3.1.0", "@formatjs/icu-messageformat-parser": "3.5.0", "tslib": "^2.8.1" } }, "sha512-vnrF2f4vfsdaFY6tuLZfzGcx1GZFMFAq6c7QdK3HSXNcGXEIQncNgbeAcnpjAOszQzq3Jbol2SwgshIGY08WyA=="], + + "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], + + "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], + + "is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="], + + "is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="], + + "is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="], + + "is-bun-module": ["is-bun-module@2.0.0", "", { "dependencies": { "semver": "^7.7.1" } }, "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ=="], + + "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], + + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], + + "is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="], + + "is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="], + + "is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="], + + "is-negative-zero": ["is-negative-zero@2.0.3", "", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="], + + "is-plain-object": ["is-plain-object@5.0.0", "", {}, "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="], + + "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], + + "is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="], + + "is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="], + + "is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="], + + "is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="], + + "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], + + "is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="], + + "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="], + + "is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="], + + "is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], + + "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="], + + "jake": ["jake@10.9.4", "", { "dependencies": { "async": "^3.2.6", "filelist": "^1.0.4", "picocolors": "^1.1.1" }, "bin": { "jake": "bin/cli.js" } }, "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA=="], + + "jest-diff": ["jest-diff@30.2.0", "", { "dependencies": { "@jest/diff-sequences": "30.0.1", "@jest/get-type": "30.1.0", "chalk": "^4.1.2", "pretty-format": "30.2.0" } }, "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A=="], + + "jest-matcher-utils": ["jest-matcher-utils@30.2.0", "", { "dependencies": { "@jest/get-type": "30.1.0", "chalk": "^4.1.2", "jest-diff": "30.2.0", "pretty-format": "30.2.0" } }, "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg=="], + + "jest-message-util": ["jest-message-util@30.2.0", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@jest/types": "30.2.0", "@types/stack-utils": "^2.0.3", "chalk": "^4.1.2", "graceful-fs": "^4.2.11", "micromatch": "^4.0.8", "pretty-format": "30.2.0", "slash": "^3.0.0", "stack-utils": "^2.0.6" } }, "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw=="], + + "jest-mock": ["jest-mock@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "jest-util": "30.2.0" } }, "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw=="], + + "jest-regex-util": ["jest-regex-util@30.0.1", "", {}, "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA=="], + + "jest-util": ["jest-util@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", "graceful-fs": "^4.2.11", "picomatch": "^4.0.2" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], + + "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="], + + "jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="], + + "language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="], + + "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], + + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + + "listr2": ["listr2@9.0.5", "", { "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + + "lru-cache": ["lru-cache@11.2.5", "", {}, "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw=="], + + "lucide-react": ["lucide-react@0.563.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-8dXPB2GI4dI8jV4MgUDGBeLdGk8ekfqVZ0BdLcrRzocGgG75ltNEmWS+gE7uokKF/0oSUuczNDT+g9hFJ23FkA=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "memoize-one": ["memoize-one@6.0.0", "", {}, "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], + + "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + + "motion-dom": ["motion-dom@12.29.2", "", { "dependencies": { "motion-utils": "^12.29.2" } }, "sha512-/k+NuycVV8pykxyiTCoFzIVLA95Nb1BFIVvfSu9L50/6K6qNeAYtkxXILy/LRutt7AzaYDc2myj0wkCVVYAPPA=="], + + "motion-utils": ["motion-utils@12.29.2", "", {}, "sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A=="], + + "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "napi-postinstall": ["napi-postinstall@0.3.4", "", { "bin": { "napi-postinstall": "lib/cli.js" } }, "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + + "next": ["next@16.1.6", "", { "dependencies": { "@next/env": "16.1.6", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.6", "@next/swc-darwin-x64": "16.1.6", "@next/swc-linux-arm64-gnu": "16.1.6", "@next/swc-linux-arm64-musl": "16.1.6", "@next/swc-linux-x64-gnu": "16.1.6", "@next/swc-linux-x64-musl": "16.1.6", "@next/swc-win32-arm64-msvc": "16.1.6", "@next/swc-win32-x64-msvc": "16.1.6", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw=="], + + "next-intl": ["next-intl@4.8.2", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.5.4", "@parcel/watcher": "^2.4.1", "@swc/core": "^1.15.2", "icu-minify": "^4.8.2", "negotiator": "^1.0.0", "next-intl-swc-plugin-extractor": "^4.8.2", "po-parser": "^2.1.1", "use-intl": "^4.8.2" }, "peerDependencies": { "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0", "typescript": "^5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-GuuwyvyEI49/oehQbBXEoY8KSIYCzmfMLhmIwhMXTb+yeBmly1PnJcpgph3KczQ+HTJMXwXCmkizgtT8jBMf3A=="], + + "next-intl-swc-plugin-extractor": ["next-intl-swc-plugin-extractor@4.8.2", "", {}, "sha512-sHDs36L1VZmFHj3tPHsD+KZJtnsRudHlNvT0ieIe3iFVn5OpGLTxW3d/Zc/2LXSj5GpGuR6wQeikbhFjU9tMQQ=="], + + "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="], + + "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], + + "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], + + "object.entries": ["object.entries@1.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-object-atoms": "^1.1.1" } }, "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw=="], + + "object.fromentries": ["object.fromentries@2.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-object-atoms": "^1.0.0" } }, "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ=="], + + "object.groupby": ["object.groupby@1.0.3", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2" } }, "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ=="], + + "object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="], + + "onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], + + "opener": ["opener@1.5.2", "", { "bin": { "opener": "bin/opener-bin.js" } }, "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A=="], + + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "po-parser": ["po-parser@2.1.1", "", {}, "sha512-ECF4zHLbUItpUgE3OTtLKlPjeBN+fKEczj2zYjDfCGOzicNs0GK3Vg2IoAYwx7LH/XYw43fZQP6xnZ4TkNxSLQ=="], + + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], + + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="], + + "prism-react-renderer": ["prism-react-renderer@2.4.1", "", { "dependencies": { "@types/prismjs": "^1.26.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": ">=16.0.0" } }, "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig=="], + + "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="], + + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], + + "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], + + "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], + + "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + + "react-select": ["react-select@5.10.2", "", { "dependencies": { "@babel/runtime": "^7.12.0", "@emotion/cache": "^11.4.0", "@emotion/react": "^11.8.1", "@floating-ui/dom": "^1.0.1", "@types/react-transition-group": "^4.4.0", "memoize-one": "^6.0.0", "prop-types": "^15.6.0", "react-transition-group": "^4.3.0", "use-isomorphic-layout-effect": "^1.2.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Z33nHdEFWq9tfnfVXaiM12rbJmk+QjFEztWLtmXqQhz6Al4UZZ9xc0wiatmGtUOCCnHN0WizL3tCMYRENX4rVQ=="], + + "react-simple-code-editor": ["react-simple-code-editor@0.14.1", "", { "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-BR5DtNRy+AswWJECyA17qhUDvrrCZ6zXOCfkQY5zSmb96BVUbpVAv03WpcjcwtCwiLbIANx3gebHOcXYn1EHow=="], + + "react-toastify": ["react-toastify@11.0.5", "", { "dependencies": { "clsx": "^2.1.1" }, "peerDependencies": { "react": "^18 || ^19", "react-dom": "^18 || ^19" } }, "sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA=="], + + "react-transition-group": ["react-transition-group@4.4.5", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="], + + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + + "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="], + + "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="], + + "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + + "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="], + + "safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="], + + "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], + + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + + "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + + "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], + + "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="], + + "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="], + + "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "sirv": ["sirv@2.0.4", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ=="], + + "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], + + "slash": ["slash@5.1.0", "", {}, "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg=="], + + "slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="], + + "source-map": ["source-map@0.5.7", "", {}, "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="], + + "stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="], + + "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], + + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "string.prototype.includes": ["string.prototype.includes@2.0.1", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3" } }, "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg=="], + + "string.prototype.matchall": ["string.prototype.matchall@4.0.12", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA=="], + + "string.prototype.repeat": ["string.prototype.repeat@1.0.0", "", { "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" } }, "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w=="], + + "string.prototype.trim": ["string.prototype.trim@1.2.10", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-object-atoms": "^1.0.0", "has-property-descriptors": "^1.0.2" } }, "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA=="], + + "string.prototype.trimend": ["string.prototype.trimend@1.0.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ=="], + + "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="], + + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + + "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], + + "stylis": ["stylis@4.2.0", "", {}, "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "tailwind-scrollbar": ["tailwind-scrollbar@4.0.2", "", { "dependencies": { "prism-react-renderer": "^2.4.1" }, "peerDependencies": { "tailwindcss": "4.x" } }, "sha512-wAQiIxAPqk0MNTPptVe/xoyWi27y+NRGnTwvn4PQnbvB9kp8QUBiGl/wsfoVBHnQxTmhXJSNt9NHTmcz9EivFA=="], + + "tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="], + + "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], + + "text-camel-case": ["text-camel-case@1.2.10", "", { "dependencies": { "text-pascal-case": "1.2.10" } }, "sha512-KNrWeZzQT+gh73V1LnmgTkjK7V+tMRjLCc6VrGwkqbiRdnGVIWBUgIvVnvnaVCxIvZ/2Ke8DCmgPirlQcCqD3Q=="], + + "text-capital-case": ["text-capital-case@1.2.10", "", { "dependencies": { "text-no-case": "1.2.10", "text-upper-case-first": "1.2.10" } }, "sha512-yvViUJKSSQcRO58je224bhPHg/Hij9MEY43zuKShtFzrPwW/fOAarUJ5UkTMSB81AOO1m8q+JiFdxMF4etKZbA=="], + + "text-case": ["text-case@1.2.10", "", { "dependencies": { "text-camel-case": "1.2.10", "text-capital-case": "1.2.10", "text-constant-case": "1.2.10", "text-dot-case": "1.2.10", "text-header-case": "1.2.10", "text-is-lower-case": "1.2.10", "text-is-upper-case": "1.2.10", "text-kebab-case": "1.2.10", "text-lower-case": "1.2.10", "text-lower-case-first": "1.2.10", "text-no-case": "1.2.10", "text-param-case": "1.2.10", "text-pascal-case": "1.2.10", "text-path-case": "1.2.10", "text-sentence-case": "1.2.10", "text-snake-case": "1.2.10", "text-swap-case": "1.2.10", "text-title-case": "1.2.10", "text-upper-case": "1.2.10", "text-upper-case-first": "1.2.10" } }, "sha512-5bY3Ks/u7OJ5YO69iyXrG5Xf2wUZeyko7U78nPUnYoSeuNeAfA5uAix5hTspfkl6smm3yCBObrex+kFvzeIcJg=="], + + "text-constant-case": ["text-constant-case@1.2.10", "", { "dependencies": { "text-no-case": "1.2.10", "text-upper-case": "1.2.10" } }, "sha512-/OfU798O2wrwKN9kQf71WhJeAlklGnbby0Tupp+Ez9NXymW+6oF9LWDRTkN+OreTmHucdvp4WQd6O5Rah5zj8A=="], + + "text-dot-case": ["text-dot-case@1.2.10", "", { "dependencies": { "text-no-case": "1.2.10" } }, "sha512-vf4xguy5y6e39RlDZeWZFMDf2mNkR23VTSVb9e68dUSpfJscG9/1YWWpW3n8TinzQxBZlsn5sT5olL33MvvQXw=="], + + "text-header-case": ["text-header-case@1.2.10", "", { "dependencies": { "text-capital-case": "1.2.10" } }, "sha512-sVb1NY9bwxtu+Z7CVyWbr+I0AkWtF0kEHL/Zz5V2u/WdkjK5tKBwl5nXf0NGy9da4ZUYTBb+TmQpOIqihzvFMQ=="], + + "text-is-lower-case": ["text-is-lower-case@1.2.10", "", {}, "sha512-dMTeTgrdWWfYf3fKxvjMkDPuXWv96cWbd1Uym6Zjv9H855S1uHxjkFsGbTYJ2tEK0NvAylRySTQlI6axlcMc4w=="], + + "text-is-upper-case": ["text-is-upper-case@1.2.10", "", {}, "sha512-PGD/cXoXECGAY1HVZxDdmpJUW2ZUAKQ6DTamDfCHC9fc/z4epOz0pB/ThBnjJA3fz+d2ApkMjAfZDjuZFcodzg=="], + + "text-kebab-case": ["text-kebab-case@1.2.10", "", { "dependencies": { "text-no-case": "1.2.10" } }, "sha512-3XZJAApx5JQpUO7eXo7GQ2TyRcGw3OVbqxz6QJb2h+N8PbLLbz3zJVeXdGrhTkoUIbkSZ6PmHx6LRDaHXTdMcA=="], + + "text-lower-case": ["text-lower-case@1.2.10", "", {}, "sha512-c9j5pIAN3ObAp1+4R7970e1bgtahTRF/5ZQdX2aJBuBngYTYZZIck0NwFXUKk5BnYpLGsre5KFHvpqvf4IYKgg=="], + + "text-lower-case-first": ["text-lower-case-first@1.2.10", "", {}, "sha512-Oro84jZPDLD9alfdZWmtFHYTvCaaSz2o4thPtjMsK4GAkTyVg9juYXWj0y0YFyjLYGH69muWsBe4/MR5S7iolw=="], + + "text-no-case": ["text-no-case@1.2.10", "", { "dependencies": { "text-lower-case": "1.2.10" } }, "sha512-4/m79pzQrywrwEG5lCULY1lQvFY+EKjhH9xSMT6caPK5plqzm9Y7rXyv+UXPd3s9qH6QODZnvsAYWW3M0JgxRA=="], + + "text-param-case": ["text-param-case@1.2.10", "", { "dependencies": { "text-dot-case": "1.2.10" } }, "sha512-hkavcLsRRzZcGryPAshct1AwIOMj/FexYjMaLpGZCYYBn1lcZEeyMzJZPSckzkOYpq35LYSQr3xZto9XU5OAsw=="], + + "text-pascal-case": ["text-pascal-case@1.2.10", "", { "dependencies": { "text-no-case": "1.2.10" } }, "sha512-/kynZD8vTYOmm/RECjIDaz3qYEUZc/N/bnC79XuAFxwXjdNVjj/jGovKJLRzqsYK/39N22XpGcVmGg7yIrbk6w=="], + + "text-path-case": ["text-path-case@1.2.10", "", { "dependencies": { "text-dot-case": "1.2.10" } }, "sha512-vbKdRCaVEeOaW6sm24QP9NbH7TS9S4ZQ3u19H8eylDox7m2HtFwYIBjAPv+v3z4I/+VjrMy9LB54lNP1uEqRHw=="], + + "text-segmentation": ["text-segmentation@1.0.3", "", { "dependencies": { "utrie": "^1.0.2" } }, "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw=="], + + "text-sentence-case": ["text-sentence-case@1.2.10", "", { "dependencies": { "text-no-case": "1.2.10", "text-upper-case-first": "1.2.10" } }, "sha512-NO4MRlbfxFhl9QgQLuCL4xHmvE7PUWHVPWsZxQ5nzRtDjXOUllWvtsvl8CP5tBEvBmzg0kwfflxfhRtr5vBQGg=="], + + "text-snake-case": ["text-snake-case@1.2.10", "", { "dependencies": { "text-dot-case": "1.2.10" } }, "sha512-6ttMZ+B9jkHKun908HYr4xSvEtlbfJJ4MvpQ06JEKRGhwjMI0x8t2Wywp+MEzN6142O6E/zKhra18KyBL6cvXA=="], + + "text-swap-case": ["text-swap-case@1.2.10", "", {}, "sha512-vO3jwInIk0N77oEFakYZ2Hn/llTmRwf2c3RvkX/LfvmLWVp+3QcIc6bwUEtbqGQ5Xh2okjFhYrfkHZstVc3N4Q=="], + + "text-title-case": ["text-title-case@1.2.10", "", { "dependencies": { "text-no-case": "1.2.10", "text-upper-case-first": "1.2.10" } }, "sha512-bqA+WWexUMWu9A3fdNar+3GXXW+c5xOvMyuK5hOx/w0AlqhyQptyCrMFjGB8Fd9dxbryBNmJ+5rWtC1OBDxlaA=="], + + "text-upper-case": ["text-upper-case@1.2.10", "", {}, "sha512-L1AtZ8R+jtSMTq0Ffma9R4Rzbrc3iuYW89BmWFH41AwnDfRmEBlBOllm1ZivRLQ/6pEu2p+3XKBHx9fsMl2CWg=="], + + "text-upper-case-first": ["text-upper-case-first@1.2.10", "", {}, "sha512-VXs7j7BbpKwvolDh5fwpYRmMrUHGkxbY8E90fhBzKUoKfadvWmPT/jFieoZ4UPLzr208pXvQEFbb2zO9Qzs9Fg=="], + + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], + + "ts-api-utils": ["ts-api-utils@2.4.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA=="], + + "ts-to-zod": ["ts-to-zod@5.1.0", "", { "dependencies": { "@clack/prompts": "1.0.0-alpha.4", "@oclif/core": "^4.5.4", "@typescript/vfs": "^1.5.0", "chokidar": "^4.0.3", "listr2": "^9.0.4", "slash": "^5.1.0", "text-case": "^1.2.4", "tslib": "^2.3.1", "tsutils": "^3.21.0", "typescript": "^5.2.2", "zod": "^4.1.5" }, "bin": { "ts-to-zod": "bin/run" } }, "sha512-giqqlvRHunlJqG9tBL/KAO3wWIVZGF//mZiWLKm/fdQnKnz4EN2mtiK5cugN9slytBkdMEXQIaLvMzIScbhhFw=="], + + "tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "tsutils": ["tsutils@3.21.0", "", { "dependencies": { "tslib": "^1.8.1" }, "peerDependencies": { "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA=="], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], + + "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], + + "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="], + + "typed-array-byte-offset": ["typed-array-byte-offset@1.0.4", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.15", "reflect.getprototypeof": "^1.0.9" } }, "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ=="], + + "typed-array-length": ["typed-array-length@1.0.7", "", { "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", "is-typed-array": "^1.1.13", "possible-typed-array-names": "^1.0.0", "reflect.getprototypeof": "^1.0.6" } }, "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "typescript-eslint": ["typescript-eslint@8.54.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.54.0", "@typescript-eslint/parser": "8.54.0", "@typescript-eslint/typescript-estree": "8.54.0", "@typescript-eslint/utils": "8.54.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-CKsJ+g53QpsNPqbzUsfKVgd3Lny4yKZ1pP4qN3jdMOg/sisIDLGyDMezycquXLE5JsEU0wp3dGNdzig0/fmSVQ=="], + + "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], + + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + + "unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="], + + "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "use-intl": ["use-intl@4.8.2", "", { "dependencies": { "@formatjs/fast-memoize": "^3.1.0", "@schummar/icu-type-parser": "1.21.5", "icu-minify": "^4.8.2", "intl-messageformat": "^11.1.0" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" } }, "sha512-3VNXZgDnPFqhIYosQ9W1Hc6K5q+ZelMfawNbexdwL/dY7BTHbceLUBX5Eeex9lgogxTp0pf1SjHuhYNAjr9H3g=="], + + "use-isomorphic-layout-effect": ["use-isomorphic-layout-effect@1.2.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA=="], + + "utrie": ["utrie@1.0.2", "", { "dependencies": { "base64-arraybuffer": "^1.0.2" } }, "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw=="], + + "webpack-bundle-analyzer": ["webpack-bundle-analyzer@4.10.1", "", { "dependencies": { "@discoveryjs/json-ext": "0.5.7", "acorn": "^8.0.4", "acorn-walk": "^8.0.0", "commander": "^7.2.0", "debounce": "^1.2.1", "escape-string-regexp": "^4.0.0", "gzip-size": "^6.0.0", "html-escaper": "^2.0.2", "is-plain-object": "^5.0.0", "opener": "^1.5.2", "picocolors": "^1.0.0", "sirv": "^2.0.3", "ws": "^7.3.1" }, "bin": { "webpack-bundle-analyzer": "lib/bin/analyzer.js" } }, "sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="], + + "which-builtin-type": ["which-builtin-type@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", "is-date-object": "^1.1.0", "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", "which-typed-array": "^1.1.16" } }, "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q=="], + + "which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="], + + "which-typed-array": ["which-typed-array@1.1.20", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg=="], + + "widest-line": ["widest-line@3.1.0", "", { "dependencies": { "string-width": "^4.0.0" } }, "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg=="], + + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "wordwrap": ["wordwrap@1.0.0", "", {}, "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="], + + "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + + "yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + + "zod-validation-error": ["zod-validation-error@4.0.2", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ=="], + + "zustand": ["zustand@5.0.11", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg=="], + + "@babel/core/json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@emotion/babel-plugin/convert-source-map": ["convert-source-map@1.9.0", "", {}, "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + + "@formatjs/ecma402-abstract/@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.8.0", "", { "dependencies": { "@formatjs/fast-memoize": "3.1.0", "tslib": "^2.8.1" } }, "sha512-zgMYWdUlmEZpX2Io+v3LHrfq9xZ6khpQVf9UAw2xYWhGerGgI9XgH1HvL/A34jWiruUJpYlP5pk4g8nIcaDrXQ=="], + + "@oclif/core/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "@oclif/core/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "cli-truncate/string-width": ["string-width@8.1.1", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-KpqHIdDL9KwYk22wEOg/VIqYbrnLeSApsKT/bSj6Ez7pn3CftUiLAv2Lccpq1ALcpLV9UX1Ppn92npZWu2w/aw=="], + + "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-plugin-import/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "eslint-plugin-react/resolve": ["resolve@2.0.0-next.5", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA=="], + + "eslint-plugin-react/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "filelist/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], + + "hoist-non-react-statics/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "jest-message-util/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], + + "listr2/wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], + + "log-update/ansi-escapes": ["ansi-escapes@7.2.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw=="], + + "log-update/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + + "log-update/wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], + + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], + + "prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], + + "stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], + + "string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "tsutils/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], + + "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "@oclif/core/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "cli-truncate/string-width/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + + "filelist/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "listr2/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "listr2/wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "listr2/wrap-ansi/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + + "log-update/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + + "log-update/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "log-update/wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "cli-truncate/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + + "listr2/wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + + "listr2/wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + + "log-update/wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + } +} diff --git a/data/as.json.br b/data/as.json.br new file mode 100644 index 0000000..6173cc9 Binary files /dev/null and b/data/as.json.br differ diff --git a/data/avatar.json.br b/data/avatar.json.br new file mode 100644 index 0000000..428d4b1 Binary files /dev/null and b/data/avatar.json.br differ diff --git a/data/changelog.json b/data/changelog.json new file mode 100644 index 0000000..cc3ac91 --- /dev/null +++ b/data/changelog.json @@ -0,0 +1,34 @@ +[ + { + "version": "4.1.6", + "date": "12/04/2026", + "type": "update", + "items": [ + "Support RobinSR" + ] + }, + { + "version": "4.1.5", + "date": "17/03/2026", + "type": "update", + "items": [ + "New data for 4.1.5" + ] + }, + { + "version": "4.0.5", + "date": "09/02/2026", + "type": "update", + "items": [ + "New data for 4.0.5" + ] + }, + { + "version": "3.8.5", + "date": "11/01/2026", + "type": "improvement", + "items": [ + "Support skills_by_anchor_type" + ] + } +] \ No newline at end of file diff --git a/data/lightcone.json.br b/data/lightcone.json.br new file mode 100644 index 0000000..22d60e6 Binary files /dev/null and b/data/lightcone.json.br differ diff --git a/data/metadata.json.br b/data/metadata.json.br new file mode 100644 index 0000000..5a62c27 Binary files /dev/null and b/data/metadata.json.br differ diff --git a/data/moc.json.br b/data/moc.json.br new file mode 100644 index 0000000..ab53572 Binary files /dev/null and b/data/moc.json.br differ diff --git a/data/monster.json.br b/data/monster.json.br new file mode 100644 index 0000000..f98d83a Binary files /dev/null and b/data/monster.json.br differ diff --git a/data/peak.json.br b/data/peak.json.br new file mode 100644 index 0000000..2bfb20a Binary files /dev/null and b/data/peak.json.br differ diff --git a/data/pf.json.br b/data/pf.json.br new file mode 100644 index 0000000..f09bcae Binary files /dev/null and b/data/pf.json.br differ diff --git a/data/relic.json.br b/data/relic.json.br new file mode 100644 index 0000000..451a587 Binary files /dev/null and b/data/relic.json.br differ diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..06f0e75 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,15 @@ +services: + srtools: + build: + context: . + dockerfile: Dockerfile + container_name: srtools + restart: unless-stopped + ports: + - "3009:3000" + networks: + - srtools-network + +networks: + srtools-network: + driver: bridge diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..3fc8cde --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,13 @@ +import nextCoreWebVitals from "eslint-config-next/core-web-vitals"; +import nextTypescript from "eslint-config-next/typescript"; +import { dirname } from "path"; +import { fileURLToPath } from "url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const eslintConfig = [...nextCoreWebVitals, ...nextTypescript, { + ignores: ["node_modules/**", ".next/**", "out/**", "build/**", "next-env.d.ts"] +}]; + +export default eslintConfig; diff --git a/i18n/request.ts b/i18n/request.ts new file mode 100644 index 0000000..bb494e3 --- /dev/null +++ b/i18n/request.ts @@ -0,0 +1,11 @@ +import { getRequestConfig } from "next-intl/server"; +import { cookies } from "next/headers"; + +export default getRequestConfig( async () => { + const locale = (await cookies()).get("MYNEXTAPP_LOCALE")?.value || "en"; + + return { + locale, + messages: (await import(`../messages/${locale}.json`)).default + } +}) \ No newline at end of file diff --git a/messages/en.json b/messages/en.json new file mode 100644 index 0000000..bba291a --- /dev/null +++ b/messages/en.json @@ -0,0 +1,284 @@ +{ + "TabTitle": { + "title": "Firefly Tools", + "description": "Firefly tools by Firefly Shelter" + }, + "DataPage": { + "skillType": "Skill Type", + "skillName": "Skill Name", + "character": "Character", + "id": "Id", + "path": "Path", + "rarity": "Rarity", + "element": "Element", + "technique": "Technique", + "talent": "Talent", + "basic": "Basic Attack", + "skill": "Skill", + "ultimate": "Ultimate", + "servant": "Servant", + "damage": "Damage", + "type": "Type", + "warrior": "The Destruction", + "knight": "The Preservation", + "mage": "The Erudition", + "priest": "The Abundance", + "rogue": "The Hunt", + "shaman": "The Harmony", + "warlock": "The Nihility", + "memory": "The Remembrance", + "elation": "The Elation", + "fire": "Fire", + "ice": "Ice", + "imaginary": "Imaginary", + "physical": "Physical", + "quantum": "Quantum", + "thunder": "Thunder", + "wind": "Wind", + "hp": "Hp", + "atk": "Atk", + "speed": "Speed", + "critRate": "Crit Rate", + "critDmg": "Crit Dmg", + "breakEffect": "Break Effect", + "effectRes": "Effect Res", + "energyRegenerationRate": "Energy Regeneration Rate", + "effectHitRate": "Effect Hit Rate", + "outgoingHealingBoost": "Outgoing Healing Boost", + "fireDmgBoost": "Fire damage boost", + "iceDmgBoost": "Ice damage Boost", + "imaginaryDmgBoost": "Imaginary damage boost", + "physicalDmgBoost": "Physical damage boost", + "quantumDmgBoost": "Quantum damage boost", + "thunderDmgBoost": "Thunder damage boost", + "windDmgBoost": "Wind damage boost", + "pursued": "Additional damage", + "true damage": "True damage", + "elationdamage": "Elation damage", + "follow-up": "Follow-up Damage", + "elemental damage": "Break and Super break damage", + "dot": "Damage over time ", + "qte": "QTE Skill", + "level": "Level", + "relics": "Relics", + "eidolons": "Eidolons", + "lightcones": "Lightcones", + "loadData": "Load data", + "exportData": "Export data", + "connectSetting": "Connection Setting", + "connected": "Connected", + "unconnected": "Unconnected", + "psConnection": "PS Connection", + "connectionType": "Connection Type", + "status": "Status", + "connectPs": "Connect PS", + "disconnect": "Disconnect", + "other": "Other", + "freeSr": "FreeSR", + "database": "Database", + "enka": "Enka", + "monsterSetting": "Monster Setting", + "serverUrl": "Server URL", + "privateType": "Private Type", + "local": "Local", + "server": "Server", + "username": "Username", + "password": "Password", + "placeholderServerUrl": "Enter server URL", + "placeholderUsername": "Enter username", + "placeholderPassword": "Enter password", + "connectedSuccess": "Connected to PS successfully", + "connectedFailed": "Failed to connect to PS", + "syncSuccess": "Synced data to PS successfully", + "syncFailed": "Failed to sync data to PS", + "sync": "Sync", + "importSetting": "Import Setting", + "profile": "Profile", + "default": "Default", + "copyProfiles": "Copy profiles", + "addNewProfile": "Add new profile", + "createNewProfile": "Create new profile", + "editProfile": "Edit profile", + "placeholderProfileName": "Enter profile name", + "profileName": "Profile name", + "create": "Create", + "update": "Update", + "characterInformation": "Character Information", + "skills": "Skills", + "showcaseCard": "Showcase Card", + "comingSoon": "Coming soon", + "characterName": "Character name", + "placeholderCharacter": "Enter character name", + "characterSettings": "Character Settings", + "levelConfiguration": "Level Configuration", + "characterLevel": "Character Level", + "max": "MAX", + "ultimateEnergy": "Ultimate Energy", + "currentEnergy": "Current Energy", + "setTo50": "Set to 50%", + "battleConfiguration": "Battle Configuration", + "useTechnique": "Use Technique", + "techniqueNote": "Enable pre-battle technique effects", + "enhancement": "Enhancement", + "enhancementLevel": "Enhancement Level", + "origin": "Origin", + "enhancedNote": "Higher enhanced unlock additional abilities", + "lightconeEquipment": "Lightcone Equipment", + "lightconeSettings": "Lightcone Settings", + "placeholderLevel": "Enter level", + "superimpositionRank": "Superimposition Rank", + "ranksNote": "Higher ranks provide stronger effects", + "changeLightcone": "Change Lightcone", + "removeLightcone": "Remove Lightcone", + "equipLightcone": "Equip Lightcone", + "noLightconeEquipped": "No Lightcone Equipped", + "equipLightconeNote": "Equip a lightcone to enhance your character's abilities", + "filter": "Filter", + "selectedCharacters": "Selected Characters", + "selectedProfiles": "Selected Profiles", + "clearAll": "Clear All", + "selectAll": "Select All", + "copy": "Copy", + "copied": "Copied", + "noAvatarSelected": "No avatar selected", + "noAvatarToCopySelected": "No avatar to copy selected", + "pleaseSelectAtLeastOneProfile": "Please select at least one profile", + "pleaseEnterUid": "Please enter UID", + "failedToFetchEnkaData": "Failed to fetch enka data", + "pleaseSelectAtLeastOneCharacter": "Please select at least one character", + "noDataToImport": "No data to import", + "pleaseSelectAFile": "Please select a file", + "fileMustBeAValidJsonFile": "File must be a valid json file", + "importEnkaDataSuccess": "Import Enka data success", + "importFreeSRDataSuccess": "Import FreeSR data success", + "importDatabaseSuccess": "Import database success", + "getData": "Get Data", + "import": "Import", + "freeSRImport": "FreeSR Import", + "onlySupportFreeSRJsonFile": "Only support FreeSR json file", + "pickAFile": "Pick a file", + "lightConeSetting": "LightCone Setting", + "relicMaker": "Relic Maker", + "pleaseSelectAllOptions": "Please select all options", + "relicSavedSuccessfully": "Relic saved successfully", + "mainSettings": "Main Settings", + "mainStat": "Main Stat", + "set": "Set", + "pleaseSelectASet": "Please select a set", + "effectBonus": "Effect Bonus", + "totalRoll": "Total Roll", + "randomizeStats": "Randomize Stats", + "randomizeRolls": "Randomize Rolls", + "selectASubStat": "Select a sub stat", + "selectASet": "Select a set", + "selectAMainStat": "Select a main stat", + "save": "Save", + "reset": "Reset", + "roll": "Roll", + "step": "Step", + "memoryOfChaos": "Memory of Chaos", + "pureFiction": "Pure Fiction", + "apocalypticShadow": "Apocalyptic Shadow", + "customEnemy": "Custom Enemy", + "simulatedUniverse": "Simulated Universe", + "floor": "Floor", + "side": "Side", + "wave": "Wave", + "stage": "Stage", + "useCycleCount": "Use cycle count?", + "useTurbulenceBuff": "Use turbulence buff?", + "firstHalfEnemies": "First half enemies", + "secondHalfEnemies": "Second half enemies", + "listEnemies": "List enemies", + "turbulenceBuff": "Turbulence Buff", + "noEventSelected": "No event selected", + "noTurbulenceBuff": "No Turbulence Buff", + "upper": "Upper", + "lower": "Lower", + "upperToLower": "Upper -> Lower", + "lowerToUpper": "Lower -> Upper", + "selectMOCEvent": "Select MOC Event", + "selectPFEvent": "Select PF Event", + "selectASEvent": "Select AS Event", + "selectCEEvent": "Select CE Event", + "selectEvent": "Select Event", + "selectFloor": "Select a Floor", + "selectSide": "Select a Side", + "selectBuff": "Select a Buff", + "selectStage": "Select a Stage", + "previous": "Previous", + "next": "Next", + "noMonstersFound": "No monsters found", + "addNewWave": "Add New Wave", + "searchStage": "Search stage...", + "noStageFound": "No stage found", + "searchMonster": "Search monster...", + "changeRelic": "Change relic", + "deleteRelic": "Delete relic", + "deleteRelicConfirm": "Are you sure you want to delete relic in slot", + "setEffects": "Set Effects", + "details": "Details", + "normal": "Basic ATK", + "bpskill": "Skill", + "maze": "Technique", + "ultra": "Ultimate", + "servantskill": "Memosprite Skill", + "severaltalent": "Memosprite Talent", + "singleattack": "Single Attack", + "enhance": "Enhance", + "summon": "Summon", + "mazeattack": "Technique Attack", + "blast": "Blast", + "restore": "Restore", + "support": "Support", + "aoeattack": "AoE Attack", + "impair": "Impair", + "bounce": "Bounce", + "active": "Active", + "defence": "Defence", + "inactive": "Inactive", + "maxAll": "Max All", + "maxAllSuccess": "Successfully set skill level to max.", + "maxAllFailed": "Failed to set skill level to max.", + "noRelicEquipped": "No relic equipped", + "anomalyArbitration": "Anomaly Arbitration", + "normalMode": "Normal Mode", + "hardMode": "Hard Mode", + "selectPEAKEvent": "Select PEAK Event", + "mode": "Mode", + "selectMode": "Select a mode", + "rollBack": "Roll Back", + "upRoll": "Up Roll", + "downRoll": "Down Roll", + "actions": "Actions", + "avatars": "Avatars", + "quickView": "Quick View", + "extraSetting": "Extra Settings", + "disableCensorship": "Disable Censorship", + "hideUI": "Hide UI", + "theoryCraftMode": "Theorycraft Mode", + "cycleCount": "Cycle Count", + "pleaseSelectAllSubStats": "Please select all sub stats", + "subStatRollCountCannotBeZero": "Sub stat roll count cannot be zero", + "theoryCraft": "Theorycraft", + "multipathCharacter": "Multipath Character", + "mainPath": "Main Path", + "march7Path": "March 7 Path", + "challenge": "Challenge", + "skipNode": "Skip Node", + "disableSkip": "Disable skip", + "skipNode1": "Skip node 1", + "skipNode2": "Skip node 2", + "extraFeatures": "Extra Features", + "detailTheoryCraft": "Enabling this feature allows you to customize the cycle count and adjust enemy HP in the enemy settings.", + "detailSkipNode": "Enabling this feature allows you to skip (Node 1/Node 2) in Memory of Chaos or Pure Fiction.", + "detailChallengePeak": "Allows changing the Peak season in the current Anomaly.", + "detailHiddenUi": "Enabling this feature will hide the game UI.", + "detailDisableCensorship": "Enabling this feature will disable in-game censorship.", + "detailMultipathCharacter": "Allows changing the Path of certain characters.", + "trailblazer": "Trailblazer", + "listExtraEffect": "List Extra Effect", + "extra": "Extra", + "customLineup": "Custom Lineup" + } +} \ No newline at end of file diff --git a/messages/ja.json b/messages/ja.json new file mode 100644 index 0000000..de6ef5c --- /dev/null +++ b/messages/ja.json @@ -0,0 +1,283 @@ +{ + "TabTitle": { + "title": "Firefly Tools", + "description": "Firefly tools by Firefly Shelter" + }, + "DataPage": { + "skillType": "スキルタイプ", + "skillName": "スキル名", + "character": "キャラクター", + "id": "ID", + "path": "運命", + "rarity": "レアリティ", + "element": "属性", + "technique": "秘技", + "talent": "天賦", + "basic": "通常攻撃", + "skill": "スキル", + "ultimate": "アルティメット", + "servant": "サーバント", + "damage": "ダメージ", + "type": "タイプ", + "warrior": "壊滅", + "knight": "存護", + "mage": "知恵", + "priest": "豊穣", + "rogue": "巡狩", + "shaman": "調和", + "warlock": "虚無", + "memory": "記憶", + "elation": "愉悦", + "fire": "火", + "ice": "氷", + "imaginary": "虚数", + "physical": "物理", + "quantum": "量子", + "thunder": "雷", + "wind": "風", + "hp": "HP", + "atk": "攻撃", + "speed": "速度", + "critRate": "会心率", + "critDmg": "会心ダメージ", + "breakEffect": "撃破特効", + "effectRes": "効果抵抗", + "energyRegenerationRate": "EP回復効率", + "effectHitRate": "効果命中率", + "outgoingHealingBoost": "回復増加", + "fireDmgBoost": "火属性ダメージ強化", + "iceDmgBoost": "氷属性ダメージ強化", + "imaginaryDmgBoost": "幻影ダメージ強化", + "physicalDmgBoost": "物理ダメージ強化", + "quantumDmgBoost": "量子ダメージ強化", + "thunderDmgBoost": "雷属性ダメージ強化", + "windDmgBoost": "風属性ダメージ強化", + "pursued": "追加ダメージ", + "true damage": "確定ダメージ", + "elationdamage": "愉悦ダメージ", + "follow-up": "追撃ダメージ", + "elemental damage": "属性ダメージ", + "dot": "継続ダメージ", + "qte": "QTEスキル", + "level": "レベル", + "relics": "遺物", + "eidolons": "エイドロン", + "lightcones": "光円錐", + "loadData": "データ読み込み", + "exportData": "データ出力", + "connectSetting": "接続設定", + "connected": "接続済み", + "unconnected": "未接続", + "psConnection": "PS接続", + "connectionType": "接続タイプ", + "status": "ステータス", + "connectPs": "PS接続", + "disconnect": "切断", + "other": "その他", + "freeSr": "FreeSR", + "database": "データベース", + "enka": "Enka", + "monsterSetting": "モンスター設定", + "serverUrl": "サーバー URL", + "privateType": "プライベートタイプ", + "local": "ローカル", + "server": "サーバー", + "username": "ユーザー名", + "password": "パスワード", + "placeholderServerUrl": "サーバー URL を入力", + "placeholderUsername": "ユーザー名を入力", + "placeholderPassword": "パスワードを入力", + "connectedSuccess": "PS接続成功", + "connectedFailed": "PS接続失敗", + "syncSuccess": "PSへのデータ同期に成功", + "syncFailed": "PSへのデータ同期に失敗", + "sync": "同期", + "importSetting": "インポート設定", + "profile": "プロファイル", + "default": "デフォルト", + "copyProfiles": "プロファイルをコピー", + "addNewProfile": "プロファイルを追加", + "createNewProfile": "新しいプロファイルを作成", + "editProfile": "プロファイル編集", + "placeholderProfileName": "プロファイル名を入力", + "profileName": "プロファイル名", + "create": "作成", + "update": "更新", + "characterInformation": "キャラクター情報", + "skills": "スキル", + "showcaseCard": "ショーケースカード", + "comingSoon": "近日公開", + "characterName": "キャラクター名", + "placeholderCharacter": "キャラクター名を入力", + "characterSettings": "キャラクター設定", + "levelConfiguration": "レベル構成", + "characterLevel": "キャラクターレベル", + "max": "最大", + "ultimateEnergy": "アルティメットエネルギー", + "currentEnergy": "現在のエネルギー", + "setTo50": "50%に設定", + "battleConfiguration": "バトル構成", + "useTechnique": "秘技を使用", + "techniqueNote": "バトル前に秘技効果を有効化", + "enhancement": "強化", + "enhancementLevel": "強化レベル", + "origin": "由来", + "enhancedNote": "強化レベルに応じて追加スキル解放", + "lightconeEquipment": "光円錐装備", + "lightconeSettings": "光円錐の設定", + "placeholderLevel": "レベルを入力", + "superimpositionRank": "重ねランク", + "ranksNote": "ランクが高いほど効果強化", + "changeLightcone": "光円錐を変更", + "removeLightcone": "光円錐を外す", + "equipLightcone": "光円錐を装備", + "noLightconeEquipped": "光円錐が装備されていません", + "equipLightconeNote": "強化のため光円錐を装備してください", + "filter": "フィルター", + "selectedCharacters": "選択中のキャラクター", + "selectedProfiles": "選択中のプロファイル", + "clearAll": "全てクリア", + "selectAll": "全て選択", + "copy": "コピー", + "copied": "コピー済み", + "noAvatarSelected": "キャラクターが選択されていません", + "noAvatarToCopySelected": "コピーするキャラクター選択されていません", + "pleaseSelectAtLeastOneProfile": "プロファイルを少なくとも1つ選んでください", + "pleaseEnterUid": "UIDを入力してください", + "failedToFetchEnkaData": "Enka のデータ取得に失敗", + "pleaseSelectAtLeastOneCharacter": "キャラクターを最低1つ選択してください", + "noDataToImport": "インポートするデータがありません", + "pleaseSelectAFile": "ファイルを選択してください", + "fileMustBeAValidJsonFile": "有効な JSON ファイルである必要があります", + "importEnkaDataSuccess": "Enka データのインポート成功", + "importFreeSRDataSuccess": "FreeSR データのインポート成功", + "importDatabaseSuccess": "データベースのインポート成功", + "getData": "データ取得", + "import": "インポート", + "freeSRImport": "FreeSR インポート", + "onlySupportFreeSRJsonFile": "FreeSR JSON ファイルのみ対応", + "pickAFile": "ファイル選択", + "lightConeSetting": "ライトコーン設定", + "relicMaker": "遺物製作", + "pleaseSelectAllOptions": "すべてのオプションを選択してください", + "relicSavedSuccessfully": "遺物の保存に成功しました", + "mainSettings": "メイン設定", + "mainStat": "メインステータス", + "set": "セット", + "pleaseSelectASet": "セットを選択してください", + "effectBonus": "効果ボーナス", + "totalRoll": "総ロール数", + "randomizeStats": "ステータスランダム化", + "randomizeRolls": "ロール回数ランダム化", + "selectASubStat": "サブステータスを選択", + "selectASet": "セットを選択", + "selectAMainStat": "メインステータスを選んでください", + "save": "保存", + "reset": "リセット", + "roll": "ロール", + "step": "ステップ", + "memoryOfChaos": "混沌の記憶", + "pureFiction": "虚構叙事", + "apocalypticShadow": "末日の幻影", + "customEnemy": "カスタム敵", + "simulatedUniverse": "模擬宇宙", + "floor": "階層", + "side": "前半/後半", + "wave": "ウェーブ", + "stage": "ステージ", + "useCycleCount": "サイクル数を使用しますか?", + "useTurbulenceBuff": "乱気流バフを使用しますか?", + "firstHalfEnemies": "前半の敵", + "secondHalfEnemies": "後半の敵", + "turbulenceBuff": "乱気流バフ", + "noEventSelected": "イベントが選択されていません", + "noTurbulenceBuff": "乱気流バフがありません", + "upper": "上半", + "lower": "下半", + "upperToLower": "上半 -> 下半", + "lowerToUpper": "下半 -> 上半", + "selectMOCEvent": "MOC イベントを選択", + "selectPFEvent": "PF イベントを選択", + "selectASEvent": "AS イベントを選択", + "selectCEEvent": "CE イベントを選択", + "selectEvent": "イベントを選択", + "selectFloor": "階層を選択", + "selectSide": "前半/後半を選択", + "selectBuff": "バフを選択", + "selectStage": "ステージを選択", + "previous": "前へ", + "next": "次へ", + "noMonstersFound": "敵が見つかりません", + "addNewWave": "新しいウェーブを追加", + "searchStage": "ステージを検索...", + "noStageFound": "ステージが見つかりません", + "searchMonster": "敵を検索...", + "changeRelic": "遺物を変更", + "deleteRelic": "遺物を削除", + "deleteRelicConfirm": "この遺物を削除してもよろしいですか?", + "setEffects": "効果を設定", + "details": "詳細", + "normal": "通常攻撃", + "bpskill": "戦闘スキル", + "maze": "秘技", + "ultra": "必殺技", + "servantskill": "メモスプライトスキル", + "severaltalent": "メモスプライトの才能", + "singleattack": "単体攻撃", + "enhance": "強化", + "summon": "召喚", + "blast": "爆発", + "restore": "回復", + "support": "支援", + "aoeattack": "範囲攻撃", + "mazeattack": "迷宮秘技", + "impair": "弱体化", + "bounce": "弾跳", + "active": "アクティブ", + "inactive": "非アクティブ", + "defence": "防御", + "maxAll": "すべて最大化", + "maxAllSuccess": "スキルレベルを最大に設定しました。", + "maxAllFailed": "スキルレベルの最大設定に失敗しました。", + "noRelicEquipped": "遺物が装備されていません", + "anomalyArbitration": "異相の仲裁", + "normalMode": "通常モード", + "hardMode": "困難モード", + "selectPEAKEvent": "PEAK イベントを選択", + "mode": "モード", + "selectMode": "モードを選択", + "rollBack": "前の状態に戻る", + "upRoll": "サブステータスを増やす", + "downRoll": "サブステータスを減らす", + "actions": "アクション", + "avatars": "アバター", + "quickView": "クイックビュー", + "extraSetting": "追加設定", + "disableCensorship": "検閲を無効化", + "hideUI": "UIを非表示", + "theoryCraftMode": "シアリークラフトモード", + "cycleCount": "サイクル数", + "pleaseSelectAllSubStats": "すべてのサブステータスを選択してください", + "subStatRollCountCannotBeZero": "サブステータスの行数は0にできません", + "theoryCraft": "シアリークラフト", + "multipathCharacter": "複数運命キャラ", + "mainPath": "主人公の運命", + "march7Path": "三月なのかの運命", + "challenge": "挑戦", + "skipNode": "ノードをスキップ", + "disableSkip": "スキップ無効", + "skipNode1": "ノード1をスキップ", + "skipNode2": "ノード2をスキップ", + "extraFeatures": "追加機能", + "detailTheoryCraft": "この機能を有効にすると、サイクル数の調整や敵設定でHPの調整が可能になります。", + "detailSkipNode": "この機能を有効にすると、混沌の記憶または虚構叙事の(ノード1/ノード2)をスキップできます。", + "detailChallengePeak": "現在の異相における「頂」のシーズンを変更できます。", + "detailHiddenUi": "この機能を有効にすると、ゲームのUIを非表示にします。", + "detailDisableCensorship": "この機能を有効にすると、ゲーム内の検閲を無効にします。", + "detailMultipathCharacter": "一部キャラクターの運命を変更できます。", + "trailblazer": "開拓者", + "listExtraEffect": "追加効果一覧", + "extra": "追加", + "customLineup": "カスタム編成" + } +} \ No newline at end of file diff --git a/messages/ko.json b/messages/ko.json new file mode 100644 index 0000000..5868708 --- /dev/null +++ b/messages/ko.json @@ -0,0 +1,283 @@ +{ + "TabTitle": { + "title": "Firefly Tools", + "description": "Firefly tools by Firefly Shelter" + }, + "DataPage": { + "skillType": "스킬 유형", + "skillName": "스킬 이름", + "character": "캐릭터", + "id": "ID", + "path": "운명", + "rarity": "희귀도", + "element": "속성", + "technique": "비기", + "talent": "탈렌트", + "basic": "기본 공격", + "skill": "스킬", + "ultimate": "궁극기", + "servant": "서번트", + "damage": "데미지", + "type": "종류", + "warrior": "파괴", + "knight": "보호", + "mage": "박학", + "priest": "풍요", + "rogue": "사냥", + "shaman": "조화", + "warlock": "허무", + "memory": "기억", + "elation": "환락", + "fire": "화염", + "ice": "얼음", + "imaginary": "환상", + "physical": "물리", + "quantum": "양자", + "thunder": "번개", + "wind": "바람", + "hp": "HP", + "atk": "공격력", + "speed": "속도", + "critRate": "치명타 확률", + "critDmg": "치명타 피해", + "breakEffect": "파괴 피해", + "effectRes": "효과 저항", + "energyRegenerationRate": "에너지 회복속도", + "effectHitRate": "효과 적중률", + "outgoingHealingBoost": "치유 강화", + "fireDmgBoost": "화염 피해 증가", + "iceDmgBoost": "얼음 피해 증가", + "imaginaryDmgBoost": "환상 피해 증가", + "physicalDmgBoost": "물리 피해 증가", + "quantumDmgBoost": "양자 피해 증가", + "thunderDmgBoost": "번개 피해 증가", + "windDmgBoost": "바람 피해 증가", + "pursued": "추가 피해", + "true damage": "진실한 피해", + "elationdamage": "환락 피해", + "follow-up": "후속 피해", + "elemental damage": "파괴 및 초파괴 피해", + "dot": "시간 경과 피해", + "qte": "QTE 스킬", + "level": "레벨", + "relics": "유물", + "eidolons": "에이돌론", + "lightcones": "라이트콘", + "loadData": "데이터 불러오기", + "exportData": "데이터 내보내기", + "connectSetting": "연결 설정", + "connected": "연결됨", + "unconnected": "연결 안됨", + "psConnection": "PS 연결", + "connectionType": "연결 타입", + "status": "상태", + "connectPs": "PS 연결", + "disconnect": "연결 끊기", + "other": "기타", + "freeSr": "FreeSR", + "database": "데이터베이스", + "enka": "Enka", + "monsterSetting": "몬스터 설정", + "serverUrl": "서버 URL", + "privateType": "프라이빗 타입", + "local": "로컬", + "server": "서버", + "username": "사용자명", + "password": "비밀번호", + "placeholderServerUrl": "서버 URL 입력", + "placeholderUsername": "사용자명 입력", + "placeholderPassword": "비밀번호 입력", + "connectedSuccess": "PS에 성공적으로 연결됨", + "connectedFailed": "PS 연결 실패", + "syncSuccess": "PS에 데이터 동기화 성공", + "syncFailed": "PS에 데이터 동기화 실패", + "sync": "동기화", + "importSetting": "설정 가져오기", + "profile": "프로필", + "default": "기본", + "copyProfiles": "프로필 복사", + "addNewProfile": "새 프로필 추가", + "createNewProfile": "새 프로필 생성", + "editProfile": "프로필 편집", + "placeholderProfileName": "프로필 이름 입력", + "profileName": "프로필 이름", + "create": "생성", + "update": "업데이트", + "characterInformation": "캐릭터 정보", + "skills": "스킬", + "showcaseCard": "쇼케이스 카드", + "comingSoon": "곧 출시", + "characterName": "캐릭터 이름", + "placeholderCharacter": "캐릭터 이름 입력", + "characterSettings": "캐릭터 설정", + "levelConfiguration": "레벨 구성", + "characterLevel": "캐릭터 레벨", + "max": "최대", + "ultimateEnergy": "궁극 에너지", + "currentEnergy": "현재 에너지", + "setTo50": "50%로 설정", + "battleConfiguration": "전투 구성", + "useTechnique": "비기 사용", + "techniqueNote": "전투 전 비기 효과 활성화", + "enhancement": "강화", + "enhancementLevel": "강화 레벨", + "origin": "출처", + "enhancedNote": "강화 레벨이 높을수록 추가 효과 해제", + "lightconeEquipment": "라이트콘 장비", + "lightconeSettings": "라이트콘 설정", + "placeholderLevel": "레벨 입력", + "superimpositionRank": "중첩 등급", + "ranksNote": "등급이 높을수록 효과가 강함", + "changeLightcone": "라이트콘 변경", + "removeLightcone": "라이트콘 제거", + "equipLightcone": "라이트콘 장착", + "noLightconeEquipped": "라이트콘 미장착", + "equipLightconeNote": "캐릭터 능력 향상을 위해 장착하세요", + "filter": "필터", + "selectedCharacters": "선택된 캐릭터", + "selectedProfiles": "선택된 프로필", + "clearAll": "전체 지우기", + "selectAll": "전체 선택", + "copy": "복사", + "copied": "복사됨", + "noAvatarSelected": "캐릭터가 선택되지 않음", + "noAvatarToCopySelected": "복사할 캐릭터가 선택되지 않음", + "pleaseSelectAtLeastOneProfile": "프로필을 최소 하나 선택하세요", + "pleaseEnterUid": "UID를 입력해주세요", + "failedToFetchEnkaData": "Enka 데이터 가져오기 실패", + "pleaseSelectAtLeastOneCharacter": "캐릭터를 최소 하나 선택하세요", + "noDataToImport": "가져올 데이터가 없습니다", + "pleaseSelectAFile": "파일을 선택하세요", + "fileMustBeAValidJsonFile": "유효한 JSON 파일이어야 합니다", + "importEnkaDataSuccess": "Enka 데이터 가져오기 성공", + "importFreeSRDataSuccess": "FreeSR 데이터 가져오기 성공", + "importDatabaseSuccess": "데이터베이스 가져오기 성공", + "getData": "데이터 가져오기", + "import": "가져오기", + "freeSRImport": "FreeSR 가져오기", + "onlySupportFreeSRJsonFile": "FreeSR JSON 파일만 지원", + "pickAFile": "파일 선택", + "lightConeSetting": "라이트콘 설정", + "relicMaker": "유물 제작", + "pleaseSelectAllOptions": "모든 옵션을 선택해주세요", + "relicSavedSuccessfully": "유물 저장 성공", + "mainSettings": "메인 설정", + "mainStat": "주 속성", + "set": "세트", + "pleaseSelectASet": "세트 하나를 선택해주세요", + "effectBonus": "효과 보너스", + "totalRoll": "총 횟수", + "randomizeStats": "속성 랜덤화", + "randomizeRolls": "횟수 랜덤화", + "selectASubStat": "부 속성 선택", + "selectASet": "세트 선택", + "selectAMainStat": "주 속성 선택", + "save": "저장", + "reset": "초기화", + "roll": "굴리기", + "step": "단계", + "memoryOfChaos": "망각의 정원", + "pureFiction": "허구 이야기", + "apocalypticShadow": "종말의 환영", + "customEnemy": "커스텀 적", + "simulatedUniverse": "시뮬레이티드 유니버스", + "floor": "층수", + "side": "전반/후반", + "wave": "웨이브", + "stage": "스테이지", + "useCycleCount": "사이클 수 사용?", + "useTurbulenceBuff": "난류 버프 사용?", + "firstHalfEnemies": "전반 적", + "secondHalfEnemies": "후반 적", + "turbulenceBuff": "난류 버프", + "noEventSelected": "이벤트가 선택되지 않음", + "noTurbulenceBuff": "난류 버프가 없음", + "upper": "상반", + "lower": "하반", + "upperToLower": "상반 -> 하반", + "lowerToUpper": "하반 -> 상반", + "selectMOCEvent": "MOC 이벤트 선택", + "selectPFEvent": "PF 이벤트 선택", + "selectASEvent": "AS 이벤트 선택", + "selectCEEvent": "CE 이벤트 선택", + "selectEvent": "이벤트 선택", + "selectFloor": "층 선택", + "selectSide": "전반/후반 선택", + "selectBuff": "버프 선택", + "selectStage": "스테이지 선택", + "previous": "이전", + "next": "다음", + "noMonstersFound": "적을 찾을 수 없음", + "addNewWave": "새 웨이브 추가", + "searchStage": "스테이지 검색...", + "noStageFound": "스테이지를 찾을 수 없음", + "searchMonster": "적 검색...", + "changeRelic": "유물 변경", + "deleteRelic": "유물 삭제", + "deleteRelicConfirm": "이 슬롯의 유물을 삭제하시겠습니까?", + "setEffects": "효과 설정", + "details": "상세", + "normal": "기본 공격", + "bpskill": "스킬", + "maze": "전술", + "ultra": "필살기", + "servantskill": "메모스프라이트 스킬", + "severaltalent": "메모스프라이트 재능", + "singleattack": "단일 공격", + "enhance": "강화", + "summon": "소환", + "blast": "광역 공격", + "restore": "회복", + "support": "지원", + "aoeattack": "광역 공격", + "mazeattack": "미궁 비기", + "impair": "약화", + "bounce": "튕김", + "active": "활성", + "inactive": "비활성", + "defence": "방어", + "maxAll": "모두 최대치", + "maxAllSuccess": "스킬 레벨이 최대치로 설정되었습니다.", + "maxAllFailed": "스킬 레벨을 최대치로 설정하지 못했습니다.", + "noRelicEquipped": "성유물이 장착되지 않음", + "anomalyArbitration": "이상 중재", + "normalMode": "일반 모드", + "hardMode": "어려움 모드", + "selectPEAKEvent": "PEAK 이벤트 선택", + "mode": "모드", + "selectMode": "모드를 선택", + "rollBack": "이전 상태로 되돌리기", + "upRoll": "부옵션 추가", + "downRoll": "부옵션 감소", + "actions": "동작", + "avatars": "아바타", + "quickView": "빠른 조회", + "extraSetting": "추가 설정", + "disableCensorship": "검열 비활성화", + "hideUI": "UI 숨기기", + "theoryCraftMode": "Theory Craft 모드", + "cycleCount": "사이클 수", + "pleaseSelectAllSubStats": "모든 부옵션을 선택하세요", + "subStatRollCountCannotBeZero": "부옵션의 줄 수는 0일 수 없습니다", + "theoryCraft": "Theory Craft", + "multipathCharacter": "다중 운명 캐릭터", + "mainPath": "개척자 운명", + "march7Path": "삼월칠일 운명", + "challenge": "도전", + "skipNode": "노드 건너뛰기", + "disableSkip": "건너뛰기 비활성화", + "skipNode1": "노드 1 건너뛰기", + "skipNode2": "노드 2 건너뛰기", + "extraFeatures": "추가 기능", + "detailTheoryCraft": "이 기능을 활성화하면 사이클 수를 조정하고 적 설정에서 HP를 변경할 수 있습니다.", + "detailSkipNode": "이 기능을 활성화하면 혼돈의 기억 또는 허구 서사의 (노드 1/노드 2)를 건너뛸 수 있습니다.", + "detailChallengePeak": "현재 이형에서의 피크 시즌을 변경할 수 있습니다.", + "detailHiddenUi": "이 기능을 활성화하면 게임 UI가 숨겨집니다.", + "detailDisableCensorship": "이 기능을 활성화하면 게임 내 검열이 비활성화됩니다.", + "detailMultipathCharacter": "일부 캐릭터의 운명을 변경할 수 있습니다.", + "trailblazer": "개척자", + "listExtraEffect": "추가 효과 목록", + "extra": "추가", + "customLineup": "커스텀 편성" + } +} \ No newline at end of file diff --git a/messages/vi.json b/messages/vi.json new file mode 100644 index 0000000..42f0ddf --- /dev/null +++ b/messages/vi.json @@ -0,0 +1,283 @@ +{ + "TabTitle": { + "title": "Firefly Tools", + "description": "Firefly tools by Firefly Shelter" + }, + "DataPage": { + "skillType": "Loại kỹ năng", + "skillName": "Tên kỹ năng", + "character": "Nhân vật", + "id": "ID", + "path": "Vận mệnh", + "rarity": "Độ hiếm", + "element": "Nguyên tố", + "technique": "Bí kỹ", + "talent": "Thiên phú", + "basic": "Đánh thường", + "skill": "Kỹ năng", + "ultimate": "Tuyệt kỹ", + "servant": "Phụ trợ", + "damage": "Sát thương", + "type": "Loại", + "warrior": "Hủy Diệt", + "knight": "Bảo Hộ", + "mage": "Tri Thức", + "priest": "Trù phú", + "rogue": "Săn Bắn", + "shaman": "Hòa Hợp", + "warlock": "Hư Vô", + "memory": "Ký Ức", + "elation": "Vui vẻ", + "fire": "Hỏa", + "ice": "Băng", + "imaginary": "Ảo Ảnh", + "physical": "Vật Lý", + "quantum": "Lượng Tử", + "thunder": "Lôi", + "wind": "Phong", + "hp": "HP", + "atk": "Tấn công", + "speed": "Tốc độ", + "critRate": "Tỷ lệ bạo", + "critDmg": "ST bạo", + "breakEffect": "sát thương kích phá", + "effectRes": "Kháng hiệu ứng", + "energyRegenerationRate": "Tốc độ hồi năng lượng", + "effectHitRate": "Tỷ lệ trúng hiệu ứng", + "outgoingHealingBoost": "Tăng hồi phục", + "fireDmgBoost": "Tăng sát thương Hỏa", + "iceDmgBoost": "Tăng sát thương Băng", + "imaginaryDmgBoost": "Tăng sát thương Ảo Ảnh", + "physicalDmgBoost": "Tăng sát thương Vật Lý", + "quantumDmgBoost": "Tăng sát thương Lượng Tử", + "thunderDmgBoost": "Tăng sát thương Lôi", + "windDmgBoost": "Tăng sát thương Phong", + "pursued": "Sát thương thêm", + "true damage": "Sát thương chuẩn", + "elationdamage": "Sát thương vui vẻ", + "follow-up": "Sát thương phản kích", + "elemental damage": "Sát thương kích phá và siêu kích phá", + "dot": "Sát thương theo thời gian", + "qte": "Kỹ năng QTE", + "level": "Cấp độ", + "relics": "Thánh di vật", + "eidolons": "Tinh hồn", + "lightcones": "Nón ánh sáng", + "loadData": "Tải dữ liệu", + "exportData": "Xuất dữ liệu", + "connectSetting": "Cài đặt kết nối", + "connected": "Đã kết nối", + "unconnected": "Chưa kết nối", + "psConnection": "Kết nối PS", + "connectionType": "Loại kết nối", + "status": "Trạng thái", + "connectPs": "Kết nối PS", + "disconnect": "Ngắt kết nối", + "other": "Khác", + "freeSr": "FreeSR", + "database": "Database", + "enka": "Enka", + "monsterSetting": "Cài đặt quái", + "serverUrl": "Địa chỉ server", + "privateType": "Loại riêng tư", + "local": "Cục bộ", + "server": "Máy chủ", + "username": "Tên người dùng", + "password": "Mật khẩu", + "placeholderServerUrl": "Nhập địa chỉ server", + "placeholderUsername": "Nhập tên người dùng", + "placeholderPassword": "Nhập mật khẩu", + "connectedSuccess": "Kết nối PS thành công", + "connectedFailed": "Kết nối PS thất bại", + "syncSuccess": "Đồng bộ dữ liệu với PS thành công", + "syncFailed": "Đồng bộ dữ liệu với PS thất bại", + "sync": "Đồng bộ", + "importSetting": "Cài đặt nhập", + "profile": "Hồ sơ", + "default": "Mặc định", + "copyProfiles": "Sao chép hồ sơ", + "addNewProfile": "Thêm hồ sơ mới", + "createNewProfile": "Tạo hồ sơ mới", + "editProfile": "Chỉnh sửa hồ sơ", + "placeholderProfileName": "Nhập tên hồ sơ", + "profileName": "Tên hồ sơ", + "create": "Tạo", + "update": "Cập nhật", + "characterInformation": "Thông tin nhân vật", + "skills": "Kỹ năng", + "showcaseCard": "Thẻ trưng bày", + "comingSoon": "Sắp ra mắt", + "characterName": "Tên nhân vật", + "placeholderCharacter": "Nhập tên nhân vật", + "characterSettings": "Cài đặt nhân vật", + "levelConfiguration": "Cấu hình cấp độ", + "characterLevel": "Cấp độ nhân vật", + "max": "Tối đa", + "ultimateEnergy": "Năng lượng tuyệt kỹ", + "currentEnergy": "Năng lượng hiện tại", + "setTo50": "Đặt thành 50%", + "battleConfiguration": "Cấu hình trận đấu", + "useTechnique": "Dùng bí kỹ", + "techniqueNote": "Bật hiệu ứng bí kỹ trước trận", + "enhancement": "Cường hóa", + "enhancementLevel": "Cấp độ cường hóa", + "origin": "Nguyên gốc", + "enhancedNote": "Cường hóa cao mở thêm kỹ năng", + "lightconeEquipment": "Trang bị nón ánh sáng", + "lightconeSettings": "Cài đặt nón ánh sáng", + "placeholderLevel": "Nhập cấp độ", + "superimpositionRank": "Bậc chồng kỹ năng", + "ranksNote": "Bậc càng cao hiệu ứng càng mạnh", + "changeLightcone": "Thay đổi nón ánh sáng", + "removeLightcone": "Gỡ nón ánh sáng", + "equipLightcone": "Trang bị nón ánh sáng", + "noLightconeEquipped": "Chưa trang bị nón ánh sáng", + "equipLightconeNote": "Trang bị nón để tăng sức mạnh cho nhân vật", + "filter": "Lọc", + "selectedCharacters": "Nhân vật đã chọn", + "selectedProfiles": "Hồ sơ đã chọn", + "clearAll": "Xóa tất cả", + "selectAll": "Chọn tất cả", + "copy": "Sao chép", + "copied": "Đã sao chép", + "noAvatarSelected": "Chưa chọn nhân vật", + "noAvatarToCopySelected": "Chưa chọn nhân vật để sao chép", + "pleaseSelectAtLeastOneProfile": "Vui lòng chọn ít nhất một hồ sơ", + "pleaseEnterUid": "Vui lòng nhập UID", + "failedToFetchEnkaData": "Lấy dữ liệu Enka thất bại", + "pleaseSelectAtLeastOneCharacter": "Vui lòng chọn ít nhất một nhân vật", + "noDataToImport": "Không có dữ liệu để nhập", + "pleaseSelectAFile": "Vui lòng chọn một tệp", + "fileMustBeAValidJsonFile": "Tệp phải là tệp JSON hợp lệ", + "importEnkaDataSuccess": "Nhập dữ liệu Enka thành công", + "importFreeSRDataSuccess": "Nhập dữ liệu FreeSR thành công", + "importDatabaseSuccess": "Nhập cơ sở dữ liệu thành công", + "getData": "Lấy dữ liệu", + "import": "Nhập", + "freeSRImport": "Nhập FreeSR", + "onlySupportFreeSRJsonFile": "Chỉ hỗ trợ tệp JSON từ FreeSR", + "pickAFile": "Chọn tệp", + "lightConeSetting": "Cài đặt Nón Ánh Sáng", + "relicMaker": "Trình tạo Thánh Di Vật", + "pleaseSelectAllOptions": "Vui lòng chọn tất cả tùy chọn", + "relicSavedSuccessfully": "Lưu thánh di vật thành công", + "mainSettings": "Cài đặt chính", + "mainStat": "Chỉ số chính", + "set": "Bộ", + "pleaseSelectASet": "Vui lòng chọn một bộ", + "effectBonus": "Hiệu ứng cộng thêm", + "totalRoll": "Tổng số dòng", + "randomizeStats": "Ngẫu nhiên chỉ số", + "randomizeRolls": "Ngẫu nhiên số dòng", + "selectASubStat": "Chọn chỉ số phụ", + "selectASet": "Chọn một bộ", + "selectAMainStat": "Chọn chỉ số chính", + "save": "Lưu", + "reset": "Đặt lại toàn bộ", + "roll": "Số dòng", + "step": "Bước nhảy", + "memoryOfChaos": "Hồi ức hỗn độn", + "pureFiction": "Kể chuyện hư cấu", + "apocalypticShadow": "Ảo ảnh tận thế", + "customEnemy": "Kẻ địch tùy chỉnh", + "simulatedUniverse": "Vũ trụ mô phỏng", + "floor": "Tầng", + "side": "Nửa trận", + "wave": "Đợt", + "stage": "Màn", + "useCycleCount": "Dùng dếm chu kỳ?", + "useTurbulenceBuff": "Dùng buff hỗn loạn?", + "firstHalfEnemies": "Địch nửa đầu", + "secondHalfEnemies": "Địch nửa sau", + "turbulenceBuff": "Buff hỗn loạn", + "noEventSelected": "Không có sự kiện", + "noTurbulenceBuff": "Không có buff hỗn loạn", + "upper": "Nửa trên", + "lower": "Nửa dưới", + "upperToLower": "Nửa trên -> Nửa dưới", + "lowerToUpper": "Nửa dưới -> Nửa trên", + "selectMOCEvent": "Chọn sự kiện MOC", + "selectPFEvent": "Chọn sự kiện PF", + "selectASEvent": "Chọn sự kiện AS", + "selectCEEvent": "Chọn sự kiện CE", + "selectPEAKEvent": "Chọn sự kiện PEAK", + "selectEvent": "Chọn sự kiện", + "selectFloor": "Chọn tầng", + "selectSide": "Chọn nửa trận", + "selectBuff": "Chọn buff", + "selectStage": "Chọn màn", + "previous": "Trước", + "next": "Tiếp", + "noMonstersFound": "Không tìm thấy quái", + "addNewWave": "Thêm đợt mới", + "searchStage": "Tìm màn...", + "noStageFound": "Không tìm thấy màn", + "searchMonster": "Tìm quái...", + "changeRelic": "Thay đổi di vật", + "deleteRelic": "Xóa di vật", + "deleteRelicConfirm": "Bạn có chắc chắn muốn xóa di vật trong ô này không?", + "setEffects": "Thiết lập hiệu ứng", + "details": "Chi tiết", + "normal": "Đánh thường", + "bpskill": "Chiến kỹ", + "maze": "Bí kỹ", + "ultra": "Tuyệt kỹ", + "servantskill": "Kỹ năng vật triệu hồi", + "severaltalent": "Thiên phú vật triệu hồi", + "singleattack": "Tấn công đơn", + "enhance": "Cường hóa", + "summon": "Triệu hồi", + "blast": "Tấn công 3 mục tiêu", + "restore": "Hồi phục", + "support": "Hỗ trợ", + "aoeattack": "Tấn công đa mục tiêu", + "mazeattack": "Bí kỹ tấn công", + "impair": "Suy yếu", + "bounce": "Nảy bật", + "active": "Kích hoạt", + "inactive": "Không kích hoạt", + "defence": "Phòng thủ", + "maxAll": "Tối đa tất cả", + "maxAllSuccess": "Đã thiết lập cấp độ kỹ năng tối đa thành công.", + "maxAllFailed": "Thiết lập cấp độ kỹ năng tối đa thất bại.", + "noRelicEquipped": "Không có di vật", + "anomalyArbitration": "Trọng tài dị tướng", + "normalMode": "Chế độ thường", + "hardMode": "Chế độ khó", + "mode": "Chế độ", + "selectMode": "Chọn chế độ", + "rollBack": "Quay lại bước trước", + "upRoll": "Tăng dòng", + "downRoll": "Giảm dòng", + "actions": "Hành động", + "avatars": "Nhân vật", + "quickView": "Xem nhanh", + "extraSetting": "Cài đặt bổ sung", + "disableCensorship": "Tắt kiểm duyệt", + "hideUI": "Ẩn giao diện", + "theoryCraftMode": "Chế độ Theory Craft", + "cycleCount": "Số vòng", + "pleaseSelectAllSubStats": "Vui lòng chọn tất cả chỉ số phụ", + "subStatRollCountCannotBeZero": "Số dòng của chỉ số phụ không thể bằng 0", + "theoryCraft": "Theory Craft", + "multipathCharacter": "Nhân vật đa Vận Mệnh", + "mainPath": "Vận Mệnh Nhân Vật Chính", + "march7Path": "Vận Mệnh March 7", + "challenge": "Thử thách", + "skipNode": "Bỏ qua node", + "disableSkip": "Tắt bỏ qua", + "skipNode1": "Bỏ qua node 1", + "skipNode2": "Bỏ qua node 2", + "extraFeatures": "Tính năng bổ sung", + "detailTheoryCraft": "Khi bật tính năng này sẽ cho phép tùy chỉnh số cycle và trong mục kẻ địch tủy chỉnh sẽ cho phép điều chỉnh số hp.", + "detailSkipNode": "Khi bật tính năng này sẽ cho phép bỏ qua (node 1/node 2) của Hồi ức hỗn độn hoặc Kể chuyện hư cấu.", + "detailChallengePeak": "Cho phép thay đổi mùa Trọng tại dị tướng hiện tại.", + "detailHiddenUi": "Khi bật tính năng này sẽ ẩn giao diện của game.", + "detailDisableCensorship": "Khi bật tính năng này sẽ tắt kiểm duyệt của game.", + "detailMultipathCharacter": "Cho phép thay đổi Vận Mệnh của một vài nhân vật.", + "trailblazer": "Nhà khai phá", + "listExtraEffect": "Danh sách hiệu ứng bổ sung", + "extra": "Bổ sung", + "customLineup": "Đội hình tùy chỉnh" + } +} \ No newline at end of file diff --git a/messages/zh.json b/messages/zh.json new file mode 100644 index 0000000..9f4c314 --- /dev/null +++ b/messages/zh.json @@ -0,0 +1,283 @@ +{ + "TabTitle": { + "title": "Firefly Tools", + "description": "Firefly tools by Firefly Shelter" + }, + "DataPage": { + "skillType": "技能类型", + "skillName": "技能名称", + "character": "角色", + "id": "ID", + "path": "命运", + "rarity": "稀有度", + "element": "元素", + "technique": "秘技", + "talent": "天赋", + "basic": "普通攻击", + "skill": "技能", + "ultimate": "终结技", + "servant": "忆灵", + "damage": "伤害", + "type": "类型", + "warrior": "毁灭", + "knight": "守护", + "mage": "博学", + "priest": "丰饶", + "rogue": "狩猎", + "shaman": "同谐", + "warlock": "虚无", + "memory": "记忆", + "elation": "欢愉", + "fire": "火", + "ice": "冰", + "imaginary": "虚数", + "physical": "物理", + "quantum": "量子", + "thunder": "雷", + "wind": "风", + "hp": "生命值", + "atk": "攻击", + "speed": "速度", + "critRate": "暴击率", + "critDmg": "暴击伤害", + "breakEffect": "击破伤害", + "effectRes": "效果抗性", + "energyRegenerationRate": "能量恢复速率", + "effectHitRate": "效果命中率", + "outgoingHealingBoost": "治疗增强", + "fireDmgBoost": "火元素伤害增强", + "iceDmgBoost": "冰元素伤害增强", + "imaginaryDmgBoost": "虚数伤害增强", + "physicalDmgBoost": "物理伤害增强", + "quantumDmgBoost": "量子伤害增强", + "thunderDmgBoost": "雷元素伤害增强", + "windDmgBoost": "风元素伤害增强", + "pursued": "附加伤害", + "true damage": "真实伤害", + "elationdamage": "欢愉伤害", + "follow-up": "后续伤害", + "elemental damage": "击破与超击破伤害", + "dot": "持续伤害", + "qte": "QTE 技能", + "level": "等级", + "relics": "遗器", + "eidolons": "星魂", + "lightcones": "光锥", + "loadData": "加载数据", + "exportData": "导出数据", + "connectSetting": "连接设置", + "connected": "已连接", + "unconnected": "未连接", + "psConnection": "PS 连接", + "connectionType": "连接类型", + "status": "状态", + "connectPs": "连接 PS", + "disconnect": "断开连接", + "other": "其他", + "freeSr": "FreeSR", + "database": "数据库", + "enka": "Enka", + "monsterSetting": "怪物设置", + "serverUrl": "服务器 URL", + "privateType": "私有类型", + "local": "本地", + "server": "服务器", + "username": "用户名", + "password": "密码", + "placeholderServerUrl": "输入服务器 URL", + "placeholderUsername": "输入用户名", + "placeholderPassword": "输入密码", + "connectedSuccess": "成功连接 PS", + "connectedFailed": "连接 PS 失败", + "syncSuccess": "已成功与 PS 同步数据", + "syncFailed": "与 PS 同步数据失败", + "sync": "同步", + "importSetting": "导入设置", + "profile": "配置档", + "default": "默认", + "copyProfiles": "复制配置档", + "addNewProfile": "添加新配置档", + "createNewProfile": "创建新配置档", + "editProfile": "编辑配置档", + "placeholderProfileName": "输入配置档名称", + "profileName": "配置档名称", + "create": "创建", + "update": "更新", + "characterInformation": "角色信息", + "skills": "技能", + "showcaseCard": "展示卡", + "comingSoon": "敬请期待", + "characterName": "角色名称", + "placeholderCharacter": "输入角色名称", + "characterSettings": "角色设置", + "levelConfiguration": "等级配置", + "characterLevel": "角色等级", + "max": "最大", + "ultimateEnergy": "终极能量", + "currentEnergy": "当前能量", + "setTo50": "设为50%", + "battleConfiguration": "战斗配置", + "useTechnique": "使用秘术", + "techniqueNote": "启用战前秘术效果", + "enhancement": "强化", + "enhancementLevel": "强化等级", + "origin": "来源", + "enhancedNote": "强化更高可解锁额外技能", + "lightconeEquipment": "光锥装备", + "lightconeSettings": "光锥设置", + "placeholderLevel": "输入等级", + "superimpositionRank": "叠加等级", + "ranksNote": "更高等级提供更强效果", + "changeLightcone": "更换光锥", + "removeLightcone": "移除光锥", + "equipLightcone": "装备光锥", + "noLightconeEquipped": "未装备光锥", + "equipLightconeNote": "装备光锥以增强角色能力", + "filter": "筛选", + "selectedCharacters": "已选角色", + "selectedProfiles": "已选配置档", + "clearAll": "清除全部", + "selectAll": "全选", + "copy": "复制", + "copied": "已复制", + "noAvatarSelected": "未选择角色", + "noAvatarToCopySelected": "未选择要复制的角色", + "pleaseSelectAtLeastOneProfile": "请至少选择一个配置档", + "pleaseEnterUid": "请输入 UID", + "failedToFetchEnkaData": "获取 Enka 数据失败", + "pleaseSelectAtLeastOneCharacter": "请至少选择一个角色", + "noDataToImport": "无可导入数据", + "pleaseSelectAFile": "请选择一个文件", + "fileMustBeAValidJsonFile": "文件必须为有效 JSON", + "importEnkaDataSuccess": "导入 Enka 数据成功", + "importFreeSRDataSuccess": "导入 FreeSR 数据成功", + "importDatabaseSuccess": "导入数据库成功", + "getData": "获取数据", + "import": "导入", + "freeSRImport": "导入 FreeSR", + "onlySupportFreeSRJsonFile": "仅支持 FreeSR JSON 文件", + "pickAFile": "选择文件", + "lightConeSetting": "光锥设置", + "relicMaker": "遗器制作", + "pleaseSelectAllOptions": "请选择所有选项", + "relicSavedSuccessfully": "遗器已成功保存", + "mainSettings": "主设置", + "mainStat": "主属性", + "set": "套装", + "pleaseSelectASet": "请选择一个套装", + "effectBonus": "效果加成", + "totalRoll": "总次数", + "randomizeStats": "随机属性", + "randomizeRolls": "随机次数", + "selectASubStat": "选择副属性", + "selectASet": "选择套装", + "selectAMainStat": "选择主属性", + "save": "保存", + "reset": "重置", + "roll": "滚动", + "step": "步数", + "memoryOfChaos": "混沌之忆", + "pureFiction": "虚构叙事", + "apocalypticShadow": "末日幻影", + "customEnemy": "自定义敌人", + "simulatedUniverse": "模拟宇宙", + "floor": "层数", + "side": "上/下半场", + "wave": "波次", + "stage": "关卡", + "useCycleCount": "使用轮次数?", + "useTurbulenceBuff": "使用紊乱增益?", + "firstHalfEnemies": "上半场敌人", + "secondHalfEnemies": "下半场敌人", + "turbulenceBuff": "紊乱增益", + "noEventSelected": "未选择事件", + "noTurbulenceBuff": "未选择紊乱增益", + "upper": "上半", + "lower": "下半", + "upperToLower": "上半 -> 下半", + "lowerToUpper": "下半 -> 上半", + "selectMOCEvent": "选择 MOC 事件", + "selectPFEvent": "选择 PF 事件", + "selectASEvent": "选择 AS 事件", + "selectCEEvent": "选择 CE 事件", + "selectEvent": "选择事件", + "selectFloor": "选择层数", + "selectSide": "选择上/下半场", + "selectBuff": "选择 buff", + "selectStage": "选择关卡", + "previous": "上一页", + "next": "下一页", + "noMonstersFound": "未找到怪物", + "addNewWave": "添加新波次", + "searchStage": "搜索关卡...", + "noStageFound": "未找到关卡", + "searchMonster": "搜索怪物...", + "changeRelic": "更换遗物", + "deleteRelic": "删除遗物", + "deleteRelicConfirm": "确定要删除插槽中的遗物吗", + "setEffects": "设置效果", + "details": "详情", + "normal": "普通攻击", + "bpskill": "技能", + "maze": "技巧", + "ultra": "终结技", + "servantskill": "记灵技能", + "severaltalent": "记灵天赋", + "singleattack": "单体攻击", + "enhance": "强化", + "summon": "召唤", + "blast": "爆裂", + "restore": "恢复", + "support": "支援", + "aoeattack": "范围攻击", + "mazeattack": "迷宫秘技", + "impair": "削弱", + "bounce": "弹跳", + "active": "活跃", + "defence": "防御", + "inactive": "不活跃", + "maxAll": "全部最大化", + "maxAllSuccess": "技能等级已成功设置为最大。", + "maxAllFailed": "设置技能等级为最大失败。", + "noRelicEquipped": "未装备圣遗物", + "anomalyArbitration": "异相", + "normalMode": "普通模式", + "hardMode": "困难模式", + "selectPEAKEvent": "选择 PEAK 事件", + "mode": "模式", + "selectMode": "选择模式", + "rollBack": "回到之前的状态", + "upRoll": "增加副属性", + "downRoll": "减少副属性", + "actions": "操作", + "avatars": "头像", + "quickView": "快速预览", + "extraSetting": "额外设置", + "disableCensorship": "禁用审查", + "hideUI": "隐藏界面", + "theoryCraftMode": "Theory Craft 模式", + "cycleCount": "循环次数", + "pleaseSelectAllSubStats": "请选取所有副属性", + "subStatRollCountCannotBeZero": "副属性的行数不能为0", + "theoryCraft": "Theory Craft", + "multipathCharacter": "多命途角色", + "mainPath": "主角命途", + "march7Path": "三月七命途", + "challenge": "挑战", + "skipNode": "跳过节点", + "disableSkip": "禁用跳过", + "skipNode1": "跳过节点1", + "skipNode2": "跳过节点2", + "extraFeatures": "附加功能", + "detailTheoryCraft": "开启后可自定义循环数,并在敌人设置中调整生命值。", + "detailSkipNode": "开启后可跳过混沌回忆或虚构叙事的(节点1/节点2)。", + "detailChallengePeak": "允许更改当前异相中的「巅峰」赛季。", + "detailHiddenUi": "开启后将隐藏游戏界面。", + "detailDisableCensorship": "开启后将关闭游戏内的审查。", + "detailMultipathCharacter": "允许更改部分角色的命途。", + "trailblazer": "开拓者", + "listExtraEffect": "额外效果列表", + "extra": "额外", + "customLineup": "自定义阵容" + } +} \ No newline at end of file diff --git a/next.config.ts b/next.config.ts new file mode 100644 index 0000000..63881a2 --- /dev/null +++ b/next.config.ts @@ -0,0 +1,42 @@ +import createNextIntlPlugin from "next-intl/plugin"; +import type { NextConfig } from "next"; +import bundleAnalyzer from "@next/bundle-analyzer"; + +const withBundleAnalyzer = bundleAnalyzer({ + enabled: process.env.ANALYZE === "true", +}); + +const withNextIntl = createNextIntlPlugin(); + +const nextConfig: NextConfig = { + reactStrictMode: false, + output: "standalone", + images: { + unoptimized: true, + remotePatterns: [ + { + protocol: "http", + hostname: "localhost", + pathname: "**", + }, + { + protocol: "https", + hostname: "localhost", + pathname: "**", + }, + { + protocol: "https", + hostname: "cdn.punklorde.org", + pathname: "**", + } + ], + }, + compiler: { + styledComponents: true, + }, + env: { + CDN_URL: "https://cdn.punklorde.org/asbres", + }, +}; + +export default withBundleAnalyzer(withNextIntl(nextConfig)); diff --git a/package.json b/package.json new file mode 100644 index 0000000..dfcd7d4 --- /dev/null +++ b/package.json @@ -0,0 +1,51 @@ +{ + "name": "firefly-tools", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "eslint ." + }, + "dependencies": { + "@next/bundle-analyzer": "16.1.6", + "@tanstack/react-query": "^5.90.20", + "axios": "^1.13.4", + "fast-average-color": "^9.5.0", + "framer-motion": "^12.29.2", + "html2canvas-pro": "^1.6.6", + "lru-cache": "^11.2.5", + "lucide-react": "^0.563.0", + "next": "16.1.6", + "next-intl": "^4.8.2", + "prismjs": "^1.30.0", + "react": "19.2.4", + "react-dom": "19.2.4", + "react-select": "^5.10.2", + "react-simple-code-editor": "^0.14.1", + "react-toastify": "^11.0.5", + "sharp": "^0.34.5", + "zod": "^4.3.6", + "zustand": "^5.0.11" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.1.18", + "@types/jest": "^30.0.0", + "@types/node": "^25.1.0", + "@types/react": "19.2.6", + "@types/react-dom": "19.2.3", + "baseline-browser-mapping": "^2.9.19", + "daisyui": "^5.5.14", + "eslint": "^9.39.2", + "eslint-config-next": "16.1.6", + "tailwind-scrollbar": "^4.0.2", + "tailwindcss": "^4.1.18", + "ts-to-zod": "^5.1.0", + "typescript": "^5.9.3" + }, + "overrides": { + "@types/react": "19.2.6", + "@types/react-dom": "19.2.3" + } +} diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..c7bcb4b --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,5 @@ +const config = { + plugins: ["@tailwindcss/postcss"], +}; + +export default config; diff --git a/public/ff-srtool.ico b/public/ff-srtool.ico new file mode 100644 index 0000000..0bc4acc Binary files /dev/null and b/public/ff-srtool.ico differ diff --git a/public/ff-srtool.png b/public/ff-srtool.png new file mode 100644 index 0000000..d39791a Binary files /dev/null and b/public/ff-srtool.png differ diff --git a/public/icon/AbyssIcon01.webp b/public/icon/AbyssIcon01.webp new file mode 100644 index 0000000..fbd5f33 Binary files /dev/null and b/public/icon/AbyssIcon01.webp differ diff --git a/public/icon/AbyssIcon02.webp b/public/icon/AbyssIcon02.webp new file mode 100644 index 0000000..631f91b Binary files /dev/null and b/public/icon/AbyssIcon02.webp differ diff --git a/public/icon/ChallengeBoss.webp b/public/icon/ChallengeBoss.webp new file mode 100644 index 0000000..4c66d59 Binary files /dev/null and b/public/icon/ChallengeBoss.webp differ diff --git a/public/icon/ChallengePeakIcon.webp b/public/icon/ChallengePeakIcon.webp new file mode 100644 index 0000000..da86ce0 Binary files /dev/null and b/public/icon/ChallengePeakIcon.webp differ diff --git a/public/icon/ChallengeStory.webp b/public/icon/ChallengeStory.webp new file mode 100644 index 0000000..6fa386f Binary files /dev/null and b/public/icon/ChallengeStory.webp differ diff --git a/public/icon/IconJoy.webp b/public/icon/IconJoy.webp new file mode 100644 index 0000000..6eab955 Binary files /dev/null and b/public/icon/IconJoy.webp differ diff --git a/public/icon/MonsterIcon.webp b/public/icon/MonsterIcon.webp new file mode 100644 index 0000000..9bd3c89 Binary files /dev/null and b/public/icon/MonsterIcon.webp differ diff --git a/public/icon/SimulatedUniverse.webp b/public/icon/SimulatedUniverse.webp new file mode 100644 index 0000000..61db653 Binary files /dev/null and b/public/icon/SimulatedUniverse.webp differ diff --git a/public/icon/attack.webp b/public/icon/attack.webp new file mode 100644 index 0000000..73328b2 Binary files /dev/null and b/public/icon/attack.webp differ diff --git a/public/icon/break-effect.webp b/public/icon/break-effect.webp new file mode 100644 index 0000000..5e260e2 Binary files /dev/null and b/public/icon/break-effect.webp differ diff --git a/public/icon/crit-damage.webp b/public/icon/crit-damage.webp new file mode 100644 index 0000000..7d5156f Binary files /dev/null and b/public/icon/crit-damage.webp differ diff --git a/public/icon/crit-rate.webp b/public/icon/crit-rate.webp new file mode 100644 index 0000000..02a30a0 Binary files /dev/null and b/public/icon/crit-rate.webp differ diff --git a/public/icon/defence.webp b/public/icon/defence.webp new file mode 100644 index 0000000..e17bad1 Binary files /dev/null and b/public/icon/defence.webp differ diff --git a/public/icon/effect-hit-rate.webp b/public/icon/effect-hit-rate.webp new file mode 100644 index 0000000..ae71045 Binary files /dev/null and b/public/icon/effect-hit-rate.webp differ diff --git a/public/icon/effect-res.webp b/public/icon/effect-res.webp new file mode 100644 index 0000000..95599b7 Binary files /dev/null and b/public/icon/effect-res.webp differ diff --git a/public/icon/elation.webp b/public/icon/elation.webp new file mode 100644 index 0000000..1ec9a9b Binary files /dev/null and b/public/icon/elation.webp differ diff --git a/public/icon/energy-rate.webp b/public/icon/energy-rate.webp new file mode 100644 index 0000000..e911bee Binary files /dev/null and b/public/icon/energy-rate.webp differ diff --git a/public/icon/fire-add.webp b/public/icon/fire-add.webp new file mode 100644 index 0000000..15f809a Binary files /dev/null and b/public/icon/fire-add.webp differ diff --git a/public/icon/fire.webp b/public/icon/fire.webp new file mode 100644 index 0000000..ad5b7f7 Binary files /dev/null and b/public/icon/fire.webp differ diff --git a/public/icon/healing-boost.webp b/public/icon/healing-boost.webp new file mode 100644 index 0000000..904094a Binary files /dev/null and b/public/icon/healing-boost.webp differ diff --git a/public/icon/hp.webp b/public/icon/hp.webp new file mode 100644 index 0000000..24060d1 Binary files /dev/null and b/public/icon/hp.webp differ diff --git a/public/icon/ice-add.webp b/public/icon/ice-add.webp new file mode 100644 index 0000000..c570ad5 Binary files /dev/null and b/public/icon/ice-add.webp differ diff --git a/public/icon/ice.webp b/public/icon/ice.webp new file mode 100644 index 0000000..37dadaf Binary files /dev/null and b/public/icon/ice.webp differ diff --git a/public/icon/imaginary-add.webp b/public/icon/imaginary-add.webp new file mode 100644 index 0000000..7d22081 Binary files /dev/null and b/public/icon/imaginary-add.webp differ diff --git a/public/icon/imaginary.webp b/public/icon/imaginary.webp new file mode 100644 index 0000000..581b72e Binary files /dev/null and b/public/icon/imaginary.webp differ diff --git a/public/icon/knight.webp b/public/icon/knight.webp new file mode 100644 index 0000000..c0c45a9 Binary files /dev/null and b/public/icon/knight.webp differ diff --git a/public/icon/mage.webp b/public/icon/mage.webp new file mode 100644 index 0000000..e9cc2a2 Binary files /dev/null and b/public/icon/mage.webp differ diff --git a/public/icon/memory.webp b/public/icon/memory.webp new file mode 100644 index 0000000..0e81907 Binary files /dev/null and b/public/icon/memory.webp differ diff --git a/public/icon/physical-add.webp b/public/icon/physical-add.webp new file mode 100644 index 0000000..c976bd9 Binary files /dev/null and b/public/icon/physical-add.webp differ diff --git a/public/icon/physical.webp b/public/icon/physical.webp new file mode 100644 index 0000000..a3a7c34 Binary files /dev/null and b/public/icon/physical.webp differ diff --git a/public/icon/priest.webp b/public/icon/priest.webp new file mode 100644 index 0000000..46f71fc Binary files /dev/null and b/public/icon/priest.webp differ diff --git a/public/icon/quantum-add.webp b/public/icon/quantum-add.webp new file mode 100644 index 0000000..3fef60d Binary files /dev/null and b/public/icon/quantum-add.webp differ diff --git a/public/icon/quantum.webp b/public/icon/quantum.webp new file mode 100644 index 0000000..0a4a3dd Binary files /dev/null and b/public/icon/quantum.webp differ diff --git a/public/icon/rogue.webp b/public/icon/rogue.webp new file mode 100644 index 0000000..a01b5ec Binary files /dev/null and b/public/icon/rogue.webp differ diff --git a/public/icon/shaman.webp b/public/icon/shaman.webp new file mode 100644 index 0000000..a5f4164 Binary files /dev/null and b/public/icon/shaman.webp differ diff --git a/public/icon/speed.webp b/public/icon/speed.webp new file mode 100644 index 0000000..e342cf6 Binary files /dev/null and b/public/icon/speed.webp differ diff --git a/public/icon/thunder-add.webp b/public/icon/thunder-add.webp new file mode 100644 index 0000000..ef66dbd Binary files /dev/null and b/public/icon/thunder-add.webp differ diff --git a/public/icon/thunder.webp b/public/icon/thunder.webp new file mode 100644 index 0000000..95d76fa Binary files /dev/null and b/public/icon/thunder.webp differ diff --git a/public/icon/warlock.webp b/public/icon/warlock.webp new file mode 100644 index 0000000..34148c6 Binary files /dev/null and b/public/icon/warlock.webp differ diff --git a/public/icon/warrior.webp b/public/icon/warrior.webp new file mode 100644 index 0000000..5bd3476 Binary files /dev/null and b/public/icon/warrior.webp differ diff --git a/public/icon/wind-add.webp b/public/icon/wind-add.webp new file mode 100644 index 0000000..06e47c2 Binary files /dev/null and b/public/icon/wind-add.webp differ diff --git a/public/icon/wind.webp b/public/icon/wind.webp new file mode 100644 index 0000000..138cc78 Binary files /dev/null and b/public/icon/wind.webp differ diff --git a/public/relics/BODY.png b/public/relics/BODY.png new file mode 100644 index 0000000..fc892f1 Binary files /dev/null and b/public/relics/BODY.png differ diff --git a/public/relics/FOOT.png b/public/relics/FOOT.png new file mode 100644 index 0000000..e287193 Binary files /dev/null and b/public/relics/FOOT.png differ diff --git a/public/relics/HAND.png b/public/relics/HAND.png new file mode 100644 index 0000000..04a3016 Binary files /dev/null and b/public/relics/HAND.png differ diff --git a/public/relics/HEAD.png b/public/relics/HEAD.png new file mode 100644 index 0000000..f4c94c3 Binary files /dev/null and b/public/relics/HEAD.png differ diff --git a/public/relics/NECK.png b/public/relics/NECK.png new file mode 100644 index 0000000..cfbd424 Binary files /dev/null and b/public/relics/NECK.png differ diff --git a/public/relics/OBJECT.png b/public/relics/OBJECT.png new file mode 100644 index 0000000..e07ac8d Binary files /dev/null and b/public/relics/OBJECT.png differ diff --git a/public/skilltree/ELATION.webp b/public/skilltree/ELATION.webp new file mode 100644 index 0000000..6480121 Binary files /dev/null and b/public/skilltree/ELATION.webp differ diff --git a/public/skilltree/KNIGHT.webp b/public/skilltree/KNIGHT.webp new file mode 100644 index 0000000..402e6ce Binary files /dev/null and b/public/skilltree/KNIGHT.webp differ diff --git a/public/skilltree/MAGE.webp b/public/skilltree/MAGE.webp new file mode 100644 index 0000000..8ba48e0 Binary files /dev/null and b/public/skilltree/MAGE.webp differ diff --git a/public/skilltree/MEMORY.webp b/public/skilltree/MEMORY.webp new file mode 100644 index 0000000..ad41bdd Binary files /dev/null and b/public/skilltree/MEMORY.webp differ diff --git a/public/skilltree/PRIEST.webp b/public/skilltree/PRIEST.webp new file mode 100644 index 0000000..b5b707a Binary files /dev/null and b/public/skilltree/PRIEST.webp differ diff --git a/public/skilltree/ROGUE.webp b/public/skilltree/ROGUE.webp new file mode 100644 index 0000000..10a1eb8 Binary files /dev/null and b/public/skilltree/ROGUE.webp differ diff --git a/public/skilltree/SHAMAN.webp b/public/skilltree/SHAMAN.webp new file mode 100644 index 0000000..c883315 Binary files /dev/null and b/public/skilltree/SHAMAN.webp differ diff --git a/public/skilltree/WARLOCK.webp b/public/skilltree/WARLOCK.webp new file mode 100644 index 0000000..488d1d9 Binary files /dev/null and b/public/skilltree/WARLOCK.webp differ diff --git a/public/skilltree/WARRIOR.webp b/public/skilltree/WARRIOR.webp new file mode 100644 index 0000000..532d2f1 Binary files /dev/null and b/public/skilltree/WARRIOR.webp differ diff --git a/script/auto-gen.bat b/script/auto-gen.bat new file mode 100644 index 0000000..4816957 --- /dev/null +++ b/script/auto-gen.bat @@ -0,0 +1,13 @@ +@echo off +echo [INFO] Create folder src\zod if not exist... +if not exist src\zod ( + mkdir src\zod +) + +echo [INFO] Start convert file .ts from src\types to Zod schemas... +for %%f in (src\types\*.ts) do ( + echo [ZOD] Chuyển %%f -> src\zod\%%~nf.zod.ts + npx ts-to-zod src\types\%%~nxf src\zod\%%~nf.zod.ts +) + +echo [DONE] ✅ Done diff --git a/src/app/api/data/[name]/route.ts b/src/app/api/data/[name]/route.ts new file mode 100644 index 0000000..c8b7583 --- /dev/null +++ b/src/app/api/data/[name]/route.ts @@ -0,0 +1,25 @@ +import { getDataCache } from "@/lib/cache/cache" + +export async function GET( + req: Request, + { params }: { params: Promise<{ name: string }> } +) { + const { name } = await params + + const item = getDataCache(name) + + if (!item) { + return new Response("Not found", { status: 404 }) + } + + const headers: Record = { + "Content-Type": "application/json", + "Cache-Control": "public, max-age=3600" + } + + if (item.type === "br") { + headers["Content-Encoding"] = "br" + } + + return new Response(new Uint8Array(item.buf), { headers }) +} \ No newline at end of file diff --git a/src/app/api/proxy/route.ts b/src/app/api/proxy/route.ts new file mode 100644 index 0000000..585fe7d --- /dev/null +++ b/src/app/api/proxy/route.ts @@ -0,0 +1,102 @@ +import { NextRequest, NextResponse } from 'next/server' +import axios from 'axios' +import net from 'net' + +function isPrivateHost(hostname: string): boolean { + if ( + hostname === 'localhost' || + hostname.startsWith('127.') || + hostname.startsWith('10.') || + hostname.startsWith('192.168.') || + /^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(hostname) + ) { + return true + } + + if (net.isIP(hostname)) { + return true + } + + return false +} + +export async function GET(req: NextRequest) { + try { + const { searchParams } = new URL(req.url) + const targetUrl = searchParams.get("url") + if (!targetUrl) { + return NextResponse.json({ error: "Missing url" }, { status: 400 }) + } + + const response = await fetch(targetUrl) + + if (!response.ok) { + return NextResponse.json({ error: "Failed to fetch image" }, { status: 500 }) + } + + const buffer = await response.arrayBuffer() + + return new NextResponse(buffer, { + headers: { + "Content-Type": response.headers.get("content-type") || "image/png", + "Cache-Control": "public, max-age=3600", + "Access-Control-Allow-Origin": "*", + }, + }) + } catch (e: unknown) { + return NextResponse.json({ error: (e as Error)?.message }, { status: 500 }) + } +} + + +export async function POST(request: NextRequest) { + try { + const body = await request.json() + const { serverUrl, method, ...payload } = body + + if (!serverUrl) { + return NextResponse.json({ error: 'Missing serverUrl' }, { status: 400 }) + } + if (!method) { + return NextResponse.json({ error: 'Missing method' }, { status: 400 }) + } + + let url = serverUrl.trim() + if (!url.startsWith('http://') && !url.startsWith('https://')) { + url = `http://${url}` + } + + const parsed = new URL(url) + if (isPrivateHost(parsed.hostname)) { + return NextResponse.json( + { error: `Connection to private/internal address (${parsed.hostname}) is not allowed` }, + { status: 403 } + ) + } + + let response + + switch (method.toUpperCase()) { + case 'GET': + const queryString = new URLSearchParams(payload as Record).toString() + const fullUrl = queryString ? `${url}?${queryString}` : url + response = await axios.get(fullUrl) + break + case 'POST': + response = await axios.post(url, payload) + break + case 'PUT': + response = await axios.put(url, payload) + break + case 'DELETE': + response = await axios.delete(url, { data: payload }) + break + default: + return NextResponse.json({ error: `Unsupported method: ${method}` }, { status: 405 }) + } + + return NextResponse.json(response.data) + } catch (err: unknown) { + return NextResponse.json({ error: (err as Error)?.message || 'Proxy failed' }, { status: 500 }) + } +} diff --git a/src/app/eidolons-info/page.tsx b/src/app/eidolons-info/page.tsx new file mode 100644 index 0000000..0b6e428 --- /dev/null +++ b/src/app/eidolons-info/page.tsx @@ -0,0 +1,9 @@ +"use client" +import EidolonsInfo from "@/components/eidolonsInfo"; +export default function EidolonsInfoPage() { + return ( +
+ +
+ ); +} diff --git a/src/app/globals.css b/src/app/globals.css new file mode 100644 index 0000000..e8aa33e --- /dev/null +++ b/src/app/globals.css @@ -0,0 +1,38 @@ +@import "tailwindcss"; + + +@plugin "daisyui" { + exclude: properties; + themes: winter --default, night --prefersdark, cupcake, coffee; +} + +@tailwind utilities; + +@plugin 'tailwind-scrollbar' { + nocompatible: true; +} + + +:root { + --size-big: 4vw; + --size-medium: 3vw; + --size-small: 2vw; +} + +::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background-color: #9ca3af; + border-radius: 0; +} + +::-webkit-scrollbar-button { + display: none; +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..7afc695 --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,102 @@ +import type { Metadata } from "next"; +import { Geist, Geist_Mono } from "next/font/google"; +import "./globals.css"; +import Header from "../components/header"; +import { ClientThemeWrapper, ThemeProvider } from "@/components/themeController"; +import Footer from "@/components/footer"; +import { NextIntlClientProvider } from "next-intl"; +import { getLocale, getMessages } from "next-intl/server"; +import { ToastContainer } from 'react-toastify'; +import QueryProviderWrapper from "@/components/queryProvider"; +import ClientDataFetcher from "@/components/clientDataFetcher"; +import AvatarBar from "@/components/avatarBar"; +import ActionBar from "@/components/actionBar"; + + +const geistSans = Geist({ + variable: "--font-geist-sans", + subsets: ["latin"], +}); + +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], +}); + +export const metadata: Metadata = { + title: "Firefly SrTools", + description: "SrTools by Firefly Shelter", + icons: { + icon: "/ff-srtool.png", + shortcut: "/ff-srtool.ico", + apple: "/ff-srtool.png", + }, + openGraph: { + title: "Firefly SrTools", + description: "SrTools by Firefly Shelter", + url: "https://srtools.punklorde.org", + siteName: "Firefly SrTools", + images: [ + { + url: "https://srtools.punklorde.org/ff-srtool.png", + width: 312, + height: 312, + alt: "Firefly SrTools Logo", + }, + ], + locale: "en_US", + type: "website", + }, + twitter: { + card: "summary_large_image", + title: "Firefly SrTools", + description: "SrTools by Firefly Shelter", + images: ["https://srtools.punklorde.org/ff-srtool.png"], + }, +}; + +export default async function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + const messages = await getMessages(); + const locale = await getLocale() + + return ( + + + + + + + +
+
+
+
+ +
+
+ + {children} +
+
+ +
+
+
+
+
+
+
+ + + + + + ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx new file mode 100644 index 0000000..d3ddec8 --- /dev/null +++ b/src/app/page.tsx @@ -0,0 +1,10 @@ +"use client" +import AvatarInfo from "@/components/avatarInfo"; + +export default function Home() { + return ( +
+ +
+ ); +} diff --git a/src/app/relics-info/page.tsx b/src/app/relics-info/page.tsx new file mode 100644 index 0000000..29b63e5 --- /dev/null +++ b/src/app/relics-info/page.tsx @@ -0,0 +1,11 @@ +"use client"; + +import RelicsInfo from "@/components/relicsInfo"; + +export default function RelicsInfoPage() { + return ( +
+ +
+ ); +} diff --git a/src/app/showcase-card/page.tsx b/src/app/showcase-card/page.tsx new file mode 100644 index 0000000..06cb75f --- /dev/null +++ b/src/app/showcase-card/page.tsx @@ -0,0 +1,11 @@ +"use client" + +import ShowCaseInfo from "@/components/showcaseCard" + +export default function ShowcaseCard() { + return ( +
+ +
+ ) +} diff --git a/src/app/skills-info/page.tsx b/src/app/skills-info/page.tsx new file mode 100644 index 0000000..fbcb679 --- /dev/null +++ b/src/app/skills-info/page.tsx @@ -0,0 +1,11 @@ +"use client"; + +import SkillsInfo from "@/components/skillsInfo"; + +export default function SkillsInfoPage() { + return ( +
+ +
+ ); +} diff --git a/src/components/actionBar/index.tsx b/src/components/actionBar/index.tsx new file mode 100644 index 0000000..d28c9a3 --- /dev/null +++ b/src/components/actionBar/index.tsx @@ -0,0 +1,445 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +"use client"; + +import { useTranslations } from "next-intl"; +import Image from "next/image"; +import { useRouter } from 'next/navigation' +import ParseText from "../parseText"; +import useLocaleStore from "@/stores/localeStore"; +import { getNameChar } from "@/helper/getName"; +import { useEffect, useMemo, useState } from "react"; +import { motion } from "framer-motion"; +import useModelStore from "@/stores/modelStore"; +import useUserDataStore from "@/stores/userDataStore"; +import { ModalConfig, RelicStore } from "@/types"; +import { toast } from "react-toastify"; +import useGlobalStore from "@/stores/globalStore"; +import { connectToPS, syncDataToPS } from "@/helper"; +import CopyImport from "../importBar/copy"; +import useCopyProfileStore from "@/stores/copyProfile"; +import AvatarBar from "../avatarBar"; +import useCurrentDataStore from "@/stores/currentDataStore"; +import useDetailDataStore from "@/stores/detailDataStore"; + +export default function ActionBar() { + const router = useRouter() + const { avatarSelected } = useCurrentDataStore() + const { damageType, baseType } = useDetailDataStore() + const { setResetData } = useCopyProfileStore() + const transI18n = useTranslations("DataPage") + const { locale } = useLocaleStore() + const { + isOpenCreateProfile, + setIsOpenCreateProfile, + isOpenCopy, + setIsOpenCopy, + isOpenAvatars, + setIsOpenAvatars + } = useModelStore() + const { avatars, setAvatar } = useUserDataStore() + const [profileName, setProfileName] = useState(""); + const [formState, setFormState] = useState("EDIT"); + const [profileEdit, setProfileEdit] = useState(-1); + const { isConnectPS } = useGlobalStore() + + const profileCurrent = useMemo(() => { + if (!avatarSelected) return null; + const avatar = avatars[avatarSelected.ID]; + return avatar?.profileList[avatar.profileSelect] || null; + }, [avatarSelected, avatars]); + + const listProfile = useMemo(() => { + if (!avatarSelected) return []; + const avatar = avatars[avatarSelected.ID]; + return avatar?.profileList || []; + }, [avatarSelected, avatars]); + + + const handleUpdateProfile = () => { + if (!profileName.trim()) return; + if (formState === "CREATE" && avatarSelected && avatars[avatarSelected.ID]) { + const newListProfile = [...listProfile] + const newProfile = { + profile_name: profileName, + lightcone: null, + relics: {} as Record + } + newListProfile.push(newProfile) + setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile, profileSelect: newListProfile.length - 1 }) + toast.success("Profile created successfully") + } else if (formState === "EDIT" && profileCurrent && avatarSelected && profileEdit !== -1) { + const newListProfile = [...listProfile] + newListProfile[profileEdit].profile_name = profileName; + setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile }) + toast.success("Profile updated successfully") + } + handleCloseModal("update_profile_modal"); + }; + + + const handleShow = (modalId: string) => { + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + modal.showModal(); + } + }; + + // Close modal handler + const handleCloseModal = (modalId: string) => { + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + modal.close(); + } + }; + + + const actionMove = (path: string) => { + router.push(`/${path}`) + } + + const handleProfileSelect = (profileId: number) => { + if (!avatarSelected) return; + if (avatars[avatarSelected.ID].profileSelect === profileId) return; + setAvatar({ ...avatars[avatarSelected.ID], profileSelect: profileId }) + toast.success(`Profile changed to Profile: ${avatars[avatarSelected.ID].profileList[profileId].profile_name}`) + } + + const handleDeleteProfile = (profileId: number, e: React.MouseEvent) => { + e.stopPropagation() + if (!avatarSelected || profileId == 0) return; + if (window.confirm(`Are you sure you want to delete profile: ${avatars[avatarSelected.ID].profileList[profileId].profile_name}?`)) { + const newListProfile = [...listProfile] + newListProfile.splice(profileId, 1) + setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile, profileSelect: profileId - 1 }) + toast.success(`Profile ${avatars[avatarSelected.ID].profileList[profileId].profile_name} deleted successfully`) + } + + } + + const handleConnectOrSyncPS = async () => { + if (isConnectPS) { + const res = await syncDataToPS() + if (res.success) { + toast.success(transI18n("syncSuccess")) + } else { + toast.error(`${transI18n("syncFailed")}: ${res.message}`) + } + } else { + const res = await connectToPS() + if (res.success) { + toast.success(transI18n("connectedSuccess")) + } else { + toast.error(`${transI18n("connectedFailed")}: ${res.message}`) + } + } + } + + const modalConfigs: ModalConfig[] = [ + { + id: "update_profile_modal", + title: formState === "CREATE" ? transI18n("createNewProfile") : transI18n("editProfile"), + isOpen: isOpenCreateProfile, + onClose: () => { + setIsOpenCreateProfile(false) + handleCloseModal("update_profile_modal") + }, + content: ( +
+
+ + setProfileName(e.target.value)} + /> +
+
+ +
+
+ ) + }, + { + id: "copy_profile_modal", + title: transI18n("copyProfiles").toUpperCase(), + isOpen: isOpenCopy, + onClose: () => { + setIsOpenCopy(false) + handleCloseModal("copy_profile_modal") + }, + content: + }, + { + id: "avatars_modal", + title: transI18n("avatars").toUpperCase(), + isOpen: isOpenAvatars, + onClose: () => { + setIsOpenAvatars(false) + handleCloseModal("avatars_modal") + }, + content: { setIsOpenAvatars(false); handleCloseModal("avatars_modal") }} /> + } + ] + + // Handle ESC key to close modal + useEffect(() => { + for (const item of modalConfigs) { + if (!item?.isOpen) { + handleCloseModal(item?.id || "") + } + } + const handleEscKey = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + for (const item of modalConfigs) { + handleCloseModal(item?.id || "") + } + } + }; + + window.addEventListener('keydown', handleEscKey); + return () => window.removeEventListener('keydown', handleEscKey); + }, [isOpenCopy, isOpenCreateProfile, isOpenAvatars]); + + + + return ( +
+
+ +
+
+
+ {avatarSelected && ( +
+ {'damage +

+ {transI18n(avatarSelected.BaseType.toLowerCase())} +

+
{" / "}
+ +
{`(${avatarSelected.ID})`}
+
+ )} +
+ +
+ +
+ {transI18n("profile")}: +
+
+ + {profileCurrent?.profile_name} + + + + +
+ +
    + {listProfile.map((profile, index) => ( +
  • + + {index !== 0 && ( + <> + + + + )} +
  • + ))} + +
  • + + +
  • +
+
+
+ +
+ +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+ +
+
+ +
+ + + + + + +
+ + {modalConfigs.map(({ id, title, onClose, content }) => ( + +
+
+ + ✕ + +
+
+

+ {title} +

+
+ {content} +
+
+ ))} + + +
+
+ ); +} \ No newline at end of file diff --git a/src/components/avatarBar/index.tsx b/src/components/avatarBar/index.tsx new file mode 100644 index 0000000..550c6c9 --- /dev/null +++ b/src/components/avatarBar/index.tsx @@ -0,0 +1,140 @@ +"use client" +import Image from "next/image" +import { useMemo } from "react" +import CharacterCard from "../card/characterCard" +import useLocaleStore from "@/stores/localeStore" +import { useTranslations } from "next-intl" +import useDetailDataStore from "@/stores/detailDataStore" +import useCurrentDataStore from '@/stores/currentDataStore'; +import { calcRarity, getNameChar } from "@/helper" + +export default function AvatarBar({ onClose }: { onClose?: () => void }) { + const { + avatarSearch, + mapAvatarElementActive, + mapAvatarPathActive, + setAvatarSearch, + setAvatarSelected, + setMapAvatarElementActive, + setMapAvatarPathActive, + setSkillIDSelected, + } = useCurrentDataStore() + const { mapAvatar, baseType, damageType } = useDetailDataStore() + const transI18n = useTranslations("DataPage") + const {locale} = useLocaleStore() + + const listAvatar = useMemo(() => { + if (!mapAvatar || !locale || !transI18n) return [] + + let list = Object.values(mapAvatar) + + if (avatarSearch) { + list = list.filter(item => + getNameChar(locale, transI18n, item) + .toLowerCase() + .includes(avatarSearch.toLowerCase()) + ) + } + + const allElementFalse = !Object.values(mapAvatarElementActive).some(v => v) + const allPathFalse = !Object.values(mapAvatarPathActive).some(v => v) + + list = list.filter(item => + (allElementFalse || mapAvatarElementActive[item.DamageType]) && + (allPathFalse || mapAvatarPathActive[item.BaseType]) + ) + + list.sort((a, b) => { + const r = calcRarity(b.Rarity) - calcRarity(a.Rarity) + if (r !== 0) return r + return b.ID - a.ID + }) + + return list + }, [mapAvatar, mapAvatarElementActive, mapAvatarPathActive, avatarSearch, locale, transI18n]) + + + return ( +
+
+
+
+
+
+ setAvatarSearch(e.target.value)} + /> +
+
+ {Object.entries(damageType).filter(([key]) => key !== "").map(([key, value]) => ( +
{ + setMapAvatarElementActive({ ...mapAvatarElementActive, [key]: !mapAvatarElementActive[key] }) + }} + className="hover:bg-gray-600 grid items-center justify-items-center cursor-pointer rounded-md shadow-lg" + style={{ + backgroundColor: mapAvatarElementActive[key] ? "#374151" : "#6B7280" + }}> + {key} +
+ ))} +
+ +
+ {Object.entries(baseType).filter(([key]) => key !== "").map(([key, value]) => ( +
{ + setMapAvatarPathActive({ ...mapAvatarPathActive, [key]: !mapAvatarPathActive[key] }) + }} + className="hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-lg cursor-pointer" + style={{ + backgroundColor: mapAvatarPathActive[key] ? "#374151" : "#6B7280" + }} + > + + {key} +
+ ))} + +
+
+ +
+
    + {listAvatar.map((item, index) => ( +
    { + setAvatarSelected(item); + setSkillIDSelected(null) + if (onClose) onClose() + }}> + +
    + ))} +
+
+
+ +
+
+
+ ) +} diff --git a/src/components/avatarInfo/index.tsx b/src/components/avatarInfo/index.tsx new file mode 100644 index 0000000..c6b3571 --- /dev/null +++ b/src/components/avatarInfo/index.tsx @@ -0,0 +1,529 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +"use client" + +import useUserDataStore from "@/stores/userDataStore"; +import { useEffect, useMemo } from "react"; +import { motion } from "framer-motion"; +import LightconeBar from '../lightconeBar' +import { calcPromotion, calcRarity, replaceByParam } from '@/helper'; +import { getSkillTree } from '@/helper/getSkillTree'; +import { useTranslations } from 'next-intl'; +import ParseText from '../parseText'; +import useLocaleStore from '@/stores/localeStore'; +import useModelStore from '@/stores/modelStore'; +import Image from 'next/image'; +import useCurrentDataStore from "@/stores/currentDataStore"; +import useDetailDataStore from "@/stores/detailDataStore"; +import { getLocaleName } from "@/helper/getName"; +export default function AvatarInfo() { + const { avatarSelected, setResetDataLightcone } = useCurrentDataStore() + const { avatars, setAvatars, setAvatar } = useUserDataStore() + const { isOpenLightcone, setIsOpenLightcone } = useModelStore() + const { mapLightCone, baseType } = useDetailDataStore() + const transI18n = useTranslations("DataPage") + const { locale } = useLocaleStore(); + + const lightcone = useMemo(() => { + if (!avatarSelected) return null; + const avatar = avatars[avatarSelected.ID]; + return avatar?.profileList[avatar.profileSelect]?.lightcone || null; + }, [avatarSelected, avatars]); + + const lightconeDetail = useMemo(() => { + if (!mapLightCone || !lightcone?.item_id) return null; + return mapLightCone?.[lightcone.item_id.toString()] || null; + }, [lightcone, mapLightCone]); + + const handleShow = (modalId: string) => { + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + setIsOpenLightcone(true); + modal.showModal(); + } + }; + + // Close modal handler + const handleCloseModal = (modalId: string) => { + setIsOpenLightcone(false); + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + modal.close(); + } + }; + + // Handle ESC key to close modal + useEffect(() => { + if (!isOpenLightcone) { + handleCloseModal("action_detail_modal"); + return; + } + + const handleEscKey = (event: KeyboardEvent) => { + if (event.key === 'Escape' && isOpenLightcone) { + handleCloseModal("action_detail_modal"); + } + }; + + window.addEventListener('keydown', handleEscKey); + + return () => window.removeEventListener('keydown', handleEscKey); + }, [isOpenLightcone]); + + return ( +
+ {avatarSelected && avatars[avatarSelected?.ID.toString() || ""] && ( +
+
+
+
+
+ {/* Header */} +
+

+
+ {transI18n("characterSettings")} +

+
+ +
+ {/* Level Control */} +
+

+
+ {transI18n("levelConfiguration")} +

+ +
+ +
+ { + const newLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1)); + + setAvatars({ ...avatars, [avatarSelected?.ID.toString() || ""]: { ...avatars[avatarSelected?.ID.toString() || ""], level: newLevel, promotion: calcPromotion(newLevel) } }); + }} + className="input input-bordered w-full pr-16 font-mono" + placeholder={transI18n("placeholderLevel")} + /> +
{ + setAvatars({ ...avatars, [avatarSelected?.ID.toString() || ""]: { ...avatars[avatarSelected?.ID.toString() || ""], level: 80, promotion: calcPromotion(80) } }); + }} + className="absolute right-3 top-1/2 -translate-y-1/2 text-base-content/60 cursor-pointer"> + {transI18n("max")} +
+
+
+ { + const newLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1)); + setAvatars({ ...avatars, [avatarSelected?.ID.toString() || ""]: { ...avatars[avatarSelected?.ID.toString() || ""], level: newLevel, promotion: calcPromotion(newLevel) } }); + }} + className="range range-info range-sm w-full" + /> +
+
+
+ + {/* Energy Control */} +
+

+
+ {transI18n("ultimateEnergy")} +

+ +
+ + { + if (!avatars[avatarSelected?.ID.toString() || ""]?.can_change_sp) return + const newSpValue = Math.min(avatars[avatarSelected?.ID.toString() || ""]?.sp_max, Math.max(0, parseInt(e.target.value) || 0)); + setAvatars({ ...avatars, [avatarSelected?.ID.toString() || ""]: { ...avatars[avatarSelected?.ID.toString() || ""], sp_value: newSpValue } }); + }} + className="range range-warning range-sm w-full" + /> +
+ 0% + {((avatars[avatarSelected?.ID.toString() || ""]?.sp_value / avatars[avatarSelected?.ID.toString() || ""]?.sp_max) * 100).toFixed(1)}% + 100% +
+
+ +
+
+
+ + {/* Technique Toggle */} +
+

+
+ {transI18n("battleConfiguration")} +

+ +
+ +
+ {transI18n("techniqueNote")} +
+
+
+ + {/* Enhancement Selection */} + {avatarSelected?.Enhanced && ( +
+

+ + + + {transI18n("enhancement")} +

+ +
+ + +
+ {transI18n("enhancedNote")} +
+
+
+ )} +
+
+
+ +
+
+
+
+
+
+ {/* Header */} +
+

+
+ {transI18n("lightconeEquipment")} +

+
+ + {lightcone && lightconeDetail ? ( +
+
+ {/* Level & Rank Controls */} +
+

+
+ {transI18n("lightconeSettings")} +

+ +
+ {/* Level Input */} +
+ +
+ { + const newLightconeLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1)) + const newLightcone = { ...lightcone, level: newLightconeLevel, promotion: calcPromotion(newLightconeLevel) } + const newAvatar = { ...avatars[avatarSelected.ID] } + newAvatar.profileList[newAvatar.profileSelect].lightcone = newLightcone + setAvatar(newAvatar) + }} + className="input input-bordered w-full pr-16 font-mono" + placeholder={transI18n("placeholderLevel")} + /> +
{ + const newLightcone = { ...lightcone, level: 80, promotion: calcPromotion(80) } + const newAvatar = { ...avatars[avatarSelected.ID] } + newAvatar.profileList[newAvatar.profileSelect].lightcone = newLightcone + setAvatar(newAvatar) + }} + className="absolute right-3 top-1/2 -translate-y-1/2 text-base-content/60 cursor-pointer"> + {transI18n("max")} +
+
+
+ { + const newLightconeLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1)) + const newLightcone = { ...lightcone, level: newLightconeLevel, promotion: calcPromotion(newLightconeLevel) } + const newAvatar = { ...avatars[avatarSelected.ID] } + newAvatar.profileList[newAvatar.profileSelect].lightcone = newLightcone + setAvatar(newAvatar) + }} + className="range range-primary range-sm" + /> +
+
+ + {/* Rank Selection */} +
+ {/* Header */} +
+ {transI18n("superimpositionRank")} + S{lightcone.rank} +
+ + {/* Rank Buttons */} +
+
+ {[1, 2, 3, 4, 5].map((r) => ( + + ))} +
+
+ + {/* Help Text */} +
+ {transI18n("ranksNote")} +
+
+
+
+ + {/* Action Buttons */} +
+ + +
+
+ + {/* Lightcone Image */} +
+
+ Lightcone +
+
+ + {/* Lightcone Info & Controls */} +
+ {/* Basic Info */} + {lightconeDetail && ( +
+
+

+ +

+
+ {transI18n(lightconeDetail.BaseType.toLowerCase())} +
+
+ {calcRarity(lightconeDetail.Rarity) + "⭐"} +
+
+ {"id: " + lightcone.item_id} +
+
+ +
+
+
+
+ )} + + +
+
+ ) : ( + /* No Lightcone Equipped State */ +
+
handleShow("action_detail_modal")} + className="w-24 h-24 mx-auto mb-6 bg-base-300 rounded-full flex items-center justify-center cursor-pointer" + > + + + +
+

{transI18n("noLightconeEquipped")}

+

{transI18n("equipLightconeNote")}

+ +
+ )} +
+
+
+
+
+ )} + + +
+
+ handleCloseModal("action_detail_modal")} + > + ✕ + +
+ +
+ +
+
+ + ); +} diff --git a/src/components/card/characterCard.tsx b/src/components/card/characterCard.tsx new file mode 100644 index 0000000..bfb816c --- /dev/null +++ b/src/components/card/characterCard.tsx @@ -0,0 +1,83 @@ +"use client"; +import { getNameChar } from '@/helper'; +import useLocaleStore from '@/stores/localeStore'; +import { AvatarDetail } from '@/types'; +import ParseText from '../parseText'; +import Image from 'next/image'; +import { useTranslations } from 'next-intl'; +import useDetailDataStore from '@/stores/detailDataStore'; + +interface CharacterCardProps { + data: AvatarDetail +} + +export default function CharacterCard({ data }: CharacterCardProps) { + const { locale } = useLocaleStore(); + const transI18n = useTranslations("DataPage"); + const { baseType, damageType } = useDetailDataStore() + + return ( +
  • +
    + +
    + ALT + {data.DamageType.toLowerCase()} + {data.BaseType.toLowerCase()} +
    +
    + + +
  • + + ); +} diff --git a/src/components/card/characterInfoCard.tsx b/src/components/card/characterInfoCard.tsx new file mode 100644 index 0000000..ea02092 --- /dev/null +++ b/src/components/card/characterInfoCard.tsx @@ -0,0 +1,125 @@ +"use client"; + +import React from 'react'; +import { CharacterInfoCardType } from '@/types'; +import useLocaleStore from '@/stores/localeStore'; +import Image from 'next/image'; +import ParseText from '../parseText'; +import useDetailDataStore from '@/stores/detailDataStore'; +import { getLocaleName } from '@/helper/getName'; + +export default function CharacterInfoCard({ character, selectedCharacters, onCharacterToggle }: { character: CharacterInfoCardType, selectedCharacters: CharacterInfoCardType[], onCharacterToggle: (characterId: CharacterInfoCardType) => void }) { + const isSelected = selectedCharacters.some((selectedCharacter) => selectedCharacter.avatar_id === character.avatar_id); + const { mapAvatar, mapLightCone, baseType, damageType } = useDetailDataStore(); + const { locale } = useLocaleStore(); + + return ( +
    onCharacterToggle(character)} + > + + {/* Character Portrait */} +
    +
    + {getLocaleName(locale, + {mapAvatar[character.avatar_id.toString()]?.DamageType.toLowerCase()} + {mapAvatar[character.avatar_id.toString()]?.BaseType.toLowerCase()} +
    +
    + + {/* Character Name and Level */} +
    +
    + +
    Lv.{character.level} E{character.rank}
    +
    +
    + {character.relics.length > 0 && ( +
    + {character.relics.map((relic, index) => ( +
    +
    + Relic +
    +
    + +{relic.level} +
    +
    + ))} +
    + )} + + {/* Light Cone */} + {character.lightcone.item_id && ( +
    +
    + {getLocaleName(locale, + +
    +
    +
    +
    + +
    +
    Lv.{character.lightcone.level} S{character.lightcone.rank}
    +
    +
    +
    + )} +
    + ); +}; \ No newline at end of file diff --git a/src/components/card/lightconeCard.tsx b/src/components/card/lightconeCard.tsx new file mode 100644 index 0000000..e313a1f --- /dev/null +++ b/src/components/card/lightconeCard.tsx @@ -0,0 +1,53 @@ +"use client"; + +import { getLocaleName } from '@/helper'; +import useLocaleStore from '@/stores/localeStore'; +import ParseText from '../parseText'; +import Image from 'next/image'; +import { LightConeDetail } from '@/types'; + +interface LightconeCardProps { + data: LightConeDetail +} + +export default function LightconeCard({ data }: LightconeCardProps) { + + const { locale } = useLocaleStore(); + const text = getLocaleName(locale, data.Name) + return ( +
  • +
    + +
    + ALT +
    +
    + + +
  • + + ); + +} diff --git a/src/components/card/profileCard.tsx b/src/components/card/profileCard.tsx new file mode 100644 index 0000000..64302ac --- /dev/null +++ b/src/components/card/profileCard.tsx @@ -0,0 +1,79 @@ +"use client"; + +import React from 'react'; +import { AvatarProfileCardType } from '@/types'; +import useLocaleStore from '@/stores/localeStore'; +import Image from 'next/image'; +import ParseText from '../parseText'; +import useDetailDataStore from '@/stores/detailDataStore'; +import { getLocaleName } from '@/helper/getName'; + + +export default function ProfileCard({ profile, selectedProfile, onProfileToggle }: { profile: AvatarProfileCardType, selectedProfile: AvatarProfileCardType[], onProfileToggle: (profileId: AvatarProfileCardType) => void }) { + const isSelected = selectedProfile.some((selectedProfile) => selectedProfile.key === profile.key); + const { mapLightCone } = useDetailDataStore(); + const { locale } = useLocaleStore(); + + return ( +
    onProfileToggle(profile)} + > + {/* Light Cone */} + {profile.lightcone && ( +
    +
    + {getLocaleName(locale, + +
    +
    +
    +
    + +
    +
    Lv.{profile.lightcone.level} S{profile.lightcone.rank}
    +
    +
    +
    + )} + + {Object.keys(profile.relics).length > 0 && ( +
    + {Object.values(profile.relics).map((relic, index) => ( +
    +
    + Relic +
    +
    + +{relic.level} +
    +
    + ))} +
    + )} +
    + ); +}; \ No newline at end of file diff --git a/src/components/card/relicCard.tsx b/src/components/card/relicCard.tsx new file mode 100644 index 0000000..466a31d --- /dev/null +++ b/src/components/card/relicCard.tsx @@ -0,0 +1,196 @@ +"use client"; + +import useRelicMakerStore from "@/stores/relicMakerStore"; +import useUserDataStore from "@/stores/userDataStore"; +import Image from "next/image"; +import { useMemo } from "react"; + +interface RelicCardProps { + slot: string + avatarId: string +} +const getRarityColor = (rarity: string) => { + switch (rarity) { + case '3': return 'border-green-500 shadow-green-500/50 bg-linear-to-br from-green-700 via-green-400 to-green-500'; + case '4': return 'border-blue-500 shadow-blue-500/50 bg-linear-to-br from-blue-700 via-blue-400 to-blue-500'; + case '5': return 'border-purple-500 shadow-purple-500/50 bg-linear-to-br from-purple-700 via-purple-400 to-purple-500'; + case '6': return 'border-yellow-500 shadow-yellow-500/50 bg-linear-to-br from-yellow-700 via-yellow-400 to-yellow-500'; + default: return 'border-gray-500 shadow-gray-500/50'; + } +}; + +const getRarityName = (slot: string) => { + switch (slot) { + case '1': return ( +
    + Head +

    Head

    +
    + ); + case '2': return ( +
    + Hand +

    Hands

    +
    + ); + case '3': return ( +
    + Body +

    Body

    +
    + ); + case '4': return ( +
    + Foot +

    Feet

    +
    + ); + case '5': return ( +
    + Neck +

    Planar sphere

    +
    + ); + case '6': return ( +
    + Object +

    Link rope

    +
    + ); + default: return ''; + } +}; +export default function RelicCard({ slot, avatarId }: RelicCardProps) { + const { avatars } = useUserDataStore() + const { selectedRelicSlot } = useRelicMakerStore() + + const relicDetail = useMemo(() => { + const avatar = avatars[avatarId]; + if (avatar) { + if (avatar.profileList[avatar.profileSelect].relics[slot]) { + return avatar.profileList[avatar.profileSelect].relics[slot]; + } + return null; + } + return null; + }, [avatars, avatarId, slot]); + + return ( +
    + {relicDetail ? ( +
    +
    + + Relic + + + {/* Level Badge */} +
    + +{relicDetail.level} +
    +
    + +
    +
    {getRarityName(slot)}
    +
    +
    + ) : ( +
    +
    + + + + + + + {/* Level Badge */} +
    + +{0} +
    +
    + +
    +
    {getRarityName(slot)}
    +
    +
    + )} + +
    + ) +} \ No newline at end of file diff --git a/src/components/card/simpleCharacterCard.tsx b/src/components/card/simpleCharacterCard.tsx new file mode 100644 index 0000000..8b61921 --- /dev/null +++ b/src/components/card/simpleCharacterCard.tsx @@ -0,0 +1,69 @@ +"use client"; +import Image from 'next/image'; +import { AvatarDetail } from '@/types'; +import useDetailDataStore from '@/stores/detailDataStore'; + + +interface SimpleAvatarCardProps { + data: AvatarDetail; + isSelected?: boolean; + onClick?: () => void; + showRemoveHover?: boolean; +} + +export const SimpleAvatarCard = ({ data, isSelected, onClick, showRemoveHover }: SimpleAvatarCardProps) => { +const { baseType, damageType } = useDetailDataStore() + + return ( +
    + Avatar +
    + Element +
    +
    + Path +
    + + {showRemoveHover && ( +
    + + + +
    + )} +
    + ); +}; \ No newline at end of file diff --git a/src/components/changelog/index.tsx b/src/components/changelog/index.tsx new file mode 100644 index 0000000..3d75c08 --- /dev/null +++ b/src/components/changelog/index.tsx @@ -0,0 +1,91 @@ + +import useLocaleStore from '@/stores/localeStore'; +import { Check, Sparkles, Bug, Zap, Package, Calendar } from 'lucide-react'; + +export default function ChangelogBar() { + const { changelog } = useLocaleStore() + + const getIcon = (type: string) => { + switch (type) { + case 'feature': + return ; + case 'fix': + return ; + case 'improvement': + return ; + default: + return ; + } + }; + + const getBadgeClass = (type: string) => { + switch (type) { + case 'feature': + return 'badge-success'; + case 'fix': + return 'badge-error'; + case 'improvement': + return 'badge-warning'; + default: + return 'badge-info'; + } + }; + + const getTypeLabel = (type: string) => { + switch (type) { + case 'feature': + return 'Feature'; + case 'fix': + return 'Fix'; + case 'improvement': + return 'Improvement'; + default: + return 'Update'; + } + }; + + return ( +
    + {/* Alert */} + {/*
    + +
    */} + + {/* Timeline */} +
    + {changelog.map((change, index) => ( +
    +
    + {/* Version Header */} +
    +
    + {getIcon(change.type)} + {getTypeLabel(change.type)} +
    +

    + Version {change.version} +

    +
    + + {change.date} +
    +
    + + {/* Changes List */} +
      + {change.items.map((item, itemIndex) => ( +
    • +
      + +
      + {item} +
    • + ))} +
    +
    +
    + ))} +
    +
    + ); +} \ No newline at end of file diff --git a/src/components/clientDataFetcher/index.tsx b/src/components/clientDataFetcher/index.tsx new file mode 100644 index 0000000..c1fa8c6 --- /dev/null +++ b/src/components/clientDataFetcher/index.tsx @@ -0,0 +1,56 @@ +"use client" + +import { + useFetchASGroupData, + useFetchAvatarData, + useFetchChangelog, + useFetchConfigData, + useFetchLightconeData, + useFetchMOCGroupData, + useFetchMonsterData, + useFetchPeakGroupData, + useFetchPFGroupData, + useFetchRelicSetData +} from "@/lib/hooks" + +export default function ClientDataFetcher({ + children +}: { + children: React.ReactNode +}) { + const q1 = useFetchConfigData() + const q2 = useFetchAvatarData() + const q3 = useFetchLightconeData() + const q4 = useFetchRelicSetData() + const q5 = useFetchMonsterData() + const q6 = useFetchPFGroupData() + const q7 = useFetchMOCGroupData() + const q8 = useFetchASGroupData() + const q9 = useFetchPeakGroupData() + const q10 = useFetchChangelog() + + const queries = [q1,q2,q3,q4,q5,q6,q7,q8,q9,q10] + + const loading = queries.some(q => q.isLoading) + + const progress = + (queries.filter(q => q.isSuccess).length / queries.length) * 100 + + if (loading) { + return ( +
    +
    Loading data...
    + + + +
    {Math.floor(progress)}%
    +
    + ) + } + + return <>{children} +} \ No newline at end of file diff --git a/src/components/connectBar/index.tsx b/src/components/connectBar/index.tsx new file mode 100644 index 0000000..f55ca86 --- /dev/null +++ b/src/components/connectBar/index.tsx @@ -0,0 +1,169 @@ +"use client" + +import { connectToPS, syncDataToPS } from "@/helper" +import useConnectStore from "@/stores/connectStore" +import useGlobalStore from "@/stores/globalStore" +import { PSConnectType } from "@/types" +import { useTranslations } from "next-intl" +import { useState } from "react" + +export default function ConnectBar() { + const transI18n = useTranslations("DataPage") + const [message, setMessage] = useState({ text: '', type: '' }); + const { + connectionType, + privateType, + serverUrl, + username, + password, + setConnectionType, + setPrivateType, + setServerUrl, + setUsername, + setPassword + } = useConnectStore() + const { isConnectPS, setIsConnectPS } = useGlobalStore() + + return ( +
    +
    + + +
    + + {connectionType === PSConnectType.Other && ( +
    +
    + + setServerUrl(e.target.value)} + /> +
    +
    + + +
    + +
    + + setUsername(e.target.value)} + /> +
    +
    + + setPassword(e.target.value)} + /> +
    +
    + )} + + {message.text && ( +
    + {message.text} +
    + )} + +
    +
    + {transI18n("status")}: + + {isConnectPS ? transI18n("connected") : transI18n("unconnected")} + +
    + + {/* Buttons */} +
    + + + {isConnectPS && ( + + )} +
    +
    + +
    + ) +} \ No newline at end of file diff --git a/src/components/eidolonsInfo/index.tsx b/src/components/eidolonsInfo/index.tsx new file mode 100644 index 0000000..08f383b --- /dev/null +++ b/src/components/eidolonsInfo/index.tsx @@ -0,0 +1,85 @@ +"use client" +import { replaceByParam, getLocaleName } from '@/helper'; +import Image from "next/image"; +import ParseText from "../parseText"; +import useLocaleStore from "@/stores/localeStore"; +import useUserDataStore from "@/stores/userDataStore"; +import { useMemo } from "react"; +import { useTranslations } from "next-intl"; +import useCurrentDataStore from "@/stores/currentDataStore"; +import ExtraEffectList from '../extraInfo'; + + +export default function EidolonsInfo() { + const { avatarSelected } = useCurrentDataStore() + const { locale } = useLocaleStore() + const transI18n = useTranslations("DataPage") + const { setAvatars, avatars } = useUserDataStore() + + const charRank = useMemo(() => { + if (!avatarSelected) return null; + const avatar = avatars[avatarSelected.ID]; + if (avatar?.enhanced != "") { + return avatarSelected?.Enhanced?.[avatar?.enhanced]?.Ranks + } + return avatarSelected?.Ranks + }, [avatarSelected, avatars]); + + return ( +
    +

    +
    + {transI18n("eidolons")} +

    +
    + {charRank && avatars[avatarSelected?.ID || ""] && ( +
    + {Object.entries(charRank || {}).map(([key, rank]) => ( +
    +
    { + let newRank = Number(rank.Rank) + if (avatars[avatarSelected?.ID || ""]?.data?.rank == Number(rank.Rank)) { + newRank = Number(rank.Rank) - 1 + } + setAvatars({ ...avatars, [avatarSelected?.ID || ""]: { ...avatars[avatarSelected?.ID || ""], data: { ...avatars[avatarSelected?.ID || ""].data, rank: newRank } } }) + }} + > + {`Rank + +
    + {rank.Rank}. + +
    +
    + +
    +
    + +
    +
    + ))} + +
    + )} +
    +
    + ); +} diff --git a/src/components/extraInfo/index.tsx b/src/components/extraInfo/index.tsx new file mode 100644 index 0000000..d1f16a9 --- /dev/null +++ b/src/components/extraInfo/index.tsx @@ -0,0 +1,99 @@ +"use client" + +import { useState } from "react" +import { replaceByParam, getLocaleName } from "@/helper" +import { ExtraEffect } from "@/types" +import { useTranslations } from "next-intl" + +type Props = { + extras: Record | undefined + locale: string +} + +export default function ExtraEffectList({ extras, locale }: Props) { + const [openList, setOpenList] = useState(false) + const [openId, setOpenId] = useState(null) + const transI18n = useTranslations("DataPage") + if (!extras || Object.keys(extras).length === 0) return null + + return ( +
    +
    setOpenList(!openList)} + > + + {transI18n("listExtraEffect")} ({Object.keys(extras).length}) + + + + ▶ + +
    + +
    +
    + {Object.values(extras).map((extra) => { + const isOpen = openId === extra.ID + + return ( +
    +
    + setOpenId(isOpen ? null : extra.ID) + } + > +
    + + {transI18n("extra")} + + + + {getLocaleName(locale, extra.Name)} + +
    + + + ▶ + +
    + +
    +
    +
    +
    + ) + })} +
    +
    +
    + ) +} \ No newline at end of file diff --git a/src/components/extraSettingBar/index.tsx b/src/components/extraSettingBar/index.tsx new file mode 100644 index 0000000..d15b40c --- /dev/null +++ b/src/components/extraSettingBar/index.tsx @@ -0,0 +1,584 @@ +'use client' +import { motion } from "framer-motion" +import { EyeOff, Eye, Hammer, RefreshCw, ShieldBan, User, Swords, SkipForward, BowArrow, Info, RouteIcon, Search, CupSoda, UsersIcon } from "lucide-react" +import useGlobalStore from '@/stores/globalStore' +import { useTranslations } from "next-intl" +import { getLocaleName, getNameChar } from "@/helper" +import useLocaleStore from "@/stores/localeStore" +import SelectCustomImage from "../select/customSelectImage" +import { useMemo, useState } from "react" +import useDetailDataStore from "@/stores/detailDataStore" +import Editor from "react-simple-code-editor" +import Prism from "prismjs" +import "prismjs/components/prism-lua" +import "prismjs/themes/prism-tomorrow.css" +import { SimpleAvatarCard } from "../card/simpleCharacterCard" + +export default function ExtraSettingBar() { + const { extraData, setExtraData, isEnableChangePath, setIsEnableChangePath, isEnableLua, setIsEnableLua } = useGlobalStore() + const transI18n = useTranslations("DataPage") + const { mapAvatar, mapPeak, stage, baseType } = useDetailDataStore() + const { locale } = useLocaleStore() + const [showSearchStage, setShowSearchStage] = useState(false) + const [showSearchLineup, setShowSearchLineup] = useState(false) + const [isChildClick, setIsChildClick] = useState(false) + const [stageSearchTerm, setStageSearchTerm] = useState("") + const [stagePage, setStagePage] = useState(1) + + const pageSize = 30 + const stageList = useMemo(() => Object.values(stage), [stage]) + + const term = stageSearchTerm.toLowerCase() + + const filteredStages = useMemo(() => stageList.filter((s) => + getLocaleName(locale, s.Name).toLowerCase().includes(term) || + String(s.ID).toLowerCase().includes(term) + ), [stageList, term, locale]) + + const paginatedStages = useMemo(() => filteredStages.slice( + (stagePage - 1) * pageSize, + stagePage * pageSize + ), [filteredStages, stagePage, pageSize]) + + const hasMorePages = useMemo(() => stagePage * pageSize < filteredStages.length, [stagePage, filteredStages]) + + const onChangeSearch = (v: string) => { + setStageSearchTerm(v) + setStagePage(1) + } + + return ( +
    + {extraData?.theory_craft && ( +
    +

    + {transI18n("theoryCraft")} +
    + +
    +

    + + + + + + {extraData?.theory_craft?.mode && ( + <> + + + + + + + + +
    + +
    + {(extraData?.theory_craft?.custom_lineup || []).length > 0 && ( + (extraData?.theory_craft?.custom_lineup || []).map((avatarId) => { + const avatarData = mapAvatar[avatarId]; + if (!avatarData) return null; + return ( + { + const newLineup = (extraData?.theory_craft?.custom_lineup || []).filter(id => id !== avatarId); + setExtraData({ + ...extraData, + theory_craft: { + stage_id: extraData?.theory_craft?.stage_id ?? 30118121, + cycle_count: extraData?.theory_craft?.cycle_count ?? 1, + mode: extraData?.theory_craft?.mode ?? false, + hp: extraData?.theory_craft?.hp ?? {}, + custom_lineup: newLineup + } + }); + }} + /> + ); + }) + )} + + +
    + + {showSearchLineup && ( +
    e.stopPropagation()} + className="mt-2 w-full z-50 border bg-base-200 border-slate-600 rounded-lg p-4 shadow-2xl" + > +
    + {transI18n("selectedCharacters")} + +
    + +
    + {Object.values(mapAvatar).map((avatar) => { + const currentLineup = extraData?.theory_craft?.custom_lineup || []; + const isSelected = currentLineup.includes(avatar.ID.toString()); + + return ( + { + const idStr = avatar.ID.toString(); + let newLineup = [...currentLineup]; + + if (isSelected) { + newLineup = newLineup.filter(id => id !== idStr); + } else { + newLineup.push(idStr); + } + + setExtraData({ + ...extraData, + theory_craft: { + stage_id: extraData?.theory_craft?.stage_id ?? 30118121, + cycle_count: extraData?.theory_craft?.cycle_count ?? 1, + mode: extraData?.theory_craft?.mode ?? false, + hp: extraData?.theory_craft?.hp ?? {}, + custom_lineup: newLineup + } + }); + }} + /> + ); + })} +
    +
    + )} +
    +
    + + )} +
    + )} + + {/*MULTIPATH CHAR */} +
    +

    + {transI18n("multipathCharacter")} +
    + +
    + setIsEnableChangePath(e.target.checked)} + /> +

    + {isEnableChangePath && ( + <> + + + + + + + + + )} +
    + + {/* CHALLENGE */} +
    +

    {transI18n("challenge")}

    + + + + + + + + +
    + + {/*EXTRA FEATURES */} +
    +

    {transI18n("extraFeatures")}

    + + {extraData?.setting?.hide_ui !== undefined && ( + + + + )} + + {extraData?.setting?.censorship !== undefined && ( + + + + )} +
    + +
    +

    + {"Lua"} +
    + +
    + setIsEnableLua(e.target.checked)} + /> +

    + {isEnableLua && ( + <> +
    + + +
    + + setExtraData( + { + ...extraData, + lua: code + } + ) + } + highlight={(code) => Prism.highlight(code, Prism.languages.lua, "lua")} + padding={16} + textareaClassName="outline-none" + className="text-sm font-mono min-h-75" + style={{ + background: "#1e1e1e", + color: "#fff", + }} + /> +
    +
    + + )} +
    + +
    + ) +} diff --git a/src/components/footer/index.tsx b/src/components/footer/index.tsx new file mode 100644 index 0000000..4992794 --- /dev/null +++ b/src/components/footer/index.tsx @@ -0,0 +1,9 @@ +export default function Footer() { + return ( +
    + +
    + ) +} diff --git a/src/components/header/index.tsx b/src/components/header/index.tsx new file mode 100644 index 0000000..893ff63 --- /dev/null +++ b/src/components/header/index.tsx @@ -0,0 +1,594 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +"use client" +import { downloadJson } from "@/helper"; +import { converterToFreeSRJson } from "@/helper/converterToFreeSRJson"; +import { useChangeTheme } from "@/hooks/useChangeTheme"; +import { listCurrentLanguage } from "@/constant/constant"; +import useLocaleStore from "@/stores/localeStore"; +import useUserDataStore from "@/stores/userDataStore"; +import { motion } from "framer-motion"; +import { useTranslations } from "next-intl"; +import { useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; +import EnkaImport from "../importBar/enka"; +import useModelStore from "@/stores/modelStore"; +import FreeSRImport from "../importBar/freesr"; +import { toast } from "react-toastify"; +import { micsSchema } from "@/zod"; +import useGlobalStore from "@/stores/globalStore"; +import MonsterBar from "../monsterBar"; +import Image from "next/image"; +import ConnectBar from "../connectBar"; +import ExtraSettingBar from "../extraSettingBar"; +import ChangelogBar from "../changelog"; +import { ModalConfig } from "@/types"; +import { ScrollText } from "lucide-react"; + +const themes = [ + { label: "Winter" }, + { label: "Night" }, + { label: "Cupcake" }, + { label: "Coffee" }, +]; + +export default function Header() { + const { changeTheme } = useChangeTheme() + const { locale, setLocale } = useLocaleStore() + const { + avatars, + battle_type, + setAvatars, + setBattleType, + moc_config, + pf_config, + as_config, + ce_config, + peak_config, + setMocConfig, + setPfConfig, + setAsConfig, + setCeConfig, + setPeakConfig, + } = useUserDataStore() + + const router = useRouter() + const transI18n = useTranslations("DataPage") + const { + setIsOpenImport, + isOpenImport, + setIsOpenMonster, + isOpenMonster, + setIsOpenConnect, + isOpenConnect, + setIsOpenExtra, + isOpenExtra, + setIsChangelog, + isChangelog + } = useModelStore() + + const [importModal, setImportModal] = useState("enka"); + + const { isConnectPS, extraData } = useGlobalStore() + + useEffect(() => { + + const cookieLocale = document.cookie.split("; ") + .find((row) => row.startsWith("MYNEXTAPP_LOCALE")) + ?.split("=")[1]; + + if (cookieLocale) { + if (!listCurrentLanguage.hasOwnProperty(cookieLocale)) { + setLocale("en") + } else { + setLocale(cookieLocale) + } + + } else { + let browserLocale = navigator.language.slice(0, 2); + + if (!listCurrentLanguage.hasOwnProperty(browserLocale)) { + browserLocale = "en" + } + setLocale(browserLocale); + document.cookie = `MYNEXTAPP_LOCALE=${browserLocale};` + router.refresh() + } + }, [router, setLocale]) + + const changeLocale = (newLocale: string) => { + setLocale(newLocale) + document.cookie = `MYNEXTAPP_LOCALE=${newLocale};` + router.refresh() + } + + const handleShow = (modalId: string) => { + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + modal.showModal(); + } + }; + + // Close modal handler + const handleCloseModal = (modalId: string) => { + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + modal.close() + } + }; + + const modalConfigs: ModalConfig[] = [ + { + id: "connect_modal", + title: transI18n("psConnection"), + isOpen: isOpenConnect, + onClose: () => { + setIsOpenConnect(false) + handleCloseModal("connect_modal") + }, + content: + }, + { + id: "import_modal", + title: transI18n("importSetting"), + isOpen: isOpenImport, + onClose: () => { + setIsOpenImport(false) + handleCloseModal("import_modal") + }, + content: ( + <> + {importModal === "enka" && } + {importModal === "freesr" && } + + ) + }, + { + id: "monster_modal", + title: transI18n("monsterSetting"), + isOpen: isOpenMonster, + onClose: () => { + setIsOpenMonster(false) + handleCloseModal("monster_modal") + }, + content: + }, + { + id: "extra_modal", + title: transI18n("extraSetting"), + isOpen: isOpenExtra, + onClose: () => { + setIsOpenExtra(false) + handleCloseModal("extra_modal") + }, + content: + }, + { + id: "changelog_modal", + title: "Changelog", + isOpen: isChangelog, + onClose: () => { + setIsChangelog(false) + handleCloseModal("changelog_modal") + }, + content: + } + ] + + // Handle ESC key to close modal + useEffect(() => { + for (const item of modalConfigs) { + if (!item?.isOpen) { + handleCloseModal(item?.id || "") + } else { + handleShow(item?.id || "") + } + } + const handleEscKey = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + for (const item of modalConfigs) { + handleCloseModal(item?.id || "") + } + } + }; + + window.addEventListener('keydown', handleEscKey); + return () => window.removeEventListener('keydown', handleEscKey); + }, [isOpenImport, isOpenMonster, isOpenConnect, isOpenExtra, isChangelog]); + + const handleImportDatabase = (event: React.ChangeEvent) => { + + const file = event.target.files?.[0]; + if (!file) { + toast.error(transI18n("pleaseSelectAFile")) + return + } + if (!file.name.endsWith(".json") || file.type !== "application/json") { + toast.error(transI18n("fileMustBeAValidJsonFile")) + return + } + + if (file) { + + const reader = new FileReader(); + reader.onload = (e) => { + try { + const data = JSON.parse(e.target?.result as string); + const parsed = micsSchema.parse(data) + setAvatars(parsed.avatars) + setBattleType(parsed.battle_type) + setMocConfig(parsed.moc_config) + setPfConfig(parsed.pf_config) + setAsConfig(parsed.as_config) + setCeConfig(parsed.ce_config) + setPeakConfig(parsed.peak_config) + toast.success(transI18n("importDatabaseSuccess")) + } catch { + toast.error(transI18n("fileMustBeAValidJsonFile")) + } + }; + reader.readAsText(file); + } + + }; + + return ( +
    +
    + {/* Mobile menu dropdown */} +
    +
    + + + +
    + +
    + + {/* Logo */} + + +
    + Logo +
    +

    + Firefly Sr + + Tools + +

    +

    Firefly Shelter

    +
    +
    +
    + +
    + + {/* Desktop navigation */} +
    + +
    + + {/* Right side items */} +
    +
    +
    +
    + {isConnectPS ? transI18n("connected") : transI18n("unconnected")} +
    +
    +
    +
    + +
    { + setIsChangelog(true) + }} + > + +
    + + {/* Language selector - REFINED */} +
    +
    + + + + + +
    +
    + +
    +
    + + + + + + +
    + +
    +
      + {themes.map((theme) => ( +
    • { + if (changeTheme) changeTheme(theme.label.toLowerCase()); + }} + > + +
    • + ))} +
    +
    +
    +
    + + {modalConfigs?.map(({ id, title, onClose, content }) => ( + +
    +
    + + ✕ + +
    + +
    +

    + {title} +

    +
    + + {content} +
    +
    + ))} +
    + ) +} \ No newline at end of file diff --git a/src/components/importBar/copy.tsx b/src/components/importBar/copy.tsx new file mode 100644 index 0000000..0dff700 --- /dev/null +++ b/src/components/importBar/copy.tsx @@ -0,0 +1,294 @@ +"use client" +import useUserDataStore from "@/stores/userDataStore"; +import { useState, useMemo } from "react"; +import useCopyProfileStore from "@/stores/copyProfile"; +import ProfileCard from "../card/profileCard"; +import { AvatarProfileCardType, AvatarProfileStore } from "@/types"; +import Image from "next/image"; +import { getNameChar, calcRarity } from "@/helper"; +import useLocaleStore from "@/stores/localeStore"; +import { useTranslations } from "next-intl"; +import SelectCustomImage from "../select/customSelectImage"; +import useCurrentDataStore from "@/stores/currentDataStore"; +import useDetailDataStore from "@/stores/detailDataStore"; + +export default function CopyImport() { + const { avatars, setAvatar } = useUserDataStore(); + const { avatarSelected } = useCurrentDataStore() + const { mapAvatar, baseType, damageType } = useDetailDataStore() + const { locale } = useLocaleStore() + const { + selectedProfiles, + avatarCopySelected, + setSelectedProfiles, + setAvatarCopySelected, + listElement, + listPath, + listRank, + setListElement, + setListPath, + setListRank + } = useCopyProfileStore() + const transI18n = useTranslations("DataPage") + + const [message, setMessage] = useState({ + type: "", + text: "" + }) + + const listAvatar = useMemo(() => { + if (!mapAvatar || !locale || !transI18n) return [] + let list = Object.values(mapAvatar); + const allElementFalse = !Object.values(listElement).some(v => v) + const allPathFalse = !Object.values(listPath).some(v => v) + const allRarityFalse = !Object.values(listRank).some(v => v) + list = list.filter(item => (allElementFalse || listElement[item.DamageType]) && (allPathFalse || listPath[item.BaseType]) && (allRarityFalse || listRank[calcRarity(item.Rarity)])) + list.sort((a, b) => { + const r = calcRarity(b.Rarity) - calcRarity(a.Rarity) + if (r !== 0) return r + return a.ID - b.ID + }) + return list + }, [mapAvatar, listElement, listPath, listRank, locale, transI18n]) + + + const handleProfileToggle = (profile: AvatarProfileCardType) => { + if (selectedProfiles.some((selectedProfile) => selectedProfile.key === profile.key)) { + setSelectedProfiles(selectedProfiles.filter((selectedProfile) => selectedProfile.key !== profile.key)); + return; + } + setSelectedProfiles([...selectedProfiles, profile]); + }; + + + const clearSelection = () => { + setSelectedProfiles([]); + setMessage({ + type: "success", + text: transI18n("clearAll") + }) + }; + + const selectAll = () => { + if (avatarCopySelected) { + setSelectedProfiles(avatars[avatarCopySelected?.ID.toString()].profileList.map((profile, index) => { + if (!profile.lightcone?.item_id && Object.keys(profile.relics).length == 0) { + return null; + } + return { + key: index, + ...profile, + } as AvatarProfileCardType + }).filter((profile) => profile !== null)); + } + setMessage({ + type: "success", + text: transI18n("selectAll") + }) + }; + + const handleCopy = () => { + if (selectedProfiles.length === 0) { + setMessage({ + type: "error", + text: transI18n("pleaseSelectAtLeastOneProfile") + }); + return; + } + if (!avatarCopySelected) { + setMessage({ + type: "error", + text: transI18n("noAvatarToCopySelected") + }); + return; + } + if (!avatarSelected) { + setMessage({ + type: "error", + text: transI18n("noAvatarSelected") + }); + return; + } + + const newListProfile = avatars[avatarCopySelected.ID.toString()].profileList.map((profile) => { + if (!profile.lightcone?.item_id && Object.keys(profile.relics).length == 0) { + return null; + } + return { + ...profile, + profile_name: profile.profile_name + ` - Copy: ${avatarCopySelected?.ID}`, + } as AvatarProfileStore + }).filter((profile) => profile !== null); + + const newAvatar = { + ...avatars[avatarSelected?.ID?.toString()], + profileList: avatars[avatarSelected?.ID?.toString()].profileList.concat(newListProfile), + profileSelect: avatars[avatarSelected?.ID?.toString()].profileList.length - 1, + } + setAvatar(newAvatar); + setSelectedProfiles([]); + setMessage({ + type: "success", + text: transI18n("copied") + }); + }; + + return ( +
    +
    + {/* Header */} +
    + +
    + +
    +
    {transI18n("filter")}
    +
    + {/* Path */} +
    +
    + {Object.entries(baseType).filter(([key]) => key !== "").map(([key, value]) => ( +
    { + setListPath({ ...listPath, [key]: !listPath[key] }) + }} + className="w-12.5 h-12.5 hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-md cursor-pointer" + style={{ + backgroundColor: listPath[key] ? "#374151" : "#6B7280" + }}> + {key} +
    + ))} +
    +
    + + {/* Element */} +
    +
    + {Object.entries(damageType).filter(([key]) => key !== "").map(([key, value]) => ( +
    { + setListElement({ ...listElement, [key]: !listElement[key] }) + }} + className="w-12.5 h-12.5 hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-md cursor-pointer" + style={{ + backgroundColor: listElement[key] ? "#374151" : "#6B7280" + }}> + {key} +
    + ))} +
    +
    + + {/* Rank */} +
    +
    + {Object.entries(listRank).map(([key], index) => ( +
    { + setListRank({ ...listRank, [key]: !listRank[key] }) + }} + className="w-12.5 h-12.5 hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-md cursor-pointer" + style={{ + backgroundColor: listRank[key] ? "#374151" : "#6B7280" + }}> +
    {key}*
    +
    + ))} +
    +
    +
    + +
    +
    + + {listAvatar.length > 0 && ( +
    +
    {transI18n("characterName")}
    + ({ + value: avatar.ID.toString(), + label: getNameChar(locale, transI18n, avatar), + imageUrl: `${process.env.CDN_URL}/${avatar.Image.AvatarIconPath}` + }))} + excludeSet={[]} + selectedCustomSet={avatarCopySelected?.ID.toString() || ""} + placeholder="Character Select" + setSelectedCustomSet={(value) => setAvatarCopySelected(mapAvatar[value] || null)} + /> +
    + )} + +
    +
    + + + + {/* Selection Info */} +
    + + {transI18n("selectedProfiles")}: {selectedProfiles.length} + + + + {selectedProfiles.length > 0 && ( + + )} +
    + {message.type && message.text && ( +
    {message.type == "error" ? "😭" : "🎉"} {message.text}
    + )} +
    + + {/* Character Grid */} +
    + {avatarCopySelected && avatars[avatarCopySelected?.ID.toString()]?.profileList.map((profile, index) => { + if (!profile.lightcone?.item_id && Object.keys(profile.relics).length == 0) { + return null; + } + return ( + + ) + })} +
    +
    +
    + ) +} \ No newline at end of file diff --git a/src/components/importBar/enka.tsx b/src/components/importBar/enka.tsx new file mode 100644 index 0000000..138a0df --- /dev/null +++ b/src/components/importBar/enka.tsx @@ -0,0 +1,224 @@ +"use client" +import { useState } from "react"; +import CharacterInfoCard from "../card/characterInfoCard"; +import useEnkaStore from "@/stores/enkaStore"; +import { SendDataThroughProxy } from "@/lib/api/api"; +import { CharacterInfoCardType, EnkaResponse } from "@/types"; +import useUserDataStore from "@/stores/userDataStore"; +import { converterOneEnkaDataToAvatarStore } from "@/helper"; +import useModelStore from "@/stores/modelStore"; +import { toast } from "react-toastify"; +import { useTranslations } from "next-intl"; + +export default function EnkaImport() { + const { + uidInput, + setUidInput, + enkaData, + selectedCharacters, + setSelectedCharacters, + setEnkaData, + } = useEnkaStore(); + const transI18n = useTranslations("DataPage") + const { avatars, setAvatar } = useUserDataStore(); + const { setIsOpenImport } = useModelStore() + const [isLoading, setIsLoading] = useState(false) + const [Error, setError] = useState("") + + const handlerFetchData = async () => { + if (!uidInput) { + setError(transI18n("pleaseEnterUid")) + return; + } + setIsLoading(true) + const data : EnkaResponse = await SendDataThroughProxy({data: {serverUrl: "https://enka.network/api/hsr/uid/" + uidInput, method: "GET"}}) + if (data) { + setEnkaData(data) + setSelectedCharacters(data.detailInfo.avatarDetailList.map((character) => { + return { + key: character.avatarId, + avatar_id: character.avatarId, + rank: character.rank ?? 0, + level: character.level, + lightcone: { + level: character.equipment?.level ?? 0, + rank: character.equipment?.rank ?? 0, + item_id: character.equipment?.tid ?? 0, + }, + relics: character.relicList.map((relic) => ({ + level: relic.level, + relic_id: relic.tid, + relic_set_id: parseInt(relic.tid.toString().slice(1, -1), 10), + })), + } as CharacterInfoCardType + })) + setError("") + } else { + setError(transI18n("failedToFetchEnkaData")) + } + setIsLoading(false) + } + + const handleCharacterToggle = (character: CharacterInfoCardType) => { + if (selectedCharacters.some((selectedCharacter) => selectedCharacter.key === character.key)) { + setSelectedCharacters(selectedCharacters.filter((selectedCharacter) => selectedCharacter.key !== character.key)); + return; + } + setSelectedCharacters([...selectedCharacters, character]); + }; + + const clearSelection = () => { + setSelectedCharacters([]); + }; + + const selectAll = () => { + if (enkaData) { + setSelectedCharacters(enkaData?.detailInfo.avatarDetailList.map((character) => { + return { + key: character.avatarId, + avatar_id: character.avatarId, + rank: character.rank ?? 0, + level: character.level, + lightcone: (character.equipment && character.equipment.tid) ? { + level: character.equipment?.level ?? 0, + rank: character.equipment?.rank ?? 0, + item_id: character.equipment?.tid ?? 0, + } : null, + relics: character.relicList.map((relic) => { + return { + level: relic.level, + relic_id: relic.tid, + relic_set_id: parseInt(relic.tid.toString().slice(1, -1), 10), + } + }), + } as CharacterInfoCardType + })); + } + }; + + const handleImport = () => { + if (selectedCharacters.length === 0) { + setError(transI18n("pleaseSelectAtLeastOneCharacter")); + return; + } + if (!enkaData) { + setError(transI18n("noDataToImport")); + return; + } + setError(""); + const listAvatars = { ...avatars } + const filterData = enkaData.detailInfo.avatarDetailList.filter((character) => selectedCharacters.some((selectedCharacter) => selectedCharacter.avatar_id === character.avatarId)) + filterData.forEach((character) => { + const newAvatar = { ...listAvatars[character.avatarId.toString()] } + if (Object.keys(newAvatar).length !== 0) { + newAvatar.level = character.level ?? 0 + newAvatar.promotion = character.promotion ?? 0 + newAvatar.data = { + rank: character.rank ?? 0, + skills: character.skillTreeList.reduce((acc, skill) => { + acc[skill.pointId] = skill.level; + return acc; + }, {} as Record), + } + const newProfile = converterOneEnkaDataToAvatarStore(character, newAvatar.profileList.length) + if (newProfile) { + newAvatar.profileList.push(newProfile) + newAvatar.profileSelect = newAvatar.profileList.length - 1 + } + setAvatar(newAvatar) + } + + }) + setIsOpenImport(false) + toast.success(transI18n("importEnkaDataSuccess")) + }; + + return ( +
    +
    + {/* Header */} +
    +

    HSR UID

    + +
    + setUidInput(e.target.value)} + className="bg-slate-800 text-white px-4 py-3 rounded-lg border border-slate-700 flex-1 max-w-md focus:outline-none focus:border-blue-500" + placeholder="Enter UID" + /> + + {selectedCharacters.length > 0 && ( + + )} +
    + {Error && ( +
    😭 {Error}
    + )} + + {/* Player Info */} + {enkaData && ( +
    +
    Name: {enkaData.detailInfo.nickname}
    +
    UID: {enkaData.detailInfo.uid}
    +
    Level: {enkaData.detailInfo.level}
    +
    + )} + + {/* Selection Info */} +
    + + {transI18n("selectedCharacters")}: {selectedCharacters.length} + + + +
    +
    + {isLoading && ( +
    +
    +
    + )} + {/* Character Grid */} +
    + {enkaData?.detailInfo.avatarDetailList.map((character) => ( + ({ + level: relic.level ?? 0, + relic_id: relic.tid, + relic_set_id: parseInt(relic.tid.toString().slice(1, -1), 10), + })), + } as CharacterInfoCardType + } + selectedCharacters={selectedCharacters} + onCharacterToggle={handleCharacterToggle} + /> + ))} +
    +
    +
    + ); +} \ No newline at end of file diff --git a/src/components/importBar/freesr.tsx b/src/components/importBar/freesr.tsx new file mode 100644 index 0000000..00c5b17 --- /dev/null +++ b/src/components/importBar/freesr.tsx @@ -0,0 +1,235 @@ +"use client" + +import useFreeSRStore from "@/stores/freesrStore"; +import useModelStore from "@/stores/modelStore"; +import useUserDataStore from "@/stores/userDataStore"; +import { CharacterInfoCardType } from "@/types"; +import { useState } from "react"; +import CharacterInfoCard from "../card/characterInfoCard"; +import { freeSrJsonSchema } from "@/zod"; +import { toast } from "react-toastify"; +import { converterOneFreeSRDataToAvatarStore } from "@/helper"; +import { useTranslations } from "next-intl"; + +export default function FreeSRImport() { + const { avatars, setAvatar } = useUserDataStore(); + const { setIsOpenImport } = useModelStore() + const [isLoading, setIsLoading] = useState(false) + const [Error, setError] = useState("") + const { freeSRData, setFreeSRData, selectedCharacters, setSelectedCharacters } = useFreeSRStore() + const transI18n = useTranslations("DataPage") + + const handleCharacterToggle = (character: CharacterInfoCardType) => { + if (selectedCharacters.some((selectedCharacter) => selectedCharacter.key === character.key)) { + setSelectedCharacters(selectedCharacters.filter((selectedCharacter) => selectedCharacter.key !== character.key)); + return; + } + setSelectedCharacters([...selectedCharacters, character]); + }; + + const clearSelection = () => { + setSelectedCharacters([]); + }; + + const selectAll = () => { + if (freeSRData) { + setSelectedCharacters(Object.values(freeSRData?.avatars).map((character) => { + const lightcone = freeSRData.lightcones.find((lightcone) => lightcone.equip_avatar === character.avatar_id) + const relics = freeSRData.relics.filter((relic) => relic.equip_avatar === character.avatar_id) + return { + key: character.avatar_id, + avatar_id: character.avatar_id, + rank: character.data.rank ?? 0, + level: character.level, + lightcone: { + level: lightcone?.level ?? 0, + rank: lightcone?.rank ?? 0, + item_id: lightcone?.item_id ?? "", + }, + relics: relics.map((relic) => ({ + level: relic.level, + relic_id: relic.relic_id, + relic_set_id: relic.relic_set_id, + })), + } as CharacterInfoCardType + })); + } + }; + + const handlerReadFile = (event: React.ChangeEvent) => { + setIsLoading(true) + const file = event.target.files?.[0]; + if (!file) { + setSelectedCharacters([]) + setFreeSRData(null) + setError(transI18n("pleaseSelectAFile")) + setIsLoading(false) + return + } + if (!file.name.endsWith(".json") || file.type !== "application/json") { + setSelectedCharacters([]) + setFreeSRData(null) + setError(transI18n("fileMustBeAValidJsonFile")) + setIsLoading(false) + return + } + + if (file) { + + const reader = new FileReader(); + reader.onload = (e) => { + try { + const data = JSON.parse(e.target?.result as string); + const parsed = freeSrJsonSchema.parse(data) + setFreeSRData(parsed) + setError("") + + setSelectedCharacters(Object.values(parsed?.avatars || {}).map((character) => { + const lightcone = parsed?.lightcones.find((lightcone) => lightcone.equip_avatar === character.avatar_id) + const relics = parsed?.relics.filter((relic) => relic.equip_avatar === character.avatar_id) + return { + key: character.avatar_id, + avatar_id: character.avatar_id, + rank: character.data.rank ?? 0, + level: character.level, + lightcone: { + level: lightcone?.level ?? 0, + rank: lightcone?.rank ?? 0, + item_id: lightcone?.item_id ?? "", + }, + relics: relics?.map((relic) => ({ + level: relic.level, + relic_id: relic.relic_id, + relic_set_id: relic.relic_set_id, + })) ?? [], + } as CharacterInfoCardType + })); + } catch { + + setSelectedCharacters([]) + setFreeSRData(null) + setError(transI18n("fileMustBeAValidJsonFile")) + } + }; + reader.readAsText(file); + setIsLoading(false) + } + setIsLoading(false) + }; + + const handleImport = () => { + if (selectedCharacters.length === 0) { + setError(transI18n("pleaseSelectAtLeastOneCharacter")); + return; + } + if (!freeSRData) { + setError(transI18n("noDataToImport")); + return; + } + setError(""); + + const listAvatars = { ...avatars } + const filterData = Object.values(freeSRData?.avatars || {}).filter((character) => selectedCharacters.some((selectedCharacter) => selectedCharacter.avatar_id === character.avatar_id)) + filterData.forEach((character) => { + const newAvatar = { ...listAvatars[character.avatar_id] } + if (Object.keys(newAvatar).length !== 0) { + newAvatar.level = character.level + newAvatar.promotion = character.promotion + newAvatar.data = { + rank: character.data.rank ?? 0, + skills: character.data.skills + } + const newProfile = converterOneFreeSRDataToAvatarStore(freeSRData, newAvatar.profileList.length, character.avatar_id) + if (newProfile) { + newAvatar.profileList.push(newProfile) + newAvatar.profileSelect = newAvatar.profileList.length - 1 + } + setAvatar(newAvatar) + } + + }) + setIsOpenImport(false) + toast.success(transI18n("importFreeSRDataSuccess")) + } + + return ( +
    +
    + {/* Header */} +
    +

    {transI18n("freeSRImport")}

    + +
    +
    + {transI18n("pickAFile")} + + +
    + + {selectedCharacters.length > 0 && ( + + )} +
    + {Error && ( +
    😭 {Error}
    + )} + + + {/* Selection Info */} +
    + + {transI18n("selectedCharacters")}: {selectedCharacters.length} + + + +
    +
    + {isLoading && ( +
    +
    +
    + )} + {/* Character Grid */} +
    + {Object.values(freeSRData?.avatars || {}).map((character) => { + const lightcone = freeSRData?.lightcones.find((lightcone) => lightcone.equip_avatar === character.avatar_id) + const relics = freeSRData?.relics.filter((relic) => relic.equip_avatar === character.avatar_id) + return ( + ({ + level: relic.level, + relic_id: relic.relic_id, + relic_set_id: relic.relic_set_id, + })) ?? [], + } as CharacterInfoCardType + } + selectedCharacters={selectedCharacters} + onCharacterToggle={handleCharacterToggle} + /> + ) + })} +
    +
    +
    + ) +} \ No newline at end of file diff --git a/src/components/lightconeBar/index.tsx b/src/components/lightconeBar/index.tsx new file mode 100644 index 0000000..bda9d2c --- /dev/null +++ b/src/components/lightconeBar/index.tsx @@ -0,0 +1,145 @@ +"use client" + +import { useMemo } from "react" +import Image from "next/image"; +import useLocaleStore from "@/stores/localeStore" +import LightconeCard from "../card/lightconeCard"; +import useUserDataStore from "@/stores/userDataStore"; +import useModelStore from "@/stores/modelStore"; +import { useTranslations } from "next-intl"; +import useCurrentDataStore from "@/stores/currentDataStore"; +import useDetailDataStore from "@/stores/detailDataStore"; +import { calcRarity, getLocaleName } from "@/helper"; + +export default function LightconeBar() { + const { locale } = useLocaleStore() + const { + avatarSelected, + mapLightconePathActive, + mapLightconeRankActive, + setMapLightconePathActive, + setMapLightconeRankActive, + lightconeSearch, + setLightconeSearch + } = useCurrentDataStore() + const { setAvatar, avatars } = useUserDataStore() + const { setIsOpenLightcone } = useModelStore() + const { mapLightCone, baseType } = useDetailDataStore() + const transI18n = useTranslations("DataPage") + + const listLightcone = useMemo(() => { + if (!mapLightCone || !locale) return [] + + let list = Object.values(mapLightCone) + + if (lightconeSearch) { + list = list.filter(item => getLocaleName(locale, item.Name).toLowerCase().includes(lightconeSearch.toLowerCase())) + } + + const allRankFalse = !Object.values(mapLightconeRankActive).some(v => v) + const allPathFalse = !Object.values(mapLightconePathActive).some(v => v) + + list = list.filter(item => + (allRankFalse || mapLightconeRankActive[item.Rarity]) && + (allPathFalse || mapLightconePathActive[item.BaseType]) + ) + + list.sort((a, b) => { + const r = calcRarity(b.Rarity) - calcRarity(a.Rarity) + if (r !== 0) return r + return b.ID - a.ID + }) + + return list + }, [mapLightCone, mapLightconePathActive, mapLightconeRankActive, lightconeSearch, locale]) + + + + return ( +
    +
    +

    + {transI18n("lightConeSetting")} +

    +
    +
    +
    +
    Search
    + setLightconeSearch(e.target.value)} + type="text" placeholder="LightCone Name" className="input input-accent mt-1 w-full" + /> +
    +
    +
    Filter
    +
    +
    + {Object.entries(baseType).filter(([key]) => key !== "").map(([key, value]) => ( +
    { + setMapLightconePathActive({ ...mapLightconePathActive, [key]: !mapLightconePathActive[key] }) + }} + className="h-9.5 w-9.5 md:h-12.5 md:w-12.5 hover:bg-gray-600 grid place-items-center rounded-md shadow-lg cursor-pointer" + style={{ + backgroundColor: mapLightconePathActive[key] ? "#374151" : "#6B7280" + }} + > + {key} +
    + ))} +
    + +
    + {Object.keys(mapLightconeRankActive).map((key, index) => ( +
    { + setMapLightconeRankActive({ ...mapLightconeRankActive, [key]: !mapLightconeRankActive[key] }) + }} + className="h-9.5 w-9.5 md:h-12.5 md:w-12.5 hover:bg-gray-600 grid place-items-center rounded-md shadow-lg cursor-pointer" + style={{ + backgroundColor: mapLightconeRankActive[key] ? "#374151" : "#6B7280" + }} + > +
    + {key}* +
    +
    + ))} +
    +
    + +
    +
    +
    + {listLightcone.map((item, index) => ( +
    { + if (avatarSelected) { + const avatar = avatars[avatarSelected?.ID?.toString()] + avatar.profileList[avatar.profileSelect].lightcone = { + level: 80, + item_id: item.ID, + rank: 1, + promotion: 6 + } + setAvatar({ ...avatar }) + setIsOpenLightcone(false) + } + }}> + +
    + ))} +
    +
    + ) +} \ No newline at end of file diff --git a/src/components/monsterBar/as.tsx b/src/components/monsterBar/as.tsx new file mode 100644 index 0000000..4551d98 --- /dev/null +++ b/src/components/monsterBar/as.tsx @@ -0,0 +1,417 @@ +"use client" +import { useEffect, useMemo } from "react"; +import SelectCustomText from "../select/customSelectText"; +import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper"; +import useLocaleStore from "@/stores/localeStore"; +import useUserDataStore from "@/stores/userDataStore"; +import Image from "next/image"; +import { MonsterStore } from "@/types"; +import { useTranslations } from "next-intl"; +import useDetailDataStore from "@/stores/detailDataStore"; + +export default function AsBar() { + const { locale } = useLocaleStore() + const { + as_config, + setAsConfig + } = useUserDataStore() + const { mapMonster, mapAS, damageType, hardLevelConfig, eliteConfig } = useDetailDataStore() + + const transI18n = useTranslations("DataPage") + + const challengeSelected = useMemo(() => { + return mapAS[as_config.event_id.toString()]?.Level.find((as) => as.ID === as_config.challenge_id) + }, [as_config, mapAS]) + + const eventSelected = useMemo(() => { + return mapAS[as_config.event_id.toString()] + }, [as_config, mapAS]) + + const buffList = useMemo(() => { + if (!eventSelected) return []; + + if (as_config.floor_side === "Upper" || as_config.floor_side === "Upper -> Lower") { + return eventSelected?.BuffList1 ?? []; + } + + if (as_config.floor_side === "Lower" || as_config.floor_side === "Lower -> Upper") { + return eventSelected?.BuffList2 ?? []; + } + return []; + }, [as_config.floor_side, eventSelected]); + + + useEffect(() => { + if (!challengeSelected || as_config.event_id === 0 || as_config.challenge_id === 0) return + const newBattleConfig = structuredClone(as_config) + newBattleConfig.cycle_count = challengeSelected.TurnLimit + + newBattleConfig.blessings = [] + if (as_config.buff_id !== 0) { + newBattleConfig.blessings.push({ + id: as_config.buff_id, + level: 1 + }) + } + + if (challengeSelected) { + challengeSelected.MazeBuff.map((item) => { + newBattleConfig.blessings.push({ + id: item.ID, + level: 1 + }) + }) + } + + newBattleConfig.monsters = [] + newBattleConfig.stage_id = 0 + if ((as_config.floor_side === "Upper" || as_config.floor_side === "Upper -> Lower") + && challengeSelected.EventList1.length > 0) { + newBattleConfig.stage_id = challengeSelected.EventList1[0].ID + for (const wave of challengeSelected.EventList1[0].MonsterList) { + const newWave: MonsterStore[] = [] + for (const value of Object.values(wave)) { + newWave.push({ + monster_id: value, + level: challengeSelected.EventList1[0].Level, + amount: 1, + }) + } + newBattleConfig.monsters.push(newWave) + } + } + if ((as_config.floor_side === "Lower" || as_config.floor_side === "Lower -> Upper") + && challengeSelected.EventList2.length > 0) { + newBattleConfig.stage_id = challengeSelected.EventList2[0].ID + for (const wave of challengeSelected.EventList2[0].MonsterList) { + const newWave: MonsterStore[] = [] + for (const value of Object.values(wave)) { + newWave.push({ + monster_id: value, + level: challengeSelected.EventList2[0].Level, + amount: 1, + }) + } + newBattleConfig.monsters.push(newWave) + } + } + if (as_config.floor_side === "Lower -> Upper" + && challengeSelected.EventList1.length > 0) { + for (const wave of challengeSelected.EventList1[0].MonsterList) { + const newWave: MonsterStore[] = [] + for (const value of Object.values(wave)) { + newWave.push({ + monster_id: value, + level: challengeSelected.EventList1[0].Level, + amount: 1, + }) + } + newBattleConfig.monsters.push(newWave) + } + } else if (as_config.floor_side === "Upper -> Lower" + && challengeSelected.EventList2.length > 0) { + for (const wave of challengeSelected.EventList2[0].MonsterList) { + const newWave: MonsterStore[] = [] + for (const value of Object.values(wave)) { + newWave.push({ + monster_id: value, + level: challengeSelected.EventList2[0].Level, + amount: 1, + }) + } + newBattleConfig.monsters.push(newWave) + } + } + setAsConfig(newBattleConfig) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + challengeSelected, + mapAS, + as_config.event_id, + as_config.challenge_id, + as_config.floor_side, + as_config.buff_id, + ]) + + if (!mapAS) return null + return ( +
    + + {/* Title Card */} +
    +
    + b.ID - a.ID).map((as) => ({ + id: as.ID.toString(), + name: getLocaleName(locale, as.Name), + time: `${as.BeginTime} - ${as.EndTime}`, + }))} + excludeSet={[]} + selectedCustomSet={as_config.event_id.toString()} + placeholder={transI18n("selectASEvent")} + setSelectedCustomSet={(id) => setAsConfig({ + ...as_config, + event_id: Number(id), + challenge_id: mapAS[Number(id)]?.Level.at(-1)?.ID || 0, + buff_id: 0 + })} + /> +
    + {/* Settings */} +
    + +
    + + +
    + +
    + + +
    +
    +
    StageId: {as_config?.stage_id}
    + {eventSelected && ( +
    + ({ + id: buff.ID?.toString() || "", + name: getLocaleName(locale, buff?.Name) || "", + description: replaceByParam(getLocaleName(locale, buff?.Desc) || "", buff?.Param || []), + }))} + excludeSet={[]} + selectedCustomSet={as_config?.buff_id?.toString()} + placeholder={transI18n("selectBuff")} + setSelectedCustomSet={(id) => setAsConfig({ ...as_config, buff_id: Number(id) })} + /> +
    + )} + {/* Turbulence Buff */} +
    +

    {transI18n("turbulenceBuff")}

    + {challengeSelected ? ( + challengeSelected.MazeBuff.map((buff, i) => ( +
    + )) + ) : ( +
    {transI18n("noTurbulenceBuff")}
    + )} +
    +
    + + {/* Enemy Waves */} + {(as_config?.challenge_id ?? 0) !== 0 && ( +
    + {/* First Half */} +
    +

    {transI18n("firstHalfEnemies")}

    + + {challengeSelected && challengeSelected?.EventList1?.length > 0 && challengeSelected?.EventList1?.[0]?.MonsterList?.map((wave, waveIndex) => ( +
    +

    {transI18n("wave")} {waveIndex + 1}

    +
    + {Object.values(wave).map((waveValue, enemyIndex) => { + const monsterStats = calcMonsterStats( + mapMonster?.[waveValue.toString()], + challengeSelected?.EventList1?.[0]?.EliteGroup, + challengeSelected?.EventList1?.[0]?.HardLevelGroup, + challengeSelected?.EventList1?.[0]?.Level, + hardLevelConfig, + eliteConfig + ); + return ( +
    +
    + Lv. {challengeSelected?.EventList1[0].Level} +
    + +
    + {mapMonster?.[waveValue.toString()]?.Image?.IconPath && ( +
    + Enemy Icon +
    + )} +
    + +
    +
    +
    + HP + {monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Speed + {monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Toughness + {monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    +
    + +
    + + Weakness + +
    + {mapMonster?.[waveValue.toString()]?.StanceWeakList?.map((icon, iconIndex) => ( + {icon} + ))} +
    +
    +
    +
    + ) + })} +
    +
    + ))} +
    + + {/* Second Half */} +
    +

    {transI18n("secondHalfEnemies")}

    + + {challengeSelected && challengeSelected?.EventList2?.length > 0 && challengeSelected?.EventList2?.[0]?.MonsterList?.map((wave, waveIndex) => ( +
    +

    {transI18n("wave")} {waveIndex + 1}

    +
    + {Object.values(wave).map((waveValue, enemyIndex) => { + const monsterStats = calcMonsterStats( + mapMonster?.[waveValue.toString()], + challengeSelected?.EventList2?.[0]?.EliteGroup, + challengeSelected?.EventList2?.[0]?.HardLevelGroup, + challengeSelected?.EventList2?.[0]?.Level, + hardLevelConfig, + eliteConfig + ); + return ( +
    +
    + Lv. {challengeSelected?.EventList2[0].Level} +
    + +
    + {mapMonster?.[waveValue.toString()]?.Image?.IconPath && ( +
    + Enemy Icon +
    + )} +
    + +
    +
    +
    + HP + {monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Speed + {monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Toughness + {monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    +
    + +
    + + Weakness + +
    + {mapMonster?.[waveValue.toString()]?.StanceWeakList?.map((icon, iconIndex) => ( + {icon} + ))} +
    +
    +
    +
    + ) + })} +
    +
    + ))} +
    +
    + )} + +
    + ) +} \ No newline at end of file diff --git a/src/components/monsterBar/ce.tsx b/src/components/monsterBar/ce.tsx new file mode 100644 index 0000000..e900891 --- /dev/null +++ b/src/components/monsterBar/ce.tsx @@ -0,0 +1,533 @@ +"use client"; +import { useEffect, useMemo, useState } from "react"; +import { + Plus, + Trash2, + ChevronUp, + ChevronDown, + Search, + CopyPlus, +} from "lucide-react"; + +import useUserDataStore from "@/stores/userDataStore"; +import useLocaleStore from "@/stores/localeStore"; +import { getLocaleName } from "@/helper"; +import Image from "next/image"; +import { useTranslations } from "next-intl"; +import useGlobalStore from "@/stores/globalStore"; +import useDetailDataStore from "@/stores/detailDataStore"; +import { MonsterDetail } from "@/types"; + + +export default function CeBar() { + const [searchTerm, setSearchTerm] = useState(""); + const [showSearchWaveId, setShowSearchWaveId] = useState(null); + const { ce_config, setCeConfig } = useUserDataStore() + const { mapMonster, stage, damageType } = useDetailDataStore() + const { locale } = useLocaleStore() + const transI18n = useTranslations("DataPage") + const [showSearchStage, setShowSearchStage] = useState(false) + const [stageSearchTerm, setStageSearchTerm] = useState("") + const [stagePage, setStagePage] = useState(1) + const { extraData, setExtraData } = useGlobalStore() + + const pageSize = 30 + + const pageSizeMonsters = 30 + const [monsterPage, setMonsterPage] = useState(1) + + const filteredMonsters = useMemo(() => { + const newlistMonster = new Set() + for (const monster of Object.values(mapMonster)) { + if (getLocaleName(locale, monster.Name).toLowerCase().includes(searchTerm.toLowerCase())) { + newlistMonster.add(monster) + } + if (monster.ID.toString().includes(searchTerm.toLowerCase())) { + newlistMonster.add(monster) + } + } + return Array.from(newlistMonster) + }, [locale, searchTerm, mapMonster]); + + const paginatedMonsters = useMemo(() => + filteredMonsters.slice((monsterPage - 1) * pageSizeMonsters, monsterPage * pageSizeMonsters), + [filteredMonsters, monsterPage] + ) + + const hasMoreMonsterPages = useMemo( + () => monsterPage * pageSizeMonsters < filteredMonsters.length, + [monsterPage, filteredMonsters] + ) + + useEffect(() => { + setMonsterPage(1) + }, [searchTerm]) + + const stageList = useMemo(() => Object.values(stage).map((item) => ({ + id: item.ID.toString(), + name: `${getLocaleName(locale, item.Name)} (${item.ID})`, + })), [stage, locale]) + + const filteredStages = useMemo(() => stageList.filter((s) => + s.name.toLowerCase().includes(stageSearchTerm.toLowerCase()) + ), [stageList, stageSearchTerm]) + + const paginatedStages = useMemo(() => filteredStages.slice( + (stagePage - 1) * pageSize, + stagePage * pageSize + ), [filteredStages, stagePage, pageSize]) + + const hasMorePages = useMemo(() => stagePage * pageSize < filteredStages.length, [stagePage, filteredStages]) + + useEffect(() => { + setStagePage(1) + }, [stageSearchTerm]) + + + useEffect(() => { + if (!ce_config) return + if (!extraData || !extraData.theory_craft?.mode) return + + const newExtraData = structuredClone(extraData) + if (!newExtraData?.theory_craft?.hp) { + newExtraData.theory_craft!.hp = {} + } + + for (let i = 0; i < ce_config.monsters.length; i++) { + const waveKey = (i + 1).toString() + if (!newExtraData.theory_craft!.hp[waveKey]) { + newExtraData.theory_craft!.hp[waveKey] = [] + } + for (let j = 0; j < ce_config.monsters[i].length; j++) { + if (newExtraData.theory_craft!.hp[waveKey][j] === undefined) { + newExtraData.theory_craft!.hp[waveKey][j] = 0 + } + } + } + setExtraData(newExtraData) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ce_config]) + + + return ( +
    { + + setShowSearchWaveId(null) + setShowSearchStage(false) + }}> + +
    +
    + +
    + {showSearchStage && ( +
    e.stopPropagation()} className="absolute top-full mt-2 w-full z-50 border bg-base-200 border-slate-600 rounded-lg p-4 shadow-lg"> +
    + + +
    + +
    + {paginatedStages.length > 0 ? ( + <> + {paginatedStages.map((stage) => ( +
    { + if (ce_config.stage_id !== Number(stage.id)) { + setCeConfig({ ...ce_config, stage_id: Number(stage.id), cycle_count: 30 }) + } + setShowSearchStage(false) + setStageSearchTerm("") + }} + > + {stage.name} +
    + ))} + + + + ) : ( +
    {transI18n("noStageFound")}
    + )} +
    + {paginatedStages.length > 0 && ( +
    + + + +
    + )} +
    + )} +
    + +
    + {ce_config.monsters.map((wave, waveIndex) => ( +
    +
    +
    +

    {transI18n("wave")} {waveIndex + 1}

    +
    + + + + + + + +
    +
    + +
    + {wave.map((member, memberIndex) => ( +
    +
    +
    + + + +
    + {mapMonster?.[member.monster_id.toString()]?.Image?.IconPath && Enemy Icon} +
    + +
    + {mapMonster?.[member.monster_id.toString()]?.StanceWeakList?.map((icon, iconIndex) => ( + {icon} + ))} +
    +
    +
    + {getLocaleName(locale, mapMonster?.[member.monster_id.toString()]?.Name)} {`(${member.monster_id})`} +
    +
    + LV. + { + const val = Number(e.target.value) + if (isNaN(val) || val < 1 || val > 95) return + if (ce_config.monsters[waveIndex][memberIndex].level === val) return + + const newCeConfig = structuredClone(ce_config) + newCeConfig.monsters[waveIndex][memberIndex].level = val + setCeConfig(newCeConfig) + }} + /> +
    + {(extraData?.theory_craft?.mode === true && ( +
    + HP + { + const val = Number(e.target.value) + if (isNaN(val) || val < 0) return + + const newData = structuredClone(extraData) + + if (!newData?.theory_craft?.hp?.[(waveIndex + 1).toString()]) { + newData.theory_craft!.hp![(waveIndex + 1).toString()] = [] + } + + newData.theory_craft!.hp![(waveIndex + 1).toString()][memberIndex] = val + + setExtraData(newData) + }} + /> +
    + ))} +
    + +
    +
    +
    + ))} + + {/* Add Member Button + Search */} +
    e.stopPropagation()} + > + + + {showSearchWaveId === waveIndex && ( +
    +
    + + + +
    + +
    + {paginatedMonsters.length > 0 ? ( + paginatedMonsters.map((monster) => ( +
    { + const newCeConfig = structuredClone(ce_config) + newCeConfig.monsters[waveIndex].push({ + monster_id: monster.ID, + level: 95, + amount: 1, + }) + setCeConfig(newCeConfig) + setShowSearchWaveId(null) + }} + > +
    + {mapMonster?.[monster.ID.toString()]?.Image?.IconPath && ( + Enemy Icon + )} +
    + {getLocaleName(locale, monster.Name)} {`(${monster.ID})`} +
    + )) + ) : ( +
    + {transI18n("noMonstersFound")} +
    + )} +
    + + {filteredMonsters.length > 0 && ( +
    + + + +
    + )} + +
    + )} +
    +
    +
    +
    + ))} + + {/* Add New Wave Button */} +
    +
    + +
    +
    +
    +
    + ); +} diff --git a/src/components/monsterBar/index.tsx b/src/components/monsterBar/index.tsx new file mode 100644 index 0000000..2b2d343 --- /dev/null +++ b/src/components/monsterBar/index.tsx @@ -0,0 +1,100 @@ +import MocBar from "./moc"; +import useUserDataStore from "@/stores/userDataStore"; +import PfBar from "./pf"; +import Image from "next/image"; +import AsBar from "./as"; +import { useTranslations } from "next-intl"; +import CeBar from "./ce"; +import PeakBar from "./peak"; + +export default function MonsterBar() { + const { battle_type, setBattleType } = useUserDataStore() + const transI18n = useTranslations("DataPage") + + const navItems = [ + { name: transI18n("memoryOfChaos"), icon: 'AbyssIcon01', value: 'MOC' }, + { name: transI18n("pureFiction"), icon: 'ChallengeStory', value: 'PF' }, + { name: transI18n("apocalypticShadow"), icon: 'ChallengeBoss', value: 'AS' }, + { name: transI18n("anomalyArbitration"), icon: 'ChallengePeakIcon', value: 'PEAK' }, + { name: transI18n("customEnemy"), icon: 'MonsterIcon', value: 'CE' }, + { name: transI18n("simulatedUniverse"), icon: 'SimulatedUniverse', value: 'SU' }, + + ]; + + + return ( +
    + {/* Header Navigation */} + + + {(battle_type.toUpperCase() === "DEFAULT" || battle_type.toUpperCase() === "") && ( +
    + {transI18n("noEventSelected")} +
    + )} + {battle_type.toUpperCase() === 'MOC' && } + {battle_type.toUpperCase() === 'PF' && } + {battle_type.toUpperCase() === 'AS' && } + {battle_type.toUpperCase() === 'CE' && } + {battle_type.toUpperCase() === 'PEAK' && } + {battle_type.toUpperCase() === 'SU' && ( +
    + {transI18n("comingSoon")} +
    + )} + +
    + ) +} \ No newline at end of file diff --git a/src/components/monsterBar/moc.tsx b/src/components/monsterBar/moc.tsx new file mode 100644 index 0000000..8901555 --- /dev/null +++ b/src/components/monsterBar/moc.tsx @@ -0,0 +1,416 @@ +"use client" + +import { useEffect, useMemo } from "react"; +import SelectCustomText from "../select/customSelectText"; +import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper"; +import useLocaleStore from "@/stores/localeStore"; +import useUserDataStore from "@/stores/userDataStore"; +import Image from "next/image"; +import { useTranslations } from "next-intl"; +import { MonsterStore } from "@/types"; +import useDetailDataStore from "@/stores/detailDataStore"; + +export default function MocBar() { + const { locale } = useLocaleStore() + const { + moc_config, + setMocConfig + } = useUserDataStore() + const { mapMonster, mapMoc, damageType, hardLevelConfig, eliteConfig } = useDetailDataStore() + + const transI18n = useTranslations("DataPage") + + const challengeSelected = useMemo(() => { + return mapMoc[moc_config.event_id.toString()]?.Level.find((moc) => moc.ID === moc_config.challenge_id) + }, [moc_config, mapMoc]) + + const eventSelected = useMemo(() => { + return mapMoc[moc_config.event_id.toString()] + }, [moc_config, mapMoc]) + + useEffect(() => { + if (!challengeSelected || moc_config.event_id === 0 || moc_config.challenge_id === 0) return + + const newBattleConfig = structuredClone(moc_config) + newBattleConfig.cycle_count = 0 + if (moc_config.use_cycle_count) { + newBattleConfig.cycle_count = challengeSelected.TurnLimit + } + newBattleConfig.blessings = [] + if (moc_config.use_turbulence_buff && challengeSelected) { + challengeSelected.MazeBuff.map((item) => { + newBattleConfig.blessings.push({ + id: item.ID, + level: 1 + }) + }) + } + newBattleConfig.monsters = [] + newBattleConfig.stage_id = 0 + if ((moc_config.floor_side === "Upper" || moc_config.floor_side === "Upper -> Lower") && challengeSelected.EventList1.length > 0) { + newBattleConfig.stage_id = challengeSelected.EventList1[0].ID + for (const wave of challengeSelected.EventList1[0].MonsterList) { + const newWave: MonsterStore[] = [] + for (const value of Object.values(wave)) { + newWave.push({ + monster_id: value, + level: challengeSelected.EventList1[0].Level, + amount: 1, + }) + } + newBattleConfig.monsters.push(newWave) + } + } + if ((moc_config.floor_side === "Lower" || moc_config.floor_side === "Lower -> Upper") && challengeSelected.EventList2.length > 0) { + newBattleConfig.stage_id = challengeSelected.EventList2[0].ID + for (const wave of challengeSelected.EventList2[0].MonsterList) { + const newWave: MonsterStore[] = [] + for (const value of Object.values(wave)) { + newWave.push({ + monster_id: value, + level: challengeSelected.EventList2[0].Level, + amount: 1, + }) + } + newBattleConfig.monsters.push(newWave) + } + } + if (moc_config.floor_side === "Lower -> Upper" && challengeSelected.EventList1.length > 0) { + for (const wave of challengeSelected.EventList1[0].MonsterList) { + const newWave: MonsterStore[] = [] + for (const value of Object.values(wave)) { + newWave.push({ + monster_id: value, + level: challengeSelected.EventList1[0].Level, + amount: 1, + }) + } + newBattleConfig.monsters.push(newWave) + } + } else if (moc_config.floor_side === "Upper -> Lower" && challengeSelected.EventList2.length > 0) { + for (const wave of challengeSelected.EventList2[0].MonsterList) { + const newWave: MonsterStore[] = [] + for (const value of Object.values(wave)) { + newWave.push({ + monster_id: value, + level: challengeSelected.EventList2[0].Level, + amount: 1, + }) + } + newBattleConfig.monsters.push(newWave) + } + } + setMocConfig(newBattleConfig) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + moc_config.event_id, + moc_config.challenge_id, + moc_config.floor_side, + moc_config.use_cycle_count, + moc_config.use_turbulence_buff, + mapMoc, + ]) + + if (!mapMoc) return null + + return ( +
    + + {/* Title Card */} +
    +
    + b.ID - a.ID).map((moc) => ({ + id: moc.ID.toString(), + name: getLocaleName(locale, moc.Name), + time: `${moc.BeginTime} - ${moc.EndTime}`, + }))} + excludeSet={[]} + selectedCustomSet={moc_config.event_id.toString()} + placeholder={transI18n("selectMOCEvent")} + setSelectedCustomSet={(id) => setMocConfig({ + ...moc_config, + event_id: Number(id), + challenge_id: mapMoc[Number(id)]?.Level.at(-1)?.ID || 0, + })} + /> +
    + {/* Settings */} + +
    + +
    + + +
    + +
    + + +
    + + + +
    + +
    +
    +
    StageId: {moc_config?.stage_id}
    + + {/* Turbulence Buff */} +
    +
    + setMocConfig({ ...moc_config, use_turbulence_buff: e.target.checked })} + className="checkbox checkbox-primary" + /> + setMocConfig({ ...moc_config, use_turbulence_buff: !moc_config.use_turbulence_buff })} + className="font-bold text-success cursor-pointer"> + {transI18n("useTurbulenceBuff")} + +
    + {challengeSelected ? ( + challengeSelected.MazeBuff.map((buff, i) => ( +
    + )) + ) : ( +
    {transI18n("noTurbulenceBuff")}
    + )} +
    +
    + + {/* Enemy Waves */} + {(moc_config?.challenge_id ?? 0) !== 0 && ( +
    + {/* First Half */} +
    +

    {transI18n("firstHalfEnemies")}

    + + {challengeSelected && challengeSelected?.EventList1?.length > 0 && challengeSelected?.EventList1?.[0]?.MonsterList?.map((wave, waveIndex) => ( +
    +

    {transI18n("wave")} {waveIndex + 1}

    +
    + {Object.values(wave).map((waveValue, enemyIndex) => { + const monsterStats = calcMonsterStats( + mapMonster?.[waveValue.toString()], + challengeSelected?.EventList1?.[0]?.EliteGroup, + challengeSelected?.EventList1?.[0]?.HardLevelGroup, + challengeSelected?.EventList1?.[0]?.Level, + hardLevelConfig, + eliteConfig + ); + return ( +
    +
    + Lv. {challengeSelected?.EventList1[0].Level} +
    + +
    + {mapMonster?.[waveValue.toString()]?.Image?.IconPath && ( +
    + Enemy Icon +
    + )} +
    + +
    +
    +
    + HP + {monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Speed + {monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Toughness + {monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    +
    + +
    + + Weakness + +
    + {mapMonster?.[waveValue.toString()]?.StanceWeakList?.map((icon, iconIndex) => ( + {icon} + ))} +
    +
    +
    +
    + ) + })} +
    +
    + ))} +
    + + {/* Second Half */} +
    +

    {transI18n("secondHalfEnemies")}

    + + {challengeSelected && challengeSelected?.EventList2?.length > 0 && challengeSelected?.EventList2?.[0]?.MonsterList?.map((wave, waveIndex) => ( +
    +

    {transI18n("wave")} {waveIndex + 1}

    +
    + {Object.values(wave).map((waveValue, enemyIndex) => { + const monsterStats = calcMonsterStats( + mapMonster?.[waveValue.toString()], + challengeSelected?.EventList2?.[0]?.EliteGroup, + challengeSelected?.EventList2?.[0]?.HardLevelGroup, + challengeSelected?.EventList2?.[0]?.Level, + hardLevelConfig, + eliteConfig + ); + return ( +
    +
    + Lv. {challengeSelected?.EventList2[0].Level} +
    + +
    + {mapMonster?.[waveValue.toString()]?.Image?.IconPath && ( +
    + Enemy Icon +
    + )} +
    + +
    +
    +
    + HP + {monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Speed + {monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Toughness + {monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    +
    + +
    + + Weakness + +
    + {mapMonster?.[waveValue.toString()]?.StanceWeakList?.map((icon, iconIndex) => ( + {icon} + ))} +
    +
    +
    +
    + ) + })} +
    +
    + ))} +
    +
    + )} + +
    + ) +} \ No newline at end of file diff --git a/src/components/monsterBar/peak.tsx b/src/components/monsterBar/peak.tsx new file mode 100644 index 0000000..090d620 --- /dev/null +++ b/src/components/monsterBar/peak.tsx @@ -0,0 +1,308 @@ +"use client" +import { useEffect, useMemo } from "react"; +import SelectCustomText from "../select/customSelectText"; +import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper"; +import useLocaleStore from "@/stores/localeStore"; +import useUserDataStore from "@/stores/userDataStore";; +import Image from "next/image"; +import { useTranslations } from "next-intl"; +import { MonsterStore } from "@/types"; +import useDetailDataStore from "@/stores/detailDataStore"; + +export default function PeakBar() { + const { locale } = useLocaleStore() + const { + peak_config, + setPeakConfig + } = useUserDataStore() + const { mapMonster, mapPeak, damageType, eliteConfig, hardLevelConfig } = useDetailDataStore() + const transI18n = useTranslations("DataPage") + + const listFloor = useMemo(() => { + const peak = mapPeak?.[peak_config?.event_id?.toString()] + if (!peak) return [] + + return [...peak.PreLevel, peak.BossLevel].filter(it => it != null) + }, [peak_config, mapPeak]) + const eventSelected = useMemo(() => { + return mapPeak?.[peak_config?.event_id?.toString()] + }, [peak_config, mapPeak]) + + const bossConfig = useMemo(() => { + return mapPeak?.[peak_config?.event_id?.toString()]?.BossConfig; + }, [peak_config, mapPeak]) + + const challengeSelected = useMemo(() => { + const challenge = structuredClone(listFloor?.find((peak) => peak?.ID === peak_config.challenge_id)) + if ( + challenge + && challenge.ID === mapPeak?.[peak_config?.event_id?.toString()]?.BossLevel?.ID + && bossConfig + && peak_config?.boss_mode === "Hard" + ) { + return { challenge: bossConfig, isBoss: true } + } + return { + challenge: challenge, + isBoss: challenge?.ID === mapPeak?.[peak_config?.event_id?.toString()]?.BossLevel?.ID, + } + }, [peak_config, listFloor, mapPeak, bossConfig]) + + useEffect(() => { + if (!challengeSelected.challenge) return + if (peak_config.event_id !== 0 && peak_config.challenge_id !== 0 && challengeSelected) { + const newBattleConfig = structuredClone(peak_config) + newBattleConfig.cycle_count = 6 + newBattleConfig.blessings = [] + for (const value of challengeSelected?.challenge?.MazeBuff) { + newBattleConfig.blessings.push({ + id: value.ID, + level: 1 + }) + } + if (peak_config.buff_id !== 0 && challengeSelected.isBoss) { + newBattleConfig.blessings.push({ + id: peak_config.buff_id, + level: 1 + }) + } + newBattleConfig.monsters = [] + newBattleConfig.stage_id = challengeSelected.challenge.EventList[0].ID + for (const wave of challengeSelected.challenge.EventList[0].MonsterList) { + if (!wave) continue + const newWave: MonsterStore[] = [] + for (const value of Object.values(wave)) { + if (!value) continue + newWave.push({ + monster_id: value, + level: challengeSelected.challenge.EventList[0].Level, + amount: 1, + }) + } + newBattleConfig.monsters.push(newWave) + } + + setPeakConfig(newBattleConfig) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + peak_config.event_id, + peak_config.challenge_id, + peak_config.buff_id, + peak_config.boss_mode, + mapPeak, + ]) + + if (!mapPeak) return null + + return ( +
    + + {/* Title Card */} +
    +
    + b.ID - a.ID).map((peak) => ({ + id: peak.ID.toString(), + name: `${getLocaleName(locale, peak.Name)} (${peak.ID})`, + }))} + excludeSet={[]} + selectedCustomSet={peak_config.event_id.toString()} + placeholder={transI18n("selectPEAKEvent")} + setSelectedCustomSet={(id) => setPeakConfig({ ...peak_config, event_id: Number(id), challenge_id: 0, buff_id: 0 })} + /> +
    + {/* Settings */} +
    + +
    + + +
    + {eventSelected && eventSelected.BossLevel?.ID === peak_config.challenge_id && ( +
    + + +
    + )} + +
    +
    StageId: {peak_config?.stage_id}
    + { + eventSelected + && eventSelected.BossLevel?.ID === peak_config.challenge_id + && bossConfig + && ( +
    + ({ + id: buff.ID.toString(), + name: getLocaleName(locale, buff?.Name || ""), + description: replaceByParam(getLocaleName(locale, buff?.Desc || ""), buff?.Param || []), + })) + } + excludeSet={[]} + selectedCustomSet={peak_config?.buff_id?.toString()} + placeholder={transI18n("selectBuff")} + setSelectedCustomSet={(id) => setPeakConfig({ ...peak_config, buff_id: Number(id) })} + /> +
    + ) + } + + {/* Turbulence Buff */} + +
    +

    + {transI18n("turbulenceBuff")} +

    + + {challengeSelected?.challenge && challengeSelected?.challenge?.MazeBuff?.length > 0 ? ( + challengeSelected.challenge.MazeBuff.map((subOption, index) => ( +
    + +
    +
    + )) + ) : ( +
    {transI18n("noTurbulenceBuff")}
    + )} +
    +
    + + {/* Enemy Waves */} + + {(peak_config?.challenge_id ?? 0) !== 0 && ( +
    + +
    +

    {getLocaleName(locale, challengeSelected?.challenge?.Name)}

    + + + {challengeSelected?.challenge && Object.values(challengeSelected?.challenge?.EventList?.[0]?.Infinite || []).map((waveValue, waveIndex) => ( +
    +

    {transI18n("wave")} {waveIndex + 1}

    +
    + {Array.from(new Set(waveValue.MonsterList)).map((monsterId, enemyIndex) => { + const monsterStats = calcMonsterStats( + mapMonster?.[monsterId.toString()], + waveValue.EliteGroup, + challengeSelected?.challenge?.EventList?.[0]?.HardLevelGroup || 0, + challengeSelected?.challenge?.EventList?.[0]?.Level || 0, + hardLevelConfig, + eliteConfig + ); + return ( +
    +
    + Lv. {challengeSelected?.challenge?.EventList?.[0]?.Level} +
    + +
    + {mapMonster?.[monsterId.toString()]?.Image?.IconPath && ( +
    + Enemy Icon +
    + )} +
    + +
    +
    +
    + HP + {monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Speed + {monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Toughness + {monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    +
    + +
    + + Weakness + +
    + {mapMonster?.[monsterId.toString()]?.StanceWeakList?.map((icon, iconIndex) => ( + {icon} + ))} +
    +
    +
    +
    + ) + })} +
    +
    + ))} +
    +
    + )} + +
    + ) +} \ No newline at end of file diff --git a/src/components/monsterBar/pf.tsx b/src/components/monsterBar/pf.tsx new file mode 100644 index 0000000..63317a3 --- /dev/null +++ b/src/components/monsterBar/pf.tsx @@ -0,0 +1,416 @@ +"use client" +import { useEffect, useMemo } from "react"; +import SelectCustomText from "../select/customSelectText"; +import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper"; +import useLocaleStore from "@/stores/localeStore"; +import useUserDataStore from "@/stores/userDataStore"; +import Image from "next/image"; +import { MonsterStore } from "@/types"; +import { useTranslations } from "next-intl"; +import useDetailDataStore from "@/stores/detailDataStore"; + +export default function PfBar() { + const { locale } = useLocaleStore() + const { + pf_config, + setPfConfig + } = useUserDataStore() + const { mapMonster, mapPF, damageType, hardLevelConfig, eliteConfig } = useDetailDataStore() + + const transI18n = useTranslations("DataPage") + const challengeSelected = useMemo(() => { + return mapPF[pf_config.event_id.toString()]?.Level.find((pf) => pf.ID === pf_config.challenge_id) + }, [pf_config, mapPF]) + + const eventSelected = useMemo(() => { + return mapPF[pf_config.event_id.toString()] + }, [pf_config, mapPF]) + + + useEffect(() => { + if (!challengeSelected || pf_config.event_id === 0 || pf_config.challenge_id === 0) { + return + } + const newBattleConfig = structuredClone(pf_config) + newBattleConfig.cycle_count = challengeSelected.TurnLimit + newBattleConfig.blessings = [] + if (pf_config.buff_id !== 0) { + newBattleConfig.blessings.push({ + id: pf_config.buff_id, + level: 1 + }) + } + if (challengeSelected) { + challengeSelected.MazeBuff.map((item) => { + newBattleConfig.blessings.push({ + id: item.ID, + level: 1 + }) + }) + } + newBattleConfig.monsters = [] + newBattleConfig.stage_id = 0 + if ((pf_config.floor_side === "Upper" || pf_config.floor_side === "Upper -> Lower") && challengeSelected.EventList1.length > 0) { + newBattleConfig.stage_id = challengeSelected.EventList1[0].ID + for (const wave of challengeSelected.EventList1[0].MonsterList) { + const newWave: MonsterStore[] = [] + for (const value of Object.values(wave)) { + newWave.push({ + monster_id: value, + level: challengeSelected.EventList1[0].Level, + amount: 1, + }) + } + newBattleConfig.monsters.push(newWave) + } + } + if ((pf_config.floor_side === "Lower" || pf_config.floor_side === "Lower -> Upper") && challengeSelected.EventList2.length > 0) { + newBattleConfig.stage_id = challengeSelected.EventList2[0].ID + for (const wave of challengeSelected.EventList2[0].MonsterList) { + const newWave: MonsterStore[] = [] + for (const value of Object.values(wave)) { + newWave.push({ + monster_id: value, + level: challengeSelected.EventList2[0].Level, + amount: 1, + }) + } + newBattleConfig.monsters.push(newWave) + } + } + if (pf_config.floor_side === "Lower -> Upper" && challengeSelected.EventList1.length > 0) { + for (const wave of challengeSelected.EventList1[0].MonsterList) { + const newWave: MonsterStore[] = [] + for (const value of Object.values(wave)) { + newWave.push({ + monster_id: value, + level: challengeSelected.EventList1[0].Level, + amount: 1, + }) + } + newBattleConfig.monsters.push(newWave) + } + } else if (pf_config.floor_side === "Upper -> Lower" && challengeSelected.EventList2.length > 0) { + for (const wave of challengeSelected.EventList2[0].MonsterList) { + const newWave: MonsterStore[] = [] + for (const value of Object.values(wave)) { + newWave.push({ + monster_id: value, + level: challengeSelected.EventList2[0].Level, + amount: 1, + }) + } + newBattleConfig.monsters.push(newWave) + } + } + setPfConfig(newBattleConfig) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + challengeSelected, + pf_config.event_id, + pf_config.challenge_id, + pf_config.floor_side, + pf_config.buff_id, + mapPF, + ]) + if (!mapPF) return null + + return ( +
    + + {/* Title Card */} +
    +
    + b.ID - a.ID).map((pf) => ({ + id: pf.ID.toString(), + name: getLocaleName(locale, pf.Name), + time: `${pf.BeginTime} - ${pf.EndTime}`, + }))} + excludeSet={[]} + selectedCustomSet={pf_config.event_id.toString()} + placeholder={transI18n("selectPFEvent")} + setSelectedCustomSet={(id) => setPfConfig({ + ...pf_config, + event_id: Number(id), + challenge_id: mapPF[Number(id)]?.Level.slice(-1)[0]?.ID || 0, + buff_id: 0 + })} + /> +
    + {/* Settings */} +
    + +
    + + +
    + +
    + + +
    +
    +
    StageId: {pf_config?.stage_id}
    + {eventSelected && ( +
    + ({ + id: buff.ID?.toString() || "", + name: getLocaleName(locale, buff?.Name) || "", + description: replaceByParam(getLocaleName(locale, buff?.Desc) || "", buff?.Param || []), + }))} + excludeSet={[]} + selectedCustomSet={pf_config?.buff_id?.toString()} + placeholder={transI18n("selectBuff")} + setSelectedCustomSet={(id) => setPfConfig({ ...pf_config, buff_id: Number(id) })} + /> +
    + )} + {/* Turbulence Buff */} +
    +

    {transI18n("turbulenceBuff")}

    + {eventSelected && eventSelected.SubOption.length > 0 ? ( + eventSelected.SubOption.map((subOption, index) => ( +
    + +
    +
    + )) + ) : eventSelected && challengeSelected && eventSelected.SubOption.length === 0 ? ( + challengeSelected?.MazeBuff?.map((buff, i) => ( +
    + )) + ) : ( +
    {transI18n("noTurbulenceBuff")}
    + )} +
    +
    + + {/* Enemy Waves */} + + {(pf_config?.challenge_id ?? 0) !== 0 && ( +
    + {/* First Half */} +
    +

    {transI18n("firstHalfEnemies")}

    + + {challengeSelected && Object.values(challengeSelected.EventList1?.[0]?.Infinite || []).map((waveValue, waveIndex) => ( +
    +

    {transI18n("wave")} {waveIndex + 1}

    +
    + {Array.from(new Set(waveValue.MonsterList)).map((monsterId, enemyIndex) => { + const monsterStats = calcMonsterStats( + mapMonster?.[monsterId.toString()], + waveValue.EliteGroup, + challengeSelected?.EventList1?.[0]?.HardLevelGroup, + challengeSelected?.EventList1?.[0]?.Level, + hardLevelConfig, + eliteConfig + ); + return ( +
    +
    + Lv. {challengeSelected?.EventList1[0].Level} +
    + +
    + {mapMonster?.[monsterId.toString()]?.Image?.IconPath && ( +
    + Enemy Icon +
    + )} +
    + +
    +
    +
    + HP + {monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Speed + {monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Toughness + {monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    +
    + +
    + + Weakness + +
    + {mapMonster?.[monsterId.toString()]?.StanceWeakList?.map((icon, iconIndex) => ( + {icon} + ))} +
    +
    +
    +
    + ) + })} +
    +
    + ))} +
    + + {/* Second Half */} +
    +

    {transI18n("secondHalfEnemies")}

    + + {challengeSelected && Object.values(challengeSelected?.EventList2[0]?.Infinite || []).map((waveValue, waveIndex) => ( +
    +

    {transI18n("wave")} {waveIndex + 1}

    +
    + {Array.from(new Set(waveValue.MonsterList)).map((monsterId, enemyIndex) => { + const monsterStats = calcMonsterStats( + mapMonster?.[monsterId.toString()], + waveValue.EliteGroup, + challengeSelected?.EventList2?.[0]?.HardLevelGroup, + challengeSelected?.EventList2?.[0]?.Level, + hardLevelConfig, + eliteConfig + ); + return ( +
    +
    + Lv. {challengeSelected?.EventList2[0].Level} +
    + +
    + {mapMonster?.[monsterId.toString()]?.Image?.IconPath && ( +
    + Enemy Icon +
    + )} +
    + +
    +
    +
    + HP + {monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Speed + {monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    + +
    + Toughness + {monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })} +
    +
    + +
    + + Weakness + +
    + {mapMonster?.[monsterId.toString()]?.StanceWeakList?.map((icon, iconIndex) => ( + {icon} + ))} +
    +
    +
    +
    + ) + })} +
    +
    + ))} +
    +
    + )} + +
    + ) +} \ No newline at end of file diff --git a/src/components/parseText/index.tsx b/src/components/parseText/index.tsx new file mode 100644 index 0000000..5f06404 --- /dev/null +++ b/src/components/parseText/index.tsx @@ -0,0 +1,16 @@ + +'use client' +import { parseRuby } from "@/helper"; + +interface TextProps { + text: string; + locale: string; + className?: string; +} + +export default function ParseText({ text, locale, className }: TextProps) { + if (locale === "ja") { + return
    ; + } + return
    {text}
    ; +} \ No newline at end of file diff --git a/src/components/queryProvider/index.tsx b/src/components/queryProvider/index.tsx new file mode 100644 index 0000000..d46af40 --- /dev/null +++ b/src/components/queryProvider/index.tsx @@ -0,0 +1,12 @@ +"use client"; + +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import React from 'react'; + +export default function QueryProviderWrapper({ children }: { children: React.ReactNode }) { + const [queryClient] = React.useState(() => new QueryClient()); + + return ( + {children} + ); +} \ No newline at end of file diff --git a/src/components/quickView/index.tsx b/src/components/quickView/index.tsx new file mode 100644 index 0000000..fdabb58 --- /dev/null +++ b/src/components/quickView/index.tsx @@ -0,0 +1,473 @@ +"use client" +import NextImage from "next/image" +import useUserDataStore from "@/stores/userDataStore"; +import { useTranslations } from "next-intl"; +import { useMemo } from "react"; +import { calcAffixBonus, calcBaseStatRaw, calcBonusStatRaw, calcMainAffixBonus, calcMainAffixBonusRaw, calcPromotion, calcSubAffixBonusRaw, getLocaleName, replaceByParam } from "@/helper"; +import { mappingStats } from "@/constant/constant"; +import RelicShowcase from "../showcaseCard/relicShowcase"; +import useLocaleStore from "@/stores/localeStore"; +import useDetailDataStore from "@/stores/detailDataStore"; +import useCurrentDataStore from "@/stores/currentDataStore"; + +export default function QuickView() { + const { avatars } = useUserDataStore() + const transI18n = useTranslations("DataPage") + const { locale } = useLocaleStore() + const { avatarSelected, } = useCurrentDataStore() + const { mainAffix, subAffix, mapRelicSet, mapLightCone, mapAvatar } = useDetailDataStore() + + + const avatarSkillTree = useMemo(() => { + if (!avatarSelected || !avatars[avatarSelected?.ID?.toString()]) return {} + if (avatars[avatarSelected?.ID?.toString()].enhanced) { + return avatarSelected?.Enhanced?.[avatars[avatarSelected?.ID?.toString()].enhanced.toString()].SkillTrees || {} + } + return avatarSelected?.SkillTrees || {} + }, [avatarSelected, avatars]) + + const avatarData = useMemo(() => { + if (!avatarSelected) return + return avatars[avatarSelected?.ID?.toString()] + }, [avatarSelected, avatars]) + + const avatarProfile = useMemo(() => { + if (!avatarSelected || !avatarData) return + return avatarData?.profileList?.[avatarData?.profileSelect] + }, [avatarSelected, avatarData]) + + const relicEffects = useMemo(() => { + const avatar = avatars[avatarSelected?.ID?.toString() || ""]; + const relicCount: { [key: string]: number } = {}; + if (avatar) { + for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) { + if (relicCount[relic.relic_set_id]) { + relicCount[relic.relic_set_id]++; + } else { + relicCount[relic.relic_set_id] = 1; + } + } + } + const listEffects: { key: string, count: number }[] = []; + Object.entries(relicCount).forEach(([key, value]) => { + if (value >= 2) { + listEffects.push({ key: key, count: value }); + } + }); + return listEffects; + }, [avatars, avatarSelected]); + + const relicStats = useMemo(() => { + if (!avatarSelected || !avatarProfile?.relics || !mainAffix || !subAffix) return + + return Object.entries(avatarProfile?.relics).map(([key, value]) => { + const mainAffixMap = mainAffix["5" + key] + const subAffixMap = subAffix["5"] + if (!mainAffixMap || !subAffixMap) return + return { + img: `${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${value.relic_set_id}_${key}.png`, + mainAffix: { + property: mainAffixMap?.[value?.main_affix_id]?.Property, + level: value?.level, + valueAffix: calcMainAffixBonus(mainAffixMap?.[value?.main_affix_id], value?.level), + detail: mappingStats?.[mainAffixMap?.[value?.main_affix_id]?.Property] + }, + subAffix: value?.sub_affixes?.map((subValue) => { + return { + property: subAffixMap?.[subValue?.sub_affix_id]?.Property, + valueAffix: calcAffixBonus(subAffixMap?.[subValue?.sub_affix_id], subValue?.step, subValue?.count), + detail: mappingStats?.[subAffixMap?.[subValue?.sub_affix_id]?.Property], + step: subValue?.step, + count: subValue?.count + } + }) + } + }) + }, [avatarSelected, avatarProfile, mainAffix, subAffix]) + + const characterStats = useMemo(() => { + if (!avatarSelected || !avatarData) return + const charPromotion = calcPromotion(avatarData.level) + + const statsData: Record = { + HP: { + value: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd, + avatarData.level + ), + base: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd, + avatarData.level + ), + name: "HP", + icon: "spriteoutput/ui/avatar/icon/IconMaxHP.png", + unit: "", + round: 0 + }, + ATK: { + value: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd, + avatarData.level + ), + base: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd, + avatarData.level + ), + name: "ATK", + icon: "spriteoutput/ui/avatar/icon/IconAttack.png", + unit: "", + round: 0 + }, + DEF: { + value: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd, + avatarData.level + ), + base: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd, + avatarData.level + ), + name: "DEF", + icon: "spriteoutput/ui/avatar/icon/IconDefence.png", + unit: "", + round: 0 + }, + SPD: { + value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0, + base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0, + name: "SPD", + icon: "spriteoutput/ui/avatar/icon/IconSpeed.png", + unit: "", + round: 1 + }, + CRITRate: { + value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0, + base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0, + name: "CRIT Rate", + icon: "spriteoutput/ui/avatar/icon/IconCriticalChance.png", + unit: "%", + round: 1 + }, + CRITDmg: { + value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0, + base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0, + name: "CRIT DMG", + icon: "spriteoutput/ui/avatar/icon/IconCriticalDamage.png", + unit: "%", + round: 1 + }, + BreakEffect: { + value: 0, + base: 0, + name: "Break Effect", + icon: "spriteoutput/ui/avatar/icon/IconBreakUp.png", + unit: "%", + round: 1 + }, + EffectRES: { + value: 0, + base: 0, + name: "Effect RES", + icon: "spriteoutput/ui/avatar/icon/IconStatusResistance.png", + unit: "%", + round: 1 + }, + EnergyRate: { + value: 0, + base: 0, + name: "Energy Rate", + icon: "spriteoutput/ui/avatar/icon/IconEnergyRecovery.png", + unit: "%", + round: 1 + }, + EffectHitRate: { + value: 0, + base: 0, + name: "Effect Hit Rate", + icon: "spriteoutput/ui/avatar/icon/IconStatusProbability.png", + unit: "%", + round: 1 + }, + HealBoost: { + value: 0, + base: 0, + name: "Healing Boost", + icon: "spriteoutput/ui/avatar/icon/IconHealRatio.png", + unit: "%", + round: 1 + }, + PhysicalAdd: { + value: 0, + base: 0, + name: "Physical Boost", + icon: "spriteoutput/ui/avatar/icon/IconPhysicalAddedRatio.png", + unit: "%", + round: 1 + }, + FireAdd: { + value: 0, + base: 0, + name: "Fire Boost", + icon: "spriteoutput/ui/avatar/icon/IconFireAddedRatio.png", + unit: "%", + round: 1 + }, + IceAdd: { + value: 0, + base: 0, + name: "Ice Boost", + icon: "spriteoutput/ui/avatar/icon/IconIceAddedRatio.png", + unit: "%", + round: 1 + }, + ThunderAdd: { + value: 0, + base: 0, + name: "Thunder Boost", + icon: "spriteoutput/ui/avatar/icon/IconThunderAddedRatio.png", + unit: "%", + round: 1 + }, + WindAdd: { + value: 0, + base: 0, + name: "Wind Boost", + icon: "spriteoutput/ui/avatar/icon/IconWindAddedRatio.png", + unit: "%", + round: 1 + }, + QuantumAdd: { + value: 0, + base: 0, + name: "Quantum Boost", + icon: "spriteoutput/ui/avatar/icon/IconQuantumAddedRatio.png", + unit: "%", + round: 1 + }, + ImaginaryAdd: { + value: 0, + base: 0, + name: "Imaginary Boost", + icon: "spriteoutput/ui/avatar/icon/IconImaginaryAddedRatio.png", + unit: "%", + round: 1 + }, + ElationAdd: { + value: 0, + base: 0, + name: "Elation Boost", + icon: "spriteoutput/ui/avatar/icon/IconJoy.png", + unit: "%", + round: 1 + } + } + + if (avatarProfile?.lightcone && mapLightCone[avatarProfile?.lightcone?.item_id]) { + const lightconePromotion = calcPromotion(avatarProfile?.lightcone?.level) + statsData.HP.value += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHP, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd, + avatarProfile?.lightcone?.level + ) + statsData.HP.base += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHP, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd, + avatarProfile?.lightcone?.level + ) + statsData.ATK.value += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttack, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttackAdd, + avatarProfile?.lightcone?.level + ) + statsData.ATK.base += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttack, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttackAdd, + avatarProfile?.lightcone?.level + ) + statsData.DEF.value += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefence, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefenceAdd, + avatarProfile?.lightcone?.level + ) + statsData.DEF.base += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefence, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefenceAdd, + avatarProfile?.lightcone?.level + ) + + const bonusData = mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Skills?.Level?.[avatarProfile?.lightcone.rank]?.Bonus + if (bonusData && bonusData.length > 0) { + const bonusSpd = bonusData.filter((bonus) => bonus.PropertyType === "BaseSpeed") + const bonusOther = bonusData.filter((bonus) => bonus.PropertyType !== "BaseSpeed") + bonusSpd.forEach((bonus) => { + statsData.SPD.value += bonus.Value + statsData.SPD.base += bonus.Value + }) + bonusOther.forEach((bonus) => { + const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat + if (statsBase && statsData[statsBase]) { + statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value) + } + }) + } + } + if (avatarSkillTree) { + Object.values(avatarSkillTree).forEach((value) => { + if (value?.["1"] + && value?.["1"]?.PointID + && typeof avatarData?.data?.skills?.[value?.["1"]?.PointID] === "number" + && avatarData?.data?.skills?.[value?.["1"]?.PointID] !== 0 + && value?.["1"]?.StatusAddList + && value?.["1"].StatusAddList.length > 0) { + value?.["1"]?.StatusAddList.forEach((status) => { + const statsBase = mappingStats?.[status?.PropertyType]?.baseStat + if (statsBase && statsData[statsBase]) { + statsData[statsBase].value += calcBonusStatRaw(status?.PropertyType, statsData[statsBase].base, status.Value) + } + }) + } + }) + } + + + + if (avatarProfile?.relics && mainAffix && subAffix) { + Object.entries(avatarProfile?.relics).forEach(([key, value]) => { + const mainAffixMap = mainAffix["5" + key] + const subAffixMap = subAffix["5"] + if (!mainAffixMap || !subAffixMap) return + const mainStats = mappingStats?.[mainAffixMap?.[value.main_affix_id]?.Property]?.baseStat + if (mainStats && statsData[mainStats]) { + statsData[mainStats].value += calcMainAffixBonusRaw(mainAffixMap?.[value.main_affix_id], value.level, statsData[mainStats].base) + } + value?.sub_affixes.forEach((subValue) => { + const subStats = mappingStats?.[subAffixMap?.[subValue.sub_affix_id]?.Property]?.baseStat + if (subStats && statsData[subStats]) { + statsData[subStats].value += calcSubAffixBonusRaw(subAffixMap?.[subValue.sub_affix_id], subValue.step, subValue.count, statsData[subStats].base) + } + }) + }) + } + + if (relicEffects && relicEffects.length > 0) { + relicEffects.forEach((relic) => { + const dataBonus = mapRelicSet?.[relic.key]?.Skills + if (!dataBonus || Object.keys(dataBonus).length === 0) return + Object.entries(dataBonus || {}).forEach(([key, value]) => { + if (relic.count < Number(key)) return + value.Bonus.forEach((bonus) => { + const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat + if (statsBase && statsData[statsBase]) { + statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value) + } + }) + }) + }) + } + + + return statsData + }, [ + avatarSelected, + avatarData, + mapAvatar, + avatarProfile?.lightcone, + avatarProfile?.relics, + mapLightCone, + mainAffix, + subAffix, + relicEffects, + mapRelicSet, + avatarSkillTree + ]) + + return ( +
    +
    +
    + {Object.entries(characterStats || {})?.map(([key, stat], index) => { + if (!stat || (key.includes("Add") && stat.value === 0)) return null + return ( +
    +
    + +
    {stat.name}
    +
    +
    +
    { + stat.value ? stat.unit === "%" ? (stat.value * 100).toFixed(stat.round) : stat.value.toFixed(stat.round) : 0 + }{stat.unit}
    +
    + ) + })} +
    +
    + +
    + {relicEffects.map((setEffect, index) => { + const relicInfo = mapRelicSet[setEffect.key]; + if (!relicInfo) return null; + return ( +
    +
    +
    + {setEffect.count} +
    +
    + ) + })} +
    +
    + +
    + + {relicStats?.map((relic, index) => { + if (!relic || !avatarSelected) return null + return ( + + ) + })} + + {(!relicStats || !relicStats?.length) && ( +
    +
    + {transI18n("noRelicEquipped")} +
    +
    + )} +
    +
    + ) +} \ No newline at end of file diff --git a/src/components/relicBar/index.tsx b/src/components/relicBar/index.tsx new file mode 100644 index 0000000..f027aca --- /dev/null +++ b/src/components/relicBar/index.tsx @@ -0,0 +1,498 @@ +"use client"; +import useUserDataStore from '@/stores/userDataStore'; +import { useEffect, useMemo, useState } from 'react'; +import SelectCustomImage from '../select/customSelectImage'; +import { calcAffixBonus, calcMainAffixBonus, randomPartition, randomStep, replaceByParam, getLocaleName } from '@/helper'; +import { mappingStats } from '@/constant/constant'; +import useModelStore from '@/stores/modelStore'; +import useRelicMakerStore from '@/stores/relicMakerStore'; +import { toast } from 'react-toastify'; +import { useTranslations } from 'next-intl' +import { ChevronDown, ChevronUp } from 'lucide-react'; +import { AnimatePresence, motion } from 'framer-motion'; +import useDetailDataStore from '@/stores/detailDataStore'; +import useCurrentDataStore from '@/stores/currentDataStore'; +import { RelicSetDetail, SubAffixData } from '@/types'; +import useLocaleStore from '@/stores/localeStore'; +import { mappingRelicSlot } from "@/constant/constant"; + +export default function RelicMaker() { + const { avatars, setAvatars } = useUserDataStore() + const { avatarSelected } = useCurrentDataStore() + const { setIsOpenRelic } = useModelStore() + const { mainAffix, subAffix, mapRelicSet } = useDetailDataStore() + const { locale } = useLocaleStore() + const transI18n = useTranslations("DataPage") + const { + selectedRelicSlot, + selectedRelicSet, + selectedMainStat, + listSelectedSubStats, + selectedRelicLevel, + preSelectedSubStats, + setSelectedRelicSet, + setSelectedMainStat, + setSelectedRelicLevel, + setListSelectedSubStats, + resetHistory, + popHistory, + addHistory, + } = useRelicMakerStore() + const [error, setError] = useState(""); + + const relicSets = useMemo(() => { + const listSet: Record = {}; + for (const [key, value] of Object.entries(mapRelicSet || {})) { + let isOk = false; + for (const key2 of Object.keys(value.Parts)) { + if (key2 == mappingRelicSlot?.[selectedRelicSlot]) { + isOk = true; + break; + } + } + if (isOk) { + listSet[key] = value; + } + } + return listSet; + }, [mapRelicSet , selectedRelicSlot]); + + const subAffixOptions = useMemo(() => { + const listSet: Record = {}; + const subAffixMap = subAffix["5"]; + const mainAffixMap = mainAffix["5" + selectedRelicSlot] + + if (Object.keys(subAffixMap || {}).length === 0 || Object.keys(mainAffixMap || {}).length === 0) return listSet; + + for (const [key, value] of Object.entries(subAffixMap)) { + if (value.Property !== mainAffixMap[selectedMainStat]?.Property) { + listSet[key] = value; + } + } + return listSet; + }, [subAffix, mainAffix, selectedRelicSlot, selectedMainStat]); + + useEffect(() => { + const subAffixMap = subAffix["5"]; + const mainAffixMap = mainAffix["5" + selectedRelicSlot]; + + if (!subAffixMap || !mainAffixMap) return; + + const mainProp = mainAffixMap[selectedMainStat]?.Property; + if (!mainProp) return; + + const newSubAffixes = structuredClone(listSelectedSubStats); + let updated = false; + + for (let i = 0; i < newSubAffixes.length; i++) { + if (newSubAffixes[i].property === mainProp) { + newSubAffixes[i].affixId = ""; + newSubAffixes[i].property = ""; + newSubAffixes[i].rollCount = 0; + newSubAffixes[i].stepCount = 0; + updated = true; + } + } + + if (updated) setListSelectedSubStats(newSubAffixes); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedMainStat, subAffix, mainAffix, selectedRelicSlot]); + + const exSubAffixOptions = useMemo(() => { + const listSet: Record = {}; + const subAffixMap = subAffix["5"]; + const mainAffixMap = mainAffix["5" + selectedRelicSlot]; + + if (!subAffixMap || !mainAffixMap) return listSet; + + for (const [key, value] of Object.entries(subAffixMap)) { + const subAffix = listSelectedSubStats.find((item) => item.property === value.Property); + if (subAffix && value.Property !== mainAffixMap[selectedMainStat]?.Property) { + listSet[key] = value; + } + } + return listSet; + }, [subAffix, listSelectedSubStats, mainAffix, selectedRelicSlot, selectedMainStat]); + + const effectBonus = useMemo(() => { + const affixSet = mainAffix?.["5" + selectedRelicSlot]; + if (!affixSet) return 0; + + const data = affixSet[selectedMainStat]; + if (!data) return 0; + + return calcMainAffixBonus(data, selectedRelicLevel); + }, [mainAffix, selectedRelicSlot, selectedMainStat, selectedRelicLevel]); + + const handleSubStatChange = (key: string, index: number, rollCount: number, stepCount: number) => { + setError(""); + const newSubAffixes = structuredClone(listSelectedSubStats); + if (!subAffixOptions[key]) { + newSubAffixes[index].affixId = ""; + newSubAffixes[index].property = ""; + newSubAffixes[index].rollCount = rollCount; + newSubAffixes[index].stepCount = stepCount; + setListSelectedSubStats(newSubAffixes); + addHistory(index, newSubAffixes[index]); + return; + } + newSubAffixes[index].affixId = key; + newSubAffixes[index].property = subAffixOptions[key].Property; + newSubAffixes[index].rollCount = rollCount; + newSubAffixes[index].stepCount = stepCount; + setListSelectedSubStats(newSubAffixes); + addHistory(index, newSubAffixes[index]); + }; + + const handlerRollback = (index: number) => { + setError(""); + if (!preSelectedSubStats[index]) return; + + const keys = Object.keys(preSelectedSubStats[index]); + if (keys.length <= 1) return; + + const newSubAffixes = structuredClone(listSelectedSubStats); + const listHistory = structuredClone(preSelectedSubStats[index]); + const secondLastKey = listHistory.length - 2; + const preSubAffixes = { ...listHistory[secondLastKey] }; + newSubAffixes[index].rollCount = preSubAffixes.rollCount; + newSubAffixes[index].stepCount = preSubAffixes.stepCount; + setListSelectedSubStats(newSubAffixes); + popHistory(index); + }; + + const resetSubStat = (index: number) => { + const newSubAffixes = structuredClone(listSelectedSubStats); + resetHistory(index); + newSubAffixes[index].affixId = ""; + newSubAffixes[index].property = ""; + newSubAffixes[index].rollCount = 0; + newSubAffixes[index].stepCount = 0; + setListSelectedSubStats(newSubAffixes); + }; + + const randomizeStats = () => { + const newSubAffixes = structuredClone(listSelectedSubStats); + const exKeys = Object.keys(exSubAffixOptions); + for (let i = 0; i < newSubAffixes.length; i++) { + const keys = Object.keys(subAffixOptions).filter((key) => !exKeys.includes(key)); + const randomKey = keys[Math.floor(Math.random() * keys.length)]; + exKeys.push(randomKey); + const randomValue = subAffixOptions[randomKey]; + newSubAffixes[i].affixId = randomKey; + newSubAffixes[i].property = randomValue.Property; + newSubAffixes[i].rollCount = 0; + newSubAffixes[i].stepCount = 0; + } + for (let i = 0; i < newSubAffixes.length; i++) { + addHistory(i, newSubAffixes[i]); + } + setListSelectedSubStats(newSubAffixes); + + }; + + const randomizeRolls = () => { + const newSubAffixes = structuredClone(listSelectedSubStats); + const randomRolls = randomPartition(9, listSelectedSubStats.length); + for (let i = 0; i < listSelectedSubStats.length; i++) { + newSubAffixes[i].rollCount = randomRolls[i]; + newSubAffixes[i].stepCount = randomStep(randomRolls[i]); + } + setListSelectedSubStats(newSubAffixes); + for (let i = 0; i < newSubAffixes.length; i++) { + addHistory(i, newSubAffixes[i]); + } + }; + + const handlerSaveRelic = () => { + setError(""); + const avatar = avatars[avatarSelected?.ID?.toString() || ""]; + if (!selectedRelicSet || !selectedMainStat || !selectedRelicLevel || !selectedRelicSlot) { + setError(transI18n("pleaseSelectAllOptions")); + return; + }; + + if (listSelectedSubStats.find((item) => item.affixId === "")) { + setError(transI18n("pleaseSelectAllSubStats")); + return; + }; + + if (avatar) { + avatar.profileList[avatar.profileSelect].relics[selectedRelicSlot] = { + level: selectedRelicLevel, + relic_id: Number(`6${selectedRelicSet}${selectedRelicSlot}`), + relic_set_id: Number(selectedRelicSet), + main_affix_id: Number(selectedMainStat), + sub_affixes: listSelectedSubStats.map((item) => { + return { + sub_affix_id: Number(item.affixId), + count: item.rollCount, + step: item.stepCount + } + }) + } + } + setAvatars({ ...avatars }); + setIsOpenRelic(false); + + toast.success(transI18n("relicSavedSuccessfully")); + } + + return ( +
    +
    +

    + {transI18n("relicMaker")} +

    +
    +
    + + {/* Left Panel */} +
    + + {/* Set Configuration */} +
    +

    {transI18n("mainSettings")}

    + +
    + {/* Main Stat */} +
    + + ({ + value: key, + label: mappingStats[value.Property].name + " " + mappingStats[value.Property].unit, + imageUrl: `${process.env.CDN_URL}/${mappingStats[value.Property].icon}` + }))} + excludeSet={[]} + selectedCustomSet={selectedMainStat} + placeholder={transI18n("selectAMainStat")} + setSelectedCustomSet={setSelectedMainStat} + /> +
    + {/* Relic Set Selection */} +
    + + ({ + value: key, + label: getLocaleName(locale, value.Name), + imageUrl: `${process.env.CDN_URL}/${value.Image.SetIconPath}` + }))} + excludeSet={[]} + selectedCustomSet={selectedRelicSet} + placeholder={transI18n("selectASet")} + setSelectedCustomSet={setSelectedRelicSet} + /> +
    +
    + + {/* Set Bonus Display */} +
    + {selectedRelicSet !== "" ? Object.entries(mapRelicSet[selectedRelicSet].Skills).map(([key, value]) => ( +
    + {key}-Pc: +
    +
    + )) :

    {transI18n("pleaseSelectASet")}

    } +
    + + + {/* Rarity */} +
    + + +
    + + {/* Level */} +
    + +
    + setSelectedRelicLevel(parseInt(e.target.value))} + className="range range-primary w-full" + /> +
    {selectedRelicLevel}
    +
    +
    + + {error && ( + + {error}! + + )} + + + + + {/* Save Button */} + +
    +
    + + {/* Right Panel - Sub Stats */} +
    + {/* Total Roll */} +
    +

    {transI18n("totalRoll")} {listSelectedSubStats.reduce((a, b) => a + b.rollCount, 0)}

    + +
    + + +
    +
    + {listSelectedSubStats.map((v, index) => ( +
    +
    + + {/* Stat Selection */} +
    + ({ + value: key, + label: mappingStats[value.Property].name + " " + mappingStats[value.Property].unit, + imageUrl: `${process.env.CDN_URL}/${mappingStats[value.Property].icon}` + }))} + excludeSet={Object.entries(exSubAffixOptions).map(([key, value]) => ({ + value: key, + label: mappingStats[value.Property].name + " " + mappingStats[value.Property].unit, + imageUrl: `${process.env.CDN_URL}/${mappingStats[value.Property].icon}` + }))} + selectedCustomSet={v.affixId} + placeholder={transI18n("selectASubStat")} + setSelectedCustomSet={(key) => handleSubStatChange(key, index, 0, 0)} + /> +
    + + {/* Current Value */} +
    + +{ } +
    {calcAffixBonus(subAffixOptions[v.affixId], v.stepCount, v.rollCount)}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
    +
    + + {/* Up Roll Values */} +
    +
    + + {transI18n("upRoll")} +
    +
    + + + +
    +
    + + {/* Down Roll Values */} +
    +
    + + {transI18n("downRoll")} +
    +
    + + + +
    +
    + + {/* Reset Button & Roll Info */} +
    +
    +
    + + +
    + +
    + {transI18n("roll")}: {v.rollCount} + {transI18n("step")}: {v.stepCount} +
    +
    +
    + +
    +
    + ))} + + +
    +
    +
    + ); +}; diff --git a/src/components/relicsInfo/index.tsx b/src/components/relicsInfo/index.tsx new file mode 100644 index 0000000..8273bdc --- /dev/null +++ b/src/components/relicsInfo/index.tsx @@ -0,0 +1,366 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +"use client"; +import { useCallback, useEffect, useMemo } from "react"; +import RelicMaker from "../relicBar"; +import { motion } from "framer-motion"; +import useUserDataStore from "@/stores/userDataStore"; +import { useTranslations } from "next-intl"; +import RelicCard from "../card/relicCard"; +import useModelStore from '@/stores/modelStore'; +import { replaceByParam } from '@/helper'; +import useRelicMakerStore from '@/stores/relicMakerStore'; +import QuickView from "../quickView"; +import { ModalConfig } from "@/types"; +import useCurrentDataStore from "@/stores/currentDataStore"; +import useDetailDataStore from "@/stores/detailDataStore"; +import { getLocaleName } from '@/helper'; +import useLocaleStore from "@/stores/localeStore"; + +export default function RelicsInfo() { + const { avatars, setAvatars } = useUserDataStore() + const { + setSelectedRelicSlot, + selectedRelicSlot, + setSelectedMainStat, + setSelectedRelicSet, + setSelectedRelicLevel, + setListSelectedSubStats, + resetHistory, + resetSubStat, + listSelectedSubStats, + } = useRelicMakerStore() + + const { + isOpenRelic, + setIsOpenRelic, + isOpenQuickView, + setIsOpenQuickView + } = useModelStore() + const transI18n = useTranslations("DataPage") + const { avatarSelected } = useCurrentDataStore() + const { mapRelicSet, subAffix } = useDetailDataStore() + const { locale } = useLocaleStore() + + const handleShow = (modalId: string) => { + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + modal.showModal(); + } + }; + + // Close modal handler + const handleCloseModal = (modalId: string) => { + const modal = document.getElementById(modalId) as HTMLDialogElement | null; + if (modal) { + modal.close(); + } + }; + + + const getRelic = useCallback((slot: string) => { + const avatar = avatars[avatarSelected?.ID.toString() || ""]; + if (avatar) { + return avatar.profileList[avatar.profileSelect]?.relics[slot] || null; + } + return null; + }, [avatars, avatarSelected]); + + const handlerDeleteRelic = (slot: string) => { + const avatar = avatars[avatarSelected?.ID.toString() || ""]; + if (avatar) { + delete avatar.profileList[avatar.profileSelect].relics[slot] + setAvatars({ ...avatars }); + } + } + + const handlerChangeRelic = (slot: string) => { + const relic = getRelic(slot) + setSelectedRelicSlot(slot) + resetSubStat() + resetHistory(null) + if (relic) { + setSelectedMainStat(relic.main_affix_id.toString()) + setSelectedRelicSet(relic.relic_set_id.toString()) + setSelectedRelicLevel(relic.level) + const newSubAffixes: { affixId: string, property: string, rollCount: number, stepCount: number }[] = [...listSelectedSubStats]; + relic.sub_affixes.forEach((item, index) => { + newSubAffixes[index].affixId = item.sub_affix_id.toString(); + newSubAffixes[index].property = subAffix["5"][item.sub_affix_id.toString()]?.Property || ""; + newSubAffixes[index].rollCount = item.count || 0; + newSubAffixes[index].stepCount = item.step || 0; + }) + setListSelectedSubStats(newSubAffixes) + } else { + setSelectedMainStat("") + setSelectedRelicSet("") + setSelectedRelicLevel(15) + const newSubAffixes: { affixId: string, property: string, rollCount: number, stepCount: number }[] = [...listSelectedSubStats]; + newSubAffixes.forEach((item) => { + item.affixId = "" + item.property = "" + item.rollCount = 0 + item.stepCount = 0 + }) + + setListSelectedSubStats(newSubAffixes) + } + setIsOpenRelic(true) + handleShow("action_detail_modal") + } + + const relicEffects = useMemo(() => { + const avatar = avatars[avatarSelected?.ID.toString() || ""]; + const relicCount: { [key: string]: number } = {}; + if (avatar) { + for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) { + if (relicCount[relic.relic_set_id]) { + relicCount[relic.relic_set_id]++; + } else { + relicCount[relic.relic_set_id] = 1; + } + } + } + const listEffects: { key: string, count: number }[] = []; + Object.entries(relicCount).forEach(([key, value]) => { + if (value >= 2) { + listEffects.push({ key: key, count: value }); + } + }); + return listEffects; + }, [avatars, avatarSelected]); + + const modalConfigs: ModalConfig[] = [ + { + id: "action_detail_modal", + title: "", + isOpen: isOpenRelic, + onClose: () => { + setIsOpenRelic(false) + handleCloseModal("action_detail_modal") + }, + content: + }, + { + id: "quick_view_modal", + title: transI18n("quickView").toUpperCase(), + isOpen: isOpenQuickView, + onClose: () => { + setIsOpenQuickView(false) + handleCloseModal("quick_view_modal") + }, + content: + } + ] + + // Handle ESC key to close modal + useEffect(() => { + for (const item of modalConfigs) { + if (!item?.isOpen) { + handleCloseModal(item?.id || "") + } + } + const handleEscKey = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + for (const item of modalConfigs) { + handleCloseModal(item?.id || "") + } + } + }; + + window.addEventListener('keydown', handleEscKey); + return () => window.removeEventListener('keydown', handleEscKey); + }, [isOpenRelic]); + + return ( +
    +
    + + {/* Left Section - Items Grid */} +
    +
    +

    +
    + {transI18n("relics")} +

    + +
    + {["1", "2", "3", "4", "5", "6"].map((item, index) => ( +
    +
    { + if (item === selectedRelicSlot) { + setSelectedRelicSlot("") + } else { + setSelectedRelicSlot(item) + } + handlerChangeRelic(item) + }} + className="cursor-pointer" + > + +
    + +
    + + + {getRelic(item) && ( + + )} +
    +
    + ))} +
    +
    + + +
    + +
    +
    + + {/* Right Section - Stats and Set Effects */} +
    + + {/* Set Effects Panel */} +
    +

    +
    + {transI18n("setEffects")} +

    + +
    + {relicEffects.map((setEffect, index) => { + const relicInfo = mapRelicSet[setEffect.key]; + if (!relicInfo) return null; + return ( +
    +
    +
    + {setEffect.count && ( + + ({setEffect.count}) + + )} +
    + +
    + {Object.entries(relicInfo.Skills).map(([requireNum, value]) => { + if (Number(requireNum) > Number(setEffect.count)) return null; + return ( +
    +
    + {requireNum}-PC: +
    +
    +
    + ) + })} +
    +
    + ) + })} +
    +
    +
    +
    + + {modalConfigs.map(({ id, title, onClose, content }) => ( + +
    +
    + + ✕ + +
    + + {title && ( +
    +

    + {title} +

    +
    + )} + + {content} +
    +
    + ))} + +
    + ); +} diff --git a/src/components/select/customSelectImage.tsx b/src/components/select/customSelectImage.tsx new file mode 100644 index 0000000..ea23a8b --- /dev/null +++ b/src/components/select/customSelectImage.tsx @@ -0,0 +1,112 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +'use client' +import dynamic from "next/dynamic" +import type { SingleValue } from "react-select" +import Image from "next/image" +import useLocaleStore from "@/stores/localeStore" +import ParseText from "../parseText" +import { themeColors } from "@/constant/constant" +import type { Props as SelectProps } from "react-select" +import { JSX } from "react" + +const Select = dynamic( + () => import("react-select").then(m => m.default), + { ssr: false } +) as ( + props: SelectProps +) => JSX.Element + +export type SelectOption = { + value: string + label: string + imageUrl: string +} + +type SelectCustomProp = { + customSet: SelectOption[] + excludeSet: SelectOption[] + selectedCustomSet: string + placeholder: string + setSelectedCustomSet: (value: string) => void +} + +export default function SelectCustomImage({ customSet, excludeSet, selectedCustomSet, placeholder, setSelectedCustomSet }: SelectCustomProp) { + const options: SelectOption[] = customSet + const { locale, theme } = useLocaleStore() + + const c = themeColors[theme] || themeColors.winter + + const customStyles = { + option: (p: any, s: any) => ({ + ...p, + display: 'flex', + alignItems: 'center', + gap: '8px', + padding: '8px 12px', + backgroundColor: s.isFocused ? c.bgHover : c.bg, + color: c.text, + cursor: 'pointer' + }), + + singleValue: (p: any) => ({ + ...p, + display: 'flex', + alignItems: 'center', + gap: '8px', + color: c.text + }), + + control: (p: any) => ({ + ...p, + backgroundColor: c.bg, + borderColor: c.border, + boxShadow: 'none' + }), + + menu: (p: any) => ({ + ...p, + backgroundColor: c.bg, + color: c.text, + zIndex: 9999 + }), + + menuPortal: (p: any) => ({ + ...p, + zIndex: 9999 + }) + } + + const formatOptionLabel = (option: SelectOption) => ( +
    + + +
    + ) + + return ( + !excludeSet.some(ex => ex.id === opt.id))} + value={options.find(opt => { + return opt.id === selectedCustomSet + }) || null} + onChange={(selected: SingleValue) => { + setSelectedCustomSet(selected?.id || '') + }} + formatOptionLabel={formatOptionLabel} + styles={customStyles} + placeholder={placeholder} + className="my-react-select-container" + classNamePrefix="my-react-select" + isSearchable + isClearable + /> + ) +} diff --git a/src/components/showcaseCard/index.tsx b/src/components/showcaseCard/index.tsx new file mode 100644 index 0000000..eba07f8 --- /dev/null +++ b/src/components/showcaseCard/index.tsx @@ -0,0 +1,1006 @@ +"use client"; +import { useEffect, useState, useRef, useMemo, useCallback } from 'react'; +import { FastAverageColor } from 'fast-average-color'; +import NextImage from 'next/image'; +import ParseText from '../parseText'; +import useLocaleStore from '@/stores/localeStore'; +import { calcAffixBonus, calcBaseStat, calcBaseStatRaw, calcBonusStatRaw, calcMainAffixBonus, calcMainAffixBonusRaw, calcPromotion, calcSubAffixBonusRaw, convertToRoman, getNameChar, replaceByParam } from '@/helper'; +import useUserDataStore from '@/stores/userDataStore'; +import { traceShowCaseMap } from '@/constant/traceConstant'; +import { mappingStats } from '@/constant/constant'; +import { useTranslations } from 'next-intl'; +import { toast } from 'react-toastify'; +import RelicShowcase from './relicShowcase'; +import useDetailDataStore from '@/stores/detailDataStore'; +import useCurrentDataStore from '@/stores/currentDataStore'; +import { getLocaleName } from '@/helper'; + + +export default function ShowCaseInfo() { + const { avatarSelected } = useCurrentDataStore() + const { mainAffix, subAffix, mapRelicSet, mapAvatar, mapLightCone, baseType, damageType } = useDetailDataStore() + const { avatars } = useUserDataStore() + const [avgColor, setAvgColor] = useState('#222'); + const imgRef = useRef(null); + const cardRef = useRef(null) + const { locale } = useLocaleStore() + const transI18n = useTranslations("DataPage") + + const handleSaveImage = useCallback(() => { + if (cardRef.current === null || !avatarSelected) { + toast.error("Avatar showcase not found!"); + return; + } + + import("html2canvas-pro") + .then(({ default: html2canvas }) => + html2canvas(cardRef.current!, { + scale: 2, + backgroundColor: "#000000", + allowTaint: false, + useCORS: true + }) + ) + .then((canvas: HTMLCanvasElement) => { + const link = document.createElement("a"); + link.download = `${getNameChar(locale, transI18n, avatarSelected)}_showcase.png`; + link.href = canvas.toDataURL("image/png"); + link.click(); + }) + .catch(() => { + toast.error("Error generating showcase card!"); + }); + }, [avatarSelected, locale, transI18n]); + + useEffect(() => { + if (!avatarSelected) return; + const fac = new FastAverageColor(); + const img = new Image(); + + img.crossOrigin = 'anonymous'; + img.src = `${process.env.CDN_URL}/${avatarSelected?.Image?.AvatarCutinFrontImgPath}`; + + img.onload = () => { + fac.getColorAsync(img) + .then((color) => { + setAvgColor(color.hex); + }) + .catch(e => console.error("Error:", e)); + }; + return () => { + fac.destroy(); + img.onload = null; + }; + }, [avatarSelected]); + + const avatarSkillTree = useMemo(() => { + if (!avatarSelected || !avatars[avatarSelected?.ID?.toString()]) return {} + if (avatars[avatarSelected?.ID?.toString()].enhanced) { + return avatarSelected?.Enhanced?.[avatars[avatarSelected?.ID?.toString()].enhanced.toString()].SkillTrees || {} + } + return avatarSelected?.SkillTrees || {} + }, [avatarSelected, avatars]) + + const avatarData = useMemo(() => { + if (!avatarSelected) return + return avatars[avatarSelected?.ID?.toString()] + }, [avatarSelected, avatars]) + + const avatarProfile = useMemo(() => { + if (!avatarSelected || !avatarData) return + return avatarData?.profileList?.[avatarData?.profileSelect] + }, [avatarSelected, avatarData]) + + const lightconeStats = useMemo(() => { + if (!avatarSelected || !avatarProfile?.lightcone || !mapLightCone[avatarProfile?.lightcone?.item_id]) return + const promotion = calcPromotion(avatarProfile?.lightcone?.level) + const atkStat = calcBaseStat( + mapLightCone[avatarProfile?.lightcone?.item_id].Stats[promotion].BaseAttack, + mapLightCone[avatarProfile?.lightcone?.item_id].Stats[promotion].BaseAttackAdd, + 0, + avatarProfile?.lightcone?.level + ) + const hpStat = calcBaseStat( + mapLightCone[avatarProfile?.lightcone?.item_id].Stats[promotion].BaseHP, + mapLightCone[avatarProfile?.lightcone?.item_id].Stats[promotion].BaseHPAdd, + 0, + avatarProfile?.lightcone?.level + ) + const defStat = calcBaseStat( + mapLightCone[avatarProfile?.lightcone?.item_id].Stats[promotion].BaseDefence, + mapLightCone[avatarProfile?.lightcone?.item_id].Stats[promotion].BaseDefenceAdd, + 0, + avatarProfile?.lightcone?.level + ) + return { + attack: atkStat, + hp: hpStat, + def: defStat, + } + }, [avatarSelected, mapLightCone, avatarProfile]) + + const relicEffects = useMemo(() => { + const avatar = avatars[avatarSelected?.ID?.toString() || ""]; + const relicCount: { [key: string]: number } = {}; + if (avatar) { + for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) { + if (relicCount[relic.relic_set_id]) { + relicCount[relic.relic_set_id]++; + } else { + relicCount[relic.relic_set_id] = 1; + } + } + } + const listEffects: { key: string, count: number }[] = []; + Object.entries(relicCount).forEach(([key, value]) => { + if (value >= 2) { + listEffects.push({ key: key, count: value }); + } + }); + return listEffects; + }, [avatars, avatarSelected]); + + const relicStats = useMemo(() => { + if (!avatarSelected || !avatarProfile?.relics || !mainAffix || !subAffix) return + + return Object.entries(avatarProfile?.relics).map(([key, value]) => { + const mainAffixMap = mainAffix["5" + key] + const subAffixMap = subAffix["5"] + if (!mainAffixMap || !subAffixMap) return + return { + img: `${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${value.relic_set_id}_${key}.png`, + mainAffix: { + property: mainAffixMap?.[value?.main_affix_id]?.Property, + level: value?.level, + valueAffix: calcMainAffixBonus(mainAffixMap?.[value?.main_affix_id], value?.level), + detail: mappingStats?.[mainAffixMap?.[value?.main_affix_id]?.Property] + }, + subAffix: value?.sub_affixes?.map((subValue) => { + return { + property: subAffixMap?.[subValue?.sub_affix_id]?.Property, + valueAffix: calcAffixBonus(subAffixMap?.[subValue?.sub_affix_id], subValue?.step, subValue?.count), + detail: mappingStats?.[subAffixMap?.[subValue?.sub_affix_id]?.Property], + step: subValue?.step, + count: subValue?.count + } + }) + } + }) + }, [avatarSelected, avatarProfile, mainAffix, subAffix]) + + const totalSubStats = useMemo(() => { + if (!relicStats?.length) return 0 + return (relicStats ?? []).reduce((acc, relic) => { + const subAffixList = relic?.subAffix ?? [] + return acc + subAffixList.reduce((subAcc, subAffix) => { + if (avatarSelected?.Relics?.SubAffixPropertyList.findIndex(it => it === subAffix.property) !== -1) { + return subAcc + (subAffix?.count ?? 0) + } + return subAcc + }, 0) + }, 0) + }, [relicStats, avatarSelected]) + + const characterStats = useMemo(() => { + if (!avatarSelected || !avatarData) return + const charPromotion = calcPromotion(avatarData.level) + + const statsData: Record = { + HP: { + value: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd, + avatarData.level + ), + base: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd, + avatarData.level + ), + name: "HP", + icon: "spriteoutput/ui/avatar/icon/IconMaxHP.png", + unit: "", + round: 0 + }, + ATK: { + value: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd, + avatarData.level + ), + base: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd, + avatarData.level + ), + name: "ATK", + icon: "spriteoutput/ui/avatar/icon/IconAttack.png", + unit: "", + round: 0 + }, + DEF: { + value: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd, + avatarData.level + ), + base: calcBaseStatRaw( + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase, + mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd, + avatarData.level + ), + name: "DEF", + icon: "spriteoutput/ui/avatar/icon/IconDefence.png", + unit: "", + round: 0 + }, + SPD: { + value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0, + base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0, + name: "SPD", + icon: "spriteoutput/ui/avatar/icon/IconSpeed.png", + unit: "", + round: 1 + }, + CRITRate: { + value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0, + base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0, + name: "CRIT Rate", + icon: "spriteoutput/ui/avatar/icon/IconCriticalChance.png", + unit: "%", + round: 1 + }, + CRITDmg: { + value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0, + base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0, + name: "CRIT DMG", + icon: "spriteoutput/ui/avatar/icon/IconCriticalDamage.png", + unit: "%", + round: 1 + }, + BreakEffect: { + value: 0, + base: 0, + name: "Break Effect", + icon: "spriteoutput/ui/avatar/icon/IconBreakUp.png", + unit: "%", + round: 1 + }, + EffectRES: { + value: 0, + base: 0, + name: "Effect RES", + icon: "spriteoutput/ui/avatar/icon/IconStatusResistance.png", + unit: "%", + round: 1 + }, + EnergyRate: { + value: 0, + base: 0, + name: "Energy Rate", + icon: "spriteoutput/ui/avatar/icon/IconEnergyRecovery.png", + unit: "%", + round: 1 + }, + EffectHitRate: { + value: 0, + base: 0, + name: "Effect Hit Rate", + icon: "spriteoutput/ui/avatar/icon/IconStatusProbability.png", + unit: "%", + round: 1 + }, + HealBoost: { + value: 0, + base: 0, + name: "Healing Boost", + icon: "spriteoutput/ui/avatar/icon/IconHealRatio.png", + unit: "%", + round: 1 + }, + PhysicalAdd: { + value: 0, + base: 0, + name: "Physical Boost", + icon: "spriteoutput/ui/avatar/icon/IconPhysicalAddedRatio.png", + unit: "%", + round: 1 + }, + FireAdd: { + value: 0, + base: 0, + name: "Fire Boost", + icon: "spriteoutput/ui/avatar/icon/IconFireAddedRatio.png", + unit: "%", + round: 1 + }, + IceAdd: { + value: 0, + base: 0, + name: "Ice Boost", + icon: "spriteoutput/ui/avatar/icon/IconIceAddedRatio.png", + unit: "%", + round: 1 + }, + ThunderAdd: { + value: 0, + base: 0, + name: "Thunder Boost", + icon: "spriteoutput/ui/avatar/icon/IconThunderAddedRatio.png", + unit: "%", + round: 1 + }, + WindAdd: { + value: 0, + base: 0, + name: "Wind Boost", + icon: "spriteoutput/ui/avatar/icon/IconWindAddedRatio.png", + unit: "%", + round: 1 + }, + QuantumAdd: { + value: 0, + base: 0, + name: "Quantum Boost", + icon: "spriteoutput/ui/avatar/icon/IconQuantumAddedRatio.png", + unit: "%", + round: 1 + }, + ImaginaryAdd: { + value: 0, + base: 0, + name: "Imaginary Boost", + icon: "spriteoutput/ui/avatar/icon/IconImaginaryAddedRatio.png", + unit: "%", + round: 1 + }, + ElationAdd: { + value: 0, + base: 0, + name: "Elation Boost", + icon: "spriteoutput/ui/avatar/icon/IconJoy.png", + unit: "%", + round: 1 + } + } + + if (avatarProfile?.lightcone && mapLightCone[avatarProfile?.lightcone?.item_id]) { + const lightconePromotion = calcPromotion(avatarProfile?.lightcone?.level) + statsData.HP.value += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHP, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd, + avatarProfile?.lightcone?.level + ) + statsData.HP.base += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHP, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd, + avatarProfile?.lightcone?.level + ) + statsData.ATK.value += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttack, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttackAdd, + avatarProfile?.lightcone?.level + ) + statsData.ATK.base += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttack, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttackAdd, + avatarProfile?.lightcone?.level + ) + statsData.DEF.value += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefence, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefenceAdd, + avatarProfile?.lightcone?.level + ) + statsData.DEF.base += calcBaseStatRaw( + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefence, + mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefenceAdd, + avatarProfile?.lightcone?.level + ) + + const bonusData = mapLightCone[avatarProfile?.lightcone?.item_id]?.Skills?.Level?.[avatarProfile?.lightcone.rank]?.Bonus + if (bonusData && bonusData.length > 0) { + const bonusSpd = bonusData.filter((bonus) => bonus.PropertyType === "BaseSpeed") + const bonusOther = bonusData.filter((bonus) => bonus.PropertyType !== "BaseSpeed") + bonusSpd.forEach((bonus) => { + statsData.SPD.value += bonus.Value + statsData.SPD.base += bonus.Value + }) + bonusOther.forEach((bonus) => { + const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat + if (statsBase && statsData[statsBase]) { + statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value) + } + }) + } + } + if (avatarSkillTree) { + Object.values(avatarSkillTree).forEach((value) => { + if (value?.["1"] + && value?.["1"]?.PointID + && typeof avatarData?.data?.skills?.[value?.["1"]?.PointID] === "number" + && avatarData?.data?.skills?.[value?.["1"]?.PointID] !== 0 + && value?.["1"]?.StatusAddList + && value?.["1"].StatusAddList.length > 0) { + value?.["1"]?.StatusAddList.forEach((status) => { + const statsBase = mappingStats?.[status?.PropertyType]?.baseStat + if (statsBase && statsData[statsBase]) { + statsData[statsBase].value += calcBonusStatRaw(status?.PropertyType, statsData[statsBase].base, status.Value) + } + }) + } + }) + } + + if (avatarProfile?.relics && mainAffix && subAffix) { + Object.entries(avatarProfile?.relics).forEach(([key, value]) => { + const mainAffixMap = mainAffix["5" + key] + const subAffixMap = subAffix["5"] + if (!mainAffixMap || !subAffixMap) return + const mainStats = mappingStats?.[mainAffixMap?.[value.main_affix_id]?.Property]?.baseStat + if (mainStats && statsData[mainStats]) { + statsData[mainStats].value += calcMainAffixBonusRaw(mainAffixMap?.[value.main_affix_id], value.level, statsData[mainStats].base) + } + value?.sub_affixes.forEach((subValue) => { + const subStats = mappingStats?.[subAffixMap?.[subValue.sub_affix_id]?.Property]?.baseStat + if (subStats && statsData[subStats]) { + statsData[subStats].value += calcSubAffixBonusRaw(subAffixMap?.[subValue.sub_affix_id], subValue.step, subValue.count, statsData[subStats].base) + } + }) + }) + } + + if (relicEffects && relicEffects.length > 0) { + relicEffects.forEach((relic) => { + const dataBonus = mapRelicSet?.[relic.key]?.Skills + if (!dataBonus || Object.keys(dataBonus).length === 0) return + Object.entries(dataBonus || {}).forEach(([key, value]) => { + if (relic.count < Number(key)) return + value.Bonus.forEach((bonus) => { + const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat + if (statsBase && statsData[statsBase]) { + statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value) + } + }) + }) + }) + } + + + return statsData + }, [ + avatarSelected, + avatarData, + mapAvatar, + avatarProfile?.lightcone, + avatarProfile?.relics, + mapLightCone, + mainAffix, + subAffix, + relicEffects, + mapRelicSet, + avatarSkillTree + ]) + + const applyBrightness = useCallback((hex: string, brightness: number): string => { + const r = Math.round(parseInt(hex.slice(1, 3), 16) * brightness); + const g = Math.round(parseInt(hex.slice(3, 5), 16) * brightness); + const b = Math.round(parseInt(hex.slice(5, 7), 16) * brightness); + + return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`; + }, []) + + return ( +
    +
    + +
    + +
    +
    +
    + +
    +
    +
    +
    + {avatarSelected && ( + + )} +
    +
    + +
    + +
    + {avatarSelected && avatarSelected && avatarData?.data && typeof avatarData?.data?.rank === "number" && ( +
    + {Object.values(avatarSelected?.Ranks || {})?.map((rank, index) => { + const isActive = avatarData?.data?.rank > index; + return ( +
    + + {isActive && ( +
    + )} + +
    +
    + +
    +
    +
    + ); + })} +
    + )} + +
    + +
    +
    +
    +
    + +
    +
    +
    Lv. {avatarData?.level}/80
    + + {totalSubStats} + + + {avatarSelected && ( +
    + + + +
    + )} + +
    +
    + +
    + {avatarSelected && ( +
    + +
    + )} + +
    + {avatarData && avatarSelected && avatarSkillTree && traceShowCaseMap[avatarSelected?.BaseType || ""] + && Object.values(traceShowCaseMap[avatarSelected?.BaseType || ""] || []).map((item, index) => { + + return ( +
    + {item.map((btn, idx) => { + const size = btn.size || "small"; + const isBig = size === "big"; + const isMedium = size === "medium"; + const isBigMemory = size === "big-memory"; + + const sizeClass = isBigMemory ? "w-12 h-12 mx-1" : isBig ? "w-12 h-12 mx-1" : isMedium ? "w-10 h-10" : "w-8 h-8"; + + const imageSize = isBigMemory ? "w-10" : isBig ? "w-10" : isMedium ? "w-8" : "w-6"; + + const bgColor = isBigMemory + ? "bg-[#2a1a39]/80" + : isBig + ? "bg-[#2b1d00]/80" + : isMedium + ? "bg-[#2b1d00]/50" + : "bg-[#000000]/50"; + + const filterClass = isBigMemory + ? "filter sepia brightness-80 hue-rotate-[280deg] saturate-400 contrast-130" + : isBig + ? "filter sepia brightness-150 hue-rotate-15 saturate-200" + : ""; + + if (!avatarSkillTree?.[btn.id]) { + return null; + } + return ( +
    +
    + + + {(isBig || isBigMemory) && ( + + {avatarData?.data?.skills?.[avatarSkillTree?.[btn.id]?.["1"]?.PointID] ? avatarData?.data?.skills?.[avatarSkillTree?.[btn.id]?.["1"]?.PointID] : 1} + + )} +
    + + {btn.isLink && idx < item.length - 1 && ( +
    + )} +
    + ); + })} +
    + + ) + })} +
    + +
    + {avatarProfile && avatarProfile?.lightcone && lightconeStats ? ( +
    + +
    + {/* Background SVG Border (offset top-left) */} + + + + + {/* Card Image */} + + + {/* Top SVG Border (offset bottom-right) */} + + + + + + {/* Stars */} +
    + {[...Array( + Number( + mapLightCone[avatarProfile?.lightcone?.item_id]?.Rarity?.[ + mapLightCone[avatarProfile?.lightcone?.item_id]?.Rarity.length - 1 + ] || 0 + ) + )].map((_, i) => ( + + ))} +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    + {convertToRoman(avatarProfile?.lightcone?.rank)} +
    + + Lv. {avatarProfile.lightcone.level}/80 + +
    +
    + +
    +
    +
    + + { + lightconeStats?.hp + } +
    +
    + + {lightconeStats?.attack} +
    + +
    +
    + + {lightconeStats?.def} +
    +
    +
    + +
    + ) : (
    + {transI18n("noLightconeEquipped")} +
    )} +
    + +
    + +
    +
    + {Object.entries(characterStats || {})?.map(([key, stat], index) => { + if (!stat || (key.includes("Add") && stat.value === 0)) return null + return ( +
    +
    + + {stat.name} +
    +
    +
    { + stat.value ? stat.unit === "%" ? (stat.value * 100).toFixed(stat.round) : stat.value.toFixed(stat.round) : 0 + }{stat.unit}
    +
    + ) + })} +
    +
    + +
    + {relicEffects.map((setEffect, index) => { + const relicInfo = mapRelicSet[setEffect.key]; + if (!relicInfo) return null; + return ( +
    +
    +
    + {setEffect.count} +
    +
    + ) + })} +
    +
    + +
    +
    + + {relicStats?.map((relic, index) => { + if (!relic || !avatarSelected) return null + return ( + + ) + })} + + {(!relicStats || !relicStats?.length) && ( +
    +
    + {transI18n("noRelicEquipped")} +
    +
    + )} +
    +
    + +
    +
    + +
    +
    + +
    + ); +} diff --git a/src/components/showcaseCard/relicShowcase.tsx b/src/components/showcaseCard/relicShowcase.tsx new file mode 100644 index 0000000..795da02 --- /dev/null +++ b/src/components/showcaseCard/relicShowcase.tsx @@ -0,0 +1,108 @@ + +"use client" + +import NextImage from "next/image" +import { AvatarDetail, RelicShowcaseType } from "@/types"; + +export default function RelicShowcase({ + relic, + avatarInfo, +}: { + relic: RelicShowcaseType; + avatarInfo: AvatarDetail; +}) { + return ( + <> +
    + {/* Subtle glow overlay */} +
    + +
    +
    + + +
    + ✦✦✦✦✦ +
    +
    + +
    +
    +
    + +
    + + {relic?.mainAffix?.valueAffix + relic?.mainAffix?.detail?.unit} + + + +{relic?.mainAffix?.level} + +
    + +
    + +
    + {relic?.subAffix?.map((subAffix, index) => { + if (!subAffix) return null + return ( +
    +
    + {subAffix?.detail?.icon ? ( + + ) : ( +
    + ? +
    + )} + + +{subAffix?.valueAffix + subAffix?.detail?.unit} + + { + (avatarInfo?.Relics?.SubAffixPropertyList.findIndex((item) => item === subAffix?.property) !== -1) && ( + + {subAffix?.count} + + )} +
    +
    + ) + })} +
    +
    + + ) +} \ No newline at end of file diff --git a/src/components/skillsInfo/index.tsx b/src/components/skillsInfo/index.tsx new file mode 100644 index 0000000..28ad8ea --- /dev/null +++ b/src/components/skillsInfo/index.tsx @@ -0,0 +1,385 @@ +"use client" + +import { useTranslations } from "next-intl"; +import { useMemo } from "react"; +import { traceButtonsInfo, traceLink } from "@/constant/traceConstant"; +import useUserDataStore from "@/stores/userDataStore"; +import useLocaleStore from "@/stores/localeStore"; +import Image from "next/image"; +import { replaceByParam, getLocaleName } from '@/helper'; +import { mappingStats } from "@/constant/constant"; +import { toast } from "react-toastify"; +import useCurrentDataStore from "@/stores/currentDataStore"; +import { StatusAdd } from '@/types/avatarDetail'; +import { SkillDescription } from "./skillDescription"; + +export default function SkillsInfo() { + const transI18n = useTranslations("DataPage") + const { theme } = useLocaleStore() + const { avatarSelected, skillIDSelected, setSkillIDSelected } = useCurrentDataStore() + const { avatars, setAvatar } = useUserDataStore() + const { locale } = useLocaleStore() + const traceButtons = useMemo(() => { + if (!avatarSelected) return + return traceButtonsInfo[avatarSelected.BaseType] + }, [avatarSelected]) + + const avatarData = useMemo(() => { + if (!avatarSelected) return + return avatars[avatarSelected?.ID?.toString()] + }, [avatarSelected, avatars]) + + const avatarSkillTree = useMemo(() => { + if (!avatarSelected || !avatars[avatarSelected?.ID?.toString()]) return {} + if (avatars[avatarSelected?.ID?.toString()].enhanced) { + return avatarSelected?.Enhanced?.[avatars[avatarSelected?.ID?.toString()].enhanced.toString()].SkillTrees || {} + } + return avatarSelected?.SkillTrees || {} + }, [avatarSelected, avatars]) + + const skillInfo = useMemo(() => { + if (!avatarSelected || !skillIDSelected) return + return avatarSkillTree?.[skillIDSelected || ""]?.["1"] + }, [avatarSelected, avatarSkillTree, skillIDSelected]) + + const getTraceBuffDisplay = (status: StatusAdd) => { + const dataDisplay = mappingStats[status.PropertyType] + if (!dataDisplay) return "" + if (dataDisplay.unit === "%") { + return `${(status.Value * 100).toFixed(1)}${dataDisplay.unit}` + } + if (dataDisplay.name === "SPD") { + return `${status.Value.toFixed(1)}${dataDisplay.unit}` + } + return `${status.Value.toFixed(0)}${dataDisplay.unit}` + } + + const dataLevelUpSkill = useMemo(() => { + const skillIds: number[] = skillInfo?.LevelUpSkillID || [] + if (!avatarSelected || !avatarData) return undefined + let result = Object.values(avatarSelected.Skills || {})?.filter((skill) => skillIds.includes(skill.ID)) + if (avatarData.enhanced) { + result = Object.values(avatarSelected?.Enhanced?.[avatarData.enhanced.toString()]?.Skills || {})?.filter((skill) => skillIds.includes(skill.ID)) + } + + if (result && result.length > 0) { + return { + isServant: false, + data: result, + servantData: null, + } + } + const resultServant = Object.values(avatarSelected?.Memosprite?.Skills || {}) + ?.filter((skill) => skillIds.includes(skill.ID)) + + if (resultServant && resultServant.length > 0) { + return { + isServant: true, + data: resultServant, + servantData: avatarSelected.Memosprite, + } + } + return undefined + }, [skillInfo?.LevelUpSkillID, avatarSelected, avatarData]) + + + const handlerMaxAll = () => { + if (!avatarData || !avatarSkillTree) { + toast.error(transI18n("maxAllFailed")) + return + } + const newData = structuredClone(avatarData) + newData.data.skills = Object.values(avatarSkillTree).reduce((acc, dataPointEntry) => { + const firstEntry = Object.values(dataPointEntry)[0]; + if (firstEntry) { + acc[firstEntry.PointID] = firstEntry.MaxLevel; + } + return acc; + }, {} as Record) + toast.success(transI18n("maxAllSuccess")) + setAvatar(newData) + } + + const handlerChangeStatusTrace = (status: boolean) => { + if (!avatarData || !skillInfo) return + const newData = structuredClone(avatarData) + newData.data.skills[skillInfo?.PointID] = status ? 1 : 0 + + if (!status && traceLink?.[avatarSelected?.BaseType || ""]?.[skillIDSelected || ""]) { + traceLink[avatarSelected?.BaseType || ""][skillIDSelected || ""].forEach((pointId) => { + if (avatarSkillTree?.[pointId]?.["1"]) { + newData.data.skills[avatarSkillTree?.[pointId]?.["1"].PointID] = 0 + } + }) + } + setAvatar(newData) + } + + return ( +
    +
    +
    +

    +
    + {transI18n("skills")} +

    +
    + + {traceButtons && avatarSelected && ( +
    + + {traceButtons.map((btn, index) => { + if (!avatarSelected?.SkillTrees?.[btn.id]) { + return null + } + return ( +
    { + setSkillIDSelected(btn.id === skillIDSelected ? null : btn.id) + }} + style={{ + left: btn.left, + top: btn.top, + transform: "translate(-50%, -50%)", + }} + > + {btn.id.replaceAll("Point", + {(btn.size === "big" || btn.size === "memory" || btn.size === "elation") && ( +

    + {`${avatarData?.data.skills?.[avatarSkillTree?.[btn.id]?.["1"]?.PointID] || 0}/${avatarSkillTree?.[btn.id]?.["1"]?.MaxLevel}`} +

    + + )} + {btn.size === "special" && ( +
    + )} + {btn.size === "memory" && ( +
    + )} + {btn.size === "elation" && ( +
    + )} + {btn.size === "big" && ( +
    + )} +
    + + + ) + })} +
    + )} + + {!traceButtons && avatarSelected && ( +
    + +
    + )} +
    + +
    +
    +

    +
    + {transI18n("details")} +

    + {skillIDSelected && avatarSelected?.SkillTrees && avatarData && ( +
    + {skillInfo?.MaxLevel && skillInfo?.MaxLevel > 1 ? ( +
    +
    {transI18n("level")}
    +
    + { + const newData = structuredClone(avatarData) + newData.data.skills[skillInfo?.PointID] = parseInt(e.target.value) + setAvatar(newData) + }} + className="range range-success" + step="1" /> +
    + {Array.from({ length: skillInfo?.MaxLevel }, (_, index) => index + 1).map((index) => ( + {index} + ))} +
    +
    +
    + ) : skillInfo?.MaxLevel && skillInfo?.MaxLevel === 1 && traceButtons?.find((btn) => btn.id === skillIDSelected)?.size !== "big" ? ( +
    + { + if (traceButtons?.find((btn) => btn.id === skillIDSelected)?.size === "special") { + if (e.target.checked) { + const newData = structuredClone(avatarData) + newData.data.skills[skillInfo?.PointID] = 1 + setAvatar(newData) + return + } + const newData = structuredClone(avatarData) + delete newData.data.skills[skillInfo?.PointID] + setAvatar(newData) + return + } + handlerChangeStatusTrace(e.target.checked) + }} + /> +
    + {avatarData?.data.skills?.[skillInfo?.PointID] === 1 ? transI18n("active") : transI18n("inactive")} +
    +
    + ) : ( + null + )} + + {((skillInfo?.PointName && skillInfo?.PointDesc) || + (skillInfo?.PointName && skillInfo?.StatusAddList.length > 0)) + && ( +
    + {getLocaleName(locale, skillInfo.PointName)} + {skillInfo.StatusAddList.length > 0 && ( +
    + {skillInfo.StatusAddList.map((status, index) => ( +
    +
    {getTraceBuffDisplay(status)}
    +
    + ))} +
    + )} +
    + )} + + {skillInfo?.PointDesc && ( +
    + )} + + {skillInfo?.LevelUpSkillID + && skillInfo?.LevelUpSkillID.length > 0 + && dataLevelUpSkill + && ( +
    + + {dataLevelUpSkill?.data?.map((skill, index) => ( +
    + +
    + {transI18n(dataLevelUpSkill.isServant ? `${skill?.AttackType ? "severaltalent" : "servantskill"}` : `${skill?.AttackType ? skill?.AttackType.toLowerCase() : "talent"}`)} + {` (${transI18n(skill?.SkillEffect?.toLowerCase())})`} +
    + + +
    + ))} + +
    + )} +
    + )} +
    +
    +
    + ); +} \ No newline at end of file diff --git a/src/components/skillsInfo/skillDescription.tsx b/src/components/skillsInfo/skillDescription.tsx new file mode 100644 index 0000000..8abc2ac --- /dev/null +++ b/src/components/skillsInfo/skillDescription.tsx @@ -0,0 +1,41 @@ +import { getLocaleName, replaceByParam } from "@/helper"; +import { AvatarStore, SkillDetail, SkillTreePoint } from "@/types"; +import ExtraEffectList from "../extraInfo"; + +export const SkillDescription = ({ skill, locale, avatarData, skillInfo }: { + skill: SkillDetail, + locale: string, + avatarData: AvatarStore, + skillInfo: SkillTreePoint +}) => { + const levelKey = avatarData?.data.skills?.[skillInfo?.PointID]?.toString() || ""; + const params = skill.Level[levelKey]?.Param || []; + const descHtml = getLocaleName(locale, skill.Desc) || getLocaleName(locale, skill.SimpleDesc); + + const extraList = Object.values(skill.Extra).length > 0 + ? skill.Extra + : skill?.SimpleExtra || {}; + + return ( +
    +
    +
    +
    +
    +
    + +
    +
    + + {Object.keys(extraList).length > 0 && ( + + )} +
    + ); +}; \ No newline at end of file diff --git a/src/components/themeController/clientThemeWrapper.tsx b/src/components/themeController/clientThemeWrapper.tsx new file mode 100644 index 0000000..704e8f5 --- /dev/null +++ b/src/components/themeController/clientThemeWrapper.tsx @@ -0,0 +1,8 @@ +"use client"; +import { PropsWithChildren, useContext } from "react"; +import { ThemeContext } from "./themeContext"; + +export function ClientThemeWrapper({ children }: PropsWithChildren) { + const { theme } = useContext(ThemeContext); + return
    {children}
    ; +} \ No newline at end of file diff --git a/src/components/themeController/index.ts b/src/components/themeController/index.ts new file mode 100644 index 0000000..5600303 --- /dev/null +++ b/src/components/themeController/index.ts @@ -0,0 +1,2 @@ +export * from "./clientThemeWrapper" +export * from "./themeContext" diff --git a/src/components/themeController/themeContext.tsx b/src/components/themeController/themeContext.tsx new file mode 100644 index 0000000..0033644 --- /dev/null +++ b/src/components/themeController/themeContext.tsx @@ -0,0 +1,41 @@ +"use client"; +import { createContext, PropsWithChildren, useEffect } from "react"; +import useLocaleStore from "@/stores/localeStore"; + +interface ThemeContextType { + theme?: string; + changeTheme?: (nextTheme: string | null) => void; +} +export const ThemeContext = createContext({}); + +export const ThemeProvider = ({ children }: PropsWithChildren) => { + + const { theme, setTheme } = useLocaleStore() + + useEffect(() => { + if (typeof window !== "undefined") { + const storedTheme = localStorage.getItem("theme"); + if (storedTheme) setTheme(storedTheme); + } + }, [setTheme]); + + const changeTheme = (nextTheme: string | null) => { + if (nextTheme) { + setTheme(nextTheme); + if (typeof window !== "undefined") { + localStorage.setItem("theme", nextTheme); + } + } else { + setTheme(theme === "winter" ? "night" : "winter"); + if (typeof window !== "undefined") { + localStorage.setItem("theme", theme); + } + } + }; + + return ( + + {children} + + ); +}; \ No newline at end of file diff --git a/src/constant/constant.ts b/src/constant/constant.ts new file mode 100644 index 0000000..e06f9e2 --- /dev/null +++ b/src/constant/constant.ts @@ -0,0 +1,206 @@ +export const listCurrentLanguage = { + ja: "JP", + ko: "KR", + en: "EN", + vi: "VN", + zh: "CN" +}; + +export const listCurrentLanguageApi : Record = { + ja: "jp", + ko: "kr", + en: "en", + vi: "vi", + zh: "cn" +}; + +export const mappingStats = > { + "HPDelta": { + name:"HP", + icon:"spriteoutput/ui/avatar/icon/IconMaxHP.png", + unit: "", + baseStat: "HP" + }, + "AttackDelta": { + name:"ATK", + icon:"spriteoutput/ui/avatar/icon/IconAttack.png", + unit: "", + baseStat: "ATK" + }, + "HPAddedRatio": { + name:"HP", + icon:"spriteoutput/ui/avatar/icon/IconMaxHP.png", + unit: "%", + baseStat: "HP" + }, + "AttackAddedRatio": { + name:"ATK", + icon:"spriteoutput/ui/avatar/icon/IconAttack.png", + unit: "%", + baseStat: "ATK" + }, + "DefenceDelta": { + name:"DEF", + icon:"spriteoutput/ui/avatar/icon/IconDefence.png", + unit: "", + baseStat: "DEF" + }, + "DefenceAddedRatio": { + name:"DEF", + icon:"spriteoutput/ui/avatar/icon/IconDefence.png", + unit: "%", + baseStat: "DEF" + }, + "SpeedAddedRatio": { + name:"SPD", + icon:"spriteoutput/ui/avatar/icon/IconSpeed.png", + unit: "%", + baseStat: "SPD" + }, + "BaseSpeed": { + name:"SPD", + icon:"spriteoutput/ui/avatar/icon/IconSpeed.png", + unit: "", + baseStat: "SPD" + }, + "CriticalChanceBase": { + name:"CRIT Rate", + icon:"spriteoutput/ui/avatar/icon/IconCriticalChance.png", + unit: "%", + baseStat: "CRITRate" + }, + "CriticalDamageBase": { + name:"CRIT DMG", + icon:"spriteoutput/ui/avatar/icon/IconCriticalDamage.png", + unit: "%", + baseStat: "CRITDmg" + }, + "HealRatioBase": { + name:"Outgoing Healing Boost", + icon:"spriteoutput/ui/avatar/icon/IconHealRatio.png", + unit: "%", + baseStat: "HealBoost" + }, + "StatusProbabilityBase": { + name:"Effect Hit Rate", + icon:"spriteoutput/ui/avatar/icon/IconStatusProbability.png", + unit: "%", + baseStat: "EffectHitRate" + }, + "StatusResistanceBase": { + name:"Effect RES", + icon:"spriteoutput/ui/avatar/icon/IconStatusResistance.png", + unit: "%", + baseStat: "EffectRES" + }, + "BreakDamageAddedRatioBase": { + name:"Break Effect", + icon:"spriteoutput/ui/avatar/icon/IconBreakUp.png", + unit: "%", + baseStat: "BreakEffect" + }, + "SpeedDelta": { + name:"SPD", + icon:"spriteoutput/ui/avatar/icon/IconSpeed.png", + unit: "", + baseStat: "SPD" + }, + "PhysicalAddedRatio": { + name:"Physical DMG Boost", + icon:"spriteoutput/ui/avatar/icon/IconPhysicalAddedRatio.png", + unit: "%", + baseStat: "PhysicalAdd" + }, + "FireAddedRatio": { + name:"Fire DMG Boost", + icon:"spriteoutput/ui/avatar/icon/IconFireAddedRatio.png", + unit: "%", + baseStat: "FireAdd" + }, + "IceAddedRatio": { + name:"Ice DMG Boost", + icon:"spriteoutput/ui/avatar/icon/IconIceAddedRatio.png", + unit: "%", + baseStat: "IceAdd" + }, + "ThunderAddedRatio": { + name:"Thunder DMG Boost", + icon:"spriteoutput/ui/avatar/icon/IconThunderAddedRatio.png", + unit: "%", + baseStat: "ThunderAdd" + }, + "WindAddedRatio": { + name:"Wind DMG Boost", + icon:"spriteoutput/ui/avatar/icon/IconWindAddedRatio.png", + unit: "%", + baseStat: "WindAdd" + }, + "QuantumAddedRatio": { + name:"Quantum DMG Boost", + icon:"spriteoutput/ui/avatar/icon/IconQuantumAddedRatio.png", + unit: "%", + baseStat: "QuantumAdd" + }, + "ImaginaryAddedRatio": { + name:"Imaginary DMG Boost", + icon:"spriteoutput/ui/avatar/icon/IconImaginaryAddedRatio.png", + unit: "%", + baseStat: "ImaginaryAdd" + }, + "ElationDamageAddedRatioBase": { + name:"Elation DMG Boost", + icon:"spriteoutput/ui/avatar/icon/IconJoy.png", + unit: "%", + baseStat: "ElationAdd" + }, + "SPRatioBase": { + name:"Energy Regeneration Rate", + icon:"spriteoutput/ui/avatar/icon/IconEnergyRecovery.png", + unit: "%", + baseStat: "EnergyRate" + } +} + + +export const ratioStats = [ + "HPAddedRatio", + "AttackAddedRatio", + "DefenceAddedRatio", + "SpeedAddedRatio", +] + +export const mappingRelicSlot: Record = { + "1": "HEAD", + "2": "HAND", + "3": "BODY", + "4": "FOOT", + "5": "NECK", + "6": "OBJECT", +} + +export const themeColors: Record = { + winter: { + bg: '#ffffff', + bgHover: '#f1f5f9', + text: '#3a4f6b', + border: '#cbd5e1' + }, + night: { + bg: '#1d232a', + bgHover: '#2a323c', + text: '#cbcdd1', + border: '#3f3f46' + }, + cupcake: { + bg: '#faf7f5', + bgHover: '#f3eae6', + text: '#281333', + border: '#e5d3cb' + }, + coffee: { + bg: '#20161f', + bgHover: '#2a1d29', + text: '#c4a051', + border: '#3a2a36' + } +} diff --git a/src/constant/traceConstant.ts b/src/constant/traceConstant.ts new file mode 100644 index 0000000..5522426 --- /dev/null +++ b/src/constant/traceConstant.ts @@ -0,0 +1,1148 @@ + + +export const traceButtonsInfo: Record = { + Knight: [ + { id: 'Point03', size: 'big', left: '50%', top: '52%' }, + { id: 'Point04', size: 'big', left: '50%', top: '35%' }, + { id: 'Point02', size: 'big', left: '67%', top: '55%' }, + { id: 'Point05', size: 'big', left: '50%', top: '69.5%' }, + { id: 'Point01', size: 'big', left: '33%', top: '55%' }, + { id: 'Point08', size: 'medium', left: '50%', top: '21.5%' }, + { id: 'Point07', size: 'medium', left: '71%', top: '86%' }, + { id: 'Point06', size: 'medium', left: '29%', top: '86%' }, + { id: 'Point16', size: 'small', left: '50%', top: '9%' }, + { id: 'Point18', size: 'small', left: '66%', top: '13%' }, + { id: 'Point17', size: 'small', left: '34%', top: '13%' }, + { id: 'Point15', size: 'small', left: '79.5%', top: '44.5%' }, + { id: 'Point12', size: 'small', left: '20.5%', top: '44.5%' }, + { id: 'Point09', size: 'small', left: '50%', top: '82.5%' }, + { id: 'Point13', size: 'small', left: '81%', top: '78%' }, + { id: 'Point10', size: 'small', left: '19.5%', top: '78.5%' }, + { id: 'Point14', size: 'small', left: '89%', top: '70%' }, + { id: 'Point11', size: 'small', left: '11%', top: '70%' } + ], + Mage: [ + { id: 'Point03', size: 'big', left: '50%', top: '52%' }, + { id: 'Point04', size: 'big', left: '50%', top: '30%' }, + { id: 'Point02', size: 'big', left: '67%', top: '52%' }, + { id: 'Point05', size: 'big', left: '50%', top: '82.5%' }, + { id: 'Point01', size: 'big', left: '33.5%', top: '52%' }, + { id: 'Point08', size: 'medium', left: '50%', top: '14.5%' }, + { id: 'Point07', size: 'medium', left: '80.5%', top: '53%' }, + { id: 'Point06', size: 'medium', left: '19.5%', top: '53%' }, + { id: 'Point17', size: 'small', left: '66%', top: '17.5%' }, + { id: 'Point16', size: 'small', left: '34%', top: '17.5%' }, + { id: 'Point18', size: 'small', left: '66%', top: '79%' }, + { id: 'Point09', size: 'small', left: '34%', top: '79%' }, + { id: 'Point13', size: 'small', left: '93%', top: '52%' }, + { id: 'Point14', size: 'small', left: '89%', top: '63%' }, + { id: 'Point15', size: 'small', left: '89%', top: '41.5%' }, + { id: 'Point10', size: 'small', left: '7%', top: '52%' }, + { id: 'Point11', size: 'small', left: '11.5%', top: '63%' }, + { id: 'Point12', size: 'small', left: '12%', top: '41.5%' } + ], + Priest: [ + { id: 'Point03', size: 'big', left: '50%', top: '47%' }, + { id: 'Point04', size: 'big', left: '50%', top: '30%' }, + { id: 'Point02', size: 'big', left: '67%', top: '46%' }, + { id: 'Point05', size: 'big', left: '50%', top: '65%' }, + { id: 'Point01', size: 'big', left: '33.5%', top: '46%' }, + { id: 'Point08', size: 'medium', left: '50%', top: '13%' }, + { id: 'Point07', size: 'medium', left: '68%', top: '79%' }, + { id: 'Point06', size: 'medium', left: '33%', top: '79%' }, + { id: 'Point16', size: 'small', left: '65%', top: '18%' }, + { id: 'Point17', size: 'small', left: '35%', top: '18%' }, + { id: 'Point09', size: 'small', left: '57%', top: '89%' }, + { id: 'Point18', size: 'small', left: '43%', top: '89%' }, + { id: 'Point10', size: 'small', left: '80.5%', top: '66%' }, + { id: 'Point11', size: 'small', left: '93%', top: '52%' }, + { id: 'Point12', size: 'small', left: '81%', top: '39%' }, + { id: 'Point13', size: 'small', left: '20.5%', top: '66%' }, + { id: 'Point14', size: 'small', left: '7%', top: '52%' }, + { id: 'Point15', size: 'small', left: '20%', top: '39%' } + ], + Rogue: [ + { id: 'Point03', size: 'big', left: '50%', top: '52%' }, + { id: 'Point04', size: 'big', left: '50%', top: '35%' }, + { id: 'Point02', size: 'big', left: '67%', top: '46%' }, + { id: 'Point05', size: 'big', left: '50%', top: '69%' }, + { id: 'Point01', size: 'big', left: '33.5%', top: '46%' }, + { id: 'Point08', size: 'medium', left: '50%', top: '22%' }, + { id: 'Point07', size: 'medium', left: '68%', top: '71%' }, + { id: 'Point06', size: 'medium', left: '30%', top: '71%' }, + { id: 'Point16', size: 'small', left: '50%', top: '9%' }, + { id: 'Point18', size: 'small', left: '66%', top: '14%' }, + { id: 'Point17', size: 'small', left: '34%', top: '14%' }, + { id: 'Point09', size: 'small', left: '50%', top: '87%' }, + { id: 'Point15', size: 'small', left: '81%', top: '35%' }, + { id: 'Point12', size: 'small', left: '19%', top: '35%' }, + { id: 'Point13', size: 'small', left: '81%', top: '57%' }, + { id: 'Point10', size: 'small', left: '20%', top: '57%' }, + { id: 'Point14', size: 'small', left: '93%', top: '44%' }, + { id: 'Point11', size: 'small', left: '7%', top: '44%' } + ], + Shaman: [ + { id: 'Point03', size: 'big', left: '50%', top: '56%' }, + { id: 'Point04', size: 'big', left: '50%', top: '35%' }, + { id: 'Point02', size: 'big', left: '67%', top: '41%' }, + { id: 'Point05', size: 'big', left: '50%', top: '74%' }, + { id: 'Point01', size: 'big', left: '33.5%', top: '41%' }, + { id: 'Point08', size: 'medium', left: '50%', top: '22%' }, + { id: 'Point07', size: 'medium', left: '89.5%', top: '56.5%' }, + { id: 'Point06', size: 'medium', left: '17.5%', top: '56.5%' }, + { id: 'Point16', size: 'small', left: '50%', top: '10%' }, + { id: 'Point18', size: 'small', left: '66%', top: '14%' }, + { id: 'Point17', size: 'small', left: '34%', top: '14%' }, + { id: 'Point09', size: 'small', left: '50%', top: '87%' }, + { id: 'Point15', size: 'small', left: '62%', top: '83%' }, + { id: 'Point12', size: 'small', left: '38%', top: '83%' }, + { id: 'Point13', size: 'small', left: '77%', top: '70%' }, + { id: 'Point14', size: 'small', left: '67%', top: '61%' }, + { id: 'Point10', size: 'small', left: '7%', top: '44%' }, + { id: 'Point11', size: 'small', left: '20%', top: '31%' } + ], + Warlock: [ + { id: 'Point03', size: 'big', left: '50%', top: '44%' }, + { id: 'Point04', size: 'big', left: '50%', top: '24%' }, + { id: 'Point02', size: 'big', left: '67%', top: '47%' }, + { id: 'Point05', size: 'big', left: '50%', top: '61%' }, + { id: 'Point01', size: 'big', left: '33%', top: '47%' }, + { id: 'Point08', size: 'medium', left: '50%', top: '8%' }, + { id: 'Point07', size: 'medium', left: '81.5%', top: '37%' }, + { id: 'Point06', size: 'medium', left: '20.5%', top: '37%' }, + { id: 'Point17', size: 'small', left: '66%', top: '14%' }, + { id: 'Point16', size: 'small', left: '34%', top: '14%' }, + { id: 'Point09', size: 'small', left: '50%', top: '74%' }, + { id: 'Point18', size: 'small', left: '50%', top: '87%' }, + { id: 'Point13', size: 'small', left: '94%', top: '48%' }, + { id: 'Point14', size: 'small', left: '81%', top: '61%' }, + { id: 'Point15', size: 'small', left: '68%', top: '74%' }, + { id: 'Point10', size: 'small', left: '6%', top: '48%' }, + { id: 'Point11', size: 'small', left: '20%', top: '61%' }, + { id: 'Point12', size: 'small', left: '33%', top: '74%' } + ], + Warrior: [ + { id: 'Point03', size: 'big', left: '50%', top: '52%' }, + { id: 'Point04', size: 'big', left: '50%', top: '35%' }, + { id: 'Point02', size: 'big', left: '69%', top: '48%' }, + { id: 'Point05', size: 'big', left: '50%', top: '69%' }, + { id: 'Point01', size: 'big', left: '33%', top: '48%' }, + { id: 'Point08', size: 'medium', left: '50%', top: '22%' }, + { id: 'Point07', size: 'medium', left: '67%', top: '83%' }, + { id: 'Point06', size: 'medium', left: '33%', top: '83%' }, + { id: 'Point16', size: 'small', left: '50%', top: '9%' }, + { id: 'Point18', size: 'small', left: '66%', top: '14%' }, + { id: 'Point17', size: 'small', left: '34%', top: '14%' }, + { id: 'Point09', size: 'small', left: '50%', top: '87%' }, + { id: 'Point15', size: 'small', left: '81%', top: '43.5%' }, + { id: 'Point12', size: 'small', left: '19%', top: '43.5%' }, + { id: 'Point13', size: 'small', left: '81%', top: '70%' }, + { id: 'Point10', size: 'small', left: '19%', top: '70%' }, + { id: 'Point14', size: 'small', left: '93%', top: '56.5%' }, + { id: 'Point11', size: 'small', left: '7%', top: '56.5%' } + ], + Memory: [ + { id: 'Point03', size: 'big', left: '50%', top: '72%' }, + { id: 'Point04', size: 'big', left: '75%', top: '53%' }, + { id: 'Point02', size: 'big', left: '67%', top: '67%' }, + { id: 'Point05', size: 'big', left: '27%', top: '53%' }, + { id: 'Point01', size: 'big', left: '35%', top: '67%' }, + { id: 'Point08', size: 'medium', left: '34%', top: '34%' }, + { id: 'Point07', size: 'medium', left: '50%', top: '87%' }, + { id: 'Point06', size: 'medium', left: '90%', top: '50%' }, + { id: 'Point16', size: 'small', left: '27.5%', top: '22%' }, + { id: 'Point17', size: 'small', left: '43%', top: '14%' }, + { id: 'Point18', size: 'small', left: '59%', top: '14%' }, + { id: 'Point19', size: 'memory', left: '50%', top: '48%' }, + { id: 'Point20', size: 'memory', left: '50%', top: '28%' }, + { id: 'Point12', size: 'small', left: '86%', top: '40%' }, + { id: 'Point13', size: 'small', left: '86%', top: '63%' }, + { id: 'Point14', size: 'small', left: '35%', top: '82%' }, + { id: 'Point15', size: 'small', left: '65%', top: '82%' }, + { id: 'Point09', size: 'small', left: '9%', top: '50%' }, + { id: 'Point10', size: 'small', left: '13%', top: '40%' }, + { id: 'Point11', size: 'small', left: '13%', top: '63%' }, + { id: 'Point21', size: 'special', left: '70%', top: '34%'} + ], + Elation: [ + { id: 'Point04', size: 'big', left: '50%', top: '50.5%' }, + { id: 'Point03', size: 'big', left: '50%', top: '35%' }, + { id: 'Point02', size: 'big', left: '67.5%', top: '20%' }, + { id: 'Point05', size: 'big', left: '50%', top: '68.5%' }, + { id: 'Point01', size: 'big', left: '32.5%', top: '20%' }, + { id: 'Point08', size: 'medium', left: '85%', top: '30%' }, + { id: 'Point07', size: 'medium', left: '15%', top: '30%' }, + { id: 'Point06', size: 'medium', left: '50%', top: '87%' }, + { id: 'Point12', size: 'small', left: '65%', top: '88%' }, + { id: 'Point15', size: 'small', left: '23%', top: '53%' }, + { id: 'Point17', size: 'small', left: '88%', top: '63%' }, + { id: 'Point18', size: 'small', left: '76%', top: '53%' }, + { id: 'Point13', size: 'small', left: '9%', top: '48.5%' }, + { id: 'Point16', size: 'small', left: '90.5%', top: '48.5%' }, + { id: 'Point09', size: 'small', left: '36%', top: '64%' }, + { id: 'Point10', size: 'small', left: '64%', top: '64%' }, + { id: 'Point14', size: 'small', left: '11.5%', top: '63%' }, + { id: 'Point11', size: 'small', left: '35%', top: '88%' }, + { id: 'Point22', size: 'elation', left: '50%', top: '17%' } + ], +} + +export const traceLink : Record> = { + Knight: { + Point06: ["Point12", "Point11"], + + Point08: ["Point16", "Point17", "Point18"], + Point16: ["Point17", "Point18"], + Point17: ["Point18"], + + Point07: ["Point13", "Point14", "Point15"], + Point13: ["Point14", "Point15"], + Point14: ["Point15"], + }, + Mage: { + Point08: ["Point16", "Point17"], + Point07: ["Point13", "Point14", "Point15"], + Point13: ["Point14", "Point15"], + Point06: ["Point10", "Point11", "Point12"], + Point10: ["Point11", "Point12"], + }, + Priest: { + Point08: ["Point16", "Point17"], + Point07: ["Point10", "Point11", "Point12"], + Point10: ["Point11", "Point12"], + Point11: ["Point12"], + Point06: ["Point13", "Point14", "Point15"], + Point13: ["Point14", "Point15"], + Point14: ["Point15"], + }, + Rogue: { + Point08: ["Point16", "Point17", "Point18"], + Point16: ["Point17", "Point18"], + Point07: ["Point13", "Point14"], + Point13: ["Point14"], + Point06: ["Point10", "Point11"], + Point10: ["Point11"], + }, + Shaman: { + Point08: ["Point16", "Point17", "Point18"], + Point16: ["Point17", "Point18"], + Point07: ["Point13", "Point14"], + Point13: ["Point14"], + Point06: ["Point10", "Point11"], + Point10: ["Point11"], + Point09: ["Point15", "Point12"], + }, + Warlock: { + Point08: ["Point16", "Point17"], + Point07: ["Point13", "Point14", "Point15"], + Point13: ["Point14", "Point15"], + Point14: ["Point15"], + Point06: ["Point10", "Point11", "Point12"], + Point10: ["Point11", "Point12"], + Point11: ["Point12"], + Point09: ["Point18"], + }, + Warrior: { + Point08: ["Point16", "Point17", "Point18"], + Point16: ["Point17", "Point18"], + Point07: ["Point13", "Point14", "Point15"], + Point13: ["Point14", "Point15"], + Point14: ["Point15"], + Point06: ["Point10", "Point11", "Point12"], + Point10: ["Point11", "Point12"], + Point11: ["Point12"], + }, + Memory: { + Point16: ["Point17", "Point18"], + Point08: ["Point16", "Point17", "Point18"], + Point17: ["Point18"], + Point09: ["Point10", "Point11"], + Point07: ["Point14", "Point15"], + Point06: ["Point12", "Point13"], + }, + Elation: { + Point08: ["Point16", "Point17", "Point18"], + Point16: ["Point17", "Point18"], + Point07: ["Point13", "Point14", "Point15"], + Point13: ["Point14", "Point15"], + Point14: ["Point15"], + Point06: ["Point10", "Point11", "Point12"], + Point10: ["Point11", "Point12"], + Point11: ["Point12"], + }, +} + +export const traceShowCaseMap : Record> = { + Knight: { + "1": [ + { + id: "Point01", + size: "big", + isLink: false + }, + { + id: "Point15", + size: "small", + isLink: true + }, + { + id: "Point12", + size: "small", + isLink: true + }, + { + id: "Point09", + size: "small", + isLink: true + } + ], + "2": [ + { + id: "Point02", + size: "big", + isLink: false + }, + { + id: "Point06", + size: "medium", + isLink: true + }, + { + id: "Point10", + size: "small", + isLink: true + }, + { + id: "Point11", + size: "small", + isLink: true + } + ], + "3": [ + { + id: "Point03", + size: "big", + isLink: false + }, + { + id: "Point07", + size: "medium", + isLink: true + }, + { + id: "Point13", + size: "small", + isLink: true + }, + { + id: "Point14", + size: "small", + isLink: true + } + ], + "4": [ + { + id: "Point04", + size: "big", + isLink: false + }, + { + id: "Point08", + size: "medium", + isLink: true + }, + { + id: "Point16", + size: "small", + isLink: true + }, + { + id: "Point18", + size: "small", + isLink: true + }, + { + id: "Point17", + size: "small", + isLink: true + } + ] + }, + Mage: { + "1": [ + { + id: "Point01", + size: "big", + isLink: false + }, + { + id: "Point09", + size: "small", + isLink: true + }, + { + id: "Point18", + size: "small", + isLink: true + } + ], + "2": [ + { + id: "Point02", + size: "big", + isLink: false + }, + { + id: "Point06", + size: "medium", + isLink: true + }, + { + id: "Point10", + size: "small", + isLink: true + }, + { + id: "Point11", + size: "small", + isLink: true + }, + { + id: "Point12", + size: "small", + isLink: true + } + ], + "3": [ + { + id: "Point03", + size: "big", + isLink: false + }, + { + id: "Point07", + size: "medium", + isLink: true + }, + { + id: "Point13", + size: "small", + isLink: true + }, + { + id: "Point14", + size: "small", + isLink: true + }, + { + id: "Point15", + size: "small", + isLink: true + } + ], + "4": [ + { + id: "Point04", + size: "big", + isLink: false + }, + { + id: "Point08", + size: "medium", + isLink: true + }, + { + id: "Point17", + size: "small", + isLink: true + }, + { + id: "Point16", + size: "small", + isLink: true + } + ] + }, + Priest: { + "1": [ + { + id: "Point01", + size: "big", + isLink: false + }, + { + id: "Point09", + size: "small", + isLink: true + }, + { + id: "Point18", + size: "small", + isLink: true + } + ], + "2": [ + { + id: "Point02", + size: "big", + isLink: false + }, + { + id: "Point06", + size: "medium", + isLink: true + }, + { + id: "Point10", + size: "small", + isLink: true + }, + { + id: "Point11", + size: "small", + isLink: true + }, + { + id: "Point12", + size: "small", + isLink: true + } + ], + "3": [ + { + id: "Point03", + size: "big", + isLink: false + }, + { + id: "Point07", + size: "medium", + isLink: true + }, + { + id: "Point13", + size: "small", + isLink: true + }, + { + id: "Point14", + size: "small", + isLink: true + }, + { + id: "Point15", + size: "small", + isLink: true + } + ], + "4": [ + { + id: "Point04", + size: "big", + isLink: false + }, + { + id: "Point08", + size: "medium", + isLink: true + }, + { + id: "Point17", + size: "small", + isLink: true + }, + { + id: "Point16", + size: "small", + isLink: true + } + ] + }, + Rogue: { + "1": [ + { + id: "Point01", + size: "big", + isLink: false + }, + { + id: "Point15", + size: "small", + isLink: true + }, + { + id: "Point12", + size: "small", + isLink: true + }, + { + id: "Point09", + size: "small", + isLink: true + } + ], + "2": [ + { + id: "Point02", + size: "big", + isLink: false + }, + { + id: "Point06", + size: "medium", + isLink: true + }, + { + id: "Point10", + size: "small", + isLink: true + }, + { + id: "Point11", + size: "small", + isLink: true + } + ], + "3": [ + { + id: "Point03", + size: "big", + isLink: false + }, + { + id: "Point07", + size: "medium", + isLink: true + }, + { + id: "Point13", + size: "small", + isLink: true + }, + { + id: "Point14", + size: "small", + isLink: true + } + ], + "4": [ + { + id: "Point04", + size: "big", + isLink: false + }, + { + id: "Point08", + size: "medium", + isLink: true + }, + { + id: "Point16", + size: "small", + isLink: true + }, + { + id: "Point18", + size: "small", + isLink: true + }, + { + id: "Point17", + size: "small", + isLink: true + } + ] + }, + Shaman: { + "1": [ + { + id: "Point01", + size: "big", + isLink: false + }, + { + id: "Point15", + size: "small", + isLink: true + }, + { + id: "Point12", + size: "small", + isLink: true + }, + { + id: "Point09", + size: "small", + isLink: true + } + ], + "2": [ + { + id: "Point02", + size: "big", + isLink: false + }, + { + id: "Point06", + size: "medium", + isLink: true + }, + { + id: "Point10", + size: "small", + isLink: true + }, + { + id: "Point11", + size: "small", + isLink: true + } + ], + "3": [ + { + id: "Point03", + size: "big", + isLink: false + }, + { + id: "Point07", + size: "medium", + isLink: true + }, + { + id: "Point13", + size: "small", + isLink: true + }, + { + id: "Point14", + size: "small", + isLink: true + } + ], + "4": [ + { + id: "Point04", + size: "big", + isLink: false + }, + { + id: "Point08", + size: "medium", + isLink: true + }, + { + id: "Point16", + size: "small", + isLink: true + }, + { + id: "Point18", + size: "small", + isLink: true + }, + { + id: "Point17", + size: "small", + isLink: true + } + ] + }, + Warlock: { + "1": [ + { + id: "Point01", + size: "big", + isLink: false + }, + { + id: "Point09", + size: "small", + isLink: true + }, + { + id: "Point18", + size: "small", + isLink: true + } + ], + "2": [ + { + id: "Point02", + size: "big", + isLink: false + }, + { + id: "Point06", + size: "medium", + isLink: true + }, + { + id: "Point10", + size: "small", + isLink: true + }, + { + id: "Point11", + size: "small", + isLink: true + }, + { + id: "Point12", + size: "small", + isLink: true + } + ], + "3": [ + { + id: "Point03", + size: "big", + isLink: false + }, + { + id: "Point07", + size: "medium", + isLink: true + }, + { + id: "Point13", + size: "small", + isLink: true + }, + { + id: "Point14", + size: "small", + isLink: true + }, + { + id: "Point15", + size: "small", + isLink: true + } + ], + "4": [ + { + id: "Point04", + size: "big", + isLink: false + }, + { + id: "Point08", + size: "medium", + isLink: true + }, + { + id: "Point17", + size: "small", + isLink: true + }, + { + id: "Point16", + size: "small", + isLink: true + } + ] + }, + Warrior: { + "1": [ + { + id: "Point01", + size: "big", + isLink: false + }, + { + id: "Point09", + size: "small", + isLink: true + } + ], + "2": [ + { + id: "Point02", + size: "big", + isLink: false + }, + { + id: "Point06", + size: "medium", + isLink: true + }, + { + id: "Point10", + size: "small", + isLink: true + }, + { + id: "Point11", + size: "small", + isLink: true + }, + { + id: "Point12", + size: "small", + isLink: true + } + ], + "3": [ + { + id: "Point03", + size: "big", + isLink: false + }, + { + id: "Point07", + size: "medium", + isLink: true + }, + { + id: "Point13", + size: "small", + isLink: true + }, + { + id: "Point14", + size: "small", + isLink: true + }, + { + id: "Point15", + size: "small", + isLink: true + } + ], + "4": [ + { + id: "Point04", + size: "big", + isLink: false + }, + { + id: "Point08", + size: "medium", + isLink: true + }, + { + id: "Point16", + size: "small", + isLink: true + }, + { + id: "Point18", + size: "small", + isLink: true + }, + { + id: "Point17", + size: "small", + isLink: true + } + ] + }, + Memory: { + "1": [ + { + id: "Point01", + size: "big", + isLink: false + }, + { + id: "Point09", + size: "small", + isLink: true + }, + { + id: "Point10", + size: "small", + isLink: true + }, + { + id: "Point11", + size: "small", + isLink: true + } + ], + "2": [ + { + id: "Point02", + size: "big", + isLink: false + }, + { + id: "Point06", + size: "medium", + isLink: true + }, + { + id: "Point13", + size: "small", + isLink: true + }, + { + id: "Point12", + size: "small", + isLink: true + } + ], + "3": [ + { + id: "Point03", + size: "big", + isLink: false + }, + { + id: "Point07", + size: "medium", + isLink: true + }, + { + id: "Point14", + size: "small", + isLink: true + }, + { + id: "Point15", + size: "small", + isLink: true + } + ], + "4": [ + { + id: "Point04", + size: "big", + isLink: false + }, + { + id: "Point08", + size: "medium", + isLink: true + }, + { + id: "Point16", + size: "small", + isLink: true + }, + { + id: "Point17", + size: "small", + isLink: true + }, + { + id: "Point18", + size: "small", + isLink: true + } + ], + "5": [ + { + id: "Point19", + size: "big-memory", + isLink: false + }, + { + id: "Point20", + size: "big-memory", + isLink: false + }, + { + id: "Point21", + size: "big", + isLink: false + } + ], + }, + Elation: { + "1": [ + { + id: "Point01", + size: "big", + isLink: false + }, + { + id: "Point09", + size: "small", + isLink: true + } + ], + "2": [ + { + id: "Point02", + size: "big", + isLink: false + }, + { + id: "Point06", + size: "medium", + isLink: true + }, + { + id: "Point10", + size: "small", + isLink: true + }, + { + id: "Point11", + size: "small", + isLink: true + }, + { + id: "Point12", + size: "small", + isLink: true + } + ], + "3": [ + { + id: "Point03", + size: "big", + isLink: false + }, + { + id: "Point07", + size: "medium", + isLink: true + }, + { + id: "Point13", + size: "small", + isLink: true + }, + { + id: "Point14", + size: "small", + isLink: true + }, + { + id: "Point15", + size: "small", + isLink: true + } + ], + "4": [ + { + id: "Point04", + size: "big", + isLink: false + }, + { + id: "Point08", + size: "medium", + isLink: true + }, + { + id: "Point16", + size: "small", + isLink: true + }, + { + id: "Point18", + size: "small", + isLink: true + }, + { + id: "Point17", + size: "small", + isLink: true + } + ] + }, +} \ No newline at end of file diff --git a/src/helper/calcData.ts b/src/helper/calcData.ts new file mode 100644 index 0000000..020e8bc --- /dev/null +++ b/src/helper/calcData.ts @@ -0,0 +1,138 @@ +import { mappingStats, ratioStats } from "@/constant/constant" +import { EliteData, HardLevelData, MainAffixData, MonsterDetail, SubAffixData} from "@/types" + +export function calcPromotion(level: number) { + if (level < 20) { + return 0 + } + if (level < 30) { + return 1 + } + if (level < 40) { + return 2 + } + if (level < 50) { + return 3 + } + if (level < 60) { + return 4 + } + if (level < 70) { + return 5 + } + return 6 +} + + +export function calcRarity(rarity: string) { + if (rarity.includes("5")) { + return 5 + } + if (rarity.includes("4")) { + return 4 + } + if (rarity.includes("3")) { + return 3 + } + return 1 +} + +export function calcMainAffixBonus(affix?: MainAffixData, level?: number) { + if (!affix || typeof level !== "number") return "0" + const value = affix.BaseValue + affix.LevelAdd * level; + + if (mappingStats?.[affix.Property].unit === "%") { + return (value * 100).toFixed(1); + } + if (mappingStats?.[affix.Property].name === "SPD") { + return value.toFixed(1); + } + + return value.toFixed(0); +} + +export const calcAffixBonus = (affix?: SubAffixData, stepCount?: number, rollCount?: number) => { + if (!affix || typeof stepCount !== "number" || typeof rollCount !== "number") return "0" + if (mappingStats?.[affix.Property].unit === "%") { + return ((affix.BaseValue * rollCount + affix.StepValue * stepCount) * 100).toFixed(1); + } + if (mappingStats?.[affix.Property].name === "SPD") { + return (affix.BaseValue * rollCount + affix.StepValue * stepCount).toFixed(1); + } + return (affix.BaseValue * rollCount + affix.StepValue * stepCount).toFixed(0); +} + +export const calcBaseStat = (baseStat: number, stepStat: number, roundFixed: number, level: number) => { + const promotionStat = baseStat + stepStat * (level-1); + return promotionStat.toFixed(roundFixed); +} + +export const calcBaseStatRaw = (baseStat?: number, stepStat?: number, level?: number) => { + if (typeof baseStat !== "number" || typeof stepStat !== "number" || typeof level !== "number") return 0 + return baseStat + stepStat * (level-1); +} + +export const calcSubAffixBonusRaw = (affix?: SubAffixData, stepCount?: number, rollCount?: number, baseStat?: number) => { + if (!affix || typeof stepCount !== "number" || typeof rollCount !== "number" || typeof baseStat !== "number") return 0 + if (ratioStats.includes(affix.Property)) { + return (affix.BaseValue * rollCount + affix.StepValue * stepCount) * baseStat; + } + return affix.BaseValue * rollCount + affix.StepValue * stepCount; +} + +export const calcMainAffixBonusRaw = (affix?: MainAffixData, level?: number, baseStat?: number) => { + if (!affix || typeof level !== "number" || typeof baseStat !== "number") return 0 + const value = affix.BaseValue + affix.LevelAdd * level; + + if (ratioStats.includes(affix.Property)) { + return baseStat * value + } + + return value +} + +export const calcBonusStatRaw = (affix?: string, baseStat?: number, bonusValue?: number) => { + if (!affix || typeof baseStat !== "number" || typeof bonusValue !== "number") return 0 + if (ratioStats.includes(affix)) { + return baseStat * bonusValue + } + return bonusValue +} + +export const calcMonsterStats = ( + monster: MonsterDetail, + eliteGroup: number, + hardLevelGroup: number, + level: number, + hardLevelConfig: Record>, + eliteConfig: Record +) => { + let hardLevelRatio = { + AttackRatio: 1, + DefenceRatio:1, + HPRatio: 1, + SpeedRatio: 1, + StanceRatio: 1 + } + if (hardLevelConfig?.[hardLevelGroup.toString()]?.[level.toString()]) { + hardLevelRatio = hardLevelConfig?.[hardLevelGroup.toString()]?.[level.toString()] + } + let eliteRatio = { + AttackRatio: 1, + DefenceRatio:1, + HPRatio: 1, + SpeedRatio: 1, + StanceRatio: 1 + } + if (eliteConfig?.[eliteGroup.toString()]) { + eliteRatio = eliteConfig?.[eliteGroup.toString()] + } + + return { + atk: monster.Base.AttackBase * monster.Modify.AttackModifyRatio * hardLevelRatio.AttackRatio * eliteRatio.AttackRatio, + def: monster.Base.DefenceBase * monster.Modify.DefenceModifyRatio * hardLevelRatio.DefenceRatio * eliteRatio.DefenceRatio, + hp: monster.Base.HPBase * monster.Modify.HPModifyRatio * hardLevelRatio.HPRatio * eliteRatio.HPRatio, + spd: monster.Base.SpeedBase * monster.Modify.SpeedModifyRatio * hardLevelRatio.SpeedRatio * eliteRatio.SpeedRatio, + stance: (monster.Base.StanceBase * monster.Modify.StanceModifyRatio * hardLevelRatio.StanceRatio * eliteRatio.StanceRatio) / 3, + } +} \ No newline at end of file diff --git a/src/helper/connect.ts b/src/helper/connect.ts new file mode 100644 index 0000000..683a4d8 --- /dev/null +++ b/src/helper/connect.ts @@ -0,0 +1,105 @@ +"use client" +import { SendDataThroughProxy, SendDataToServer } from "@/lib/api/api" +import useConnectStore from "@/stores/connectStore" +import useUserDataStore from "@/stores/userDataStore" +import { converterToFreeSRJson } from "./converterToFreeSRJson" +import { psResponseSchema } from "@/zod" +import useGlobalStore from "@/stores/globalStore" +import { ActionResult, ExtraData, ProxyPayload, ProxyResponse, PSConnectType, PSResponse } from "@/types" + + +const getUrlQuery = (connectionType: PSConnectType | string, serverUrl: string): string => { + if (connectionType === PSConnectType.FireflyGo) return "http://localhost:21000/sync" + if (connectionType === PSConnectType.RobinSR) return "http://localhost:21000/srtools" + + if (!serverUrl.startsWith("http://") && !serverUrl.startsWith("https://")) { + return `http://${serverUrl}` + } + return serverUrl +} + +const handleProxyRequest = async (payload: ProxyPayload): Promise => { + const response = await SendDataThroughProxy({ + data: { ...payload, method: "POST" } + }) as ProxyResponse | Error + + if (response instanceof Error) { + return { success: false, message: response.message } + } + if (response.error) { + return { success: false, message: response.error } + } + + const parsed = psResponseSchema.safeParse(response.data) + if (!parsed.success) { + return { success: false, message: "Invalid response schema" } + } + + return { success: true, message: "" } +} + +const handleDirectServerResponse = ( + response: PSResponse | string, + setIsConnectPS: (val: boolean) => void, + onSuccess: (extraData?: ExtraData) => void +): ActionResult => { + if (typeof response === "string") { + setIsConnectPS(false) + return { success: false, message: response } + } + if (response.status !== 200) { + setIsConnectPS(false) + return { success: false, message: response.message } + } + + setIsConnectPS(true) + onSuccess(response?.extra_data) + return { success: true, message: "" } +} + + +export const connectToPS = async (): Promise => { + const { connectionType, privateType, serverUrl, username, password } = useConnectStore.getState() + const { setExtraData, setIsConnectPS } = useGlobalStore.getState() + + if (connectionType === "Other" && privateType === "Server") { + return handleProxyRequest({ username, password, serverUrl, data: undefined }) + } + + const urlQuery = getUrlQuery(connectionType, serverUrl) + const response = await SendDataToServer(username, password, urlQuery, undefined) + + return handleDirectServerResponse(response, setIsConnectPS, (extraData) => { + setExtraData(extraData) + }) +} + +export const syncDataToPS = async (): Promise => { + const { connectionType, privateType, serverUrl, username, password } = useConnectStore.getState() + const { extraData, setIsConnectPS, setExtraData, isEnableChangePath, isEnableLua } = useGlobalStore.getState() + const { avatars, battle_type, moc_config, pf_config, as_config, ce_config, peak_config } = useUserDataStore.getState() + + const data = converterToFreeSRJson(avatars, battle_type, moc_config, pf_config, as_config, ce_config, peak_config) + + if (connectionType === "Other" && privateType === "Server") { + return handleProxyRequest({ username, password, serverUrl, data }) + } + + const urlQuery = getUrlQuery(connectionType, serverUrl) + + const payloadExtra: PSResponse['extra_data'] = structuredClone(extraData) + if (payloadExtra) { + if (!isEnableChangePath) payloadExtra.multi_path = undefined + if (!isEnableLua) payloadExtra.lua = null + } + + const response = await SendDataToServer(username, password, urlQuery, data, payloadExtra) + + return handleDirectServerResponse(response, setIsConnectPS, (responseExtraData) => { + const newData = structuredClone(responseExtraData) + if (newData) { + newData.lua = extraData?.lua || null + } + setExtraData(newData) + }) +} \ No newline at end of file diff --git a/src/helper/convertData.ts b/src/helper/convertData.ts new file mode 100644 index 0000000..c0cf832 --- /dev/null +++ b/src/helper/convertData.ts @@ -0,0 +1,15 @@ +export function convertToRoman(num: number): string { + const roman: [number, string][] = [ + [1000, 'M'], [900, 'CM'], [500, 'D'], [400, 'CD'], + [100, 'C'], [90, 'XC'], [50, 'L'], [40, 'XL'], + [10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I'] + ]; + let result = ''; + for (const [val, sym] of roman) { + while (num >= val) { + result += sym; + num -= val; + } + } + return result; + } \ No newline at end of file diff --git a/src/helper/converterToAvatarStore.ts b/src/helper/converterToAvatarStore.ts new file mode 100644 index 0000000..dfe5fe3 --- /dev/null +++ b/src/helper/converterToAvatarStore.ts @@ -0,0 +1,101 @@ +import { AvatarEnkaDetail, AvatarProfileStore, AvatarStore, AvatarDetail, FreeSRJson, RelicStore } from "@/types"; + +function safeNumber(val: string | number | null, fallback = 0): number { + if (!val) return fallback; + const num = Number(val); + return Number.isFinite(num) && num !== 0 ? num : fallback; +} + +export function converterToAvatarStore(data: Record): { [key: string]: AvatarStore } { + return Object.fromEntries( + Object.entries(data).map(([key, value]) => [ + key, + { + owner_uid: 0, + avatar_id: Number(key), + data: { + rank: 0, + skills: Object.values(value.SkillTrees).reduce((acc, dataPointEntry) => { + const firstEntry = Object.values(dataPointEntry)[0]; + if (firstEntry) { + acc[firstEntry.PointID] = firstEntry.MaxLevel; + } + return acc; + }, {} as Record) + }, + level: 80, + promotion: 6, + techniques: [], + sp_max: safeNumber(value.SPNeed, 100), + can_change_sp: safeNumber(value.SPNeed) !== 0, + sp_value: Math.ceil(safeNumber(value.SPNeed) / 2), + profileSelect: 0, + enhanced: "", + profileList: [{ + profile_name: "Default", + lightcone: null, + relics: {} as Record + } as AvatarProfileStore] + } + ]) + ) as { [key: string]: AvatarStore } +} + +export function converterOneEnkaDataToAvatarStore(data: AvatarEnkaDetail, count: number): AvatarProfileStore | null { + if (!data.equipment && (!data.relicList || data.relicList.length === 0)) return null + const profile: AvatarProfileStore = { + profile_name: `Enka Profile ${count}`, + lightcone: (data.equipment && data.equipment.tid) ? { + level: data.equipment?.level ?? 0, + item_id: data.equipment?.tid ?? 0, + rank: data.equipment?.rank ?? 0, + promotion: data.equipment?.promotion ?? 0, + } : null, + relics: Object.fromEntries(data.relicList.map((relic) => [relic.tid.toString()[relic.tid.toString().length - 1], { + level: relic.level ?? 0, + relic_id: relic.tid, + relic_set_id: parseInt(relic.tid.toString().slice(1, -1), 10), + main_affix_id: relic.mainAffixId, + sub_affixes: relic.subAffixList.map((subAffix) => ({ + sub_affix_id: subAffix.affixId, + count: subAffix.cnt, + step: subAffix.step ?? 0 + })) + }])) + } + return profile +} + + +export function converterOneFreeSRDataToAvatarStore(data: FreeSRJson, count: number , avatar_id: number): AvatarProfileStore | null { + const lightcone = data.lightcones.find((lightcone) => lightcone.equip_avatar === avatar_id) + const relics = data.relics.filter((relic) => relic.equip_avatar === avatar_id) + if (!lightcone && (!relics || relics.length === 0)) return null + const relicsMap = {} as Record + + relics.forEach((relic) => { + relicsMap[relic.relic_id.toString()[relic.relic_id.toString().length - 1]] = { + level: relic.level, + relic_id: relic.relic_id, + relic_set_id: relic.relic_set_id, + main_affix_id: relic.main_affix_id, + sub_affixes: relic.sub_affixes.map((subAffix) => ({ + sub_affix_id: subAffix.sub_affix_id, + count: subAffix.count, + step: subAffix.step ?? 0 + })) + } + }) + + const profile: AvatarProfileStore = { + profile_name: `FreeSR Profile ${count}`, + lightcone: (lightcone && lightcone.item_id) ? { + level: lightcone?.level ?? 0, + item_id: lightcone?.item_id ?? 0, + rank: lightcone?.rank ?? 0, + promotion: lightcone?.promotion ?? 0, + } : null, + relics: relicsMap + } + return profile +} diff --git a/src/helper/converterToFreeSRJson.ts b/src/helper/converterToFreeSRJson.ts new file mode 100644 index 0000000..fcf1896 --- /dev/null +++ b/src/helper/converterToFreeSRJson.ts @@ -0,0 +1,149 @@ + +import useConnectStore from "@/stores/connectStore"; +import useDetailDataStore from "@/stores/detailDataStore"; +import { ASConfigStore, AvatarJson, AvatarStore, BattleConfigJson, CEConfigStore, FreeSRJson, LightconeJson, MOCConfigStore, PEAKConfigStore, PFConfigStore, PSConnectType, RelicJson } from "@/types"; + + +export function converterToFreeSRJson( + avatars: Record, + battle_type: string, + moc_config: MOCConfigStore, + pf_config: PFConfigStore, + as_config: ASConfigStore, + ce_config: CEConfigStore, + peak_config: PEAKConfigStore, +): FreeSRJson { + const { skillConfig } = useDetailDataStore.getState() + const { connectionType } = useConnectStore.getState() + const lightcones: LightconeJson[] = [] + const relics: RelicJson[] = [] + let battleJson: BattleConfigJson + if (battle_type === "MOC") { + battleJson = { + battle_type: battle_type, + blessings: moc_config.blessings, + custom_stats: [], + cycle_count: moc_config.cycle_count, + stage_id: moc_config.stage_id, + path_resonance_id: 0, + monsters: moc_config.monsters, + } + } else if (battle_type === "PF") { + battleJson = { + battle_type: battle_type, + blessings: pf_config.blessings, + custom_stats: [], + cycle_count: pf_config.cycle_count, + stage_id: pf_config.stage_id, + path_resonance_id: 0, + monsters: pf_config.monsters, + } + } else if (battle_type === "AS") { + battleJson = { + battle_type: battle_type, + blessings: as_config.blessings, + custom_stats: [], + cycle_count: as_config.cycle_count, + stage_id: as_config.stage_id, + path_resonance_id: 0, + monsters: as_config.monsters, + } + } else if (battle_type === "CE") { + battleJson = { + battle_type: connectionType === PSConnectType.FireflyGo ? battle_type : "DEFAULT", + blessings: ce_config.blessings, + custom_stats: [], + cycle_count: ce_config.cycle_count, + stage_id: ce_config.stage_id, + path_resonance_id: 0, + monsters: ce_config.monsters, + } + } else if (battle_type === "PEAK") { + battleJson = { + battle_type: connectionType === PSConnectType.FireflyGo ? battle_type : "DEFAULT", + blessings: peak_config.blessings, + custom_stats: [], + cycle_count: peak_config.cycle_count, + stage_id: peak_config.stage_id, + path_resonance_id: 0, + monsters: peak_config.monsters, + } + } else { + battleJson = { + battle_type: battle_type, + blessings: [], + custom_stats: [], + cycle_count: 0, + stage_id: 0, + path_resonance_id: 0, + monsters: [], + } + } + + const avatarsJson: { [key: string]: AvatarJson } = {} + let internalUidLightcone = 0 + let internalUidRelic = 0 + + Object.entries(avatars).forEach(([avatarId, avatar]) => { + const skillsByAnchorType: Record = {} + for (const [skillId, level] of Object.entries(avatar?.data?.skills || {})) { + if (skillConfig?.[skillId]) { + skillsByAnchorType[skillConfig[skillId].IndexSlot] = level > skillConfig[skillId].MaxLevel ? skillConfig[skillId].MaxLevel : level + } + } + avatarsJson[avatarId] = { + owner_uid: Number(avatar.owner_uid || 0), + avatar_id: Number(avatar.avatar_id || 0), + data: { + rank: Number(avatar.data.rank || 0), + skills: avatar.data.skills, + skills_by_anchor_type: Object.keys(skillsByAnchorType).length > 0 ? skillsByAnchorType : undefined, + }, + level: Number(avatar.level || 0), + promotion: Number(avatar.promotion || 0), + techniques: avatar.techniques, + sp_value: Number(avatar.sp_value || 0), + sp_max: Number(avatar.sp_max || 0), + } + const currentProfile = avatar.profileList[avatar.profileSelect] + if (currentProfile.lightcone && currentProfile.lightcone.item_id !== 0) { + const newLightcone: LightconeJson = { + level: Number(currentProfile.lightcone.level || 0), + item_id: Number(currentProfile.lightcone.item_id || 0), + rank: Number(currentProfile.lightcone.rank || 0), + promotion: Number(currentProfile.lightcone.promotion || 0), + internal_uid: internalUidLightcone, + equip_avatar: Number(avatar.avatar_id || 0), + } + internalUidLightcone++ + lightcones.push(newLightcone) + } + + if (currentProfile.relics) { + ["1", "2", "3", "4", "5", "6"].forEach(slot => { + const relic = currentProfile.relics[slot] + if (relic && relic.relic_id !== 0) { + const newRelic: RelicJson = { + level: Number(relic.level || 0), + relic_id: Number(relic.relic_id || 0), + relic_set_id: Number(relic.relic_set_id || 0), + main_affix_id: Number(relic.main_affix_id || 0), + sub_affixes: relic.sub_affixes, + internal_uid: internalUidRelic, + equip_avatar: Number(avatar.avatar_id || 0), + } + internalUidRelic++ + relics.push(newRelic) + } + }) + } + + }) + + return { + lightcones, + relics, + avatars: avatarsJson, + battle_config: battleJson, + } +} \ No newline at end of file diff --git a/src/helper/getName.ts b/src/helper/getName.ts new file mode 100644 index 0000000..406b43b --- /dev/null +++ b/src/helper/getName.ts @@ -0,0 +1,62 @@ +import { listCurrentLanguage } from "@/constant/constant"; +import { AvatarDetail } from "@/types"; +import { useTranslations } from "next-intl" + +type TFunc = ReturnType +function cleanText(text: string): string { + if (!text) return "" + return text.replace(/(.*?)<\/unbreak>/g, "$1") +} + +export function getNameChar( + locale: string, + t: TFunc, + data: AvatarDetail | undefined +): string { + if (!data) return ""; + + if (!Object.prototype.hasOwnProperty.call(listCurrentLanguage, locale)) { + return ""; + } + + const langKey = listCurrentLanguage[locale as keyof typeof listCurrentLanguage].toLowerCase(); + + let text = data.Name[langKey] ?? ""; + + if (!text) { + text = data.Name["en"] ?? ""; + } + + if (data.ID > 8000) { + text = `${t("trailblazer")} • ${t(data?.BaseType?.toLowerCase() ?? "")}`; + } + + return cleanText(text) +} + +export function getLocaleName(locale: string, data: Record | undefined | null): string { + if (!data) { + return "" + } + if (!Object.prototype.hasOwnProperty.call(listCurrentLanguage, locale)) { + return "" + } + + const langKey = listCurrentLanguage[locale as keyof typeof listCurrentLanguage].toLowerCase(); + + + let text = data[langKey] ?? ""; + + if (!text) { + text = data["en"] ?? ""; + } + + return cleanText(text) +} + +export function parseRuby(text: string): string { + const rubyRegex = /\{RUBY_B#(.*?)\}(.*?)\{RUBY_E#\}/gs; + return text.replace(rubyRegex, (_match, furigana, kanji) => { + return `${kanji}${furigana}`; + }); +} \ No newline at end of file diff --git a/src/helper/getSkillTree.ts b/src/helper/getSkillTree.ts new file mode 100644 index 0000000..4fd5e8e --- /dev/null +++ b/src/helper/getSkillTree.ts @@ -0,0 +1,23 @@ +import { AvatarDetail } from "@/types"; + +export function getSkillTree(avatarSelected: AvatarDetail | null, enhanced: string) { + if (!avatarSelected) return null; + if (enhanced != "" && !!avatarSelected?.Enhanced?.[enhanced]?.SkillTrees) { + return Object.values(avatarSelected?.Enhanced?.[enhanced]?.SkillTrees).reduce((acc, dataPointEntry) => { + const firstEntry = Object.values(dataPointEntry)[0]; + if (firstEntry) { + acc[firstEntry.PointID] = firstEntry.MaxLevel; + } + return acc; + }, {} as Record) + } + + return Object.values(avatarSelected?.SkillTrees).reduce((acc, dataPointEntry) => { + const firstEntry = Object.values(dataPointEntry)[0]; + if (firstEntry) { + acc[firstEntry.PointID] = firstEntry.MaxLevel; + } + return acc; + }, {} as Record); +} + diff --git a/src/helper/index.ts b/src/helper/index.ts new file mode 100644 index 0000000..ba8080d --- /dev/null +++ b/src/helper/index.ts @@ -0,0 +1,8 @@ +export * from "./getName" +export * from "./replaceByParam" +export * from "./converterToAvatarStore" +export * from "./calcData" +export * from "./random" +export * from "./json" +export * from "./convertData" +export * from "./connect" \ No newline at end of file diff --git a/src/helper/json.ts b/src/helper/json.ts new file mode 100644 index 0000000..c484958 --- /dev/null +++ b/src/helper/json.ts @@ -0,0 +1,15 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function downloadJson(fileName: string, data: any) { + const json = JSON.stringify(data, null, 2) + const blob = new Blob([json], { type: 'application/json' }) + const url = URL.createObjectURL(blob) + + const link = document.createElement('a') + link.href = url + link.download = `${fileName}.json` + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + + URL.revokeObjectURL(url) +} diff --git a/src/helper/random.ts b/src/helper/random.ts new file mode 100644 index 0000000..206575a --- /dev/null +++ b/src/helper/random.ts @@ -0,0 +1,27 @@ +export function randomPartition(sum: number, parts: number): number[] { + const raw = Array.from({ length: parts }, () => Math.random()); + const total = raw.reduce((a, b) => a + b, 0); + const result = raw.map(r => Math.floor((r / total) * (sum - parts)) + 1); + let diff = sum - result.reduce((a, b) => a + b, 0); + while (diff !== 0) { + for (let i = 0; i < result.length && diff !== 0; i++) { + if (diff > 0) { + result[i]++; + diff--; + } else if (result[i] > 1) { + result[i]--; + diff++; + } + } + } + + return result; +} + +export function randomStep(x: number): number { + let total = 0; + for (let i = 0; i < x; i++) { + total += Math.floor(Math.random() * 3); + } + return total; +} \ No newline at end of file diff --git a/src/helper/replaceByParam.ts b/src/helper/replaceByParam.ts new file mode 100644 index 0000000..f14fd53 --- /dev/null +++ b/src/helper/replaceByParam.ts @@ -0,0 +1,38 @@ +const formatValue = (value: number, format: string, floatDigits?: string, hasPercent?: boolean): string => { + if (format.startsWith('f')) { + const digits = parseInt(floatDigits || "1", 10); + const num = hasPercent ? value * 100 : value; + return `${num.toFixed(digits)}${hasPercent ? "%" : ""}`; + } + + if (format === 'i') { + const num = hasPercent ? value * 100 : value; + return `${Math.round(num)}${hasPercent ? "%" : ""}`; + } + + return String(value); +}; + +export function replaceByParam(desc: string, params: number[]): string { + + const PARAM_REGEX = /#(\d+)\[(f(\d+)|i)\](%)?/g; + + const processor = (_match: string, index: string, format: string, digits?: string, percent?: string): string => { + const i = parseInt(index, 10) - 1; + const val = params[i]; + return val !== undefined ? formatValue(val, format, digits, !!percent) : ""; + }; + + let result = desc.replace(/(.*?)<\/color>/g, (_, color, inner) => { + const processedInner = inner.replace(PARAM_REGEX, processor); + return `${processedInner}`; + }); + + result = result.replace(/(.*?)<\/unbreak>/g, (_, inner) => { + return inner.replace(PARAM_REGEX, processor); + }); + + result = result.replace(PARAM_REGEX, processor); + + return result.split("\\n").join("
    "); +} \ No newline at end of file diff --git a/src/hooks/index.ts b/src/hooks/index.ts new file mode 100644 index 0000000..c13fec0 --- /dev/null +++ b/src/hooks/index.ts @@ -0,0 +1 @@ +export * from "./useChangeTheme"; diff --git a/src/hooks/useChangeTheme.ts b/src/hooks/useChangeTheme.ts new file mode 100644 index 0000000..9007ded --- /dev/null +++ b/src/hooks/useChangeTheme.ts @@ -0,0 +1,4 @@ +import { useContext } from "react"; +import { ThemeContext } from "@/components/themeController"; + +export const useChangeTheme = () => useContext(ThemeContext); \ No newline at end of file diff --git a/src/lib/api/api.ts b/src/lib/api/api.ts new file mode 100644 index 0000000..6830fa6 --- /dev/null +++ b/src/lib/api/api.ts @@ -0,0 +1,142 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { ASGroupDetail, ChangelogItemType, AvatarDetail, FreeSRJson, LightConeDetail, MOCGroupDetail, MonsterDetail, PeakGroupDetail, PFGroupDetail, PSResponse, RelicSetDetail } from "@/types"; +import axios from 'axios'; +import { psResponseSchema } from "@/zod"; +import { ExtraData, Metadata } from "@/types"; + +export async function getMetadataApi(): Promise { + try { + const res = await axios.get(`/api/data/metadata`); + return res.data as Metadata; + } catch (error: unknown) { + console.error('Failed to fetch metadata:', error); + return { + BaseType: {}, + DamageType: {}, + MainAffix: {}, + SubAffix: {}, + SkillConfig: {}, + Stage: {}, + HardLevelConfig: {}, + EliteConfig: {} + }; + } +} + +export async function getAvatarListApi(): Promise> { + try { + const res = await axios.get>(`/api/data/avatar`); + return res.data; + } catch (error) { + console.error('Failed to fetch Avatars:', error); + return {}; + } +} + +export async function getLightconeListApi(): Promise> { + try { + const res = await axios.get>(`/api/data/lightcone`); + return res.data; + } catch (error) { + console.error('Failed to fetch lightcones:', error); + return {}; + } +} + +export async function getRelicSetListApi(): Promise> { + try { + const res = await axios.get>(`/api/data/relic`); + return res.data; + } catch (error) { + console.error('Failed to fetch relics:', error); + return {}; + } +} + +export async function getMonsterListApi(): Promise> { + try { + const res = await axios.get>(`/api/data/monster`); + return res.data; + } catch (error) { + console.error('Failed to fetch monster:', error); + return {}; + } +} + +export async function getASEventListApi(): Promise> { + try { + const res = await axios.get>(`/api/data/as`); + return res.data; + } catch (error) { + console.error('Failed to fetch AS:', error); + return {}; + } +} + +export async function getPFEventListApi(): Promise> { + try { + const res = await axios.get>(`/api/data/pf`); + return res.data; + } catch (error) { + console.error('Failed to fetch PF:', error); + return {}; + } +} + +export async function getMOCEventListApi(): Promise> { + try { + const res = await axios.get>(`/api/data/moc`); + return res.data; + } catch (error) { + console.error('Failed to fetch MOC:', error); + return {}; + } +} + +export async function getPeakEventListApi(): Promise> { + try { + const res = await axios.get>(`/api/data/peak`); + return res.data; + } catch (error) { + console.error('Failed to fetch peak:', error); + return {}; + } +} + +export async function getChangelog(): Promise { + try { + const res = await axios.get(`/api/data/changelog`); + return res.data; + } catch (error) { + console.error('Failed to fetch monster:', error); + return []; + } +} + +export async function SendDataToServer( + username: string, + password: string, + serverUrl: string, + data?: FreeSRJson, + extraData?: ExtraData +): Promise { + try { + const response = await axios.post(`${serverUrl}`, { username, password, data, extra_data: extraData }) + const parsed = psResponseSchema.safeParse(response.data) + if (!parsed.success) { + return "Invalid response schema"; + } + return parsed.data; + } catch (error: any) { + return error?.message || "Unknown error"; + } +} + +export async function SendDataThroughProxy({ data }: { data: any }) { + try { + const response = await axios.post(`/api/proxy`, { ...data }) + return response.data; + } catch (error: any) { + return error; + } +} \ No newline at end of file diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts new file mode 100644 index 0000000..7de5cd9 --- /dev/null +++ b/src/lib/api/index.ts @@ -0,0 +1 @@ +export * from "./api"; \ No newline at end of file diff --git a/src/lib/cache/cache.ts b/src/lib/cache/cache.ts new file mode 100644 index 0000000..de5b5a2 --- /dev/null +++ b/src/lib/cache/cache.ts @@ -0,0 +1,38 @@ +import { readFileSync, readdirSync } from "fs" +import path from "path" + +type CacheItem = { + buf: Uint8Array + type: "json" | "br" +} + +const cache = new Map() + +const dir = path.join(process.cwd(), "data") + +for (const f of readdirSync(dir)) { + const file = path.join(dir, f) + + if (f.endsWith(".json.br")) { + const name = f.replace(".json.br", "") + const buf = new Uint8Array(readFileSync(file)) + cache.set(name, { + buf, + type: "br" + }) + } + + if (f.endsWith(".json")) { + const name = f.replace(".json", "") + const buf = new Uint8Array(readFileSync(file)) + + cache.set(name, { + buf, + type: "json" + }) + } +} + +export function getDataCache(name: string) { + return cache.get(name) +} \ No newline at end of file diff --git a/src/lib/cache/index.ts b/src/lib/cache/index.ts new file mode 100644 index 0000000..d590da3 --- /dev/null +++ b/src/lib/cache/index.ts @@ -0,0 +1 @@ +export * from "./cache" \ No newline at end of file diff --git a/src/lib/hooks/index.ts b/src/lib/hooks/index.ts new file mode 100644 index 0000000..b376ad3 --- /dev/null +++ b/src/lib/hooks/index.ts @@ -0,0 +1,10 @@ +export * from "./useFetchMetaData"; +export * from "./useFetchAvatarData"; +export * from "./useFetchLightconeData"; +export * from "./useFetchRelicData"; +export * from "./useFetchMonsterData"; +export * from "./useFetchPFData"; +export * from "./useFetchMOCData"; +export * from "./useFetchASData"; +export * from "./useFetchPEAKData"; +export * from "./useFetchChangelog"; \ No newline at end of file diff --git a/src/lib/hooks/useFetchASData.ts b/src/lib/hooks/useFetchASData.ts new file mode 100644 index 0000000..7ba42c7 --- /dev/null +++ b/src/lib/hooks/useFetchASData.ts @@ -0,0 +1,26 @@ +"use client" +import { useQuery } from '@tanstack/react-query' +import { getASEventListApi } from '@/lib/api' +import { useEffect } from 'react' +import { toast } from 'react-toastify' +import useDetailDataStore from '@/stores/detailDataStore' + +export const useFetchASGroupData = () => { + const { setMapAS } = useDetailDataStore() + + const query = useQuery({ + queryKey: ['ASGroupData'], + queryFn: getASEventListApi, + staleTime: 1000 * 60 * 5, + }) + + useEffect(() => { + if (query.data) { + setMapAS(query.data) + } else if (query.error) { + toast.error("Failed to load ASGroup data") + } + }, [query.data, query.error, setMapAS]) + + return query +} diff --git a/src/lib/hooks/useFetchAvatarData.ts b/src/lib/hooks/useFetchAvatarData.ts new file mode 100644 index 0000000..983dc9f --- /dev/null +++ b/src/lib/hooks/useFetchAvatarData.ts @@ -0,0 +1,43 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +"use client" +import { useQuery } from '@tanstack/react-query' +import { getAvatarListApi } from '@/lib/api' +import { useEffect } from 'react' +import { toast } from 'react-toastify' +import useDetailDataStore from '@/stores/detailDataStore' +import useCurrentDataStore from '@/stores/currentDataStore' +import useUserDataStore from '@/stores/userDataStore' +import { converterToAvatarStore } from '@/helper' + +export const useFetchAvatarData = () => { + const { setMapAvatar, mapAvatar } = useDetailDataStore() + const { setAvatar, avatars } = useUserDataStore() + const { avatarSelected, setAvatarSelected } = useCurrentDataStore() + + const query = useQuery({ + queryKey: ['AvatarData'], + queryFn: getAvatarListApi, + staleTime: 1000 * 60 * 5, + }) + useEffect(() => { + const listAvatarId = Object.keys(avatars) + const listAvatarNotExist = Object.entries(mapAvatar).filter(([avatarId]) => !listAvatarId.includes(avatarId)) + const avatarStore = converterToAvatarStore(Object.fromEntries(listAvatarNotExist)) + if (Object.keys(avatarStore).length === 0) return + for (const avatar of Object.values(avatarStore)) { + setAvatar(avatar) + } + }, [mapAvatar]) + + useEffect(() => { + if (query.data) { + setMapAvatar(query.data) + if (!avatarSelected) { + setAvatarSelected(Object.values(query.data)[0]) + } + } + if (query.error) toast.error("Failed to load Avatar data") + }, [query.data, query.error, setMapAvatar, avatarSelected, setAvatarSelected]) + + return query +} \ No newline at end of file diff --git a/src/lib/hooks/useFetchChangelog.ts b/src/lib/hooks/useFetchChangelog.ts new file mode 100644 index 0000000..ea30889 --- /dev/null +++ b/src/lib/hooks/useFetchChangelog.ts @@ -0,0 +1,32 @@ +"use client" +import { useQuery } from '@tanstack/react-query' +import { getChangelog } from '@/lib/api' +import { useEffect } from 'react' +import { toast } from 'react-toastify' +import useModelStore from '@/stores/modelStore' +import useLocaleStore from '@/stores/localeStore' + +export const useFetchChangelog = () => { + const { currentVersion, setChangelog, setCurrentVersion } = useLocaleStore() + const { setIsChangelog } = useModelStore() + const query = useQuery({ + queryKey: ['changelog'], + queryFn: getChangelog, + staleTime: 1000 * 60 * 5, + }) + + useEffect(() => { + if (query.data && !query.error) { + setChangelog(query.data) + if (query.data?.[0] && query.data[0].version != currentVersion) { + setIsChangelog(true) + setCurrentVersion(query.data[0].version) + } + } else if (query.error) { + toast.error("Failed to load changelog data") + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [query.data, query.error, setChangelog, setCurrentVersion, setIsChangelog]) + + return query +} diff --git a/src/lib/hooks/useFetchLightconeData.ts b/src/lib/hooks/useFetchLightconeData.ts new file mode 100644 index 0000000..8481ef3 --- /dev/null +++ b/src/lib/hooks/useFetchLightconeData.ts @@ -0,0 +1,25 @@ +"use client" +import { useQuery } from '@tanstack/react-query' +import { getLightconeListApi } from '@/lib/api'; +import { useEffect } from 'react' +import { toast } from 'react-toastify' +import useDetailDataStore from '@/stores/detailDataStore'; + +export const useFetchLightconeData = () => { + const { setMapLightCone } = useDetailDataStore() + const query = useQuery({ + queryKey: ['lightconeData'], + queryFn: getLightconeListApi, + staleTime: 1000 * 60 * 5, + }) + + useEffect(() => { + if (query.data && !query.error) { + setMapLightCone(query.data) + } else if (query.error) { + toast.error("Failed to load lightcone data") + } + }, [query.data, query.error, setMapLightCone]) + + return query +} diff --git a/src/lib/hooks/useFetchMOCData.ts b/src/lib/hooks/useFetchMOCData.ts new file mode 100644 index 0000000..6f91533 --- /dev/null +++ b/src/lib/hooks/useFetchMOCData.ts @@ -0,0 +1,25 @@ +"use client" +import { useQuery } from '@tanstack/react-query' +import { getMOCEventListApi } from '@/lib/api' +import { useEffect } from 'react' +import useDetailDataStore from '@/stores/detailDataStore' +import { toast } from 'react-toastify' + +export const useFetchMOCGroupData = () => { + const { setMapMoc } = useDetailDataStore() + const query = useQuery({ + queryKey: ['MOCGroupData'], + queryFn: getMOCEventListApi, + staleTime: 1000 * 60 * 5, + }) + + useEffect(() => { + if (query.data && !query.error) { + setMapMoc(query.data) + } else if (query.error) { + toast.error("Failed to load MOCGroup data") + } + }, [query.data, query.error, setMapMoc]) + + return query +} diff --git a/src/lib/hooks/useFetchMetaData.ts b/src/lib/hooks/useFetchMetaData.ts new file mode 100644 index 0000000..9d0ec50 --- /dev/null +++ b/src/lib/hooks/useFetchMetaData.ts @@ -0,0 +1,33 @@ +"use client" +import { useQuery } from '@tanstack/react-query' +import { useEffect } from 'react' +import { toast } from 'react-toastify' +import { getMetadataApi } from '@/lib/api' +import useCurrentDataStore from '@/stores/currentDataStore' +import useDetailDataStore from '@/stores/detailDataStore' + +export const useFetchConfigData = () => { + const { setMetaData } = useDetailDataStore() + const { setSettingData } = useCurrentDataStore() + + const query = useQuery({ + queryKey: ['initialConfigData'], + queryFn: async () => { + const metaData = await getMetadataApi() + return { metaData } + }, + staleTime: 1000 * 60 * 5, + }) + + useEffect(() => { + if (query.data && !query.error) { + setSettingData(query.data.metaData) + setMetaData(query.data.metaData) + } + else if (query.error) { + toast.error("Failed to load initial config data") + } + }, [query.data, query.error, setMetaData, setSettingData]) + + return query +} diff --git a/src/lib/hooks/useFetchMonsterData.ts b/src/lib/hooks/useFetchMonsterData.ts new file mode 100644 index 0000000..5794928 --- /dev/null +++ b/src/lib/hooks/useFetchMonsterData.ts @@ -0,0 +1,25 @@ +"use client" +import { useQuery } from '@tanstack/react-query' +import { getMonsterListApi } from '@/lib/api' +import { useEffect } from 'react' +import useDetailDataStore from '@/stores/detailDataStore' +import { toast } from 'react-toastify' + +export const useFetchMonsterData = () => { + const { setMapMonster } = useDetailDataStore() + const query = useQuery({ + queryKey: ['MonsterData'], + queryFn: getMonsterListApi, + staleTime: 1000 * 60 * 5, + }) + + useEffect(() => { + if (query.data && !query.error) { + setMapMonster(query.data) + } else if (query.error) { + toast.error("Failed to load Monster data") + } + }, [query.data, query.error, setMapMonster]) + + return query +} diff --git a/src/lib/hooks/useFetchPEAKData.ts b/src/lib/hooks/useFetchPEAKData.ts new file mode 100644 index 0000000..d5da8d5 --- /dev/null +++ b/src/lib/hooks/useFetchPEAKData.ts @@ -0,0 +1,25 @@ +"use client" +import { useQuery } from '@tanstack/react-query' +import { getPeakEventListApi } from '@/lib/api' +import { useEffect } from 'react' +import useDetailDataStore from '@/stores/detailDataStore' +import { toast } from 'react-toastify' + +export const useFetchPeakGroupData = () => { + const { setMapPeak } = useDetailDataStore() + const query = useQuery({ + queryKey: ['PeakGroupData'], + queryFn: getPeakEventListApi, + staleTime: 1000 * 60 * 5, + }) + + useEffect(() => { + if (query.data && !query.error) { + setMapPeak(query.data) + } else if (query.error) { + toast.error("Failed to load PeakGroup data") + } + }, [query.data, query.error, setMapPeak]) + + return query +} diff --git a/src/lib/hooks/useFetchPFData.ts b/src/lib/hooks/useFetchPFData.ts new file mode 100644 index 0000000..aed6e3f --- /dev/null +++ b/src/lib/hooks/useFetchPFData.ts @@ -0,0 +1,25 @@ +"use client" +import { useQuery } from '@tanstack/react-query' +import { getPFEventListApi } from '@/lib/api' +import { useEffect } from 'react' +import useDetailDataStore from '@/stores/detailDataStore' +import { toast } from 'react-toastify' + +export const useFetchPFGroupData = () => { + const { setMapPF } = useDetailDataStore() + const query = useQuery({ + queryKey: ['PFGroupData'], + queryFn: getPFEventListApi, + staleTime: 1000 * 60 * 5, + }) + + useEffect(() => { + if (query.data && !query.error) { + setMapPF(query.data) + } else if (query.error) { + toast.error("Failed to load PFGroup data") + } + }, [query.data, query.error, setMapPF]) + + return query +} diff --git a/src/lib/hooks/useFetchRelicData.ts b/src/lib/hooks/useFetchRelicData.ts new file mode 100644 index 0000000..6fdf35d --- /dev/null +++ b/src/lib/hooks/useFetchRelicData.ts @@ -0,0 +1,25 @@ +"use client" +import { useQuery } from '@tanstack/react-query' +import { getRelicSetListApi } from '@/lib/api' +import { useEffect } from 'react' +import useDetailDataStore from '@/stores/detailDataStore' +import { toast } from 'react-toastify' + +export const useFetchRelicSetData = () => { + const { setMapRelicSet } = useDetailDataStore() + const query = useQuery({ + queryKey: ['RelicSetData'], + queryFn: getRelicSetListApi, + staleTime: 1000 * 60 * 5, + }) + + useEffect(() => { + if (query.data && !query.error) { + setMapRelicSet(query.data) + } else if (query.error) { + toast.error("Failed to load RelicSet data") + } + }, [query.data, query.error, setMapRelicSet]) + + return query +} diff --git a/src/stores/connectStore.ts b/src/stores/connectStore.ts new file mode 100644 index 0000000..9f4928c --- /dev/null +++ b/src/stores/connectStore.ts @@ -0,0 +1,39 @@ +import { create } from 'zustand' +import { createJSONStorage, persist } from 'zustand/middleware'; + + +interface ConnectState { + connectionType: string; + privateType: string; + serverUrl: string; + username: string; + password: string; + setConnectionType: (newConnectionType: string) => void; + setPrivateType: (newPrivateType: string) => void; + setServerUrl: (newServerUrl: string) => void; + setUsername: (newUsername: string) => void; + setPassword: (newPassword: string) => void; +} + +const useConnectStore = create()( + persist( + (set) => ({ + connectionType: "FireflyGo", + privateType: "Local", + serverUrl: "http://localhost:21000", + username: "", + password: "", + setConnectionType: (newConnectionType: string) => set({ connectionType: newConnectionType }), + setPrivateType: (newPrivateType: string) => set({ privateType: newPrivateType }), + setServerUrl: (newServerUrl: string) => set({ serverUrl: newServerUrl }), + setUsername: (newUsername: string) => set({ username: newUsername }), + setPassword: (newPassword: string) => set({ password: newPassword }), + }), + { + name: 'connect-storage', + storage: createJSONStorage(() => localStorage), + } + ) +); + +export default useConnectStore; \ No newline at end of file diff --git a/src/stores/copyProfile.ts b/src/stores/copyProfile.ts new file mode 100644 index 0000000..612a95a --- /dev/null +++ b/src/stores/copyProfile.ts @@ -0,0 +1,41 @@ +import { AvatarDetail, AvatarProfileCardType, BaseTypeData, DamageTypeData } from '@/types'; +import { create } from 'zustand' + +interface CopyProfileState { + selectedProfiles: AvatarProfileCardType[]; + avatarCopySelected: AvatarDetail | null; + listElement: Record; + listPath: Record; + listRank: Record; + setListElement: (newListElement: Record) => void; + setListPath: (newListPath: Record) => void; + setListRank: (newListRank: Record) => void; + setSelectedProfiles: (newListAvatar: AvatarProfileCardType[]) => void; + setAvatarCopySelected: (newAvatarSelected: AvatarDetail | null) => void; + setResetData: (baseType: Record, damageType: Record) => void; +} + +const useCopyProfileStore = create((set) => ({ + selectedProfiles: [], + avatarCopySelected: null, + listElement: {}, + listPath: {}, + listRank: { "4": false, "5": false }, + listCopyAvatar: [], + listRawCopyAvatar: [], + + setListElement: (newListElement: Record) => set({ listElement: newListElement }), + setListPath: (newListPath: Record) => set({ listPath: newListPath }), + setListRank: (newListRank: Record) => set({ listRank: newListRank }), + setAvatarCopySelected: (newAvatarSelected: AvatarDetail | null) => set({ avatarCopySelected: newAvatarSelected }), + setSelectedProfiles: (newListAvatar: AvatarProfileCardType[]) => set({ selectedProfiles: newListAvatar }), + setResetData: (baseType: Record, damageType: Record) => { + set({ + listElement: Object.fromEntries(Object.keys(damageType).map(key => [key, false])) as Record, + listPath: Object.fromEntries(Object.keys(baseType).map(key => [key, false])) as Record, + listRank: { "4": false, "5": false } + }) + } +})); + +export default useCopyProfileStore; \ No newline at end of file diff --git a/src/stores/currentDataStore.ts b/src/stores/currentDataStore.ts new file mode 100644 index 0000000..c0be7cd --- /dev/null +++ b/src/stores/currentDataStore.ts @@ -0,0 +1,58 @@ +import { AvatarDetail, BaseTypeData, Metadata } from '@/types' +import { create } from 'zustand' + +interface CurrentDataState { + avatarSelected: AvatarDetail | null; + skillIDSelected: string | null; + avatarSearch: string; + lightconeSearch: string; + mapAvatarElementActive: Record; + mapAvatarPathActive: Record; + mapLightconePathActive: Record; + mapLightconeRankActive: Record; + setSettingData: (newMetaData: Metadata) => void; + setAvatarSelected: (avatar: AvatarDetail | null) => void; + setSkillIDSelected: (skillID: string | null) => void; + setAvatarSearch: (avatarSearch: string) => void; + setLightconeSearch: (lightconeSearch: string) => void; + setMapAvatarElementActive: (mapAvatarElementActive: Record) => void; + setMapAvatarPathActive: (mapAvatarPathActive: Record) => void; + setMapLightconePathActive: (mapLightconePathActive: Record) => void; + setMapLightconeRankActive: (mapLightconeRankActive: Record) => void; + setResetDataLightcone: (avtarSeleted: AvatarDetail| null, baseType: Record) => void; +} + +const useCurrentDataStore = create((set) => ({ + avatarSelected: null, + skillIDSelected: null, + mapAvatarElementActive: {}, + mapAvatarPathActive: {}, + mapLightconePathActive: {}, + mapLightconeRankActive: {"3": false, "4": false, "5": false }, + avatarSearch: "", + lightconeSearch: "", + setSettingData: (newMetaData: Metadata) => set({ + mapAvatarElementActive: Object.fromEntries(Object.keys(newMetaData.DamageType).map(key => [key, false])) as Record, + mapAvatarPathActive: Object.fromEntries(Object.keys(newMetaData.BaseType).map(key => [key, false])) as Record, + mapLightconePathActive: Object.fromEntries(Object.keys(newMetaData.BaseType).map(key => [key, false])) as Record, + }), + setAvatarSearch: (avatarSearch: string) => set({ avatarSearch }), + setLightconeSearch: (lightconeSearch: string) => set({ lightconeSearch }), + setAvatarSelected: (avatar: AvatarDetail | null) => set({ avatarSelected: avatar }), + setSkillIDSelected: (skillID: string | null) => set({ skillIDSelected: skillID }), + setMapAvatarElementActive: (mapAvatarElementActive: Record) => set({ mapAvatarElementActive }), + setMapAvatarPathActive: (mapAvatarPathActive: Record) => set({ mapAvatarPathActive }), + setMapLightconePathActive: (mapLightconePathActive: Record) => set({ mapLightconePathActive }), + setMapLightconeRankActive: (mapLightconeRankActive: Record) => set({ mapLightconeRankActive }), + setResetDataLightcone: (avtarSeleted: AvatarDetail| null, baseType: Record) => { + const newListPath: Record = Object.fromEntries(Object.keys(baseType).map(key => [key, false])) + const newListRank: Record = {"3": false, "4": false, "5": false } + if (avtarSeleted) { + newListPath[avtarSeleted.BaseType] = true + } + set({ mapLightconeRankActive: newListRank }) + set({ mapLightconePathActive: newListPath }) + } +})) + +export default useCurrentDataStore; \ No newline at end of file diff --git a/src/stores/detailDataStore.ts b/src/stores/detailDataStore.ts new file mode 100644 index 0000000..fe5cd98 --- /dev/null +++ b/src/stores/detailDataStore.ts @@ -0,0 +1,69 @@ +import { ASGroupDetail, AvatarDetail, BaseTypeData, DamageTypeData, EliteData, HardLevelData, LightConeDetail, MainAffixData, Metadata, MOCGroupDetail, MonsterDetail, PeakGroupDetail, PFGroupDetail, RelicSetDetail, SkillMaxLevelData, StageData, SubAffixData } from '@/types' +import { create } from 'zustand' + +interface DetailDataState { + mapMonster: Record + mapRelicSet: Record + mapAvatar: Record + mapLightCone: Record + mapAS: Record + mapMoc: Record + mapPF: Record + mapPeak: Record + baseType: Record; + damageType: Record; + mainAffix: Record>; + subAffix: Record>; + skillConfig: Record; + stage: Record; + hardLevelConfig: Record>; + eliteConfig: Record; + setMetaData: (newMetaData: Metadata) => void; + setMapMonster: (newMonster: Record) => void + setMapRelicSet: (newRelicSet: Record) => void + setMapAvatar: (newAvatar: Record) => void + setMapLightCone: (newLightCone: Record) => void + setMapAS: (newAS: Record) => void + setMapMoc: (newMoc: Record) => void + setMapPF: (newPF: Record) => void + setMapPeak: (newPeak: Record) => void +} + +const useDetailDataStore = create((set) => ({ + mapMonster: {}, + mapRelicSet: {}, + mapAvatar: {}, + mapLightCone: {}, + mapAS: {}, + mapMoc: {}, + mapPF: {}, + mapPeak: {}, + baseType: {}, + damageType: {}, + mainAffix: {}, + subAffix: {}, + skillConfig: {}, + stage: {}, + hardLevelConfig: {}, + eliteConfig: {}, + setMapMonster: (newMonster) => set({ mapMonster: newMonster }), + setMapRelicSet: (newRelicSet) => set({ mapRelicSet: newRelicSet }), + setMapAvatar: (newAvatar) => set({ mapAvatar: newAvatar }), + setMapLightCone: (newLightCone) => set({ mapLightCone: newLightCone }), + setMapAS: (newAS) => set({ mapAS: newAS }), + setMapMoc: (newMoc) => set({ mapMoc: newMoc }), + setMapPF: (newPF) => set({ mapPF: newPF }), + setMapPeak: (newPeak) => set({ mapPeak: newPeak }), + setMetaData: (newMetaData: Metadata) => set({ + baseType: newMetaData.BaseType, + damageType: newMetaData.DamageType, + mainAffix: newMetaData.MainAffix, + subAffix: newMetaData.SubAffix, + skillConfig: newMetaData.SkillConfig, + stage: newMetaData.Stage, + hardLevelConfig: newMetaData.HardLevelConfig, + eliteConfig: newMetaData.EliteConfig, + }), +})) + +export default useDetailDataStore diff --git a/src/stores/enkaStore.ts b/src/stores/enkaStore.ts new file mode 100644 index 0000000..28b3211 --- /dev/null +++ b/src/stores/enkaStore.ts @@ -0,0 +1,22 @@ +import { CharacterInfoCardType, EnkaResponse } from '@/types'; +import { create } from 'zustand' + +interface EnkaState { + selectedCharacters: CharacterInfoCardType[]; + enkaData: EnkaResponse | null; + uidInput: string; + setSelectedCharacters: (newListAvatar: CharacterInfoCardType[]) => void; + setEnkaData: (newEnkaData: EnkaResponse) => void; + setUidInput: (newUidInput: string) => void; +} + +const useEnkaStore = create((set) => ({ + selectedCharacters: [], + enkaData: null, + uidInput: "", + setUidInput: (newUidInput: string) => set({ uidInput: newUidInput }), + setSelectedCharacters: (newListAvatar: CharacterInfoCardType[]) => set({ selectedCharacters: newListAvatar }), + setEnkaData: (newEnkaData: EnkaResponse) => set({ enkaData: newEnkaData }), +})); + +export default useEnkaStore; \ No newline at end of file diff --git a/src/stores/freesrStore.ts b/src/stores/freesrStore.ts new file mode 100644 index 0000000..47bc707 --- /dev/null +++ b/src/stores/freesrStore.ts @@ -0,0 +1,18 @@ +import { CharacterInfoCardType, FreeSRJson } from '@/types'; +import { create } from 'zustand' + +interface FreeSRState { + selectedCharacters: CharacterInfoCardType[]; + freeSRData: FreeSRJson | null; + setSelectedCharacters: (newListAvatar: CharacterInfoCardType[]) => void; + setFreeSRData: (newFreeSRData: FreeSRJson | null) => void; +} + +const useFreeSRStore = create((set) => ({ + selectedCharacters: [], + freeSRData: null, + setSelectedCharacters: (newListAvatar: CharacterInfoCardType[]) => set({ selectedCharacters: newListAvatar }), + setFreeSRData: (newFreeSRData: FreeSRJson | null) => set({ freeSRData: newFreeSRData }), +})); + +export default useFreeSRStore; \ No newline at end of file diff --git a/src/stores/globalStore.ts b/src/stores/globalStore.ts new file mode 100644 index 0000000..b0c46e5 --- /dev/null +++ b/src/stores/globalStore.ts @@ -0,0 +1,26 @@ +import { create } from 'zustand' +import { ExtraData } from '@/types' + +interface GlobalState { + isConnectPS: boolean; + extraData?: ExtraData; + isEnableChangePath: boolean + isEnableLua: boolean + setIsEnableChangePath: (newIsEnableChangePath: boolean) => void; + setIsEnableLua: (newIsEnableLua: boolean) => void; + setExtraData: (newExtraData: ExtraData | undefined) => void; + setIsConnectPS: (newIsConnectPS: boolean) => void; +} + +const useGlobalStore = create((set) => ({ + isConnectPS: false, + extraData: undefined, + isEnableChangePath: false, + isEnableLua: false, + setIsEnableChangePath: (newIsEnableChangePath: boolean) => set({ isEnableChangePath: newIsEnableChangePath }), + setIsEnableLua: (newIsEnableLua: boolean) => set({ isEnableLua: newIsEnableLua }), + setExtraData: (newExtraData: ExtraData | undefined) => set({ extraData: newExtraData }), + setIsConnectPS: (newIsConnectPS: boolean) => set({ isConnectPS: newIsConnectPS }), +})); + +export default useGlobalStore; \ No newline at end of file diff --git a/src/stores/localeStore.ts b/src/stores/localeStore.ts new file mode 100644 index 0000000..419ffbc --- /dev/null +++ b/src/stores/localeStore.ts @@ -0,0 +1,36 @@ +import { ChangelogItemType } from '@/types'; +import { create } from 'zustand' +import { createJSONStorage, persist } from 'zustand/middleware'; + + +interface LocaleState { + locale: string; + theme: string; + currentVersion: string + changelog: ChangelogItemType[] + setCurrentVersion: (currentVersion: string) => void; + setChangelog: (newChangelog: ChangelogItemType[]) => void; + setTheme: (newTheme: string) => void; + setLocale: (newLocale: string) => void; +} + +const useLocaleStore = create()( + persist( + (set) => ({ + locale: "en", + theme: "night", + currentVersion: "", + changelog: [], + setCurrentVersion: (currentVersion: string) => set({ currentVersion }), + setChangelog: (newChangelog: ChangelogItemType[]) => set({ changelog: newChangelog }), + setTheme: (newTheme: string) => set({ theme: newTheme }), + setLocale: (newLocale: string) => set({ locale: newLocale }), + }), + { + name: 'locale-storage', + storage: createJSONStorage(() => localStorage), + } + ) +); + +export default useLocaleStore; \ No newline at end of file diff --git a/src/stores/modelStore.ts b/src/stores/modelStore.ts new file mode 100644 index 0000000..cb0ff3f --- /dev/null +++ b/src/stores/modelStore.ts @@ -0,0 +1,58 @@ +import { create } from 'zustand' + + +interface ModelState { + isOpenLightcone: boolean; + isOpenRelic: boolean; + isOpenAvatar: boolean; + isOpenCreateProfile: boolean; + isOpenImport: boolean; + isOpenCopy: boolean; + isOpenMonster: boolean; + isOpenConnect: boolean; + isOpenAvatars: boolean; + isOpenQuickView: boolean; + isOpenExtra: boolean; + isChangelog: boolean; + setIsOpenExtra: (newIsOpenExtra: boolean) => void; + setIsOpenQuickView: (newIsOpenQuickView: boolean) => void; + setIsOpenAvatars: (newIsOpenAvatars: boolean) => void; + setIsOpenConnect: (newIsOpenConnect: boolean) => void; + setIsOpenMonster: (newIsOpenMonster: boolean) => void; + setIsOpenLightcone: (newIsOpenLightcone: boolean) => void; + setIsOpenRelic: (newIsOpenRelic: boolean) => void; + setIsOpenAvatar: (newIsOpenAvatar: boolean) => void; + setIsOpenCreateProfile: (newIsOpenCreateProfile: boolean) => void; + setIsOpenImport: (newIsOpenImport: boolean) => void; + setIsOpenCopy: (newIsOpenCopy: boolean) => void; + setIsChangelog: (newChangelog: boolean) => void; +} + +const useModelStore = create((set) => ({ + isOpenLightcone: false, + isOpenRelic: false, + isOpenAvatar: false, + isOpenCreateProfile: false, + isOpenImport: false, + isOpenCopy: false, + isOpenMonster: false, + isOpenConnect: false, + isOpenAvatars: false, + isOpenQuickView: false, + isOpenExtra: false, + isChangelog: false, + setIsOpenExtra: (newIsOpenExtra: boolean) => set({ isOpenExtra: newIsOpenExtra }), + setIsOpenQuickView: (newIsOpenQuickView: boolean) => set({ isOpenQuickView: newIsOpenQuickView }), + setIsOpenAvatars: (newIsOpenAvatars: boolean) => set({ isOpenAvatars: newIsOpenAvatars }), + setIsOpenConnect: (newIsOpenConnect: boolean) => set({ isOpenConnect: newIsOpenConnect }), + setIsOpenMonster: (newIsOpenMonster: boolean) => set({ isOpenMonster: newIsOpenMonster }), + setIsOpenLightcone: (newIsOpenLightcone: boolean) => set({ isOpenLightcone: newIsOpenLightcone }), + setIsOpenRelic: (newIsOpenRelic: boolean) => set({ isOpenRelic: newIsOpenRelic }), + setIsOpenAvatar: (newIsOpenAvatar: boolean) => set({ isOpenAvatar: newIsOpenAvatar }), + setIsOpenCreateProfile: (newIsOpenCreateProfile: boolean) => set({ isOpenCreateProfile: newIsOpenCreateProfile }), + setIsOpenImport: (newIsOpenImport: boolean) => set({ isOpenImport: newIsOpenImport }), + setIsOpenCopy: (newIsOpenCopy: boolean) => set({ isOpenCopy: newIsOpenCopy }), + setIsChangelog: (newChangelog: boolean) => set({ isChangelog: newChangelog }), +})); + +export default useModelStore; \ No newline at end of file diff --git a/src/stores/relicMakerStore.ts b/src/stores/relicMakerStore.ts new file mode 100644 index 0000000..2c16960 --- /dev/null +++ b/src/stores/relicMakerStore.ts @@ -0,0 +1,213 @@ +import { create } from 'zustand' +interface RelicMakerState { + selectedRelicSlot: string; + selectedMainStat: string; + selectedRelicSet: string; + selectedRelicLevel: number; + listSelectedSubStats: { + property: string, + affixId: string, + rollCount: number, + stepCount: number + }[]; + preSelectedSubStats: Record; + setSelectedRelicSlot: (newSelectedRelicSlot: string) => void; + setSelectedRelicSet: (newSelectedRelicSet: string) => void; + setSelectedMainStat: (newSelectedMainStat: string) => void; + setSelectedRelicLevel: (newSelectedRelicLevel: number) => void; + setListSelectedSubStats: (newSubAffixes: { property: string, affixId: string, rollCount: number, stepCount: number }[]) => void; + resetHistory: (index: number | null) => void; + resetSubStat: () => void; + popHistory: (index: number) => void; + addHistory: (index: number, affix: { property: string, affixId: string, rollCount: number, stepCount: number }) => void; + +} + +const useRelicMakerStore = create((set) => ({ + selectedRelicSlot: "", + selectedMainStat: "", + selectedRelicSet: "", + selectedRelicLevel: 15, + listSelectedSubStats: [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + preSelectedSubStats: { + "0": [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + "1": [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + "2": [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + "3": [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + }, + resetSubStat: () => set({ + listSelectedSubStats: [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + }), + setSelectedRelicSlot: (newSelectedRelicSlot: string) => set({ selectedRelicSlot: newSelectedRelicSlot }), + setSelectedRelicSet: (newSelectedRelicSet: string) => set({ selectedRelicSet: newSelectedRelicSet }), + setSelectedMainStat: (newSelectedMainStat: string) => set({ selectedMainStat: newSelectedMainStat }), + setSelectedRelicLevel: (newSelectedRelicLevel: number) => set({ selectedRelicLevel: newSelectedRelicLevel }), + setListSelectedSubStats: (newSubAffixes) => set(() => ({ + listSelectedSubStats: newSubAffixes.map(item => ({ ...item })) + })), + resetHistory: (index: number | null) => set((state) => { + if (index !== null) { + return { + preSelectedSubStats: { + "0": [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + "1": [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + "2": [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + "3": [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ], + } + } + } else if (index !== null) { + const newPreSelectedSubStats = { ...state.preSelectedSubStats }; + newPreSelectedSubStats[index] = [ + { + property: "", + affixId: "", + rollCount: 0, + stepCount: 0 + }, + ] + return { + preSelectedSubStats: newPreSelectedSubStats + } + } + return {} + }), + popHistory: (index: number) => set((state) => { + const newPreSelectedSubStats = structuredClone(state.preSelectedSubStats); + const copied = state.preSelectedSubStats[index].map(item => ({ ...item })); + copied.pop(); + newPreSelectedSubStats[index] = copied; + return { preSelectedSubStats: newPreSelectedSubStats }; + }), + addHistory: (index, affix) => set((state) => { + const newPreSelectedSubStats = structuredClone(state.preSelectedSubStats); + + const currentList = state.preSelectedSubStats[index]; + const copied = currentList ? currentList.map(item => ({ ...item })) : [ + { property: "", affixId: "", rollCount: 0, stepCount: 0 }, + { property: "", affixId: "", rollCount: 0, stepCount: 0 }, + { property: "", affixId: "", rollCount: 0, stepCount: 0 }, + { property: "", affixId: "", rollCount: 0, stepCount: 0 }, + ]; + + if (copied) { + const newAffix = structuredClone(affix); + copied.push(newAffix); + newPreSelectedSubStats[index] = copied; + return { preSelectedSubStats: newPreSelectedSubStats }; + } + return {} + }), +})); + +export default useRelicMakerStore; \ No newline at end of file diff --git a/src/stores/userDataStore.ts b/src/stores/userDataStore.ts new file mode 100644 index 0000000..134f236 --- /dev/null +++ b/src/stores/userDataStore.ts @@ -0,0 +1,92 @@ +import { ASConfigStore, AvatarStore, CEConfigStore, MOCConfigStore, PEAKConfigStore, PFConfigStore } from '@/types'; +import { create } from 'zustand' +import { createJSONStorage, persist } from 'zustand/middleware'; + + +interface UserDataState { + avatars: { [key: string]: AvatarStore }; + battle_type: string; + moc_config: MOCConfigStore; + pf_config: PFConfigStore; + as_config: ASConfigStore; + peak_config: PEAKConfigStore; + ce_config: CEConfigStore; + setAvatars: (newAvatars: { [key: string]: AvatarStore }) => void; + setAvatar: (newAvatar: AvatarStore) => void; + setBattleType: (newBattleType: string) => void; + setMocConfig: (newMocConfig: MOCConfigStore) => void; + setPfConfig: (newPfConfig: PFConfigStore) => void; + setAsConfig: (newAsConfig: ASConfigStore) => void; + setPeakConfig: (newPeakConfig: PEAKConfigStore) => void; + setCeConfig: (newCeConfig: CEConfigStore) => void; +} + +const useUserDataStore = create()( + persist( + (set) => ({ + avatars: {}, + battle_type: "", + moc_config: { + event_id: 0, + challenge_id: 0, + floor_side: "Upper", + use_turbulence_buff: true, + use_cycle_count: true, + blessings: [], + cycle_count: 0, + stage_id: 0, + monsters: [], + }, + pf_config: { + event_id: 0, + challenge_id: 0, + buff_id: 0, + floor_side: "Upper", + blessings: [], + cycle_count: 0, + stage_id: 0, + monsters: [], + }, + as_config: { + event_id: 0, + challenge_id: 0, + buff_id: 0, + floor_side: "Upper", + blessings: [], + cycle_count: 0, + stage_id: 0, + monsters: [], + }, + ce_config: { + blessings: [], + cycle_count: 30, + stage_id: 0, + monsters: [], + }, + peak_config: { + event_id: 0, + challenge_id: 0, + buff_id: 0, + boss_mode: "Normal", + blessings: [], + cycle_count: 0, + stage_id: 0, + monsters: [], + }, + setAvatars: (newAvatars: { [key: string]: AvatarStore }) => set({ avatars: newAvatars }), + setAvatar: (newAvatar: AvatarStore) => set((state) => ({ avatars: { ...state.avatars, [newAvatar.avatar_id.toString()]: newAvatar } })), + setBattleType: (newBattleType: string) => set({ battle_type: newBattleType }), + setMocConfig: (newMocConfig: MOCConfigStore) => set({ moc_config: newMocConfig }), + setPfConfig: (newPfConfig: PFConfigStore) => set({ pf_config: newPfConfig }), + setAsConfig: (newAsConfig: ASConfigStore) => set({ as_config: newAsConfig }), + setPeakConfig: (newPeakConfig: PEAKConfigStore) => set({ peak_config: newPeakConfig }), + setCeConfig: (newCeConfig: CEConfigStore) => set({ ce_config: newCeConfig }), + }), + { + name: 'user-data-storage', + storage: createJSONStorage(() => localStorage), + } + ) +); + +export default useUserDataStore; \ No newline at end of file diff --git a/src/types/asDetail.ts b/src/types/asDetail.ts new file mode 100644 index 0000000..5c21b07 --- /dev/null +++ b/src/types/asDetail.ts @@ -0,0 +1,116 @@ +import { ExtraEffect } from "./avatarDetail"; +import { InfiniteWave, MazeBuff } from "./pfDetail"; + +export interface ASGroupDetail { + ID: number; + ChallengeGroupType: string; + Name: Record; + BeginTime: string; + EndTime: string; + BuffList1: ASBuff[]; + BuffList2: ASBuff[]; + Level: ASLevel[]; +} + +export interface ASBuff { + ID: number; + Param: number[]; + Icon: string; + Name: Record; + Desc: Record; + ExtraList?: ExtraEffect[]; +} + +export interface ASLevel { + Floor: number; + ID: number; + StageNum: number; + Name: Record; + Target: ASTarget[]; + DamageType1: string[]; + DamageType2: string[]; + MazeBuff: MazeBuff[]; + TurnLimit: number; + EventList1: ASEvent[]; + EventList2: ASEvent[]; + Monster1: ASMonster; + Monster2: ASMonster; +} + +export interface ASTarget { + ID: number; + Name: Record; + Param: number[]; +} + +export interface ASEvent { + ID: number; + Name: Record; + HardLevelGroup: number; + EliteGroup: number; + Level: number; + Release: boolean; + MonsterList: number[][]; + Infinite: InfiniteWave[] | null; +} + +export interface ASMonster { + ID: number; + Tag: ASTag[]; + Phase: ASPhase[]; + DifficultyGuide: ASDifficultyGuide[]; + TextGuide: ASTextGuide[]; +} + +export interface ASTag { + ID: number; + Name: Record; + BriefDescription: Record; + Param: number[]; + SkillID: number | null; + Effect: ASEffect[]; +} + +export interface ASEffect { + ID: number; + Desc: Record; + Name: Record; + Icon: string; + Param: number[]; + EffectType: number; +} + +export interface ASPhase { + ID: number; + Name: Record; + Answer: Record; + Description: Record; + SkillList: ASSkillList[]; +} + +export interface ASSkillList { + ID: number; + SkillType: string; + Name: Record; + TextList: ASTextList[]; +} + +export interface ASTextList { + ID: number; + Description: Record; + Param: number[]; + Effect: ASEffect[]; +} + +export interface ASDifficultyGuide { + ID: number; + Description: Record; + Param: number[]; + SkillID: number | null; +} + +export interface ASTextGuide { + ID: number; + Description: Record; + Param: number[]; +} \ No newline at end of file diff --git a/src/types/avatarDetail.ts b/src/types/avatarDetail.ts new file mode 100644 index 0000000..2c7ff85 --- /dev/null +++ b/src/types/avatarDetail.ts @@ -0,0 +1,227 @@ + +export interface AvatarDetail { + ID: number; + BaseType: string; + DamageType: string; + Name: Record; + Image: AvatarImage; + Release: boolean; + MaxPromotion: number; + MaxRank: number; + SPNeed: number | null; + Rarity: string; + BGDesc: Record; + Lightcones: number[]; + Stats: Record; + Skins: Record; + Ranks: Record; + Skills: Record; + Memosprite: Memosprite | null; + SkillTrees: Record>; + Relics: Relics; + Teams: AvatarTeams; + Enhanced: Record | null; + Unique: Record | null; + MazeBuff: number[]; +} + +export interface AvatarImage { + DefaultAvatarHeadIconPath: string; + AvatarSideIconPath: string; + AvatarMiniIconPath: string; + AvatarGachaResultImgPath: string; + ActionAvatarHeadIconPath: string; + SideAvatarHeadIconPath: string; + WaitingAvatarHeadIconPath: string; + AvatarCutinImgPath: string; + AvatarCutinBgImgPath: string; + AvatarCutinFrontImgPath: string; + AvatarIconPath: string; +} + + +export interface StatLevel { + Promotion: number; + MaxLevel: number; + WorldLevelRequirement: number; + PlayerLevelRequirement: number; + AttackBase: number; + AttackAdd: number; + DefenceBase: number; + DefenceAdd: number; + HPBase: number; + HPAdd: number; + SpeedBase: number; + CriticalChance: number; + CriticalDamage: number; + BaseAggro: number; +} + +export interface AvatarSkin { + ID: number; + Type: string; + PlayerCardID: number; + Name: Record; + PlayerCardTitleText: Record; + AvatarNameOnDropSkin: Record; + AvatarSkinSynopsis: Record; + Image: AvatarSkinImage; +} + +export interface AvatarSkinImage { + AvatarCutinFrontImgPath: string; + DefaultAvatarHeadIconPath: string; + AdventureDefaultAvatarHeadIconPath: string; + WaitingAvatarHeadIconPath: string; + ActionAvatarHeadIconPath: string; + SideAvatarHeadIconPath: string; + AvatarSideIconPath: string; + AvatarCutinImgPath: string; + AvatarCutinBgImgPath: string; + AvatarMiniIconPath: string; + DressIconPath: string; +} + +export interface RankDetail { + Rank: number; + Name: Record; + Desc: Record; + RankAbility: string[]; + SkillAddLevelList: Record; + Icon: string; + Image: string; + Param: number[]; + Extra: Record; +} + +export interface ExtraEffect { + ID: number; + Desc: Record; + Name: Record; + Icon: string; + Param: number[]; + EffectType: number; +} + +export interface SkillDetail { + ID: number; + Name: Record; + Tag: Record; + TypeDesc: Record; + MaxLevel: number; + Level: Record; + Icon: string; + Desc: Record; + SimpleDesc: Record; + RatedSkillTreeID: number[]; + RatedRankID: number[]; + Extra: Record; + SimpleExtra: Record; + SPBase: number | null; + StanceDamageDisplay: number; + SPMultipleRatio: number | null; + BPNeed: number | null; + BPAdd: number | null; + StanceDamageType: string | null; + AttackType: string | null; + SkillEffect: string; +} + +export interface SkillLevel { + Level: number; + Param: number[]; +} + +export interface Memosprite { + ID: number; + Name: Record; + Image: MemospriteImage; + Skills: Record; + HPBase?: number | null; + HPInherit?: number | null; + HPSkill?: number | null; + SpeedBase?: number | null; + SpeedInherit?: number | null; + SpeedSkill?: number | null; + Aggro?: number | null; +} + +export interface MemospriteImage { + HeadIcon: string; + UnCreateHeadIconPath: string; + WaitingServantHeadIconPath: string; + ActionServantHeadIconPath: string; + ServantSideIconPath: string; + ServantMiniIconPath: string; +} + +export interface SkillTreePoint { + PointID: number; + PointType: number; + AnchorType: string; + Level: number; + MaxLevel: number; + PrePoint: number[]; + StatusAddList: StatusAdd[]; + LevelUpSkillID: number[]; + Icon: string; + PointName: Record; + PointDesc: Record; + Extra: Record; + Param: number[]; +} + + +export interface StatusAdd { + PropertyType: string; + Value: number; +} + +export interface Relics { + Set4IDList: number[]; + Set2IDList: number[]; + PropertyList3: string[]; + PropertyList4: string[]; + PropertyList5: string[]; + PropertyList6: string[]; + PropertyList: RelicProperty[]; + SubAffixPropertyList: string[]; + ScoreRankList: number[]; + LocalCriticalChance: { Value: number }; +} + +export interface RelicProperty { + RelicType: string; + PropertyType: string; +} + +export interface AvatarTeams { + TeamID: number; + Position: number; + MemberList: number[]; + BackupList1: number[]; + BackupList2: number[]; + BackupList3: number[]; + BackupGroupList1: number[]; + BackupGroupList2: number[]; + BackupGroupList3: number[]; +} + +export interface EnhancedData { + EnhancedID: number; + SPNeed: number | null; + Ranks: Record; + Skills: Record; + SkillTrees: Record> | null; +} + +export interface GlobalBuff { + ID: number; + AvatarID: number; + Name: Record; + Tag: Record; + Desc: Record; + Param: number[]; + Extra: Record; + MazeBuffID: number | null; +} diff --git a/src/types/card.ts b/src/types/card.ts new file mode 100644 index 0000000..c4ca0bc --- /dev/null +++ b/src/types/card.ts @@ -0,0 +1,25 @@ +import { LightconeStore, RelicStore } from "./mics"; + +export interface CharacterInfoCardType { + key: number; + avatar_id: number; + rank: number; + level: number; + lightcone: { + level: number; + rank: number; + item_id: number; + }; + relics: { + level: number; + relic_id: number; + relic_set_id: number; + }[]; +} + +export interface AvatarProfileCardType { + key: number; + profile_name: string, + lightcone?: LightconeStore | null, + relics: Record +} \ No newline at end of file diff --git a/src/types/changelog.ts b/src/types/changelog.ts new file mode 100644 index 0000000..05923f0 --- /dev/null +++ b/src/types/changelog.ts @@ -0,0 +1,7 @@ + +export interface ChangelogItemType { + version: string, + date: string, + type: string, + items: string[] +} \ No newline at end of file diff --git a/src/types/enka.ts b/src/types/enka.ts new file mode 100644 index 0000000..47c15a6 --- /dev/null +++ b/src/types/enka.ts @@ -0,0 +1,96 @@ +interface PrivacySettingInfo { + displayCollection: boolean + displayRecord: boolean + displayRecordTeam: boolean + displayOnlineStatus: boolean + displayDiary: boolean +} + +interface RecordInfo { + achievementCount: number + bookCount: number + avatarCount: number + equipmentCount: number + musicCount: number + relicCount: number + challengeInfo: unknown; + maxRogueChallengeScore: number +} + + +interface SubAffix { + affixId: number + cnt: number + step?: number +} + +interface FlatProp { + type: string + value: number +} + +interface RelicFlat { + props: FlatProp[] + setName: string + setID: number +} + +interface Relic { + mainAffixId: number + subAffixList: SubAffix[] + tid: number + type: number + level: number + _flat: RelicFlat +} + +interface SkillTree { + pointId: number + level: number +} + +interface EquipmentFlat { + props: FlatProp[] + name: string +} + +interface Equipment { + rank: number + tid: number + promotion: number + level: number + _flat: EquipmentFlat +} + +export interface AvatarEnkaDetail { + relicList: Relic[] + level: number + promotion: number + rank?: number + skillTreeList: SkillTree[] + equipment: Equipment + avatarId: number + _assist?: boolean +} + +interface DetailInfo { + worldLevel: number + privacySettingInfo: PrivacySettingInfo + headIcon: number + signature: string + avatarDetailList: AvatarEnkaDetail[] + platform: string + recordInfo: RecordInfo + uid: number + level: number + nickname: string + isDisplayAvatar: boolean + friendCount: number + personalCardId: number +} + +export interface EnkaResponse { + detailInfo: DetailInfo + ttl: number + uid: string +} diff --git a/src/types/extraData.ts b/src/types/extraData.ts new file mode 100644 index 0000000..ef042f8 --- /dev/null +++ b/src/types/extraData.ts @@ -0,0 +1,27 @@ +export interface ExtraData { + theory_craft?: { + hp: Record + cycle_count: number + mode: boolean + stage_id: number + custom_lineup?: string[] + } + setting?: { + censorship: boolean + cm: boolean + first_person: boolean + hide_ui: boolean + } + challenge?: { + skip_node: number + challenge_peak_group_id: number + challenge_peak_group_id_list: number[] + } + multi_path?: { + main: number + march_7: number + multi_path_main: number[] + multi_path_march_7: number[] + }, + lua?: string | null +} diff --git a/src/types/filter.ts b/src/types/filter.ts new file mode 100644 index 0000000..0944db8 --- /dev/null +++ b/src/types/filter.ts @@ -0,0 +1,20 @@ +export interface FilterAvatarType { + name: string; + path: string[]; + element: string[]; + rarity: string[]; + locale: string; +} + +export interface FilterLightconeType { + path: string[]; + rarity: string[]; + locale: string; + name: string; +} + + +export interface FilterRelicType { + locale: string; + name: string; +} diff --git a/src/types/gloval.d.ts b/src/types/gloval.d.ts new file mode 100644 index 0000000..847afa7 --- /dev/null +++ b/src/types/gloval.d.ts @@ -0,0 +1,4 @@ +declare module '*.css'; +declare module '*.scss'; +declare module '*.module.css'; +declare module '*.module.scss'; \ No newline at end of file diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..e8a0944 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,21 @@ +export * from "./avatarDetail" +export * from "./srtools" +export * from "./mics" +export * from "./lightconeDetail" +export * from "./relicDetail" +export * from "./enka" +export * from "./card" +export * from "./pfDetail" +export * from "./asDetail" +export * from "./mocDetail" +export * from "./peakDetail" +export * from "./extraData" +export * from "./showcase" +export * from "./srtools" +export * from "./changelog" +export * from "./modelConfig" +export * from "./metaData" +export * from "./monsterDetail" +export * from "./filter" +export * from "./metaData" +export * from "./psConnect" \ No newline at end of file diff --git a/src/types/lightconeDetail.ts b/src/types/lightconeDetail.ts new file mode 100644 index 0000000..6d084fc --- /dev/null +++ b/src/types/lightconeDetail.ts @@ -0,0 +1,52 @@ +export interface LightConeDetail { + ID: number; + Release: boolean; + Name: Record; + Desc: Record; + Rarity: string; + BaseType: string; + MaxPromotionLevel: number; + MaxRank: number; + Skills: LightconeSkill; + Image: LightconeImage; + Stats: Record; +} + +export interface LightconeSkill { + ID: number; + Name: Record; + Desc: Record; + AbilityName: string; + Level: Record; +} + +export interface LightconeSkillLevel { + Level: number; + Param: number[]; + Bonus: LightconeSkillBonus[]; +} + +export interface LightconeSkillBonus { + PropertyType: string; + Value: number; +} + +export interface LightconeImage { + ThumbnailPath: string; + ImagePath: string; + IconPath: string; + FigureIconPath: string; +} + +export interface LightconeStatLevel { + Promotion: number; + PlayerLevelRequire: number; + WorldLevelRequire: number; + MaxLevel: number; + BaseHP: number; + BaseHPAdd: number; + BaseAttack: number; + BaseAttackAdd: number; + BaseDefence: number; + BaseDefenceAdd: number; +} \ No newline at end of file diff --git a/src/types/metaData.ts b/src/types/metaData.ts new file mode 100644 index 0000000..ef2d077 --- /dev/null +++ b/src/types/metaData.ts @@ -0,0 +1,59 @@ +export interface Metadata { + BaseType: Record; + DamageType: Record; + MainAffix: Record>; + SubAffix: Record>; + SkillConfig: Record; + Stage: Record; + HardLevelConfig: Record>; + EliteConfig: Record; +} + +export interface HardLevelData { + AttackRatio: number + DefenceRatio: number + HPRatio: number + SpeedRatio: number + StanceRatio: number +} + +export interface EliteData { + AttackRatio: number + DefenceRatio: number + HPRatio: number + SpeedRatio: number + StanceRatio: number +} + +export interface StageData { + ID: number; + Name: Record; +} + +export interface BaseTypeData { + Text: Record; + Icon: string; +} + +export interface DamageTypeData { + Name: Record; + Icon: string; +} + +export interface MainAffixData { + Property: string; + BaseValue: number; + LevelAdd: number; +} + +export interface SubAffixData { + Property: string; + BaseValue: number; + StepValue: number; + StepNum: number; +} + +export interface SkillMaxLevelData { + MaxLevel: number; + IndexSlot: number; +} diff --git a/src/types/mics.ts b/src/types/mics.ts new file mode 100644 index 0000000..08ae3ad --- /dev/null +++ b/src/types/mics.ts @@ -0,0 +1,122 @@ +export type AvatarDataStore = { + rank: number, + skills: Record +} +export type LightconeStore = { + level: number; + item_id: number; + rank: number; + promotion: number; +} + +export type SubAffixStore = { + sub_affix_id: number; + count: number; + step: number; +} +export type RelicStore = { + level: number; + relic_id: number; + relic_set_id: number; + main_affix_id: number; + sub_affixes: SubAffixStore[]; +} + +export type AvatarProfileStore = { + profile_name: string, + lightcone: LightconeStore | null, + relics: Record +} + +export type AvatarStore = { + owner_uid?: number; + avatar_id: number; + data: AvatarDataStore; + level: number; + promotion: number; + techniques: number[]; + sp_value: number; + sp_max: number; + can_change_sp: boolean; + enhanced: string; + profileSelect: number; + profileList: AvatarProfileStore[] +} + +export type MonsterStore = { + monster_id: number; + level: number; + amount: number; +} + +export type DynamicKeyStore = { + key: string; + value: number; +} + +export type BattleBuffStore = { + level: number; + id: number; + dynamic_key?: DynamicKeyStore; +} +export type MOCConfigStore = { + event_id: number; + challenge_id: number; + floor_side: string; + use_turbulence_buff: boolean; + use_cycle_count: boolean; + blessings: BattleBuffStore[] + cycle_count: number; + stage_id: number; + monsters: MonsterStore[][]; +} + +export type PFConfigStore = { + event_id: number; + challenge_id: number; + buff_id: number; + floor_side: string; + blessings: BattleBuffStore[] + cycle_count: number; + stage_id: number; + monsters: MonsterStore[][]; +} + +export type ASConfigStore = { + event_id: number; + challenge_id: number; + buff_id: number; + floor_side: string; + blessings: BattleBuffStore[] + cycle_count: number; + stage_id: number; + monsters: MonsterStore[][]; +} + +export type PEAKConfigStore = { + event_id: number; + challenge_id: number; + buff_id: number; + boss_mode: string; + blessings: BattleBuffStore[] + cycle_count: number; + stage_id: number; + monsters: MonsterStore[][]; +} + +export type CEConfigStore = { + blessings: BattleBuffStore[] + cycle_count: number; + stage_id: number; + monsters: MonsterStore[][]; +} + +export interface Mics { + avatars: Record, + battle_type: string; + moc_config: MOCConfigStore; + pf_config: PFConfigStore; + as_config: ASConfigStore; + ce_config: CEConfigStore; + peak_config: PEAKConfigStore +} \ No newline at end of file diff --git a/src/types/mocDetail.ts b/src/types/mocDetail.ts new file mode 100644 index 0000000..f7b1863 --- /dev/null +++ b/src/types/mocDetail.ts @@ -0,0 +1,41 @@ +import { InfiniteWave, MazeBuff } from "./pfDetail"; + +export interface MOCGroupDetail { + ID: number; + ChallengeGroupType: string; + Name: Record; + BeginTime: string; + EndTime: string; + Level: MoCLevel[]; +} + +export interface MoCLevel { + Floor: number; + ID: number; + StageNum: number; + Name: Record; + Target: MoCTarget[]; + TurnLimit: number; + DamageType1: string[]; + DamageType2: string[]; + MazeBuff: MazeBuff[]; + EventList1: MoCEvent[]; + EventList2: MoCEvent[]; +} + +export interface MoCTarget { + ID: number; + Name: Record; + Param: number[]; +} + +export interface MoCEvent { + ID: number; + Name: Record; + HardLevelGroup: number; + EliteGroup: number; + Level: number; + Release: boolean; + MonsterList: number[][]; + Infinite: InfiniteWave[] | null; +} \ No newline at end of file diff --git a/src/types/modelConfig.ts b/src/types/modelConfig.ts new file mode 100644 index 0000000..ba7001d --- /dev/null +++ b/src/types/modelConfig.ts @@ -0,0 +1,7 @@ +export type ModalConfig = { + id: string + title: string + isOpen: boolean + onClose: () => void + content: React.ReactNode +} \ No newline at end of file diff --git a/src/types/monsterDetail.ts b/src/types/monsterDetail.ts new file mode 100644 index 0000000..5fe6a24 --- /dev/null +++ b/src/types/monsterDetail.ts @@ -0,0 +1,45 @@ + +export interface MonsterDetail { + ID: number; + Name: Record; + EliteGroup: number; + HardLevelGroup: number; + StanceWeakList: string[]; + Modify: MonsterModify; + Rank: string; + StanceCount: number; + StanceType: string; + Image: MonsterImage; + Base: MonsterBase; +} + + +export interface MonsterModify { + AttackModifyRatio: number; + DefenceModifyRatio: number; + HPModifyRatio: number; + SpeedModifyRatio: number; + StanceModifyRatio: number; + SpeedModifyValue: number; + StanceModifyValue: number; +} + +export interface MonsterImage { + IconPath: string; + RoundIconPath: string; + ImagePath: string; + ManikinImagePath: string; +} + +export interface MonsterBase { + AttackBase: number; + DefenceBase: number; + HPBase: number; + SpeedBase: number; + StanceBase: number; + CriticalDamageBase: number; + StatusResistanceBase: number; + SpeedModifyValue: number; + StanceModifyValue: number; + InitialDelayRatio: number; +} \ No newline at end of file diff --git a/src/types/peakDetail.ts b/src/types/peakDetail.ts new file mode 100644 index 0000000..5eb453c --- /dev/null +++ b/src/types/peakDetail.ts @@ -0,0 +1,41 @@ +import { BattleTarget, InfiniteWave, MazeBuff } from "./pfDetail"; + +export interface PeakGroupDetail { + ID: number; + ChallengeGroupType: string; + Name: Record; + PreLevel: PeakMazeConfig[]; + BossLevel: PeakMazeConfig | null; + BossConfig: PeakBossConfig | null; +} + +export interface PeakMazeConfig { + ID: number; + Name: Record; + BattleTarget: BattleTarget[]; + DamageType: string[]; + MazeBuff: MazeBuff[]; + TurnLimit: number; + EventList: PeakEvent[]; +} + +export interface PeakBossConfig { + ID: number; + Name: Record; + BuffList: MazeBuff[]; + BattleTarget: BattleTarget[]; + MazeBuff: MazeBuff[]; + TurnLimit: number; + EventList: PeakEvent[]; +} + +export interface PeakEvent { + ID: number; + Name: Record; + HardLevelGroup: number; + EliteGroup: number; + Level: number; + Release: boolean; + MonsterList: number[][]; + Infinite: InfiniteWave[] | null; +} \ No newline at end of file diff --git a/src/types/pfDetail.ts b/src/types/pfDetail.ts new file mode 100644 index 0000000..b18639d --- /dev/null +++ b/src/types/pfDetail.ts @@ -0,0 +1,64 @@ +export interface PFGroupDetail { + ID: number; + ChallengeGroupType: string; + Name: Record; + BeginTime: string; + EndTime: string; + SubOption: MazeBuff[]; + Option: MazeBuff[]; + Level: LevelData[]; +} + +export interface MazeBuff { + ID: number; + Param: number[]; + Icon: string; + Name: Record; + Desc: Record; +} + +export interface LevelData { + Floor: number; + ID: number; + StageNum: number; + Name: Record; + Target: StoryTarget[]; + DamageType1: string[]; + DamageType2: string[]; + MazeBuff: MazeBuff[]; + EventList1: StageConfig[]; + EventList2: StageConfig[]; + TurnLimit: number; + BattleTarget: BattleTarget[]; + ClearScore: number; +} + +export interface StoryTarget { + ID: number; + Name: Record; + Param: number[]; +} + +export interface BattleTarget { + ID: number; + Param: number[]; + Name: Record; +} + +export interface StageConfig { + ID: number; + Name: Record; + HardLevelGroup: number; + EliteGroup: number; + Level: number; + Release: boolean; + MonsterList: number[][]; + Infinite?: InfiniteWave[] | null; +} + +export interface InfiniteWave { + ID: number; + MaxMonsterCount: number; + MonsterList: number[]; + EliteGroup: number; +} \ No newline at end of file diff --git a/src/types/psConnect.ts b/src/types/psConnect.ts new file mode 100644 index 0000000..7402f49 --- /dev/null +++ b/src/types/psConnect.ts @@ -0,0 +1,32 @@ +import { ExtraData } from "./extraData"; +import { FreeSRJson } from "./srtools"; + +export enum PSConnectType { + FireflyGo = "FireflyGo", + RobinSR = "RobinSR", + Other = "Other", +} + +export interface ProxyPayload { + username?: string; + password?: string; + serverUrl: string; + data?: FreeSRJson; +} + +export interface ProxyResponse { + error?: string; + message?: string; + data?: unknown; +} + +export interface PSResponse { + status: number; + message: string; + extra_data?: ExtraData +} + +export interface ActionResult { + success: boolean; + message: string; +} \ No newline at end of file diff --git a/src/types/relicDetail.ts b/src/types/relicDetail.ts new file mode 100644 index 0000000..f760840 --- /dev/null +++ b/src/types/relicDetail.ts @@ -0,0 +1,59 @@ + +export interface RelicSetDetail { + ID: number; + Name: Record; + Image: RelicSetImage; + Release: boolean; + ReleaseVersion: string; + IsPlanarSuit: boolean; + DisplayItemID: number; + DisplayItemIDRarity4: number; + SkillList: number[]; + Skills: Record; + Parts: Record; +} + +export interface RelicSetImage { + SetIconPath: string; + SetIconFigurePath: string; +} + +export interface RelicSkill { + RequireNum: number; + Desc: Record; + AbilityName: string; + Param: number[]; + Bonus: RelicSkillBonus[]; +} + +export interface RelicSkillBonus { + PropertyType: string; + Value: number; +} + +export interface RelicPart { + Type: string; + Image: RelicPartImage; + Name: Record; + Desc: Record; + Data: Record | null; +} + +export interface RelicPartImage { + IconPath: string; + ItemFigureIconPath: string; +} + +export interface RelicPartData { + ID: number; + SetID: number; + Type: string; + Rarity: string; + MainAffixGroup: number; + SubAffixGroup: number; + MaxLevel: number; + ExpType: number; + ExpProvide: number; + CoinCost: number; + Mode: string; +} \ No newline at end of file diff --git a/src/types/showcase.ts b/src/types/showcase.ts new file mode 100644 index 0000000..c3b0837 --- /dev/null +++ b/src/types/showcase.ts @@ -0,0 +1,26 @@ +export type RelicShowcaseType = { + img: string; + mainAffix: { + property: string; + level: number; + valueAffix: string; + detail: { + name: string; + icon: string; + unit: string; + baseStat: string; + }; + }; + subAffix: { + property: string; + valueAffix: string; + detail: { + name: string; + icon: string; + unit: string; + baseStat: string; + }; + step: number; + count: number; + }[] +} \ No newline at end of file diff --git a/src/types/srtools.ts b/src/types/srtools.ts new file mode 100644 index 0000000..f829675 --- /dev/null +++ b/src/types/srtools.ts @@ -0,0 +1,82 @@ +export interface SubAffix { + sub_affix_id: number; + count: number; + step: number; +} + +export interface RelicJson { + level: number; + relic_id: number; + relic_set_id: number; + main_affix_id: number; + sub_affixes: SubAffix[]; + internal_uid: number; + equip_avatar: number; +} + +export interface LightconeJson { + level: number; + item_id: number; + equip_avatar: number; + rank: number; + promotion: number; + internal_uid: number; +} +export interface AvatarData { + rank: number, + skills: Record + skills_by_anchor_type?: Record +} + +export interface AvatarJson { + owner_uid?: number; + avatar_id: number; + data: AvatarData; + level: number; + promotion: number; + techniques: number[]; + sp_value: number; + sp_max: number; +} +export interface MonsterJson { + monster_id: number; + level: number; + amount: number; +} + +export interface DynamicKeyJson { + key: string; + value: number; +} + +//BattleBuff +export interface BattleBuffJson { + level: number; + id: number; + dynamic_key?: DynamicKeyJson; +} + +export interface BattleConfigJson { + battle_type: string; + blessings: BattleBuffJson[] + custom_stats: SubAffix[]; + cycle_count: number; + stage_id: number; + path_resonance_id: number; + monsters: MonsterJson[][]; +} +type LoadoutJson = { + name: string + avatar_id: number + relic_list: string[] +} +export interface FreeSRJson { + key?: string; + lightcones: LightconeJson[]; + relics: RelicJson[]; + avatars: Record; + battle_config: BattleConfigJson; + loadout?: LoadoutJson[]; +} + + diff --git a/src/zod/card.zod.ts b/src/zod/card.zod.ts new file mode 100644 index 0000000..89557ca --- /dev/null +++ b/src/zod/card.zod.ts @@ -0,0 +1,30 @@ +// Generated by ts-to-zod +import { z } from "zod"; + +export const characterInfoCardTypeSchema = z.object({ + key: z.number(), + avatar_id: z.number(), + rank: z.number(), + level: z.number(), + lightcone: z.object({ + level: z.number(), + rank: z.number(), + item_id: z.number() + }), + relics: z.array(z.object({ + level: z.number(), + relic_id: z.number(), + relic_set_id: z.number() + })) +}); + +const lightconeStoreSchema = z.any(); + +const relicStoreSchema = z.any(); + +export const avatarProfileCardTypeSchema = z.object({ + key: z.number(), + profile_name: z.string(), + lightcone: lightconeStoreSchema.optional().nullable(), + relics: z.record(z.string(), relicStoreSchema) +}); diff --git a/src/zod/enka.zod.ts b/src/zod/enka.zod.ts new file mode 100644 index 0000000..d87879e --- /dev/null +++ b/src/zod/enka.zod.ts @@ -0,0 +1,98 @@ +// Generated by ts-to-zod +import { z } from "zod"; + +const privacySettingInfoSchema = z.object({ + displayCollection: z.boolean(), + displayRecord: z.boolean(), + displayRecordTeam: z.boolean(), + displayOnlineStatus: z.boolean(), + displayDiary: z.boolean() +}); + +const recordInfoSchema = z.object({ + achievementCount: z.number(), + bookCount: z.number(), + avatarCount: z.number(), + equipmentCount: z.number(), + musicCount: z.number(), + relicCount: z.number(), + challengeInfo: z.unknown(), + maxRogueChallengeScore: z.number() +}); + +const subAffixSchema = z.object({ + affixId: z.number(), + cnt: z.number(), + step: z.number().optional() +}); + +const flatPropSchema = z.object({ + type: z.string(), + value: z.number() +}); + +const relicFlatSchema = z.object({ + props: z.array(flatPropSchema), + setName: z.string(), + setID: z.number() +}); + +const relicSchema = z.object({ + mainAffixId: z.number(), + subAffixList: z.array(subAffixSchema), + tid: z.number(), + type: z.number(), + level: z.number(), + _flat: relicFlatSchema +}); + +const skillTreeSchema = z.object({ + pointId: z.number(), + level: z.number() +}); + +const equipmentFlatSchema = z.object({ + props: z.array(flatPropSchema), + name: z.string() +}); + +const equipmentSchema = z.object({ + rank: z.number(), + tid: z.number(), + promotion: z.number(), + level: z.number(), + _flat: equipmentFlatSchema +}); + +export const avatarEnkaDetailSchema = z.object({ + relicList: z.array(relicSchema), + level: z.number(), + promotion: z.number(), + rank: z.number().optional(), + skillTreeList: z.array(skillTreeSchema), + equipment: equipmentSchema, + avatarId: z.number(), + _assist: z.boolean().optional() +}); + +const detailInfoSchema = z.object({ + worldLevel: z.number(), + privacySettingInfo: privacySettingInfoSchema, + headIcon: z.number(), + signature: z.string(), + avatarDetailList: z.array(avatarEnkaDetailSchema), + platform: z.string(), + recordInfo: recordInfoSchema, + uid: z.number(), + level: z.number(), + nickname: z.string(), + isDisplayAvatar: z.boolean(), + friendCount: z.number(), + personalCardId: z.number() +}); + +export const enkaResponseSchema = z.object({ + detailInfo: detailInfoSchema, + ttl: z.number(), + uid: z.string() +}); diff --git a/src/zod/extraData.zod.ts b/src/zod/extraData.zod.ts new file mode 100644 index 0000000..6923e4e --- /dev/null +++ b/src/zod/extraData.zod.ts @@ -0,0 +1,29 @@ +// Generated by ts-to-zod +import { z } from "zod"; + +export const extraDataSchema = z.object({ + theory_craft: z.object({ + hp: z.record(z.string(), z.array(z.number())), + cycle_count: z.number(), + mode: z.boolean(), + stage_id: z.number() + }), + setting: z.object({ + censorship: z.boolean(), + cm: z.boolean(), + first_person: z.boolean(), + hide_ui: z.boolean() + }), + challenge: z.object({ + skip_node: z.number(), + challenge_peak_group_id: z.number(), + challenge_peak_group_id_list: z.array(z.number()) + }), + multi_path: z.object({ + main: z.number(), + march_7: z.number(), + multi_path_main: z.array(z.number()), + multi_path_march_7: z.array(z.number()) + }), + lua: z.string().optional() +}); diff --git a/src/zod/filter.zod.ts b/src/zod/filter.zod.ts new file mode 100644 index 0000000..e638849 --- /dev/null +++ b/src/zod/filter.zod.ts @@ -0,0 +1,22 @@ +// Generated by ts-to-zod +import { z } from "zod"; + +export const filterAvatarTypeSchema = z.object({ + name: z.string(), + path: z.array(z.string()), + element: z.array(z.string()), + rarity: z.array(z.string()), + locale: z.string() +}); + +export const filterLightconeTypeSchema = z.object({ + path: z.array(z.string()), + rarity: z.array(z.string()), + locale: z.string(), + name: z.string() +}); + +export const filterRelicTypeSchema = z.object({ + locale: z.string(), + name: z.string() +}); diff --git a/src/zod/index.ts b/src/zod/index.ts new file mode 100644 index 0000000..99e5817 --- /dev/null +++ b/src/zod/index.ts @@ -0,0 +1,9 @@ +export * from "./srtools.zod" +export * from "./filter.zod" +export * from "./mics.zod" +export * from "./enka.zod" +export * from "./card.zod" +export * from "./extraData.zod" +export * from "./showcase.zod" +export * from "./srtools.zod" + diff --git a/src/zod/mics.zod.ts b/src/zod/mics.zod.ts new file mode 100644 index 0000000..caae13a --- /dev/null +++ b/src/zod/mics.zod.ts @@ -0,0 +1,128 @@ +// Generated by ts-to-zod +import { z } from "zod"; + +export const avatarDataStoreSchema = z.object({ + rank: z.number(), + skills: z.record(z.string(), z.number()) +}); + +export const lightconeStoreSchema = z.object({ + level: z.number(), + item_id: z.number(), + rank: z.number(), + promotion: z.number() +}); + +export const subAffixStoreSchema = z.object({ + sub_affix_id: z.number(), + count: z.number(), + step: z.number() +}); + +export const relicStoreSchema = z.object({ + level: z.number(), + relic_id: z.number(), + relic_set_id: z.number(), + main_affix_id: z.number(), + sub_affixes: z.array(subAffixStoreSchema) +}); + +export const avatarProfileStoreSchema = z.object({ + profile_name: z.string(), + lightcone: lightconeStoreSchema.nullable(), + relics: z.record(z.string(), relicStoreSchema) +}); + +export const avatarStoreSchema = z.object({ + owner_uid: z.number().optional(), + avatar_id: z.number(), + data: avatarDataStoreSchema, + level: z.number(), + promotion: z.number(), + techniques: z.array(z.number()), + sp_value: z.number(), + sp_max: z.number(), + can_change_sp: z.boolean(), + enhanced: z.string(), + profileSelect: z.number(), + profileList: z.array(avatarProfileStoreSchema) +}); + +export const monsterStoreSchema = z.object({ + monster_id: z.number(), + level: z.number(), + amount: z.number() +}); + +export const dynamicKeyStoreSchema = z.object({ + key: z.string(), + value: z.number() +}); + +export const battleBuffStoreSchema = z.object({ + level: z.number(), + id: z.number(), + dynamic_key: dynamicKeyStoreSchema.optional() +}); + +export const mocConfigStoreSchema = z.object({ + event_id: z.number(), + challenge_id: z.number(), + floor_side: z.string(), + use_turbulence_buff: z.boolean(), + use_cycle_count: z.boolean(), + blessings: z.array(battleBuffStoreSchema), + cycle_count: z.number(), + stage_id: z.number(), + monsters: z.array(z.array(monsterStoreSchema)) +}); + +export const pfConfigStoreSchema = z.object({ + event_id: z.number(), + challenge_id: z.number(), + buff_id: z.number(), + floor_side: z.string(), + blessings: z.array(battleBuffStoreSchema), + cycle_count: z.number(), + stage_id: z.number(), + monsters: z.array(z.array(monsterStoreSchema)) +}); + +export const asConfigStoreSchema = z.object({ + event_id: z.number(), + challenge_id: z.number(), + buff_id: z.number(), + floor_side: z.string(), + blessings: z.array(battleBuffStoreSchema), + cycle_count: z.number(), + stage_id: z.number(), + monsters: z.array(z.array(monsterStoreSchema)) +}); + +export const peakConfigStoreSchema = z.object({ + event_id: z.number(), + challenge_id: z.number(), + buff_id: z.number(), + boss_mode: z.string(), + blessings: z.array(battleBuffStoreSchema), + cycle_count: z.number(), + stage_id: z.number(), + monsters: z.array(z.array(monsterStoreSchema)) +}); + +export const ceConfigStoreSchema = z.object({ + blessings: z.array(battleBuffStoreSchema), + cycle_count: z.number(), + stage_id: z.number(), + monsters: z.array(z.array(monsterStoreSchema)) +}); + +export const micsSchema = z.object({ + avatars: z.record(z.string(), avatarStoreSchema), + battle_type: z.string(), + moc_config: mocConfigStoreSchema, + pf_config: pfConfigStoreSchema, + as_config: asConfigStoreSchema, + ce_config: ceConfigStoreSchema, + peak_config: peakConfigStoreSchema +}); diff --git a/src/zod/showcase.zod.ts b/src/zod/showcase.zod.ts new file mode 100644 index 0000000..304d71d --- /dev/null +++ b/src/zod/showcase.zod.ts @@ -0,0 +1,29 @@ +// Generated by ts-to-zod +import { z } from "zod"; + +export const relicShowcaseTypeSchema = z.object({ + img: z.string(), + mainAffix: z.object({ + property: z.string(), + level: z.number(), + valueAffix: z.string(), + detail: z.object({ + name: z.string(), + icon: z.string(), + unit: z.string(), + baseStat: z.string() + }) + }), + subAffix: z.array(z.object({ + property: z.string(), + valueAffix: z.string(), + detail: z.object({ + name: z.string(), + icon: z.string(), + unit: z.string(), + baseStat: z.string() + }), + step: z.number(), + count: z.number() + })) +}); diff --git a/src/zod/srtools.zod.ts b/src/zod/srtools.zod.ts new file mode 100644 index 0000000..ee74856 --- /dev/null +++ b/src/zod/srtools.zod.ts @@ -0,0 +1,94 @@ +// Generated by ts-to-zod +import { z } from "zod"; + +export const subAffixSchema = z.object({ + sub_affix_id: z.number(), + count: z.number(), + step: z.number() +}); + +export const relicJsonSchema = z.object({ + level: z.number(), + relic_id: z.number(), + relic_set_id: z.number(), + main_affix_id: z.number(), + sub_affixes: z.array(subAffixSchema), + internal_uid: z.number(), + equip_avatar: z.number() +}); + +export const lightconeJsonSchema = z.object({ + level: z.number(), + item_id: z.number(), + equip_avatar: z.number(), + rank: z.number(), + promotion: z.number(), + internal_uid: z.number() +}); + +export const avatarDataSchema = z.object({ + rank: z.number(), + skills: z.record(z.string(), z.number()), + skills_by_anchor_type: z.record(z.string(), z.number()).optional() +}); + +export const avatarJsonSchema = z.object({ + owner_uid: z.number().optional(), + avatar_id: z.number(), + data: avatarDataSchema, + level: z.number(), + promotion: z.number(), + techniques: z.array(z.number()), + sp_value: z.number(), + sp_max: z.number() +}); + +export const monsterJsonSchema = z.object({ + monster_id: z.number(), + level: z.number(), + amount: z.number() +}); + +export const dynamicKeyJsonSchema = z.object({ + key: z.string(), + value: z.number() +}); + +export const battleBuffJsonSchema = z.object({ + level: z.number(), + id: z.number(), + dynamic_key: dynamicKeyJsonSchema.optional() +}); + +export const battleConfigJsonSchema = z.object({ + battle_type: z.string(), + blessings: z.array(battleBuffJsonSchema), + custom_stats: z.array(subAffixSchema), + cycle_count: z.number(), + stage_id: z.number(), + path_resonance_id: z.number(), + monsters: z.array(z.array(monsterJsonSchema)) +}); + +const loadoutJsonSchema = z.object({ + name: z.string(), + avatar_id: z.number(), + relic_list: z.array(z.string()) +}); + +export const freeSrJsonSchema = z.object({ + key: z.string().optional(), + lightcones: z.array(lightconeJsonSchema), + relics: z.array(relicJsonSchema), + avatars: z.record(z.string(), avatarJsonSchema), + battle_config: battleConfigJsonSchema, + loadout: z.array(loadoutJsonSchema).optional() +}); + +const extraDataSchema = z.any(); + +export const psResponseSchema = z.object({ + status: z.number(), + message: z.string(), + extra_data: extraDataSchema.optional() +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2169551 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,41 @@ +{ + "compilerOptions": { + "target": "ES2018", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": [ + "./src/*" + ] + }, + "esModuleInterop": true + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] +}