From 65ff18792570d3498cdafbe050c89ba9965832c3 Mon Sep 17 00:00:00 2001 From: the-ghost-bot Date: Thu, 21 Nov 2024 06:59:55 +0100 Subject: [PATCH] first --- .gitignore | 24 + bun.lockb | Bin 0 -> 75043 bytes index.html | 12 + package-lock.json | 2929 ++++++++++++++++++++++++++ package.json | 33 + postcss.config.js | 6 + public/CNAME | 1 + src/App.tsx | 305 +++ src/components/ContentTable.tsx | 186 ++ src/components/ErrorBoundary.tsx | 85 + src/components/ErrorLog.tsx | 90 + src/components/GameDetails.tsx | 175 ++ src/components/Header.tsx | 83 + src/components/Pagination.tsx | 100 + src/components/ScreenshotGallery.tsx | 51 + src/components/SearchBar.tsx | 43 + src/components/Settings.tsx | 274 +++ src/components/StatsCard.tsx | 28 + src/components/TabNavigation.tsx | 42 + src/hooks/useGameInfo.ts | 52 + src/hooks/useSearch.ts | 132 ++ src/index.css | 48 + src/main.tsx | 13 + src/services/gameData.ts | 227 ++ src/services/statsService.ts | 21 + src/store/userPreferences.ts | 101 + src/types/index.ts | 53 + src/utils/cache.ts | 52 + src/utils/contentGrouping.ts | 66 + src/utils/formatters.ts | 37 + src/utils/logger.ts | 70 + tailwind.config.js | 24 + tsconfig.json | 25 + tsconfig.node.json | 10 + vite.config.ts | 11 + 35 files changed, 5409 insertions(+) create mode 100644 .gitignore create mode 100644 bun.lockb create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 postcss.config.js create mode 100644 public/CNAME create mode 100644 src/App.tsx create mode 100644 src/components/ContentTable.tsx create mode 100644 src/components/ErrorBoundary.tsx create mode 100644 src/components/ErrorLog.tsx create mode 100644 src/components/GameDetails.tsx create mode 100644 src/components/Header.tsx create mode 100644 src/components/Pagination.tsx create mode 100644 src/components/ScreenshotGallery.tsx create mode 100644 src/components/SearchBar.tsx create mode 100644 src/components/Settings.tsx create mode 100644 src/components/StatsCard.tsx create mode 100644 src/components/TabNavigation.tsx create mode 100644 src/hooks/useGameInfo.ts create mode 100644 src/hooks/useSearch.ts create mode 100644 src/index.css create mode 100644 src/main.tsx create mode 100644 src/services/gameData.ts create mode 100644 src/services/statsService.ts create mode 100644 src/store/userPreferences.ts create mode 100644 src/types/index.ts create mode 100644 src/utils/cache.ts create mode 100644 src/utils/contentGrouping.ts create mode 100644 src/utils/formatters.ts create mode 100644 src/utils/logger.ts create mode 100644 tailwind.config.js create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json create mode 100644 vite.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..54f07af --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? \ No newline at end of file diff --git a/bun.lockb b/bun.lockb new file mode 100644 index 0000000000000000000000000000000000000000..6c9089d4bb9bf077b3d20ac104d172361b8de6f7 GIT binary patch literal 75043 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 + + + + + NX Working + + +
+ + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..59b6973 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2929 @@ +{ + "name": "nx-working", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "nx-working", + "version": "1.0.0", + "dependencies": { + "@rollup/rollup-win32-x64-msvc": "^4.27.3", + "clsx": "^2.1.0", + "fuse.js": "^7.0.0", + "lucide-react": "^0.344.0", + "nx-working": "file:", + "photoswipe": "^5.4.3", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-photoswipe-gallery": "^3.0.1", + "zustand": "^4.5.0" + }, + "devDependencies": { + "@types/react": "^18.2.56", + "@types/react-dom": "^18.2.19", + "@vitejs/plugin-react": "^4.2.1", + "autoprefixer": "^10.4.18", + "postcss": "^8.4.35", + "tailwindcss": "^3.4.1", + "typescript": "^5.2.2", + "vite": "^5.1.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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", + "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.26.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", + "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", + "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "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, + "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, + "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, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.3.tgz", + "integrity": "sha512-EzxVSkIvCFxUd4Mgm4xR9YXrcp976qVaHnqom/Tgm+vU79k4vV4eYTjmRvGfeoW8m9LVcsAy/lGjcgVegKEhLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.3.tgz", + "integrity": "sha512-LJc5pDf1wjlt9o/Giaw9Ofl+k/vLUaYsE2zeQGH85giX2F+wn/Cg8b3c5CDP3qmVmeO5NzwVUzQQxwZvC2eQKw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.3.tgz", + "integrity": "sha512-OuRysZ1Mt7wpWJ+aYKblVbJWtVn3Cy52h8nLuNSzTqSesYw1EuN6wKp5NW/4eSre3mp12gqFRXOKTcN3AI3LqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.3.tgz", + "integrity": "sha512-xW//zjJMlJs2sOrCmXdB4d0uiilZsOdlGQIC/jjmMWT47lkLLoB1nsNhPUcnoqyi5YR6I4h+FjBpILxbEy8JRg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.3.tgz", + "integrity": "sha512-58E0tIcwZ+12nK1WiLzHOD8I0d0kdrY/+o7yFVPRHuVGY3twBwzwDdTIBGRxLmyjciMYl1B/U515GJy+yn46qw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.3.tgz", + "integrity": "sha512-78fohrpcVwTLxg1ZzBMlwEimoAJmY6B+5TsyAZ3Vok7YabRBUvjYTsRXPTjGEvv/mfgVBepbW28OlMEz4w8wGA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.3.tgz", + "integrity": "sha512-h2Ay79YFXyQi+QZKo3ISZDyKaVD7uUvukEHTOft7kh00WF9mxAaxZsNs3o/eukbeKuH35jBvQqrT61fzKfAB/Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.3.tgz", + "integrity": "sha512-Sv2GWmrJfRY57urktVLQ0VKZjNZGogVtASAgosDZ1aUB+ykPxSi3X1nWORL5Jk0sTIIwQiPH7iE3BMi9zGWfkg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.3.tgz", + "integrity": "sha512-FPoJBLsPW2bDNWjSrwNuTPUt30VnfM8GPGRoLCYKZpPx0xiIEdFip3dH6CqgoT0RnoGXptaNziM0WlKgBc+OWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.3.tgz", + "integrity": "sha512-TKxiOvBorYq4sUpA0JT+Fkh+l+G9DScnG5Dqx7wiiqVMiRSkzTclP35pE6eQQYjP4Gc8yEkJGea6rz4qyWhp3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.3.tgz", + "integrity": "sha512-v2M/mPvVUKVOKITa0oCFksnQQ/TqGrT+yD0184/cWHIu0LoIuYHwox0Pm3ccXEz8cEQDLk6FPKd1CCm+PlsISw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.3.tgz", + "integrity": "sha512-LdrI4Yocb1a/tFVkzmOE5WyYRgEBOyEhWYJe4gsDWDiwnjYKjNs7PS6SGlTDB7maOHF4kxevsuNBl2iOcj3b4A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.3.tgz", + "integrity": "sha512-d4wVu6SXij/jyiwPvI6C4KxdGzuZOvJ6y9VfrcleHTwo68fl8vZC5ZYHsCVPUi4tndCfMlFniWgwonQ5CUpQcA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.3.tgz", + "integrity": "sha512-/6bn6pp1fsCGEY5n3yajmzZQAh+mW4QPItbiWxs69zskBzJuheb3tNynEjL+mKOsUSFK11X4LYF2BwwXnzWleA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.3.tgz", + "integrity": "sha512-nBXOfJds8OzUT1qUreT/en3eyOXd2EH5b0wr2bVB5999qHdGKkzGzIyKYaKj02lXk6wpN71ltLIaQpu58YFBoQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.3.tgz", + "integrity": "sha512-ogfbEVQgIZOz5WPWXF2HVb6En+kWzScuxJo/WdQTqEgeyGkaa2ui5sQav9Zkr7bnNCLK48uxmmK0TySm22eiuw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.3.tgz", + "integrity": "sha512-ecE36ZBMLINqiTtSNQ1vzWc5pXLQHlf/oqGp/bSbi7iedcjcNb6QbCBNG73Euyy2C+l/fn8qKWEwxr+0SSfs3w==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.3.tgz", + "integrity": "sha512-vliZLrDmYKyaUoMzEbMTg2JkerfBjn03KmAw9CykO0Zzkzoyd7o3iZNam/TpyWNjNT+Cz2iO3P9Smv2wgrR+Eg==", + "cpu": [ + "x64" + ], + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/@types/prop-types": { + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", + "devOptional": true + }, + "node_modules/@types/react": { + "version": "18.3.12", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", + "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", + "devOptional": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.3.tgz", + "integrity": "sha512-NooDe9GpHGqNns1i8XDERg0Vsg5SSYRhRxxyTGogUdkdNt47jal+fbuYi+Yfq6pzRCKXyoPcWisfxE6RIM3GKA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/plugin-transform-react-jsx-self": "^7.24.7", + "@babel/plugin-transform-react-jsx-source": "^7.24.7", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "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" + } + ], + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "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 + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "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, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "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" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001682", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001682.tgz", + "integrity": "sha512-rJFwz3yRO6NU6Y8aEJKPzS4fngOE8j05pd33FW5Uk9v9b5StWNhGFeVpogwS2FFl78wNDGW5NsVvlwySPEDU5w==", + "dev": true, + "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" + } + ] + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/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, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true, + "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==", + "dev": true + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "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, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "devOptional": true + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.5.63", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.63.tgz", + "integrity": "sha512-ddeXKuY9BHo/mw145axlyWjlJ1UBt4WK3AlvkT7W2AbqfRQoacVoRUCF6wL3uIx/8wT9oLKXzI+rFqHHscByaA==", + "dev": true + }, + "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 + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "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, + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "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, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "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, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "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, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/fuse.js": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.0.0.tgz", + "integrity": "sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==", + "engines": { + "node": ">=10" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "dependencies": { + "hasown": "^2.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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "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==" + }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "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==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.344.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.344.0.tgz", + "integrity": "sha512-6YyBnn91GB45VuVT96bYCOKElbJzUHqp65vX8cDcu55MQL9T969v4dhGClpljamuI/+KMO9P6w9Acq1CVQGvIQ==", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, + "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, + "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, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "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, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "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 + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nx-working": { + "resolved": "", + "link": true + }, + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "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, + "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 + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/photoswipe": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/photoswipe/-/photoswipe-5.4.4.tgz", + "integrity": "sha512-WNFHoKrkZNnvFFhbHL93WDkW3ifwVOXSW3w1UuZZelSmgXpIGiZSNlZJq37rR8YejqME2rHs9EhH9ZvlvFH2NA==", + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "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" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "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 + }, + "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==", + "peer": true, + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "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" + } + ] + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "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==", + "peer": true + }, + "node_modules/react-photoswipe-gallery": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/react-photoswipe-gallery/-/react-photoswipe-gallery-3.0.2.tgz", + "integrity": "sha512-TLkpzp2BSUUL/4GPRU5SQWXfJ8xuUBKgS8qUaHyhsT1co6CStr1mVCl4oQrSSFbWxAKhB5fHbr12l1R+TkqFcQ==", + "peerDependencies": { + "photoswipe": ">= 5.2.2", + "prop-types": ">= 15.7.0", + "react": ">= 16.8.0" + } + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "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/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.27.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.27.3.tgz", + "integrity": "sha512-SLsCOnlmGt9VoZ9Ek8yBK8tAdmPHeppkw+Xa7yDlCEhDTvwYei03JlWo1fdc7YTfLZ4tD8riJCUyAgTbszk1fQ==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.27.3", + "@rollup/rollup-android-arm64": "4.27.3", + "@rollup/rollup-darwin-arm64": "4.27.3", + "@rollup/rollup-darwin-x64": "4.27.3", + "@rollup/rollup-freebsd-arm64": "4.27.3", + "@rollup/rollup-freebsd-x64": "4.27.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.27.3", + "@rollup/rollup-linux-arm-musleabihf": "4.27.3", + "@rollup/rollup-linux-arm64-gnu": "4.27.3", + "@rollup/rollup-linux-arm64-musl": "4.27.3", + "@rollup/rollup-linux-powerpc64le-gnu": "4.27.3", + "@rollup/rollup-linux-riscv64-gnu": "4.27.3", + "@rollup/rollup-linux-s390x-gnu": "4.27.3", + "@rollup/rollup-linux-x64-gnu": "4.27.3", + "@rollup/rollup-linux-x64-musl": "4.27.3", + "@rollup/rollup-win32-arm64-msvc": "4.27.3", + "@rollup/rollup-win32-ia32-msvc": "4.27.3", + "@rollup/rollup-win32-x64-msvc": "4.27.3", + "fsevents": "~2.3.2" + } + }, + "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" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "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, + "bin": { + "semver": "bin/semver.js" + } + }, + "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, + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "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, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.15.tgz", + "integrity": "sha512-r4MeXnfBmSOuKUWmXe6h2CcyfzJCEk4F0pptO5jlnYSIViUkVmsawj80N5h2lO3gwcmSb4n3PuN+e+GC1Guylw==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "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, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "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" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", + "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/vite": { + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", + "dev": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "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, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz", + "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/zustand": { + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.5.tgz", + "integrity": "sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==", + "dependencies": { + "use-sync-external-store": "1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1e27d5f --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "nx-working", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@rollup/rollup-win32-x64-msvc": "^4.27.3", + "clsx": "^2.1.0", + "fuse.js": "^7.0.0", + "lucide-react": "^0.344.0", + "nx-working": "file:", + "photoswipe": "^5.4.3", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-photoswipe-gallery": "^3.0.1", + "zustand": "^4.5.0" + }, + "devDependencies": { + "@types/react": "^18.2.56", + "@types/react-dom": "^18.2.19", + "@vitejs/plugin-react": "^4.2.1", + "autoprefixer": "^10.4.18", + "postcss": "^8.4.35", + "tailwindcss": "^3.4.1", + "typescript": "^5.2.2", + "vite": "^5.1.4" + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..fbe14a4 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; \ No newline at end of file diff --git a/public/CNAME b/public/CNAME new file mode 100644 index 0000000..9e813ef --- /dev/null +++ b/public/CNAME @@ -0,0 +1 @@ +nx-working.ghostland.at \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..25f701d --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,305 @@ +import { useEffect, useState } from 'react'; +import { ContentItem } from './types'; +import { ContentTable } from './components/ContentTable'; +import { TabNavigation } from './components/TabNavigation'; +import { StatsCard } from './components/StatsCard'; +import { Header } from './components/Header'; +import { SearchBar } from './components/SearchBar'; +import { logger } from './utils/logger'; +import { useSearch } from './hooks/useSearch'; +import { useUserPreferences } from './store/userPreferences'; + +interface WorkingJsonItem { + "Game Name": string; + "Version": string; + "Size": number; +} + +interface WorkingJson { + [key: string]: WorkingJsonItem; +} + +interface VersionsJson { + [titleId: string]: { + [version: string]: string; + }; +} + +function parseDate(dateStr: string | undefined): number { + if (!dateStr) return 0; + try { + const date = new Date(dateStr); + return isNaN(date.getTime()) ? 0 : date.getTime(); + } catch { + return 0; + } +} + +function getUpdateReleaseDate(titleId: string, version: string, versionsJson: VersionsJson): string | undefined { + const baseId = titleId.slice(0, -3) + '000'; + const versionInfo = versionsJson[baseId]; + + if (versionInfo) { + const versionParts = version.split('.').map(Number); + let versionNumber = 65536; + + if (versionParts.length >= 3) { + versionNumber = versionParts[0] * 65536 + versionParts[1] * 256 + versionParts[2]; + } + + return versionInfo[versionNumber.toString()]; + } + + return undefined; +} + +function processUpdates(baseId: string, versionsJson: VersionsJson): ContentItem[] { + const updates: ContentItem[] = []; + const versionInfo = versionsJson[baseId]; + + if (versionInfo) { + const updateId = baseId.slice(0, -3) + '800'; + + Object.entries(versionInfo).forEach(([versionNumber, releaseDate]) => { + const version = parseInt(versionNumber); + const major = Math.floor(version / 65536); + const minor = Math.floor((version % 65536) / 256); + const patch = version % 256; + + updates.push({ + id: updateId, + uniqueId: `${updateId}_${version}`, + type: 'update', + version: `${major}.${minor}.${patch}`, + releaseDate, + name: undefined, + size: undefined + }); + }); + } + + return updates; +} + +function sortByReleaseDate(items: ContentItem[]): ContentItem[] { + return [...items].sort((a, b) => { + const dateA = parseDate(a.releaseDate); + const dateB = parseDate(b.releaseDate); + + // Always put items without dates at the end + if (!dateA && !dateB) return 0; + if (!dateA) return 1; + if (!dateB) return -1; + + // Sort by date + if (dateA !== dateB) { + return dateB - dateA; // Descending order + } + + // If dates are equal, sort by name as secondary criteria + const nameA = a.name || 'Unknown Title'; + const nameB = b.name || 'Unknown Title'; + return nameA.localeCompare(nameB); + }); +} + +export default function App() { + const { isDark, setDarkMode, itemsPerPage, lastActiveTab, setLastActiveTab, dataSources } = useUserPreferences(); + const [items, setItems] = useState([]); + const [loading, setLoading] = useState(true); + const [currentPage, setCurrentPage] = useState(1); + const { query, setQuery, results, sortField, sortDirection, toggleSort } = useSearch(items); + + useEffect(() => { + const loadData = async () => { + try { + setLoading(true); + logger.info('Starting data load'); + + // Fetch all data sources in parallel + const [workingResponse, titlesResponse, workingJsonResponse, versionsResponse] = await Promise.all([ + fetch(dataSources.workingContent), + fetch(dataSources.titlesDb), + fetch('https://raw.githubusercontent.com/ghost-land/NX-Missing/refs/heads/main/data/working.json'), + fetch('https://raw.githubusercontent.com/blawar/titledb/refs/heads/master/versions.json') + ]); + + const [workingText, titlesText] = await Promise.all([ + workingResponse.text(), + titlesResponse.text() + ]); + + const [workingJson, versionsJson]: [WorkingJson, VersionsJson] = await Promise.all([ + workingJsonResponse.json(), + versionsResponse.json() + ]); + + // Process titles database + const titlesMap = new Map(titlesText.trim().split('\n').map(line => { + const [id, date, name, size] = line.split('|'); + return [id, { date, name, size: parseInt(size) }]; + })); + + // Process working content + const processedItems = new Map(); + const baseTitleIds = new Set(); + + // First pass: collect base title IDs and process working.txt entries + workingText.trim().split('\n').forEach(line => { + const [id] = line.split('|'); + if (id.endsWith('000')) { + baseTitleIds.add(id); + } + }); + + // Second pass: process all content including updates from versions.json + workingText.trim().split('\n').forEach(line => { + const [id, version] = line.split('|'); + const type = id.endsWith('800') ? 'update' as const : + id.endsWith('000') ? 'base' as const : 'dlc' as const; + + const uniqueId = `${id}_${version || '0'}`; + const titleInfo = titlesMap.get(id); + const jsonInfo = workingJson[id]; + + // Get release date based on content type + let releaseDate = titleInfo?.date; + if (type === 'update' && version) { + releaseDate = getUpdateReleaseDate(id, version, versionsJson) || releaseDate; + } + + if (!processedItems.has(uniqueId)) { + processedItems.set(uniqueId, { + id, + uniqueId, + type, + version: version || jsonInfo?.Version, + name: titleInfo?.name || jsonInfo?.["Game Name"] || 'Unknown Title', + size: titleInfo?.size || jsonInfo?.Size, + releaseDate + }); + } + }); + + // Add all updates from versions.json + baseTitleIds.forEach(baseId => { + const updates = processUpdates(baseId, versionsJson); + updates.forEach(update => { + if (!processedItems.has(update.uniqueId)) { + processedItems.set(update.uniqueId, update); + } + }); + }); + + // Convert to array and sort by release date + const sortedItems = sortByReleaseDate(Array.from(processedItems.values())); + setItems(sortedItems); + + setLoading(false); + logger.info('Data load complete', { + totalItems: sortedItems.length, + baseCount: sortedItems.filter(item => item.type === 'base').length, + updateCount: sortedItems.filter(item => item.type === 'update').length, + dlcCount: sortedItems.filter(item => item.type === 'dlc').length, + withDatesCount: sortedItems.filter(item => item.releaseDate).length, + withoutDatesCount: sortedItems.filter(item => !item.releaseDate).length + }); + } catch (error) { + logger.error('Failed to load content data', { + errorMessage: error instanceof Error ? error.message : 'Unknown error' + }); + setLoading(false); + throw new Error('Failed to load content data'); + } + }; + + loadData(); + }, [dataSources]); + + useEffect(() => { + if (isDark) { + document.documentElement.classList.add('dark'); + } else { + document.documentElement.classList.remove('dark'); + } + }, [isDark]); + + const filteredItems = results.filter(item => item.type === lastActiveTab); + const totalPages = Math.ceil(filteredItems.length / itemsPerPage); + const paginatedItems = filteredItems.slice( + (currentPage - 1) * itemsPerPage, + currentPage * itemsPerPage + ); + + const counts = { + base: items.filter(item => item.type === 'base').length, + update: items.filter(item => item.type === 'update').length, + dlc: items.filter(item => item.type === 'dlc').length + }; + + if (loading) { + return ( +
+
+
+

Loading games...

+
+
+ ); + } + + return ( +
+
setDarkMode(!isDark)} /> + +
+
+ setLastActiveTab('base')} + /> + setLastActiveTab('update')} + /> + setLastActiveTab('dlc')} + /> +
+ +
+ item.type === lastActiveTab).length} + /> + + + + +
+
+
+ ); +} \ No newline at end of file diff --git a/src/components/ContentTable.tsx b/src/components/ContentTable.tsx new file mode 100644 index 0000000..7ebfde9 --- /dev/null +++ b/src/components/ContentTable.tsx @@ -0,0 +1,186 @@ +import { useState } from 'react'; +import { Info, ArrowUp, ArrowDown } from 'lucide-react'; +import { ContentItem, PaginationProps, SortField, SortDirection } from '../types'; +import { getIconUrl } from '../utils/formatters'; +import { getBaseTitleId, getRelatedContent } from '../utils/contentGrouping'; +import { GameDetails } from './GameDetails'; +import { Pagination } from './Pagination'; +import { formatFileSize, formatDate } from '../utils/formatters'; + +interface ContentTableProps extends PaginationProps { + items: ContentItem[]; + allItems: ContentItem[]; + sortField: SortField; + sortDirection: SortDirection; + onSort: (field: SortField) => void; +} + +export function ContentTable({ + items, + allItems, + currentPage, + totalPages, + onPageChange, + sortField, + sortDirection, + onSort +}: ContentTableProps) { + const [selectedId, setSelectedId] = useState(null); + + const handleDetails = (titleId: string) => { + const baseId = getBaseTitleId(titleId); + setSelectedId(baseId); + }; + + const getContentBadges = (titleId: string) => { + const baseId = getBaseTitleId(titleId); + const { updates, dlcs } = getRelatedContent(allItems, baseId); + + return ( +
+ {updates.length > 0 && ( + + {updates.length} Update{updates.length !== 1 ? 's' : ''} + + )} + {dlcs.length > 0 && ( + + {dlcs.length} DLC{dlcs.length !== 1 ? 's' : ''} + + )} +
+ ); + }; + + const renderSortHeader = (field: SortField, label: string) => ( + + + + ); + + return ( +
+
+ + + + + {renderSortHeader('id', 'Title ID')} + {renderSortHeader('name', 'Name')} + {renderSortHeader('size', 'Size')} + {renderSortHeader('releaseDate', 'Release Date')} + + + + + + {items.map((item) => ( + handleDetails(item.id)} + className="group hover:bg-muted/50 active:bg-muted transition-colors cursor-pointer" + > + + + + + + + + + ))} + +
+ Icon + + Related Content +
+
+ Game Icon { + const img = e.target as HTMLImageElement; + img.src = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="%23666" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"%3E%3Crect x="3" y="3" width="18" height="18" rx="2" ry="2"%3E%3C/rect%3E%3Ccircle cx="8.5" cy="8.5" r="1.5"%3E%3C/circle%3E%3Cpolyline points="21 15 16 10 5 21"%3E%3C/polyline%3E%3C/svg%3E'; + }} + /> +
+
+
+ {item.id} +
+ {item.version && ( +
+ v{item.version} +
+ )} +
+
+ {item.name || 'Unknown Title'} +
+
+
+ {formatFileSize(item.size)} +
+
+
+ {formatDate(item.releaseDate)} +
+
+ {getContentBadges(item.id)} + + +
+
+ + {items.length > 0 ? ( + + ) : ( +
+ No items found +
+ )} + + {selectedId && ( +
+
+ setSelectedId(null)} + /> +
+
+ )} +
+ ); +} \ No newline at end of file diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx new file mode 100644 index 0000000..23caf83 --- /dev/null +++ b/src/components/ErrorBoundary.tsx @@ -0,0 +1,85 @@ +import { Component, type ErrorInfo, type ReactNode } from 'react'; +import { AlertTriangle, RefreshCw } from 'lucide-react'; +import { useUserPreferences } from '../store/userPreferences'; + +interface Props { + children: ReactNode; +} + +interface State { + hasError: boolean; + error: Error | null; +} + +export class ErrorBoundary extends Component { + public state: State = { + hasError: false, + error: null + }; + + public static getDerivedStateFromError(error: Error): State { + return { hasError: true, error }; + } + + public componentDidCatch(error: Error, errorInfo: ErrorInfo) { + console.error('Uncaught error:', error, errorInfo); + } + + private handleReset = () => { + const { resetDataSources } = useUserPreferences.getState(); + resetDataSources(); + window.location.reload(); + }; + + public render() { + if (this.state.hasError) { + const isDataLoadError = this.state.error?.message?.includes('Failed to load game data'); + + return ( +
+
+
+ +

Something went wrong

+
+ +

+ {this.state.error?.message || 'An unexpected error occurred'} +

+ +
+ {isDataLoadError && ( + + )} + + +
+ + {isDataLoadError && ( +
+

💡 Tip

+

+ If you've modified the data source URLs in settings, try resetting them to their default values. + This often resolves loading issues. +

+
+ )} +
+
+ ); + } + + return this.props.children; + } +} \ No newline at end of file diff --git a/src/components/ErrorLog.tsx b/src/components/ErrorLog.tsx new file mode 100644 index 0000000..ed21a1f --- /dev/null +++ b/src/components/ErrorLog.tsx @@ -0,0 +1,90 @@ +import { useState, type ReactNode } from 'react'; +import { AlertTriangle, X } from 'lucide-react'; +import { logger, type LogEntry, type LogDetails } from '../utils/logger'; + +interface ErrorLogProps { + onClose: () => void; +} + +function formatLogDetails(details: LogDetails): ReactNode { + try { + return ( +
+        {JSON.stringify(details, null, 2)}
+      
+ ); + } catch { + return ( +
+        [Error formatting details]
+      
+ ); + } +} + +export function ErrorLog({ onClose }: ErrorLogProps) { + const [filter, setFilter] = useState<'all' | 'error'>('error'); + const logs = filter === 'all' ? logger.getLogs() : logger.getErrorLogs(); + + return ( +
+
+
+
+ +

Application Logs

+
+ +
+ +
+ +
+ +
+ {logs.length === 0 ? ( +

No logs to display

+ ) : ( +
+ {logs.map((log: LogEntry, index) => ( +
+
+ + {log.level.toUpperCase()} + + + {new Date(log.timestamp).toLocaleString()} + +
+

{log.message}

+ {log.details && formatLogDetails(log.details)} +
+ ))} +
+ )} +
+
+
+ ); +} \ No newline at end of file diff --git a/src/components/GameDetails.tsx b/src/components/GameDetails.tsx new file mode 100644 index 0000000..33ee0e9 --- /dev/null +++ b/src/components/GameDetails.tsx @@ -0,0 +1,175 @@ +import { useState } from 'react'; +import { X, Package, Download, ExternalLink } from 'lucide-react'; +import { ContentItem } from '../types'; +import { getVisualAssets } from '../utils/contentGrouping'; +import { ScreenshotGallery } from './ScreenshotGallery'; +import { formatFileSize, formatDate } from '../utils/formatters'; +import { useUserPreferences } from '../store/userPreferences'; + +interface ContentListProps { + items: ContentItem[]; + maxVisible?: number; + type: 'update' | 'dlc'; +} + +function ContentList({ items, maxVisible = 5, type }: ContentListProps) { + const [showAll, setShowAll] = useState(false); + const visibleItems = showAll ? items : items.slice(0, maxVisible); + const hasMore = items.length > maxVisible; + + return ( +
+ {visibleItems.map(item => ( +
+

+ {item.id} +

+ {item.name &&

{item.name}

} + {item.version && type === 'update' && ( +

+ Version {item.version} +

+ )} + {item.size && ( +

+ Size: {formatFileSize(item.size)} +

+ )} + {item.releaseDate && ( +

+ Released: {formatDate(item.releaseDate)} +

+ )} +
+ ))} + {hasMore && ( + + )} +
+ ); +} + +interface GameDetailsProps { + content: { + base: ContentItem | null; + updates: ContentItem[]; + dlcs: ContentItem[]; + }; + onClose: () => void; +} + +export function GameDetails({ content, onClose }: GameDetailsProps) { + const { base, updates, dlcs } = content; + const { maxDlcDisplay, maxUpdateDisplay } = useUserPreferences(); + + if (!base) return null; + + const assets = getVisualAssets(base.id); + + return ( +
+
+

Content Details

+ +
+ + {/* Banner */} +
+ Game Banner { + const img = e.target as HTMLImageElement; + img.style.display = 'none'; + }} + /> +
+ + {/* Content Info */} +
+
+
+ Game Icon { + const img = e.target as HTMLImageElement; + img.style.display = 'none'; + }} + /> +
+

Base Game

+

{base.id}

+
+
+ {base.name &&

{base.name}

} + {base.size && ( +

+ Size: {formatFileSize(base.size)} +

+ )} + {base.releaseDate && ( +

+ Released: {formatDate(base.releaseDate)} +

+ )} +
+ + {updates.length > 0 && ( +
+
+ +

Updates ({updates.length})

+
+ +
+ )} + + {dlcs.length > 0 && ( +
+
+ +

DLCs ({dlcs.length})

+
+ +
+ )} +
+ + {/* Screenshots */} + +
+ ); +} \ No newline at end of file diff --git a/src/components/Header.tsx b/src/components/Header.tsx new file mode 100644 index 0000000..809afea --- /dev/null +++ b/src/components/Header.tsx @@ -0,0 +1,83 @@ +import { useState } from 'react'; +import { Database, AlertTriangle, Sun, Moon, Settings as SettingsIcon } from 'lucide-react'; +import { ErrorLog } from './ErrorLog'; +import { Settings } from './Settings'; +import { useUserPreferences } from '../store/userPreferences'; +import { logger } from '../utils/logger'; + +interface HeaderProps { + onToggleTheme: () => void; +} + +export function Header({ onToggleTheme }: HeaderProps) { + const [showErrorLog, setShowErrorLog] = useState(false); + const [showSettings, setShowSettings] = useState(false); + const { isDark, showLogs } = useUserPreferences(); + const errorCount = logger.getErrorLogs().length; + + return ( +
+
+
+ +

NX Working Content Tracker

+
+ +
+ {showLogs && ( + + )} + + + + + + + + + + View on GitHub + +
+
+ + {showErrorLog && setShowErrorLog(false)} />} + {showSettings && setShowSettings(false)} />} +
+ ); +} \ No newline at end of file diff --git a/src/components/Pagination.tsx b/src/components/Pagination.tsx new file mode 100644 index 0000000..4defcd7 --- /dev/null +++ b/src/components/Pagination.tsx @@ -0,0 +1,100 @@ +import { ChevronLeft, ChevronRight } from 'lucide-react'; +import { PaginationProps } from '../types'; +import { useUserPreferences } from '../store/userPreferences'; + +export function Pagination({ currentPage, totalPages, onPageChange }: PaginationProps) { + const { itemsPerPage, setItemsPerPage } = useUserPreferences(); + const showPages = 5; + const halfShowPages = Math.floor(showPages / 2); + let startPage = Math.max(currentPage - halfShowPages, 1); + let endPage = Math.min(startPage + showPages - 1, totalPages); + + if (endPage - startPage + 1 < showPages) { + startPage = Math.max(endPage - showPages + 1, 1); + } + + const pages = Array.from( + { length: endPage - startPage + 1 }, + (_, i) => startPage + i + ); + + return ( +
+
+ + + Page {currentPage} of {totalPages} + +
+ +
+ + + {startPage > 1 && ( + <> + + {startPage > 2 && ( + ... + )} + + )} + + {pages.map(page => ( + + ))} + + {endPage < totalPages && ( + <> + {endPage < totalPages - 1 && ( + ... + )} + + + )} + + +
+
+ ); +} \ No newline at end of file diff --git a/src/components/ScreenshotGallery.tsx b/src/components/ScreenshotGallery.tsx new file mode 100644 index 0000000..1c0f78d --- /dev/null +++ b/src/components/ScreenshotGallery.tsx @@ -0,0 +1,51 @@ +import { Gallery, Item } from 'react-photoswipe-gallery'; +import 'photoswipe/style.css'; + +interface ScreenshotGalleryProps { + screenshots: string[]; +} + +export function ScreenshotGallery({ screenshots }: ScreenshotGalleryProps) { + return ( +
+

Screenshots

+ + +
+ {screenshots.map((url, index) => ( + + {({ ref, open }) => ( +
+ {`Screenshot { + const img = e.target as HTMLImageElement; + img.style.display = 'none'; + }} + /> +
+ + View Fullscreen + +
+
+ )} +
+ ))} +
+
+
+ ); +} \ No newline at end of file diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx new file mode 100644 index 0000000..44f30d9 --- /dev/null +++ b/src/components/SearchBar.tsx @@ -0,0 +1,43 @@ +import { Search, X } from 'lucide-react'; + +interface SearchBarProps { + value: string; + onChange: (value: string) => void; + resultCount?: number; + totalCount?: number; +} + +export function SearchBar({ value, onChange, resultCount, totalCount }: SearchBarProps) { + return ( +
+
+ +
+ onChange(e.target.value)} + className="block w-full pl-10 pr-24 py-3 bg-card border border-border rounded-lg + text-foreground placeholder-muted-foreground + focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent + hover:border-primary/50 transition-all duration-200" + placeholder="Search by Title ID or Name..." + /> +
+ {resultCount !== undefined && totalCount !== undefined && ( + + {resultCount.toLocaleString()} / {totalCount.toLocaleString()} + + )} + {value && ( + + )} +
+
+ ); +} \ No newline at end of file diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx new file mode 100644 index 0000000..a7eb591 --- /dev/null +++ b/src/components/Settings.tsx @@ -0,0 +1,274 @@ +import { useState } from 'react'; +import { Settings as SettingsIcon, X, ChevronDown, ChevronUp } from 'lucide-react'; +import { useUserPreferences } from '../store/userPreferences'; + +interface SettingsProps { + onClose: () => void; +} + +export function Settings({ onClose }: SettingsProps) { + const [showAdvanced, setShowAdvanced] = useState(false); + const { + itemsPerPage, + setItemsPerPage, + searchPrecision, + setSearchPrecision, + showLogs, + setShowLogs, + showVersionHistory, + setShowVersionHistory, + autoRefreshInterval, + setAutoRefreshInterval, + maxDlcDisplay, + setMaxDlcDisplay, + maxUpdateDisplay, + setMaxUpdateDisplay, + dataSources, + setDataSource, + resetDataSources + } = useUserPreferences(); + + const handlePrecisionChange = (value: string) => { + setSearchPrecision(parseFloat(value)); + }; + + return ( +
+
+
+
+ +

Settings

+
+ +
+ +
+ {/* Display Settings */} +
+

Display Settings

+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ + {/* Search Settings */} +
+

Search Settings

+ +
+ + handlePrecisionChange(e.target.value)} + className="w-full accent-primary" + /> +
+ Exact Match + Fuzzy Match +
+
+ {searchPrecision <= 0.3 ? ( + "High precision: Requires almost exact matches" + ) : searchPrecision <= 0.6 ? ( + "Medium precision: Allows some typos and variations" + ) : ( + "Low precision: Very forgiving, finds similar matches" + )} +
+
+
+ + {/* Feature Toggles */} +
+

Features

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + {/* Advanced Settings */} +
+ + + {showAdvanced && ( +
+
+ + setDataSource('workingContent', e.target.value)} + className="w-full bg-muted border border-border rounded-lg px-3 py-2 text-sm hover:border-primary/50 transition-colors" + placeholder="Enter URL for working.txt" + /> +
+ +
+ + setDataSource('titlesDb', e.target.value)} + className="w-full bg-muted border border-border rounded-lg px-3 py-2 text-sm hover:border-primary/50 transition-colors" + placeholder="Enter URL for titles_db.txt" + /> +
+ +
+ +
+ +
+

⚠️ Warning

+

+ Modifying these URLs may affect the application's functionality. + Only change them if you know what you're doing. +

+
+
+ )} +
+
+
+
+ ); +} \ No newline at end of file diff --git a/src/components/StatsCard.tsx b/src/components/StatsCard.tsx new file mode 100644 index 0000000..4a5392c --- /dev/null +++ b/src/components/StatsCard.tsx @@ -0,0 +1,28 @@ +import { type MouseEventHandler } from 'react'; + +interface StatsCardProps { + title: string; + count: number; + isActive?: boolean; + onClick?: MouseEventHandler; +} + +export function StatsCard({ title, count, isActive = false, onClick }: StatsCardProps) { + return ( + + ); +} \ No newline at end of file diff --git a/src/components/TabNavigation.tsx b/src/components/TabNavigation.tsx new file mode 100644 index 0000000..65565b2 --- /dev/null +++ b/src/components/TabNavigation.tsx @@ -0,0 +1,42 @@ +import { TabNavigationProps } from '../types'; +import { Database, Download, Package } from 'lucide-react'; + +export function TabNavigation({ activeTab, onTabChange, counts }: TabNavigationProps) { + const tabs = [ + { id: 'base' as const, name: 'Base Games', icon: Database, count: counts?.base ?? 0 }, + { id: 'update' as const, name: 'Updates', icon: Download, count: counts?.update ?? 0 }, + { id: 'dlc' as const, name: 'DLC', icon: Package, count: counts?.dlc ?? 0 }, + ]; + + return ( +
+ +
+ ); +} \ No newline at end of file diff --git a/src/hooks/useGameInfo.ts b/src/hooks/useGameInfo.ts new file mode 100644 index 0000000..e6ef041 --- /dev/null +++ b/src/hooks/useGameInfo.ts @@ -0,0 +1,52 @@ +import { useState, useEffect } from 'react'; +import { GameInfo } from '../types'; +import { gameDataService } from '../services/gameData'; +import { logger } from '../utils/logger'; + +export function useGameInfo(titleId: string | null) { + const [data, setData] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + if (!titleId) { + setData(null); + setError(null); + return; + } + + let mounted = true; + + const fetchData = async () => { + try { + setLoading(true); + setError(null); + + const gameInfo = await gameDataService.getGameData(titleId); + + if (mounted) { + setData(gameInfo); + setLoading(false); + } + } catch (err) { + logger.error('Error in useGameInfo hook', { + titleId, + error: err instanceof Error ? err.message : 'Unknown error' + }); + + if (mounted) { + setError(err instanceof Error ? err : new Error('Failed to fetch game data')); + setLoading(false); + } + } + }; + + fetchData(); + + return () => { + mounted = false; + }; + }, [titleId]); + + return { data, loading, error }; +} \ No newline at end of file diff --git a/src/hooks/useSearch.ts b/src/hooks/useSearch.ts new file mode 100644 index 0000000..6a4d0d0 --- /dev/null +++ b/src/hooks/useSearch.ts @@ -0,0 +1,132 @@ +import { useMemo, useState, useCallback } from 'react'; +import Fuse from 'fuse.js'; +import { ContentItem, SearchOptions, SortField, SortDirection } from '../types'; +import { useUserPreferences } from '../store/userPreferences'; + +const getSearchOptions = (precision: number): SearchOptions => ({ + threshold: precision, + distance: Math.floor(100 * (1 + precision)), + minMatchCharLength: Math.max(2, Math.floor(4 * (1 - precision))), +}); + +function parseDate(dateStr: string | undefined): number { + if (!dateStr) return 0; + try { + const date = new Date(dateStr); + return isNaN(date.getTime()) ? 0 : date.getTime(); + } catch { + return 0; + } +} + +function sortItems(items: ContentItem[], field: SortField, direction: SortDirection): ContentItem[] { + return [...items].sort((a, b) => { + let comparison = 0; + + switch (field) { + case 'id': + comparison = (a.id || '').localeCompare(b.id || ''); + break; + + case 'name': { + const nameA = a.name || 'Unknown Title'; + const nameB = b.name || 'Unknown Title'; + + // Always put Unknown at the end regardless of sort direction + if (nameA === 'Unknown Title' && nameB !== 'Unknown Title') return 1; + if (nameB === 'Unknown Title' && nameA !== 'Unknown Title') return -1; + + comparison = nameA.localeCompare(nameB); + break; + } + + case 'releaseDate': { + const dateA = parseDate(a.releaseDate); + const dateB = parseDate(b.releaseDate); + + // Always put items without dates at the end + if (!dateA && !dateB) return 0; + if (!dateA) return 1; + if (!dateB) return 1; + + // Sort by date + if (dateA !== dateB) { + comparison = dateA - dateB; + } else { + // If dates are equal, sort by name as secondary criteria + const nameA = a.name || 'Unknown Title'; + const nameB = b.name || 'Unknown Title'; + comparison = nameA.localeCompare(nameB); + } + break; + } + + case 'size': { + const sizeA = a.size || 0; + const sizeB = b.size || 0; + + // Always put items without size at the end + if (sizeA === 0 && sizeB !== 0) return 1; + if (sizeB === 0 && sizeA !== 0) return -1; + + comparison = sizeA - sizeB; + break; + } + } + + return direction === 'asc' ? comparison : -comparison; + }); +} + +export function useSearch(items: ContentItem[]) { + const [query, setQuery] = useState(''); + const [sortField, setSortField] = useState('releaseDate'); + const [sortDirection, setSortDirection] = useState('desc'); + const { searchPrecision } = useUserPreferences(); + + const searchOptions = useMemo(() => getSearchOptions(searchPrecision), [searchPrecision]); + + const fuse = useMemo(() => new Fuse(items, { + keys: [ + { name: 'id', weight: 2 }, + { name: 'name', weight: 1 } + ], + ...searchOptions, + shouldSort: false, + includeScore: true, + ignoreLocation: true, + useExtendedSearch: true, + getFn: (obj, path) => { + const value = obj[path as keyof ContentItem]; + return value?.toString() || ''; + } + }), [items, searchOptions]); + + const search = useCallback((searchQuery: string) => { + if (!searchQuery) return items; + return fuse.search(searchQuery).map(result => result.item); + }, [fuse, items]); + + const toggleSort = useCallback((field: SortField) => { + if (field === sortField) { + setSortDirection(current => current === 'asc' ? 'desc' : 'asc'); + } else { + setSortField(field); + setSortDirection('desc'); + } + }, [sortField]); + + const results = useMemo(() => { + const searchResults = search(query); + return sortItems(searchResults, sortField, sortDirection); + }, [search, query, sortField, sortDirection]); + + return { + query, + setQuery, + results, + sortField, + sortDirection, + toggleSort, + }; +} \ No newline at end of file diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..a6ac67d --- /dev/null +++ b/src/index.css @@ -0,0 +1,48 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --background: 255 255 255; + --foreground: 15 23 42; + + --muted: 241 245 249; + --muted-foreground: 100 116 139; + + --card: 255 255 255; + --card-foreground: 15 23 42; + + --border: 226 232 240; + + --primary: 37 99 235; + --primary-foreground: 255 255 255; +} + +:root[class~="dark"] { + --background: 15 23 42; + --foreground: 226 232 240; + + --muted: 30 41 59; + --muted-foreground: 148 163 184; + + --card: 30 41 59; + --card-foreground: 226 232 240; + + --border: 51 65 85; + + --primary: 59 130 246; + --primary-foreground: 255 255 255; +} + +body { + @apply antialiased bg-background text-foreground; +} + +.icon-container { + @apply relative w-16 h-16 flex-shrink-0; +} + +.icon-container img { + @apply absolute inset-0 w-full h-full object-cover rounded-lg; + aspect-ratio: 1/1; +} \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..1bee4b5 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; +import { ErrorBoundary } from './components/ErrorBoundary'; +import './index.css'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + + + +); \ No newline at end of file diff --git a/src/services/gameData.ts b/src/services/gameData.ts new file mode 100644 index 0000000..91bef9e --- /dev/null +++ b/src/services/gameData.ts @@ -0,0 +1,227 @@ +import { GameInfo } from '../types'; +import { logger } from '../utils/logger'; + +const API_CONFIG = { + baseUrls: [ + 'https://api.nlib.cc', + 'https://api-nlib.vercel.app', + 'https://nlib-api.vercel.app' + ], + timeout: 5000, + retryDelay: 1000, + maxRetries: 2, + cacheExpiry: 24 * 60 * 60 * 1000 // 24 hours +}; + +// IndexedDB configuration +const DB_CONFIG = { + name: 'NXWorkingDB', + version: 1, + storeName: 'gameData' +}; + +class GameDataService { + private db: IDBDatabase | null = null; + + constructor() { + this.initDB().catch(error => { + logger.error('Failed to initialize IndexedDB', { error }); + }); + } + + private async initDB(): Promise { + return new Promise((resolve, reject) => { + const request = indexedDB.open(DB_CONFIG.name, DB_CONFIG.version); + + request.onerror = () => { + logger.error('IndexedDB access denied'); + reject(new Error('IndexedDB access denied')); + }; + + request.onsuccess = (event) => { + this.db = (event.target as IDBOpenDBRequest).result; + resolve(); + }; + + request.onupgradeneeded = (event) => { + const db = (event.target as IDBOpenDBRequest).result; + if (!db.objectStoreNames.contains(DB_CONFIG.storeName)) { + db.createObjectStore(DB_CONFIG.storeName, { keyPath: 'id' }); + } + }; + }); + } + + private async getCachedData(titleId: string): Promise { + if (!this.db) return null; + + return new Promise((resolve) => { + const transaction = this.db!.transaction([DB_CONFIG.storeName], 'readonly'); + const store = transaction.objectStore(DB_CONFIG.storeName); + const request = store.get(titleId); + + request.onsuccess = () => { + const data = request.result; + if (!data || Date.now() - data.timestamp > API_CONFIG.cacheExpiry) { + resolve(null); + return; + } + resolve(data.gameInfo); + }; + + request.onerror = () => { + logger.warn('Failed to read from cache', { titleId }); + resolve(null); + }; + }); + } + + private async setCachedData(titleId: string, gameInfo: GameInfo): Promise { + if (!this.db) return; + + return new Promise((resolve) => { + const transaction = this.db!.transaction([DB_CONFIG.storeName], 'readwrite'); + const store = transaction.objectStore(DB_CONFIG.storeName); + const request = store.put({ + id: titleId, + gameInfo, + timestamp: Date.now() + }); + + request.onsuccess = () => resolve(); + request.onerror = () => { + logger.warn('Failed to write to cache', { titleId }); + resolve(); + }; + }); + } + + private async fetchWithTimeout(url: string): Promise { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), API_CONFIG.timeout); + + try { + const response = await fetch(url, { + signal: controller.signal, + headers: { + 'Accept': 'application/json', + 'Cache-Control': 'no-cache' + } + }); + clearTimeout(timeoutId); + return response; + } catch (error) { + clearTimeout(timeoutId); + throw error; + } + } + + private normalizeGameData(rawData: any, titleId: string): GameInfo { + return { + id: titleId, + name: rawData.name || 'Unknown Title', + publisher: rawData.publisher || 'Unknown Publisher', + description: rawData.description || 'No description available', + size: typeof rawData.size === 'number' ? rawData.size : null, + version: rawData.version || 'Unknown', + releaseDate: rawData.release_date || null, + rating: rawData.rating || null, + categories: Array.isArray(rawData.categories) ? rawData.categories : [], + languages: Array.isArray(rawData.languages) ? rawData.languages : [], + screenshots: Array.isArray(rawData.screenshots) ? rawData.screenshots : [] + }; + } + + async getGameData(titleId: string): Promise { + try { + // Try to get from cache first + const cachedData = await this.getCachedData(titleId); + if (cachedData) { + logger.info('Cache hit', { titleId }); + return cachedData; + } + + logger.info('Fetching game data', { titleId }); + + // Try each API endpoint + for (const baseUrl of API_CONFIG.baseUrls) { + for (let retry = 0; retry <= API_CONFIG.maxRetries; retry++) { + try { + if (retry > 0) { + await new Promise(resolve => setTimeout(resolve, API_CONFIG.retryDelay)); + } + + const response = await this.fetchWithTimeout(`${baseUrl}/nx/${titleId}`); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + const gameInfo = this.normalizeGameData(data, titleId); + + // Cache the successful response + await this.setCachedData(titleId, gameInfo); + + logger.info('Successfully fetched and cached game data', { titleId }); + return gameInfo; + } catch (error) { + logger.warn(`Attempt ${retry + 1} failed for ${baseUrl}`, { + titleId, + error: error instanceof Error ? error.message : 'Unknown error' + }); + + if (retry === API_CONFIG.maxRetries) { + continue; // Try next API endpoint + } + } + } + } + + // If all attempts failed, throw error + throw new Error('All API endpoints failed'); + } catch (error) { + logger.error('Failed to fetch game data', { + titleId, + error: error instanceof Error ? error.message : 'Unknown error' + }); + + // Return fallback data + return { + id: titleId, + name: 'Title Information Unavailable', + publisher: 'Unknown Publisher', + description: 'Game information is currently unavailable.', + size: null, + version: 'Unknown', + releaseDate: null, + rating: null, + categories: [], + languages: [], + screenshots: [] + }; + } + } + + async clearCache(): Promise { + if (!this.db) return; + + return new Promise((resolve) => { + const transaction = this.db!.transaction([DB_CONFIG.storeName], 'readwrite'); + const store = transaction.objectStore(DB_CONFIG.storeName); + const request = store.clear(); + + request.onsuccess = () => { + logger.info('Cache cleared successfully'); + resolve(); + }; + + request.onerror = () => { + logger.warn('Failed to clear cache'); + resolve(); + }; + }); + } +} + +export const gameDataService = new GameDataService(); \ No newline at end of file diff --git a/src/services/statsService.ts b/src/services/statsService.ts new file mode 100644 index 0000000..3e386ef --- /dev/null +++ b/src/services/statsService.ts @@ -0,0 +1,21 @@ +import { logger } from '../utils/logger'; + +interface StatsResponse { + total_downloads: number; +} + +export async function getDownloadStats(titleId: string): Promise { + try { + const response = await fetch(`https://stats.ghostland.at/${titleId}/json`); + if (!response.ok) return null; + + const data: StatsResponse = await response.json(); + return data.total_downloads; + } catch (error) { + logger.error('Failed to fetch download stats', { + titleId, + errorMessage: error instanceof Error ? error.message : 'Unknown error' + }); + return null; + } +} \ No newline at end of file diff --git a/src/store/userPreferences.ts b/src/store/userPreferences.ts new file mode 100644 index 0000000..6e79b61 --- /dev/null +++ b/src/store/userPreferences.ts @@ -0,0 +1,101 @@ +import { create } from 'zustand'; +import { persist, createJSONStorage } from 'zustand/middleware'; + +interface DataSources { + workingContent: string; + titlesDb: string; +} + +interface UserPreferences { + isDark: boolean; + itemsPerPage: number; + lastActiveTab: 'base' | 'update' | 'dlc'; + searchPrecision: number; + showLogs: boolean; + showVersionHistory: boolean; + autoRefreshInterval: number | null; + maxDlcDisplay: number; + maxUpdateDisplay: number; + dataSources: DataSources; + setDarkMode: (isDark: boolean) => void; + setItemsPerPage: (count: number) => void; + setLastActiveTab: (tab: 'base' | 'update' | 'dlc') => void; + setSearchPrecision: (precision: number) => void; + setShowLogs: (show: boolean) => void; + setShowVersionHistory: (show: boolean) => void; + setAutoRefreshInterval: (interval: number | null) => void; + setMaxDlcDisplay: (count: number) => void; + setMaxUpdateDisplay: (count: number) => void; + setDataSource: (key: keyof DataSources, url: string) => void; + resetDataSources: () => void; +} + +const DEFAULT_DATA_SOURCES: DataSources = { + workingContent: 'https://raw.githubusercontent.com/ghost-land/NX-Missing/refs/heads/main/data/working.txt', + titlesDb: 'https://raw.githubusercontent.com/ghost-land/NX-Missing/refs/heads/main/data/titles_db.txt' +}; + +const initialState = { + isDark: window.matchMedia('(prefers-color-scheme: dark)').matches, + itemsPerPage: 25, + lastActiveTab: 'base' as const, + searchPrecision: 0.4, + showLogs: false, + showVersionHistory: true, + autoRefreshInterval: null, + maxDlcDisplay: 5, + maxUpdateDisplay: 5, + dataSources: DEFAULT_DATA_SOURCES, +}; + +export const useUserPreferences = create()( + persist( + (set) => ({ + ...initialState, + setDarkMode: (isDark) => set({ isDark }), + setItemsPerPage: (itemsPerPage) => set({ itemsPerPage }), + setLastActiveTab: (lastActiveTab) => set({ lastActiveTab }), + setSearchPrecision: (searchPrecision) => set({ searchPrecision }), + setShowLogs: (showLogs) => set({ showLogs }), + setShowVersionHistory: (showVersionHistory) => set({ showVersionHistory }), + setAutoRefreshInterval: (autoRefreshInterval) => set({ autoRefreshInterval }), + setMaxDlcDisplay: (maxDlcDisplay) => set({ maxDlcDisplay }), + setMaxUpdateDisplay: (maxUpdateDisplay) => set({ maxUpdateDisplay }), + setDataSource: (key, url) => set(state => ({ + dataSources: { + ...state.dataSources, + [key]: url + } + })), + resetDataSources: () => set({ dataSources: DEFAULT_DATA_SOURCES }), + }), + { + name: 'nx-working-preferences', + version: 5, + storage: createJSONStorage(() => localStorage), + partialize: (state) => ({ + isDark: state.isDark, + itemsPerPage: state.itemsPerPage, + lastActiveTab: state.lastActiveTab, + searchPrecision: state.searchPrecision, + showLogs: state.showLogs, + showVersionHistory: state.showVersionHistory, + autoRefreshInterval: state.autoRefreshInterval, + maxDlcDisplay: state.maxDlcDisplay, + maxUpdateDisplay: state.maxUpdateDisplay, + dataSources: state.dataSources, + }), + migrate: (persistedState: any, version) => { + if (version < 5) { + return { + ...initialState, + ...persistedState, + maxDlcDisplay: 5, + maxUpdateDisplay: 5, + }; + } + return persistedState as UserPreferences; + }, + } + ) +); \ No newline at end of file diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..32cb67a --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,53 @@ +export interface ContentItem { + id: string; + uniqueId: string; + type: 'base' | 'update' | 'dlc'; + version?: string; + name?: string; + size?: number; + releaseDate?: string; +} + +export interface PaginationProps { + currentPage: number; + totalPages: number; + onPageChange: (page: number) => void; +} + +export type SortField = 'id' | 'name' | 'releaseDate' | 'size'; +export type SortDirection = 'asc' | 'desc'; + +export interface SearchOptions { + threshold: number; + distance: number; + minMatchCharLength: number; +} + +export interface SortOptions { + field: SortField; + direction: SortDirection; +} + +export interface GameInfo { + id: string; + name: string; + publisher: string; + description: string; + size: number | null; + version: string; + releaseDate: string | null; + rating: string | null; + categories: string[]; + languages: string[]; + screenshots: string[]; +} + +export interface TabNavigationProps { + activeTab: 'base' | 'update' | 'dlc'; + onTabChange: (tab: 'base' | 'update' | 'dlc') => void; + counts: { + base: number; + update: number; + dlc: number; + }; +} \ No newline at end of file diff --git a/src/utils/cache.ts b/src/utils/cache.ts new file mode 100644 index 0000000..030fe1d --- /dev/null +++ b/src/utils/cache.ts @@ -0,0 +1,52 @@ +import { logger } from './logger'; + +const CACHE_PREFIX = 'nx-working-'; +const CACHE_EXPIRY = 24 * 60 * 60 * 1000; // 24 hours + +export async function getCachedData(key: string): Promise { + try { + const item = localStorage.getItem(`${CACHE_PREFIX}${key}`); + if (!item) return null; + + const { value, timestamp } = JSON.parse(item); + if (Date.now() - timestamp > CACHE_EXPIRY) { + localStorage.removeItem(`${CACHE_PREFIX}${key}`); + return null; + } + + return value as T; + } catch (error) { + logger.error('Cache read error', { + key, + errorMessage: error instanceof Error ? error.message : 'Unknown error' + }); + return null; + } +} + +export function setCachedData(key: string, value: unknown): void { + try { + const item = { + value, + timestamp: Date.now(), + }; + localStorage.setItem(`${CACHE_PREFIX}${key}`, JSON.stringify(item)); + } catch (error) { + logger.error('Cache write error', { + key, + errorMessage: error instanceof Error ? error.message : 'Unknown error' + }); + } +} + +export function clearCache(): void { + try { + Object.keys(localStorage) + .filter(key => key.startsWith(CACHE_PREFIX)) + .forEach(key => localStorage.removeItem(key)); + } catch (error) { + logger.error('Cache clear error', { + errorMessage: error instanceof Error ? error.message : 'Unknown error' + }); + } +} \ No newline at end of file diff --git a/src/utils/contentGrouping.ts b/src/utils/contentGrouping.ts new file mode 100644 index 0000000..c56fb34 --- /dev/null +++ b/src/utils/contentGrouping.ts @@ -0,0 +1,66 @@ +import { ContentItem } from '../types'; + +export function getBaseTitleId(titleId: string): string { + // For base titles (ending in 000), return as is + if (titleId.endsWith('000')) { + return titleId; + } + + // For updates (ending in 800), use the base title ID + if (titleId.endsWith('800')) { + return titleId.slice(0, -3) + '000'; + } + + // For DLCs, change the fourth-to-last digit and set last 3 digits to 000 + const fourthFromEnd = titleId.charAt(titleId.length - 4); + const prevChar = (char: string): string => { + if (char >= '1' && char <= '9') return String(parseInt(char) - 1); + if (char >= 'b' && char <= 'z') return String.fromCharCode(char.charCodeAt(0) - 1); + if (char >= 'B' && char <= 'Z') return String.fromCharCode(char.charCodeAt(0) - 1); + return char; + }; + return titleId.slice(0, -4) + prevChar(fourthFromEnd) + '000'; +} + +export function getBasePrefix(titleId: string): string { + return titleId.slice(0, 12); +} + +export function getRelatedContent(items: ContentItem[], baseId: string): { + base: ContentItem | null; + updates: ContentItem[]; + dlcs: ContentItem[]; +} { + // Get base prefix for finding related content + const basePrefix = getBasePrefix(baseId); + + // Find all related items + const relatedItems = items.filter(item => getBasePrefix(item.id) === basePrefix); + + // Get base game (should be the one matching baseId) + const base = relatedItems.find(item => item.id === baseId) || null; + + // Get updates (ending with 800) + const updates = relatedItems.filter(item => item.id.endsWith('800')) + .sort((a, b) => (b.version || '0').localeCompare(a.version || '0')); + + // Get DLCs (not base and not updates) + const dlcs = relatedItems.filter(item => + !item.id.endsWith('000') && !item.id.endsWith('800') + ).sort((a, b) => a.id.localeCompare(b.id)); + + return { base, updates, dlcs }; +} + +export function getVisualAssets(titleId: string) { + const baseId = getBaseTitleId(titleId); + const baseUrl = 'https://api.nlib.cc/nx'; + + return { + icon: `${baseUrl}/${baseId}/icon/128/128`, + banner: `${baseUrl}/${baseId}/banner/1280/720`, + screenshots: Array.from({ length: 6 }, (_, i) => + `${baseUrl}/${baseId}/screen/${i + 1}` + ) + }; +} \ No newline at end of file diff --git a/src/utils/formatters.ts b/src/utils/formatters.ts new file mode 100644 index 0000000..7c1ec7c --- /dev/null +++ b/src/utils/formatters.ts @@ -0,0 +1,37 @@ +import { getBaseTitleId } from './contentGrouping'; + +export function getIconUrl(titleId: string): string { + return `https://api.nlib.cc/nx/${getBaseTitleId(titleId)}/icon/128/128`; +} + +export function formatFileSize(bytes?: number): string { + if (!bytes) return 'Unknown'; + + const units = ['B', 'KB', 'MB', 'GB']; + let size = bytes; + let unitIndex = 0; + + while (size >= 1024 && unitIndex < units.length - 1) { + size /= 1024; + unitIndex++; + } + + return `${size.toFixed(2)} ${units[unitIndex]}`; +} + +export function formatDate(dateString?: string): string { + if (!dateString) return 'Unknown'; + + try { + const date = new Date(dateString); + if (isNaN(date.getTime())) return 'Unknown'; + + return new Intl.DateTimeFormat('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric' + }).format(date); + } catch { + return 'Unknown'; + } +} \ No newline at end of file diff --git a/src/utils/logger.ts b/src/utils/logger.ts new file mode 100644 index 0000000..23c8263 --- /dev/null +++ b/src/utils/logger.ts @@ -0,0 +1,70 @@ +export type LogLevel = 'info' | 'warn' | 'error'; + +export interface LogDetails { + [key: string]: string | number | boolean | null | undefined; +} + +export interface LogEntry { + timestamp: string; + level: LogLevel; + message: string; + details?: LogDetails; +} + +class Logger { + private logs: LogEntry[] = []; + private readonly maxLogs = 1000; + + private createEntry(level: LogLevel, message: string, details?: LogDetails): LogEntry { + return { + timestamp: new Date().toISOString(), + level, + message, + details, + }; + } + + private addLog(entry: LogEntry) { + this.logs.unshift(entry); + if (this.logs.length > this.maxLogs) { + this.logs.pop(); + } + + const style = `color: ${ + entry.level === 'error' ? 'red' : + entry.level === 'warn' ? 'orange' : + 'blue' + }; font-weight: bold;`; + + console.groupCollapsed(`%c${entry.level.toUpperCase()}: ${entry.message}`, style); + console.log('Timestamp:', entry.timestamp); + if (entry.details) console.log('Details:', entry.details); + console.groupEnd(); + } + + info(message: string, details?: LogDetails) { + this.addLog(this.createEntry('info', message, details)); + } + + warn(message: string, details?: LogDetails) { + this.addLog(this.createEntry('warn', message, details)); + } + + error(message: string, details?: LogDetails) { + this.addLog(this.createEntry('error', message, details)); + } + + getLogs(): LogEntry[] { + return [...this.logs]; + } + + getErrorLogs(): LogEntry[] { + return this.logs.filter(log => log.level === 'error'); + } + + clearLogs() { + this.logs = []; + } +} + +export const logger = new Logger(); \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..2f73add --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,24 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + "./index.html", + "./src/**/*.{js,ts,jsx,tsx}", + ], + darkMode: 'class', + theme: { + extend: { + colors: { + background: 'rgb(var(--background) / )', + foreground: 'rgb(var(--foreground) / )', + muted: 'rgb(var(--muted) / )', + 'muted-foreground': 'rgb(var(--muted-foreground) / )', + card: 'rgb(var(--card) / )', + 'card-foreground': 'rgb(var(--card-foreground) / )', + border: 'rgb(var(--border) / )', + primary: 'rgb(var(--primary) / )', + 'primary-foreground': 'rgb(var(--primary-foreground) / )', + }, + }, + }, + plugins: [], +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7a7611e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} \ No newline at end of file diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..099658c --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..bfc3060 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + port: 5173, + strictPort: true, + host: true + } +}); \ No newline at end of file