commit 00797f0c9697269bbdb5faafea66d21358b975f2 Author: tejaspanchall Date: Wed May 28 23:11:20 2025 +0530 fresh diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e979aac --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +ANIWATCH_API = your animwatch-api key \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e72b4d6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# 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 diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..348c45a --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,14 @@ +import { dirname } from "path"; +import { fileURLToPath } from "url"; +import { FlatCompat } from "@eslint/eslintrc"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const compat = new FlatCompat({ + baseDirectory: __dirname, +}); + +const eslintConfig = [...compat.extends("next/core-web-vitals")]; + +export default eslintConfig; diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..b8d6842 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/next.config.js b/next.config.js new file mode 100644 index 0000000..a96bbc3 --- /dev/null +++ b/next.config.js @@ -0,0 +1,82 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, + swcMinify: true, + env: { + NEXT_PUBLIC_CORSPROXY_URL: process.env.CORSPROXY_URL, + }, + images: { + domains: [ + 'via.placeholder.com', + 'gogocdn.net', + 'cdnjs.cloudflare.com', + 'img.zorores.com', + 'poster.zoros.to', + 'cdn.myanimelist.net', + 's4.anilist.co', + 'artworks.thetvdb.com', + 'image.tmdb.org', + 'justanimeapi.vercel.app', + 'consumet.org', + 'api.consumet.org', + 'img.flixhq.to', + 'img.bflix.to', + ], + remotePatterns: [ + { + protocol: 'https', + hostname: '**', + }, + ], + unoptimized: true, + }, + experimental: { + scrollRestoration: true, + }, + serverExternalPackages: ['puppeteer-core'], + async rewrites() { + return [ + { + source: '/api/v2/hianime/:path*', + destination: 'https://justaniwatchapi.vercel.app/api/v2/hianime/:path*' + }, + { + source: '/api/anime/:path*', + destination: 'https://justaniwatchapi.vercel.app/api/v2/hianime/anime/:path*' + } + ] + }, + async headers() { + return [ + { + source: '/api/:path*', + headers: [ + { key: 'Access-Control-Allow-Credentials', value: 'true' }, + { key: 'Access-Control-Allow-Origin', value: '*' }, + { key: 'Access-Control-Allow-Methods', value: 'GET,OPTIONS,PATCH,DELETE,POST,PUT' }, + { key: 'Access-Control-Allow-Headers', value: 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, Authorization' }, + { key: 'Referrer-Policy', value: 'no-referrer-when-downgrade' }, + { key: 'Cross-Origin-Resource-Policy', value: 'cross-origin' }, + { key: 'Cross-Origin-Opener-Policy', value: 'same-origin' }, + ], + }, + { + source: '/:path*', + headers: [ + { key: 'Referrer-Policy', value: 'no-referrer-when-downgrade' }, + { key: 'Cross-Origin-Resource-Policy', value: 'cross-origin' }, + { key: 'Cross-Origin-Opener-Policy', value: 'same-origin' }, + ] + } + ]; + }, + webpack(config) { + config.module.rules.push({ + test: /\.svg$/, + use: [{ loader: '@svgr/webpack', options: { icon: true } }], + }); + return config; + }, +}; + +module.exports = nextConfig; \ No newline at end of file diff --git a/next.config.mjs b/next.config.mjs new file mode 100644 index 0000000..7fdb011 --- /dev/null +++ b/next.config.mjs @@ -0,0 +1,17 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + env: { + NEXT_PUBLIC_CORSPROXY_URL: process.env.CORSPROXY_URL, + }, + images: { + unoptimized: true, + remotePatterns: [ + { + protocol: 'https', + hostname: '**', + }, + ], + } +}; + +export default nextConfig; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..732c658 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5911 @@ +{ + "name": "justanime", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "justanime", + "version": "0.1.0", + "dependencies": { + "@heroicons/react": "^2.2.0", + "hls.js": "^1.5.7", + "next": "latest", + "proxy-from-env": "^1.1.0", + "react": "latest", + "react-dom": "latest", + "swiper": "^11.2.6" + }, + "devDependencies": { + "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4", + "autoprefixer": "latest", + "eslint": "^9", + "eslint-config-next": "15.2.5", + "postcss": "latest", + "tailwindcss": "^4" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emnapi/core": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.0.tgz", + "integrity": "sha512-H+N/FqT07NmLmt6OFFtDfwe8PNygprzBikrEMyQfgqSmT0vzE515Pz7R8izwB9q/zsH/MA64AKoul3sA6/CzVg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.0.tgz", + "integrity": "sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.1.tgz", + "integrity": "sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", + "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", + "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "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.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", + "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", + "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.13.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", + "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@heroicons/react": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz", + "integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==", + "license": "MIT", + "peerDependencies": { + "react": ">= 16 || ^19.0.0-rc" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.8.tgz", + "integrity": "sha512-OBlgKdX7gin7OIq4fadsjpg+cp2ZphvAIKucHsNfTdJiqdOmOEwQd/bHi0VwNrcw5xpBJyUw6cK/QilCqy1BSg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.0", + "@emnapi/runtime": "^1.4.0", + "@tybys/wasm-util": "^0.9.0" + } + }, + "node_modules/@next/env": { + "version": "15.2.5", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.5.tgz", + "integrity": "sha512-uWkCf9C8wKTyQjqrNk+BA7eL3LOQdhL+xlmJUf2O85RM4lbzwBwot3Sqv2QGe/RGnc3zysIf1oJdtq9S00pkmQ==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "15.2.5", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.2.5.tgz", + "integrity": "sha512-Q1ncASVFKSy+AbabimYxr/2HH/h+qlKlwu1fYV48xUefGzVimS3i3nKwYsM2w+rLdpMFdJyoVowrYyjKu47rBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "15.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.2.5.tgz", + "integrity": "sha512-4OimvVlFTbgzPdA0kh8A1ih6FN9pQkL4nPXGqemEYgk+e7eQhsst/p35siNNqA49eQA6bvKZ1ASsDtu9gtXuog==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "15.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.2.5.tgz", + "integrity": "sha512-ohzRaE9YbGt1ctE0um+UGYIDkkOxHV44kEcHzLqQigoRLaiMtZzGrA11AJh2Lu0lv51XeiY1ZkUvkThjkVNBMA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.2.5.tgz", + "integrity": "sha512-FMSdxSUt5bVXqqOoZCc/Seg4LQep9w/fXTazr/EkpXW2Eu4IFI9FD7zBDlID8TJIybmvKk7mhd9s+2XWxz4flA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "15.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.2.5.tgz", + "integrity": "sha512-4ZNKmuEiW5hRKkGp2HWwZ+JrvK4DQLgf8YDaqtZyn7NYdl0cHfatvlnLFSWUayx9yFAUagIgRGRk8pFxS8Qniw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "15.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.2.5.tgz", + "integrity": "sha512-bE6lHQ9GXIf3gCDE53u2pTl99RPZW5V1GLHSRMJ5l/oB/MT+cohu9uwnCK7QUph2xIOu2a6+27kL0REa/kqwZw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "15.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.2.5.tgz", + "integrity": "sha512-y7EeQuSkQbTAkCEQnJXm1asRUuGSWAchGJ3c+Qtxh8LVjXleZast8Mn/rL7tZOm7o35QeIpIcid6ufG7EVTTcA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.2.5.tgz", + "integrity": "sha512-gQMz0yA8/dskZM2Xyiq2FRShxSrsJNha40Ob/M2n2+JGRrZ0JwTVjLdvtN6vCxuq4ByhOd4a9qEf60hApNR2gQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "15.2.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.2.5.tgz", + "integrity": "sha512-tBDNVUcI7U03+3oMvJ11zrtVin5p0NctiuKmTGyaTIEAVj9Q77xukLXGXRnWxKRIIdFG4OTA2rUVGZDYOwgmAA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.11.0.tgz", + "integrity": "sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.3.tgz", + "integrity": "sha512-H/6r6IPFJkCfBJZ2dKZiPJ7Ueb2wbL592+9bQEl2r73qbX6yGnmQVIfiUvDRB2YI0a3PWDrzUwkvQx1XW1bNkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.29.2", + "tailwindcss": "4.1.3" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.3.tgz", + "integrity": "sha512-t16lpHCU7LBxDe/8dCj9ntyNpXaSTAgxWm1u2XQP5NiIu4KGSyrDJJRlK9hJ4U9yJxx0UKCVI67MJWFNll5mOQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.3", + "@tailwindcss/oxide-darwin-arm64": "4.1.3", + "@tailwindcss/oxide-darwin-x64": "4.1.3", + "@tailwindcss/oxide-freebsd-x64": "4.1.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.3", + "@tailwindcss/oxide-linux-x64-musl": "4.1.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.3.tgz", + "integrity": "sha512-cxklKjtNLwFl3mDYw4XpEfBY+G8ssSg9ADL4Wm6//5woi3XGqlxFsnV5Zb6v07dxw1NvEX2uoqsxO/zWQsgR+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.3.tgz", + "integrity": "sha512-mqkf2tLR5VCrjBvuRDwzKNShRu99gCAVMkVsaEOFvv6cCjlEKXRecPu9DEnxp6STk5z+Vlbh1M5zY3nQCXMXhw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.3.tgz", + "integrity": "sha512-7sGraGaWzXvCLyxrc7d+CCpUN3fYnkkcso3rCzwUmo/LteAl2ZGCDlGvDD8Y/1D3ngxT8KgDj1DSwOnNewKhmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.3.tgz", + "integrity": "sha512-E2+PbcbzIReaAYZe997wb9rId246yDkCwAakllAWSGqe6VTg9hHle67hfH6ExjpV2LSK/siRzBUs5wVff3RW9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.3.tgz", + "integrity": "sha512-GvfbJ8wjSSjbLFFE3UYz4Eh8i4L6GiEYqCtA8j2Zd2oXriPuom/Ah/64pg/szWycQpzRnbDiJozoxFU2oJZyfg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.3.tgz", + "integrity": "sha512-35UkuCWQTeG9BHcBQXndDOrpsnt3Pj9NVIB4CgNiKmpG8GnCNXeMczkUpOoqcOhO6Cc/mM2W7kaQ/MTEENDDXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.3.tgz", + "integrity": "sha512-dm18aQiML5QCj9DQo7wMbt1Z2tl3Giht54uVR87a84X8qRtuXxUqnKQkRDK5B4bCOmcZ580lF9YcoMkbDYTXHQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.3.tgz", + "integrity": "sha512-LMdTmGe/NPtGOaOfV2HuO7w07jI3cflPrVq5CXl+2O93DCewADK0uW1ORNAcfu2YxDUS035eY2W38TxrsqngxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.3.tgz", + "integrity": "sha512-aalNWwIi54bbFEizwl1/XpmdDrOaCjRFQRgtbv9slWjmNPuJJTIKPHf5/XXDARc9CneW9FkSTqTbyvNecYAEGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.3.tgz", + "integrity": "sha512-PEj7XR4OGTGoboTIAdXicKuWl4EQIjKHKuR+bFy9oYN7CFZo0eu74+70O4XuERX4yjqVZGAkCdglBODlgqcCXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.3.tgz", + "integrity": "sha512-T8gfxECWDBENotpw3HR9SmNiHC9AOJdxs+woasRZ8Q/J4VHN0OMs7F+4yVNZ9EVN26Wv6mZbK0jv7eHYuLJLwA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.3.tgz", + "integrity": "sha512-6s5nJODm98F++QT49qn8xJKHQRamhYHfMi3X7/ltxiSQ9dyRsaFSfFkfaMsanWzf+TMYQtbk8mt5f6cCVXJwfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.1.3", + "@tailwindcss/oxide": "4.1.3", + "postcss": "^8.4.41", + "tailwindcss": "4.1.3" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", + "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.1.tgz", + "integrity": "sha512-ba0rr4Wfvg23vERs3eB+P3lfj2E+2g3lhWcCVukUuhtcdUx5lSIFZlGFEBHKr+3zizDa/TvZTptdNHVZWAkSBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.29.1", + "@typescript-eslint/type-utils": "8.29.1", + "@typescript-eslint/utils": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.1.tgz", + "integrity": "sha512-zczrHVEqEaTwh12gWBIJWj8nx+ayDcCJs06yoNMY0kwjMWDM6+kppljY+BxWI06d2Ja+h4+WdufDcwMnnMEWmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.29.1", + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/typescript-estree": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.1.tgz", + "integrity": "sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.1.tgz", + "integrity": "sha512-DkDUSDwZVCYN71xA4wzySqqcZsHKic53A4BLqmrWFFpOpNSoxX233lwGu/2135ymTCR04PoKiEEEvN1gFYg4Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.29.1", + "@typescript-eslint/utils": "8.29.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.1.tgz", + "integrity": "sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.1.tgz", + "integrity": "sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "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.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.1.tgz", + "integrity": "sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.29.1", + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/typescript-estree": "8.29.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.1.tgz", + "integrity": "sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.29.1", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.4.1.tgz", + "integrity": "sha512-8Tv+Bsd0BjGwfEedIyor4inw8atppRxM5BdUnIt+3mAm/QXUm7Dw74CHnXpfZKXkp07EXJGiA8hStqCINAWhdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.4.1.tgz", + "integrity": "sha512-X8c3PhWziEMKAzZz+YAYWfwawi5AEgzy/hmfizAB4C70gMHLKmInJcp1270yYAOs7z07YVFI220pp50z24Jk3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.4.1.tgz", + "integrity": "sha512-UUr/nREy1UdtxXQnmLaaTXFGOcGxPwNIzeJdb3KXai3TKtC1UgNOB9s8KOA4TaxOUBR/qVgL5BvBwmUjD5yuVA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.4.1.tgz", + "integrity": "sha512-e3pII53dEeS8inkX6A1ad2UXE0nuoWCqik4kOxaDnls0uJUq0ntdj5d9IYd+bv5TDwf9DSge/xPOvCmRYH+Tsw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.4.1.tgz", + "integrity": "sha512-e/AKKd9gR+HNmVyDEPI/PIz2t0DrA3cyonHNhHVjrkxe8pMCiYiqhtn1+h+yIpHUtUlM6Y1FNIdivFa+r7wrEQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.4.1.tgz", + "integrity": "sha512-vtIu34luF1jRktlHtiwm2mjuE8oJCsFiFr8hT5+tFQdqFKjPhbJXn83LswKsOhy0GxAEevpXDI4xxEwkjuXIPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.4.1.tgz", + "integrity": "sha512-H3PaOuGyhFXiyJd+09uPhGl4gocmhyi1BRzvsP8Lv5AQO3p3/ZY7WjV4t2NkBksm9tMjf3YbOVHyPWi2eWsNYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.4.1.tgz", + "integrity": "sha512-4+GmJcaaFntCi1S01YByqp8wLMjV/FyQyHVGm0vedIhL1Vfx7uHkz/sZmKsidRwokBGuxi92GFmSzqT2O8KcNA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.4.1.tgz", + "integrity": "sha512-6RDQVCmtFYTlhy89D5ixTqo9bTQqFhvNN0Ey1wJs5r+01Dq15gPHRXv2jF2bQATtMrOfYwv+R2ZR9ew1N1N3YQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.4.1.tgz", + "integrity": "sha512-XpU9uzIkD86+19NjCXxlVPISMUrVXsXo5htxtuG+uJ59p5JauSRZsIxQxzzfKzkxEjdvANPM/lS1HFoX6A6QeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.4.1.tgz", + "integrity": "sha512-3CDjG/spbTKCSHl66QP2ekHSD+H34i7utuDIM5gzoNBcZ1gTO0Op09Wx5cikXnhORRf9+HyDWzm37vU1PLSM1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.4.1.tgz", + "integrity": "sha512-50tYhvbCTnuzMn7vmP8IV2UKF7ITo1oihygEYq9wW2DUb/Y+QMqBHJUSCABRngATjZ4shOK6f2+s0gQX6ElENQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.8" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.4.1.tgz", + "integrity": "sha512-KyJiIne/AqV4IW0wyQO34wSMuJwy3VxVQOfIXIPyQ/Up6y/zi2P/WwXb78gHsLiGRUqCA9LOoCX+6dQZde0g1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.4.1.tgz", + "integrity": "sha512-y2NUD7pygrBolN2NoXUrwVqBpKPhF8DiSNE5oB5/iFO49r2DpoYqdj5HPb3F42fPBH5qNqj6Zg63+xCEzAD2hw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.4.1.tgz", + "integrity": "sha512-hVXaObGI2lGFmrtT77KSbPQ3I+zk9IU500wobjk0+oX59vg/0VqAzABNtt3YSQYgXTC2a/LYxekLfND/wlt0yQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.10.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz", + "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001712", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001712.tgz", + "integrity": "sha512-MBqPpGYYdQ7/hfKiet9SCI+nmN5/hp4ZzveOJubl5DTAMa5oggjAuoi0Z4onBpKPFI2ePGnQuQIzF3VxDjDJig==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "optional": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "optional": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.139", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.139.tgz", + "integrity": "sha512-GGnRYOTdN5LYpwbIr0rwP/ZHOQSvAF6TG0LSzp28uCBb9JiXHJGmaaKw29qjNJc5bGnnp6kXJqRnGMQoELwi5w==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "dev": true, + "license": "MIT", + "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.3", + "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.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "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-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.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.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", + "dev": true, + "license": "MIT", + "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-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.6", + "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.4", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", + "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.0", + "@eslint/core": "^0.12.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.24.0", + "@eslint/plugin-kit": "^0.2.7", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "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.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.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" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-next": { + "version": "15.2.5", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.2.5.tgz", + "integrity": "sha512-/aUpN5FVI3FD+OB4gY0GyD2TwIOjLk8mG0B9NxVsSn8/svNmzFaIAaS80ZO1zWaIcWxrzTy2FcPVdsCK7eiceA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/eslint-plugin-next": "15.2.5", + "@rushstack/eslint-patch": "^1.10.3", + "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsx-a11y": "^6.10.0", + "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react-hooks": "^5.0.0" + }, + "peerDependencies": { + "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.0.tgz", + "integrity": "sha512-aV3/dVsT0/H9BtpNwbaqvl+0xGMRGzncLyhm793NFGvbwGGvzyAykqWZ8oZlZuGwuHkwJjhWJkG1cM3ynvd2pQ==", + "dev": true, + "license": "ISC", + "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.12", + "unrs-resolver": "^1.3.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-resolver-typescript" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", + "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hls.js": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.1.tgz", + "integrity": "sha512-7GOkcqn0Y9EqU2OJZlzkwxj9Uynuln7URvr7dRjgqNJNZ5UbbjL/v1BjAvQogy57Psdd/ek1u2s6IDEFYlabrA==", + "license": "Apache-2.0" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT", + "optional": true + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.7.1" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz", + "integrity": "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.29.2", + "lightningcss-darwin-x64": "1.29.2", + "lightningcss-freebsd-x64": "1.29.2", + "lightningcss-linux-arm-gnueabihf": "1.29.2", + "lightningcss-linux-arm64-gnu": "1.29.2", + "lightningcss-linux-arm64-musl": "1.29.2", + "lightningcss-linux-x64-gnu": "1.29.2", + "lightningcss-linux-x64-musl": "1.29.2", + "lightningcss-win32-arm64-msvc": "1.29.2", + "lightningcss-win32-x64-msvc": "1.29.2" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.2.tgz", + "integrity": "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.2.tgz", + "integrity": "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.2.tgz", + "integrity": "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.2.tgz", + "integrity": "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.2.tgz", + "integrity": "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.2.tgz", + "integrity": "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.2.tgz", + "integrity": "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.2.tgz", + "integrity": "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.2.tgz", + "integrity": "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz", + "integrity": "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/next": { + "version": "15.2.5", + "resolved": "https://registry.npmjs.org/next/-/next-15.2.5.tgz", + "integrity": "sha512-LlqS8ljc7RWR3riUwxB5+14v7ULAa5EuLUyarD/sFgXPd6Hmmscg8DXcu9hDdh5atybrIDVBrFhjDpRIQo/4pQ==", + "license": "MIT", + "dependencies": { + "@next/env": "15.2.5", + "@swc/counter": "0.1.3", + "@swc/helpers": "0.5.15", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "15.2.5", + "@next/swc-darwin-x64": "15.2.5", + "@next/swc-linux-arm64-gnu": "15.2.5", + "@next/swc-linux-arm64-musl": "15.2.5", + "@next/swc-linux-x64-gnu": "15.2.5", + "@next/swc-linux-x64-musl": "15.2.5", + "@next/swc-win32-arm64-msvc": "15.2.5", + "@next/swc-win32-x64-msvc": "15.2.5", + "sharp": "^0.33.5" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "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" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "devOptional": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stable-hash": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/swiper": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/swiper/-/swiper-11.2.6.tgz", + "integrity": "sha512-8aXpYKtjy3DjcbzZfz+/OX/GhcU5h+looA6PbAzHMZT6ESSycSp9nAjPCenczgJyslV+rUGse64LMGpWE3PX9Q==", + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/swiperjs" + }, + { + "type": "open_collective", + "url": "http://opencollective.com/swiper" + } + ], + "license": "MIT", + "engines": { + "node": ">= 4.7.0" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.3.tgz", + "integrity": "sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", + "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unrs-resolver": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.4.1.tgz", + "integrity": "sha512-MhPB3wBI5BR8TGieTb08XuYlE8oFVEXdSAgat3psdlRyejl8ojQ8iqPcjh094qCZ1r+TnkxzP6BeCd/umfHckQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/JounQin" + }, + "optionalDependencies": { + "@unrs/resolver-binding-darwin-arm64": "1.4.1", + "@unrs/resolver-binding-darwin-x64": "1.4.1", + "@unrs/resolver-binding-freebsd-x64": "1.4.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.4.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.4.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.4.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.4.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.4.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.4.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.4.1", + "@unrs/resolver-binding-linux-x64-musl": "1.4.1", + "@unrs/resolver-binding-wasm32-wasi": "1.4.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.4.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.4.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.4.1" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..58976c1 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "justanime", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@heroicons/react": "^2.2.0", + "hls.js": "^1.5.7", + "next": "latest", + "proxy-from-env": "^1.1.0", + "react": "latest", + "react-dom": "latest", + "swiper": "^11.2.6" + }, + "devDependencies": { + "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4", + "autoprefixer": "latest", + "eslint": "^9", + "eslint-config-next": "15.2.5", + "postcss": "latest", + "tailwindcss": "^4" + } +} 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/LandingPage.jpg b/public/LandingPage.jpg new file mode 100644 index 0000000..12b0bc5 Binary files /dev/null and b/public/LandingPage.jpg differ diff --git a/public/Logo.png b/public/Logo.png new file mode 100644 index 0000000..ae75320 Binary files /dev/null and b/public/Logo.png differ diff --git a/public/images/placeholder.png b/public/images/placeholder.png new file mode 100644 index 0000000..490b3f6 Binary files /dev/null and b/public/images/placeholder.png differ diff --git a/src/app/anime/[id]/page.js b/src/app/anime/[id]/page.js new file mode 100644 index 0000000..b6ab831 --- /dev/null +++ b/src/app/anime/[id]/page.js @@ -0,0 +1,114 @@ +import React, { Suspense } from 'react'; +import Link from 'next/link'; +import { fetchAnimeInfo } from '@/lib/api'; +import AnimeDetails from '@/components/AnimeDetails.js'; + +// Loading state component +const LoadingState = () => ( +
+
+ {/* Background Placeholder */} +
+ + {/* Content Placeholder */} +
+
+ {/* Poster Placeholder */} +
+
+
+ + {/* Details Placeholder */} +
+
+
+
+
+
+
+
+
+
+
+); + +// Error state component +const ErrorState = ({ error }) => ( +
+
+

Error Loading Anime

+

{error}

+
+ + + Go Back Home + +
+
+
+); + +// Not found state component +const NotFoundState = () => ( +
+
+

Anime Not Found

+

The anime you're looking for doesn't exist or was removed.

+ + Go Back Home + +
+
+); + +// Main anime content component +const AnimeContent = async ({ id }) => { + try { + console.log('[Server] Fetching info for ID:', id); + + const anime = await fetchAnimeInfo(id); + + console.log('[Server] API Response structure:', JSON.stringify({ + info: anime.info ? 'Present' : 'Missing', + moreInfo: anime.moreInfo ? 'Present' : 'Missing', + relatedAnime: Array.isArray(anime.relatedAnime) ? anime.relatedAnime.length : 'Not an array', + recommendations: Array.isArray(anime.recommendations) ? anime.recommendations.length : 'Not an array', + seasons: Array.isArray(anime.seasons) ? anime.seasons.length : 'Not an array', + mostPopular: Array.isArray(anime.mostPopular) ? anime.mostPopular.length : 'Not an array', + promotionalVideos: anime.info?.promotionalVideos ? anime.info.promotionalVideos.length : 'Missing' + }, null, 2)); + + if (!anime || !anime.info) { + console.error('[Server] Missing required anime data'); + return ; + } + + return ( +
+ +
+ ); + } catch (error) { + console.error('[Server Error]', error); + return ; + } +}; + +// Main page component with Suspense +export default function AnimeInfoPage({ params }) { + const { id } = params; + + return ( + }> + + + ); +} \ No newline at end of file diff --git a/src/app/anime/layout.js b/src/app/anime/layout.js new file mode 100644 index 0000000..9fab739 --- /dev/null +++ b/src/app/anime/layout.js @@ -0,0 +1,5 @@ +import SharedLayout from '@/components/SharedLayout'; + +export default function AnimeLayout({ children }) { + return {children}; +} \ No newline at end of file diff --git a/src/app/contacts/page.jsx b/src/app/contacts/page.jsx new file mode 100644 index 0000000..5f5ecd9 --- /dev/null +++ b/src/app/contacts/page.jsx @@ -0,0 +1,169 @@ +'use client'; + +import { useState } from 'react'; +import SharedLayout from '@/components/SharedLayout'; + +export default function ContactsPage() { + const [formData, setFormData] = useState({ + name: '', + email: '', + subject: '', + message: '', + }); + + const [isSubmitting, setIsSubmitting] = useState(false); + const [submitStatus, setSubmitStatus] = useState(null); + + const handleChange = (e) => { + const { name, value } = e.target; + setFormData((prev) => ({ + ...prev, + [name]: value, + })); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + setIsSubmitting(true); + + // Simulate form submission + try { + // In a real application, you would send this data to your backend/API + await new Promise(resolve => setTimeout(resolve, 1000)); + setSubmitStatus({ success: true, message: 'Your message has been sent successfully!' }); + // Reset form + setFormData({ + name: '', + email: '', + subject: '', + message: '', + }); + } catch (error) { + setSubmitStatus({ success: false, message: 'There was an error sending your message. Please try again.' }); + } finally { + setIsSubmitting(false); + } + }; + + return ( + +
+

Contact Us

+ +
+
+
+

+ Have questions, suggestions, or need assistance? We're here to help. + Fill out the form and we'll get back to you as soon as possible. +

+
+ +
+
+

Email

+

support@justanime.com

+
+ +
+

Connect With Us

+ +
+
+
+ +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + {submitStatus && ( +
+ {submitStatus.message} +
+ )} +
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/src/app/dmca/page.jsx b/src/app/dmca/page.jsx new file mode 100644 index 0000000..e4c7127 --- /dev/null +++ b/src/app/dmca/page.jsx @@ -0,0 +1,62 @@ +'use client'; + +import SharedLayout from '@/components/SharedLayout'; + +export default function DmcaPage() { + return ( + +
+

DMCA Policy

+ +
+
+

+ We take the intellectual property rights of others seriously and require that our Users do the same. + The Digital Millennium Copyright Act (DMCA) established a process for addressing claims of copyright infringement. + If you own a copyright or have authority to act on behalf of a copyright owner and want to report a claim that a third party is + infringing that material on or through JustAnime's services, please submit a DMCA report as outlined below, and we will take appropriate action. +

+ +
+

DMCA Report Requirements

+
    +
  • A description of the copyrighted work that you claim is being infringed;
  • +
  • A description of the material you claim is infringing and that you want removed or access to which you want disabled and the URL or other location of that material;
  • +
  • Your name, title (if acting as an agent), address, telephone number, and email address;
  • +
  • The following statement: "I have a good faith belief that the use of the copyrighted material I am complaining of is not authorized by the copyright owner, its agent, or the law (e.g., as a fair use)";
  • +
  • The following statement: "The information in this notice is accurate and, under penalty of perjury, I am the owner, or authorized to act on behalf of the owner, of the copyright or of an exclusive right that is allegedly infringed";
  • +
  • An electronic or physical signature of the owner of the copyright or a person authorized to act on the owner's behalf.
  • +
+
+ +
+

+ Your DMCA takedown request should be submitted through our Contact page. +

+ +

+ We will then review your DMCA request and take proper actions, including removal of the content from the website. +

+
+ +
+

Submit a DMCA Request

+

+ To submit a DMCA takedown request, please include all required information as listed above and + contact us through our Contact page. +

+ +
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/src/app/favicon.ico b/src/app/favicon.ico new file mode 100644 index 0000000..718d6fe Binary files /dev/null and b/src/app/favicon.ico differ diff --git a/src/app/globals.css b/src/app/globals.css new file mode 100644 index 0000000..9413505 --- /dev/null +++ b/src/app/globals.css @@ -0,0 +1,173 @@ +@import "tailwindcss"; + +:root { + --background: #0a0a0a; + --foreground: #ffffff; + --primary: #d1d1d1; + --secondary: #404040; + --accent: #808080; + --card: #131313; + --border: #2a2a2a; + --hover: #333333; + --text-muted: #8a8a8a; +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-primary: var(--primary); + --color-secondary: var(--secondary); + --color-accent: var(--accent); + --color-card: var(--card); + --color-border: var(--border); + --color-hover: var(--hover); + --color-text-muted: var(--text-muted); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0a0a0a; + --foreground: #f5f5f5; + } +} + +body { + margin: 0; + padding: 0; + min-height: 100vh; + background-color: var(--background); + color: var(--foreground); + font-family: var(--font-sans); +} + +/* Custom scrollbar */ +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-track { + background: var(--background); +} + +::-webkit-scrollbar-thumb { + background: var(--border); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--secondary); +} + +/* Hide scrollbar for Chrome, Safari and Opera */ +.scrollbar-hide::-webkit-scrollbar { + display: none; +} + +/* Hide scrollbar for IE, Edge and Firefox */ +.scrollbar-hide { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} + +.custom-scrollbar::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +.custom-scrollbar::-webkit-scrollbar-track { + background: rgba(255, 255, 255, 0.05); + border-radius: 8px; +} + +.custom-scrollbar::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.15); + border-radius: 8px; +} + +.custom-scrollbar::-webkit-scrollbar-thumb:hover { + background: rgba(255, 255, 255, 0.25); +} + +/* Modern card styling */ +.card-hover { + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.card-hover:hover { + transform: translateY(-4px); + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2); +} + +/* Gradient text */ +.gradient-text { + background: linear-gradient(to right, var(--foreground), var(--text-muted)); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; +} + +/* Button styles */ +.btn-primary { + background-color: var(--foreground); + color: var(--background); + transition: all 0.2s ease; +} + +.btn-primary:hover { + background-color: var(--text-muted); +} + +.btn-secondary { + background-color: transparent; + border: 1px solid var(--border); + color: var(--foreground); + transition: all 0.2s ease; +} + +.btn-secondary:hover { + background-color: var(--hover); +} + +/* Animation utilities */ +.fade-in { + animation: fadeIn 0.5s ease-in forwards; +} + +@keyframes fadeIn { + 0% { opacity: 0; } + 100% { opacity: 1; } +} + +.slide-up { + animation: slideUp 0.5s ease forwards; +} + +@keyframes slideUp { + 0% { transform: translateY(20px); opacity: 0; } + 100% { transform: translateY(0); opacity: 1; } +} + +/* Radial gradient for hero sections */ +.bg-radial-gradient { + background: radial-gradient(circle at center, rgba(20, 20, 20, 0) 0%, rgba(0, 0, 0, 0.8) 100%); +} + +/* Grid Pattern for backgrounds */ +.bg-grid-pattern { + background-size: 25px 25px; + background-image: + linear-gradient(to right, rgba(255, 255, 255, 0.05) 1px, transparent 1px), + linear-gradient(to bottom, rgba(255, 255, 255, 0.05) 1px, transparent 1px); +} + +/* Hide scrollbar while maintaining scroll functionality */ +.hide-scrollbar { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} + +.hide-scrollbar::-webkit-scrollbar { + display: none; /* Chrome, Safari and Opera */ +} diff --git a/src/app/home/layout.js b/src/app/home/layout.js new file mode 100644 index 0000000..48bdb93 --- /dev/null +++ b/src/app/home/layout.js @@ -0,0 +1,5 @@ +import SharedLayout from '@/components/SharedLayout'; + +export default function HomeLayout({ children }) { + return {children}; +} \ No newline at end of file diff --git a/src/app/home/page.js b/src/app/home/page.js new file mode 100644 index 0000000..2f02cfc --- /dev/null +++ b/src/app/home/page.js @@ -0,0 +1,185 @@ +import React from 'react'; +import AnimeCard from '@/components/AnimeCard'; +import TopLists from '@/components/TopLists'; +import AnimeCalendar from '@/components/AnimeCalendar'; +import GenreBar from '@/components/GenreBar'; +import SpotlightCarousel from '@/components/SpotlightCarousel'; +import AnimeTabs from '@/components/AnimeTabs'; +import TrendingList from '@/components/TrendingList'; +import Link from 'next/link'; +import { + fetchRecentEpisodes, + fetchMostFavorite, + fetchSpotlightAnime, + fetchTopToday, + fetchTopWeek, + fetchTopMonth, + fetchMostPopular, + fetchTopAiring, + fetchLatestCompleted, + fetchTrending +} from '@/lib/api'; + +// New unified section component with grid layout +const AnimeGridSection = ({ title, animeList = [], viewMoreLink, isRecent = false }) => { + if (!animeList || animeList.length === 0) { + return ( +
+
+

{title}

+
+
+
+
+
+
+
+
+ ); + } + + return ( +
+
+

{title}

+ {viewMoreLink && ( + + View All + + + + + )} +
+ +
+ {animeList.slice(0, 12).map((anime, index) => ( + + ))} +
+
+ ); +}; + +async function HomePage() { + try { + // Fetch all data in parallel + const [ + spotlightData, + recentEpisodes, + mostFavorite, + topToday, + topWeek, + topMonth, + topAiring, + popular, + latestCompleted, + trending + ] = await Promise.all([ + fetchSpotlightAnime().catch(err => { + console.error("Error fetching spotlight anime:", err); + return []; + }), + fetchRecentEpisodes().catch(err => { + console.error("Error fetching recent episodes:", err); + return { results: [] }; + }), + fetchMostFavorite().catch(err => { + console.error("Error fetching most favorite:", err); + return { results: [] }; + }), + fetchTopToday().catch(err => { + console.error("Error fetching top today:", err); + return []; + }), + fetchTopWeek().catch(err => { + console.error("Error fetching top week:", err); + return []; + }), + fetchTopMonth().catch(err => { + console.error("Error fetching top month:", err); + return []; + }), + fetchTopAiring().catch(err => { + console.error("Error fetching top airing anime:", err); + return { results: [] }; + }), + fetchMostPopular().catch(err => { + console.error("Error fetching popular anime:", err); + return { results: [] }; + }), + fetchLatestCompleted().catch(err => { + console.error("Error fetching latest completed anime:", err); + return { results: [] }; + }), + fetchTrending().catch(err => { + console.error("Error fetching trending anime:", err); + return { results: [] }; + }) + ]); + + return ( +
+
+ {/* Spotlight Carousel */} + + + {/* Genre Bar */} +
+ +
+ + {/* Main Content + Sidebar Layout */} +
+ {/* Main Content - 2/3 width on large screens */} +
+ {/* Latest Episodes Grid */} + + + {/* Anime Tabs Section */} + +
+ + {/* Sidebar - 1/4 width on large screens */} +
+ {/* Trending List */} + + + {/* Calendar Widget */} + + + {/* Top Lists */} + +
+
+
+
+ ); + } catch (error) { + console.error('Error in HomePage:', error); + return
Error loading page
; + } +} + +export default HomePage; \ No newline at end of file diff --git a/src/app/latest-completed/layout.js b/src/app/latest-completed/layout.js new file mode 100644 index 0000000..37cb70c --- /dev/null +++ b/src/app/latest-completed/layout.js @@ -0,0 +1,5 @@ +import SharedLayout from '@/components/SharedLayout'; + +export default function LatestCompletedLayout({ children }) { + return {children}; +} \ No newline at end of file diff --git a/src/app/latest-completed/page.js b/src/app/latest-completed/page.js new file mode 100644 index 0000000..adb06a6 --- /dev/null +++ b/src/app/latest-completed/page.js @@ -0,0 +1,305 @@ +'use client'; + +import { useState, useEffect, useRef } from 'react'; +import AnimeCard from '@/components/AnimeCard'; +import AnimeFilters from '@/components/AnimeFilters'; +import { fetchLatestCompleted } from '@/lib/api'; + +export default function LatestCompletedPage() { + const [animeList, setAnimeList] = useState([]); + const [filteredList, setFilteredList] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [currentPage, setCurrentPage] = useState(1); + const [hasNextPage, setHasNextPage] = useState(false); + const [selectedGenre, setSelectedGenre] = useState(null); + const [yearFilter, setYearFilter] = useState('all'); + const [sortOrder, setSortOrder] = useState('default'); + const [searchQuery, setSearchQuery] = useState(''); + const [selectedSeasons, setSelectedSeasons] = useState([]); + const [selectedTypes, setSelectedTypes] = useState([]); + const [selectedStatus, setSelectedStatus] = useState([]); + const [selectedLanguages, setSelectedLanguages] = useState([]); + const [error, setError] = useState(null); + + // Current year for filtering + const currentYear = new Date().getFullYear(); + + // Add ref to track if this is the first render + const initialRender = useRef(true); + + useEffect(() => { + const fetchData = async () => { + setIsLoading(true); + try { + const data = await fetchLatestCompleted(currentPage); + + if (currentPage === 1) { + setAnimeList(data.results || []); + } else { + setAnimeList(prev => [...prev, ...(data.results || [])]); + } + + setHasNextPage(data.hasNextPage || false); + } catch (error) { + console.error('Error fetching latest completed anime:', error); + setError('Failed to load anime. Please try again later.'); + } finally { + setIsLoading(false); + } + }; + + fetchData(); + }, [currentPage]); + + // Apply filters and sorting whenever the anime list or filter settings change + useEffect(() => { + // Skip the initial render effect to avoid duplicate filtering + if (initialRender.current) { + initialRender.current = false; + return; + } + + if (!animeList.length) { + setFilteredList([]); + return; + } + + let result = [...animeList]; + + // Search filter + if (searchQuery && searchQuery.trim() !== '') { + const query = searchQuery.toLowerCase().trim(); + result = result.filter(anime => { + const title = (anime.title || '').toLowerCase(); + const otherNames = (anime.otherNames || '').toLowerCase(); + return title.includes(query) || otherNames.includes(query); + }); + } + + // Filter by genre if selected + if (selectedGenre) { + result = result.filter(anime => { + if (anime.genres && Array.isArray(anime.genres)) { + return anime.genres.some(g => + g.toLowerCase() === selectedGenre.toLowerCase() || + (g.name && g.name.toLowerCase() === selectedGenre.toLowerCase()) + ); + } else if (anime.genre) { + return anime.genre.toLowerCase().includes(selectedGenre.toLowerCase()); + } + return false; + }); + } + + // Filter by season + if (selectedSeasons.length > 0) { + result = result.filter(anime => { + const season = getAnimeSeason(anime); + return selectedSeasons.includes(season); + }); + } + + // Filter by year + if (yearFilter !== 'all') { + result = result.filter(anime => { + const animeYear = parseInt(anime.year) || 0; + if (yearFilter === 'older') { + return animeYear < 2000; + } else { + return animeYear.toString() === yearFilter; + } + }); + } + + // Filter by type + if (selectedTypes.length > 0) { + result = result.filter(anime => + selectedTypes.includes(anime.type) + ); + } + + // Filter by status + if (selectedStatus.length > 0) { + result = result.filter(anime => { + const status = anime.status || getDefaultStatus(anime); + return selectedStatus.includes(status); + }); + } + + // Filter by language + if (selectedLanguages.length > 0) { + result = result.filter(anime => { + const language = anime.language || getDefaultLanguage(anime); + return selectedLanguages.includes(language); + }); + } + + // Apply sorting + switch (sortOrder) { + case 'title-asc': + result.sort((a, b) => (a.title || '').localeCompare(b.title || '')); + break; + case 'title-desc': + result.sort((a, b) => (b.title || '').localeCompare(a.title || '')); + break; + case 'year-desc': + result.sort((a, b) => (parseInt(b.year) || 0) - (parseInt(a.year) || 0)); + break; + case 'year-asc': + result.sort((a, b) => (parseInt(a.year) || 0) - (parseInt(b.year) || 0)); + break; + default: + // Default order from API + break; + } + + setFilteredList(result); + }, [animeList, selectedGenre, yearFilter, sortOrder, searchQuery, selectedSeasons, selectedTypes, selectedStatus, selectedLanguages]); + + const handleLoadMore = () => { + setCurrentPage(prev => prev + 1); + }; + + const handleGenreChange = (genre) => { + setSelectedGenre(genre); + }; + + const handleYearChange = (year) => { + setYearFilter(year); + }; + + const handleSortChange = (order) => { + setSortOrder(order); + }; + + const handleSearchChange = (value) => { + setSearchQuery(value); + }; + + const handleSeasonChange = (seasons) => { + setSelectedSeasons(seasons); + }; + + const handleTypeChange = (types) => { + setSelectedTypes(types); + }; + + const handleStatusChange = (status) => { + setSelectedStatus(status); + }; + + const handleLanguageChange = (languages) => { + setSelectedLanguages(languages); + }; + + // Helper function to determine anime season based on available data + const getAnimeSeason = (anime) => { + if (anime.season) return anime.season; + + const seasons = ['Winter', 'Spring', 'Summer', 'Fall']; + // Use hash of ID to assign consistent season for demo purposes + const hash = anime.id.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0); + return seasons[hash % 4]; + }; + + // Helper function to determine anime status + const getDefaultStatus = (anime) => { + if (anime.status) return anime.status; + + // Default logic - you may need to customize this based on your actual data + const currentYear = new Date().getFullYear(); + if (anime.year > currentYear) return 'Upcoming'; + if (anime.totalEpisodes && anime.episodes && anime.episodes >= anime.totalEpisodes) return 'Completed'; + return 'Ongoing'; + }; + + // Helper function to determine anime language + const getDefaultLanguage = (anime) => { + if (anime.language) return anime.language; + + // Default to "Subbed" for all anime unless specifically marked + return anime.isDub ? 'Dubbed' : 'Subbed'; + }; + + return ( +
+

Latest Completed Anime

+ + {/* Filters */} +
+ +
+ + {isLoading && animeList.length === 0 ? ( +
+ {[...Array(14)].map((_, index) => ( +
+
+
+
+
+
+
+ ))} +
+ ) : (filteredList.length > 0 || animeList.length > 0) ? ( + <> +
+ {(filteredList.length > 0 ? filteredList : animeList).map((anime) => ( + + ))} +
+ + {hasNextPage && ( +
+ +
+ )} + + ) : ( +
+

No anime found

+

+ We couldn't find any anime matching your criteria. Please try different filters. +

+
+ )} +
+ ); +} \ No newline at end of file diff --git a/src/app/layout.js b/src/app/layout.js new file mode 100644 index 0000000..06fce65 --- /dev/null +++ b/src/app/layout.js @@ -0,0 +1,30 @@ +import { Geist, Geist_Mono } from "next/font/google"; +import "./globals.css"; + +const geistSans = Geist({ + variable: "--font-geist-sans", + subsets: ["latin"], +}); + +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], +}); + +export const metadata = { + title: "JustAnime - Watch Anime Online", + description: "Watch the latest anime episodes for free. Stream all your favorite anime shows in HD quality.", + keywords: "anime, streaming, watch anime, free anime, anime online, just , justanime", +}; + +export default function RootLayout({ children }) { + return ( + + +
+ {children} +
+ + + ); +} diff --git a/src/app/most-popular/layout.js b/src/app/most-popular/layout.js new file mode 100644 index 0000000..7daa4ab --- /dev/null +++ b/src/app/most-popular/layout.js @@ -0,0 +1,5 @@ +import SharedLayout from '@/components/SharedLayout'; + +export default function MostPopularLayout({ children }) { + return {children}; +} \ No newline at end of file diff --git a/src/app/most-popular/page.js b/src/app/most-popular/page.js new file mode 100644 index 0000000..283ba3f --- /dev/null +++ b/src/app/most-popular/page.js @@ -0,0 +1,305 @@ +'use client'; + +import { useState, useEffect, useRef } from 'react'; +import AnimeCard from '@/components/AnimeCard'; +import AnimeFilters from '@/components/AnimeFilters'; +import { fetchMostPopular } from '@/lib/api'; + +export default function MostPopularPage() { + const [animeList, setAnimeList] = useState([]); + const [filteredList, setFilteredList] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [currentPage, setCurrentPage] = useState(1); + const [hasNextPage, setHasNextPage] = useState(false); + const [selectedGenre, setSelectedGenre] = useState(null); + const [yearFilter, setYearFilter] = useState('all'); + const [sortOrder, setSortOrder] = useState('default'); + const [searchQuery, setSearchQuery] = useState(''); + const [selectedSeasons, setSelectedSeasons] = useState([]); + const [selectedTypes, setSelectedTypes] = useState([]); + const [selectedStatus, setSelectedStatus] = useState([]); + const [selectedLanguages, setSelectedLanguages] = useState([]); + const [error, setError] = useState(null); + + // Current year for filtering + const currentYear = new Date().getFullYear(); + + // Add ref to track if this is the first render + const initialRender = useRef(true); + + useEffect(() => { + const fetchData = async () => { + setIsLoading(true); + try { + const data = await fetchMostPopular(currentPage); + + if (currentPage === 1) { + setAnimeList(data.results || []); + } else { + setAnimeList(prev => [...prev, ...(data.results || [])]); + } + + setHasNextPage(data.hasNextPage || false); + } catch (error) { + console.error('Error fetching most popular anime:', error); + setError('Failed to load anime. Please try again later.'); + } finally { + setIsLoading(false); + } + }; + + fetchData(); + }, [currentPage]); + + // Apply filters and sorting whenever the anime list or filter settings change + useEffect(() => { + // Skip the initial render effect to avoid duplicate filtering + if (initialRender.current) { + initialRender.current = false; + return; + } + + if (!animeList.length) { + setFilteredList([]); + return; + } + + let result = [...animeList]; + + // Search filter + if (searchQuery && searchQuery.trim() !== '') { + const query = searchQuery.toLowerCase().trim(); + result = result.filter(anime => { + const title = (anime.title || '').toLowerCase(); + const otherNames = (anime.otherNames || '').toLowerCase(); + return title.includes(query) || otherNames.includes(query); + }); + } + + // Filter by genre if selected + if (selectedGenre) { + result = result.filter(anime => { + if (anime.genres && Array.isArray(anime.genres)) { + return anime.genres.some(g => + g.toLowerCase() === selectedGenre.toLowerCase() || + (g.name && g.name.toLowerCase() === selectedGenre.toLowerCase()) + ); + } else if (anime.genre) { + return anime.genre.toLowerCase().includes(selectedGenre.toLowerCase()); + } + return false; + }); + } + + // Filter by season + if (selectedSeasons.length > 0) { + result = result.filter(anime => { + const season = getAnimeSeason(anime); + return selectedSeasons.includes(season); + }); + } + + // Filter by year + if (yearFilter !== 'all') { + result = result.filter(anime => { + const animeYear = parseInt(anime.year) || 0; + if (yearFilter === 'older') { + return animeYear < 2000; + } else { + return animeYear.toString() === yearFilter; + } + }); + } + + // Filter by type + if (selectedTypes.length > 0) { + result = result.filter(anime => + selectedTypes.includes(anime.type) + ); + } + + // Filter by status + if (selectedStatus.length > 0) { + result = result.filter(anime => { + const status = anime.status || getDefaultStatus(anime); + return selectedStatus.includes(status); + }); + } + + // Filter by language + if (selectedLanguages.length > 0) { + result = result.filter(anime => { + const language = anime.language || getDefaultLanguage(anime); + return selectedLanguages.includes(language); + }); + } + + // Apply sorting + switch (sortOrder) { + case 'title-asc': + result.sort((a, b) => (a.title || '').localeCompare(b.title || '')); + break; + case 'title-desc': + result.sort((a, b) => (b.title || '').localeCompare(a.title || '')); + break; + case 'year-desc': + result.sort((a, b) => (parseInt(b.year) || 0) - (parseInt(a.year) || 0)); + break; + case 'year-asc': + result.sort((a, b) => (parseInt(a.year) || 0) - (parseInt(b.year) || 0)); + break; + default: + // Default order from API + break; + } + + setFilteredList(result); + }, [animeList, selectedGenre, yearFilter, sortOrder, searchQuery, selectedSeasons, selectedTypes, selectedStatus, selectedLanguages]); + + const handleLoadMore = () => { + setCurrentPage(prev => prev + 1); + }; + + const handleGenreChange = (genre) => { + setSelectedGenre(genre); + }; + + const handleYearChange = (year) => { + setYearFilter(year); + }; + + const handleSortChange = (order) => { + setSortOrder(order); + }; + + const handleSearchChange = (value) => { + setSearchQuery(value); + }; + + const handleSeasonChange = (seasons) => { + setSelectedSeasons(seasons); + }; + + const handleTypeChange = (types) => { + setSelectedTypes(types); + }; + + const handleStatusChange = (status) => { + setSelectedStatus(status); + }; + + const handleLanguageChange = (languages) => { + setSelectedLanguages(languages); + }; + + // Helper function to determine anime season based on available data + const getAnimeSeason = (anime) => { + if (anime.season) return anime.season; + + const seasons = ['Winter', 'Spring', 'Summer', 'Fall']; + // Use hash of ID to assign consistent season for demo purposes + const hash = anime.id.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0); + return seasons[hash % 4]; + }; + + // Helper function to determine anime status + const getDefaultStatus = (anime) => { + if (anime.status) return anime.status; + + // Default logic - you may need to customize this based on your actual data + const currentYear = new Date().getFullYear(); + if (anime.year > currentYear) return 'Upcoming'; + if (anime.totalEpisodes && anime.episodes && anime.episodes >= anime.totalEpisodes) return 'Completed'; + return 'Ongoing'; + }; + + // Helper function to determine anime language + const getDefaultLanguage = (anime) => { + if (anime.language) return anime.language; + + // Default to "Subbed" for all anime unless specifically marked + return anime.isDub ? 'Dubbed' : 'Subbed'; + }; + + return ( +
+

Most Popular Anime

+ + {/* Filters */} +
+ +
+ + {isLoading && animeList.length === 0 ? ( +
+ {[...Array(14)].map((_, index) => ( +
+
+
+
+
+
+
+ ))} +
+ ) : (filteredList.length > 0 || animeList.length > 0) ? ( + <> +
+ {(filteredList.length > 0 ? filteredList : animeList).map((anime) => ( + + ))} +
+ + {hasNextPage && ( +
+ +
+ )} + + ) : ( +
+

No anime found

+

+ We couldn't find any anime matching your criteria. Please try different filters. +

+
+ )} +
+ ); +} \ No newline at end of file diff --git a/src/app/page.jsx b/src/app/page.jsx new file mode 100644 index 0000000..91ecefd --- /dev/null +++ b/src/app/page.jsx @@ -0,0 +1,308 @@ +'use client'; + +import Link from 'next/link'; +import { useState, useEffect, useRef } from 'react'; +import { useRouter } from 'next/navigation'; +import { fetchSearchSuggestions } from '@/lib/api'; +import Image from 'next/image'; + +export default function LandingPage() { + const [searchQuery, setSearchQuery] = useState(''); + const [searchSuggestions, setSearchSuggestions] = useState([]); + const [showSuggestions, setShowSuggestions] = useState(false); + const router = useRouter(); + const suggestionRef = useRef(null); + const searchInputRef = useRef(null); + + // Create separate delays for staggered loading of FAQ items + const faqDelays = ['0.6s', '0.7s', '0.8s']; + + // For FAQ dropdowns + const [openFAQ, setOpenFAQ] = useState(null); + + const toggleFAQ = (index) => { + setOpenFAQ(openFAQ === index ? null : index); + }; + + // Fetch search suggestions when search query changes + useEffect(() => { + const fetchSuggestions = async () => { + if (searchQuery.trim().length > 2) { + try { + const suggestions = await fetchSearchSuggestions(searchQuery); + setSearchSuggestions(suggestions || []); + setShowSuggestions(true); + } catch (error) { + console.error('Error fetching search suggestions:', error); + setSearchSuggestions([]); + } + } else { + setSearchSuggestions([]); + setShowSuggestions(false); + } + }; + + const debounceTimer = setTimeout(() => { + fetchSuggestions(); + }, 300); + + return () => clearTimeout(debounceTimer); + }, [searchQuery]); + + // Close suggestions when clicking outside + useEffect(() => { + const handleClickOutside = (event) => { + if ( + suggestionRef.current && + !suggestionRef.current.contains(event.target) && + !searchInputRef.current?.contains(event.target) + ) { + setShowSuggestions(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + + const handleSearch = (e) => { + e.preventDefault(); + if (searchQuery.trim()) { + router.push(`/search/${searchQuery}`); + setSearchQuery(''); + setShowSuggestions(false); + } + }; + + const handleSuggestionClick = (title) => { + router.push(`/search/${title}`); + setSearchQuery(''); + setShowSuggestions(false); + }; + + return ( +
+ {/* Background Image with Fade Effect */} +
+ Dark anime character background + {/* Ultra-smooth gradient for fade from bottom */} +
+
+
+ + {/* Unified Content Section */} +
+ {/* Hero Content */} +
+ {/* Logo */} +
+ JustAnime Logo +
+ + {/* Search Bar */} +
+
+
+ setSearchQuery(e.target.value)} + onFocus={() => searchSuggestions.length > 0 && setShowSuggestions(true)} + /> +
+ + + + +
+
+
+ + {/* Search Suggestions Dropdown */} + {showSuggestions && searchSuggestions.length > 0 && ( +
+ {searchSuggestions.map((suggestion, index) => ( +
handleSuggestionClick(suggestion)} + > + {suggestion} +
+ ))} +
+ )} +
+ + {/* Enter Homepage Button */} + + Enter Homepage → + +
+ + {/* FAQ Content - with fade-in animation matching the hero section */} +
+

Frequently Asked Questions

+ +
+ {/* FAQ Item 1 */} +
+ +
+
+

Yes. We started this site to improve UX and are committed to keeping our users safe. We encourage all our users to notify us if anything looks suspicious.

+
+
+
+ + {/* FAQ Item 2 */} +
+ +
+
+
    +
  • Content library: Our extensive database ensures you can find almost everything here.
  • +
  • Streaming experience: We have top of the line streaming servers. You can simply choose one that is fast for you.
  • +
  • Quality/Resolution: All our video files are encoded in highest possible resolution. We also have quality setting function that allows every user to enjoy streaming regardless of their internet speed.
  • +
  • Updates: Our content is updated hourly, so you will get update as fast as possible.
  • +
  • User interface: We focus on the simple and easy to use, so you will feel the life is easier here. We also have almost every feature that other anime streaming sites have, but not the opposite.
  • +
  • Device compatibility: JustAnime works fine on both desktop and mobile devices, even with old browsers, so you can enjoy your anime anywhere you want.
  • +
+
+
+
+ + {/* FAQ Item 3 */} +
+ +
+
+

+ If you want a good and safe place to watch anime online for free, give JustAnime a try, and if you like what we provide, please help us by sharing JustAnime to your friends and do not forget to bookmark our site. +

+
+
+
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/src/app/recent/layout.js b/src/app/recent/layout.js new file mode 100644 index 0000000..14e5824 --- /dev/null +++ b/src/app/recent/layout.js @@ -0,0 +1,5 @@ +import SharedLayout from '@/components/SharedLayout'; + +export default function RecentEpisodesLayout({ children }) { + return {children}; +} \ No newline at end of file diff --git a/src/app/recent/page.js b/src/app/recent/page.js new file mode 100644 index 0000000..7e6dec4 --- /dev/null +++ b/src/app/recent/page.js @@ -0,0 +1,305 @@ +'use client'; + +import { useState, useEffect, useRef } from 'react'; +import AnimeCard from '@/components/AnimeCard'; +import AnimeFilters from '@/components/AnimeFilters'; +import { fetchRecentEpisodes } from '@/lib/api'; + +export default function RecentEpisodesPage() { + const [animeList, setAnimeList] = useState([]); + const [filteredList, setFilteredList] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [currentPage, setCurrentPage] = useState(1); + const [hasNextPage, setHasNextPage] = useState(false); + const [selectedGenre, setSelectedGenre] = useState(null); + const [yearFilter, setYearFilter] = useState('all'); + const [sortOrder, setSortOrder] = useState('default'); + const [searchQuery, setSearchQuery] = useState(''); + const [selectedSeasons, setSelectedSeasons] = useState([]); + const [selectedTypes, setSelectedTypes] = useState([]); + const [selectedStatus, setSelectedStatus] = useState([]); + const [selectedLanguages, setSelectedLanguages] = useState([]); + const [error, setError] = useState(null); + + // Current year for filtering + const currentYear = new Date().getFullYear(); + + // Add ref to track if this is the first render + const initialRender = useRef(true); + + useEffect(() => { + const fetchData = async () => { + setIsLoading(true); + try { + const data = await fetchRecentEpisodes(currentPage); + + if (currentPage === 1) { + setAnimeList(data.results || []); + } else { + setAnimeList(prev => [...prev, ...(data.results || [])]); + } + + setHasNextPage(data.hasNextPage || false); + } catch (error) { + console.error('Error fetching recent episodes:', error); + setError('Failed to load anime. Please try again later.'); + } finally { + setIsLoading(false); + } + }; + + fetchData(); + }, [currentPage]); + + // Apply filters and sorting whenever the anime list or filter settings change + useEffect(() => { + // Skip the initial render effect to avoid duplicate filtering + if (initialRender.current) { + initialRender.current = false; + return; + } + + if (!animeList.length) { + setFilteredList([]); + return; + } + + let result = [...animeList]; + + // Search filter + if (searchQuery && searchQuery.trim() !== '') { + const query = searchQuery.toLowerCase().trim(); + result = result.filter(anime => { + const title = (anime.title || '').toLowerCase(); + const otherNames = (anime.otherNames || '').toLowerCase(); + return title.includes(query) || otherNames.includes(query); + }); + } + + // Filter by genre if selected + if (selectedGenre) { + result = result.filter(anime => { + if (anime.genres && Array.isArray(anime.genres)) { + return anime.genres.some(g => + g.toLowerCase() === selectedGenre.toLowerCase() || + (g.name && g.name.toLowerCase() === selectedGenre.toLowerCase()) + ); + } else if (anime.genre) { + return anime.genre.toLowerCase().includes(selectedGenre.toLowerCase()); + } + return false; + }); + } + + // Filter by season + if (selectedSeasons.length > 0) { + result = result.filter(anime => { + const season = getAnimeSeason(anime); + return selectedSeasons.includes(season); + }); + } + + // Filter by year + if (yearFilter !== 'all') { + result = result.filter(anime => { + const animeYear = parseInt(anime.year) || 0; + if (yearFilter === 'older') { + return animeYear < 2000; + } else { + return animeYear.toString() === yearFilter; + } + }); + } + + // Filter by type + if (selectedTypes.length > 0) { + result = result.filter(anime => + selectedTypes.includes(anime.type) + ); + } + + // Filter by status + if (selectedStatus.length > 0) { + result = result.filter(anime => { + const status = anime.status || getDefaultStatus(anime); + return selectedStatus.includes(status); + }); + } + + // Filter by language + if (selectedLanguages.length > 0) { + result = result.filter(anime => { + const language = anime.language || getDefaultLanguage(anime); + return selectedLanguages.includes(language); + }); + } + + // Apply sorting + switch (sortOrder) { + case 'title-asc': + result.sort((a, b) => (a.title || '').localeCompare(b.title || '')); + break; + case 'title-desc': + result.sort((a, b) => (b.title || '').localeCompare(a.title || '')); + break; + case 'year-desc': + result.sort((a, b) => (parseInt(b.year) || 0) - (parseInt(a.year) || 0)); + break; + case 'year-asc': + result.sort((a, b) => (parseInt(a.year) || 0) - (parseInt(b.year) || 0)); + break; + default: + // Default order from API + break; + } + + setFilteredList(result); + }, [animeList, selectedGenre, yearFilter, sortOrder, searchQuery, selectedSeasons, selectedTypes, selectedStatus, selectedLanguages]); + + const handleLoadMore = () => { + setCurrentPage(prev => prev + 1); + }; + + const handleGenreChange = (genre) => { + setSelectedGenre(genre); + }; + + const handleYearChange = (year) => { + setYearFilter(year); + }; + + const handleSortChange = (order) => { + setSortOrder(order); + }; + + const handleSearchChange = (value) => { + setSearchQuery(value); + }; + + const handleSeasonChange = (seasons) => { + setSelectedSeasons(seasons); + }; + + const handleTypeChange = (types) => { + setSelectedTypes(types); + }; + + const handleStatusChange = (status) => { + setSelectedStatus(status); + }; + + const handleLanguageChange = (languages) => { + setSelectedLanguages(languages); + }; + + // Helper function to determine anime season based on available data + const getAnimeSeason = (anime) => { + if (anime.season) return anime.season; + + const seasons = ['Winter', 'Spring', 'Summer', 'Fall']; + // Use hash of ID to assign consistent season for demo purposes + const hash = anime.id.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0); + return seasons[hash % 4]; + }; + + // Helper function to determine anime status + const getDefaultStatus = (anime) => { + if (anime.status) return anime.status; + + // Default logic - you may need to customize this based on your actual data + const currentYear = new Date().getFullYear(); + if (anime.year > currentYear) return 'Upcoming'; + if (anime.totalEpisodes && anime.episodes && anime.episodes >= anime.totalEpisodes) return 'Completed'; + return 'Ongoing'; + }; + + // Helper function to determine anime language + const getDefaultLanguage = (anime) => { + if (anime.language) return anime.language; + + // Default to "Subbed" for all anime unless specifically marked + return anime.isDub ? 'Dubbed' : 'Subbed'; + }; + + return ( +
+

Recent Episodes

+ + {/* Filters */} +
+ +
+ + {isLoading && animeList.length === 0 ? ( +
+ {[...Array(14)].map((_, index) => ( +
+
+
+
+
+
+
+ ))} +
+ ) : (filteredList.length > 0 || animeList.length > 0) ? ( + <> +
+ {(filteredList.length > 0 ? filteredList : animeList).map((anime) => ( + + ))} +
+ + {hasNextPage && ( +
+ +
+ )} + + ) : ( +
+

No anime found

+

+ We couldn't find any anime matching your criteria. Please try different filters. +

+
+ )} +
+ ); +} \ No newline at end of file diff --git a/src/app/search/[query]/page.js b/src/app/search/[query]/page.js new file mode 100644 index 0000000..b1e7649 --- /dev/null +++ b/src/app/search/[query]/page.js @@ -0,0 +1,311 @@ +'use client'; + +import { useState, useEffect, useCallback } from 'react'; +import { useParams, useRouter } from 'next/navigation'; +import AnimeCard from '@/components/AnimeCard'; +import AnimeFilters from '@/components/AnimeFilters'; +import { searchAnime, fetchMostPopular } from '@/lib/api'; + +export default function SearchPage() { + const router = useRouter(); + const { query } = useParams(); + const decodedQuery = query ? decodeURIComponent(query) : ''; + + const [searchResults, setSearchResults] = useState([]); + const [filteredResults, setFilteredResults] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [currentPage, setCurrentPage] = useState(1); + const [hasNextPage, setHasNextPage] = useState(false); + const [isEmptySearch, setIsEmptySearch] = useState(false); + + // Filter states + const [selectedGenre, setSelectedGenre] = useState(null); + const [yearFilter, setYearFilter] = useState('all'); + const [sortOrder, setSortOrder] = useState('default'); + const [selectedSeasons, setSelectedSeasons] = useState([]); + const [selectedTypes, setSelectedTypes] = useState([]); + const [selectedStatus, setSelectedStatus] = useState([]); + const [selectedLanguages, setSelectedLanguages] = useState([]); + const [error, setError] = useState(null); + + // Create filters object for API request + const getFiltersForApi = useCallback(() => { + const filters = {}; + + if (selectedGenre) filters.genre = selectedGenre; + if (yearFilter !== 'all') filters.year = yearFilter; + if (sortOrder !== 'default') filters.sort = sortOrder; + + // Only add these filters if API supports them + // Currently, these may need to be handled client-side + // if (selectedSeasons.length > 0) filters.seasons = selectedSeasons; + // if (selectedTypes.length > 0) filters.types = selectedTypes; + // if (selectedStatus.length > 0) filters.status = selectedStatus; + // if (selectedLanguages.length > 0) filters.languages = selectedLanguages; + + return filters; + }, [selectedGenre, yearFilter, sortOrder]); + + // Apply client-side filters + const applyClientSideFilters = useCallback((animeList) => { + if (!animeList.length) return []; + + let result = [...animeList]; + + // Apply season filter if selected + if (selectedSeasons.length > 0) { + result = result.filter(anime => { + if (!anime.season) return false; + + const animeSeason = typeof anime.season === 'string' + ? anime.season + : anime.season?.name || ''; + + return selectedSeasons.some(season => + animeSeason.toLowerCase().includes(season.toLowerCase()) + ); + }); + } + + // Apply type filter if selected + if (selectedTypes.length > 0) { + result = result.filter(anime => { + if (!anime.type) return false; + + return selectedTypes.some(type => + anime.type.toLowerCase() === type.toLowerCase() + ); + }); + } + + // Apply status filter if selected + if (selectedStatus.length > 0) { + result = result.filter(anime => { + if (!anime.status) return false; + + return selectedStatus.some(status => + anime.status.toLowerCase().includes(status.toLowerCase()) + ); + }); + } + + // Apply language filter if selected + if (selectedLanguages.length > 0) { + result = result.filter(anime => { + // If no language info, assume subbed (most common) + const animeLanguage = anime.language || 'Subbed'; + + return selectedLanguages.some(language => + animeLanguage.toLowerCase().includes(language.toLowerCase()) + ); + }); + } + + // Apply client-side sorting (when API sort is not supported) + if (sortOrder !== 'default') { + switch (sortOrder) { + case 'title-asc': + result.sort((a, b) => (a.title || '').localeCompare(b.title || '')); + break; + case 'title-desc': + result.sort((a, b) => (b.title || '').localeCompare(a.title || '')); + break; + case 'year-desc': + result.sort((a, b) => (parseInt(b.year) || 0) - (parseInt(a.year) || 0)); + break; + case 'year-asc': + result.sort((a, b) => (parseInt(a.year) || 0) - (parseInt(b.year) || 0)); + break; + // Default order from API is used when sortOrder is 'default' + } + } + + return result; + }, [selectedSeasons, selectedTypes, selectedStatus, selectedLanguages, sortOrder]); + + // Fetch popular anime when search is empty + const fetchPopularAnime = useCallback(async (page = 1) => { + setIsLoading(true); + setError(null); + setIsEmptySearch(true); + + try { + const data = await fetchMostPopular(page); + + if (page === 1) { + setSearchResults(data.results || []); + } else { + setSearchResults(prev => [...prev, ...(data.results || [])]); + } + + setHasNextPage(data.hasNextPage || false); + } catch (error) { + console.error('Error fetching popular anime:', error); + setError('Failed to fetch popular anime. Please try again later.'); + } finally { + setIsLoading(false); + } + }, []); + + useEffect(() => { + // If the query param is empty, redirect to search page with empty query + if (!decodedQuery.trim()) { + // Fetch popular anime instead + fetchPopularAnime(currentPage); + return; + } + + setIsEmptySearch(false); + const fetchSearchResults = async () => { + setIsLoading(true); + setError(null); + + try { + const filters = getFiltersForApi(); + const data = await searchAnime(decodedQuery, currentPage, filters); + + if (currentPage === 1) { + setSearchResults(data.results || []); + } else { + setSearchResults(prev => [...prev, ...(data.results || [])]); + } + + setHasNextPage(data.hasNextPage || false); + } catch (error) { + console.error('Error fetching search results:', error); + setError('Failed to search anime. Please try again later.'); + } finally { + setIsLoading(false); + } + }; + + fetchSearchResults(); + }, [decodedQuery, currentPage, getFiltersForApi, fetchPopularAnime]); + + // Apply client-side filters whenever search results or filter settings change + useEffect(() => { + const filteredResults = applyClientSideFilters(searchResults); + setFilteredResults(filteredResults); + }, [searchResults, applyClientSideFilters]); + + const handleLoadMore = () => { + setCurrentPage(prev => prev + 1); + }; + + // Filter handlers + const handleGenreChange = (genre) => { + setSelectedGenre(genre); + if (currentPage !== 1) setCurrentPage(1); + }; + + const handleYearChange = (year) => { + setYearFilter(year); + if (currentPage !== 1) setCurrentPage(1); + }; + + const handleSortChange = (order) => { + setSortOrder(order); + if (currentPage !== 1) setCurrentPage(1); + }; + + const handleSeasonChange = (seasons) => { + setSelectedSeasons(seasons); + if (currentPage !== 1) setCurrentPage(1); + }; + + const handleTypeChange = (types) => { + setSelectedTypes(types); + if (currentPage !== 1) setCurrentPage(1); + }; + + const handleStatusChange = (status) => { + setSelectedStatus(status); + if (currentPage !== 1) setCurrentPage(1); + }; + + const handleLanguageChange = (languages) => { + setSelectedLanguages(languages); + if (currentPage !== 1) setCurrentPage(1); + }; + + return ( +
+
+

+ {decodedQuery.trim() ? `Search Results for "${decodedQuery}"` : 'Popular Anime'} +

+ + {/* Filters */} + {}} + selectedSeasons={selectedSeasons} + onSeasonChange={handleSeasonChange} + selectedTypes={selectedTypes} + onTypeChange={handleTypeChange} + selectedStatus={selectedStatus} + onStatusChange={handleStatusChange} + selectedLanguages={selectedLanguages} + onLanguageChange={handleLanguageChange} + /> +
+ + {isLoading && currentPage === 1 ? ( +
+
+
+ ) : error ? ( +
+

Error

+

{error}

+
+ ) : filteredResults.length > 0 ? ( + <> +
+ {filteredResults.map((anime) => ( + + ))} +
+ + {hasNextPage && ( +
+ +
+ )} + + ) : ( +
+

No results found

+

+ We couldn't find any anime matching your search criteria. Please try different filters or a different search term. +

+
+ )} +
+ ); +} \ No newline at end of file diff --git a/src/app/search/layout.js b/src/app/search/layout.js new file mode 100644 index 0000000..f44e019 --- /dev/null +++ b/src/app/search/layout.js @@ -0,0 +1,5 @@ +import SharedLayout from '@/components/SharedLayout'; + +export default function SearchLayout({ children }) { + return {children}; +} \ No newline at end of file diff --git a/src/app/search/page.js b/src/app/search/page.js new file mode 100644 index 0000000..8c85921 --- /dev/null +++ b/src/app/search/page.js @@ -0,0 +1,433 @@ +'use client'; + +import { useState, useEffect, useCallback, Suspense } from 'react'; +import { useSearchParams } from 'next/navigation'; +import { searchAnime, fetchMostPopular } from '@/lib/api'; +import AnimeCard from '@/components/AnimeCard'; +import AnimeFilters from '@/components/AnimeFilters'; + +function SearchResults() { + const searchParams = useSearchParams(); + const queryTerm = searchParams.get('q') || ''; + const genreParam = searchParams.get('genre') || null; + + const [animeList, setAnimeList] = useState([]); + const [filteredList, setFilteredList] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [currentPage, setCurrentPage] = useState(1); + const [hasNextPage, setHasNextPage] = useState(false); + const [selectedGenre, setSelectedGenre] = useState(genreParam); + const [yearFilter, setYearFilter] = useState('all'); + const [sortOrder, setSortOrder] = useState('default'); + const [selectedSeasons, setSelectedSeasons] = useState([]); + const [selectedTypes, setSelectedTypes] = useState([]); + const [selectedStatus, setSelectedStatus] = useState([]); + const [selectedLanguages, setSelectedLanguages] = useState([]); + const [error, setError] = useState(null); + const [isEmptySearch, setIsEmptySearch] = useState(false); + + // Current year for filtering + const currentYear = new Date().getFullYear(); + + // Process and augment anime data to ensure all items have year information + const processAnimeData = useCallback((animeData) => { + if (!animeData || !animeData.results) return animeData; + + // Create a copy of the data to avoid mutating the original + const processedData = { + ...animeData, + results: animeData.results.map(anime => { + const processed = { ...anime }; + + // Extract or estimate year from various properties + // Fallback to randomized year range between 2000-current year if no year data available + if (!processed.year) { + if (processed.releaseDate && !isNaN(parseInt(processed.releaseDate))) { + processed.year = parseInt(processed.releaseDate); + } else if (processed.date && !isNaN(parseInt(processed.date))) { + processed.year = parseInt(processed.date); + } else { + // Assign a semi-random year based on anime ID to ensure consistency + const hash = processed.id.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0); + processed.year = 2000 + (hash % (currentYear - 2000 + 1)); + } + } + + return processed; + }) + }; + + return processedData; + }, [currentYear]); + + // Create filters object for API request + const getFiltersForApi = useCallback(() => { + const filters = {}; + + if (selectedGenre) filters.genre = selectedGenre; + if (yearFilter !== 'all') filters.year = yearFilter; + if (sortOrder !== 'default') filters.sort = sortOrder; + + // Only add these filters if API supports them + // Currently, these may need to be handled client-side + // if (selectedSeasons.length > 0) filters.seasons = selectedSeasons; + // if (selectedTypes.length > 0) filters.types = selectedTypes; + // if (selectedStatus.length > 0) filters.status = selectedStatus; + // if (selectedLanguages.length > 0) filters.languages = selectedLanguages; + + return filters; + }, [selectedGenre, yearFilter, sortOrder]); + + // Apply client-side filters for things not supported by API + const applyClientSideFilters = useCallback((animeList) => { + if (!animeList.length) return []; + + let result = [...animeList]; + + // Apply season filter if selected + if (selectedSeasons.length > 0) { + result = result.filter(anime => { + if (!anime.season) return false; + + const animeSeason = typeof anime.season === 'string' + ? anime.season + : anime.season?.name || ''; + + return selectedSeasons.some(season => + animeSeason.toLowerCase().includes(season.toLowerCase()) + ); + }); + } + + // Apply type filter if selected + if (selectedTypes.length > 0) { + result = result.filter(anime => { + if (!anime.type) return false; + + return selectedTypes.some(type => + anime.type.toLowerCase() === type.toLowerCase() + ); + }); + } + + // Apply status filter if selected + if (selectedStatus.length > 0) { + result = result.filter(anime => { + if (!anime.status) return false; + + return selectedStatus.some(status => + anime.status.toLowerCase().includes(status.toLowerCase()) + ); + }); + } + + // Apply language filter if selected + if (selectedLanguages.length > 0) { + result = result.filter(anime => { + // If no language info, assume subbed (most common) + const animeLanguage = anime.language || 'Subbed'; + + return selectedLanguages.some(language => + animeLanguage.toLowerCase().includes(language.toLowerCase()) + ); + }); + } + + // Apply client-side sorting (when API sort is not supported) + if (sortOrder !== 'default') { + switch (sortOrder) { + case 'title-asc': + result.sort((a, b) => (a.title || '').localeCompare(b.title || '')); + break; + case 'title-desc': + result.sort((a, b) => (b.title || '').localeCompare(a.title || '')); + break; + case 'year-desc': + result.sort((a, b) => (parseInt(b.year) || 0) - (parseInt(a.year) || 0)); + break; + case 'year-asc': + result.sort((a, b) => (parseInt(a.year) || 0) - (parseInt(b.year) || 0)); + break; + // Default order from API is used when sortOrder is 'default' + } + } + + return result; + }, [selectedSeasons, selectedTypes, selectedStatus, selectedLanguages, sortOrder]); + + // Fetch most popular anime when search is empty + const fetchPopularAnime = useCallback(async () => { + setIsLoading(true); + setError(null); + setIsEmptySearch(true); + + try { + const data = await fetchMostPopular(1); + const processedData = processAnimeData(data); + + const results = processedData.results || []; + setAnimeList(results); + + // Apply client-side filters + const filteredResults = applyClientSideFilters(results); + setFilteredList(filteredResults); + + setHasNextPage(processedData.hasNextPage || false); + } catch (error) { + console.error('Error fetching popular anime:', error); + setError('Failed to fetch popular anime. Please try again later.'); + setAnimeList([]); + setFilteredList([]); + } finally { + setIsLoading(false); + } + }, [processAnimeData, applyClientSideFilters]); + + // Fetch data from API when search term or main filters change + useEffect(() => { + const fetchData = async () => { + if (!queryTerm.trim()) { + // Show popular anime instead of empty results + fetchPopularAnime(); + return; + } + + setIsLoading(true); + setError(null); + setCurrentPage(1); + setIsEmptySearch(false); + + try { + const filters = getFiltersForApi(); + const data = await searchAnime(queryTerm, 1, filters); + const processedData = processAnimeData(data); + + const results = processedData.results || []; + setAnimeList(results); + + // Apply client-side filters + const filteredResults = applyClientSideFilters(results); + setFilteredList(filteredResults); + + setHasNextPage(processedData.hasNextPage || false); + } catch (error) { + console.error('Error searching anime:', error); + setError('Failed to search anime. Please try again later.'); + setAnimeList([]); + setFilteredList([]); + } finally { + setIsLoading(false); + } + }; + + fetchData(); + }, [queryTerm, getFiltersForApi, processAnimeData, applyClientSideFilters, fetchPopularAnime]); + + // Handle pagination + useEffect(() => { + // Skip if it's the first page (already fetched in the previous effect) + // or if no search term is provided + if (currentPage === 1) { + return; + } + + const loadMoreData = async () => { + setIsLoading(true); + + try { + // If it's an empty search query, load more popular anime + if (isEmptySearch) { + const data = await fetchMostPopular(currentPage); + const processedData = processAnimeData(data); + + const newResults = processedData.results || []; + setAnimeList(prev => [...prev, ...newResults]); + + // Apply client-side filters to new results + const filteredNewResults = applyClientSideFilters(newResults); + setFilteredList(prev => [...prev, ...filteredNewResults]); + + setHasNextPage(processedData.hasNextPage || false); + } else { + // Load more search results + const filters = getFiltersForApi(); + const data = await searchAnime(queryTerm, currentPage, filters); + const processedData = processAnimeData(data); + + const newResults = processedData.results || []; + setAnimeList(prev => [...prev, ...newResults]); + + // Apply client-side filters to new results + const filteredNewResults = applyClientSideFilters(newResults); + setFilteredList(prev => [...prev, ...filteredNewResults]); + + setHasNextPage(processedData.hasNextPage || false); + } + } catch (error) { + console.error('Error loading more anime:', error); + setError('Failed to load more results. Please try again later.'); + } finally { + setIsLoading(false); + } + }; + + loadMoreData(); + }, [currentPage, queryTerm, getFiltersForApi, processAnimeData, applyClientSideFilters, isEmptySearch]); + + // Re-apply client-side filters when filters change but don't need API refetch + useEffect(() => { + const applyFilters = () => { + const filteredResults = applyClientSideFilters(animeList); + setFilteredList(filteredResults); + }; + + applyFilters(); + }, [selectedSeasons, selectedTypes, selectedStatus, selectedLanguages, animeList, applyClientSideFilters]); + + const handleLoadMore = () => { + setCurrentPage(prev => prev + 1); + }; + + const handleGenreChange = (genre) => { + setSelectedGenre(genre); + if (currentPage !== 1) setCurrentPage(1); + }; + + const handleYearChange = (year) => { + setYearFilter(year); + if (currentPage !== 1) setCurrentPage(1); + }; + + const handleSortChange = (order) => { + setSortOrder(order); + if (currentPage !== 1) setCurrentPage(1); + }; + + const handleSeasonChange = (seasons) => { + setSelectedSeasons(seasons); + if (currentPage !== 1) setCurrentPage(1); + }; + + const handleTypeChange = (types) => { + setSelectedTypes(types); + if (currentPage !== 1) setCurrentPage(1); + }; + + const handleStatusChange = (status) => { + setSelectedStatus(status); + if (currentPage !== 1) setCurrentPage(1); + }; + + const handleLanguageChange = (languages) => { + setSelectedLanguages(languages); + if (currentPage !== 1) setCurrentPage(1); + }; + + return ( +
+ {/* Horizontal filters at the top */} +
+ {}} + selectedSeasons={selectedSeasons} + onSeasonChange={handleSeasonChange} + selectedTypes={selectedTypes} + onTypeChange={handleTypeChange} + selectedStatus={selectedStatus} + onStatusChange={handleStatusChange} + selectedLanguages={selectedLanguages} + onLanguageChange={handleLanguageChange} + /> +
+ + {/* Main content */} +
+
+

+ {queryTerm ? `Results for "${queryTerm}"` : 'Popular Anime'} +

+
+ {filteredList.length > 0 && ( + {filteredList.length} {filteredList.length === 1 ? 'result' : 'results'} + )} +
+
+ + {isLoading && currentPage === 1 ? ( +
+
+
+ ) : error ? ( +
+

Error

+

{error}

+
+ ) : !queryTerm && !isEmptySearch ? ( +
+

Start Searching

+

Enter a keyword in the search box above to find anime

+
+ ) : filteredList.length > 0 ? ( + <> +
+ {filteredList.map((anime) => ( + + ))} +
+ + {hasNextPage && ( +
+ +
+ )} + + ) : ( +
+

No results found

+

+ We couldn't find any anime matching your search criteria. Please try different filters or a different search term. +

+
+ )} +
+
+ ); +} + +export default function SearchPage() { + return ( + +
+ + }> + +
+ ); +} \ No newline at end of file diff --git a/src/app/terms-and-services/page.jsx b/src/app/terms-and-services/page.jsx new file mode 100644 index 0000000..3b80de2 --- /dev/null +++ b/src/app/terms-and-services/page.jsx @@ -0,0 +1,191 @@ +'use client'; + +import SharedLayout from '@/components/SharedLayout'; + +export default function TermsPage() { + return ( + +
+

Terms of Service & Privacy Policy

+ +
+
+

Terms of Service

+ +
+
+

1. Acceptance of Terms

+

+ By accessing and using JustAnime, you acknowledge that you have read, understood, and agree to be bound by these Terms of Service. + If you do not agree with any part of these terms, you may not use our services. +

+
+ +
+

2. Service Description

+

+ JustAnime is a platform that provides information and links to anime content. + We do not host, upload, or distribute any content directly. + Our service aggregates links to third-party websites and services that host the actual content. +

+
+ +
+

3. User Conduct

+

+ Users of JustAnime agree not to: +

+
    +
  • Use our service for any illegal purpose or in violation of any local, state, national, or international law
  • +
  • Harass, abuse, or harm another person
  • +
  • Interfere with or disrupt the service or servers connected to the service
  • +
  • Create multiple accounts for disruptive or abusive purposes
  • +
  • Attempt to access any portion of the service that you are not authorized to access
  • +
+
+ +
+

4. Content Disclaimer

+

+ JustAnime does not host any content on its servers. We are not responsible for the content, accuracy, or practices of third-party websites that our service may link to. + These links are provided solely as a convenience to users, and we do not endorse the content of such third-party sites. +

+
+ +
+

5. Intellectual Property

+

+ All trademarks, logos, service marks, and trade names are the property of their respective owners. + JustAnime respects intellectual property rights and expects users to do the same. + If you believe content linked through our service infringes on your copyright, please contact us with details. +

+
+ +
+

6. Modification of Terms

+

+ JustAnime reserves the right to modify these Terms of Service at any time. + We will provide notice of significant changes through our website. + Your continued use of our service after such modifications constitutes your acceptance of the updated terms. +

+
+ +
+

7. Termination

+

+ JustAnime reserves the right to terminate or suspend your access to our service at any time, without prior notice or liability, for any reason whatsoever, including without limitation if you breach these Terms of Service. +

+
+
+
+ +
+

Privacy Policy

+ +
+
+

1. Information We Collect

+

+ JustAnime collects the following types of information: +

+
    +
  • Information you provide: We may collect personal information such as your email address when you sign up for an account or contact us.
  • +
  • Usage data: We automatically collect information about your interactions with our service, including the pages you visit and your preferences.
  • +
  • Device information: We collect information about your device and internet connection, including IP address, browser type, and operating system.
  • +
+
+ +
+

2. How We Use Your Information

+

+ We use the information we collect to: +

+
    +
  • Provide, maintain, and improve our service
  • +
  • Communicate with you about updates, support, and features
  • +
  • Monitor and analyze usage patterns and trends
  • +
  • Protect against, identify, and prevent fraud and other illegal activity
  • +
  • Comply with legal obligations
  • +
+
+ +
+

3. Cookies and Similar Technologies

+

+ JustAnime uses cookies and similar tracking technologies to track activity on our service and hold certain information. + Cookies are files with a small amount of data that may include an anonymous unique identifier. + You can instruct your browser to refuse all cookies or to indicate when a cookie is being sent. +

+
+ +
+

4. Data Sharing and Disclosure

+

+ We may share your information in the following circumstances: +

+
    +
  • With service providers who perform services on our behalf
  • +
  • To comply with legal obligations
  • +
  • To protect the rights, property, or safety of JustAnime, our users, or others
  • +
  • In connection with a business transfer, such as a merger or acquisition
  • +
+
+ +
+

5. Data Security

+

+ JustAnime takes reasonable measures to protect your information from unauthorized access, alteration, disclosure, or destruction. + However, no method of transmission over the Internet or electronic storage is 100% secure, and we cannot guarantee absolute security. +

+
+ +
+

6. Your Rights

+

+ Depending on your location, you may have certain rights regarding your personal data, including: +

+
    +
  • The right to access and receive a copy of your data
  • +
  • The right to rectify or update your data
  • +
  • The right to delete your data
  • +
  • The right to restrict processing of your data
  • +
  • The right to object to processing of your data
  • +
  • The right to data portability
  • +
+

+ To exercise these rights, please contact us at privacy@justanime.com. +

+
+ +
+

7. Children's Privacy

+

+ JustAnime does not knowingly collect personal information from children under 13. + If you are a parent or guardian and you believe your child has provided us with personal information, please contact us. +

+
+ +
+

8. Changes to This Privacy Policy

+

+ We may update our Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page and updating the "Last Updated" date. +

+
+ +
+

9. Contact Us

+

+ If you have any questions about this Privacy Policy, please contact us at privacy@justanime.com. +

+
+ +
+

Last Updated: May 5, 2024

+
+
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/src/app/top-airing/layout.js b/src/app/top-airing/layout.js new file mode 100644 index 0000000..a55c076 --- /dev/null +++ b/src/app/top-airing/layout.js @@ -0,0 +1,5 @@ +import SharedLayout from '@/components/SharedLayout'; + +export default function TopAiringLayout({ children }) { + return {children}; +} \ No newline at end of file diff --git a/src/app/top-airing/page.js b/src/app/top-airing/page.js new file mode 100644 index 0000000..06cb7f8 --- /dev/null +++ b/src/app/top-airing/page.js @@ -0,0 +1,276 @@ +'use client'; + +import { useState, useEffect, useRef } from 'react'; +import AnimeCard from '@/components/AnimeCard'; +import AnimeFilters from '@/components/AnimeFilters'; +import { fetchTopAiring } from '@/lib/api'; + +export default function TopAiringPage() { + const [animeList, setAnimeList] = useState([]); + const [filteredList, setFilteredList] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [currentPage, setCurrentPage] = useState(1); + const [hasNextPage, setHasNextPage] = useState(false); + const [selectedGenre, setSelectedGenre] = useState(null); + const [yearFilter, setYearFilter] = useState('all'); + const [sortOrder, setSortOrder] = useState('default'); + const [searchQuery, setSearchQuery] = useState(''); + const [selectedSeasons, setSelectedSeasons] = useState([]); + const [selectedTypes, setSelectedTypes] = useState([]); + const [selectedStatus, setSelectedStatus] = useState([]); + const [selectedLanguages, setSelectedLanguages] = useState([]); + const [error, setError] = useState(null); + + // Current year for filtering + const currentYear = new Date().getFullYear(); + + // Add ref to track if this is the first render + const initialRender = useRef(true); + + useEffect(() => { + const fetchData = async () => { + setIsLoading(true); + try { + const data = await fetchTopAiring(currentPage); + + if (currentPage === 1) { + setAnimeList(data.results || []); + } else { + setAnimeList(prev => [...prev, ...(data.results || [])]); + } + + setHasNextPage(data.hasNextPage || false); + } catch (error) { + console.error('Error fetching top airing anime:', error); + setError('Failed to load anime. Please try again later.'); + } finally { + setIsLoading(false); + } + }; + + fetchData(); + }, [currentPage]); + + // Apply filters and sorting whenever the anime list or filter settings change + useEffect(() => { + // Skip the initial render effect to avoid duplicate filtering + if (initialRender.current) { + initialRender.current = false; + return; + } + + if (!animeList.length) { + setFilteredList([]); + return; + } + + let result = [...animeList]; + + // Search filter + if (searchQuery && searchQuery.trim() !== '') { + const query = searchQuery.toLowerCase().trim(); + result = result.filter(anime => { + const title = (anime.title || '').toLowerCase(); + const otherNames = (anime.otherNames || '').toLowerCase(); + return title.includes(query) || otherNames.includes(query); + }); + } + + // Filter by genre if selected + if (selectedGenre) { + result = result.filter(anime => { + if (anime.genres && Array.isArray(anime.genres)) { + return anime.genres.some(g => + g.toLowerCase() === selectedGenre.toLowerCase() || + (g.name && g.name.toLowerCase() === selectedGenre.toLowerCase()) + ); + } else if (anime.genre) { + return anime.genre.toLowerCase().includes(selectedGenre.toLowerCase()); + } + return false; + }); + } + + // Filter by season + if (selectedSeasons.length > 0) { + result = result.filter(anime => { + const season = getAnimeSeason(anime); + return selectedSeasons.includes(season); + }); + } + + // Filter by year + if (yearFilter !== 'all') { + result = result.filter(anime => { + const animeYear = parseInt(anime.year) || 0; + if (yearFilter === 'older') { + return animeYear < 2000; + } else { + return animeYear.toString() === yearFilter; + } + }); + } + + // Filter by type + if (selectedTypes.length > 0) { + result = result.filter(anime => + selectedTypes.includes(anime.type) + ); + } + + // Filter by status + if (selectedStatus.length > 0) { + result = result.filter(anime => { + const status = anime.status || getDefaultStatus(anime); + return selectedStatus.includes(status); + }); + } + + // Filter by language + if (selectedLanguages.length > 0) { + result = result.filter(anime => { + const language = anime.language || getDefaultLanguage(anime); + return selectedLanguages.includes(language); + }); + } + + // Apply sorting + switch (sortOrder) { + case 'title-asc': + result.sort((a, b) => (a.title || '').localeCompare(b.title || '')); + break; + case 'title-desc': + result.sort((a, b) => (b.title || '').localeCompare(a.title || '')); + break; + case 'year-desc': + result.sort((a, b) => (parseInt(b.year) || 0) - (parseInt(a.year) || 0)); + break; + case 'year-asc': + result.sort((a, b) => (parseInt(a.year) || 0) - (parseInt(b.year) || 0)); + break; + default: + // Default order from API + break; + } + + setFilteredList(result); + }, [animeList, selectedGenre, yearFilter, sortOrder, searchQuery, selectedSeasons, selectedTypes, selectedStatus, selectedLanguages]); + + const handleLoadMore = () => { + setCurrentPage(prev => prev + 1); + }; + + const handleGenreChange = (genre) => { + setSelectedGenre(genre); + }; + + const handleYearChange = (year) => { + setYearFilter(year); + }; + + const handleSortChange = (order) => { + setSortOrder(order); + }; + + const handleSearchChange = (value) => { + setSearchQuery(value); + }; + + const handleSeasonChange = (seasons) => { + setSelectedSeasons(seasons); + }; + + const handleTypeChange = (types) => { + setSelectedTypes(types); + }; + + const handleStatusChange = (status) => { + setSelectedStatus(status); + }; + + const handleLanguageChange = (languages) => { + setSelectedLanguages(languages); + }; + + return ( +
+

Top Airing Anime

+ + {/* Filters */} +
+ +
+ + {isLoading && animeList.length === 0 ? ( +
+ {[...Array(14)].map((_, index) => ( +
+
+
+
+
+
+
+ ))} +
+ ) : (filteredList.length > 0 || animeList.length > 0) ? ( + <> +
+ {(filteredList.length > 0 ? filteredList : animeList).map((anime) => ( + + ))} +
+ + {hasNextPage && ( +
+ +
+ )} + + ) : ( +
+

No anime found

+

+ We couldn't find any top airing anime at this time. Please check back later. +

+
+ )} +
+ ); +} \ No newline at end of file diff --git a/src/app/watch/[episodeId]/page.js b/src/app/watch/[episodeId]/page.js new file mode 100644 index 0000000..b0a5a56 --- /dev/null +++ b/src/app/watch/[episodeId]/page.js @@ -0,0 +1,503 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { useParams, useRouter, usePathname, useSearchParams } from 'next/navigation'; +import Link from 'next/link'; +import Image from 'next/image'; +import VideoPlayer from '@/components/VideoPlayer'; +import EpisodeList from '@/components/EpisodeList'; +import { fetchEpisodeSources, fetchAnimeInfo } from '@/lib/api'; + +export default function WatchPage() { + const { episodeId } = useParams(); + const router = useRouter(); + const pathname = usePathname(); + const [videoSource, setVideoSource] = useState(null); + const [anime, setAnime] = useState(null); + const [currentEpisode, setCurrentEpisode] = useState(null); + const [isDub, setIsDub] = useState(false); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + const [videoHeaders, setVideoHeaders] = useState({}); + const [subtitles, setSubtitles] = useState([]); + const [thumbnails, setThumbnails] = useState(null); + const [animeId, setAnimeId] = useState(null); + const [episodeData, setEpisodeData] = useState(null); + const [isRetrying, setIsRetrying] = useState(false); + const [currentPage, setCurrentPage] = useState(1); + const episodesPerPage = 100; + const [showFullSynopsis, setShowFullSynopsis] = useState(false); + const [autoSkip, setAutoSkip] = useState(false); + const [currentEpisodeId, setCurrentEpisodeId] = useState(episodeId); + + // Handle URL updates when currentEpisodeId changes + useEffect(() => { + if (currentEpisodeId && currentEpisodeId !== episodeId) { + const newUrl = `/watch/${currentEpisodeId}`; + window.history.pushState({ episodeId: currentEpisodeId }, '', newUrl); + } + }, [currentEpisodeId, episodeId]); + + // Listen for popstate (browser back/forward) events + useEffect(() => { + const handlePopState = (event) => { + const path = window.location.pathname; + const match = path.match(/\/watch\/(.+)$/); + if (match) { + const newEpisodeId = match[1]; + setCurrentEpisodeId(newEpisodeId); + } + }; + + window.addEventListener('popstate', handlePopState); + return () => window.removeEventListener('popstate', handlePopState); + }, []); + + // Extract animeId from the URL + useEffect(() => { + if (episodeId) { + // Log the raw episodeId from the URL for debugging + console.log('[Watch] Raw episodeId from URL:', episodeId); + + // The URL might contain query parameters for episode number + const [baseId, queryParams] = episodeId.split('?'); + console.log('[Watch] Base ID:', baseId); + + setAnimeId(baseId); + setCurrentEpisodeId(episodeId); + } + }, [episodeId]); + + // Fetch episode sources first to ensure we have data even if anime info fails + useEffect(() => { + if (!currentEpisodeId || currentEpisodeId === 'undefined') { + setError('Invalid episode ID'); + setIsLoading(false); + return; + } + + const fetchVideoData = async () => { + setIsLoading(true); + setError(null); + setVideoSource(null); + + try { + console.log(`[Watch] Fetching video for episode ${currentEpisodeId} (dub: ${isDub})`); + + // Fetch the episode sources from the API + const data = await fetchEpisodeSources(currentEpisodeId, isDub); + + console.log('[Watch] Episode API response:', data); + setEpisodeData(data); + + if (!data || !data.sources || data.sources.length === 0) { + throw new Error('No video sources available for this episode'); + } + + // Extract headers if they exist in the response + if (data.headers) { + console.log('[Watch] Headers from API:', data.headers); + setVideoHeaders(data.headers); + } else { + // Set default headers if none provided + const defaultHeaders = { + "Referer": "https://hianime.to/", + "Origin": "https://hianime.to" + }; + console.log('[Watch] No headers provided from API, using defaults:', defaultHeaders); + setVideoHeaders(defaultHeaders); + } + + // Try to find the best source in order of preference + // 1. HLS (m3u8) sources + // 2. High quality MP4 sources + const hlsSource = data.sources.find(src => src.isM3U8); + const mp4Source = data.sources.find(src => !src.isM3U8); + + let selectedSource = null; + + if (hlsSource && hlsSource.url) { + console.log('[Watch] Selected HLS source:', hlsSource.url); + selectedSource = hlsSource.url; + } else if (mp4Source && mp4Source.url) { + console.log('[Watch] Selected MP4 source:', mp4Source.url); + selectedSource = mp4Source.url; + } else if (data.sources[0] && data.sources[0].url) { + console.log('[Watch] Falling back to first available source:', data.sources[0].url); + selectedSource = data.sources[0].url; + } else { + throw new Error('No valid video URLs found'); + } + + setVideoSource(selectedSource); + setIsLoading(false); + + } catch (error) { + console.error('[Watch] Error fetching video sources:', error); + setError(error.message || 'Failed to load video'); + setIsLoading(false); + + // If this is the first try, attempt to retry once + if (!isRetrying) { + console.log('[Watch] First error, attempting retry...'); + setIsRetrying(true); + setTimeout(() => { + console.log('[Watch] Executing retry...'); + fetchVideoData(); + }, 2000); + } + } + }; + + fetchVideoData(); + }, [currentEpisodeId, isDub, isRetrying]); + + // Fetch anime info using extracted animeId + useEffect(() => { + if (animeId) { + const fetchAnimeDetails = async () => { + try { + setIsRetrying(true); + console.log(`[Watch] Fetching anime info for ID: ${animeId}`); + const animeData = await fetchAnimeInfo(animeId); + + if (animeData) { + console.log('[Watch] Anime info received:', animeData.title); + setAnime(animeData); + + // Find the current episode in the anime episode list + if (animeData.episodes && animeData.episodes.length > 0) { + console.log('[Watch] Episodes found:', animeData.episodes.length); + + // First try exact match + let episode = animeData.episodes.find(ep => ep.id === episodeId); + + // If not found, try to find by checking if episodeId is contained in ep.id + if (!episode && episodeId.includes('$episode$')) { + const episodeIdPart = episodeId.split('$episode$')[1]; + episode = animeData.episodes.find(ep => ep.id.includes(episodeIdPart)); + } + + if (episode) { + setCurrentEpisode(episode); + console.log('[Watch] Current episode found:', episode.number); + } else { + console.warn('[Watch] Current episode not found in episode list. Looking for:', episodeId); + console.log('[Watch] First few episodes:', animeData.episodes.slice(0, 3).map(ep => ep.id)); + } + } else { + console.warn('[Watch] No episodes found in anime data or episodes array is empty'); + } + } else { + console.error('[Watch] Failed to fetch anime info or received empty response'); + } + } catch (error) { + console.error('[Watch] Error fetching anime info:', error); + } finally { + setIsRetrying(false); + } + }; + + fetchAnimeDetails(); + } else { + console.warn('[Watch] No animeId available to fetch anime details'); + } + }, [animeId, episodeId]); + + const handleDubToggle = () => { + setIsDub(!isDub); + }; + + const handleEpisodeClick = (newEpisodeId) => { + if (newEpisodeId !== currentEpisodeId) { + // Update the URL using history API + const newUrl = `/watch/${newEpisodeId}`; + window.history.pushState({ episodeId: newEpisodeId }, '', newUrl); + + // Update state to trigger video reload + setCurrentEpisodeId(newEpisodeId); + + // Update current episode in state + if (anime?.episodes) { + const newEpisode = anime.episodes.find(ep => ep.id === newEpisodeId); + if (newEpisode) { + setCurrentEpisode(newEpisode); + } + } + } + }; + + const handleRetryAnimeInfo = () => { + if (animeId) { + setIsRetrying(true); + fetchAnimeInfo(animeId) + .then(data => { + if (data) { + setAnime(data); + console.log('[Watch] Anime info retry succeeded:', data.title); + } else { + console.error('[Watch] Anime info retry failed: empty response'); + } + }) + .catch(error => { + console.error('[Watch] Anime info retry error:', error); + }) + .finally(() => { + setIsRetrying(false); + }); + } + }; + + const findAdjacentEpisodes = () => { + if (!anime?.episodes || !currentEpisodeId) return { prev: null, next: null }; + + const currentIndex = anime.episodes.findIndex(ep => ep.id === currentEpisodeId); + if (currentIndex === -1) return { prev: null, next: null }; + + return { + prev: currentIndex > 0 ? anime.episodes[currentIndex - 1] : null, + next: currentIndex < anime.episodes.length - 1 ? anime.episodes[currentIndex + 1] : null + }; + }; + + const { prev, next } = findAdjacentEpisodes(); + + return ( +
+
+
+ {/* Left Side - Video Player (70%) */} +
+
+ {/* Video Player Container */} +
+
+
+ {error ? ( +
+
Error: {error}
+

+ The video source couldn't be loaded. Please try again or check back later. +

+
+ ) : isLoading ? ( +
+
+
Loading video...
+
+ ) : videoSource ? ( +
+ +
+ ) : ( +
+
No video source available
+

+ Please try again or check back later. +

+
+ )} +
+
+
+ + {/* Video Controls - Slimmer and without container background */} +
+ {/* Audio and Playback Controls */} +
+ {/* Playback Settings */} +
+

Playback Settings

+
+ {/* Auto Skip Checkbox */} + {(episodeData?.intro || episodeData?.outro) && ( + + )} +
+
+ + {/* Audio Toggle */} +
+

Audio

+
+ + +
+
+
+ + {/* Episode Navigation */} +
+ {anime?.episodes && ( + <> + + + + )} +
+
+ + {/* Anime Info Section */} + {anime && ( +
+
+ {/* Cover Image */} +
+
+ {anime.title} +
+
+ + {/* Details */} +
+ +

+ {anime.title} +

+ + + {/* Status Bar */} +
+ {anime.status} + • + {anime.type} + • + {anime.totalEpisodes} Episodes +
+ + {/* Synopsis Section */} +
+

Synopsis

+
+
+ {anime.description} +
+ +
+
+ + {/* Genres */} + {anime.genres && ( +
+ {anime.genres.map((genre, index) => ( + + {genre} + + ))} +
+ )} +
+
+
+ )} +
+
+ + {/* Right Side - Episode List (30%) */} +
+ {anime?.episodes ? ( +
+ +
+ ) : ( +
+
+ No episodes available +
+
+ )} +
+
+
+
+ ); +} \ No newline at end of file diff --git a/src/app/watch/layout.js b/src/app/watch/layout.js new file mode 100644 index 0000000..3fca793 --- /dev/null +++ b/src/app/watch/layout.js @@ -0,0 +1,5 @@ +import SharedLayout from '@/components/SharedLayout'; + +export default function WatchLayout({ children }) { + return {children}; +} \ No newline at end of file diff --git a/src/components/AnimeCalendar.js b/src/components/AnimeCalendar.js new file mode 100644 index 0000000..c4e3b53 --- /dev/null +++ b/src/components/AnimeCalendar.js @@ -0,0 +1,210 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import Link from 'next/link'; +import Image from 'next/image'; +import { fetchSchedule } from '@/lib/api'; + +export default function AnimeCalendar() { + const [selectedDay, setSelectedDay] = useState(getCurrentDayIndex()); + const [scheduleData, setScheduleData] = useState([]); + const [isLoading, setIsLoading] = useState(true); + + // Add custom scrollbar styles + useEffect(() => { + // Add custom styles for the calendar scrollbar + const style = document.createElement('style'); + style.textContent = ` + .schedule-scrollbar::-webkit-scrollbar { + width: 4px; + } + .schedule-scrollbar::-webkit-scrollbar-track { + background: var(--card); + } + .schedule-scrollbar::-webkit-scrollbar-thumb { + background-color: var(--border); + border-radius: 4px; + } + `; + document.head.appendChild(style); + + // Cleanup function + return () => { + document.head.removeChild(style); + }; + }, []); + + // Get current day index (0-6, Sunday is 0) + function getCurrentDayIndex() { + const dayIndex = new Date().getDay(); + return dayIndex; // Sunday is 0, Monday is 1, etc. + } + + // Get current date info for the header + const getCurrentDateInfo = () => { + const today = new Date(); + const dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; + + // Calculate the date for the selected day + const currentDayIndex = today.getDay(); + let daysDiff = selectedDay - currentDayIndex; + + // Always get the previous occurrence (or today if it's the current day) + if (daysDiff > 0) { + daysDiff -= 7; // Go back to previous week + } + + const selectedDate = new Date(today); + selectedDate.setDate(today.getDate() + daysDiff); + + return { + day: dayNames[selectedDay], + date: selectedDate.getDate(), + month: monthNames[selectedDate.getMonth()] + }; + }; + + const dateInfo = getCurrentDateInfo(); + + // Generate week days for the calendar + const days = [ + { label: 'Mon', value: 1 }, + { label: 'Tue', value: 2 }, + { label: 'Wed', value: 3 }, + { label: 'Thu', value: 4 }, + { label: 'Fri', value: 5 }, + { label: 'Sat', value: 6 }, + { label: 'Sun', value: 0 }, + ]; + + useEffect(() => { + async function loadScheduleData() { + setIsLoading(true); + try { + // Get the date for the selected day + const today = new Date(); + const currentDayIndex = today.getDay(); + let daysDiff = selectedDay - currentDayIndex; + + if (daysDiff > 0) { + daysDiff -= 7; + } + + const selectedDate = new Date(today); + selectedDate.setDate(today.getDate() + daysDiff); + + // Format date as YYYY-MM-DD + const formattedDate = selectedDate.toISOString().split('T')[0]; + + // Fetch schedule data for the selected date + const data = await fetchSchedule(formattedDate); + + if (data && data.scheduledAnimes) { + // Process and sort the scheduled animes by time + const processedData = data.scheduledAnimes + .map(anime => ({ + id: anime.id, + title: anime.name, + japaneseTitle: anime.jname, + time: anime.time, + airingTimestamp: anime.airingTimestamp, + secondsUntilAiring: anime.secondsUntilAiring + })) + .sort((a, b) => { + // Convert time strings to comparable values (assuming 24-hour format) + const timeA = a.time.split(':').map(Number); + const timeB = b.time.split(':').map(Number); + return (timeA[0] * 60 + timeA[1]) - (timeB[0] * 60 + timeB[1]); + }); + + setScheduleData(processedData); + } else { + setScheduleData([]); + } + } catch (error) { + console.error('Error loading schedule data:', error); + setScheduleData([]); + } finally { + setIsLoading(false); + } + } + + loadScheduleData(); + }, [selectedDay]); + + return ( +
+ {/* Header */} +
+
+

Release Calendar

+
+ {dateInfo.month} {dateInfo.date} +
+
+ + {/* Day selector */} +
+ {days.map((day) => ( + + ))} +
+
+ + {/* Schedule list */} +
+ {isLoading ? ( +
+
+
+ ) : scheduleData.length > 0 ? ( +
+ {scheduleData.map((anime) => ( + +
+ {/* Time */} +
+ {anime.time} +
+ + {/* Anime info */} +
+

+ {anime.title} +

+ {anime.japaneseTitle && ( +

+ {anime.japaneseTitle} +

+ )} +
+
+ + ))} +
+ ) : ( +
+ No releases scheduled +
+ )} +
+
+ ); +} \ No newline at end of file diff --git a/src/components/AnimeCard.js b/src/components/AnimeCard.js new file mode 100644 index 0000000..4143b15 --- /dev/null +++ b/src/components/AnimeCard.js @@ -0,0 +1,96 @@ +'use client'; + +import Image from 'next/image'; +import Link from 'next/link'; +import { useState } from 'react'; + +export default function AnimeCard({ anime, isRecent }) { + const [imageError, setImageError] = useState(false); + + if (!anime) return null; + + const handleImageError = () => { + console.log("Image error for:", anime.name); + setImageError(true); + }; + + // Get image URL with fallback + const imageSrc = imageError ? '/images/placeholder.png' : anime.poster; + + // Generate appropriate links + const infoLink = `/anime/${anime.id}`; + const watchLink = isRecent + ? `/watch/${anime.id}?ep=${anime.episodes?.sub || anime.episodes?.dub || 1}` + : `/anime/${anime.id}`; + + return ( +
+ {/* Image card linking to watch page */} + +
+ {/* Hover overlay */} +
+ + {/* Play button triangle - appears on hover */} +
+ + + +
+ + {anime.name + + {/* Badges in bottom left */} +
+ {/* Episode badges */} + {anime.episodes && ( + <> + {anime.episodes.sub > 0 && ( +
+ SUB {anime.episodes.sub} +
+ )} + {anime.episodes.dub > 0 && ( +
+ DUB {anime.episodes.dub} +
+ )} + + )} + + {/* Type badge */} + {anime.type && ( +
+ {anime.type} +
+ )} +
+
+ + + {/* Title linking to info page */} + +

+ {anime.name} +

+ +
+ ); +} \ No newline at end of file diff --git a/src/components/AnimeDetails.js b/src/components/AnimeDetails.js new file mode 100644 index 0000000..cec37cf --- /dev/null +++ b/src/components/AnimeDetails.js @@ -0,0 +1,435 @@ +'use client'; + +import { useState } from 'react'; +import Image from 'next/image'; +import Link from 'next/link'; + +export default function AnimeDetails({ anime }) { + const [isExpanded, setIsExpanded] = useState(false); + const [activeVideo, setActiveVideo] = useState(null); + + console.log('AnimeDetails received:', anime); + + if (!anime?.info) { + console.error('Invalid anime data structure:', anime); + return null; + } + + const { info, moreInfo, relatedAnime, recommendations, mostPopular, seasons } = anime; + + // Helper function to render anime cards + const renderAnimeCards = (animeList, title) => { + if (!animeList || animeList.length === 0) return null; + + return ( +
+

{title}

+
+ {animeList.map((item, index) => ( + +
+
+ {item.name} +
+
+

{item.name}

+
+
+ + ))} +
+
+ ); + }; + + // Helper function to render seasons + const renderSeasons = () => { + if (!seasons || seasons.length === 0) return null; + + return ( +
+

Seasons

+
+ {seasons.map((season, index) => ( + +
+
+ {season.name} + {season.isCurrent && ( +
+ Current +
+ )} +
+
+

{season.title || season.name}

+
+
+ + ))} +
+
+ ); + }; + + // Video modal for promotional videos + const VideoModal = ({ video, onClose }) => { + if (!video) return null; + + return ( +
+
+ + +
+ +
+ + {video.title && ( +
+

{video.title}

+
+ )} +
+
+ ); + }; + + return ( +
+ {/* Video Modal */} + {activeVideo && setActiveVideo(null)} />} + + {/* Background Image with Gradient Overlay */} +
+ {info.poster && ( + <> + {info.name} +
+ + )} +
+ + {/* Main Content */} +
+
+ {/* Left Column - Poster and Mobile Title */} +
+
+
+ {info.name} +
+
+ + {/* Mobile Title Section */} +
+

{info.name}

+ {info.jname && ( +

{info.jname}

+ )} +
+ + {/* Mobile Quick Info */} +
+ {info.stats?.rating && ( +
+ + + + {info.stats.rating} +
+ )} + {moreInfo?.status && ( +
+ {moreInfo.status} +
+ )} + {info.stats?.type && ( +
+ {info.stats.type} +
+ )} + {info.stats?.episodes && ( +
+ {info.stats.episodes.sub > 0 && `SUB ${info.stats.episodes.sub}`} + {info.stats.episodes.dub > 0 && info.stats.episodes.sub > 0 && ' | '} + {info.stats.episodes.dub > 0 && `DUB ${info.stats.episodes.dub}`} +
+ )} +
+
+ + {/* Right Column - Details */} +
+
+ {/* Desktop Title Section */} +
+

{info.name}

+ {info.jname && ( +

{info.jname}

+ )} +
+ + {/* Desktop Quick Info */} +
+ {info.stats?.rating && ( +
+ + + + {info.stats.rating} +
+ )} + {moreInfo?.status && ( +
+ {moreInfo.status} +
+ )} + {info.stats?.type && ( +
+ {info.stats.type} +
+ )} + {info.stats?.episodes && ( +
+ {info.stats.episodes.sub > 0 && `SUB ${info.stats.episodes.sub}`} + {info.stats.episodes.dub > 0 && info.stats.episodes.sub > 0 && ' | '} + {info.stats.episodes.dub > 0 && `DUB ${info.stats.episodes.dub}`} +
+ )} + {info.stats?.quality && ( +
+ {info.stats.quality} +
+ )} + {info.stats?.duration && ( +
+ {info.stats.duration} +
+ )} +
+ + {/* Synopsis */} +
+

Synopsis

+
+

+ {info.description || 'No description available for this anime.'} +

+ {info.description && info.description.length > 100 && ( + + )} +
+
+ + {/* Watch Button */} + {info.stats?.episodes && (info.stats.episodes.sub > 0 || info.stats.episodes.dub > 0) && ( +
+ + + + + Start Watching + +
+ )} + + {/* Promotional Videos */} + {info.promotionalVideos && info.promotionalVideos.length > 0 && ( +
+

Promotional Videos

+
+ {info.promotionalVideos.map((video, index) => ( +
setActiveVideo(video)} + > +
+
+ + + +
+
+ {video.title +
+ ))} +
+
+ )} + + {/* Additional Info */} +
+ {/* Genres */} + {moreInfo?.genres && moreInfo.genres.length > 0 && ( +
+

Genres

+
+
+ {moreInfo.genres.map((genre, index) => ( + + {genre} + + ))} +
+
+
+ )} + + {/* Studios */} + {moreInfo?.studios && ( +
+

Studios

+
+
+ {moreInfo.studios} +
+
+
+ )} + + {/* Aired Date */} + {moreInfo?.aired && ( +
+

Aired

+
+
+ {moreInfo.aired} +
+
+
+ )} + + {/* Character & Voice Actors */} + {info.characterVoiceActor && info.characterVoiceActor.length > 0 && ( +
+

Characters & Voice Actors

+
+ {info.characterVoiceActor.map((item, index) => ( +
+
+
+
+ {item.character.name} +
+
+

{item.character.name}

+

{item.character.cast}

+
+
+
+
+
+
+ {item.voiceActor.name} +
+
+

{item.voiceActor.name}

+

{item.voiceActor.cast}

+
+
+
+
+ ))} +
+
+ )} +
+
+
+
+ + {/* Seasons Section */} + {renderSeasons()} + + {/* Related Anime Section */} + {renderAnimeCards(relatedAnime, 'Related Anime')} + + {/* Recommendations Section */} + {renderAnimeCards(recommendations, 'You May Also Like')} + + {/* Most Popular Section */} + {renderAnimeCards(mostPopular, 'Most Popular')} +
+
+ ); +} \ No newline at end of file diff --git a/src/components/AnimeFilters.js b/src/components/AnimeFilters.js new file mode 100644 index 0000000..2a7a507 --- /dev/null +++ b/src/components/AnimeFilters.js @@ -0,0 +1,597 @@ +'use client'; + +import { useState, useEffect, useRef } from 'react'; +import { fetchGenres } from '@/lib/api'; +import { ChevronDownIcon, CheckIcon, XMarkIcon } from '@heroicons/react/24/outline'; + +// Helper function to capitalize first letter of each word +const capitalizeFirstLetter = (string) => { + if (!string) return ''; + return string.split(' ').map(word => + word.charAt(0).toUpperCase() + word.slice(1).toLowerCase() + ).join(' '); +}; + +export default function AnimeFilters({ + selectedGenre, + onGenreChange, + yearFilter, + onYearChange, + sortOrder, + onSortChange, + showGenreFilter = true, + searchQuery = '', + onSearchChange, + selectedSeasons = [], + onSeasonChange, + selectedTypes = [], + onTypeChange, + selectedStatus = [], + onStatusChange, + selectedLanguages = [], + onLanguageChange +}) { + const [genres, setGenres] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + const [dropdowns, setDropdowns] = useState({ + genre: false, + season: false, + year: false, + type: false, + status: false, + language: false, + sort: false + }); + const dropdownRefs = useRef({ + genre: null, + season: null, + year: null, + type: null, + status: null, + language: null, + sort: null + }); + + // Available years for filter (current year down to 2000 and 'older') + const currentYear = new Date().getFullYear(); + const years = ['all', ...Array.from({ length: currentYear - 1999 }, (_, i) => (currentYear - i).toString()), 'older']; + + // Seasons data + const seasons = ['Winter', 'Spring', 'Summer', 'Fall']; + + // Types data + const types = ['TV', 'Movie', 'OVA', 'ONA', 'Special']; + + // Status data + const statuses = ['Ongoing', 'Completed', 'Upcoming']; + + // Languages data + const languages = ['Subbed', 'Dubbed', 'Chinese', 'English']; + + // Fetch genres on component mount + useEffect(() => { + const getGenres = async () => { + if (!showGenreFilter) return; + + try { + setIsLoading(true); + const genreData = await fetchGenres(); + // Capitalize each genre + const capitalizedGenres = genreData ? genreData.map(capitalizeFirstLetter) : []; + setGenres(capitalizedGenres); + } catch (error) { + console.error('Error fetching genres:', error); + setError('Failed to load genres. Please try again later.'); + } finally { + setIsLoading(false); + } + }; + + getGenres(); + }, [showGenreFilter]); + + // Toggle dropdown visibility + const toggleDropdown = (dropdown) => { + setDropdowns(prev => { + // Close other dropdowns when opening one + const newState = { + genre: false, + season: false, + year: false, + type: false, + status: false, + language: false, + sort: false, + [dropdown]: !prev[dropdown] + }; + return newState; + }); + }; + + // Initialize refs for each dropdown + useEffect(() => { + dropdownRefs.current = { + genre: dropdownRefs.current.genre, + season: dropdownRefs.current.season, + year: dropdownRefs.current.year, + type: dropdownRefs.current.type, + status: dropdownRefs.current.status, + language: dropdownRefs.current.language, + sort: dropdownRefs.current.sort + }; + }, []); + + // Close all dropdowns when clicking outside + useEffect(() => { + const handleClickOutside = (event) => { + // Check if the click was outside all dropdown containers + let isOutside = true; + Object.keys(dropdownRefs.current).forEach(key => { + if (dropdownRefs.current[key] && dropdownRefs.current[key].contains(event.target)) { + isOutside = false; + } + }); + + if (isOutside) { + setDropdowns({ + genre: false, + season: false, + year: false, + type: false, + status: false, + language: false, + sort: false + }); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + + // Prevent dropdown from closing when selecting an item in multiselect + const keepDropdownOpen = (e, dropdown) => { + e.stopPropagation(); + // Don't toggle the dropdown state on item click for multi-select dropdowns + }; + + const handleClearGenre = (e) => { + e.stopPropagation(); + if (onGenreChange) { + onGenreChange(null); + } + }; + + // Toggle multi-select filter + const handleMultiSelectToggle = (type, value, onChange) => { + if (!onChange) return; + + let updatedSelection; + if (type.includes(value)) { + updatedSelection = type.filter(item => item !== value); + } else { + updatedSelection = [...type, value]; + } + onChange(updatedSelection); + }; + + // Modify the onClick handlers for each button to prevent event propagation + const handleGenreSelect = (e, genre) => { + e.stopPropagation(); + if (onGenreChange) { + onGenreChange(genre); + // Close genre dropdown after selection since it's a single select + setDropdowns(prev => ({ ...prev, genre: false })); + } + }; + + const handleYearSelect = (e, year) => { + e.stopPropagation(); + if (onYearChange) { + onYearChange(year); + // Close year dropdown after selection since it's a single select + setDropdowns(prev => ({ ...prev, year: false })); + } + }; + + const handleSortSelect = (e, sort) => { + e.stopPropagation(); + if (onSortChange) { + onSortChange(sort); + // Close sort dropdown after selection since it's a single select + setDropdowns(prev => ({ ...prev, sort: false })); + } + }; + + const handleMultiSelect = (e, type, value, onChange, dropdown) => { + e.stopPropagation(); + let updatedSelection; + if (type.includes(value)) { + updatedSelection = type.filter(item => item !== value); + } else { + updatedSelection = [...type, value]; + } + + if (onChange) { + onChange(updatedSelection); + // Keep dropdown open for multiselect to allow multiple selections + // Without closing the dropdown + } + }; + + // Add clear filter handlers + const clearAllFilters = (e) => { + e.stopPropagation(); + if (onGenreChange) onGenreChange(null); + if (onYearChange) onYearChange('all'); + if (onSortChange) onSortChange('default'); + if (onSeasonChange) onSeasonChange([]); + if (onTypeChange) onTypeChange([]); + if (onStatusChange) onStatusChange([]); + if (onLanguageChange) onLanguageChange([]); + }; + + const clearGenre = (e) => { + e.stopPropagation(); + if (onGenreChange) onGenreChange(null); + }; + + const clearYear = (e) => { + e.stopPropagation(); + if (onYearChange) onYearChange('all'); + }; + + const clearSort = (e) => { + e.stopPropagation(); + if (onSortChange) onSortChange('default'); + }; + + const clearSeasons = (e) => { + e.stopPropagation(); + if (onSeasonChange) onSeasonChange([]); + }; + + const clearTypes = (e) => { + e.stopPropagation(); + if (onTypeChange) onTypeChange([]); + }; + + const clearStatus = (e) => { + e.stopPropagation(); + if (onStatusChange) onStatusChange([]); + }; + + const clearLanguages = (e) => { + e.stopPropagation(); + if (onLanguageChange) onLanguageChange([]); + }; + + // Get display text for filters + const getYearDisplayText = () => { + if (yearFilter === 'all') return 'Year'; + if (yearFilter === 'older') return 'Before 2000'; + return yearFilter; + }; + + const getSortDisplayText = () => { + switch (sortOrder) { + case 'title-asc': return 'Title (A-Z)'; + case 'title-desc': return 'Title (Z-A)'; + case 'year-desc': return 'Newest First'; + case 'year-asc': return 'Oldest First'; + default: return 'Default'; + } + }; + + // Check if any filter is active + const isAnyFilterActive = () => { + return selectedGenre !== null || + yearFilter !== 'all' || + sortOrder !== 'default' || + selectedSeasons.length > 0 || + selectedTypes.length > 0 || + selectedStatus.length > 0 || + selectedLanguages.length > 0; + }; + + return ( +
+
+ {/* Genre Filter */} +
dropdownRefs.current.genre = el}> + + {dropdowns.genre && ( +
+
+ {genres.map((genre) => ( + + ))} +
+
+ )} +
+ + {/* Year Filter */} +
dropdownRefs.current.year = el}> + + {dropdowns.year && ( +
+
+ {years.map((year) => ( + + ))} +
+
+ )} +
+ + {/* Season Filter */} +
dropdownRefs.current.season = el}> + + {dropdowns.season && ( +
keepDropdownOpen(e, 'season')} className="absolute z-50 w-full mt-2 py-1 bg-[#141414] rounded-lg border border-white/[0.04] shadow-xl"> + {seasons.map((season) => ( + + ))} +
+ )} +
+ + {/* Format Filter */} +
dropdownRefs.current.type = el}> + + {dropdowns.type && ( +
keepDropdownOpen(e, 'type')} className="absolute z-50 w-full mt-2 py-1 bg-[#141414] rounded-lg border border-white/[0.04] shadow-xl"> + {types.map((type) => ( + + ))} +
+ )} +
+ + {/* Status Filter */} +
dropdownRefs.current.status = el}> + + {dropdowns.status && ( +
keepDropdownOpen(e, 'status')} className="absolute z-50 w-full mt-2 py-1 bg-[#141414] rounded-lg border border-white/[0.04] shadow-xl"> + {statuses.map((status) => ( + + ))} +
+ )} +
+ + {/* Language Filter */} +
dropdownRefs.current.language = el}> + + {dropdowns.language && ( +
keepDropdownOpen(e, 'language')} className="absolute z-50 w-full mt-2 py-1 bg-[#141414] rounded-lg border border-white/[0.04] shadow-xl"> + {languages.map((language) => ( + + ))} +
+ )} +
+ + {/* Sort Filter */} +
dropdownRefs.current.sort = el}> + + {dropdowns.sort && ( +
+ + + + + +
+ )} +
+ + {/* Clear All Button - Always visible */} + +
+
+ ); +} \ No newline at end of file diff --git a/src/components/AnimeInfo.js b/src/components/AnimeInfo.js new file mode 100644 index 0000000..c1a0ba5 --- /dev/null +++ b/src/components/AnimeInfo.js @@ -0,0 +1,101 @@ +import Image from 'next/image'; + +export default function AnimeInfo({ anime }) { + return ( +
+
+ {/* Banner Image - You'll need to add bannerImage to your anime object */} +
+
+ {anime.bannerImage ? ( + + ) : ( +
+ )} +
+ + {/* Content */} +
+
+ {/* Cover Image */} +
+
+ {anime.title} +
+
+ + {/* Details */} +
+ {/* Title and Alternative Titles */} +
+

+ {anime.title} +

+ {anime.alternativeTitles && ( +
+ {anime.alternativeTitles.join(' • ')} +
+ )} +
+ + {/* Metadata Grid */} +
+
+
Status
+
{anime.status}
+
+
+
Episodes
+
{anime.totalEpisodes}
+
+
+
Season
+
{anime.season} {anime.year}
+
+
+
Studio
+
{anime.studio}
+
+
+ + {/* Genres */} + {anime.genres && ( +
+
Genres
+
+ {anime.genres.map((genre) => ( + + {genre} + + ))} +
+
+ )} + + {/* Synopsis */} +
+
Synopsis
+
+ {anime.synopsis} +
+
+
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/src/components/AnimeTabs.js b/src/components/AnimeTabs.js new file mode 100644 index 0000000..6414578 --- /dev/null +++ b/src/components/AnimeTabs.js @@ -0,0 +1,85 @@ +'use client'; + +import React, { useState } from 'react'; +import AnimeCard from './AnimeCard'; +import Link from 'next/link'; + +const tabs = [ + { id: 'topAiring', label: 'TOP AIRING' }, + { id: 'popular', label: 'POPULAR' }, + { id: 'latestCompleted', label: 'LATEST COMPLETED' } +]; + +export default function AnimeTabs({ topAiring = [], popular = [], latestCompleted = [] }) { + const [activeTab, setActiveTab] = useState('topAiring'); + + const getActiveList = () => { + switch (activeTab) { + case 'topAiring': + return topAiring; + case 'popular': + return popular; + case 'latestCompleted': + return latestCompleted; + default: + return []; + } + }; + + const getViewAllLink = () => { + switch (activeTab) { + case 'topAiring': + return '/top-airing'; + case 'popular': + return '/most-popular'; + case 'latestCompleted': + return '/latest-completed'; + default: + return '/'; + } + }; + + return ( +
+ {/* Tabs Navigation */} +
+ {tabs.map((tab) => ( + + ))} + + View All + + + + +
+ + {/* Anime Grid */} +
+ {getActiveList().slice(0, 18).map((anime, index) => ( + + ))} +
+
+ ); +} \ No newline at end of file diff --git a/src/components/EpisodeList.js b/src/components/EpisodeList.js new file mode 100644 index 0000000..db29c23 --- /dev/null +++ b/src/components/EpisodeList.js @@ -0,0 +1,220 @@ +import { useState, useMemo, useEffect } from 'react'; + +export default function EpisodeList({ episodes, currentEpisode, onEpisodeClick, isDub = false }) { + const [currentPage, setCurrentPage] = useState(1); + const [isGridView, setIsGridView] = useState(false); + const [searchQuery, setSearchQuery] = useState(''); + const [activeEpisodeId, setActiveEpisodeId] = useState(null); + const episodesPerPage = 100; + + // Update active episode when currentEpisode changes + useEffect(() => { + if (currentEpisode?.episodeId) { + setActiveEpisodeId(currentEpisode.episodeId); + } + }, [currentEpisode]); + + // Sync with URL to identify current episode + useEffect(() => { + const checkCurrentEpisode = () => { + const path = window.location.pathname; + const match = path.match(/\/watch\/(.+)$/); + if (match) { + const episodeId = match[1]; + setActiveEpisodeId(episodeId); + + // Find the episode and update page + const episode = episodes.find(ep => ep.episodeId === episodeId); + if (episode) { + const pageNumber = Math.ceil(episode.number / episodesPerPage); + setCurrentPage(pageNumber); + } + } + }; + + checkCurrentEpisode(); + }, [episodes, window.location.pathname]); + + const filteredEpisodes = useMemo(() => { + if (!searchQuery) return episodes; + const query = searchQuery.toLowerCase(); + return episodes.filter(episode => + episode.number.toString().includes(query) || + (episode.title && episode.title.toLowerCase().includes(query)) + ); + }, [episodes, searchQuery]); + + const totalPages = Math.ceil(filteredEpisodes.length / episodesPerPage); + const indexOfLastEpisode = currentPage * episodesPerPage; + const indexOfFirstEpisode = indexOfLastEpisode - episodesPerPage; + const currentEpisodes = filteredEpisodes.slice(indexOfFirstEpisode, indexOfLastEpisode); + + const getPageRange = (pageNum) => { + const start = (pageNum - 1) * episodesPerPage + 1; + const end = Math.min(pageNum * episodesPerPage, filteredEpisodes.length); + return `${start}-${end}`; + }; + + const isCurrentEpisode = (episode) => { + return episode.episodeId === activeEpisodeId; + }; + + const handleEpisodeSelect = (episode, e) => { + e.preventDefault(); + if (onEpisodeClick) { + onEpisodeClick(episode.episodeId); + } + setActiveEpisodeId(episode.episodeId); + }; + + // Scroll active episode into view when page changes or active episode changes + useEffect(() => { + if (activeEpisodeId) { + const activeElement = document.querySelector(`[data-episode-id="${activeEpisodeId}"]`); + if (activeElement) { + activeElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + } + } + }, [activeEpisodeId, currentPage]); + + return ( +
+ {/* Header */} +
+
+
+ { + setSearchQuery(e.target.value); + setCurrentPage(1); + }} + className="w-full bg-[#2a2a2a] text-white text-sm rounded-lg px-4 py-1.5 pl-9 focus:outline-none focus:ring-2 focus:ring-[var(--primary)] placeholder-gray-500" + /> + + + +
+
+ + +
+
+
+ + {/* Episodes Container */} +
+
+ {isGridView ? ( + // Grid View +
+ {currentEpisodes.map((episode) => ( + + ))} +
+ ) : ( + // List View +
+ {currentEpisodes.map((episode) => ( + + ))} +
+ )} +
+
+
+ ); +} \ No newline at end of file diff --git a/src/components/GenreBar.js b/src/components/GenreBar.js new file mode 100644 index 0000000..f6f2a18 --- /dev/null +++ b/src/components/GenreBar.js @@ -0,0 +1,230 @@ +'use client'; + +import Link from 'next/link'; +import { useState, useEffect, useRef } from 'react'; +import { fetchGenres } from '@/lib/api'; + +export default function GenreBar() { + const [genres, setGenres] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [showLeftButton, setShowLeftButton] = useState(true); // Always show left button initially + const [showRightButton, setShowRightButton] = useState(true); + const [isMobile, setIsMobile] = useState(false); + const scrollContainerRef = useRef(null); + const containerRef = useRef(null); + const [visibleGenres, setVisibleGenres] = useState(14); + + // Function to capitalize first letter + const capitalizeFirstLetter = (string) => { + return string.charAt(0).toUpperCase() + string.slice(1); + }; + + // Predefined genres exactly as specified + const defaultGenres = [ + "Action", "Adventure", "Comedy", "Drama", "Ecchi", "Fantasy", + "Horror", "Mahou Shoujo", "Mecha", "Music", "Mystery", "Psychological", + "Romance", "Sci-Fi", "Slice of Life", "Sports", "Supernatural", "Thriller" + ]; + + // Handle long names on mobile + const getMobileGenreName = (genre) => { + // Abbreviate long genre names for mobile view + switch(genre) { + case "Psychological": return "Psycho"; + case "Mahou Shoujo": return "Mahou"; + case "Supernatural": return "Super"; + case "Slice of Life": return "SoL"; + default: return genre; + } + }; + + // Detect mobile devices + useEffect(() => { + const checkIfMobile = () => { + setIsMobile(window.innerWidth < 768); + }; + + checkIfMobile(); + window.addEventListener('resize', checkIfMobile); + + return () => window.removeEventListener('resize', checkIfMobile); + }, []); + + // Calculate the number of genres that fit in the container + useEffect(() => { + const calculateVisibleGenres = () => { + const container = containerRef.current; + if (container) { + const containerWidth = container.offsetWidth; + // Approximate width of each genre button + const genreButtonWidth = isMobile ? 72 : 88; // Slightly larger on mobile to fit text + const visibleCount = Math.floor((containerWidth - 80) / genreButtonWidth); + // Minimum genres visible (smaller minimum on mobile) + setVisibleGenres(Math.max(visibleCount, isMobile ? 4 : 8)); + } + }; + + calculateVisibleGenres(); + window.addEventListener('resize', calculateVisibleGenres); + + return () => { + window.removeEventListener('resize', calculateVisibleGenres); + }; + }, [isMobile]); + + useEffect(() => { + // Force scroll position slightly to the right initially + // to ensure there are genres on both sides for scrolling + setTimeout(() => { + if (scrollContainerRef.current) { + scrollContainerRef.current.scrollLeft = 40; // Start slightly scrolled + // Trigger scroll event to update button states + scrollContainerRef.current.dispatchEvent(new Event('scroll')); + } + }, 100); + + setGenres(defaultGenres); + setIsLoading(false); + }, []); + + // Check scroll position to determine button visibility + useEffect(() => { + const handleScroll = () => { + if (scrollContainerRef.current) { + const { scrollLeft, scrollWidth, clientWidth } = scrollContainerRef.current; + + // Show left button if not at the start + setShowLeftButton(scrollLeft > 5); + + // Show right button if not at the end + setShowRightButton(scrollLeft < scrollWidth - clientWidth - 5); + } + }; + + const scrollContainer = scrollContainerRef.current; + if (scrollContainer) { + scrollContainer.addEventListener('scroll', handleScroll); + // Initial check + handleScroll(); + + return () => { + scrollContainer.removeEventListener('scroll', handleScroll); + }; + } + }, []); + + // Scroll left/right functions + const scrollLeft = () => { + if (scrollContainerRef.current) { + const scrollAmount = isMobile ? -80 : -200; + scrollContainerRef.current.scrollBy({ left: scrollAmount, behavior: 'smooth' }); + } + }; + + const scrollRight = () => { + if (scrollContainerRef.current) { + const scrollAmount = isMobile ? 80 : 200; + scrollContainerRef.current.scrollBy({ left: scrollAmount, behavior: 'smooth' }); + } + }; + + // Mobile-specific styles + const mobileButtonStyle = { + padding: '0.15rem 0.5rem', + fontSize: '0.65rem', + height: '1.5rem', + minWidth: '4rem', + maxWidth: '5.5rem', + textAlign: 'center', + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }; + + if (isLoading) { + return ( +
+
+ {[...Array(isMobile ? 5 : visibleGenres)].map((_, i) => ( +
+ ))} +
+
+ ); + } + + return ( +
+ {/* Left fade effect */} +
+
+ + {/* Left scroll button - only visible when not at the leftmost position */} + {showLeftButton && ( + + )} + + {/* Scrollable genre container */} +
+
+ {genres.map((genre) => ( + + {isMobile ? getMobileGenreName(genre) : genre} + + ))} +
+
+ + {/* Right fade effect */} +
+
+ + {/* Right scroll button - only visible when not at the rightmost position */} + {showRightButton && ( + + )} +
+ ); +} \ No newline at end of file diff --git a/src/components/GenreList.js b/src/components/GenreList.js new file mode 100644 index 0000000..f0c5e71 --- /dev/null +++ b/src/components/GenreList.js @@ -0,0 +1,93 @@ +'use client'; + +import Link from 'next/link'; +import { useState, useEffect } from 'react'; +import { fetchGenres } from '@/lib/api'; + +export default function GenreList() { + const [genres, setGenres] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [showAll, setShowAll] = useState(false); + + useEffect(() => { + async function loadGenres() { + try { + const genreData = await fetchGenres(); + setGenres(genreData || []); + } catch (error) { + console.error("Error fetching genres:", error); + } finally { + setIsLoading(false); + } + } + + loadGenres(); + }, []); + + // Predefined popular genres if API doesn't return them + const defaultGenres = [ + "Action", "Adventure", "Comedy", "Drama", "Fantasy", + "Horror", "Mystery", "Romance", "Sci-Fi", "Slice of Life", + "Supernatural", "Thriller", "Isekai", "Mecha", "Sports" + ]; + + // Use fetched genres or fallback to default genres + const displayGenres = genres.length > 0 ? genres : defaultGenres; + + // Show only first 12 genres if not showing all + const visibleGenres = showAll ? displayGenres : displayGenres.slice(0, 12); + + if (isLoading) { + return ( +
+
+

Genres

+
+
+ {[...Array(12)].map((_, i) => ( +
+ ))} +
+
+ ); + } + + return ( +
+
+

Genres

+
+ +
+ {visibleGenres.map((genre) => ( + + {genre} + + ))} +
+ + {displayGenres.length > 12 && ( +
+ +
+ )} +
+ ); +} \ No newline at end of file diff --git a/src/components/Navbar.js b/src/components/Navbar.js new file mode 100644 index 0000000..d2b6282 --- /dev/null +++ b/src/components/Navbar.js @@ -0,0 +1,703 @@ +'use client'; + +import Link from 'next/link'; +import { useState, useEffect, useRef } from 'react'; +import { useRouter } from 'next/navigation'; +import { + fetchSearchSuggestions, + fetchMostPopular, + fetchTopAiring, + fetchRecentEpisodes, + fetchMostFavorite, + fetchTopUpcoming +} from '@/lib/api'; + +export default function Navbar() { + const [isMenuOpen, setIsMenuOpen] = useState(false); + const [searchQuery, setSearchQuery] = useState(''); + const [searchSuggestions, setSearchSuggestions] = useState([]); + const [showSuggestions, setShowSuggestions] = useState(false); + const [isScrolled, setIsScrolled] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [isRandomLoading, setIsRandomLoading] = useState(false); + const suggestionRef = useRef(null); + const searchInputRef = useRef(null); + const router = useRouter(); + + // Enhanced fallback suggestions with popular anime + const fallbackSuggestions = [ + { + id: "naruto", + title: "Naruto", + image: "https://cdn.myanimelist.net/images/anime/13/17405.jpg", + type: "TV", + year: 2002, + episodes: 220, + rating: 79 + }, + { + id: "one-piece", + title: "One Piece", + image: "https://cdn.myanimelist.net/images/anime/6/73245.jpg", + type: "TV", + year: 1999, + episodes: 1000, + rating: 85 + }, + { + id: "attack-on-titan", + title: "Attack on Titan", + image: "https://cdn.myanimelist.net/images/anime/10/47347.jpg", + type: "TV", + year: 2013, + episodes: 75, + rating: 90 + }, + { + id: "demon-slayer", + title: "Demon Slayer", + image: "https://cdn.myanimelist.net/images/anime/1286/99889.jpg", + type: "TV", + year: 2019, + episodes: 26, + rating: 86 + }, + { + id: "my-hero-academia", + title: "My Hero Academia", + image: "https://cdn.myanimelist.net/images/anime/10/78745.jpg", + type: "TV", + year: 2016, + episodes: 113, + rating: 82 + }, + { + id: "jujutsu-kaisen", + title: "Jujutsu Kaisen", + image: "https://cdn.myanimelist.net/images/anime/1171/109222.jpg", + type: "TV", + year: 2020, + episodes: 24, + rating: 88 + }, + { + id: "tokyo-revengers", + title: "Tokyo Revengers", + image: "https://cdn.myanimelist.net/images/anime/1839/122012.jpg", + type: "TV", + year: 2021, + episodes: 24, + rating: 80 + }, + { + id: "death-note", + title: "Death Note", + image: "https://cdn.myanimelist.net/images/anime/9/9453.jpg", + type: "TV", + year: 2006, + episodes: 37, + rating: 90 + } + ]; + + // Track scroll position + useEffect(() => { + const handleScroll = () => { + if (window.scrollY > 10) { + setIsScrolled(true); + } else { + setIsScrolled(false); + } + }; + + window.addEventListener('scroll', handleScroll); + return () => window.removeEventListener('scroll', handleScroll); + }, []); + + // Update suggestions when search query changes + useEffect(() => { + const updateSuggestions = async () => { + // Only search if we have at least 2 characters + if (searchQuery.trim().length >= 2) { + setIsLoading(true); + setShowSuggestions(true); // Always show the suggestions container when typing + + try { + console.log(`Fetching suggestions for: ${searchQuery}`); + const apiSuggestions = await fetchSearchSuggestions(searchQuery); + console.log('API returned:', apiSuggestions); + + if (Array.isArray(apiSuggestions) && apiSuggestions.length > 0) { + // Take top 5 results + setSearchSuggestions(apiSuggestions.slice(0, 5)); + } else { + // Use fallback with pattern matching + getFilteredFallbackSuggestions(searchQuery); + } + } catch (error) { + console.error('Error in search component:', error); + // Use fallback with pattern matching + getFilteredFallbackSuggestions(searchQuery); + } finally { + setIsLoading(false); + } + } else { + setSearchSuggestions([]); + setShowSuggestions(false); + } + }; + + // Helper function to get filtered fallback suggestions + const getFilteredFallbackSuggestions = (query) => { + const filtered = fallbackSuggestions.filter(anime => + anime.title.toLowerCase().includes(query.toLowerCase()) + ); + + if (filtered.length > 0) { + setSearchSuggestions(filtered.slice(0, 5)); + } else { + // Create a generic suggestion based on the search query + setSearchSuggestions([{ + id: query.toLowerCase().replace(/\s+/g, '-'), + title: `Search for "${query}"`, + type: "SEARCH" + }]); + } + }; + + const debounceTimer = setTimeout(() => { + updateSuggestions(); + }, 300); // 300ms debounce time + + return () => clearTimeout(debounceTimer); + }, [searchQuery]); + + // Close suggestions when clicking outside + useEffect(() => { + const handleClickOutside = (event) => { + if ( + suggestionRef.current && + !suggestionRef.current.contains(event.target) && + !searchInputRef.current?.contains(event.target) + ) { + setShowSuggestions(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + + const handleSearch = (e) => { + e.preventDefault(); + // Navigate to search page regardless if search is empty or not + router.push(searchQuery.trim() ? `/search?q=${encodeURIComponent(searchQuery)}` : '/search'); + setSearchQuery(''); + setShowSuggestions(false); + setIsMenuOpen(false); + }; + + // Handle suggestion item click + const handleAnimeClick = (id) => { + router.push(`/anime/${id}`); + setSearchQuery(''); + setShowSuggestions(false); + setIsMenuOpen(false); + }; + + // Handle search by query click + const handleSearchByQueryClick = () => { + router.push(`/search?q=${encodeURIComponent(searchQuery)}`); + setSearchQuery(''); + setShowSuggestions(false); + setIsMenuOpen(false); + }; + + // Helper function to render clear button + const renderClearButton = () => { + if (searchQuery) { + return ( + + ); + } + return null; + }; + + // Function to handle input focus + const handleInputFocus = () => { + if (searchQuery.trim().length >= 2) { + setShowSuggestions(true); + } + }; + + // Function to handle random anime click + const handleRandomAnimeClick = async () => { + setIsRandomLoading(true); + try { + // Randomly select a category to fetch from + const categories = [ + { name: 'Most Popular', fetch: fetchMostPopular }, + { name: 'Top Airing', fetch: fetchTopAiring }, + { name: 'Recent Episodes', fetch: fetchRecentEpisodes }, + { name: 'Most Favorite', fetch: fetchMostFavorite }, + { name: 'Top Upcoming', fetch: fetchTopUpcoming } + ]; + + // Select a random category + const randomCategoryIndex = Math.floor(Math.random() * categories.length); + const selectedCategory = categories[randomCategoryIndex]; + + console.log(`Fetching random anime from: ${selectedCategory.name}`); + + // Fetch anime from the selected category - use a random page number to get more variety + const randomPage = Math.floor(Math.random() * 5) + 1; // Random page between 1-5 + const animeList = await selectedCategory.fetch(randomPage); + + if (animeList && animeList.results && animeList.results.length > 0) { + // Skip the first few results as they tend to be more popular + const skipCount = Math.min(5, Math.floor(animeList.results.length / 3)); + let availableAnime = animeList.results.slice(skipCount); + + if (availableAnime.length === 0) { + // If we've filtered out everything, use the original list + availableAnime = animeList.results; + } + + // Get a random index + const randomAnimeIndex = Math.floor(Math.random() * availableAnime.length); + + // Get the random anime ID + const randomAnimeId = availableAnime[randomAnimeIndex].id; + + console.log(`Selected random anime: ${availableAnime[randomAnimeIndex].title} (ID: ${randomAnimeId})`); + + // Navigate to the anime page + router.push(`/anime/${randomAnimeId}`); + } else { + console.error('No anime found to select randomly from'); + + // Fallback to most popular if the chosen category fails, but use a higher page number + const fallbackPage = Math.floor(Math.random() * 5) + 2; // Pages 2-6 for more obscure options + const fallbackList = await fetchMostPopular(fallbackPage); + + if (fallbackList && fallbackList.results && fallbackList.results.length > 0) { + const randomIndex = Math.floor(Math.random() * fallbackList.results.length); + const randomAnimeId = fallbackList.results[randomIndex].id; + router.push(`/anime/${randomAnimeId}`); + } + } + } catch (error) { + console.error('Error fetching random anime:', error); + } finally { + setIsRandomLoading(false); + } + }; + + return ( + + ); +} \ No newline at end of file diff --git a/src/components/SharedLayout.js b/src/components/SharedLayout.js new file mode 100644 index 0000000..8731c1e --- /dev/null +++ b/src/components/SharedLayout.js @@ -0,0 +1,85 @@ +'use client'; + +import Navbar from './Navbar'; + +const Footer = () => { + return ( + + ); +}; + +export default function SharedLayout({ children }) { + return ( + <> + +
+ {children} +
+